Log in

DotP 2014: Coding Cards

Coding cards is a really broad topic, but it does generally fall into several smaller categories.


Contents

Structure of a card

Cards are made up of several parts. The structure of a very simple card is below.

Field Creeper

That is the complete code for the card Field Creeper. It has no abilities, but it does have flavor text. All of the tags included, with the exception of <FLAVOURTEXT>, will appear on all creature cards. If it was not a creature, then it would be the same as the above, except it would not have the following tags.

<TYPE metaname="Creature" />
<POWER value="2" />
<TOUGHNESS value="1" />

You'll notice in Field Creeper that there's one parent tag named CARD_V2 which contains all of the other tags. Some tags, such as TITLE and FLAVOURTEXT contain even more tags. All cards have this same basic setup.

The ordering of the tags shown is always the same. From TOUGHNESS and up, they always appear in that order. From SFX down, they always appear in that order at the end of the card. Though tags might be missing, you'll likely never find cards in a different order. However, the ordering of tags is generally not strict. They can be reordered, but they're kept in this order to maintain ease of reading. The AUTHOR, EDITORS, and DATE tags should always be the last tags inside CARD_V2. FILENAME and CARDNAME should always be the first.

Tags

So, at this point, I'll explain exactly what a tag is. A tag refers to an XML element between angle brackets <>. Tags come in two forms.

<SOMETAG >...</SOMETAG>
and
<SOMETAG />

Every tag that begins somewhere must be closed somewhere. Both of the examples above are opened and closed. The first example is the normal tag setup, and I usually refer to it as a block. The second version works the same as the first, except it can't contain other tags, and I usually refer to it as a one-line tag (technically an empty element tag). In XML, you can have tag trees.

<CARD>
 <NAME />
 <TYPE />
</CARD>

In the above example, the tag <CARD> contains the <NAME> and <TYPE> tags. <CARD> opens the CARD tag, while </CARD> closes it. the NAME and TYPE tags are closed automatically thanks to their ending "/>". This is the difference between the two types of tags. Each tag is opened with "<TAG_NAME". It is then closed later with either " />" at the end of the opening tag, or with "</TAG_NAME>" elsewhere.

Tags can also have attributes. Everything in a tag, from the tag's name to each attribute, is separated by spaces. The beginning of a tag, the section before the first space (if any), is the name. It is written on its own, meaning without ="" following it. Attributes, however, have values. For instance, in the type tag, it has the attribute "metavalue". And "metavalue" is an attribute whose valuetype is a string. In the Field Creeper example, you'll see:

<TYPE metavalue="Artifact" />

This begins the "TYPE" tag, adds the attribute "metavalue" with a value of "Artifact", and then closes the tag. Many attributes don't take strings, but rather numbers, such as in the "POWER" and "TOUGHNESS" tags (which are technically strings, because they accept * and a few other things, but are almost universally numbers).

Sometimes, however, it uses a 1 in place "true", like a boolean. It simply means "this attribute applies". Attributes MUST have values. You can't declare an attribute without a value. For example, consider the following.

<RESOLUTION_TIME_ACTION repeating_action="1">
...
</RESOLUTION_TIME_ACTION>

In the above example, there's a resolution time action (which will be explained later). Normally, the ellipsis would be replaced with Lua code which will run one time when the ability this is in resolves off the stack. However, because it has the "repeating_action" attribute, it can run multiple times as needed. The "1" in this case is less of a number and more of a boolean value simply meaning true. It doesn't mean to repeat the code 1 time. It just means to repeat the code. Even if the code needs to run twenty times, it will still only have a 1 there. (The number of repetitions is handled in the Lua code.)

There are many tags cards can have. For help with tags, you can have a look at this spreadsheet. It's not exhaustive, and it will require much editing over time as other possibilities are remembered or discovered, but it should prove to be a useful resource. Its best use is likely to be the list of possible attributes different tags accept.

