diff --git a/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java b/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java index 60bf0ccadbe..9922e9ebc15 100644 --- a/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java +++ b/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java @@ -59,11 +59,7 @@ public final class AKillerAmongUs extends CardImpl { 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. - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanToken())); - 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 ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanToken()).withAdditionalTokens(new MerfolkToken(), new GoblinToken())); ability.addEffect(new ChooseHumanMerfolkOrGoblinEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BestialMenace.java b/Mage.Sets/src/mage/cards/b/BestialMenace.java index 4486ec31b81..bded1309460 100644 --- a/Mage.Sets/src/mage/cards/b/BestialMenace.java +++ b/Mage.Sets/src/mage/cards/b/BestialMenace.java @@ -19,9 +19,7 @@ public final class BestialMenace extends CardImpl { 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. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken())); - 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")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken()).withAdditionalTokens(new WolfToken(), new ElephantToken())); } private BestialMenace(final BestialMenace card) { diff --git a/Mage.Sets/src/mage/cards/d/DevouringSugarmaw.java b/Mage.Sets/src/mage/cards/d/DevouringSugarmaw.java index 85d472f4152..7d05d4df5db 100644 --- a/Mage.Sets/src/mage/cards/d/DevouringSugarmaw.java +++ b/Mage.Sets/src/mage/cards/d/DevouringSugarmaw.java @@ -64,9 +64,7 @@ public final class DevouringSugarmaw extends AdventureCard { // Have for Dinner // 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 FoodToken()) - .setText("and a Food token")); + this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new HumanToken()).withAdditionalTokens(new FoodToken())); this.finalizeAdventure(); } diff --git a/Mage.Sets/src/mage/cards/f/FaeOffering.java b/Mage.Sets/src/mage/cards/f/FaeOffering.java index ee965c8f645..191f6c83998 100644 --- a/Mage.Sets/src/mage/cards/f/FaeOffering.java +++ b/Mage.Sets/src/mage/cards/f/FaeOffering.java @@ -31,16 +31,14 @@ public final class FaeOffering extends CardImpl { 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. - Ability ability = new ConditionalInterveningIfTriggeredAbility( + this.addAbility(new ConditionalInterveningIfTriggeredAbility( 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, " + "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.addEffect(new CreateTokenEffect(new FoodToken())); - ability.addEffect(new CreateTokenEffect(new TreasureToken())); - this.addAbility(ability.addHint(FaeOfferingHint.instance)); + ).addHint(FaeOfferingHint.instance)); } private FaeOffering(final FaeOffering card) { diff --git a/Mage.Sets/src/mage/cards/f/FarmerCotton.java b/Mage.Sets/src/mage/cards/f/FarmerCotton.java index 854a4fe2e1b..ac6ea6e7a7f 100644 --- a/Mage.Sets/src/mage/cards/f/FarmerCotton.java +++ b/Mage.Sets/src/mage/cards/f/FarmerCotton.java @@ -1,7 +1,6 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.CreateTokenEffect; @@ -31,15 +30,8 @@ public final class FarmerCotton extends CardImpl { this.toughness = new MageInt(1); // When Farmer Cotton enters the battlefield, create X 1/1 white Halfling creature tokens and X Food tokens. - TriggeredAbility trigger = new EntersBattlefieldTriggeredAbility( - new CreateTokenEffect(new HalflingToken(), GetXValue.instance) - ); - trigger.addEffect( - new CreateTokenEffect(new FoodToken(), GetXValue.instance) - .setText("and X Food tokens") - ); - - this.addAbility(trigger); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HalflingToken(), + GetXValue.instance).withAdditionalTokens(new FoodToken()))); } private FarmerCotton(final FarmerCotton card) { diff --git a/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java b/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java index e1e0487a661..5414bd288e8 100644 --- a/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java +++ b/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java @@ -18,9 +18,7 @@ public final class ForbiddenFriendship extends CardImpl { 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. - this.getSpellAbility().addEffect(new CreateTokenEffect(new DinosaurHasteToken())); - this.getSpellAbility().addEffect(new CreateTokenEffect(new HumanSoldierToken()) - .setText("and a 1/1 white Human Soldier creature token")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new DinosaurHasteToken()).withAdditionalTokens(new HumanSoldierToken())); } private ForbiddenFriendship(final ForbiddenFriendship card) { diff --git a/Mage.Sets/src/mage/cards/f/FrostFairLureFish.java b/Mage.Sets/src/mage/cards/f/FrostFairLureFish.java index 47e2e014f22..dc53dd8d689 100644 --- a/Mage.Sets/src/mage/cards/f/FrostFairLureFish.java +++ b/Mage.Sets/src/mage/cards/f/FrostFairLureFish.java @@ -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. Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FishNoAbilityToken(), 2)); ability.addEffect(new CreateTokenEffect(new TreasureToken(), 2, true) - .setText("and two tapped Treasure tokens")); + .setText("and create two tapped Treasure tokens")); this.addAbility(ability); // Fish you control have haste and can't be blocked by Humans. diff --git a/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java b/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java index 3958517fa8d..a5d1976e31b 100644 --- a/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java +++ b/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java @@ -1,7 +1,6 @@ package mage.cards.l; import java.util.Arrays; -import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -64,34 +63,34 @@ class LiberatedLivestockEffect extends OneShotEffect { if (controller == null) { return false; } - List tokens = Arrays.asList(new CatToken2(), new BirdToken(), new OxToken()); - tokens.forEach(token -> token.putOntoBattlefield(1, game, source, source.getControllerId())); + Token firstToken = new CatToken2(); + firstToken.putOntoBattlefield(1, game, source, source.getControllerId(), + false, false, null, null, true, + Arrays.asList(firstToken, new BirdToken(), new OxToken())); game.processAction(); - for (Token token : tokens) { - for (UUID tokenId : token.getLastAddedTokenIds()) { - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent == null) { - continue; - } - FilterCard filter = new FilterCard("Aura from your hand or graveyard that can attach to " + tokenPermanent.getName()); - filter.add(SubType.AURA.getPredicate()); - filter.add(new AuraCardCanAttachToPermanentId(tokenPermanent.getId())); - Cards auraCards = new CardsImpl(); - auraCards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); - auraCards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); - if (auraCards.isEmpty()) { - continue; - } - TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); - target.withNotTarget(true); - controller.chooseTarget(outcome, auraCards, target, source, game); - Card auraCard = game.getCard(target.getFirstTarget()); - if (auraCard != null && !tokenPermanent.cantBeAttachedBy(auraCard, source, game, true)) { - game.getState().setValue("attachTo:" + auraCard.getId(), tokenPermanent); - controller.moveCards(auraCard, Zone.BATTLEFIELD, source, game); - tokenPermanent.addAttachment(auraCard.getId(), source, game); - } + for (UUID tokenId : firstToken.getLastAddedTokenIds()) { + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent == null) { + continue; + } + FilterCard filter = new FilterCard("Aura from your hand or graveyard that can attach to " + tokenPermanent.getName()); + filter.add(SubType.AURA.getPredicate()); + filter.add(new AuraCardCanAttachToPermanentId(tokenPermanent.getId())); + Cards auraCards = new CardsImpl(); + auraCards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + auraCards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (auraCards.isEmpty()) { + continue; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); + target.withNotTarget(true); + controller.chooseTarget(outcome, auraCards, target, source, game); + Card auraCard = game.getCard(target.getFirstTarget()); + if (auraCard != null && !tokenPermanent.cantBeAttachedBy(auraCard, source, game, true)) { + game.getState().setValue("attachTo:" + auraCard.getId(), tokenPermanent); + controller.moveCards(auraCard, Zone.BATTLEFIELD, source, game); + tokenPermanent.addAttachment(auraCard.getId(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/m/MadameVastra.java b/Mage.Sets/src/mage/cards/m/MadameVastra.java index 372a8c7e660..4096b9f34d6 100644 --- a/Mage.Sets/src/mage/cards/m/MadameVastra.java +++ b/Mage.Sets/src/mage/cards/m/MadameVastra.java @@ -1,7 +1,6 @@ package mage.cards.m; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -36,9 +35,7 @@ public final class MadameVastra extends CardImpl { 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. - TriggeredAbility trigger = new DealtDamageAndDiedTriggeredAbility(new CreateTokenEffect(new ClueArtifactToken())); - trigger.addEffect(new CreateTokenEffect(new FoodToken()).setText("and a Food token")); - this.addAbility(trigger); + this.addAbility(new DealtDamageAndDiedTriggeredAbility(new CreateTokenEffect(new ClueArtifactToken()).withAdditionalTokens(new FoodToken()))); } private MadameVastra(final MadameVastra card) { diff --git a/Mage.Sets/src/mage/cards/m/MascotExhibition.java b/Mage.Sets/src/mage/cards/m/MascotExhibition.java index 61582afee5b..88b232de4be 100644 --- a/Mage.Sets/src/mage/cards/m/MascotExhibition.java +++ b/Mage.Sets/src/mage/cards/m/MascotExhibition.java @@ -22,11 +22,7 @@ public final class MascotExhibition extends CardImpl { 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. - this.getSpellAbility().addEffect(new CreateTokenEffect(new InklingToken())); - 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")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new InklingToken()).withAdditionalTokens(new Spirit32Token(), new Elemental44Token())); } private MascotExhibition(final MascotExhibition card) { diff --git a/Mage.Sets/src/mage/cards/p/PolukranosEngineOfRuin.java b/Mage.Sets/src/mage/cards/p/PolukranosEngineOfRuin.java index 8b92c0f466c..a1489a1f9cf 100644 --- a/Mage.Sets/src/mage/cards/p/PolukranosEngineOfRuin.java +++ b/Mage.Sets/src/mage/cards/p/PolukranosEngineOfRuin.java @@ -1,7 +1,6 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.LifelinkAbility; @@ -50,12 +49,9 @@ public final class PolukranosEngineOfRuin extends CardImpl { 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. - Ability ability = new DiesThisOrAnotherTriggeredAbility( - new CreateTokenEffect(new PhyrexianHydraWithReachToken()), 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); + this.addAbility(new DiesThisOrAnotherTriggeredAbility( + new CreateTokenEffect(new PhyrexianHydraWithReachToken()).withAdditionalTokens(new PhyrexianHydraWithLifelinkToken()), false, filter + )); } private PolukranosEngineOfRuin(final PolukranosEngineOfRuin card) { diff --git a/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java index 77bf08713c6..2e0fa124374 100644 --- a/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java +++ b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java @@ -48,12 +48,9 @@ public final class ReckonerBankbuster extends CardImpl { new DrawCardSourceControllerEffect(1), new GenericManaCost(2) ); ability.addEffect(new ConditionalOneShotEffect( - new CreateTokenEffect(new TreasureToken()), condition, - "Then if there are no charge counters on {this}, create a Treasure token" - )); - 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.\"" + new CreateTokenEffect(new TreasureToken()).withAdditionalTokens(new PilotToken()), condition, + "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.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); diff --git a/Mage.Sets/src/mage/cards/s/SomberwaldBeastmaster.java b/Mage.Sets/src/mage/cards/s/SomberwaldBeastmaster.java index f9e1d8fb970..8a6d63343e3 100644 --- a/Mage.Sets/src/mage/cards/s/SomberwaldBeastmaster.java +++ b/Mage.Sets/src/mage/cards/s/SomberwaldBeastmaster.java @@ -1,7 +1,6 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -33,10 +32,7 @@ public final class SomberwaldBeastmaster extends CardImpl { 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. - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WolfToken())); - 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); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WolfToken()).withAdditionalTokens(new BeastToken(), new BeastToken2()))); // Creature tokens you control have deathtouch. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/s/SongOfEarendil.java b/Mage.Sets/src/mage/cards/s/SongOfEarendil.java index ee4bd8b1f4e..8487e4d0468 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfEarendil.java +++ b/Mage.Sets/src/mage/cards/s/SongOfEarendil.java @@ -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. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_II, - new CreateTokenEffect(new TreasureToken()), - new CreateTokenEffect(new SwanSongBirdToken()) - .setText("and a 2/2 blue Bird creature token with flying") + new CreateTokenEffect(new TreasureToken()).withAdditionalTokens(new SwanSongBirdToken()) ); // III-- Put a flying counter on each creature you control without flying. diff --git a/Mage.Sets/src/mage/cards/s/SpecimenCollector.java b/Mage.Sets/src/mage/cards/s/SpecimenCollector.java index 13c7b3affd5..c0ef77ce61b 100644 --- a/Mage.Sets/src/mage/cards/s/SpecimenCollector.java +++ b/Mage.Sets/src/mage/cards/s/SpecimenCollector.java @@ -39,12 +39,10 @@ public final class SpecimenCollector extends CardImpl { 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. - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SquirrelToken())); - ability.addEffect(new CreateTokenEffect(new CrabToken()).setText("and a 0/3 blue Crab creature token")); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SquirrelToken()).withAdditionalTokens(new CrabToken()))); // 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)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TheEleventhHour.java b/Mage.Sets/src/mage/cards/t/TheEleventhHour.java index 27fecb4274f..e37d9f5a82c 100644 --- a/Mage.Sets/src/mage/cards/t/TheEleventhHour.java +++ b/Mage.Sets/src/mage/cards/t/TheEleventhHour.java @@ -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." sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_II, - new CreateTokenEffect(new FoodToken()), - new CreateTokenEffect(new TheEleventhHourToken()) - .setText("and a 1/1 white Human creature token with " + - "\"Doctor spells you cast cost {1} less to cast.\"") + new CreateTokenEffect(new FoodToken()).withAdditionalTokens(new TheEleventhHourToken()) ); // III -- Create a token that's a copy of target creature, except it's a legendary Alien named Prisoner Zero. diff --git a/Mage.Sets/src/mage/cards/t/TriplicateTitan.java b/Mage.Sets/src/mage/cards/t/TriplicateTitan.java index 67127ce5976..5ef0af77377 100644 --- a/Mage.Sets/src/mage/cards/t/TriplicateTitan.java +++ b/Mage.Sets/src/mage/cards/t/TriplicateTitan.java @@ -1,7 +1,6 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlyingAbility; @@ -39,12 +38,7 @@ public final class TriplicateTitan extends CardImpl { 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. - Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new GolemFlyingToken())); - 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); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new GolemFlyingToken()).withAdditionalTokens(new GolemVigilanceToken(), new GolemTrampleToken()))); } private TriplicateTitan(final TriplicateTitan card) { diff --git a/Mage.Sets/src/mage/cards/t/TrostanisSummoner.java b/Mage.Sets/src/mage/cards/t/TrostanisSummoner.java index 0dfd2b28d5c..86d180cb69e 100644 --- a/Mage.Sets/src/mage/cards/t/TrostanisSummoner.java +++ b/Mage.Sets/src/mage/cards/t/TrostanisSummoner.java @@ -3,7 +3,6 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; @@ -29,10 +28,7 @@ public final class TrostanisSummoner extends CardImpl { 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. - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken())); - 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); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken()).withAdditionalTokens(new CentaurToken(), new RhinoToken()))); } diff --git a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java index 3f6f1e4ba4b..782e74392f4 100644 --- a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java +++ b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java @@ -35,8 +35,7 @@ public final class Vault101BirthdayParty extends CardImpl { // I -- Create a 1/1 white Human Soldier creature token and a Food token. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, - new CreateTokenEffect(new HumanSoldierToken()), - new CreateTokenEffect(new FoodToken()).setText("and a Food token")); + new CreateTokenEffect(new HumanSoldierToken()).withAdditionalTokens(new FoodToken())); // 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. diff --git a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java index 1fb322332da..3b204f3c767 100644 --- a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java +++ b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java @@ -32,8 +32,7 @@ public final class WurmcoilEngine extends CardImpl { 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. - Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false); - ability.addEffect(new CreateTokenEffect(new WurmWithLifelinkToken()).setText("and a 3/3 colorless Phyrexian Wurm artifact creature token with lifelink")); + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()).withAdditionalTokens(new WurmWithLifelinkToken()), false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WurmcoilLarva.java b/Mage.Sets/src/mage/cards/w/WurmcoilLarva.java index 9fbb4b81168..c5fb9b3c162 100644 --- a/Mage.Sets/src/mage/cards/w/WurmcoilLarva.java +++ b/Mage.Sets/src/mage/cards/w/WurmcoilLarva.java @@ -35,9 +35,7 @@ public final class WurmcoilLarva extends CardImpl { 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. - Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new PhyrexianWurm12DeathtouchToken()), false); - ability.addEffect(new CreateTokenEffect(new PhyrexianWurm21LifelinkToken()) - .setText("and a 2/1 black Phyrexian Wurm artifact creature token with lifelink")); + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new PhyrexianWurm12DeathtouchToken()).withAdditionalTokens(new PhyrexianWurm21LifelinkToken()), false); this.addAbility(ability); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/otj/KambalProfiteeringMayorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/otj/KambalProfiteeringMayorTest.java index 5866e8a215f..8bee483ea73 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/otj/KambalProfiteeringMayorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/otj/KambalProfiteeringMayorTest.java @@ -59,8 +59,8 @@ public class KambalProfiteeringMayorTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Wolf Token", 1); assertPermanentCount(playerA, "Elephant Token", 1); assertPermanentCount(playerB, "Snake Token", 1); - assertPermanentCount(playerB, "Wolf Token", 0); // TODO: this is a bug, should be 1, see #10811 - assertPermanentCount(playerB, "Elephant Token", 0); // TODO: this is a bug, should be 1, see #10811 + assertPermanentCount(playerB, "Wolf Token", 1); + assertPermanentCount(playerB, "Elephant Token", 1); } @Test diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java index ae1dc01eea6..e10190dd4c0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java @@ -16,6 +16,7 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -24,7 +25,7 @@ import java.util.UUID; */ public class CreateTokenEffect extends OneShotEffect { - private final Token token; + private final List tokens = new ArrayList<>(); private final DynamicValue amount; private final boolean tapped; private final boolean attacking; @@ -56,7 +57,10 @@ public class CreateTokenEffect extends OneShotEffect { public CreateTokenEffect(Token token, DynamicValue amount, boolean tapped, boolean attacking) { 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.tapped = tapped; this.attacking = attacking; @@ -66,7 +70,9 @@ public class CreateTokenEffect extends OneShotEffect { protected CreateTokenEffect(final CreateTokenEffect effect) { super(effect); 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.attacking = effect.attacking; this.lastAddedTokenIds.addAll(effect.lastAddedTokenIds); @@ -82,6 +88,11 @@ public class CreateTokenEffect extends OneShotEffect { return this; } + public CreateTokenEffect withAdditionalTokens(Token... tokens) { + this.tokens.addAll(Arrays.asList(tokens)); + return this; + } + @Override public CreateTokenEffect copy() { return new CreateTokenEffect(this); @@ -90,8 +101,8 @@ public class CreateTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int value = amount.calculate(game, source, this); - token.putOntoBattlefield(value, game, source, source.getControllerId(), tapped, attacking); - this.lastAddedTokenIds = token.getLastAddedTokenIds(); + tokens.get(0).putOntoBattlefield(value, game, source, source.getControllerId(), tapped, attacking, null, null, true, tokens); + 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 if (counterType != null) { for (UUID tokenId : lastAddedTokenIds) { @@ -150,41 +161,53 @@ public class CreateTokenEffect extends OneShotEffect { } private void setText() { - String tokenDescription = token.getDescription(); boolean singular = amount.toString().equals("1"); - if (tokenDescription.contains(", a legendary")) { - staticText = "create " + tokenDescription; - return; - } - if (oldPhrasing) { - tokenDescription = tokenDescription.replace("token with \"", - singular ? "token. It has \"" : "tokens. They have \""); - } StringBuilder sb = new StringBuilder("create "); - if (singular) { - if (tapped && !attacking) { - sb.append("a tapped "); + 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")) { sb.append(tokenDescription); + continue; + } + if (oldPhrasing) { + tokenDescription = tokenDescription.replace("token with \"", + singular ? "token. It has \"" : "tokens. They have \""); + } + if (singular) { + if (tapped && !attacking) { + sb.append("a tapped "); + sb.append(tokenDescription); + } else { + sb.append(CardUtil.addArticle(tokenDescription)); + } } else { - sb.append(CardUtil.addArticle(tokenDescription)); - } - } else { - sb.append(CardUtil.numberToText(amount.toString())).append(' '); - if (tapped && !attacking) { - sb.append("tapped "); - } - sb.append(tokenDescription); - if (tokenDescription.endsWith("token")) { - sb.append("s"); - } - int tokenLocation = sb.indexOf("token "); - if (tokenLocation != -1) { - sb.replace(tokenLocation, tokenLocation + 6, "tokens "); + sb.append(CardUtil.numberToText(amount.toString())).append(' '); + if (tapped && !attacking) { + sb.append("tapped "); + } + sb.append(tokenDescription); + if (tokenDescription.endsWith("token")) { + sb.append("s"); + } + int tokenLocation = sb.indexOf("token "); + if (tokenLocation != -1) { + sb.replace(tokenLocation, tokenLocation + 6, "tokens "); + } } } if (attacking) { - if (singular) { + if (singular && tokens.size() == 1) { sb.append(" that's"); } else { sb.append(" that are"); diff --git a/Mage/src/main/java/mage/game/events/CreateTokenEvent.java b/Mage/src/main/java/mage/game/events/CreateTokenEvent.java index 5033a7a4244..6daac509b63 100644 --- a/Mage/src/main/java/mage/game/events/CreateTokenEvent.java +++ b/Mage/src/main/java/mage/game/events/CreateTokenEvent.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import mage.game.permanent.token.Token; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -17,11 +18,15 @@ public class CreateTokenEvent extends GameEvent { * @param source * @param controllerId * @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 tokensList) { super(GameEvent.EventType.CREATE_TOKEN, null, source, controllerId, amount, false); - tokens.put(token, amount); + if (tokensList != null) { + for (Token token : tokensList) { + tokens.put(token, amount); + } + } } public Map getTokens() { diff --git a/Mage/src/main/java/mage/game/permanent/token/Token.java b/Mage/src/main/java/mage/game/permanent/token/Token.java index 8c10813529e..49d769fbc53 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/Token.java @@ -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, List additionalTokens); + void setPower(int power); void setToughness(int toughness); diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index 4fa616e2df6..57af383b9ba 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -218,8 +218,12 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { 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) { + 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 tokens) { Player controller = game.getPlayer(controllerId); if (controller == null) { return false; @@ -229,7 +233,11 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { } 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)) { int currentTokens = game.getBattlefield().countTokens(event.getPlayerId()); int tokenSlots = Math.max(MAX_TOKENS_PER_GAME - currentTokens, 0);