Ghostly Prison: that's a difficult card!
Posted: 11 Aug 2012, 16:34
I finally managed to make a working Ghostly Prison, after lots of tries. Here's the code:
I'm not 100% satisfied because the trigger at the beginning of combat would have been more correct as an END_OF_STEP trigger. I tried to make it like that, but the card started to give odd results because there wasn't enough time to process the new conditions before attackers were declared. Anyway I can't think of scenarios where being able to do something between the payment and the declaration of the attackers could disrupt the game, so I can live with the approximation.
About the AI: I think it understands quite well what's going on. From the few tests I made with the fully working card, it seems that it often gives more importance to playing spells rather than attacking when it has to deal with Ghostly Prison, but I saw it paying and attacking with a creature, so the test is positive.
If you think you can code Ghostly Prison in a simpler and/or cleaner way, or you just have a suggestion, please let me know.
- Code: Select all
<?xml version='1.0'?>
<CARD_V2>
<FILENAME text="GHOSTLY_PRISON_75328" />
<CARDNAME text="GHOSTLY_PRISON" />
<TITLE>
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Ghostly Prison]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Prigione Spettrale]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Gespenstisches Gefängnis]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Prison fantomale]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Prisión fantasmagórica]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[亡霊の牢獄]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Ghostly Prison]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Ghostly Prison]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Prisão Fantasmagórica]]></LOCALISED_TEXT>
</TITLE>
<MULTIVERSEID value="75328" />
<ARTID value="A75328" />
<ARTIST name="Lars Grant-West" />
<CASTING_COST cost="{2}{W}" />
<FLAVOURTEXT>
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Destroyed in one of the first battles of the Kami War, the town of Reito still grieved.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Distrutta durante una delle prime battaglie della Guerra dei Kami, la città di Reito piange ancora.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Die Stadt Reito war immer noch am Trauern, obwohl sie schon in einer der ersten Schlachten des Kami-Krieges zerstört worden war.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Détruite lors d’une des premières batailles de la Guerre des kami, la ville de Reito pleurait encore.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Destruida en una de las primeras batallas de la guerra con los kami, la ciudad de Reito aún se lamenta.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[神の乱の最初の戦闘で破壊された霊都は、いまだに嘆きの中にある。]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Destroyed in one of the first battles of the Kami War, the town of Reito still grieved.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Destroyed in one of the first battles of the Kami War, the town of Reito still grieved.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Destruída em uma das primeiras batalhas da Guerra dos Kamis, a cidade de Reito ainda estava em luto.]]></LOCALISED_TEXT>
</FLAVOURTEXT>
<TYPE metaname="Enchantment" />
<EXPANSION value="CHK" />
<RARITY metaname="U" />
<TRIGGERED_ABILITY auto_skip="1">
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Creatures can’t attack you unless their controller pays {2} for each creature he or she controls that’s attacking you.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Le creature non possono attaccarti a meno che il loro controllore non paghi {2} per ogni creatura che controlla che ti sta attaccando.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Kreaturen können dich nicht angreifen, falls ihr Beherrscher nicht für jede dich angreifende Kreatur, die er kontrolliert, {2} bezahlt.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Les créatures ne peuvent pas vous attaquer à moins que leur contrôleur ne paie {2} pour chaque créature qu’il contrôle qui vous attaque.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Las criaturas no pueden atacarte a menos que su controlador pague {2} por cada criatura que controla que te está atacando.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[クリーチャーは、それのコントローラーが、そのプレイヤーがコントロールするあなたを攻撃しているクリーチャー1体につき{2}を支払わないかぎり、あなたを攻撃できない。]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Creatures can’t attack you unless their controller pays {2} for each creature he or she controls that’s attacking you.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Creatures can’t attack you unless their controller pays {2} for each creature he or she controls that’s attacking you.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[As criaturas não podem atacá-lo a menos que seu controlador pague {2} para cada criatura atacante.]]></LOCALISED_TEXT>
<TRIGGER value="BEGINNING_OF_STEP">
local player = TriggerPlayer()
local cost = 2
if MTG():GetStep() == STEP_BEGIN_COMBAT and
player:MyTurn() ~= 0 and
player:GetTeam() ~= EffectController():GetTeam() and
player:CanAfford("{"..cost.."}") == 1 then
local filter = Object():GetFilter()
filter:Clear()
filter:AddCardType( CARD_TYPE_CREATURE )
filter:SetZone( ZONE_IN_PLAY )
filter:SetController( player )
filter:NotTargetted()
if filter:CountStopAt(1) == 1 then
return true
end
end
return false
</TRIGGER>
<RESOLUTION_TIME_ACTION>
local player = TriggerPlayer()
local cost = 2
if player:CanAfford("{"..cost.."}") == 1 then
local filter = Object():GetFilter()
filter:Clear()
filter:AddCardType( CARD_TYPE_CREATURE )
filter:SetZone( ZONE_IN_PLAY )
filter:SetController( player )
filter:NotTargetted()
if filter:CountStopAt(1) == 1 then
local max_attackers = player:GetTotalMana() / cost
player:SetTargetCount( max_attackers )
for i=0,max_attackers-1 do
player:SetTargetPrompt( i, "CARD_QUERY_CHOOSE_CREATURE_GHOSTLY_PRISON" )
end
player:ChooseTargetsWithFlags( NO_VALIDATION, EffectDC():Make_Targets(0), QUERY_FLAG_CAN_BE_FINISHED_EARLY + QUERY_FLAG_CAN_BE_FINISHED_EARLY_FOR_AI_AS_WELL )
end
end
</RESOLUTION_TIME_ACTION>
<RESOLUTION_TIME_ACTION>
local player = TriggerPlayer()
local cost = 2
if player:CanAfford("{"..cost.."}") == 1 then
local filter = Object():GetFilter()
filter:Clear()
filter:AddCardType( CARD_TYPE_CREATURE )
filter:SetZone( ZONE_IN_PLAY )
filter:SetController( player )
filter:NotTargetted()
if filter:CountStopAt(1) == 1 and EffectDC() ~= nil and EffectDC():Get_Targets(0) ~= nil then
local max_attackers = player:GetTotalMana() / cost
local ptr_index = 0
local player_index = -1
local nsp = MTG():GetNumberOfStartingPlayers()
for i=0,nsp-1 do
if MTG():GetNthStartingPlayer(i) == player then
player_index = i
end
end
local player_chest = ObjectDC():Make_Chest(player_index)
for i=0,max_attackers-1 do
local creature = EffectDC():Get_Targets(0):Get_CardPtr(i)
if creature ~= nil then
player_chest:Set_CardPtr(ptr_index, creature)
if player:CanAfford("{"..cost.."}") == 1 then
player:TapLand("{"..cost.."}")
end
ptr_index = ptr_index + 1
end
end
ObjectDC():Set_Int(nsp+player_index, ptr_index)
end
end
</RESOLUTION_TIME_ACTION>
</TRIGGERED_ABILITY>
<TRIGGERED_ABILITY internal="1" pre_trigger="1">
<TRIGGER value="CANT_ATTACK_PLAYER_TEST">
if TriggerPlayer() == EffectController() and ObjectDC() ~= nil then
local player = TriggerObject():GetController()
local player_index = -1
local nsp = MTG():GetNumberOfStartingPlayers()
for i=0,nsp-1 do
if MTG():GetNthStartingPlayer(i) == player then
player_index = i
end
end
local ptr_index = ObjectDC():Get_Int(nsp+player_index)
if ptr_index > 0 then
local player_chest = ObjectDC():Get_Chest(player_index)
if player_chest ~= nil then
for i=0,ptr_index-1 do
if player_chest:Get_CardPtr(i) ~= nil and TriggerObject() == player_chest:Get_CardPtr(i) then
return false
end
end
end
end
return true
end
return false
</TRIGGER>
</TRIGGERED_ABILITY>
<TRIGGERED_ABILITY internal="1">
<TRIGGER value="BEGINNING_OF_STEP">
return MTG():GetStep() == STEP_END_OF_COMBAT and
TriggerPlayer():MyTurn() ~= 0 and
TriggerPlayer():GetTeam() ~= EffectController():GetTeam()
</TRIGGER>
<RESOLUTION_TIME_ACTION>
local player_index = -1
local nsp = MTG():GetNumberOfStartingPlayers()
for i=0,nsp-1 do
if MTG():GetNthStartingPlayer(i) == player then
player_index = i
end
end
local player_chest = ObjectDC():Get_Chest(player_index)
if player_chest ~= nil then
player_chest:Clear()
end
ObjectDC():Set_Int(nsp+player_index, 0)
</RESOLUTION_TIME_ACTION>
</TRIGGERED_ABILITY>
<AI_BASE_SCORE score="450" zone="ZONE_IN_PLAY" />
</CARD_V2>
I'm not 100% satisfied because the trigger at the beginning of combat would have been more correct as an END_OF_STEP trigger. I tried to make it like that, but the card started to give odd results because there wasn't enough time to process the new conditions before attackers were declared. Anyway I can't think of scenarios where being able to do something between the payment and the declaration of the attackers could disrupt the game, so I can live with the approximation.
About the AI: I think it understands quite well what's going on. From the few tests I made with the fully working card, it seems that it often gives more importance to playing spells rather than attacking when it has to deal with Ghostly Prison, but I saw it paying and attacking with a creature, so the test is positive.
If you think you can code Ghostly Prison in a simpler and/or cleaner way, or you just have a suggestion, please let me know.