It is currently 19 Apr 2024, 06:58
   
Text Size

How to correctly copy */* tokens?

Moderator: CCGHQ Admins

How to correctly copy */* tokens?

Postby thefiremind » 13 Nov 2012, 11:12

I was about to code Slime Molding when I thought about this problem: does anyone have a good idea about how to code */* tokens (and the cards that generate them, of course) so that eventual copies of those tokens still retain their power and toughness? I'm sure that the code I used for Phyrexian Rebirth wouldn't work in that case. I'd also like to avoid adding things to the cards that make the copies, if possible.
< 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 to correctly copy */* tokens?

Postby thefiremind » 29 Jan 2013, 14:54

I worked some more on this problem and made a small step forward, though I still miss the big idea to make everything work as expected.

I made a storage (much like nabeshin's storage in DotP2012... I actually started from his work :lol:) that can be used to save power and toughness of */* tokens. The complexity of the cards generating */* tokens isn't higher than before, and the code for the tokens themselves is modular (I mean you can copy-paste it).
Here is the functions file, and an example, the RTR card Slime Molding:
Functions file | Open
Code: Select all
COMPARTMENT_ID_STAR_TOKEN_INDEX = 2222

STMChest = {}

function StarTokensManager()

   local v = {}

   v.CreateNext = function()
      local i = 0
      while STMChest[i] ~= nil do
         i = i + 1
      end
      local subchest = {}
      subchest[1] = -1
      subchest[2] = -1
      STMChest[i] = subchest
      return i
   end

   v.Set = function(index, power, toughness)
      if STMChest[index] ~= nil then
         STMChest[index][1] = power
         STMChest[index][2] = toughness
      end
   end

   v.GetPower = function(index)
      return STMChest[index] and STMChest[index][1]
   end

   v.GetToughness = function(index)
      return STMChest[index] and STMChest[index][2]
   end

   return v

end
Slime Molding | Open
Code: Select all
<?xml version='1.0'?>
<CARD_V2>
  <FILENAME text="SLIME_MOLDING_290533" />
  <CARDNAME text="SLIME_MOLDING" />
  <TITLE>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Slime Molding]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Plasmare la Melma]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Schleimformung]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Façonnage de boue]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Moldear el cieno]]></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[Moldagem de Limo]]></LOCALISED_TEXT>
  </TITLE>
  <MULTIVERSEID value="290533" />
  <ARTID value="A290533" />
  <ARTIST name="Marco Nelor" />
  <CASTING_COST cost="{X}{G}" />
  <FLAVOURTEXT>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[“Give me enough refuse and mana and I will summon an ooze that can engulf all of Ravnica.”
—Cevraya, Golgari shaman]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[“Dammi abbastanza rifiuti e mana e ti evocherò una melma in grado di ingoiare Ravnica intera.”
—Cevraya, sciamana Golgari]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[„Gib mir nur genug Abfälle und Mana, und ich beschwöre einen Schleim herbei, der ganz Ravnica verschlingen kann.”
—Cevraya, Golgari-Schamane]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[« Donnez-moi suffisamment de déchets et de mana et j’invoquerai un limon capable d’engloutir tout Ravnica. »
—Cevraya, shamane de Golgari]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[“Dame suficientes desechos y maná e invocaré un cieno que sepultará toda Rávnica.”
—Cevraya, chamán golgari]]></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[“Com lixo e mana suficiente, eu posso invocar um lodo capaz de engolir toda Ravnica.” — Cevraya, xamã Golgari]]></LOCALISED_TEXT>
  </FLAVOURTEXT>
  <TYPE metaname="Sorcery" />
  <EXPANSION value="RTR" />
  <RARITY metaname="U" />
  <SPELL_ABILITY>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Put an X/X green Ooze creature token onto the battlefield.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Metti sul campo di battaglia una pedina creatura Melma X/X verde.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Bringe einen X/X grünen Schlammwesen-Kreaturenspielstein ins Spiel.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Mettez sur le champ de bataille un jeton de créature X/X verte Limon.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Pon en el campo de batalla una ficha de criatura Cieno verde X/X.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[緑のX/Xのウーズ・クリーチャー・トークンを1体戦場に出す。]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[X/X 녹색 점액괴물 생물 토큰 한 개를 전장에 놓는다.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Положите на поле битвы одну фишку существа Х/Х зеленая Тина.]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Coloque no campo de batalha uma ficha de criatura verde X/X do tipo Lodo.]]></LOCALISED_TEXT>
    <RESOLUTION_TIME_ACTION>
    local x = GetObjectX()
    local index = StarTokensManager().CreateNext()
    StarTokensManager().Set(index, x, x)
    EffectDC():Set_Int(0, index)
    MTG():PutTokensIntoPlay( "TOKEN_OOZE_S_S_19990127", 1, EffectController(), EffectDC():Make_Chest(1) )
    </RESOLUTION_TIME_ACTION>
    <RESOLUTION_TIME_ACTION>
    local chest = EffectDC():Get_Chest(1)
    if chest ~= nil then
       local n = 0
       while chest:Get_CardPtr(n) ~= nil do
          chest:Get_CardPtr(n):GetDataChest():Set_Int( COMPARTMENT_ID_STAR_TOKEN_INDEX, EffectDC():Get_Int(0) )
          n = n + 1
       end
    end
    </RESOLUTION_TIME_ACTION>
  </SPELL_ABILITY>
  <TOKEN_REGISTRATION reservation="1" type="TOKEN_OOZE_S_S_19990127" />
  <AI_BASE_SCORE score="600" zone="ZONE_HAND" />
</CARD_V2>
Ooze token | Open
Code: Select all
<?xml version='1.0'?>
<CARD_V2>
  <FILENAME text="TOKEN_OOZE_S_S_19990127" />
  <CARDNAME text="OOZE" />
  <TITLE>
    <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[OOZE]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[LIMON]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[CIENO]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[SCHLAMMWESEN]]></LOCALISED_TEXT>
    <LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[MELMA]]></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[LODO]]></LOCALISED_TEXT>
  </TITLE>
  <MULTIVERSEID value="19990127" />
  <ARTID value="A19990127" />
  <COLOUR value="G" />
  <ARTIST name="Marco Nelor" />
  <CASTING_COST cost="" />
  <TYPE metaname="Creature" />
  <SUB_TYPE metaname="Ooze" />
  <EXPANSION value="DPG" />
  <RARITY metaname="T" />
  <POWER value="*" />
  <TOUGHNESS value="*" />
  <TOKEN />
  <STATIC_ABILITY filter_zone="ZONE_IN_PLAY">
    <CONTINUOUS_ACTION layer="7A">
    local index = ObjectDC():Get_Int(COMPARTMENT_ID_STAR_TOKEN_INDEX)
    local stm = StarTokensManager()
    local power = stm.GetPower(index)
    local toughness = stm.GetToughness(index)
    local characteristics = Object():GetCurrentCharacteristics()
    characteristics:Power_Set(power)
    characteristics:Toughness_Set(toughness)
    </CONTINUOUS_ACTION>
  </STATIC_ABILITY>
  <SFX text="COMBAT_BLUNT_LARGE_ATTACK" power_boundary_min="4" power_boundary_max="-1" />
  <SFX text="COMBAT_BLUNT_SMALL_ATTACK" power_boundary_min="1" power_boundary_max="3" />
</CARD_V2>
As you can see, calling StarTokensManager().CreateNext() makes a new space available, automatically incrementing the index and returning it afterwards. This index is used to store power and toughness and to retrieve those values from the token.

This is also compatible with Doubling Season (and similar cards)! The "while" loop passes the index to any number of generated tokens.

With this code, the base power and toughness of every */* token are in a safe place, and passing the index to the created token is easy with GetDataChest()... but the main problem is still unsolved: is there a way to pass the same index to a copy of the token without recoding all the cards that can copy tokens (Clone, Rite of Replication, etc.)? I really can't think of anything. :(
Attachments
SLIME_MOLDING_290533.zip
Slime Molding example, with illustrations, token and functions
(188.23 KiB) Downloaded 259 times
< 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 to correctly copy */* tokens?

Postby pcastellazzi » 30 Jan 2013, 00:27

I would try with something along the lines of:

Code: Select all
-- token generator code
local token = ObtainToken("TOKEN_X_X", EffectController())
token:GetDataChest():SetInt(0, X_VALUE)
token:PutIntoPlay(EffectController())
Code: Select all
-- 0/0 token static ablity
<static_ability>
  local x = ObjectDC():Get_Int(0)
  local c = Object():GetCurrentCharacteristics()
  c:Power_Set(x)
  c:Toughness_Set(x)
</static_ability>
The lights then came up and the crowd erupted in applause, because that's what the crowd does after it watches destruction on a large screen.
— Ben Kuchera, Mordern Warfare 3 review.
User avatar
pcastellazzi
 
Posts: 184
Joined: 25 Apr 2012, 00:40
Location: Montevideo, Uruguay
Has thanked: 11 times
Been thanked: 30 times

Re: How to correctly copy */* tokens?

Postby thefiremind » 30 Jan 2013, 00:35

This is a lot easier than the code I produced, but it still doesn't solve the problem... when you make a copy or clone something, you don't copy the ObjectDC, and as far as I know there's no trigger that can get a pointer to object B when it's trying to copy object A so that object A can send its ObjectDC data to object B.
< 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 to correctly copy */* tokens?

Postby pcastellazzi » 31 Jan 2013, 03:21

I am not sure about ObjectDC not being duplicated, i guess it make sense.

Code: Select all
-- Add this to a lol file
X_TOKEN_CACHE = setmetatable({}, {__mode = "k"})

-- Use this instead of token:GetDataChest():SetInt(0, X_VALUE)
X_TOKEN_CACHE[token] = X_VALUE

-- Use this instead of local x = ObjectDC():Get_Int(0)
local x = X_TOKEN_CACHE[Object()] or 0
The rest should work as expected.
The lights then came up and the crowd erupted in applause, because that's what the crowd does after it watches destruction on a large screen.
— Ben Kuchera, Mordern Warfare 3 review.
User avatar
pcastellazzi
 
Posts: 184
Joined: 25 Apr 2012, 00:40
Location: Montevideo, Uruguay
Has thanked: 11 times
Been thanked: 30 times

Re: How to correctly copy */* tokens?

Postby thefiremind » 31 Jan 2013, 09:52

But that can't solve the problem as well, because X_TOKEN_CACHE[token] indexes through a pointer to the original token. When I copy the original token, X_TOKEN_CACHE[Object()] inside the copy indexes through a pointer to the copy itself, which is different.
< 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 to correctly copy */* tokens?

Postby pcastellazzi » 02 Feb 2013, 03:52

All our solutions where overly complicated. I believe the setters can be used like this without the need to dynamically set P/T with a static ability.

Code: Select all
local token = ObtainToken("TOKEN_X_X", EffectController())
token:GetCurrentCharacteristics():Power_Set(X_VALUE)
token:GetCurrentCharacteristics():Toughness_Set(X_VALUE)
token:PutIntoPlay(EffectController())
The lights then came up and the crowd erupted in applause, because that's what the crowd does after it watches destruction on a large screen.
— Ben Kuchera, Mordern Warfare 3 review.
User avatar
pcastellazzi
 
Posts: 184
Joined: 25 Apr 2012, 00:40
Location: Montevideo, Uruguay
Has thanked: 11 times
Been thanked: 30 times

Re: How to correctly copy */* tokens?

Postby thefiremind » 02 Feb 2013, 10:39

pcastellazzi wrote:All our solutions where overly complicated. I believe the setters can be used like this without the need to dynamically set P/T with a static ability.

Code: Select all
local token = MTG():ObtainToken("TOKEN_X_X", EffectController())
token:GetCurrentCharacteristics():Power_Set(X_VALUE)
token:GetCurrentCharacteristics():Toughness_Set(X_VALUE)
token:PutIntoPlay(EffectController())
:shock: Really??? I'll test it right now!

EDIT: No, it doesn't work. The token can't be even seen. Setting characteristics in a resolution time action probably lasts only until the next time state-based effects are checked.
< 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 to correctly copy */* tokens?

Postby pcastellazzi » 02 Feb 2013, 13:19

Different strategy then, on the spell, use the resolution time action to create a */* token and put it into play, also add a continous action on layer 7a with a duration of "while the token is in play" to set its P/T.
The lights then came up and the crowd erupted in applause, because that's what the crowd does after it watches destruction on a large screen.
— Ben Kuchera, Mordern Warfare 3 review.
User avatar
pcastellazzi
 
Posts: 184
Joined: 25 Apr 2012, 00:40
Location: Montevideo, Uruguay
Has thanked: 11 times
Been thanked: 30 times

Re: How to correctly copy */* tokens?

Postby thefiremind » 02 Feb 2013, 13:58

pcastellazzi wrote:Different strategy then, on the spell, use the resolution time action to create a */* token and put it into play, also add a continous action on layer 7a with a duration of "while the token is in play" to set its P/T.
...which was my first implementation of Phyrexian Rebirth, but you can't Clone such a token: the continuous action is simply ignored by the copy. I know that Phyrexian Processor is coded this way (using layer="0"), but it's a challenge card, so nobody tries to copy it (and I tried to use layer="0" myself, but the Clone is immediately dumped to the graveyard as usual).

EDIT: Do you want to hear something funny? You sure suggested lots of ideas without testing them, but I did something far worse: I classified my functions as not working with Clone without testing them first! And surprise surprise, they work! I think that the ObjectDC gets copied along with the card. Now I just have to test if putting copies on the battlefield (for example with Followed Footsteps) works as good as Clone.

EDIT 2: It works also with Followed Footsteps! :shock: So many doubts, and my functions were the right answer already... #-o Well now I can make an Ooze deck or something like that. :lol:
< 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 to correctly copy */* tokens?

Postby pcastellazzi » 02 Feb 2013, 19:45

thefiremind wrote: I think that the ObjectDC gets copied along with the card.
Then the first solution i gave you should work too. Or i am wrong?
The lights then came up and the crowd erupted in applause, because that's what the crowd does after it watches destruction on a large screen.
— Ben Kuchera, Mordern Warfare 3 review.
User avatar
pcastellazzi
 
Posts: 184
Joined: 25 Apr 2012, 00:40
Location: Montevideo, Uruguay
Has thanked: 11 times
Been thanked: 30 times

Re: How to correctly copy */* tokens?

Postby thefiremind » 02 Feb 2013, 20:16

pcastellazzi wrote:
thefiremind wrote: I think that the ObjectDC gets copied along with the card.
Then the first solution i gave you should work too. Or i am wrong?
Yes, it's the same principle, but I'll keep my functions just in case I have to code a */* token where power and toughness are different (my functions would still need only 1 slot in the ObjectDC).
< 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


Return to Programming Talk

Who is online

Users browsing this forum: No registered users and 23 guests


Who is online

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

Login Form