It is currently 23 May 2024, 08:16
   
Text Size

General DotP 2014 Coding Questions

Moderator: CCGHQ Admins

Re: General DotP 2014 Coding Questions

Postby thefiremind » 13 Oct 2013, 15:59

GrovyleXShinyCelebi wrote:-How would you make an effect that triggers depending on whether you spent mana of a specific colour when paying for it (i.e. Ribbons of Night, which I know was an official card in Duels 2013 so it should be doable)
You worked on sunburst so you should already know it... :roll: :wink:

GrovyleXShinyCelebi wrote:-Why is it that you can't put a "ChooseItems( EffectDC():Make_Targets(1))" and "local target = EffectDC():Get_Targets(1) and EffectDC():Get_Targets(1):Get_CardPtr(i)" in the same RESOLUTION_TIME_ACTION block?
I never wondered why. I guess that the choices made by players aren't ready to be read until the next action begins.
< 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: General DotP 2014 Coding Questions

Postby GrovyleXShinyCelebi » 19 Oct 2013, 16:11

Hey, I have a question.

Do you know what might be wrong with this Dispense Justice code?

Code: Select all
<?xml version='1.0' encoding='UTF-8'?>
<CARD_V2 ExportVersion="1">
  <FILENAME text="DISPENSE_JUSTICE_99209015" />
  <CARDNAME text="DISPENSE_JUSTICE" />
  <TITLE>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Dispense Justice]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Prononcer la  justice]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Dispensar justicia]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Recht sprechen]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Dispensare Giustizia]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[正義の施行]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Dispense Justice]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Вершить Правосудие]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Fazer Justiça]]></LOCALISED_TEXT>
  </TITLE>
  <MULTIVERSEID value="209015" />
  <ARTID value="131072" />
  <ARTIST name="Austin Hsu" />
  <CASTING_COST cost="{2}{W}" />
  <FLAVOURTEXT>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[The Accorders never strike first, but they always strike back.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Les Accordeurs ne frappent jamais les premiers, mais ils ripostent toujours.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Los acordantes nunca golpean primero, pero siempre devuelven el golpe.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Die Harmonier schlagen nie zuerst zu, aber immer zurück.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[I Concordi non colpiscono mai per primi, ma restituiscono sempre il colpo.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[調和者隊は先制攻撃に出ることは無いが、確実に反撃する。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[The Accorders never strike first, but they always strike back.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Согласители никогда не наносят первый удар, но всегда наносят ответный.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Os Concordantes nunca atacam primeiro, mas sempre revidam o ataque.]]></LOCALISED_TEXT>
  </FLAVOURTEXT>
  <TYPE metaname="Instant" />
  <EXPANSION value="SOM" />
  <RARITY metaname="U" />
  <SPELL_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Target player sacrifices an attacking creature.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Le joueur ciblé sacrifie une créature attaquante.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[El jugador objetivo sacrifica una criatura atacante.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Ein Spieler deiner Wahl opfert eine angreifende Kreatur.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Un giocatore bersaglio sacrifica una creatura attaccante.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[プレイヤー1人を対象とする。そのプレイヤーは攻撃クリーチャーを1体生け贄に捧げる。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Target player sacrifices an attacking creature.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Целевой игрок приносит в жертву атакующее существо.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[O jogador alvo sacrifica uma criatura atacante.]]></LOCALISED_TEXT>
    <TARGET tag="CARD_QUERY_CHOOSE_PLAYER_TO_SACRIFICE_CREATURE" definition="0" compartment="0" count="1" />
    <TARGET_DEFINITION id="0">
    local filter = ClearFilter()
    filter:SetFilterType( FILTER_TYPE_PLAYERS )
    filter:Add( FE_LUA_CONDITION, 4, EffectController(), EffectDC() )
    </TARGET_DEFINITION>
    <FILTER_CONDITION id="4">
    local player = FilteredPlayer()
    if player ~= nil then
   local filter = ClearFilter()
   filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE )
        filter:Add( FE_IS_ATTACKING, true )
   filter:Add( FE_CONTROLLER, OP_IS, player )
   local count = filter:Count()
        if count &gt; 0 then
          return true
       else
          return false
       end
    else
       return false
    end
    </FILTER_CONDITION>
    <RESOLUTION_TIME_ACTION>
    local filter = ClearFilter()
    filter:Add( FE_TYPE, OP_IS, CARD_TYPE_ARTIFACT )
    filter:Add( FE_CONTROLLER, OP_IS, EffectController() )
    local count = filter:Count()
    if count ~= nil then
   EffectDC():Set_Int(5, count)
    end
    </RESOLUTION_TIME_ACTION>
    <RESOLUTION_TIME_ACTION>
    local target = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
    if target ~= nil then
      local metalcraft = EffectDC():Get_Int(5)
      if metalcraft &gt; 2 then
        local filter = ClearFilter()
         filter:Add( FE_CONTROLLER, OP_IS, target )
         filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE )
            filter:Add( FE_IS_ATTACKING, true )
        target:SetItemCount( 2 )
        for i=0, (1) do
      target:SetItemPrompt( i, "CARD_QUERY_CHOOSE_CREATURE_TO_SACRIFICE" )
        end
        target:ChooseItems( EffectDC():Make_Targets(2) )
      else
        local filter = ClearFilter()
         filter:Add( FE_CONTROLLER, OP_IS, target )
         filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE )
            filter:Add( FE_IS_ATTACKING, true )
         target:ChooseItem( "CARD_QUERY_CHOOSE_CREATURE_TO_SACRIFICE", EffectDC():Make_Targets(1) )
       end
    end
    </RESOLUTION_TIME_ACTION>
    <RESOLUTION_TIME_ACTION>
    local target = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
    if target ~= nil then
      local creature = EffectDC():Get_Targets(1) and EffectDC():Get_Targets(1):Get_CardPtr(0)
      if creature ~= nil then
         target:Sacrifice( creature )
      end
    end
    </RESOLUTION_TIME_ACTION>
    <RESOLUTION_TIME_ACTION>
    local target = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
    if target ~= nil then
      local objectivo = EffectDC():Get_Targets(2)
      if objectivo ~= nil then
     for i=0, (1) do
      local creature = objectivo:Get_CardPtr(i)
      if creature ~= nil then
               target:Sacrifice( creature )
      end
     end
      end
    end
    </RESOLUTION_TIME_ACTION>
    <AI_SIMPLIFIED_TARGETING compartment="0" hint="HINT_ENEMY_ONLY" />
  </SPELL_ABILITY>
  <SPELL_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Metalcraft — That player sacrifices two attacking creatures instead if you control three or more artifacts.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Art des métaux — Ce joueur sacrifie deux créatures attaquantes à la place si vous contrôlez au moins trois artefacts.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Metalurgia — Ese jugador sacrifica dos criaturas atacantes en vez de eso si tú controlas tres o más artefactos.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Metallkunst — Falls du drei oder mehr Artefakte kontrollierst, opfert dieser Spieler stattdessen zwei angreifende Kreaturen.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Metallurgia — Quel giocatore sacrifica invece due creature attaccanti se controlli tre o più artefatti.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[金属術 ― あなたが3つ以上のアーティファクトをコントロールしている場合、代わりにそのプレイヤーは攻撃クリーチャーを2体生け贄に捧げる。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Metalcraft — That player sacrifices two attacking creatures instead if you control three or more artifacts.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Работа по металлу — Тот игрок приносит в жертву два атакующих существа вместо этого, если вы контролируете не менее трех артефактов.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Maestria com Metais — Se você controlar três ou mais artefatos, em vez disso, aquele jogador sacrificará duas criaturas atacantes.]]></LOCALISED_TEXT>
  </SPELL_ABILITY>
  <AI_AVAILABILITY type="in_response" response_source="1" />
  <AI_AVAILABILITY type="in_response" response_target="1" />
  <AI_AVAILABILITY window_step="declare_attackers" window_turn="their_turn" type="window" />
  <AI_AVAILABILITY window_step="declare_blockers" type="window" />
</CARD_V2>
For some reason, it lets me target myself, but not any opponent! Is there something special about FE_LUA_CONDITION and FILTER_CONDITION that's causing this?
User avatar
GrovyleXShinyCelebi
 
Posts: 294
Joined: 12 Jun 2013, 18:23
Has thanked: 14 times
Been thanked: 37 times

Re: General DotP 2014 Coding Questions

Postby thefiremind » 19 Oct 2013, 16:23

I can't see anything wrong and there's probably something I don't know or I'm missing, but the whole problem shouldn't exist: the card doesn't say "Target player that controls an attacking creature sacrifices an attacking creature"... I mean, you should be allowed to target a player that isn't attacking or even a player that controls no creatures at all. If that's the case, the spell will resolve doing nothing.
< 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: General DotP 2014 Coding Questions

Postby GrovyleXShinyCelebi » 19 Oct 2013, 17:57

thefiremind wrote:I can't see anything wrong and there's probably something I don't know or I'm missing, but the whole problem shouldn't exist: the card doesn't say "Target player that controls an attacking creature sacrifices an attacking creature"... I mean, you should be allowed to target a player that isn't attacking or even a player that controls no creatures at all. If that's the case, the spell will resolve doing nothing.
The reason I put that filter condition was because I'm worried the AI will cast the spell arbitrarily and it'll just fizzle. I'm not exactly sure how smart the AI is at detecting "ChooseItems()" functions in RESOLUTION_TIME_ACTION. Though I guess I'll just have to take out the filter condition for now.

By the way, know how AI_AVAILABILITY blocks work? And do they work on anything that's not an instant or can be activated/cast at instant speed?
User avatar
GrovyleXShinyCelebi
 
Posts: 294
Joined: 12 Jun 2013, 18:23
Has thanked: 14 times
Been thanked: 37 times

Re: General DotP 2014 Coding Questions

Postby thefiremind » 19 Oct 2013, 21:35

GrovyleXShinyCelebi wrote:The reason I put that filter condition was because I'm worried the AI will cast the spell arbitrarily and it'll just fizzle. I'm not exactly sure how smart the AI is at detecting "ChooseItems()" functions in RESOLUTION_TIME_ACTION.
In previous games I would have doubted, too, but DotP2014's AI is the best so far. Not to mention that the card would be totally wrong in some scenarios if you limit the legal targets more than necessary (you should be able to Swerve Dispense Justice to your opponent, for example, basically turning Swerve into a counterspell).

GrovyleXShinyCelebi wrote:By the way, know how AI_AVAILABILITY blocks work? And do they work on anything that's not an instant or can be activated/cast at instant speed?
Trying to be short but exhaustive: if the AI is in a situation that is not defined in any of the AI_AVAILABILITY blocks of a spell/ability, it won't even think about casting/activating that spell/ability during that situation. I'm still not sure about the real meaning of response_source and response_target, what I think is that response_source means "you can respond this way if someone tries to do something on the ability's source" and response_target means "you can respond this way targetting card X if someone tries to do something on card X", but that's just my guess about them.
You can put AI_AVAILABILITY anywhere you like and it might be useful even inside non-instant spells or abilities: for example, the official cards don't do that, but Act of Treason and similar spells could be limited to main phase 1 since you get the full effect only if you cast it pre-combat... well, maybe official cards don't do that because if you cast Relentless Assault you end up having a pre-combat main phase 2, but I hope you get the point despite the poor example. :P
< 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: General DotP 2014 Coding Questions

Postby GrovyleXShinyCelebi » 20 Oct 2013, 03:47

Alright, I get it. It's just that I added AI_AVALIABILITY blocks in my Bloodthirst cards telling the AI only to cast them in main phase 2. The AI still casts them in main phase 1 :/ Know what might be happening?

Also, is the AI different in encounters than duels? My logic says no because the developers had to make special cards in the challenges just to get the AI to behave a certain way (ie Grave Titan forces all opponents to block if possible), but it seems the AI is smarter in encounters (in the Gruul encounter for instance the AI actually knows how to utilize Bloodthirst).
User avatar
GrovyleXShinyCelebi
 
Posts: 294
Joined: 12 Jun 2013, 18:23
Has thanked: 14 times
Been thanked: 37 times

Re: General DotP 2014 Coding Questions

Postby thefiremind » 20 Oct 2013, 08:38

GrovyleXShinyCelebi wrote:Alright, I get it. It's just that I added AI_AVALIABILITY blocks in my Bloodthirst cards telling the AI only to cast them in main phase 2. The AI still casts them in main phase 1 :/ Know what might be happening?
Where did you put the AI_AVAILABILITY block? Since you are interested in the spell itself, it should be outside any ability, like on instants. If you did that right, then maybe I was wrong and AI_AVAILABILITY is ignored on creatures without flash. But since the AI is capable of ignoring the simplified targetting on beneficial spells if the situation requires to cast them on opponents' cards, maybe it's the same for AI_AVAILABILITY.

GrovyleXShinyCelebi wrote:Also, is the AI different in encounters than duels? My logic says no because the developers had to make special cards in the challenges just to get the AI to behave a certain way (ie Grave Titan forces all opponents to block if possible), but it seems the AI is smarter in encounters (in the Gruul encounter for instance the AI actually knows how to utilize Bloodthirst).
The restraints on challenge cards are meant to be sure that the AI always does the same things no matter what you do. The AI isn't smarter in encounters (as far as I know), but the situation is always optimal since the deck order is always the same: if I have a Goblin Fireslinger I obviously activate bloodthirst whenever I can, but if I don't, I might need to cast a creature without bothering about bloodthirst just to protect myself, and I think that the AI decides the same way. Try to make a custom deck with lots of "pingers" (Goblin Fireslinger, Prodigal Pyromancer, etc.) and lots of bloodthirst creatures, and see if the AI plays properly.
< 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: General DotP 2014 Coding Questions

Postby Jspapp » 22 Oct 2013, 21:41

How long will LinkedDC():Protect_CardPtr(0) keep track of a card? It seems to lose the pointer if the card changes zone through any means other than the effect source.
Jspapp
 
Posts: 8
Joined: 03 Jul 2012, 00:17
Has thanked: 2 times
Been thanked: 0 time

Re: General DotP 2014 Coding Questions

Postby thefiremind » 22 Oct 2013, 22:36

Jspapp wrote:How long will LinkedDC():Protect_CardPtr(0) keep track of a card? It seems to lose the pointer if the card changes zone through any means other than the effect source.
In general, protecting a card pointer saves it for 1 zone change, but I'm not sure if I understood your scenario... let's make an example: inside the LinkedDC of card A, you save a pointer to card B and protect it. Then:
  • If card B changes zone but card A doesn't, the pointer will be saved.
  • If both cards change zone, LinkedDC will be reset because of card A changing zone, so the pointer won't get saved, but it's not due to the protection not working.
Depending on what you need to do, you might need to store your data into a player's data chest or the duel's data chest.
< 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: General DotP 2014 Coding Questions

Postby Jspapp » 22 Oct 2013, 23:04

thefiremind wrote:
Jspapp wrote:How long will LinkedDC():Protect_CardPtr(0) keep track of a card? It seems to lose the pointer if the card changes zone through any means other than the effect source.
In general, protecting a card pointer saves it for 1 zone change, but I'm not sure if I understood your scenario... let's make an example: inside the LinkedDC of card A, you save a pointer to card B and protect it. Then:
  • If card B changes zone but card A doesn't, the pointer will be saved.
  • If both cards change zone, LinkedDC will be reset because of card A changing zone, so the pointer won't get saved, but it's not due to the protection not working.
Depending on what you need to do, you might need to store your data into a player's data chest or the duel's data chest.
I'm working on an implementation of EDH and want to be able to track the commander wherever it goes. I'm trying to keep all of the logic on one card, but I think I can do the same thing by granting abilities, unless there's any way to keep a more permanent card pointer reference.

EDIT: Actually, I'm using this for now as a workaround since Protect_CardPtr() only protects through one zonechange:
Code: Select all
<TRIGGERED_ABILITY internal="1" linked_ability_group="1">
  <TRIGGER value="ZONECHANGE_END">
  local commander = LinkedDC():Get_CardPtr(0)
  return TriggerObject() == commander
  </TRIGGER>
  <RESOLUTION_TIME_ACTION>
  LinkedDC():Protect_CardPtr(0)
  </RESOLUTION_TIME_ACTION>
  <AUTO_SKIP always="1" />
</TRIGGERED_ABILITY>
Jspapp
 
Posts: 8
Joined: 03 Jul 2012, 00:17
Has thanked: 2 times
Been thanked: 0 time

Re: General DotP 2014 Coding Questions

Postby thefiremind » 23 Oct 2013, 08:51

Jspapp wrote:
Code: Select all
<TRIGGERED_ABILITY internal="1" linked_ability_group="1">
  <TRIGGER value="ZONECHANGE_END">
  local commander = LinkedDC():Get_CardPtr(0)
  return TriggerObject() == commander
  </TRIGGER>
  <RESOLUTION_TIME_ACTION>
  LinkedDC():Protect_CardPtr(0)
  </RESOLUTION_TIME_ACTION>
  <AUTO_SKIP always="1" />
</TRIGGERED_ABILITY>
The internal tag is DotP2013 syntax, you want replacement_effect instead of internal, which will make the AUTO_SKIP block unneeded.
Also, isn't it too late if you protect the card pointer at the end of the zone change? I would make the trigger like this:
Code: Select all
<TRIGGERED_ABILITY replacement_effect="1" linked_ability_group="1">
  <TRIGGER value="ZONECHANGE_BEGIN">
  local commander = LinkedDC():Get_CardPtr(0)
  if TriggerObject() == commander then
      LinkedDC():Protect_CardPtr(0)
  end
  return false
  </TRIGGER>
</TRIGGERED_ABILITY>
What you do inside the trigger condition of a ZONECHANGE_BEGIN trigger happens before the zone change. Once you protected the pointer, you don't need the trigger to actually fire, so you can always return false.
< 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: General DotP 2014 Coding Questions

Postby GrovyleXShinyCelebi » 25 Oct 2013, 19:51

Hey, I have a question.

What arguments does MTG():SetTargetAnswerer() need? Because I just set this as my TARGET script for Pandemonium and when an opponent drops a creature onto the battlefield it refuses to allow them to target!

Code: Select all
   <TARGET tag="CARD_QUERY_CHOOSE_DEAL_CREATURES_POWER_DAMAGE" definition="0" compartment="0" count="1" >
     if TriggerObject() ~= nil then   
        MTG():SetTargetAnswerer( TriggerObject():GetController() )
       return
    end
    </TARGET>
User avatar
GrovyleXShinyCelebi
 
Posts: 294
Joined: 12 Jun 2013, 18:23
Has thanked: 14 times
Been thanked: 37 times

Re: General DotP 2014 Coding Questions

Postby thefiremind » 25 Oct 2013, 21:26

GrovyleXShinyCelebi wrote:What arguments does MTG():SetTargetAnswerer() need? Because I just set this as my TARGET script for Pandemonium and when an opponent drops a creature onto the battlefield it refuses to allow them to target!
By looking at Evangelize, I'd say it needs nothing but the player. Try to remove everything except the SetTargetAnswerer line.
< 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: General DotP 2014 Coding Questions

Postby MC Brodie » 26 Oct 2013, 03:07

