It is currently 25 Apr 2024, 23:06
   
Text Size

How would you code Rooftop Storm?

Moderator: CCGHQ Admins

How would you code Rooftop Storm?

Postby thefiremind » 13 Jul 2013, 19:58

It could seem easy, but it isn't. Rooftop Storm says "You may pay {0} rather than pay the mana cost for Zombie creature spells you cast". Only the controller of the enchantment benefits from this, but the benefit should include all zones where that player could cast a Zombie from.

First attempt:
Code: Select all
  <STATIC_ABILITY>
    <FILTER filter_id="0">
    local filter = ClearFilter()
    filter:SetZone(ZONE_ANYWHERE)
    filter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
    filter:Add(FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE)
    filter:Add( FE_CONTROLLER, OP_IS, EffectController() )
    </FILTER>
    <CONTINUOUS_ACTION layer="8" filter_id="0">
    if FilteredCard() ~= nil then
       FilteredCard():GetCurrentCharacteristics():GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate" active_zone="ZONE_ANY">
    <COST mana_cost="{0}" type="Mana" />
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
No way, this would allow me to always cast Zombies for free from any zone that I own.

Second attempt:
Code: Select all
  <STATIC_ABILITY>
    <FILTER filter_id="0">
    local filter = ClearFilter()
    filter:SetZone(ZONE_STACK)
    filter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
    filter:Add(FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE)
    filter:Add( FE_CONTROLLER, OP_IS, EffectController() )
    </FILTER>
    <CONTINUOUS_ACTION layer="8" filter_id="0">
    if FilteredCard() ~= nil then
       FilteredCard():GetCurrentCharacteristics():GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate" active_zone="ZONE_ANY">
    <COST mana_cost="{0}" type="Mana" />
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
This works only if I have enough mana to cast the Zombie by paying its full mana cost: the game cannot guess that the Zombie cards will have this ability on the stack while they are still in other zones.

Third attempt:
Code: Select all
  <TRIGGERED_ABILITY replacement_effect="1">
    <TRIGGER value="CONSIDERED_FOR_CAST" simple_qualifier="controller" pre_trigger="1">
    return TriggerObject():GetCardType():Test(CARD_TYPE_CREATURE) and TriggerObject():GetSubType():Test(CREATURE_TYPE_ZOMBIE)
    </TRIGGER>
    <RESOLUTION_TIME_ACTION>
    if TriggerObject() ~= nil then
       TriggerObject():GetCurrentCharacteristics():GrantAbility(0)
    end
    </RESOLUTION_TIME_ACTION>
  </TRIGGERED_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate" active_zone="ZONE_ANY">
    <COST mana_cost="{0}" type="Mana" />
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
This sounds like the most promising, doesn't it? Well, the AI can use this implementation... but human players cannot! Try it and the game will freeze at some point during the cast. Merge this attempt with the second, and you'll delay the freeze, but it will happen anyway.

I'm afraid this is like the problem with activating abilities from hand that don't move the card before resolution: we know the right way to do it, but a stupid engine bug prevents us from acting that way. I still hope I'm wrong and someone else finds a good implementation that doesn't freeze the game, though. Any ideas?

EDIT: I found a combination that doesn't cause freezes, too bad it isn't totally correct.
Code: Select all
  <TRIGGERED_ABILITY replacement_effect="1">
    <TRIGGER value="CONSIDERED_FOR_CAST" simple_qualifier="controller" pre_trigger="1">
    return TriggerObject():GetCardType():Test(CARD_TYPE_CREATURE) and TriggerObject():GetSubType():Test(CREATURE_TYPE_ZOMBIE)
    </TRIGGER>
    <RESOLUTION_TIME_ACTION>
    if TriggerObject() ~= nil then
       TriggerObject():GetCurrentCharacteristics():GrantAbility(0)
    end
    </RESOLUTION_TIME_ACTION>
  </TRIGGERED_ABILITY>
  <STATIC_ABILITY>
    <FILTER filter_id="0">
    local filter = ClearFilter()
    filter:SetZone(ZONE_STACK)
    filter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
    filter:Add(FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE)
    filter:Add( FE_CONTROLLER, OP_IS, EffectController() )
    </FILTER>
    <CONTINUOUS_ACTION layer="8" filter_id="0">
    if FilteredCard() ~= nil then
       FilteredCard():GetCurrentCharacteristics():GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate" active_zone="ZONE_ANY">
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
Note that I removed the {0} cost. The problem is that I understood the difference between "You may pay {0} rather than pay the mana cost for ..." and "You may cast ... without paying their mana cost": the difference is Lodestone Golem and similar cards. If I still have to pay {0}, Lodestone Golem will bring my Zombies' alternative cost to {1}. If I could totally avoid paying the mana cost, Lodestone Golem would have no effect.

