Monday, March 23, 2009

Static Abilities as Triggered Abilities

Static abilities are difficult to implement, so today I’m going to talk about how to transform many static abilities into triggered abilities. Static abilities are always “on” and Glorious Anthem is a good example. Triggered abilities occur when the condition is fulfilled such as “When this card comes into play” such as Venerable Monk. Triggered abilities always start with “when”, “whenever” or “as” so they should be easy to identify.

First, let me briefly digress and talk about listeners and events. Usually a Magic implementation like MTG Forge has many events that other parts of the program can observe. One of the most common events is when a card is added or removed from a zone, addCardEvent and removeCardEvent.

Now that we have these two events can you figure out how to implement Glorious Anthem? Hopefully this should be easy, every time that addCardEvent triggers Glorious Anthem checks to see if you control that card and then adds +1/+1 to it.

Fortunately the above logic works most of the time but there are also other considerations to keep in mind. If you “steal” your opponent’s creature with Control Magic, addCardEvent wouldn’t trigger because the “play area” is a shared zone, both you and your opponent share the same play zone.

One solution is to have another event that only triggers when cards are added to your play area. (Your play area is defined as, when a new creature comes into play under your control.) Since many cards like Glorious Anthem care about only creatures that you control, subdividing addCardEvent into more specific events is a good idea.

Currently MTG Forge does implement cards like Glorious Anthem but they are a little slow and inefficient. If you have many Glorious Anthems in play MTG Forge will react a little bit slower because of the extra CPU time that is required. In essence the Glorious Anthem code is executed “too many times” and usually doesn’t do anything. Using triggered events for Glorious Anthem is both efficient and easy to understand.

12 comments:

Gando the Wandering Fool said...

Wouldn't the term "passive abilities" work better than static which reminds me of things that either stand still or variables that have only a local scope or what happens when you touch a cat sometimes?

Is "static" the term WoTC uses to describe these passive abilities? (Before I came across forge Id never heard them called that in my 15+ years of playing the game.)

The reason why I bring this up is it seems to me properly naming something so that it is described by its name alleviates some of the tendancies to think about such things in a fog. Well thats my experience anyway.

Anonymous said...

@Gando: it's called static abilities

Noon Knight said...

I was wonder if when needing to know the power/toughness of a creature whether it would be good to just give each card in play a chance to add to the base P/T? So most cards would just return +0/+0 but Glorious Anthem would return +1/+1. Is this too much of a hard coding approach?

@Forge: BTW, I'm really glad you wrote and decided to share MTG Forge. I've been wanting to program MTG too.

Forge said...

Noon Knight,
I really wanted a program like MTG Forge for me, so I programmed it. I had to make some major concessions, like the user interface wouldn't be perfect and it doesn't have all the phases and the AI would be weak, but it is a fun project to play and to program.

Forge said...

For more rules info from the Basic Rulebook and the Comprehensive Rules from here

The Comprehensive Rules are a good resource for definitions.

405.1. A static ability does something all the time rather than being activated or triggered. The ability isn't played -- it just "exists." Such abilities apply only while the ability is on a permanent in play, unless the ability is covered by rule 402.8.

Anonymous said...

Forge,

That's how static abilities are handled in Incantus as well, except they handle more than entering and leaving play. They also listen for controller change events and other events relevant for the matching a particular card (for example, if the static event said: +1/+1 to all red Goblins creatures you control, it would also listen for ColorModifiedEvent (if a permanent gains/loses red), TypeModifiedEvent (if a permanent gains or loses the creature type) and SubtypeModifiedEvent (for the Goblin subtype)). The alternative, which you mentioned, is to go through each static ability at each "point" in game time and make it's modifications.

Gando the Wandering Fool said...

I guess I was quibbling with WOTCs wording then :) I still think something that just exists would be better called a passive ability. Just my sense of language I guess. I do remember long long ago learning that stuff about static vs triggered and activated but its been nearly a decade since I needed to know it so lol.

Anonymous said...

Oh, by the way, if you generalize the part that listens for zone change events, you can easily implement cards that have static abilities that affect cards in other zones (like the graveyard - for example Yixlid Jailer). Also, you can generalize when the abilities become active (in the graveyard, for example Riftstone Portal) and have the card implementer specify that.

Forge said...

Yes I try to be as general as possible when I program, such as adding events that would be useful in many different situations.

Unknown said...

I think if instead of each card listening for an event, that a single event handler could poll each card in play, and execute an appropriate command object. If it doesn't have one, then it just moves on. Each card currently has a comes into play command and a leaves play command... why not a Main1 command, or an Attack command... etc and of course also a StateEffects command.

Silly Freak said...

@incantus:
what you say is storing the modified attributes and whatever at every moment of the game. this isn't neccessary. it's enough to recalculate a value every time it is used. this includes, of course, knowing what static abilities modify which values. such things could be handled with a listener system.

the listener would get the card to modify and the sort of modification as parameters and then decide if it is appropriate for the task.

for example, GA would check if the card is a creature that glorious anthem's controller controlls and if the modification is something like "Layer6d" (in which GA applies corresponding to the CR). If both matches, GA adds 1/1 to the temporary P/T for this calculation

Anonymous said...

Sillyfreak - I'm not exactly following what you are saying. Maybe we could move the discussion over to a new topic on slightlymagic.net.