Well I got the absurd idea of trying to make somewhat working version of Momir Basic. I know we can't create copies of cards not in the specific deck playing in the duel. But I decided to make a deck with 40 lands and 59 different creatures (1 for the Momir Vanguard), exile all the creatures in the beginning, then use the exile zone as the creature pool for the Momir Vanguard. Low and behold, it (sort of) worked!!!! I had to make some limitations like no creatures with a cmc < 3 and if you pay 8 or more for Momir, you get any available creature with a cmc >= 8.

Even though I came up with a working version, I have a few questions but mostly on how to improve the AI. I've noticed a couple of things while testing.
  • I've been using a 100 card deck in my tests so far and haven't had a problem. Is there a way to increase the deck size? That would be nice because it would increase the potential card pool. Though, I know in the unmodded version of the game you are limited to 100 cards.
  • The AI seems finicky on how it uses Momir Vig. Sometimes it will play a land then activate, then other times it will activate then play a land. In my initial testing I noticed this but I had 4 different cards in my pool (1 cmc, 2 cmc, 3 cmc and 4 cmc). I figured the AI was smart and knew not to activate for 4 mana because the 3 cmc creature would save it and the 4 cmc creature was basically a dud. Though when I tested the deck I attached below, I got mixed results. Is the AI smart enough to know which cmc it should pay for the Momir activation? I can probably figure this out wiht some more testing myself but I'd like to get someone else's opinion.
  • This is sort of a dull question but I'm just looking for improvement. A 2-player game went by somewhat smoothly. The AI didn't take too long to make decisions and for the most part knew when to activate Momir (i.e., didn't activate for X=1 or X=2). Though, when I tested it in a 4-player game, the AI seemed to awhile to make decisions. I'm not sure if that is just characteristic of a 4-player game or if that is due to my lakc of coding knowledge. So now to my question :D. Is there a way I can improve my Momir code so that the AI can handle it more smoothly?
  • I'm struggling with how to implement a Vanguard card. This version just uses an "Enchantment" modeled after the TFMs Leyline of Deity. I was thinking of attempting some code that would have an invisible token grant the code to the first basic land that comes into play. Then if that basic land is destroyed or somehow loses its abilities, the invisible token would grant the Momir ability to a different land. So, any thoughts on the best way I could implement this?
  • The more appropriate way to implement Momir Basic is probably to convert every creature card to a token, then list about 7000 token registrations onto Momir (of course I could be overlooking somethign that would make this a worse idea than what it already is). Though that seems like a real pain, it doesn't look like there is much of a difference between a creature XML and a token XML. From a simple person (i.e. me), it would be possible to write a program that would take a creature XML file then change the text in the <FILENAME../> and <MULTIVERSEID../> blocks and add <TOKEN/> to the code. If that is possible, any help to point me in the right direction would be appreciated. Even if it isn't the best idea, it would be a good learning experience for someone like me whose only real programming experience is DotP.

For the likes like me that don't want to download a file to read the cards, here is what I came up with for a beta version of the Momir Vig, Simic Visionary Avatar:
Momir Vanguard | Open
Code: Select all
<?xml version='1.0' encoding='UTF-8'?>
<CARD_V2 ExportVersion="1">
  <FILENAME text="MOMIR_VIG_SIMIC_VISIONARY_AVATAR_238182271" />
  <CARDNAME text="MOMIR_VIG_SIMIC_VISIONARY_AVATAR" />
  <TITLE>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Momir Vig, Simic Visionary Avatar]]></LOCALISED_TEXT>
  </TITLE>
  <MULTIVERSEID value="238182271" />
  <ARTID value="238182271" />
  <ARTIST name="UDON" />
  <CASTING_COST cost="" />
  <TYPE metaname="Enchantment" />
  <EXPANSION value="VAN" />
  <RARITY metaname="S" />
  <STATIC_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Hand Modifier: +0 , Life Modifier: +4]]></LOCALISED_TEXT>
  </STATIC_ABILITY>
  <ACTIVATED_ABILITY resource_id="0">
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[{X}, Discard a card: Put a token onto the battlefield that’s a copy of a creature card with converted mana cost X chosen at random. Activate this ability only any time you could cast a sorcery and only once each turn.]]></LOCALISED_TEXT>
   <AVAILABILITY sorcery_time="1" per_turn_limit="1" />
   <COST mana_cost="{X}" type="Mana" />
   <COST type="Discard" definition="0" compartment="1" query_tag="CARD_QUERY_CHOOSE_CARD_TO_DISCARD" item_count="1" />
   <COST_DEFINITION id="0">
      local filter = ClearFilter()
      filter:SetZone( ZONE_HAND, EffectController() )
    </COST_DEFINITION>
   <RESOLUTION_TIME_ACTION>
      local cmc = GetEffectX()
       local filter = ClearFilter()
       filter:SetZone(ZONE_EXILE)
       filter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
      if cmc &lt; 8 then
         filter:Add(FE_CMC, OP_IS, cmc )
      else
         filter:Add(FE_CMC, OP_GREATER_THAN_OR_EQUAL_TO, 8 )
      end
       filter:EvaluateObjects()
       local oCopy = filter:GetRandomEvaluatedObject()
       if oCopy ~= nil then
          MTG():PutTokenCopiesOntoBattlefield( oCopy, 1, EffectController() )
       end
    </RESOLUTION_TIME_ACTION>
   <AI_AVAILABILITY window_step="main_1" window_turn="my_turn" type="window" />
   <AI_AVAILABILITY window_step="main_2" window_turn="my_turn" type="window" />
  </ACTIVATED_ABILITY>
  <TRIGGERED_ABILITY active_zone="ZONE_ANY">
    <TRIGGER value="BEGINNING_OF_STEP">
      return MTG():GetStep() == STEP_UPKEEP and MTG():GetTurnNumber() == 0
    </TRIGGER>
   <FILTER filter_id="1">
      local filter = ClearFilter()
      filter:SetZone( ZONE_ANYWHERE, EffectController() )
      filter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE )
   </FILTER>
    <RESOLUTION_TIME_ACTION>
      if EffectSource() ~= nil then
         EffectSource():PutOntoBattlefield( EffectController() )
      end
    </RESOLUTION_TIME_ACTION>
   <RESOLUTION_TIME_ACTION filter_id="1">
      if FilteredCard() ~= nil then
         FilteredCard():Exile()
      end
    </RESOLUTION_TIME_ACTION>
   <RESOLUTION_TIME_ACTION>
      local player = EffectController()
      if player ~= nil then
         local to_draw = 7 - player:Hand_Count()
         if to_draw &gt; 0 then
            player:DrawCards(to_draw)
         end
         player:GainLife(4)
      end
    </RESOLUTION_TIME_ACTION>
    <AUTO_SKIP always="1" />
  </TRIGGERED_ABILITY>
  <STATIC_ABILITY>
    <INTRINSIC characteristic="CHARACTERISTIC_SHROUD" />
    <INTRINSIC characteristic="CHARACTERISTIC_INDESTRUCTIBLE" />
    <CONTINUOUS_ACTION layer="6">
      if EffectSource() ~= nil then
         for i=0,1-1 do
            EffectSource():GetCurrentCharacteristics():GrantAbility(i)
         end
      end
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <TRIGGERED_ABILITY>
    <TRIGGER value="ZONECHANGE_BEGIN" simple_qualifier="self" to_zone="ZONE_GRAVEYARD" from_zone="ZONE_BATTLEFIELD" />
    <RESOLUTION_TIME_ACTION>
      if TriggerObject() ~= nil then
         TriggerObject():PutOntoBattlefield( TriggerObject():GetOwner() )
      end
    </RESOLUTION_TIME_ACTION>
  </TRIGGERED_ABILITY>
  <HELP title="MORE_INFO_MOMIR_BASIC_LIMITED_TITLE" body="MORE_INFO_MOMIR_BASIC_LIMITED_BODY" zone="ZONE_ANY" />
  <HELP title="MORE_INFO_VANGUARD_TITLE" body="MORE_INFO_VANGUARD_BODY" zone="ZONE_ANY" />
  <AI_BASE_SCORE score="-5000" zone="ZONE_BATTLEFIELD" />
