mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 19:11:59 -08:00
Refactor CreateTokenEffect to allow multiple tokens at once. (#12704)
* Refactor CreateTokenEffect to allow multiple tokens at once. Partial solution to #10811 - Token copy effects still need to be redone so that mass token copy effects (Ocelot Pride, Mirror Match, other similar effects) can be created in a single batch.
This commit is contained in:
parent
da48821754
commit
543f9f074e
26 changed files with 132 additions and 154 deletions
|
|
@ -59,11 +59,7 @@ public final class AKillerAmongUs extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}");
|
||||||
|
|
||||||
// When A Killer Among Us enters the battlefield, create a 1/1 white Human creature token, a 1/1 blue Merfolk creature token, and a 1/1 red Goblin creature token. Then secretly choose Human, Merfolk, or Goblin.
|
// When A Killer Among Us enters the battlefield, create a 1/1 white Human creature token, a 1/1 blue Merfolk creature token, and a 1/1 red Goblin creature token. Then secretly choose Human, Merfolk, or Goblin.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanToken()));
|
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanToken()).withAdditionalTokens(new MerfolkToken(), new GoblinToken()));
|
||||||
ability.addEffect(new CreateTokenEffect(new MerfolkToken())
|
|
||||||
.setText(", a 1/1 blue Merfolk creature token"));
|
|
||||||
ability.addEffect(new CreateTokenEffect(new GoblinToken())
|
|
||||||
.setText(", and a 1/1 red Goblin creature token."));
|
|
||||||
ability.addEffect(new ChooseHumanMerfolkOrGoblinEffect());
|
ability.addEffect(new ChooseHumanMerfolkOrGoblinEffect());
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,7 @@ public final class BestialMenace extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}");
|
||||||
|
|
||||||
// Create a 1/1 green Snake creature token, a 2/2 green Wolf creature token, and a 3/3 green Elephant creature token.
|
// Create a 1/1 green Snake creature token, a 2/2 green Wolf creature token, and a 3/3 green Elephant creature token.
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken()));
|
this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken()).withAdditionalTokens(new WolfToken(), new ElephantToken()));
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new WolfToken()).setText(", a 2/2 green Wolf creature token"));
|
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new ElephantToken()).setText(", and a 3/3 green Elephant creature token"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BestialMenace(final BestialMenace card) {
|
private BestialMenace(final BestialMenace card) {
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,7 @@ public final class DevouringSugarmaw extends AdventureCard {
|
||||||
|
|
||||||
// Have for Dinner
|
// Have for Dinner
|
||||||
// Create a 1/1 white Human creature token and a Food token.
|
// Create a 1/1 white Human creature token and a Food token.
|
||||||
this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new HumanToken()));
|
this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new HumanToken()).withAdditionalTokens(new FoodToken()));
|
||||||
this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new FoodToken())
|
|
||||||
.setText("and a Food token"));
|
|
||||||
|
|
||||||
this.finalizeAdventure();
|
this.finalizeAdventure();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,14 @@ public final class FaeOffering extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
||||||
|
|
||||||
// At the beginning of each end step, if you've cast both a creature spell and a noncreature spell this turn, create a Clue token, a Food token, and a Treasure token.
|
// At the beginning of each end step, if you've cast both a creature spell and a noncreature spell this turn, create a Clue token, a Food token, and a Treasure token.
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||||
new BeginningOfEndStepTriggeredAbility(
|
new BeginningOfEndStepTriggeredAbility(
|
||||||
new CreateTokenEffect(new ClueArtifactToken()), TargetController.ANY, false
|
new CreateTokenEffect(new ClueArtifactToken()).withAdditionalTokens(new FoodToken(), new TreasureToken()),
|
||||||
|
TargetController.ANY, false
|
||||||
), FaeOfferingCondition.instance, "At the beginning of each end step, " +
|
), FaeOfferingCondition.instance, "At the beginning of each end step, " +
|
||||||
"if you've cast both a creature spell and a noncreature spell this turn, " +
|
"if you've cast both a creature spell and a noncreature spell this turn, " +
|
||||||
"create a Clue token, a Food token, and a Treasure token."
|
"create a Clue token, a Food token, and a Treasure token."
|
||||||
);
|
).addHint(FaeOfferingHint.instance));
|
||||||
ability.addEffect(new CreateTokenEffect(new FoodToken()));
|
|
||||||
ability.addEffect(new CreateTokenEffect(new TreasureToken()));
|
|
||||||
this.addAbility(ability.addHint(FaeOfferingHint.instance));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FaeOffering(final FaeOffering card) {
|
private FaeOffering(final FaeOffering card) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.f;
|
package mage.cards.f;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.TriggeredAbility;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
|
@ -31,15 +30,8 @@ public final class FarmerCotton extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// When Farmer Cotton enters the battlefield, create X 1/1 white Halfling creature tokens and X Food tokens.
|
// When Farmer Cotton enters the battlefield, create X 1/1 white Halfling creature tokens and X Food tokens.
|
||||||
TriggeredAbility trigger = new EntersBattlefieldTriggeredAbility(
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HalflingToken(),
|
||||||
new CreateTokenEffect(new HalflingToken(), GetXValue.instance)
|
GetXValue.instance).withAdditionalTokens(new FoodToken())));
|
||||||
);
|
|
||||||
trigger.addEffect(
|
|
||||||
new CreateTokenEffect(new FoodToken(), GetXValue.instance)
|
|
||||||
.setText("and X Food tokens")
|
|
||||||
);
|
|
||||||
|
|
||||||
this.addAbility(trigger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FarmerCotton(final FarmerCotton card) {
|
private FarmerCotton(final FarmerCotton card) {
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ public final class ForbiddenFriendship extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}");
|
||||||
|
|
||||||
// Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.
|
// Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new DinosaurHasteToken()));
|
this.getSpellAbility().addEffect(new CreateTokenEffect(new DinosaurHasteToken()).withAdditionalTokens(new HumanSoldierToken()));
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new HumanSoldierToken())
|
|
||||||
.setText("and a 1/1 white Human Soldier creature token"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ForbiddenFriendship(final ForbiddenFriendship card) {
|
private ForbiddenFriendship(final ForbiddenFriendship card) {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public final class FrostFairLureFish extends CardImpl {
|
||||||
// When Frost Fair Lure Fish enters the battlefield, create two 1/1 blue Fish creature tokens and two tapped Treasure tokens.
|
// When Frost Fair Lure Fish enters the battlefield, create two 1/1 blue Fish creature tokens and two tapped Treasure tokens.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FishNoAbilityToken(), 2));
|
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FishNoAbilityToken(), 2));
|
||||||
ability.addEffect(new CreateTokenEffect(new TreasureToken(), 2, true)
|
ability.addEffect(new CreateTokenEffect(new TreasureToken(), 2, true)
|
||||||
.setText("and two tapped Treasure tokens"));
|
.setText("and create two tapped Treasure tokens"));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
// Fish you control have haste and can't be blocked by Humans.
|
// Fish you control have haste and can't be blocked by Humans.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.l;
|
package mage.cards.l;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
|
@ -64,12 +63,13 @@ class LiberatedLivestockEffect extends OneShotEffect {
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<Token> tokens = Arrays.asList(new CatToken2(), new BirdToken(), new OxToken());
|
Token firstToken = new CatToken2();
|
||||||
tokens.forEach(token -> token.putOntoBattlefield(1, game, source, source.getControllerId()));
|
firstToken.putOntoBattlefield(1, game, source, source.getControllerId(),
|
||||||
|
false, false, null, null, true,
|
||||||
|
Arrays.asList(firstToken, new BirdToken(), new OxToken()));
|
||||||
game.processAction();
|
game.processAction();
|
||||||
|
|
||||||
for (Token token : tokens) {
|
for (UUID tokenId : firstToken.getLastAddedTokenIds()) {
|
||||||
for (UUID tokenId : token.getLastAddedTokenIds()) {
|
|
||||||
Permanent tokenPermanent = game.getPermanent(tokenId);
|
Permanent tokenPermanent = game.getPermanent(tokenId);
|
||||||
if (tokenPermanent == null) {
|
if (tokenPermanent == null) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -93,7 +93,6 @@ class LiberatedLivestockEffect extends OneShotEffect {
|
||||||
tokenPermanent.addAttachment(auraCard.getId(), source, game);
|
tokenPermanent.addAttachment(auraCard.getId(), source, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.m;
|
package mage.cards.m;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.TriggeredAbility;
|
|
||||||
import mage.abilities.common.DealtDamageAndDiedTriggeredAbility;
|
import mage.abilities.common.DealtDamageAndDiedTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
|
@ -36,9 +35,7 @@ public final class MadameVastra extends CardImpl {
|
||||||
this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield)));
|
this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield)));
|
||||||
|
|
||||||
// Whenever a creature dealt damage by Madame Vastra this turn dies, create a Clue token and a Food token.
|
// Whenever a creature dealt damage by Madame Vastra this turn dies, create a Clue token and a Food token.
|
||||||
TriggeredAbility trigger = new DealtDamageAndDiedTriggeredAbility(new CreateTokenEffect(new ClueArtifactToken()));
|
this.addAbility(new DealtDamageAndDiedTriggeredAbility(new CreateTokenEffect(new ClueArtifactToken()).withAdditionalTokens(new FoodToken())));
|
||||||
trigger.addEffect(new CreateTokenEffect(new FoodToken()).setText("and a Food token"));
|
|
||||||
this.addAbility(trigger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MadameVastra(final MadameVastra card) {
|
private MadameVastra(final MadameVastra card) {
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,7 @@ public final class MascotExhibition extends CardImpl {
|
||||||
this.subtype.add(SubType.LESSON);
|
this.subtype.add(SubType.LESSON);
|
||||||
|
|
||||||
// Create a 2/1 white and black Inkling creature token with flying, a 3/2 red and white Spirit creature token, and a 4/4 blue and red Elemental creature token.
|
// Create a 2/1 white and black Inkling creature token with flying, a 3/2 red and white Spirit creature token, and a 4/4 blue and red Elemental creature token.
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new InklingToken()));
|
this.getSpellAbility().addEffect(new CreateTokenEffect(new InklingToken()).withAdditionalTokens(new Spirit32Token(), new Elemental44Token()));
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new Spirit32Token())
|
|
||||||
.setText(", a 3/2 red and white Spirit creature token"));
|
|
||||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new Elemental44Token())
|
|
||||||
.setText(", and a 4/4 blue and red Elemental creature token"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MascotExhibition(final MascotExhibition card) {
|
private MascotExhibition(final MascotExhibition card) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
|
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.abilities.keyword.LifelinkAbility;
|
import mage.abilities.keyword.LifelinkAbility;
|
||||||
|
|
@ -50,12 +49,9 @@ public final class PolukranosEngineOfRuin extends CardImpl {
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// Whenever Polukranos, Engine of Ruin or another nontoken Hydra you control dies, create a 3/3 green and white Phyrexian Hydra creature token with reach and a 3/3 green and white Phyrexian Hydra creature token with lifelink.
|
// Whenever Polukranos, Engine of Ruin or another nontoken Hydra you control dies, create a 3/3 green and white Phyrexian Hydra creature token with reach and a 3/3 green and white Phyrexian Hydra creature token with lifelink.
|
||||||
Ability ability = new DiesThisOrAnotherTriggeredAbility(
|
this.addAbility(new DiesThisOrAnotherTriggeredAbility(
|
||||||
new CreateTokenEffect(new PhyrexianHydraWithReachToken()), false, filter
|
new CreateTokenEffect(new PhyrexianHydraWithReachToken()).withAdditionalTokens(new PhyrexianHydraWithLifelinkToken()), false, filter
|
||||||
);
|
));
|
||||||
ability.addEffect(new CreateTokenEffect(new PhyrexianHydraWithLifelinkToken())
|
|
||||||
.setText("and a 3/3 green and white Phyrexian Hydra creature token with lifelink"));
|
|
||||||
this.addAbility(ability);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PolukranosEngineOfRuin(final PolukranosEngineOfRuin card) {
|
private PolukranosEngineOfRuin(final PolukranosEngineOfRuin card) {
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,9 @@ public final class ReckonerBankbuster extends CardImpl {
|
||||||
new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
|
new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
|
||||||
);
|
);
|
||||||
ability.addEffect(new ConditionalOneShotEffect(
|
ability.addEffect(new ConditionalOneShotEffect(
|
||||||
new CreateTokenEffect(new TreasureToken()), condition,
|
new CreateTokenEffect(new TreasureToken()).withAdditionalTokens(new PilotToken()), condition,
|
||||||
"Then if there are no charge counters on {this}, create a Treasure token"
|
"Then if there are no charge counters on {this}, create a Treasure token and a 1/1 colorless " +
|
||||||
));
|
"Pilot creature token with \"This creature crews Vehicles as though its power were 2 greater.\""
|
||||||
ability.addEffect(new ConditionalOneShotEffect(
|
|
||||||
new CreateTokenEffect(new PilotToken()), condition, "and a 1/1 colorless Pilot creature token " +
|
|
||||||
"with \"This creature crews Vehicles as though its power were 2 greater.\""
|
|
||||||
));
|
));
|
||||||
ability.addCost(new TapSourceCost());
|
ability.addCost(new TapSourceCost());
|
||||||
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
|
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.s;
|
package mage.cards.s;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
|
@ -33,10 +32,7 @@ public final class SomberwaldBeastmaster extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// When Somberwald Beastmaster enters the battlefield, create a 2/2 green Wolf creature token, a 3/3 green Beast creature token, and a 4/4 green Beast creature token.
|
// When Somberwald Beastmaster enters the battlefield, create a 2/2 green Wolf creature token, a 3/3 green Beast creature token, and a 4/4 green Beast creature token.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WolfToken()));
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WolfToken()).withAdditionalTokens(new BeastToken(), new BeastToken2())));
|
||||||
ability.addEffect(new CreateTokenEffect(new BeastToken()).setText(", a 3/3 green Beast creature token"));
|
|
||||||
ability.addEffect(new CreateTokenEffect(new BeastToken2()).setText(", and a 4/4 green Beast creature token"));
|
|
||||||
this.addAbility(ability);
|
|
||||||
|
|
||||||
// Creature tokens you control have deathtouch.
|
// Creature tokens you control have deathtouch.
|
||||||
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
|
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,7 @@ public final class SongOfEarendil extends CardImpl {
|
||||||
// II-- Create a Treasure token and a 2/2 blue Bird creature token with flying.
|
// II-- Create a Treasure token and a 2/2 blue Bird creature token with flying.
|
||||||
sagaAbility.addChapterEffect(
|
sagaAbility.addChapterEffect(
|
||||||
this, SagaChapter.CHAPTER_II,
|
this, SagaChapter.CHAPTER_II,
|
||||||
new CreateTokenEffect(new TreasureToken()),
|
new CreateTokenEffect(new TreasureToken()).withAdditionalTokens(new SwanSongBirdToken())
|
||||||
new CreateTokenEffect(new SwanSongBirdToken())
|
|
||||||
.setText("and a 2/2 blue Bird creature token with flying")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// III-- Put a flying counter on each creature you control without flying.
|
// III-- Put a flying counter on each creature you control without flying.
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,10 @@ public final class SpecimenCollector extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// When Specimen Collector enters the battlefield, create a 1/1 green Squirrel creature token and a 0/3 blue Crab creature token.
|
// When Specimen Collector enters the battlefield, create a 1/1 green Squirrel creature token and a 0/3 blue Crab creature token.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SquirrelToken()));
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SquirrelToken()).withAdditionalTokens(new CrabToken())));
|
||||||
ability.addEffect(new CreateTokenEffect(new CrabToken()).setText("and a 0/3 blue Crab creature token"));
|
|
||||||
this.addAbility(ability);
|
|
||||||
|
|
||||||
// When Specimen Collector dies, create a token that's a copy of target token you control.
|
// When Specimen Collector dies, create a token that's a copy of target token you control.
|
||||||
ability = new DiesSourceTriggeredAbility(new CreateTokenCopyTargetEffect());
|
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenCopyTargetEffect());
|
||||||
ability.addTarget(new TargetPermanent(filter));
|
ability.addTarget(new TargetPermanent(filter));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,7 @@ public final class TheEleventhHour extends CardImpl {
|
||||||
// II -- Create a Food token and a 1/1 white Human creature token with "Doctor spells you cast cost 1 less to cast."
|
// II -- Create a Food token and a 1/1 white Human creature token with "Doctor spells you cast cost 1 less to cast."
|
||||||
sagaAbility.addChapterEffect(
|
sagaAbility.addChapterEffect(
|
||||||
this, SagaChapter.CHAPTER_II,
|
this, SagaChapter.CHAPTER_II,
|
||||||
new CreateTokenEffect(new FoodToken()),
|
new CreateTokenEffect(new FoodToken()).withAdditionalTokens(new TheEleventhHourToken())
|
||||||
new CreateTokenEffect(new TheEleventhHourToken())
|
|
||||||
.setText("and a 1/1 white Human creature token with " +
|
|
||||||
"\"Doctor spells you cast cost {1} less to cast.\"")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// III -- Create a token that's a copy of target creature, except it's a legendary Alien named Prisoner Zero.
|
// III -- Create a token that's a copy of target creature, except it's a legendary Alien named Prisoner Zero.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.t;
|
package mage.cards.t;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.DiesSourceTriggeredAbility;
|
import mage.abilities.common.DiesSourceTriggeredAbility;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
|
@ -39,12 +38,7 @@ public final class TriplicateTitan extends CardImpl {
|
||||||
this.addAbility(TrampleAbility.getInstance());
|
this.addAbility(TrampleAbility.getInstance());
|
||||||
|
|
||||||
// When Triplicate Titan dies, create a 3/3 colorless Golem artifact creature token with flying, a 3/3 colorless Golem artifact creature token with vigilance, and a 3/3 colorless Golem artifact creature token with trample.
|
// When Triplicate Titan dies, create a 3/3 colorless Golem artifact creature token with flying, a 3/3 colorless Golem artifact creature token with vigilance, and a 3/3 colorless Golem artifact creature token with trample.
|
||||||
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new GolemFlyingToken()));
|
this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new GolemFlyingToken()).withAdditionalTokens(new GolemVigilanceToken(), new GolemTrampleToken())));
|
||||||
ability.addEffect(new CreateTokenEffect(new GolemVigilanceToken())
|
|
||||||
.setText(", a 3/3 colorless Golem artifact creature token with vigilance"));
|
|
||||||
ability.addEffect(new CreateTokenEffect(new GolemTrampleToken())
|
|
||||||
.setText(", and a 3/3 colorless Golem artifact creature token with trample"));
|
|
||||||
this.addAbility(ability);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TriplicateTitan(final TriplicateTitan card) {
|
private TriplicateTitan(final TriplicateTitan card) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package mage.cards.t;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -29,10 +28,7 @@ public final class TrostanisSummoner extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// When Trostani's Summoner enters the battlefield, create a 2/2 white Knight creature token with vigilance, a 3/3 green Centaur creature token, and a 4/4 green Rhino creature token with trample.
|
// When Trostani's Summoner enters the battlefield, create a 2/2 white Knight creature token with vigilance, a 3/3 green Centaur creature token, and a 4/4 green Rhino creature token with trample.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken()));
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken()).withAdditionalTokens(new CentaurToken(), new RhinoToken())));
|
||||||
ability.addEffect(new CreateTokenEffect(new CentaurToken()).setText(", a 3/3 green Centaur creature token"));
|
|
||||||
ability.addEffect(new CreateTokenEffect(new RhinoToken()).setText(", and a 4/4 green Rhino creature token with trample"));
|
|
||||||
this.addAbility(ability);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,7 @@ public final class Vault101BirthdayParty extends CardImpl {
|
||||||
|
|
||||||
// I -- Create a 1/1 white Human Soldier creature token and a Food token.
|
// I -- Create a 1/1 white Human Soldier creature token and a Food token.
|
||||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I,
|
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I,
|
||||||
new CreateTokenEffect(new HumanSoldierToken()),
|
new CreateTokenEffect(new HumanSoldierToken()).withAdditionalTokens(new FoodToken()));
|
||||||
new CreateTokenEffect(new FoodToken()).setText("and a Food token"));
|
|
||||||
|
|
||||||
// II, III -- You may put an Aura or Equipment card from your hand or graveyard onto the battlefield.
|
// II, III -- You may put an Aura or Equipment card from your hand or graveyard onto the battlefield.
|
||||||
// If an Equipment is put onto the battlefield this way, you may attach it to a creature you control.
|
// If an Equipment is put onto the battlefield this way, you may attach it to a creature you control.
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,7 @@ public final class WurmcoilEngine extends CardImpl {
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
|
// When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
|
||||||
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false);
|
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()).withAdditionalTokens(new WurmWithLifelinkToken()), false);
|
||||||
ability.addEffect(new CreateTokenEffect(new WurmWithLifelinkToken()).setText("and a 3/3 colorless Phyrexian Wurm artifact creature token with lifelink"));
|
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,7 @@ public final class WurmcoilLarva extends CardImpl {
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// When Wurmcoil Larva dies, create a 1/2 black Phyrexian Wurm artifact creature token with deathtouch and a 2/1 black Phyrexian Wurm artifact creature token with lifelink.
|
// When Wurmcoil Larva dies, create a 1/2 black Phyrexian Wurm artifact creature token with deathtouch and a 2/1 black Phyrexian Wurm artifact creature token with lifelink.
|
||||||
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new PhyrexianWurm12DeathtouchToken()), false);
|
Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new PhyrexianWurm12DeathtouchToken()).withAdditionalTokens(new PhyrexianWurm21LifelinkToken()), false);
|
||||||
ability.addEffect(new CreateTokenEffect(new PhyrexianWurm21LifelinkToken())
|
|
||||||
.setText("and a 2/1 black Phyrexian Wurm artifact creature token with lifelink"));
|
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ public class KambalProfiteeringMayorTest extends CardTestPlayerBase {
|
||||||
assertPermanentCount(playerA, "Wolf Token", 1);
|
assertPermanentCount(playerA, "Wolf Token", 1);
|
||||||
assertPermanentCount(playerA, "Elephant Token", 1);
|
assertPermanentCount(playerA, "Elephant Token", 1);
|
||||||
assertPermanentCount(playerB, "Snake Token", 1);
|
assertPermanentCount(playerB, "Snake Token", 1);
|
||||||
assertPermanentCount(playerB, "Wolf Token", 0); // TODO: this is a bug, should be 1, see #10811
|
assertPermanentCount(playerB, "Wolf Token", 1);
|
||||||
assertPermanentCount(playerB, "Elephant Token", 0); // TODO: this is a bug, should be 1, see #10811
|
assertPermanentCount(playerB, "Elephant Token", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import mage.target.targetpointer.FixedTarget;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -24,7 +25,7 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public class CreateTokenEffect extends OneShotEffect {
|
public class CreateTokenEffect extends OneShotEffect {
|
||||||
|
|
||||||
private final Token token;
|
private final List<Token> tokens = new ArrayList<>();
|
||||||
private final DynamicValue amount;
|
private final DynamicValue amount;
|
||||||
private final boolean tapped;
|
private final boolean tapped;
|
||||||
private final boolean attacking;
|
private final boolean attacking;
|
||||||
|
|
@ -56,7 +57,10 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
|
|
||||||
public CreateTokenEffect(Token token, DynamicValue amount, boolean tapped, boolean attacking) {
|
public CreateTokenEffect(Token token, DynamicValue amount, boolean tapped, boolean attacking) {
|
||||||
super(Outcome.PutCreatureInPlay);
|
super(Outcome.PutCreatureInPlay);
|
||||||
this.token = token;
|
if (token == null) {
|
||||||
|
throw new IllegalArgumentException("Wrong code usage. Token provided to CreateTokenEffect must not be null.");
|
||||||
|
}
|
||||||
|
this.tokens.add(token);
|
||||||
this.amount = amount.copy();
|
this.amount = amount.copy();
|
||||||
this.tapped = tapped;
|
this.tapped = tapped;
|
||||||
this.attacking = attacking;
|
this.attacking = attacking;
|
||||||
|
|
@ -66,7 +70,9 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
protected CreateTokenEffect(final CreateTokenEffect effect) {
|
protected CreateTokenEffect(final CreateTokenEffect effect) {
|
||||||
super(effect);
|
super(effect);
|
||||||
this.amount = effect.amount.copy();
|
this.amount = effect.amount.copy();
|
||||||
this.token = effect.token.copy();
|
for (Token token : effect.tokens) {
|
||||||
|
this.tokens.add(token.copy());
|
||||||
|
}
|
||||||
this.tapped = effect.tapped;
|
this.tapped = effect.tapped;
|
||||||
this.attacking = effect.attacking;
|
this.attacking = effect.attacking;
|
||||||
this.lastAddedTokenIds.addAll(effect.lastAddedTokenIds);
|
this.lastAddedTokenIds.addAll(effect.lastAddedTokenIds);
|
||||||
|
|
@ -82,6 +88,11 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CreateTokenEffect withAdditionalTokens(Token... tokens) {
|
||||||
|
this.tokens.addAll(Arrays.asList(tokens));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CreateTokenEffect copy() {
|
public CreateTokenEffect copy() {
|
||||||
return new CreateTokenEffect(this);
|
return new CreateTokenEffect(this);
|
||||||
|
|
@ -90,8 +101,8 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
int value = amount.calculate(game, source, this);
|
int value = amount.calculate(game, source, this);
|
||||||
token.putOntoBattlefield(value, game, source, source.getControllerId(), tapped, attacking);
|
tokens.get(0).putOntoBattlefield(value, game, source, source.getControllerId(), tapped, attacking, null, null, true, tokens);
|
||||||
this.lastAddedTokenIds = token.getLastAddedTokenIds();
|
this.lastAddedTokenIds = tokens.get(0).getLastAddedTokenIds();
|
||||||
// TODO: Workaround to add counters to all created tokens, necessary for correct interactions with cards like Chatterfang, Squirrel General and Ochre Jelly / Printlifter Ooze. See #10786
|
// TODO: Workaround to add counters to all created tokens, necessary for correct interactions with cards like Chatterfang, Squirrel General and Ochre Jelly / Printlifter Ooze. See #10786
|
||||||
if (counterType != null) {
|
if (counterType != null) {
|
||||||
for (UUID tokenId : lastAddedTokenIds) {
|
for (UUID tokenId : lastAddedTokenIds) {
|
||||||
|
|
@ -150,17 +161,28 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setText() {
|
private void setText() {
|
||||||
String tokenDescription = token.getDescription();
|
|
||||||
boolean singular = amount.toString().equals("1");
|
boolean singular = amount.toString().equals("1");
|
||||||
|
StringBuilder sb = new StringBuilder("create ");
|
||||||
|
for (int i = 0; i < tokens.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (tokens.size() > 2) {
|
||||||
|
sb.append(", ");
|
||||||
|
} else {
|
||||||
|
sb.append(" ");
|
||||||
|
}
|
||||||
|
if (i+1 == tokens.size()) {
|
||||||
|
sb.append("and ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String tokenDescription = tokens.get(i).getDescription();
|
||||||
if (tokenDescription.contains(", a legendary")) {
|
if (tokenDescription.contains(", a legendary")) {
|
||||||
staticText = "create " + tokenDescription;
|
sb.append(tokenDescription);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (oldPhrasing) {
|
if (oldPhrasing) {
|
||||||
tokenDescription = tokenDescription.replace("token with \"",
|
tokenDescription = tokenDescription.replace("token with \"",
|
||||||
singular ? "token. It has \"" : "tokens. They have \"");
|
singular ? "token. It has \"" : "tokens. They have \"");
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder("create ");
|
|
||||||
if (singular) {
|
if (singular) {
|
||||||
if (tapped && !attacking) {
|
if (tapped && !attacking) {
|
||||||
sb.append("a tapped ");
|
sb.append("a tapped ");
|
||||||
|
|
@ -182,9 +204,10 @@ public class CreateTokenEffect extends OneShotEffect {
|
||||||
sb.replace(tokenLocation, tokenLocation + 6, "tokens ");
|
sb.replace(tokenLocation, tokenLocation + 6, "tokens ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attacking) {
|
if (attacking) {
|
||||||
if (singular) {
|
if (singular && tokens.size() == 1) {
|
||||||
sb.append(" that's");
|
sb.append(" that's");
|
||||||
} else {
|
} else {
|
||||||
sb.append(" that are");
|
sb.append(" that are");
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
||||||
import mage.game.permanent.token.Token;
|
import mage.game.permanent.token.Token;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -17,12 +18,16 @@ public class CreateTokenEvent extends GameEvent {
|
||||||
* @param source
|
* @param source
|
||||||
* @param controllerId
|
* @param controllerId
|
||||||
* @param amount
|
* @param amount
|
||||||
* @param token
|
* @param tokensList
|
||||||
*/
|
*/
|
||||||
public CreateTokenEvent(Ability source, UUID controllerId, int amount, Token token) {
|
public CreateTokenEvent(Ability source, UUID controllerId, int amount, List<Token> tokensList) {
|
||||||
super(GameEvent.EventType.CREATE_TOKEN, null, source, controllerId, amount, false);
|
super(GameEvent.EventType.CREATE_TOKEN, null, source, controllerId, amount, false);
|
||||||
|
if (tokensList != null) {
|
||||||
|
for (Token token : tokensList) {
|
||||||
tokens.put(token, amount);
|
tokens.put(token, amount);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Token, Integer> getTokens() {
|
public Map<Token, Integer> getTokens() {
|
||||||
return tokens;
|
return tokens;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ public interface Token extends MageObject {
|
||||||
|
|
||||||
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created);
|
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created);
|
||||||
|
|
||||||
|
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created, List<Token> additionalTokens);
|
||||||
|
|
||||||
void setPower(int power);
|
void setPower(int power);
|
||||||
|
|
||||||
void setToughness(int toughness);
|
void setToughness(int toughness);
|
||||||
|
|
|
||||||
|
|
@ -218,8 +218,12 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
||||||
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, attachedTo, true);
|
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, attachedTo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created) {
|
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created) {
|
||||||
|
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, attachedTo, created, Collections.singletonList(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created, List<Token> tokens) {
|
||||||
Player controller = game.getPlayer(controllerId);
|
Player controller = game.getPlayer(controllerId);
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -229,7 +233,11 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
||||||
}
|
}
|
||||||
lastAddedTokenIds.clear();
|
lastAddedTokenIds.clear();
|
||||||
|
|
||||||
CreateTokenEvent event = new CreateTokenEvent(source, controllerId, amount, this);
|
if (tokens == null || tokens.get(0) != this) {
|
||||||
|
throw new IllegalArgumentException("Wrong code usage. token.putOntoBattlefield parameter tokens must be initialized to a list of all tokens to be made, with the first element being the token you are calling putOntoBattlefield() on.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateTokenEvent event = new CreateTokenEvent(source, controllerId, amount, tokens);
|
||||||
if (!created || !game.replaceEvent(event)) {
|
if (!created || !game.replaceEvent(event)) {
|
||||||
int currentTokens = game.getBattlefield().countTokens(event.getPlayerId());
|
int currentTokens = game.getBattlefield().countTokens(event.getPlayerId());
|
||||||
int tokenSlots = Math.max(MAX_TOKENS_PER_GAME - currentTokens, 0);
|
int tokenSlots = Math.max(MAX_TOKENS_PER_GAME - currentTokens, 0);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue