Monday, June 8, 2009

Zone Events - Part 2

Other common events include when a player gains or loses life. Although more complicated, damage and combat damage events would simplify the coding for cards like Hypnotic Specter. (MTG Forge implements this card through some clever hacking, thanks Dennis!!) Since Control Magic and other cards allow players to steal creatures, you have to carefully update the damage and combat damage events of the stolen creature.

In summary some basic Magic events include when a card is added or removed from a zone, when a player's life changes and damage/combat damage triggered abilities. Additional events include discard for Megrim and "how many card were played this turn" for storm.

To further complicate the situation, technically the stack is an oddball zone that only holds spells and abilities while all of the other zones hold cards. Internally I'm thinking about letting the stack hold card objects so I could use only one method to move a card, moveCard(Card, Zone to, Zone from).

Anytime a card is moved it would have to use this one method. This would also allow me add an event that would fire every time a card changed zones, addEvent_moveCard(Event). Megrim could use this event to see when a player discards a card. Cards that care about the storm count could also use this event to get the information that they need. This last event is very general and very powerful.

16 comments:

Silly Freak said...

hmm... i think the stack is really an odd zone and just doesn't work the same. as such, it might be not so a problem to use other methods for these actions.
Additionally, triggering for spells played and cards moved are different things and it might also be more comfortable to have it separated.
i think of three methods:
move, play, resolve

move is straight forward, as you described it.
play has to consider if the played card is a land. in that case, it moves to play, otherwise to the stack. by using move from the play method, you get the desired effect of all code in a single method and don't have to worry about the differences of land and nonland.

similarly, resolve makes a difference between permanent and non-permanent spells and again uses move

Moxy said...

Just a couple things. The stack certainly should not be a zone. It's not a true zone on the game and if you move a *card* from a any zone into the stack well then you'll need special code for cards that have a leaves play or comes into play ability to see if they're operating on the stack or not. The stack should really be treated differently. Also what if you're playing a "spell" not a card for instance a copied spell from fork?

I think if you're going to use the AWT event stack you should just create an event for everything that happens. Cards can then listen to the stack and introducing new events from or for new abilities or keywords should be fairly painless.

Cards with storm for instance shouldn't really be watching events to see how many spells are being played. For storm you should have certain attributes that you could query one of those attributes is for the number of spells played this turn. When a storm card is played it just queries this attribute to make the necessary number of copies. Other attributes might be life/poison gained/lossed this turn and number & type of cards placed in the graveyard/rfg this turn.

Ian said...

The stack is a zone, and it can have cards in it. See Comprehensive rules 217.1 for a listing of zones, and 217.6 for the rules on the Stack.

Moxy said...

About the Stack being a zone. My mistake but you're probably going to have some real issues if you move a card into the stack zone when you activate it's ability if it's got triggered abilities that trigger when entering or leaving play.

Incantus said...

The stack is a zone. However, it's a special zone, which can contain both cards and abilities (activated and triggered). This makes it really tricky to implement. For example, if you play a spell from your hand, the card moves from your hand to the stack. This may seem unimportant, but just consider Threshold. You don't want to count the just played spell as being in your hand.

William said...

Yeah, the stack is just an odd zone. But, it definitely exists, because the rules say so. They (wizards) prefer not mentioning it in cards because of that. It's probably really hard to code.

Also, that is my second favorite art for counterspell. With my favorite being the original one by mark poole, but that is more of a nostalgia thing.

Just to tell you, in Mark Rosewater's article for today, he mentioned that there was a single cycle that was removed from a set because they couldn't figure out how to program it for Magic Online. He didn't say what it was though, because he planned to try and get it into a later set. I just thought you would find that interesting.

Rob Cashwalker said...

I wonder if there's anything that we could code that the MTGO team can't....

Nurova said...

Well, I think you can resolve the problem of mixing up the stack zone with the in play zone. Technically, the graveyard is also a zone, and certain cards would also see if a card enters that zone as well. Wizards is trying to alter the idea of 'play' by making cards on the table the 'battlefield' (coming out in 11th edition i believe).

By treating the stack more like a zone also makes cards like counterspell and fork more straightforward. It may be harder to think of it because of abilities going into the stack, but then, they can also be altered by other played cards.

The benefit to making it into its own zone is you can have the rules depicted easier. For instance, instead of making sorceries only castable when a player has priority, you can also make them unable to be cast when there is anything on the stack (which is in the rules). Another benefit is cards entering the zone are 'played' and cards that leave the zone 'resolve' unless they were countered and removed prematurely.