In this guide, attributes are often preceded by a type identifier. const - Uses constant as its value, such as TRIGGER_DESTROYED. (Trigger values are special, though.) text - Uses a text permanent identifier as its value, such as CARD_QUERY_CHOOSE_CARD_TO_DISCARD. string - Uses a string of text as its value. These are special cases. The most common is "simple_qualifier" for triggers. Values like 'simple_qualifier="controller"'. mana - Uses a mana string as its value, such as "{1}{U}". int - Uses a number as its value. bool - Uses 1 as its value. (Presumably also 0, but that's untested and never needed.)

More Tag Info

A few tags need some more in-depth information. Specifically, CDATA, TRIGGER, and COST.

CDATA

In the spreadsheet linked above, you may notice a few tags have the child tag "CDATA". "CDATA" is not actually a tag, but rather a special instruction to treat everything inside the inner brackets as literal text (which the game engine will parse for italics and symbols such as mana and tap). CDATA stands for CharacterData, and it's always set up the same way.

<![CDATA[This is literal text, such as ability text to display on the card or its name.]]>

CDATA sections are always nested inside of a subtag within the <CARD> tag. By that, I mean these are all possible:

<CARD>
 <TITLE>
  <LOCALISED_TEXT><![CDATA[The card's name]]></LOCALISED_TEXT>
 </TITLE>
</CARD>

and

<CARD>
 <FLAVOURTEXT>
  <LOCALISED_TEXT><![CDATA[The card's name]]></LOCALISED_TEXT>
 </FLAVOURTEXT>
</CARD>

and

<CARD>
 <AUTHOR><![CDATA[The card's name]]></AUTHOR>
</CARD>

However, the following will never occur:

<CARD>
 <![CDATA[The card's name]]>
</CARD>

TRIGGER

TRIGGER tags only exist inside TRIGGERED_ABILITY blocks. They specify under what conditions the triggered ability's actions should run. TRIGGER tags always have the const attribute "value", which can be any trigger found in the Decompilable LOL Contents page. The triggers there always start with "TRIGGER_". However, when used in the "value" attribute of a TRIGGER tag, that prefix is removed. Whenever the game fires the trigger associated with a TRIGGER tag, the tag's attributes are checked. If those attributes all evaluate to true, then the tag's Lua code is run if there is any. If the code returns true, then the ability is put onto the stack.

Here's an example:

<TRIGGERED_ABILITY>
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[Whenever you sacrifice a creature, draw a card.]]></LOCALIZED_TEXT>
 <TRIGGER value="SACRIFICE" simple_qualifier="controller">
  return TriggerObject():GetCardType():Test(CARD_TYPE_CREATURE)
 </TRIGGER>
 <RESOLUTION_TIME_ACTION>
  EffectController():DrawCards(1)
 </RESOLUTIONT_TIME_ACTION>
</TRIGGERED_ABILITY>

In the code above, the TRIGGER's "SACRIFICE" value means this trigger will only be checked when something is sacrificed. When anything is sacrificed, even if it was an opponent who sacrificed something or it was a land, the trigger is checked. If the "simple_qualifier" string attribute is true (so the person who sacrificed something was you, the person who controls this card), then and only then will the code inside the TRIGGER block run. When it runs, it checks if the object that was sacrificed was a creature and returns that answer. If it returns true, then the ability goes onto the stack, and the rest of the ability will run when it resolves, meaning you draw a card. (PLAY_TIME_ACTION blocks are run as soon as it's put onto the stack. INTERVENING_IF blocks are run both when it's put onto the stack and right before it resolves off of the stack.)

COST

COST tags are generally simple, but can be very complicated because of how versatile they are. COST tags always have a "type" string attribute, and its value determines which other attributes are needed, as well as whether or not a COST_DEFINITION tag block is required.

These are all of the possible values for a COST tag's "type" attribute.

  • Mana
  • Generic
  • TapSelf
  • Tap
  • SacrificeSelf
  • Sacrifice
  • DiscardSelf
  • Discard
  • RemoveCountersSelf
  • AddCountersSelf
  • Life
  • ExileSelf
  • Exile
  • Return_To_HandSelf
  • Return_to_hand
  • Reveal
  • RevealSelf

If the type of cost is "Mana", then the mana attribute "mana_cost" must also be present.

If the type of cost is one for which the player chooses cards, then the attributes "definition" (int), "compartment" (int), "query_tag" (text), and "item_count" (int) must be present. ("item_count" might be optional if using a block version of the COST tag and setting it dynamically). The value for "definition" must match the value of "id" in a corresponding COST_DEFINITION tag. The COST tag will use the COST_DEFINITION tag to decide which objects the player may choose to pay the cost. These types are:

  • Tap
  • Sacrifice
  • Discard
  • Exile
  • Return_to_hand
  • Reveal

If the type of cost is one which requires a number, but it is not a number of objects, then "amount" must be present. For instance, adding a number of counters. These types are:

  • RemoveCountersSelf
  • AddCountersSelf
  • Life

The remaining types except Generic require nothing more than the type attribute being defined.

  • TapSelf
  • SacrificeSelf
  • DiscardSelf
  • ExileSelf
  • Return_To_HandSelf
  • RevealSelf

Generic costs are special. They are the only type of cost for which the COST tag is made into a block instead of a one-line tag. A generic COST tag contains other tags. It is generally treated as an ability all its own. This means that it can contain PLAY_TIME_ACTION and RESOLUTION_TIME_ACTION tags. It might even be able to contain CONTINUOUS_ACTION and DURATION tags, though that's unconfirmed. It can contain filters, but not FILTER blocks. Also, any FILTER_CONDITION blocks those filters reference must be outside the COST block. For example:

<UTILITY_ABILITY qualifier="Alternate">
 <LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Dash {2}{B}]]></LOCALISED_TEXT>
 <COST type="Generic">
  <PREREQUISITE>
   local oFilter = ClearFilter()
   oFilter:Add(FE_CONTROLLER, OP_IS, EffectController())
   oFilter:Add(FE_LUA_CONDITION, 1, EffectController(), EffectDC())
   ...
  </PREREQUISITE>
  <PLAY_TIME_ACTION>
   ...
  </PLAY_TIME_ACTION>
 </COST>
 <FILTER_CONDITION id="1">
  return RSN_Characteristics_Get(FilteredCard(), NEO_ABILITIES_ACTIVE)
 </FILTER_CONDITION>
 ...
</UTILITY_ABILITY>

If you've read the DotP 2014: Filters and Interrogations, you'll notice the FILTER_CONDITION block. Note that oFilter here works fine even inside the COST block tag. However, the FILTER_CONDITION block tag is outisde COST.

Possible attributes and their valuetype:

  • definition (number)
  • compartment (number)
  • query_tag (text_permanent entry)
  • item_count (number)
  • amount (number)
  • counter_type (string)

Abilities

Now that we've covered the basics of tags and attributes, we can take a look at some abilities.

Abilities in DotP are handled by ability tags.

  • SPELL_ABILITY
  • STATIC_ABILITY
  • ACTIVATED_ABILITY
  • TRIGGERED_ABILITY
  • UTILITY_ABILITY

Inside ability blocks, you'll find several other types of tags, including COST, TARGET, TRIGGER, and things of that sort. However, the most notable is RESOLUTION_TIME_ACTION, often referred to as an RTA. Analogous to an RTA is a PTA (PLAY_TIME_ACTION). These are blocks of code which will run when the ability resolves off the stack or is put onto the stack (played). The next most common after RTAs are CAs (CONTINUOUS_ACTIONs). They run at the same time as RTAs (meaning when the spell resolves), but they then keep running. RTAs are used when something needs to happen once, such as gaining 3 life or drawing a card. CAs are used when you need something to keep happening, like giving a creature flying. Flying isn't granted by just once saying "this creature now has flying," but rather by saying every time the CA runs "this creature currently has flying."

CAs can exist in a STATIC_ABILITY, and there they can stand on their own without requiring any other tags. In all other types of abilities, however, they must be accompanied by a DURATION tag. When the DURATION tag returns true, the CA stops running permanently.

A single RTA can handle many different things all at once. For example, if you need to destroy a creature and gain 3 life, you only need a single RTA for that effect. However, some events require multiple RTAs. Specifically, if the player has to make a choice, that choice will not be available until the next action. Also, if a card moves zones as part of an RTA, it may not be available in the new zone (or the old zone) until the next action. So, if the ability, for instance, targets two creatures and the player chooses one of those to destroy when the ability resolves, then you need two RTAs. The first RTA will ask the player which target to destroy, and the second RTA destroys the chosen creature. Events which might require multiple RTAs:

  • Revealing cards
  • Any interaction with the player (whether human or AI)
  • Zone changes
  • Probably some others

Generally, even though abilities could potentially be coded such that they use a single RTA, they're coded with multiple ones. Each RTA performs a specific simple part of the effect.

Coding

The actual code in DotP's cards is Lua. Most Lua functions are available for use, such as string manipulation, even though most are never used. Rather, the functions you'll use are almost exclusively those within DotP's engine. They're listed here, including what objects the functions can be called on. If a function is listed under Data Chest, for example, you can only call it on a Data Chest. For instance:

local iNumberOfObjects = Count() -- This will not work.

local iNumberOfObjects = EffectDC():Count() -- This will work because EffectDC() gives you a Data Chest.

There are only a handful of differences between Lua code in a .LOL file (.LOL is the extension of an uncompiled Lua file) and inside a card's code blocks. The only major one is that two operators are different: < and >. When you need to use the 'less than' or 'greater than' symbols, because those are inherently vital to XML, using them directly would cause major errors when the game tries to parse the XML card files. Instead, they're obfuscated by replacing "<" with "&lt;" and ">" with "&gt;". So, in a .LOL file, you'd compare two numbers like this:

if Int1 < Int2 then

In a card file, you'd compare the numbers like this:

if Int1 &lt; Int2 then

Most other functionality is identical.

OBSCURE NOTE: You can even declare new functions and store global values from card abilities. For instance, consider running an ability with this:

<RESOLUTION_TIME_ACTION>
 temp = 0
 TempFunc = function()
  temp = temp + 1
  Debug("temp", temp)
 end
</RESOLUTION_TIME_ACTION>

Calling "TempFunc()" in the future will not only work once, but will actually properly increment "temp" and display the results repeatedly (shows 1, then 2, then 3, and so on). This is very unexplored territory, but it's interesting that it's possible.

Data Chests

The functions most often used are ones which refer to Data Chests, or DCs. EffectDC() is a function which returns a data chest which is only accessible within the current ability.

<SPELL_ABILITY>
 <RESOLUTION_TIME_ACTION>
  EffectDC():Set_Int(0, 1)
 </RESOLUTION_TIME_ACTION>
 <RESOLUTION_TIME_ACTION>
  local iNumber1 = EffectDC():Get_Int(0)
 </RESOLUTION_TIME_ACTION>
</SPELL_ABILITY>
<SPELL_ABILITY>
 <RESOLUTION_TIME_ACTION>
  local iNumber2 = EffectDC():Get_Int(0)
 </RESOLUTION_TIME_ACTION>
</SPELL_ABILITY>

In the code above, iNumber1 will be "1". However, iNumber2 will be 0 (default value), because it's in a different ability. Where the integer is set and where it's retrieved for iNumber2 are in different SPELL_ABILITY blocks. EffectDataChests persist through all actions, costs, triggers, durations, and any other blocks within the same ability. But they are completely inaccessible in other abilities (with an exception for Utility Abilities used for additional or alternate costs). The EffectDC() will persist as long as the card doesn't change zones and the ability is active. So, if an RTA repeats, then it can use EffectDC() to store data between repetitions. CAs can also use it to store data for later. When the card changes zones, however, or when the ability leaves the stack with no CAs to keep it active, the EffectDC() is cleared.

There is, however, a special type of chest which is available to multiple abilities on the same card: LinkedDC(). LinkedDC() is invalid unless the ability's opening tag contains the "linked_ability_group" bool attribute. Any abilities on that card with "linked_ability_group" will access the same data chest. Unlike EffectDC(), LinkedDC() may or may not be cleared when the card changes zones or its abilities are no longer active. (Testing would need to be done to determine the specifics of this.)

Other data chests include PlayerDataChest() and DuelDataChest(). DuelDataChest() will always be valid. It is created when the game starts and isn't cleared until the match ends. PlayerDataChest() will be nil after a player leaves the game, whether by conceding or losing. These can be used to store information from one card and allow another card to read that information.

In previous versions of the game, there was also an ObjectDC(). ObjectDC() was a data chest which existed tied only to a specific card. That data chest could be read by any card and always get the same data. This was used to store information about a card that other cards might need to read. However, in DotP 2014, ObjectDC was removed. RiiakShiNal created a set of ObjectDC() functions which work the same as the previous games' versions. They're not referenced by ObjectDC(), though. Rather, they're referenced by RSN_ObjectDC() (for getting the ObjectDC of this card) or RSN_GetObjectDC(SomeCard) (to get the ObjectDC of SomeCard). In order to use these functions, you need either RSN's ObjectDC mod or the Community Wad, which has the functions included.

Those are the data chests available. But how do they work? Data chests are basically boxes. Inside of any box, you can put either a number, a pointer, or another box. Numbers are straight forward, typically. They're referred to as Int for integer (for those unaware, an integer is any whole number with no decimal places). There are functions for floats, as well, which could have decimals, but I've never seen them used and can't verify if they work. Pointers can be either a card or a player. The ability to nest data chests makes them extremely versatile.

However, note that "strings" and "bools" are missing. While numbers can easily be used for bools, 1 and 0 work fine, strings are much more complicated. A relatively easy way to store strings if needed is CW_Global_Store() and CW_Global_Retrieve(). Inside the CW's functions folder, you'll find CW_GLOBAL.LOL. If you open it (Notepad++) and read the function list at the top, it should give you an idea of how to do this. However, it's not very commonly needed.

Pointers

A pointer is a reference to either a card or a player. EffectSource() gives you a pointer to the card this effect is part of. EffectSource():GetParent() (used on cards that "attach" such as equipment and auras) will return this card's "parent" (the card it's attached to).

If you targeted a creature and stored it in compartment 0 (the standard), then that target is stored in EffectDC():Get_Targets(0). This returns a chest containing all of the targets chosen. They fill in from 0 up, so the first target is at register 0, the second is at register 1, and so on. With only one target, getting the pointer would be this:

EffectDC():Get_Targets(0):Get_CardPtr(0)

If you have a second target, but it's different than the first (so instead of "two target creatures" it's "target creature and target player" such as on Cunning Strike), then you'd need to be careful when setting those up.

<TARGET tag="CARD_QUERY_CUNNING_STRIKE_DEALS_2_DAMAGE_TO_TARGET_CREATURE" definition="0" compartment="0" count="1" />
<TARGET_DEFINITION id="0">
 local oFilter = ClearFilter()
 oFilter:Add( FE_TYPE, OP_IS, CARD_TYPE_CREATURE )
</TARGET_DEFINITION>
<TARGET tag="CARD_QUERY_CUNNING_STRIKE_DEALS_2_DAMAGE_TO_TARGET_PLAYER" definition="1" compartment="1" count="1" />
<TARGET_DEFINITION id="1">
 local oFilter = ClearFilter()
 oFilter:SetFilterType(FILTER_TYPE_PLAYERS)
</TARGET_DEFINITION>
<RESOLUTION_TIME_ACTION>
 local oTargetCreature = EffectDC():Get_Targets(0):Get_CardPtr(0)
 local oTargetPlayer = EffectDC():Get_Targets(1):Get_PlayerPtr(0)
 if oTargetCreature ~= nil and oTargetPlayer ~= nil then
  EffectSourceLKI():DealDamageTo(2, oTargetCreature)
  EffectSourceLKI():DealDamageTo(2, oTargetPlayer)
 end
</RESOLUTION_TIME_ACTION>

Note that the TARGET tags have different definition and compartment numbers. If they had the same number, then the second target would overwrite the first. Targets will be explained a bit more in depth later.

Functions

In Lua, functions are usually called on some object. The object can be a pointer to a card, a player, a chest, or some base object like MTG(). Very few inherent functions are called on their own. When you call a function in Lua, it can return a value such as true or false, a string like a card's CARDNAME value, a number, chest, or array, or some other result. Storing that result requires either putting it directly in a function ready to receive such a result or making a variable for it. Variables on cards (and generally in functions as well) require "local" before them as they're declared.

To call a function on an object, you separate the two with a colon.

MTG():GetNumberOfPlayers()
EffectController():GainLife(2)

Above, what actually happens is 'MTG()' runs and returns the internal MTG object. This object isn't a card or player or anything like that, but rather something much more abstract. Its only purpose is allowing the use of other functions, such as GetNumberofPlayer(). So, when you call GetNumberOfPlayers() on that object, it runs correctly.

Calling 'EffectController()' returns a player pointer. It then calls 'GainLife(2)' on that pointer.

The above block is semantically identical to this one.

local MTGObject = MTG()
MTGObject:GetNumberOfPlayers()

local oController = EffectController()
oController:GainLife(2)

The local variables "MTGObject" and "oController" just store the MTG Object reference and the controller's player pointer for later use, and then uses them later.

OBSCURE NOTE: There do exist some extremely rare cases where calling functions all in one line (like the first example) and calling them separately (like the second example) will behave differently, but I've encountered this only a handful of times, and it was always when the entire line was fed to another function as a parameter. You're unlikely to have this issue, and by the time you do, you'll probably be able to recognize it without issue.

Custom Functions

Mentioned here and there have been some functions that aren't part of the game originally, but which are instead custom functions. RSN_ObjectDC(), CW_Global_Store(), etc. These functions are stored in .LOL files in the FUNCTIONS folder. All custom functions are called on their own without being called on an object. You'll never call EffectSource():RSN_GetObjectDC(). Instead, you'd need RSN_GetObjectDC(EffectSource()). You can pass in parameters, but you cannot reference the calling object.

In order to write your own, simply make a LOL file (or add to an appropriate one) the function and save it. Declaring a custom function is very simple. You give it a name and follow the name with " = function()". Place the names of any parameters you'll need for the function in the parentheses. Two lines down, place "end". Between the two, place any code that should run when the function is called including returning any values the function should produce. In other words, here's the template:

SomeFunction = function(parameter1, parameter2)
 if parameter1 > 3 then
  return parameter2 + 2
 end
 return parameter2 + 1
end

Replace "SomeFunction" with the desired function name. Replace the parameter names with parameters you'll need, and replace the contents from the 2nd line to the 5th with the actual code.

Custom functions can return arrays, and those arrays can be stored in and manipulated via variables on the cards themselves. Because of the data chests, needing arrays is extremely rare.

If you make your own functions which you plan to include in the Community Wad, please make sure they follow the format shown in CW_GENERAL.LOL. Each function should begin with "CW_GENERAL_" (replaced with the name of the file). Each function file should have a section near the top which outlines the various functions in that file and how to use them including the function call, input, and output.

Constants

Sometimes, you need to use a DC within a function. However, if two or more functions use the same DC register, then they would interfere with one another and cause errors. In order to avoid this interference, you use constants. Constants are just global values that are decided before the game runs, and they can be used anywhere in code to get the same value. Constants need to be declared before they can be used, so they're always at the very top of function files. Decompilable LOL Contents contains all of the constants the game has by default. Constants should only be read, not written to. It'd be possible to alter them, but doing so would cause any code referencing the constant to see that change. While this might be useful, you should be extremely careful should you decide to do so. Without knowing precisely what you're doing, you could cause some major issues. Constants are available to all cards and to all functions at all times. Constants are usually used to store a number, though they are normal variables, and as such, they can contain any information a variable could contain (which is any value in Lua of any kind). That constant, rather than the number itself, is then used in functions and cards. This makes it very easy to change the number if needed, which makes it simple to ensure various functions don't interfere with one another.

As an example, imagine that for some reason you need to store a value in DuelDataChest(). Since this DC is available to other cards and functions, and thus they might also want to store data there, we need to make sure our stored data isn't overwriting other values or being overwritten. We do so by making a global constant in a lua file and assigning it a value not used by any other constants that are used in DuelDataChest().

MY_NEW_CONSTANT = 1234

Then, in functions and on cards, we use the variable instead of the number itself:

SomeFunction = function(IntToStore)
 DuelDataChest():Set_Int(MY_NEW_CONSTANT, IntToStore)
end

Now, so long as there aren't any other constants with that same value, we can be sure that DuelDataChest register 1234 is safe. If we discover it's not safe, we can change it only in the location where it's defined and everything that uses that value will get the update.

Here are some important notes for using constants in the CW.

  • All constants which access data chests should be listed in CW_CONSTANTS.LOL. They may also be in another file. If they are in another file, comment out the copy in CW_CONSTANTS.LOL and note the file where it's defined.
  • No constants which access data chests should be below 1000. Values below 1000 are reserved for use on the cards themselves (which should generally only use values below 100, with 100-999 being reserved further for special cases).

When using the CW, you'll need to make certain your constants aren't interfering with another constant. Looking in CW_CONSTANTS.LOL will have all of the information you need to determine this, but it can be confusing at first. They are grouped within the file by two criteria.

1: How many cards use the constant. When it's going to be used on lots of cards (including when it's going to be in a function which will be used on lots of cards), it's in the "Multi-Card Constants" section. Otherwise, it's in the bottom half, "Single Card Constants".

2: Which chest the constant will be used in. For instance, whether it will be in a Player Data Chest or in the Duel Data Chest. Since the two are completely separate, they can safely overlap.

Constants used on multiple cards and on one card can still interfere with one another, so you'll still need to make sure they don't overlap, but if you stick with the numbering format the section already contains, they won't (I use 8 digits for multi-card constants and 6 digits for single-card constants).

On the off-chance you use CW_Globals with a constant, these will need declared, too, and verified they don't interfere with any already in use. Those already in use are listed at the very bottom of CW_CONSTANTS.LOL and near the top of CW_GLOBALS.LOL. Please list yours in both locations. Be sure to add yours to CW_Globals_ReservedConstants, or else the functions may overwrite your values (though it's unlikely to ever get there if it's a large number, but better safe than sorry).

Coding Cards from Scratch

Coding a card is usually done by finding a card with a similar or identical ability and copying the code to the one you're coding. You should start with a basic outline, which will include any simple abilities such as flying, and also many that are slightly more complex, thanks to thefiremind's Universal Card Generator. Here, I'll list two abilities that the generator wouldn't handle correctly and show how to code them.

Target player discards a creature card. Let's assume this is an instant. Instants and sorceries are almost identical, except instants should have AI_AVAILABILITY tags. Sorceries can only be cast during the main phases, so they're already very limited. Using the generator, you will have gotten everything you need except the AI_AVAILABILITY tags and the actual code for that ability. Instead, you'll have this:

<STATIC_ABILITY>
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[Target player discards a creature card.]]></LOCALIZED_TEXT>
</STATIC_ABILITY>

It's not a static ability. This is an effect which is an inherent ability on the card; an ability that goes onto the stack and resolves off of it with the spell itself. As such, it should be a SPELL_ABILITY.

Here's what else we know about the ability:

  1. It needs a target player.
  2. That player needs to choose a creature card in their hand.
  3. That card needs to be discarded.

Working on those in order: A target requires a TARGET tag with the "compartment", "definition", "count", and "tag" attributes, and it also requires a TARGET_DEFINITION block. The TARGET tag will determine what text to show (the "tag" attribute), where to store the results (compartment), which target definition block to use (definition), and how many to choose (count). Target definitions and cost definitions both store things in EffectDC(), so make sure they don't overlap. So, if you've got a cost definition (meaning you're choosing cards as part of a cost like sacrificing a creature), then they should have different compartment and definition tags from any target compartment and definition tags. Generally speaking, just use 0 and go up from there. Also, while not necessary, it's generally easiest to simply make compartment and definition the same. They can be different, but it's a lot easier to have them match. The tag is a TEXT_PERMANENT entry, and for targets, they generally take the form "CARD_QUERY_...". This seems like a lot, but it's not too bad, really. Here's the result of this paragraph:

 <TARGET tag="CARD_QUERY_..." definition="0" compartment="0" count="1">  --This tells the game this ability has 1 target, and to show "CARD_QUERY_..." while choosing that target. Look in definition 0 for how to choose the target, and store the results in EffectDC() register 0.
 <TARGET_DEFINITION id="0">  --Matches the 0 from definition above.
  local oFilter = ClearFilter()  --Target and Cost definitions have a filter. When the target is chosen, it'll let the player choose anything matching the filter.
  ...Some filtering code to get players to choose from...
 </TARGET_DEFINITION>

Next up, we need to have the target player choose a creature card to discard. Choosing a card in a particular zone of a particular type will require a filter (not a FILTER XML tag, but just a normal filter) followed by the ChooseItem function, which requires a TEXT_PERMANENT entry, the chest to put the result in, and any arguments (such as "up to" or "may"). This is being done when the spell resolves, and that means it's in an RTA.

 <RESOLUTION_TIME_ACTION>
  local oPlayer = ...Get the pointer to the target player...
  local oFilter = ClearFilter()
  ...Filter to the chosen player's hand, and limit it to creatures...
  oPlayer:ChooseItem("CARD_QUERY_...", ...Some chest...) --There are no options for this choice.
 </RESOLUTION_TIME_ACTION>

Since a player is being asked to make a decision, the results of that decision must be handled in a separate RTA. In this case, a card was chosen, and it needs to be discarded.

 <RESOLUTION_TIME_ACTION>
  local oCard = ...Get the pointer to the chosen card...
  oCard:Discard()
 </RESOLUTION_TIME_ACTION>

Putting all of that together (remember that STATIC_ABILITY above was changed to SPELL_ABLITY), you'll get something like this:

<SPELL_ABILITY>
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[Target player discards a creature card.]]></LOCALIZED_TEXT>
 <TARGET tag="CARD_QUERY_..." definition="0" compartment="0" count="1">
 <TARGET_DEFINITION id="0">
  local oFilter = ClearFilter()
  ...Some filtering code to get players to choose from...
 </TARGET_DEFINITION>
 <RESOLUTION_TIME_ACTION>
  local oPlayer = ...Get the pointer to the target player...
  local oFilter = ClearFilter()
  ...Filter to the chosen player's hand, and limit it to creatures...
  oPlayer:ChooseItem("CARD_QUERY_...", ...Some chest...) --There are no options for this choice.
 </RESOLUTION_TIME_ACTION>
 <RESOLUTION_TIME_ACTION>
  local oCard = ...Get the pointer to the chosen card...
  oCard:Discard()
 </RESOLUTION_TIME_ACTION>
</SPELL_ABILITY>

In order to filter for players, you just set the filter type to players with SetFilterType(FILTER_TYPE_PLAYERS). So, the target definition becomes this:

 <TARGET_DEFINITION id="0">
  local oFilter = ClearFilter()
  oFilter:SetFilterType(FILTER_TYPE_PLAYERS)
 </TARGET_DEFINITION>

The TARGET tag has a text permanent reference (the tag attribute). You'll need to either look up a working TEXT_PERMANENT entry from another card or make one yourself. Looking up some card that chooses a player to discard reveals the TEXT_PERMANENT entry "CARD_QUERY_CHOOSE_PLAYER_DISCARD_1". From the same card, you should be able to find the TEXT_PERMANENT entry for choosing a card: "CARD_QUERY_CHOOSE_CARD_TO_DISCARD". Simply put those in place of the ones we already have.

Next up, in the first RTA, we need to get the pointer to the player. Since it's a target, it's done by getting the first target from the target chest. THe target chest is a chest inside of EffectDC(). Which register it's stored at is determined by the target's compartment attribute.

local oPlayer = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
--Get_Targets(0) comes from the compartment attribute.
--Get_PlayerPtr(0) is the first target.
--The result is stored in oPlayer for later use.

Filtering for cards in a zone is done by calling SetZone(...Zone Constant..., ...Player Pointer...) on the filter; in this case, SetZone(ZONE_HAND, oPlayer). If you're choosing from any graveyard (Rise from the Grave), or anywhere on the battlefield (Unsummon), for instance, just exclude the player. oPlayer, for the record, came from the code we made above. It's the name of the variable containing the pointer to the target player. To then limit it to creatures, you call Add() on the filter. Add is a very versatile function which accepts many different combinations of arguments.

For an overview, look at Filtering Cards. So, the first RTA now looks something like this:

 <RESOLUTION_TIME_ACTION>
  local oPlayer = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
  local oFilter = ClearFilter()
  oFilter:SetZone(ZONE_HAND, oPlayer)
  oFilter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
  oPlayer:ChooseItem("CARD_QUERY_CHOOSE_CARD_TO_DISCARD", ...Some chest...) --There are no options for this choice.
 </RESOLUTION_TIME_ACTION>

...Some Chest... needs to be replaced by a new target chest. You make a chest with Make_Chest(). A chest can only be made inside another already valid chest. The one used for targets and such (unless under special circumstances) is EffectDC(). Also, note the EffectDC() already contains the player target in register 0. So, you can't reuse that one. Go up to 1 instead. And finally, while Make_Chest() will make a chest, while making a chest specifically for choosing cards, we generally use Marke_Targets, instead. So: EffectDC():Make_Targets(1). So, that line becomes:

  oPlayer:ChooseItem("CARD_QUERY_CHOOSE_CARD_TO_DISCARD", EffectDC():Make_Targets(1))

Finally, we have to set up the second RTA. Getting the pointer for the chosen card is done the same as for the player, except it's in register 1. Since it's a card, it'll use Get_CardPtr() instead of Get_PlayerPtr().

 <RESOLUTION_TIME_ACTION>
  local oCard = EffectDC():Get_Targets(1):Get_CardPtr(0)
  oCard:Discard()
 </RESOLUTION_TIME_ACTION>

So, each section has been dealt with. Putting all of that together, you end up with the following.

<SPELL_ABILITY>
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[Target player discards a creature card.]]></LOCALIZED_TEXT>
 <TARGET tag="CARD_QUERY_CHOOSE_PLAYER_DISCARD_1" definition="0" compartment="0" count="1">
 <TARGET_DEFINITION id="0">
  local oFilter = ClearFilter()
  oFilter:SetFilterType(FILTER_TYPE_PLAYERS)
 </TARGET_DEFINITION>
 <RESOLUTION_TIME_ACTION>
  local oPlayer = EffectDC():Get_Targets(0):Get_PlayerPtr(0)
  local oFilter = ClearFilter()
  oFilter:SetZone(ZONE_HAND, oPlayer)
  oPlayer:ChooseItem("CARD_QUERY_CHOOSE_PLAYER_DISCARD_1", EffectDC():Make_Targets(1))
 </RESOLUTION_TIME_ACTION>
 <RESOLUTION_TIME_ACTION>
  local oCard = EffectDC():Get_Targets(1):Get_CardPtr(0)
  oCard:Discard()
 </RESOLUTION_TIME_ACTION>
</SPELL_ABILITY>

The code above would work in game and do everything it's supposed to. There would only be a few minor changes needed to ensure stability and resilience, such as checking the pointers to make sure they aren't nil in each action before trying to use them.

If enchanted creature would be destroyed, regenerate it and sacrifice THIS CARD. The code you get from the generator might look like this:

<STATIC_ABILITY>
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[If enchanted creature would be destroyed, regenerate it and sacrifice THIS CARD.]]></LOCALIZED_TEXT>
</STATIC_ABILITY>