It doesn't end here: if I keep only the TRIGGERED_ABILITY, removing the {0} cost doesn't solve the problem and the game still freezes. This is becoming frustrating...

EDIT 2: I tried to add this:
Code: Select all
  <TRIGGERED_ABILITY replacement_effect="1">
    <TRIGGER value="SPELL_PLAYED" simple_qualifier="controller">
    return TriggerObject():GetCardType():Test(CARD_TYPE_CREATURE) and TriggerObject():GetSubType():Test(CREATURE_TYPE_ZOMBIE)
    </TRIGGER>
    <CONTINUOUS_ACTION layer="8">
    if TriggerObject() ~= nil then
       TriggerObject():GetCurrentCharacteristics():GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
    <DURATION>
    return TriggerObject() == nil
    </DURATION>
  </TRIGGERED_ABILITY>
to the combination that wasn't causing freezes, and it started to freeze as well. It seems that the problem is granting an ability to something that is changing zone.
< 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: 721 times

Re: How would you code Rooftop Storm?

Postby thefiremind » 15 Jul 2013, 20:26

Answering Persee's post that he decided to delete while I was writing: yeah, I ended up making something really close to your implementation.
One question: doesn't giving the ability to Gravecrawlers in your graveyard make them always playable no matter how many Zombies you control? I added a check that doesn't grant the ability to Gravecrawlers in my graveyard if I don't control any Zombie, but I haven't tried without that check.
< 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: 721 times

Re: How would you code Rooftop Storm?

Postby Persee » 16 Jul 2013, 08:32

I delete my post because my code of Roottop Storm doesn't work with card like Lodestone Golem. :)
I try to find a solution.

However, thank you for your question, I will correct my code.
User avatar
Persee
 
Posts: 168
Joined: 02 Jun 2011, 08:33
Has thanked: 42 times
Been thanked: 24 times

Re: How would you code Rooftop Storm?

Postby thefiremind » 16 Jul 2013, 09:02

Persee wrote:I delete my post because my code of Roottop Storm doesn't work with card like Lodestone Golem. :)
Are you sure? It's something that the game manages automatically: the choice will still say "Pay {0}" because it can't modify the string on runtime, but you'll need to pay {1} if a Lodestone Golem is on the battlefield. Only if you remove
Code: Select all
<COST mana_cost="{0}" type="Mana" />
it won't work with Lodestone Golem.
< 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: 721 times

Re: How would you code Rooftop Storm?

Postby Persee » 16 Jul 2013, 09:13

You're right.
All we need to do is use the code of Omniscience.