Forge said...

I've had my share of problems treating the stack as a separate but different zone. I would also love to use one method, moveCard(Card, Zone to, Zone from), which would handle all card movement.

I've also had other insane ideas that I probably won't do like making the "in hand" zone shared, so players would only hold cards that they control.

Forge said...

Rob,

"I wonder if there's anything that we could code that the MTGO team can't...."

Probably but Wizards doesn't print cards that MTGO can't implement, I read an article awhile ago about a specific instance that this happened. They also had a hard time with Dryad Arbor which is trivial in MTG Forge. (Granted that MTGO implements a ton of rules that MTG Forge ignores.)

Incantus said...

Forge:

"They also had a hard time with Dryad Arbor which is trivial in MTG Forge. (Granted that MTGO implements a ton of rules that MTG Forge ignores.)"

This is why they had a hard time with Dryad Arbor (all those extra rules ;). I think the main problem they had was with the ruling that if it gains flash it can be played at any time during your own turn (this is slightly inconsistent with the rest of the rules so it probably has to be special-cased).

Nurova said...

Using one method would work very well with the idea of moveCard, and the idea itself of moveCard makes implementing the stack as a zone easier I would think...

I know its your program and you can do with it what you will, but I really want to push the idea of the stack being a zone, I think it would help in the long run. And I think one of the biggest examples is cards in play that say "whenever a player plays..." it can just check when a moveCard has the target as the stack zone. But like I said, it's your program.

'Course, I am a bit rusty with programming, so I'm not sure if you can make the moveCard an event and just have an onEvent(method) for the example card in play I said earlier. That's currently how I'm picturing cards like that working, they would look at everything that gets played and activate on a spell.

Moxy said...

How about a stack that's a sub class of zone? I mean the stack is a specialized zone right?

Have you considered making cards and abilities separate classes? A card can have zero or more abilities. If you make the stack specialized then you could add an additional method like playAbility(...). This could still trigger an event but it wouldn't cause you to trigger an event for a card entering or leaving a zone since it's not a card.

Maybe have an event "stack changed" that would trigger anytime anything happens on the stack. Basically I assume like passing priority IRL.

For things interested in cards being played they could watch for the moveZone event on the stack zone. For cards interested in the stack changing in anyway they could watch for the stack change event. They could query the event to see if it's a spell/ability added to the stack (fork), a spell/ability removed from the stack (forked-copy was countered), or whatever. Probably lots of other tiny events that rarely need to be watched too.

Doing this you lump the majority of the interesting (zone change) into one event and the rest of the specialized stack stuff into a larger event the cards, spells, abilities, whatnot can query and figure out themselves if it's interesting.


In fact you could generalize this down to a "zone change" event where you trigger an event for any zone that has changed in any way (card tapped?, card added?, card removed? card modified?, step changed - not phase change). A specialized "zone change" event might be "card removed" and "card entered" whereby you can watch to see if a card leaves a zone or a card enters a zone. If you need to tie the two together you could do a "card moved" event that specifies the from and to zones. Your "card moved" event could be the parent of "card entered" and "cared removed".

I've noticed in past programming of generic frameworks that are extended later with plugins and such you can never have to many events because there's always some corner case you're going to forget about.

Silly Freak said...

about zone changed events: i think you're on the wrong path in the way that it's not the zone which changes, but its content, if a card is tapped, it's the card, not the zone, that changes.
i can't think of any real zone changed events, but having all these taps etc in the same class of events would be really boring for a class that's really interested in the zone events.

i think the right way would be to register a listener on the card when it is moved to and unregister it when it is moved from the game. if the events are implemented properly, there should be no leaking of listeners when a card changes zones

also, passing priority is imo a game event and not a stack event

Forge said...

Nurova,

"I know its your program and you can do with it what you will, but I really want to push the idea of the stack being a zone."

My idea that the stack is a zone and I want all of the zones, including the stack, to only hold cards (or Card objects). Technically the stack doesn't hold cards.

In programming you want to make everything as general and similar as possible, so if I can make the stack "look and act" like the other zones it will mean less code because you don't have special cases that only deal with the stack. You the player won't notice anything and the stack will act as normal.

Forge said...

Incantus,

"This is why they had a hard time with Dryad Arbor (all those extra rules ;). I think the main problem they had was with the ruling that if it gains flash it can be played at any time during your own turn (this is slightly inconsistent with the rest of the rules so it probably has to be special-cased)."

Lol, that is funny, an instant speed land. Yes I'm sure that was a programming headache for a crappy card.