Wednesday, December 10, 2008

SpellAbility

Last week I talked about SpellAbility and since it is such an important topic, I plan to talk about it again. (Here is a short review, the Card object holds many SpellAbility objects. SpellAbility's resolve() method does the actual function of the card, like dealing 3 damage. Java capitalizes object names like Card and SpellAbility. A card refers to a regular Magic card, but Card refers to the Card object. And an object is just a bunch of related Java code.)

SpellAbility has a mana cost which at first seems like it should go in Card. But this goes back to the question of "What is a Magic card?" I envision that a Magic card just holds spells, so the Card object is just a bucket and SpellAbility actually gets to do the fun stuff like having a mana cost and resolving off of the stack.

I'm using SpellAbilty in both MTG Forge version 1 and 2. In version 2 I have made a few minor changes to SpellAbility. In version 1, Programming ability costs like "sacrifice this card" and "tap" were complicated. In version 2 I will add a simple Command to SpellAbility that is executed before the SpellAbility is put on the stack. A Command object only has one method which is usually called something like execute(). Any code can be put into execute() so I can easily code activated ability costs like "sacrifice this card" and "tap".

Command
execute()

TapCommand extends Command
execute() - tap the card

Above is a simple representation of Command. The Command object doesn't do anything and a subclass has to be created in order make execute() useful. A subclass gets all of the code from its "parent". I subclass SpellAbility to handle instants. SpellAbility has the canPlay() method which returns true if this effect can be played. The canPlay() method of the instant subclass always returns true. The SpellAbility canPlay() method representing a sorcery would check to see if it is the Main 1 or 2 phase and that nothing is on the stack.

That last paragraph is a little complicated, but I have faith that you guys can understand it :+)

5 comments:

Anonymous said...

I have a question. Some time ago I read on your blog that you stuck programming on version 2. And you are not sure to ever got it finished because it becomes to big for you. Can you tell me where exactly you got to your limitations?

Forge said...

Version 2 has had a few false starts. First I started coding it in Java, then I started over again using Python, and now I've started over again using Java.

Version 2 is coming along and you can play one card, Watchwolf. I didn't write version 1 on any sort of a timetable, so I have no idea when version 2 will be playable.

nantuko84 said...

The idea is great as it become so simple to code cards with several abilities (e.g., with "choose one" or with activated abilities that can be played only in play and have manacost that differs from spell one)

The other brilliant idea I've found in your code was Spell_Evoke class. It's so easy to add evoke mechanic for a new card using it. Moreover I found out that Spell_Kicker and Spell_Madness can be made the same easy way.
Could you please describe the Spell_Evoke class idea, how it is used in the project, may be also about some other Spell_"Mechanic" classes. I do believe it will be very interesting for blog readers, at least for me.

BR

Forge said...

Thanks Nantuko for the suggestions. I programmed the Spell_Evoke class it order to easily add the few evoke cards. Right now I forget how I used it, but I think Spell_Evoke is a simple class. I have to look back at it and read my code.

I plan to write about Spell_Evoke after the holidays, Merry Christmas.

Anonymous said...

A couple of points...

First, an instant can't be played at any point in time. It can be played almost all of the time, but it's only the "second-fastest" ability variety. The fastest are, of course, mana abilities, which can not only be played in the middle of the announcement of another ability, but don't even use the stack, resolving immediately upon announcement. If you allow an instant to be played any time a mana ability could be played, you get some wacky rules violations going on.

Second, while abilities should have their own costs (well, activated abilities, anyway), the "casting" ability should reference a cost variable on the card itself (or, more precisely, some sort of special class that determines the cost of the card). The card itself has a mana cost (the cost in the upper-right hand corner), which is both manipulated and read by various abilities, and should be an attribute of the card itself, not its playing ability (we recently fixed our CastAbility objects to use "source.cost" instead of a cost argument, which means there's only one object being dealt with now (incredibly useful!). Mind you, we broke cost-modifying cards (Etherium Sculptor), but we got copy effects working (including Vesuvan Doppelganger and Sakashima the Impostor), so it was probably a worthwhile tradeoff... especially since a cost-modification framework can probably be created so that it works with copy effects).

Of course, you can always just do whatever the hell you want, but doing something similar to this will probably make things easier for you. ;)