Code: Select all
<STATIC_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[You may cast nonland cards from your hand without paying their mana costs.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Vous pouvez lancer des cartes non-terrain depuis votre main sans payer leur coût de mana.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Puedes lanzar cartas que no sean de tierra de tu mano sin pagar sus costes de maná.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Du kannst Karten, die kein Land sind, aus deiner Hand wirken, ohne ihre Manakosten zu bezahlen.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Puoi lanciare carte non terra dalla tua mano senza pagare il loro costo di mana.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[あなたはあなたの手札にある土地でないカードを、それらのマナ・コストを支払うことなく唱えてもよい。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[당신은 손에 있는 대지가 아닌 카드를 마나비용의 지불 없이 발동할 수 있다.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Вы можете разыгрывать не являющиеся землями карты из вашей руки без уплаты их мана-стоимости.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Você pode conjurar cards que não sejam terrenos da sua mão sem pagar seus custos de mana.]]></LOCALISED_TEXT>
    <FILTER filter_id="1">
    local filter = ClearFilter()
    filter:SetZone(ZONE_STACK)
    filter:Add(FE_CONTROLLER, OP_IS, EffectController())
    filter:Add(FE_TYPE, OP_NOT, CARD_TYPE_LAND)
    </FILTER>
    <FILTER filter_id="0">
    local filter = ClearFilter()
    filter:SetZone(ZONE_HAND, EffectController())
    filter:Add(FE_TYPE, OP_NOT, CARD_TYPE_LAND)
    </FILTER>
    <CONTINUOUS_ACTION layer="8" filter_id="0">
    if FilteredCard() ~= nil then
       local characteristics = FilteredCard():GetCurrentCharacteristics()
       characteristics:GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
    <CONTINUOUS_ACTION layer="8" filter_id="1">
    if FilteredCard() ~= nil then
       local characteristics = FilteredCard():GetCurrentCharacteristics()
       characteristics:GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate">
    <ABILITY_TEXT tag="CARD_QUERY_DJINN_OF_WISHED_CAST_CARD_WITH_PYING_ITS_MANA_COST" />
  </UTILITY_ABILITY>
Paying 0 or without pay the mana cost is the same thing.

It's a core card and I think it works with other cards like Thalia, Guardian of Thraben or Aura of Silence

But I can not try right now, I'm at work. :mrgreen:
User avatar
Persee
 
Posts: 168
Joined: 02 Jun 2011, 08:33
Has thanked: 42 times
Been thanked: 24 times

Re: How would you code Rooftop Storm?

Postby thefiremind » 16 Jul 2013, 10:04

I think you understood the exact opposite of what I was trying to say. :lol:

If you REMOVE this:
Code: Select all
<COST mana_cost="{0}" type="Mana" />
THEN IT DOESN'T WORK with cards like Lodestone Golem and Thalia. And yes, there is a difference between paying {0} and not paying any mana cost and that's exactly this, as I explained in the first post.
< 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: 721 times

Re: How would you code Rooftop Storm?

Postby Persee » 16 Jul 2013, 10:20

In the rules, there is no difference between them.

http://www.mtgthesource.com/forums/show ... ax-effects
User avatar
Persee
 
Posts: 168
Joined: 02 Jun 2011, 08:33
Has thanked: 42 times
Been thanked: 24 times

Re: How would you code Rooftop Storm?

Postby thefiremind » 16 Jul 2013, 11:11

Persee wrote:In the rules, there is no difference between them.

http://www.mtgthesource.com/forums/show ... ax-effects
I couldn't believe it so I searched for something else:
http://forums.mtgsalvation.com/showthread.php?t=497676
Here there are some answers from official judges so I'm convinced now. But then I can't understand why they use different wordings for identical things... oh well, I'll keep my Rooftop Storm as it is, since both solutions are correct. :)
< 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: 721 times

Re: How would you code Rooftop Storm?

Postby Persee » 16 Jul 2013, 12:17

I think I'll code it like that.

Code: Select all
 <STATIC_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Vous pouvez payer {0} à la place du coût de mana des sorts de créature Zombie que vous lancez.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Puedes pagar {0} en lugar de pagar el coste de maná de los hechizos de criatura Zombie que lanzas.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Du kannst {0} anstatt der Manakosten für Zombie-Kreaturenzauber bezahlen, die du wirkst.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Puoi pagare {0} invece di pagare il costo di mana per le magie creatura Zombie che lanci.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[あなたは、あなたが唱えるゾンビ・クリーチャー呪文のマナ・コストを支払うのではなく、{0}を支払ってもよい。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Вы можете заплатить {0} вместо оплаты мана-стоимости разыгрываемых вами заклинаний существ-Зомби.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Você pode pagar {0} em vez de pagar o custo de mana das mágicas de criatura do tipo Zumbi que conjura.]]></LOCALISED_TEXT>
    <FILTER filter_id="0">
    local filter = ClearFilter()
    filter:SetZone(ZONE_STACK)
    filter:Add(FE_CONTROLLER, OP_IS, EffectController())
   filter:Add( FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE )
   filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE  )
    </FILTER>   
   <FILTER filter_id="1">
    local filter = ClearFilter()
   filter:Add( FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE )
   filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE  )
   filter:SetZone( ZONE_HAND, EffectController() )
    </FILTER>
    <FILTER filter_id="2">
    local filter = ClearFilter()
   filter:Add( FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE )
   filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE  )
   filter:Add( FE_CARD_NAME, OP_IS, "GRAVECRAWLER" )
   filter:SetZone( ZONE_GRAVEYARD, EffectController() )      
    </FILTER>   
   <CONTINUOUS_ACTION layer="8" filter_id="0">
    if FilteredCard() ~= nil then
       local characteristics = FilteredCard():GetCurrentCharacteristics()
       characteristics:GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
   <CONTINUOUS_ACTION layer="8" filter_id="1">
    if FilteredCard() ~= nil then
       local characteristics = FilteredCard():GetCurrentCharacteristics()
       characteristics:GrantAbility(0)
    end
    </CONTINUOUS_ACTION>
   <CONTINUOUS_ACTION layer="8" filter_id="2">
    if FilteredCard() ~= nil then
       local characteristics = FilteredCard():GetCurrentCharacteristics()
       characteristics:GrantAbility(1)
    end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <UTILITY_ABILITY resource_id="0" qualifier="Alternate">
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Vous pouvez payer {0} à la place du coût de mana des sorts de créature Zombie que vous lancez.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Puedes pagar {0} en lugar de pagar el coste de maná de los hechizos de criatura Zombie que lanzas.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Du kannst {0} anstatt der Manakosten für Zombie-Kreaturenzauber bezahlen, die du wirkst.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Puoi pagare {0} invece di pagare il costo di mana per le magie creatura Zombie che lanci.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[あなたは、あなたが唱えるゾンビ・クリーチャー呪文のマナ・コストを支払うのではなく、{0}を支払ってもよい。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Вы можете заплатить {0} вместо оплаты мана-стоимости разыгрываемых вами заклинаний существ-Зомби.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Você pode pagar {0} em vez de pagar o custo de mana das mágicas de criatura do tipo Zumbi que conjura.]]></LOCALISED_TEXT> 
   <AVAILABILITY>
    return true
    </AVAILABILITY> 
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
   <UTILITY_ABILITY resource_id="1" qualifier="Alternate" active_zone="ZONE_GRAVEYARD">
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Vous pouvez payer {0} à la place du coût de mana des sorts de créature Zombie que vous lancez.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Puedes pagar {0} en lugar de pagar el coste de maná de los hechizos de criatura Zombie que lanzas.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Du kannst {0} anstatt der Manakosten für Zombie-Kreaturenzauber bezahlen, die du wirkst.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Puoi pagare {0} invece di pagare il costo di mana per le magie creatura Zombie che lanci.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[あなたは、あなたが唱えるゾンビ・クリーチャー呪文のマナ・コストを支払うのではなく、{0}を支払ってもよい。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[You may pay {0} rather than pay the mana cost for Zombie creature spells you cast.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Вы можете заплатить {0} вместо оплаты мана-стоимости разыгрываемых вами заклинаний существ-Зомби.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Você pode pagar {0} em vez de pagar o custo de mana das mágicas de criatura do tipo Zumbi que conjura.]]></LOCALISED_TEXT> 
   <AVAILABILITY>
    local filter = ClearFilter()
    filter:Add(FE_CONTROLLER, OP_IS, EffectController() )
    filter:Add(FE_SUBTYPE, OP_IS, CREATURE_TYPE_ZOMBIE )
    if filter:CountStopAt(1) == 1 then
       return true
    else
       return false
    end
    </AVAILABILITY> 
    <ABILITY_TEXT tag="ALTERNATE_COST_PAY_0" />
  </UTILITY_ABILITY>
But I have not tested yet.
And it's not compatible with Yawgmoth's Will. But it would be easy to do.

edit : it's works
User avatar
Persee
 
Posts: 168
Joined: 02 Jun 2011, 08:33
Has thanked: 42 times
Been thanked: 24 times


Return to Programming Talk

Who is online

Users browsing this forum: No registered users and 42 guests


Who is online

In total there are 42 users online :: 0 registered, 0 hidden and 42 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 42 guests

Login Form