</CARD_V2>
In the end, even though it is a very rough approximation of the real Momir Basic, it is still fun to play. The AI can even beat you if you don't read the cards you put into your own deck :lol: .
Attachments
Magic 2014 - MOMIR.rar
(397.38 KiB) Downloaded 324 times
-----------------------------------------------------------------------
Song of the Day: 46 and 2 (cover)
MC Brodie
 
Posts: 310
Joined: 01 Jun 2013, 00:10
Has thanked: 44 times
Been thanked: 34 times

Re: General DotP 2014 Coding Questions

Postby RiiakShiNal » 26 Oct 2013, 15:12

MC Brodie wrote:The more appropriate way to implement Momir Basic is probably to convert every creature card to a token, then list about 7000 token registrations onto Momir (of course I could be overlooking somethign that would make this a worse idea than what it already is). Though that seems like a real pain, it doesn't look like there is much of a difference between a creature XML and a token XML. From a simple person (i.e. me), it would be possible to write a program that would take a creature XML file then change the text in the <FILENAME../> and <MULTIVERSEID../> blocks and add <TOKEN/> to the code. If that is possible, any help to point me in the right direction would be appreciated. Even if it isn't the best idea, it would be a good learning experience for someone like me whose only real programming experience is DotP.
You actually don't have to convert cards to tokens as it is possible to put a card that is not a token onto the battlefield as a token. I do this with my Manual Mana. My Mana tokens do not have the <TOKEN/> block and I am still able to put them onto the battlefield using MTG():PutTokensOntoBattlefield().