Since this is triggered by some other event happening, it's a TRIGGERED_ABILITY. TRIGGERED_ABILITYs must have a TRIGGER tag with a "value" attribute set to the trigger they listen for.

Since the trigger is that something is being destroyed, you need to listen for a DESTROYED trigger. And since you want to process stuff before that has actually occurred, you need "replacement_effect" in the TRIGGERED_ABILITY opening tag and "pre_trigger" in the TRIGGER tag.

In order to work with the enchanted creature, you need a pointer to it. For equipment and auras, you get the card they're attached to with GetParent() called on the equipment or aura itself. So, EffectSource():GetParent().

To work with "this card" (for sacrificing it), you get the pointer with EffectSource().

To regenerate something, you just call GiveRegeneration() on it.

To sacrifice something, you call Sacrifice() on the controller with the card to be sacrificed as a parameter. To get the controller of this effect, you call EffectController(). So, EffectController():Sacrifice(EffectSource())

While two things are happening, nothing within the RTAs will be changing zones before needing to interact with them again. The card that was going to die isn't going to, and the equipment being sacrificed isn't interacted with afterward. So, this will only require one RTA.

<TRIGGERED_ABILITY replacement_effect="1">
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[If enchanted creature would be destroyed, regenerate it and sacrifice THIS CARD.]]></LOCALIZED_TEXT>
 <TRIGGER value="DESTROYED" pre_trigger="1">
  return ...Determine if it was the parent that is going to be destroyed.
 </TRIGGER>
 <RESOLUTION_TIME_ACTION>
  EffectSource():GetParent():GiveRegeneration()
  EffectController():Sacrifice(EffectSource())
 </RESOLUTION_TIME_ACTION>
