It is currently 18 Aug 2019, 21:13
   
Text Size

Ghostly Prison: that's a difficult card!

Moderators: Xander9009, CCGHQ Admins

Ghostly Prison: that's a difficult card!

Postby thefiremind » 11 Aug 2012, 16:34

I finally managed to make a working Ghostly Prison, after lots of tries. Here's the code:
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 &gt; 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>
When an opponent is at the beginning of combat, the card will ask which creatures should be allowed to attack (of course, the upper limit is the maximum number of times that the cost can be paid). The chosen creatures are saved in a chest (each player's creatures in a different one). The CANT_ATTACK_PLAYER_TEST trigger checks if the creatures are saved in the chest: if not, they aren't allowed to attack the controller of Ghostly Prison. At the end of combat, the chest is deleted.

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 {2} 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.
< Former DotP 2012/2013/2014 modder >
Currently busy with life...
User avatar
thefiremind
Programmer
 
Posts: 3515
Joined: 07 Nov 2011, 10:55
Has thanked: 118 times
Been thanked: 717 times

Re: Ghostly Prison: that's a difficult card!

Postby sadlyblue » 11 Aug 2012, 17:46

Great job. I always loved that card, and the blue version Propaganda. It's great for control...
sadlyblue
 
Posts: 175
Joined: 06 Feb 2012, 13:18
Has thanked: 18 times
Been thanked: 16 times

Re: Ghostly Prison: that's a difficult card!

Postby nabeshin » 13 Aug 2012, 18:24

I made some options, all problem in that that AI would understand that occurs and correctly reacted, almost done Norn's Annex , but I am dissatisfied
User avatar
nabeshin
 
Posts: 207
Joined: 27 Jun 2011, 20:07
Has thanked: 5 times
Been thanked: 31 times

Re: Ghostly Prison: that's a difficult card!

Postby spakattack » 17 May 2013, 00:39

I love this card, but the AI hardly ever attacks, even when they have enough mana. Sometimes they will pay the mana and attack, but most of the time they won't, even if they could directly attack me. Sometimes they will pay the mana and do nothing on the battle phase.

I have to do more testing, but maybe they didn't attack for some other reason. It just seems the AI doesn't understand the card completely, and won't always attack when they could(probably should of). I win almost every game when I get 1, 2, or more of these out because of the AI not attacking when they had enough mana (and sometimes when I have no creatures and they have like a 30/30 primalcrux, a dozen creatures/tokens or something like that.)

I'm just glad it's coded and exists, but it seems like there is something up with the AI dealing with it. I guess that's why there are other humans out there to play with. Though, sadly, I really don't know how to play with other people with custom wads.
spakattack
 
Posts: 32
Joined: 30 Apr 2013, 21:45
Has thanked: 9 times
Been thanked: 2 times

Re: Ghostly Prison: that's a difficult card!

Postby thefiremind » 17 May 2013, 09:16

Sorry to hear that... I tested it really fast, and I stopped when I saw the AI paying {2} once. I couldn't imagine that it was a rare event. Anyway, I wouldn't know how to make the card better: as I said in the first post, if someone has a better idea, please don't hesitate to explain it.
< Former DotP 2012/2013/2014 modder >
Currently busy with life...
User avatar
thefiremind
Programmer
 
Posts: 3515
Joined: 07 Nov 2011, 10:55
Has thanked: 118 times
Been thanked: 717 times


Return to Programming Talk

Who is online

Users browsing this forum: No registered users and 3 guests


Who is online

In total there are 3 users online :: 0 registered, 0 hidden and 3 guests (based on users active over the past 10 minutes)
Most users ever online was 287 on 31 Mar 2019, 04:11

Users browsing this forum: No registered users and 3 guests

Login Form