カード効果の実装手順 - aharalabMeiji/fireplaceAharaLab GitHub Wiki
カード実装は、キリがありません。ブリザードエンターテインメントはつぎつぎと新しいカードセットを発表しますので。「最新の実装はしない」とか言っていたのですが、ちょっと休むとあっという間にカードセット3つくらい置いて行かれます。
基本的にはfireplace.cards
フォルダのなかですべて実装を行います。カードセットごとにフォルダをつくり、たとえば「スクロマンス」に関するカードはすべてscholo
フォルダに入れています。
カードセットのフォルダの中では、大きなルールとしてneutral.py
を中立カード、そのほかは属性ごとにファイルを分けて、これまではhunter.py
,mage.py
,paladin.py
は作るようにしてきています。また、これらのファイルをシステムに読み込んでもらうためのファイル__init__.py
も必要で、
from .neutral import *
from .hunter import *
from .mage import *
from .paladin import *
のような内容にしておきます。
カードの情報は運営が発表しているものに準拠します。どうしてもfireplace-Aharalabに載らないものもありますので、それはあきらめたうえで「カードの除外」をしておきます。
(カード実装の例)
class SCH_142:#done
""" Voracious Reader (rare) """
#At the end of your turn, draw until you have 3 cards.
events = OWN_TURN_END.on(DrawUntil(CONTROLLER,3))#
pass
こういう感じで、カードの種類ごとにclassを作ります。記述は基本的に英語を使います。カードの名前を次の行に書きます。効果をその次の行に書きます。4行目以降で実装を行います。最後のpassはあってもなくてもいいです。(あると心が休まります。)なお、#done
や#OK
という記載が見られますがこれは「カード効果の実装手順#カードの動作確認」を済ませた、という意味です。
実際の手順としては、運営のHPにカードのリストがあります。ここでは例として、Madness at the Darkmoon Faire
の中立カードを実装する手順を示します。まずhttps://playhearthstone.com/en-us/cards?class=neutral&set=madness-at-the-darkmoon-faire&viewMode=table
を表示します。ここにあるカード名をすべてコピーします。カード名以外の情報はいらないのですが、つけておいてもいいです。あとでクラスのコメント文にしますので、"""
ではさんでおきます。
"""Armor Vendor"""
"""Safety Inspector"""
・・・・
これを
from ..utils import *
class DMF_:
"""Armor Vendor"""
##
pass
class DMF_:
"""Safety Inspector"""
##
pass
の形に整えておきます。一行目は各種関数へのリンクです。##はカード記述を書く行です。また、ダークムーンフェアのカードは'DMF_***'というIDであることがわかっているので、そこまで書き込んでおくと後が少し楽になります。
次は「CardDefs.xml」から、この名前のカードを探し出します。この巨大なファイルは、ウィンドウズであるならば
c:\user\(user-name)\AppData\Local\Programs\Python\Python38\Lib\site-packages\hearthstone-data
という奥深いフォルダにいますので、デスクトップにリンクを作っておくと簡単です。また、このファイルは書き換えてしまうと動作が変わってしまうので、読み込み専用で開くのが良いと思います。
さて、たとえばArmor Venderを検索すると(言語タグはenUSだけをコピーしてきました)(カードIDがYOP_***
でした。そういうこともあるのか・・・)こういう感じです。
<Entity CardID="YOP_032" ID="61970" version="2">
<Tag enumID="185" name="CARDNAME" type="LocString">
<enUS>Armor Vendor</enUS>
</Tag>
<Tag enumID="184" name="CARDTEXT" type="LocString">
<enUS><b>Battlecry:</b> Give 4 Armor to_each hero.</enUS>
</Tag>
<Tag enumID="351" name="FLAVORTEXT" type="LocString">
<enUS>"You look like the kind of person that would buy three sets of armor. One to wear, a spare, and a spare for the spare."</enUS>
</Tag>
<Tag enumID="342" name="ARTISTNAME" type="String">Maria Trepalina</Tag>
<Tag enumID="45" name="HEALTH" type="Int" value="3"/>
<Tag enumID="47" name="ATK" type="Int" value="1"/>
<Tag enumID="48" name="COST" type="Int" value="1"/>
<Tag enumID="183" name="CARD_SET" type="Int" value="1466"/>
<Tag enumID="199" name="CLASS" type="Int" value="12"/>
<Tag enumID="202" name="CARDTYPE" type="Int" value="4"/>
<Tag enumID="203" name="RARITY" type="Int" value="3"/>
<Tag enumID="218" name="BATTLECRY" type="Int" value="1"/>
<Tag enumID="321" name="COLLECTIBLE" type="Int" value="1"/>
<Tag enumID="1824" name="MINI_SET" type="Int" value="1"/>
</Entity>
これを見て
class YOP_032:
"""Armor Vendor"""
## <b>Battlecry:</b> Give 4 Armor to_each hero.
pass
としておきます。フレーバーテキストは要りません。カードテキストの英語の意味が分かりにくければ、日本語[x]<b>雄叫び:</b>各ヒーローに装甲を4ずつ付与する。
を参照するとよいでしょう。
あとは実装です。まず、カードに付随するバフ(Buff)のためのエンチャント(Enchant)カードがあるかどうかを調べましょう。YOP_032カードの一つつぎを見るとYOP_033になっていますので、付随するカードがないことがわかります。ここに、たとえばYOP_032eのようなカードがあると、これがエンチャントカードで、このカードを用いて実装することになります。
まずbattlecryなのでplay = コマンド
という文法になります。これはカードをプレイした瞬間に発動する、という意味です。自分のヒーローのアーマーを4増やすということなので、fireplace.action.py
というファイルを見てみてArmorで検索をかけてみると
class GainArmor(TargetedAction):
"""
Make hero targets gain \a amount armor.
"""
というクラスがあることがわかりますので、これがそのまま使えます。(この段階で、ぴったりするクラスが見つからない場合には、自分で作ることになります。)
「自分のヒーローのアーマーを4増やす」はGainArmor(FRIENDLY_HERO,4)
で「敵のヒーローのアーマーを4増やすはGainArmor(ENEMY_HERO,4)
になりますので、実装は
class YOP_032:
"""Armor Vendor"""
## <b>Battlecry:</b> Give 4 Armor to_each hero.
play = (GainArmor(FRIENDLY_HERO,4),GainArmor(ENEMY_HERO,4))
pass
となります。(二つの作業を並列するときにはこんな感じで並べれば大丈夫です。)これを延々とつづけるのです・・・・・(溜息)
カードを実装したら、それが正しく動作するかどうかを確認する必要があります。確認自体はstart.py
の設定で「人vsランダム」という対戦で十分ですので、そのようにして「1回対戦」にしておきます。
play_set_of_games(Human, Vector2, deck1=[], deck2=[], gameNumber=1, debugLog=True, P1MAXMANA=10)
つぎにutils.py
にあるPresetHands(player1, player2)
という関数で「強制的に目当てのカードをハンドに入れる」作業をします。これは自分方にも相手方にもできます。たとえば
Discard(player1.hand[-1]).trigger(player1)
Give(player1,'YOP_032').trigger(player1)#target
としておけば、「player1=人」にこのカードを引かせることができます。このカードをすぐに使いたいので、マナが十分にあることが必要ですが、P1MAXMANA=10としておけば、自分のマナは10から始まります。
と、こういう感じで始まります。該当するカードがハンドに入っていることと、その効果が正しく表示されていることを確認して、カードを使います。
ログを表示しておくと、実際にGainArmorが両ヒーローに対して行われていることが確認できます(青線)。
実際に、各ヒーローに装甲4が付いていることがわかります(赤線)。