</TRIGGERED_ABILITY>

In a TRIGGERED_ABILITY, there's usually a specific object that caused the trigger to fire. That object is referenced with TriggerObject(). The trigger "DESTROYED" will have TriggerObject() set to the object that is being destroyed. So, in the TRIGGER block, you can use TriggerObject() to make sure the thing being destroyed is the parent.

<TRIGGERED_ABILITY replacement_effect="1">
 <LOCALIZED_TEXT Language_Code="en_US"><![CDATA[If enchanted creature would be destroyed, regenerate it and sacrifice THIS CARD.]]></LOCALIZED_TEXT>
 <TRIGGER value="DESTROYED" pre_trigger="1">
  return TriggerObject() == EffectSource():GetParent()
 </TRIGGER>
 <RESOLUTION_TIME_ACTION>
  EffectSource():GetParent():GiveRegeneration()
  EffectController():Sacrifice(EffectSource())
 </RESOLUTION_TIME_ACTION>
</TRIGGERED_ABILITY>

Functions that you can use in triggered abilities for getting the objects and players associated with this instance are:

  • TriggerObject()
  • TriggerObjectLKI()
  • TriggerPlayer()
  • TriggerStackObjectID()
  • SecondaryObject()
  • SecondaryObjectLKI()
  • SecondaryPlayer()
  • Damage()

