Exploring a new card format
Posted: 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.