Exploring a new card format
by Incantus
Moderator: CCGHQ Admins
Exploring a new card format
by MageKing17 » 23 Jun 2010, 01:45
Incantus and I have talked a bit about changing the card format so that abilites are defined as classes. For those of you who haven't been watching the evolution of the card format (and those who want a refresher), cards used to look like this:
After a while, the card format "evolved" into this slightly better format:
Finally, a massive change occurred when abilities changed to being defined as decorators:
This is what Chainer, Dementia Master's code looks like now:
Now, Incantus and I are considering another major change: class-based ability definitions. Here's a mockup of how Chainer's abilities might look under the new system:
As you can see, all the various types of abilities should still work, and in fact the engine might not need to change that much to accommodate it (and we think things like Feral Hydra, where someone other than the card's controller activates an ability, would be much easier). It's a big change, though, and since it's not a simple issue, I thought I'd ask you all for thoughts and opinions.
- Code: Select all
name = 'Chainer, Dementia Master'
cardnum = 0
expansion = ''
type = characteristic('Creature')
supertype = characteristic('Legendary')
subtypes = characteristic(['Human', 'Minion'])
cost = '3BB'
color = characteristic(['B'])
text = ['Nightmare creatures get +1/+1.', 'BBB, Pay 3 life: Put target creature card from a graveyard into play under your control. That creature is black and is a Nightmare in addition to its other creature types.', 'When Chainer, Dementia Master leaves play, remove all Nightmares from the game.']
out_play_role.abilities = [CastPermanentSpell(card, cost)]
subrole = Creature(5, 3)
in_play_role = Permanent(card, subrole)
#################################
subrole.abilities = [ActivatedAbility(card,
cost=MultipleCosts([ManaCost("BBB"),LifeCost(3)]),
target=Target(target_types=isCreatureType, zone="graveyard", player_zone=None),
effects=[ChangeZoneToPlay(from_zone="graveyard"),
ModifyColor(characteristic("B"), expire=False),
ModifySubtype(additional_characteristic("Nightmare"), expire=False)
]
)]
subrole.triggered_abilities=[TriggeredAbility(card,
trigger=LeavingTrigger(zone="play"),
match_condition=SelfMatch(card),
ability=Ability(card, target=AllPermanentTargets(target_types=isCreature.with_condition(lambda c: c.subtypes == "Nightmare")),
effects=ForEach(ChangeZone(from_zone="play", to_zone="removed"))
)
)]
subrole.static_abilities = [PermanentTrackingAbility(card,
condition = isCreature.with_condition(lambda c: c.subtypes == "Nightmare"),
events = SubtypeModifiedEvent(),
effects = AugmentPowerToughness(power=1,toughness=1,expire=False)
)]
#################################
After a while, the card format "evolved" into this slightly better format:
- Code: Select all
name = 'Chainer, Dementia Master'
cardnum = 0
expansion = ''
type = characteristic('Creature')
supertype = characteristic('Legendary')
subtypes = characteristic(['Human', 'Minion'])
cost = '3BB'
color = characteristic(['B'])
text = ['Nightmare creatures get +1/+1.', 'BBB, Pay 3 life: Put target creature card from a graveyard into play under your control. That creature is black and is a Nightmare in addition to its other creature types.', 'When Chainer, Dementia Master leaves play, remove all Nightmares from the game.']
play_spell = CastPermanentSpell(card, cost)
in_play_role = Permanent(card, Creature(3, 3))
#################################
abilities.add(PermanentTrackingAbility(card,
condition = isCreature.with_condition(lambda c: c.subtypes == "Nightmare"),
events = SubtypeModifiedEvent(),
effects = AugmentPowerToughness(power=1,toughness=1,expire=False),
txt=text[0]
))
abilities.add(ActivatedAbility(card,
cost=MultipleCosts([ManaCost("BBB"),LifeCost(3)]),
target=Target(target_types=isCreatureType, zone="graveyard", player_zone=None),
effects=[ChangeZoneToPlay(from_zone="graveyard"),
ModifyColor(characteristic("B"), expire=False),
ModifySubtype(additional_characteristic("Nightmare"), expire=False)],
txt=text[1]
))
abilities.add(TriggeredAbility(card,
trigger=LeavingTrigger(zone="play"),
match_condition=SelfMatch(card),
ability=Ability(card, target=AllPermanentTargets(target_types=isCreature.with_condition(lambda c: c.subtypes == "Nightmare")),
effects=ForEach(ChangeZone(from_zone="play", to_zone="removed"))
),
txt=text[2]
))
#################################
Finally, a massive change occurred when abilities changed to being defined as decorators:
- Code: Select all
name = 'Chainer, Dementia Master'
cardnum = 0
expansion = ''
types = characteristic('Creature')
supertypes = characteristic('Legendary')
subtypes = characteristic('Minion', 'Human')
cost = '3BB'
color = characteristic('B')
power = 3
toughness = 3
text = ['Nightmare creatures get +1/+1.', 'BBB, Pay 3 life: Put target creature card from a graveyard into play under your control. That creature is black and is a Nightmare in addition to its other creature types.', 'When Chainer, Dementia Master leaves play, remove all Nightmares from the game.']
play_spell = play_permanent()
#################################
@static_tracking(txt=text[0], events=(TypesModifiedEvent(), SubtypesModifiedEvent()))
def ability():
def condition(source, card):
return isCreature(card) and card.subtypes == "Nightmare"
def effects(source, card):
yield card.augment_power_toughness_static(1, 1)
return condition, effects
abilities.add(ability)
@activated(txt=text[1])
def ability():
def effects(controller, source):
payment = yield ManaCost('BBB') + LifeCost(3)
target = yield Target(isCreatureCard, zone="graveyard")
# Code for effect
def enter_play_black_nightmare(self):
self.color.set('B')
self.subtypes.add('Nightmare')
expire = CiP(target, enter_play_black_nightmare, txt='~ - Target is black and a Nightmare in addition to its other creature types')
target = target.move_to(controller.play)
expire()
yield # I CAN'T BELIEVE I FORGOT THE YIELD
return effects
abilities.add(ability)
@triggered(LeaveTrigger("play"), txt=text[2])
def ability():
def effects(controller, source, card):
target = yield NoTarget()
# Code for effect
for nightmare in controller.play.get(isPermanent.with_condition(lambda c: c.subtypes == "Nightmare"), all=True):
nightmare.move_to("removed")
yield
return source_match, effects
abilities.add(ability)
This is what Chainer, Dementia Master's code looks like now:
- Code: Select all
name = 'Chainer, Dementia Master'
cost = '3BB'
types = Creature
supertypes = Legendary
subtypes = Human, Minion
power = 3
toughness = 3
text = ['Nightmare creatures get +1/+1.', 'BBB, Pay 3 life: Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types.', 'When ~ leaves the battlefield, exile all Nightmares.']
#################################
@static_tracking(events=(TypesModifiedEvent(), SubtypesModifiedEvent()), txt=text[0])
def ability():
def condition(source, card):
return isCreature(card) and card.subtypes == Nightmare
def effects(source, card):
yield card.augment_power_toughness(1, 1)
return condition, effects
abilities.add(ability)
@activated(txt=text[1])
def ability():
def effects(controller, source):
cost = yield ManaCost('BBB') + LifeCost(3)
target = yield Target(isCreatureCard, zone="graveyard")
# Code for effect
def enter_battlefield_black_nightmare(self):
self.color.set(Black)
self.subtypes.add(Nightmare)
expire = CiP(target, enter_battlefield_black_nightmare, txt='~ - Target is black and a Nightmare in addition to its other creature types')
newcard = target.move_to(controller.battlefield)
expire()
yield
return effects
abilities.add(ability)
@triggered(txt=text[2])
def ability():
def effects(controller, source, card):
target = yield NoTarget()
# Code for effect
for nightmare in controller.battlefield.get(isPermanent.with_condition(lambda c: c.subtypes == Nightmare), all=True):
nightmare.move_to("exile")
yield
return LeaveTrigger("battlefield", source_match), effects
abilities.add(ability)
Now, Incantus and I are considering another major change: class-based ability definitions. Here's a mockup of how Chainer's abilities might look under the new system:
- Code: Select all
@static_tracking()
class ability:
events = lambda self: (TypesModifiedEvent(), SubtypesModifiedEvent())
def condition(self, card):
return isCreature(card) and card.subtypes == Nightmare
def effects(self, card):
return card.augment_power_toughness(1, 1)
abilities.add(ability)
@activated()
class ability:
cost = lambda self: ManaCost('BBB') + LifeCost(3)
target = lambda self: Target(isCreatureCard, zone="graveyard")
def resolve(self):
def enter_battlefield_black_nightmare(self):
self.color.set(Black)
self.subtypes.add(Nightmare)
expire = CiP(target, enter_battlefield_black_nightmare, txt='~ - Target is black and a Nightmare in addition to its other creature types')
newcard = target.move_to(controller.battlefield)
expire()
abilities.add(ability)
@triggered()
class ability:
triggers = lambda self: LeaveTrigger("battlefield", source_match)
def resolve(self):
for nightmare in controller.battlefield.get(isPermanent.with_condition(lambda c: c.subtypes == Nightmare), all=True):
nightmare.move_to("exile")
abilities.add(ability)
As you can see, all the various types of abilities should still work, and in fact the engine might not need to change that much to accommodate it (and we think things like Feral Hydra, where someone other than the card's controller activates an ability, would be much easier). It's a big change, though, and since it's not a simple issue, I thought I'd ask you all for thoughts and opinions.
-
MageKing17 - Programmer
- Posts: 473
- Joined: 12 Jun 2008, 20:40
- Has thanked: 5 times
- Been thanked: 9 times
Re: Exploring a new card format
by Marek14 » 23 Jun 2010, 04:53
I think that if it improves the system, you should go for it
Re: Exploring a new card format
by MageKing17 » 23 Jun 2010, 06:28
Such deeply insightful commentary.Marek14 wrote:I think that if it improves the system, you should go for it
The real question is, of course, whether there are any features people would like to make sure get in now, while it's still in the planning stages, rather than waiting until after it's already been coded, and very difficult (if not impossible) to change.
-
MageKing17 - Programmer
- Posts: 473
- Joined: 12 Jun 2008, 20:40
- Has thanked: 5 times
- Been thanked: 9 times
Re: Exploring a new card format
by juzamjedi » 23 Jun 2010, 14:35
I think it's safe to say that we all want a rules engine that can handle the most situations possible. One that is flexible so that most (or all!) of the current abilities in Magic could be implemented and crazy new abilities have a reasonable shot at being added to the engine.
I'm not a Python expert at all and 90% or more of the Python code I have ever seen has been Incantus. I'm looking at the code for Chainer above and I don't see a significant difference from last version to current. I don't see timestep() events in the example would this happen as part of resolve() ?
With the current state of the card code base I don't think making this change now would cause a large issue. There haven't been too many cards coded with the "current" system anyway.
Can we copy spells on the stack now?
Mana types and restrictions would be nice to have in the engine (snow mana, Mishra's Workshop, Ancient Ziggurat, etc.).
Also more generally have you looked at keyword abilities that have not been implemented yet and given thought to how they would work (or just add them to the engine on the next update)?
I'm not a Python expert at all and 90% or more of the Python code I have ever seen has been Incantus. I'm looking at the code for Chainer above and I don't see a significant difference from last version to current. I don't see timestep() events in the example would this happen as part of resolve() ?
With the current state of the card code base I don't think making this change now would cause a large issue. There haven't been too many cards coded with the "current" system anyway.
Can we copy spells on the stack now?
Mana types and restrictions would be nice to have in the engine (snow mana, Mishra's Workshop, Ancient Ziggurat, etc.).
Also more generally have you looked at keyword abilities that have not been implemented yet and given thought to how they would work (or just add them to the engine on the next update)?
Re: Exploring a new card format
by MageKing17 » 23 Jun 2010, 19:59
Well, yeah. It's easier said than done, though.juzamjedi wrote:I think it's safe to say that we all want a rules engine that can handle the most situations possible. One that is flexible so that most (or all!) of the current abilities in Magic could be implemented and crazy new abilities have a reasonable shot at being added to the engine.
Yes. Currently, the effects() functions are generators, which yield back to the rules engine to send timesteps. With resolve() functions, they won't be generators anymore, so they'll send the timesteps directly. Here's an example of "draw a card, then discard a card, in the old system, and the new:juzamjedi wrote:I'm not a Python expert at all and 90% or more of the Python code I have ever seen has been Incantus. I'm looking at the code for Chainer above and I don't see a significant difference from last version to current. I don't see timestep() events in the example would this happen as part of resolve() ?
- Code: Select all
OLD:
def effects(controller, source):
cost = yield blah blah
target = yield NoTarget()
controller.draw()
yield
controller.force_discard()
yield
NEW:
cost = lambda self: blah blah
def resolve(self):
self.controller.draw()
self.timestep()
self.controller.force_discard()
Indeed, this is an ideal time to make the transition, which is why I want everyone's input on it.juzamjedi wrote:With the current state of the card code base I don't think making this change now would cause a large issue. There haven't been too many cards coded with the "current" system anyway.
This is one of the reasons this new system is being considered. Copying spells on the stack should be easier if we do this right. So should entwine.juzamjedi wrote:Can we copy spells on the stack now?
Mana restrictions are almost certainly going to have to wait until individual mana points are represented by their own objects. That being said, snow mana may be possible anyway, by splitting the mana pool into two halves. Which won't be pretty. On second thought, maybe it'd be best to wait.juzamjedi wrote:Mana types and restrictions would be nice to have in the engine (snow mana, Mishra's Workshop, Ancient Ziggurat, etc.).
Well, I know that rebound should be possible (I just didn't have time for it when I was doing madness and flashback), if a bit complicated. Off the top of my head, bloodthirst and bloodthirst X should also be pretty easy. The problem is the real problem keywords, like phasing and morph, and they're not gonna be implemented for a while. I have no hope of getting morph to work right without Incantus's help, at any rate, and I doubt phasing will ever work (but I suppose I could be proven wrong on that). Beyond that, if you look at the source code (specifically, engine/Ability/UnimplementedAbility.py), you should be able to figure out which abilities are possible using existing techniques and which ones require an extensive overhaul. I'll give them a once-over myself before I work on the next version (be it v0.7.1b or v0.7.2 or whatever).juzamjedi wrote:Also more generally have you looked at keyword abilities that have not been implemented yet and given thought to how they would work (or just add them to the engine on the next update)?
EDIT:As an example of something the new system can do that the old can't, here's how the Feral Hydra ability would work:
- Code: Select all
@activated()
class ability:
cost = lambda self: ManaCost('3')
def playable_by(self, player): return True
def resolve(self):
self.source.add_counters(PowerToughnessCounter(1, 1))
abilities.add(ability)
-
MageKing17 - Programmer
- Posts: 473
- Joined: 12 Jun 2008, 20:40
- Has thanked: 5 times
- Been thanked: 9 times
Re: Exploring a new card format
by Huggybaby » 25 Jun 2010, 02:29
That's the main reason to start over isn't it, to be able to do things the current system can't. Is there a list of functions the current system cannot perform? And are they all possible in the new one?
-
Huggybaby - Administrator
- Posts: 3209
- Joined: 15 Jan 2006, 19:44
- Location: Finally out of Atlanta
- Has thanked: 704 times
- Been thanked: 595 times
Re: Exploring a new card format
by MageKing17 » 25 Jun 2010, 04:51
A comprehensive list of things we can't do is pretty much impossible, but on a more limited scale, we never bothered to get around to making a note whenever we ran into something we couldn't do. Whenever we get around to modifying our to-do lists, it's pretty much the sort of stuff I've already mentioned (entwine, copying spells on the stack, activating abilities of permanents you don't control, et cetera).Huggybaby wrote:That's the main reason to start over isn't it, to be able to do things the current system can't. Is there a list of functions the current system cannot perform? And are they all possible in the new one?
-
MageKing17 - Programmer
- Posts: 473
- Joined: 12 Jun 2008, 20:40
- Has thanked: 5 times
- Been thanked: 9 times
7 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 2 guests