LKI in the above functions (and also in "EffectSourceLKI()") is an acronym for Last Known Instance. It would be used in cases where the pointer's information needs to be used even if the card itself is no longer valid. For instance, "Whenever a creature dies, you gain life equal to its toughness." By the time your ability resolves, TriggerObject() isn't valid anymore because the creature has already died. You can still get the information the card had by using TriggerObjectLKI() to get its toughness.

For cleaning up the code in order to keep it legible, changeable, and to make it handle errors better (which isn't optional; users will get error logs when they close the game otherwise), have a look at Cleaning Code.

Coding Cards From Other Cards

Coding cards from other cards is the easiest , fasted, and most reliable way to code cards. Simply open the deck builder, search for a card with a similar ability, and copy over the relevant pieces of code. One thing that can be tricky in this case is making sure you don't copy over code that shouldn't exist on the new card. Perhaps the abilities were very similar but one has two targets and the other has one.

You'll also need to make sure you know what different functions do in order to make sure you're not copying over code that didn't work to begin with, thus perpetuating bugs. Overall, it can be a really simple endeavor or a really complicated one; it depends entirely on the complexity of the ability. If you're ever not sure about whether or not a card is coded right, ask on the forums. Someone is likely to look it over and point out any major mistakes.

What Next

Once you've got the card coded, you'll need to get it added to the game. For that, continue in the Modding Tutorial in section 3.2>Phase 2>Step 2.