Though if you actually want to write a program to read in a card XML and make changes like that then it is quite possible; how exactly you go about it will depend on which language you are going to write it in. For example in C# you can read in a card's XML by simply using this:
Code: Select all
using System;
using System.Text;
using System.Xml;

public XmlDocument LoadCard(string strFilename)
{
   XmlDocument xdCard = new XmlDocument();
   xdDoc.Load(strFilename);
   return xdCard;
}
Once you locate the CARD_V2 block in the XML you can very simply add the TOKEN block by appending a new "TOKEN" element similar to this:
Code: Select all
public void AddTokenBlock(XmlDocument xdCard, XmlNode xnCard)
{
   xnCard.AppendChild(xdCard.CreateElement("TOKEN"));
}
An easy way to find nodes in a Case Sensitive fashion is to use the default accessor of the XML object for example you can get the CARD_V2 node by doing:
Code: Select all
public XmlNode GetCardV2Node(XmlDocument xdCard)
{
   return xdCard["CARD_V2"];
}
Though it should be noted that this only works in a Case Sensitive fashion so it will find <CARD_V2>, but it won't find <Card_V2>, <CARD_v2>, etc.... Not all modders have used all uppercase XML tags in their cards (I ran into that problem when coding my Deck Builder).

The attributes of the FILENAME and MULTIVERSEID blocks can also be changed quite simply by first finding those nodes, then locating the attribute to change on those node (should be very easy because they each only have one attribute) and then just assign the new value.

Once you have finished making changes then you can simply save out the modified XML using:
Code: Select all
public void SaveCard(XmlDocument xdCard, string strFilename)
{
   xdCard.Save(strFilename);
}
I put all examples in functions so you can see what types the variables are how they are used.

For more detailed examples on how to parse the Card XML you can look at the source code of my Deck Builder (all card information is parsed in CardInfo.cs, though some of the actual XML parsing is in XmlTools.cs). Though in the Deck Builder I am parsing out the information to then easily display and use rather than to change and re-output.
RiiakShiNal
Programmer
 
Posts: 2185
Joined: 16 May 2011, 21:37
Has thanked: 75 times
Been thanked: 497 times

PreviousNext

Return to Programming Talk

Who is online

Users browsing this forum: No registered users and 8 guests


Who is online

In total there are 8 users online :: 0 registered, 0 hidden and 8 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 8 guests

Login Form