カード効果の実装手順 - 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>&lt;b&gt;Battlecry:&lt;/b&gt; Give 4 Armor to_each hero.</enUS>
	</Tag>
	<Tag enumID="351" name="FLAVORTEXT" type="LocString">
		<enUS>&quot;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.&quot;</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"""
   	## &lt;b&gt;Battlecry:&lt;/b&gt; Give 4 Armor to_each hero.
   	pass

としておきます。フレーバーテキストは要りません。カードテキストの英語の意味が分かりにくければ、日本語[x]&lt;b&gt;雄叫び:&lt;/b&gt;各ヒーローに装甲を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"""
   	## &lt;b&gt;Battlecry:&lt;/b&gt; 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が付いていることがわかります(赤線)。

⚠️ **GitHub.com Fallback** ⚠️