From fcbf22380e3a3f0f42cafa60aa51aec43c1f9ba4 Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:16:21 +0000 Subject: [PATCH 01/71] [TDM] Implement Teeming Dragonstorm --- .../src/mage/cards/t/TeemingDragonstorm.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 2 + .../game/permanent/token/Soldier22Token.java | 29 +++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TeemingDragonstorm.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/Soldier22Token.java diff --git a/Mage.Sets/src/mage/cards/t/TeemingDragonstorm.java b/Mage.Sets/src/mage/cards/t/TeemingDragonstorm.java new file mode 100644 index 00000000000..0683450554e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeemingDragonstorm.java @@ -0,0 +1,41 @@ +package mage.cards.t; + +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.game.permanent.token.Soldier22Token; + +import java.util.UUID; + +/** + * @author PurpleCrowbar + */ +public final class TeemingDragonstorm extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.DRAGON, "a Dragon"); + + public TeemingDragonstorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + // When this enchantment enters, create two 2/2 white Soldier creature tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Soldier22Token(), 2), false)); + + // When a Dragon you control enters, return this enchantment to its owner's hand. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ReturnToHandSourceEffect(), filter)); + } + + private TeemingDragonstorm (final TeemingDragonstorm card) { + super(card); + } + + @Override + public TeemingDragonstorm copy() { + return new TeemingDragonstorm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 0afdfafe8bc..1178deda0d8 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -150,6 +150,8 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sunset Strikemaster", 126, Rarity.UNCOMMON, mage.cards.s.SunsetStrikemaster.class)); cards.add(new SetCardInfo("Swamp", 281, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swiftwater Cliffs", 268, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); + cards.add(new SetCardInfo("Teeming Dragonstorm", 30, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teeming Dragonstorm", 293, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tempest Hawk", 31, Rarity.COMMON, mage.cards.t.TempestHawk.class)); cards.add(new SetCardInfo("Temur Devotee", 61, Rarity.COMMON, mage.cards.t.TemurDevotee.class)); cards.add(new SetCardInfo("Temur Monument", 248, Rarity.UNCOMMON, mage.cards.t.TemurMonument.class)); diff --git a/Mage/src/main/java/mage/game/permanent/token/Soldier22Token.java b/Mage/src/main/java/mage/game/permanent/token/Soldier22Token.java new file mode 100644 index 00000000000..0ddf938b3c1 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Soldier22Token.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author PurpleCrowbar + */ +public final class Soldier22Token extends TokenImpl { + + public Soldier22Token() { + super("Soldier Token", "2/2 white Soldier creature token"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add(SubType.SOLDIER); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private Soldier22Token(final Soldier22Token token) { + super(token); + } + + @Override + public Soldier22Token copy() { + return new Soldier22Token(this); + } +} From 2286dd2bbd975c4561e9ddad80ed44614acbc157 Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:32:37 +0000 Subject: [PATCH 02/71] Fix Great Arashin City --- .../src/mage/cards/g/GreatArashinCity.java | 6 ++-- .../token/NoFlyingSpiritWhiteToken.java | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 Mage/src/main/java/mage/game/permanent/token/NoFlyingSpiritWhiteToken.java diff --git a/Mage.Sets/src/mage/cards/g/GreatArashinCity.java b/Mage.Sets/src/mage/cards/g/GreatArashinCity.java index 9166e95fedc..ce375db023c 100644 --- a/Mage.Sets/src/mage/cards/g/GreatArashinCity.java +++ b/Mage.Sets/src/mage/cards/g/GreatArashinCity.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; -import mage.game.permanent.token.SpiritWhiteToken; +import mage.game.permanent.token.NoFlyingSpiritWhiteToken; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -47,9 +47,9 @@ public final class GreatArashinCity extends CardImpl { this.addAbility(new BlackManaAbility()); // {1}{B}, {T}, Exile a creature card from your graveyard: Create a 1/1 white Spirit creature token. - Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl<>("{1}{B}")); + Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new NoFlyingSpiritWhiteToken()), new ManaCostsImpl<>("{1}{B}")); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE)).withSourceExileZone(false)); this.addAbility(ability); } diff --git a/Mage/src/main/java/mage/game/permanent/token/NoFlyingSpiritWhiteToken.java b/Mage/src/main/java/mage/game/permanent/token/NoFlyingSpiritWhiteToken.java new file mode 100644 index 00000000000..60e7644cc70 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/NoFlyingSpiritWhiteToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author PurpleCrowbar + */ +public final class NoFlyingSpiritWhiteToken extends TokenImpl { + + public NoFlyingSpiritWhiteToken() { + super("Spirit Token", "1/1 white Spirit creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + color.setWhite(true); + power = new MageInt(1); + toughness = new MageInt(1); + } + + private NoFlyingSpiritWhiteToken(final NoFlyingSpiritWhiteToken token) { + super(token); + } + + @Override + public NoFlyingSpiritWhiteToken copy() { + return new NoFlyingSpiritWhiteToken(this); + } +} From 89189b4d3b58c85de9c40eb883ae12219f454c46 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Fri, 28 Mar 2025 21:46:32 -0400 Subject: [PATCH 03/71] fix verify --- Mage.Sets/src/mage/sets/UnknownEvent.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/UnknownEvent.java b/Mage.Sets/src/mage/sets/UnknownEvent.java index 776949d124a..21975fb4b22 100644 --- a/Mage.Sets/src/mage/sets/UnknownEvent.java +++ b/Mage.Sets/src/mage/sets/UnknownEvent.java @@ -1,4 +1,3 @@ - package mage.sets; import mage.cards.ExpansionSet; @@ -17,7 +16,7 @@ public final class UnknownEvent extends ExpansionSet { } private UnknownEvent() { - super("Unknown Event", "DA1", ExpansionSet.buildDate(2023, 2, 15), SetType.JOKE_SET); + super("Unknown Event", "UNK", ExpansionSet.buildDate(2023, 2, 15), SetType.JOKE_SET); this.hasBasicLands = false; this.hasBoosters = false; From b254b9528da7b40fe599ab3b1cc00746a592d8a8 Mon Sep 17 00:00:00 2001 From: padfoothelix Date: Sat, 29 Mar 2025 02:52:15 +0100 Subject: [PATCH 04/71] [WHO] Implement The Second Doctor (#13435) --- .../src/mage/cards/t/TheSecondDoctor.java | 141 ++++++++++++++++++ Mage.Sets/src/mage/sets/DoctorWho.java | 14 +- 2 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TheSecondDoctor.java diff --git a/Mage.Sets/src/mage/cards/t/TheSecondDoctor.java b/Mage.Sets/src/mage/cards/t/TheSecondDoctor.java new file mode 100644 index 00000000000..3553ed8bf51 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSecondDoctor.java @@ -0,0 +1,141 @@ +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author padfoothelix + */ +public final class TheSecondDoctor extends CardImpl { + + public TheSecondDoctor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.TIME_LORD); + this.subtype.add(SubType.DOCTOR); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Players have no maximum hand size. + this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect( + Integer.MAX_VALUE, Duration.WhileOnBattlefield, + MaximumHandSizeControllerEffect.HandSizeModification.SET, TargetController.ANY + ))); + + // How Civil of You -- At the beginning of your end step, each player may draw a card. Each opponent who does can't attack you or permanents you control during their next turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new TheSecondDoctorEffect()).withFlavorWord("How Civil of You")); + } + + private TheSecondDoctor(final TheSecondDoctor card) { + super(card); + } + + @Override + public TheSecondDoctor copy() { + return new TheSecondDoctor(this); + } +} + +class TheSecondDoctorEffect extends OneShotEffect { + TheSecondDoctorEffect() { + super(Outcome.Benefit); + this.staticText = "each player may draw a card. Each opponent who does can't attack you or permanents you control during their next turn."; + } + + private TheSecondDoctorEffect(final TheSecondDoctorEffect effect) { + super(effect); + } + + @Override + public TheSecondDoctorEffect copy() { + return new TheSecondDoctorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null + && player.chooseUse(Outcome.DrawCard, "Draw a card ?", source, game) + && player.drawCards(1, source, game) > 0 + && game.getOpponents(controller.getId()).contains(playerId)) { + RestrictionEffect effect = new TheSecondDoctorCantAttackEffect(player.getId()); + game.addEffect(effect, source); + } + } + return true; + } +} + +class TheSecondDoctorCantAttackEffect extends RestrictionEffect { + + private final UUID opponentId; + + public TheSecondDoctorCantAttackEffect(UUID opponentId) { + super(Duration.UntilEndOfYourNextTurn); + this.opponentId = opponentId; + staticText = ""; + } + + private TheSecondDoctorCantAttackEffect(final TheSecondDoctorCantAttackEffect effect) { + super(effect); + this.opponentId = effect.opponentId; + } + + @Override + public TheSecondDoctorCantAttackEffect copy() { + return new TheSecondDoctorCantAttackEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (opponentId != null) { + setStartingControllerAndTurnNum(game, opponentId, game.getActivePlayerId()); + } else { + discard(); + } + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return game.isActivePlayer(opponentId); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + UUID controllerId = source.getControllerId(); + // defender is null + if (defenderId == null) { + return true; + } + // defender is a permanent + Permanent defender = game.getPermanent(defenderId); + if (defender != null) { + return !defender.isControlledBy(controllerId); + } + // defender is a player + return !defenderId.equals(controllerId); + } +} diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index b70357f6180..c0009b9bb88 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -1010,13 +1010,13 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("The Rani", 754, Rarity.RARE, mage.cards.t.TheRani.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Sea Devils", 108, Rarity.RARE, mage.cards.t.TheSeaDevils.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Sea Devils", 713, Rarity.RARE, mage.cards.t.TheSeaDevils.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", "553z", Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 1031, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 1144, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 156, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 440, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 553, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Second Doctor", 761, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", "553z", Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 1031, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 1144, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 156, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 440, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 553, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Second Doctor", 761, Rarity.RARE, mage.cards.t.TheSecondDoctor.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Seventh Doctor", "558z", Rarity.RARE, mage.cards.t.TheSeventhDoctor.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Seventh Doctor", 1033, Rarity.RARE, mage.cards.t.TheSeventhDoctor.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Seventh Doctor", 1149, Rarity.RARE, mage.cards.t.TheSeventhDoctor.class, NON_FULL_USE_VARIOUS)); From fa909b2b40f1847646ce82d43188becaf6924cb7 Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:52:34 +0000 Subject: [PATCH 05/71] Add TDM token images for implemented tokens --- .../dl/sources/ScryfallImageSupportTokens.java | 16 ++++++++++++++++ Mage/src/main/resources/tokens-database.txt | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index d8dc2cd18c2..55602d438ad 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -2615,6 +2615,22 @@ public class ScryfallImageSupportTokens { put("DRC/Zombie Army", "https://api.scryfall.com/cards/tdrc/8/en?format=image"); put("DRC/Zombie Warrior", "https://api.scryfall.com/cards/tdrc/9/en?format=image"); + // TDM + put("TDM/Bird", "https://api.scryfall.com/cards/ttdm/2/en?format=image"); + put("TDM/Dragon", "https://api.scryfall.com/cards/ttdm/11/en?format=image"); + put("TDM/Elephant", "https://api.scryfall.com/cards/ttdm/14/en?format=image"); + put("TDM/Goblin", "https://api.scryfall.com/cards/ttdm/12/en?format=image"); + put("TDM/Monk", "https://api.scryfall.com/cards/ttdm/3/en?format=image"); + put("TDM/Reliquary Dragon", "https://api.scryfall.com/cards/ttdm/15/en?format=image"); + put("TDM/Soldier/1", "https://api.scryfall.com/cards/ttdm/4/en?format=image"); + put("TDM/Soldier/2", "https://api.scryfall.com/cards/ttdm/5/en?format=image"); + put("TDM/Spirit/1", "https://api.scryfall.com/cards/ttdm/9/en?format=image"); + put("TDM/Spirit/2", "https://api.scryfall.com/cards/ttdm/6/en?format=image"); + // TODO: 2/2 and 3/3 Spirit tokens (no relevant cards revealed, token not implemented) + put("TDM/Treasure", "https://api.scryfall.com/cards/ttdm/16/en?format=image"); + put("TDM/Warrior", "https://api.scryfall.com/cards/ttdm/13/en?format=image"); + put("TDM/Zombie Druid", "https://api.scryfall.com/cards/ttdm/10/en?format=image"); + // TDC put("TDC/Angel", "https://api.scryfall.com/cards/ttdc/2/en?format=image"); put("TDC/Citizen", "https://api.scryfall.com/cards/ttdc/26/en?format=image"); diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index 2064f6cd4ef..39c9c643fd3 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -2482,6 +2482,23 @@ |Generate|TOK:DRC|Zombie Army|||ZombieArmyToken| |Generate|TOK:DRC|Zombie Warrior|||GodEternalOketraToken| +# TDM +|Generate|TOK:TDM|Bird|||BirdToken| +|Generate|TOK:TDM|Dragon|||DragonToken| +|Generate|TOK:TDM|Elephant|||Elephant55Token| +|Generate|TOK:TDM|Goblin|||GoblinToken| +|Generate|TOK:TDM|Monk|||MonasteryMentorToken| +|Generate|TOK:TDM|Reliquary Dragon|||ReliquaryDragonToken| +|Generate|TOK:TDM|Soldier|1||SoldierToken| +|Generate|TOK:TDM|Soldier|2||Soldier22Token| +|Generate|TOK:TDM|Spirit|1||SpiritXXToken| +|Generate|TOK:TDM|Spirit|2||NoFlyingSpiritWhiteToken| +# TODO: 2/2 Spirit (no relevant cards revealed, token not implemented) +# TODO: 3/3 Spirit (no relevant cards revealed, token not implemented) +|Generate|TOK:TDM|Treasure|||TreasureToken| +|Generate|TOK:TDM|Warrior|||RedWarriorToken| +|Generate|TOK:TDM|Zombie Druid|||ZombieDruidToken| + # TDC |Generate|TOK:TDC|Angel|||AngelVigilanceToken| |Generate|TOK:TDC|Citizen|||CitizenGreenWhiteToken| From 299c7dc56f80740ee33d3573b9fd16930d4ba125 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 21:47:51 -0400 Subject: [PATCH 06/71] [TDM] update spoiler and reprints --- .../src/mage/sets/TarkirDragonstorm.java | 6 +-- Utils/mtg-cards-data.txt | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 1178deda0d8..9f2391c1230 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -12,7 +12,7 @@ import java.util.List; */ public final class TarkirDragonstorm extends ExpansionSet { - private static final List unfinished = Arrays.asList("Channeled Dragonfire", "Glacial Dragonhunt", "Mammoth Bellow", "Nature's Rhythm", "Roamer's Routine", "Songcrafter Mage", "Synchronized Charge", "Unending Whisper", "Ureni's Rebuff", "Winternight Stories"); + private static final List unfinished = Arrays.asList("Channeled Dragonfire", "Glacial Dragonhunt", "Mammoth Bellow", "Nature's Rhythm", "Roamer's Routine", "Songcrafter Mage", "Synchronized Charge", "Unending Whisper", "Ureni's Rebuff", "Wild Ride", "Winternight Stories"); private static final TarkirDragonstorm instance = new TarkirDragonstorm(); public static TarkirDragonstorm getInstance() { @@ -150,14 +150,14 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sunset Strikemaster", 126, Rarity.UNCOMMON, mage.cards.s.SunsetStrikemaster.class)); cards.add(new SetCardInfo("Swamp", 281, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swiftwater Cliffs", 268, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); - cards.add(new SetCardInfo("Teeming Dragonstorm", 30, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Teeming Dragonstorm", 293, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teeming Dragonstorm", 30, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class)); cards.add(new SetCardInfo("Tempest Hawk", 31, Rarity.COMMON, mage.cards.t.TempestHawk.class)); cards.add(new SetCardInfo("Temur Devotee", 61, Rarity.COMMON, mage.cards.t.TemurDevotee.class)); cards.add(new SetCardInfo("Temur Monument", 248, Rarity.UNCOMMON, mage.cards.t.TemurMonument.class)); cards.add(new SetCardInfo("Temur Tawnyback", 229, Rarity.COMMON, mage.cards.t.TemurTawnyback.class)); cards.add(new SetCardInfo("Thornwood Falls", 269, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class)); cards.add(new SetCardInfo("Underfoot Underdogs", 129, Rarity.COMMON, mage.cards.u.UnderfootUnderdogs.class)); cards.add(new SetCardInfo("Undergrowth Leopard", 165, Rarity.COMMON, mage.cards.u.UndergrowthLeopard.class)); diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 92dc2faa708..9a845ef758c 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -57200,6 +57200,7 @@ Cloud, Planet's Champion|Final Fantasy|552|M|{3}{R}{W}|Legendary Creature - Huma Sephiroth, Planet's Heir|Final Fantasy|553|M|{4}{U}{B}|Legendary Creature - Human Avatar Soldier|4|4|Vigilance$When Sephiroth enters, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, put a +1/+1 counter on Sephiroth.| Ugin, Eye of the Storms|Tarkir: Dragonstorm|1|M|{7}|Legendary Planeswalker - Ugin|7|When you cast this spell, exile up to one target permanent that's one or more colors.$Whenever you cast a colorless spell, exile up to one target permanent that's one or more colors.$+2: You gain 3 life and draw a card.$0: Add {C}{C}{C}.$-11: Search your library for any number of colorless nonland cards, exile them, then shuffle. Until end of turn, you may cast those cards without paying their mana costs.| Anafenza, Unyielding Lineage|Tarkir: Dragonstorm|2|R|{2}{W}|Legendary Creature - Spirit Soldier|2|2|Flash$First strike$Whenever another nontoken creature you control dies, Anafenza endures 2.| +Arashin Sunshield|Tarkir: Dragonstorm|3|C|{3}{W}|Creature - Human Warrior|3|4|When this creature enters, exile up to two target cards from a single graveyard.${W}, {T}: Tap target creature.| Bearer of Glory|Tarkir: Dragonstorm|4|C|{1}{W}|Creature - Human Soldier|2|1|During your turn, this creature has first strike.${4}{W}: Creatures you control get +1/+1 until end of turn.| Clarion Conqueror|Tarkir: Dragonstorm|5|R|{2}{W}|Creature - Dragon|3|3|Flying$Activated abilities of artifacts, creatures, and planeswalkers can't be activated.| Coordinated Maneuver|Tarkir: Dragonstorm|6|C|{1}{W}|Instant|||Choose one --$* Coordinated Maneuver deals damage equal to the number of creatures you control to target creature or planeswalker.$* Destroy target enchantment.| @@ -57209,10 +57210,12 @@ Dragonback Lancer|Tarkir: Dragonstorm|9|C|{3}{W}|Creature - Human Soldier|3|3|Fl Duty Beyond Death|Tarkir: Dragonstorm|10|U|{1}{W}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Creatures you control gain indestructible until end of turn. Put a +1/+1 counter on each creature you control.| Elspeth, Storm Slayer|Tarkir: Dragonstorm|11|M|{3}{W}{W}|Legendary Planeswalker - Elspeth|5|If one or more tokens would be created under your control, twice that many of those tokens are created instead.$+1: Create a 1/1 white Soldier creature token.$0: Put a +1/+1 counter on each creature you control. Those creatures gain flying until your next turn.$-3: Destroy target creature an opponent controls with mana value 3 or greater.| Fortress Kin-Guard|Tarkir: Dragonstorm|12|C|{1}{W}|Creature - Dog Soldier|1|2|When this creature enters, it endures 1.| +Furious Forebear|Tarkir: Dragonstorm|13|U|{1}{W}|Creature - Spirit Warrior|3|1|Whenever a creature you control dies while this card is in your graveyard, you may pay {1}{W}. If you do, return this card from your graveyard to your hand.| Lightfoot Technique|Tarkir: Dragonstorm|14|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. It gains flying and indestructible until end of turn.| Loxodon Battle Priest|Tarkir: Dragonstorm|15|U|{4}{W}|Creature - Elephant Cleric|3|5|At the beginning of combat on your turn, put a +1/+1 counter on another target creature you control.| Mardu Devotee|Tarkir: Dragonstorm|16|C|{W}|Creature - Human Scout|1|2|When this creature enters, scry 2.${1}: Add {R}, {W}, or {B}. Activate only once each turn.| Osseous Exhale|Tarkir: Dragonstorm|17|C|{1}{W}|Instant|||As an additional cost to cast this spell, you may behold a Dragon.$Osseous Exhale deals 5 damage to target attacking or blocking creature. If a Dragon was beheld, you gain 2 life.| +Poised Practitioner|Tarkir: Dragonstorm|18|C|{2}{W}|Creature - Human Monk|2|3|Flurry -- Whenever you cast your second spell each turn, put a +1/+1 counter on this creature. Scry 1.| Rally the Monastery|Tarkir: Dragonstorm|19|U|{3}{W}|Instant|||This spell costs {2} less to cast if you've cast another spell this turn.$Choose one --$* Create two 1/1 white Monk creature tokens with prowess.$* Up to two target creatures you control each get +2/+2 until end of turn.$* Destroy target creature with power 4 or greater.| Rebellious Strike|Tarkir: Dragonstorm|20|C|{1}{W}|Instant|||Target creature gets +3/+0 until end of turn.$Draw a card.| Riling Dawnbreaker|Tarkir: Dragonstorm|21|C|{4}{W}|Creature - Dragon|3|4|Flying, vigilance$At the beginning of combat on your turn, another target creature you control gets +1/+0 until end of turn.| @@ -57222,6 +57225,7 @@ Salt Road Packbeast|Tarkir: Dragonstorm|23|C|{5}{W}|Creature - Beast|4|3|This sp Smile at Death|Tarkir: Dragonstorm|24|M|{3}{W}{W}|Enchantment|||At the beginning of your upkeep, return up to two target creature cards with power 2 or less from your graveyard to the battlefield. Put a +1/+1 counter on each of those creatures.| Starry-Eyed Skyrider|Tarkir: Dragonstorm|25|U|{2}{W}|Creature - Human Scout|1|3|Flying$Whenever this creature attacks, another target creature you control gains flying until end of turn.$Attacking tokens you control have flying.| Static Snare|Tarkir: Dragonstorm|26|U|{4}{W}|Enchantment|||Flash$This spell costs {1} less to cast for each attacking creature.$When this enchantment enters, exile target artifact or creature an opponent controls until this enchantment leaves the battlefield.| +Stormbeacon Blade|Tarkir: Dragonstorm|27|U|{1}{W}|Artifact - Equipment|||Equipped creature gets +3/+0.$Whenever equipped creature attacks, draw a card if you control three or more attacking creatures.$Equip {2}| Stormplain Detainment|Tarkir: Dragonstorm|28|C|{2}{W}|Enchantment|||When this enchantment enters, exile target nonland permanent an opponent controls until this enchantment leaves the battlefield.| Sunpearl Kirin|Tarkir: Dragonstorm|29|U|{1}{W}|Creature - Kirin|2|1|Flash$Flying$When this creature enters, return up to one other target nonland permanent you control to its owner's hand. If it was a token, draw a card.| Teeming Dragonstorm|Tarkir: Dragonstorm|30|U|{3}{W}|Enchantment|||When this enchantment enters, create two 2/2 white Soldier creature tokens.$When a Dragon you control enters, return this enchantment to its owner's hand.| @@ -57233,21 +57237,26 @@ Aegis Sculptor|Tarkir: Dragonstorm|35|U|{3}{U}|Creature - Bird Wizard|2|3|Flying Agent of Kotis|Tarkir: Dragonstorm|36|C|{1}{U}|Creature - Human Rogue|2|1|Renew -- {3}{U}, Exile this card from your graveyard: Put two +1/+1 counters on target creature. Activate only as a sorcery.| Ambling Stormshell|Tarkir: Dragonstorm|37|R|{3}{U}{U}|Creature - Turtle|5|9|Ward {2}$Whenever this creature attacks, put three stun counters on it and draw three cards.$Whenever you cast a Turtle spell, untap this creature.| Bewildering Blizzard|Tarkir: Dragonstorm|38|U|{4}{U}{U}|Instant|||Draw three cards. Creatures your opponents control get -3/-0 until end of turn.| +Constrictor Sage|Tarkir: Dragonstorm|39|U|{4}{U}|Creature - Snake Wizard|4|4|When this creature enters, tap target creature an opponent controls and put a stun counter on it.$Renew -- 2{U}, Exile this card from your graveyard: Tap target creature an opponent controls and put a stun counter on it. Activate only as a sorcery.| Dirgur Island Dragon|Tarkir: Dragonstorm|40|C|{5}{U}|Creature - Dragon|4|4|Flying$Ward {2}| Skimming Strike|Tarkir: Dragonstorm|40|C|{1}{U}|Instant - Omen|4|4|Tap up to one target creature. Draw a card.| Dispelling Exhale|Tarkir: Dragonstorm|41|C|{1}{U}|Instant|||As an additional cost to cast this spell, you may behold a Dragon.$Counter target spell unless its controller pays {2}. If a Dragon was beheld, counter that spell unless its controller pays {4} instead.| Dragonologist|Tarkir: Dragonstorm|42|R|{2}{U}|Creature - Human Wizard|1|3|When this creature enters, look at the top six cards of your library. You may reveal an instant, sorcery, or Dragon card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.$Untapped Dragons you control have hexproof.| Dragonstorm Forecaster|Tarkir: Dragonstorm|43|U|{U}|Creature - Human Scout|0|3|{2}, {T}: Search your library for a card named Dragonstorm Globe or Boulderborn Dragon, reveal it, put it into your hand, then shuffle.| Essence Anchor|Tarkir: Dragonstorm|44|U|{2}{U}|Artifact|||At the beginning of your upkeep, surveil 1.${T}: Create a 2/2 black Zombie Druid creature token. Activate only during your turn and only if a card left your graveyard this turn.| +Focus the Mind|Tarkir: Dragonstorm|45|C|{4}{U}|Instant|||This spell costs {2} less to cast if you've cast another spell this turn.$Draw three cards, then discard a card.| Fresh Start|Tarkir: Dragonstorm|46|U|{1}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets -5/-0 and loses all abilities.| Highspire Bell-Ringer|Tarkir: Dragonstorm|47|C|{2}{U}|Creature - Djinn Monk|1|4|Flying$The second spell you cast each turn costs {1} less to cast.| Humbling Elder|Tarkir: Dragonstorm|48|C|{U}|Creature - Human Monk|1|2|Flash$When this creature enters, target creature an opponent controls gets -2/-0 until end of turn.| +Iceridge Serpent|Tarkir: Dragonstorm|49|C|{4}{U}|Creature - Serpent|3|3|When this creature enters, return target creature an opponent controls to its owner's hand.| Kishla Trawlers|Tarkir: Dragonstorm|50|U|{2}{U}|Creature - Human Citizen|3|2|When this creature enters, you may exile a creature card from your graveyard. When you do, return target instant or sorcery card from your graveyard to your hand.| Marang River Regent|Tarkir: Dragonstorm|51|R|{4}{U}{U}|Creature - Dragon|6|7|Flying$When this creature enters, return up to two other target nonland permanents to their owners' hands.| Coil and Catch|Tarkir: Dragonstorm|51|R|{3}{U}|Instant - Omen|6|7|Draw three cards, then discard a card.| Naga Fleshcrafter|Tarkir: Dragonstorm|52|R|{3}{U}|Creature - Snake Shapeshifter|0|0|You may have this creature enter as a copy of any creature on the battlefield.$Renew -- {2}{U}, Exile this card from your graveyard: Put a +1/+1 counter on target nonlegendary creature you control. Each other creature you control becomes a copy of that creature until end of turn. Activate only as a sorcery.| Ringing Strike Mastery|Tarkir: Dragonstorm|53|C|{U}|Enchantment - Aura|||Enchant creature$When this Aura enters, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.$Enchanted creature has "{5}: Untap this creature."| +Riverwalk Technique|Tarkir: Dragonstorm|54|C|{3}{U}|Instant|||Choose one --$* The owner of target nonland permanent puts it on their choice of the top or bottom of their library.$* Counter target noncreature spell.| Roiling Dragonstorm|Tarkir: Dragonstorm|55|U|{1}{U}|Enchantment|||When this enchantment enters, draw two cards, then discard a card.$When a Dragon you control enters, return this enchantment to its owner's hand.| +Sibsig Appraiser|Tarkir: Dragonstorm|56|C|{2}{U}|Creature - Zombie Advisor|2|1|When this creature enters, look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.| Snowmelt Stag|Tarkir: Dragonstorm|57|C|{3}{U}|Creature - Elemental Elk|2|5|Vigilance$During your turn, this creature has base power and toughness 5/2.${5}{U}{U}: This creature can't be blocked this turn.| Spectral Denial|Tarkir: Dragonstorm|58|U|{X}{U}|Instant|||This spell costs {1} less to cast for each creature you control with power 4 or greater.$Counter target spell unless its controller pays {X}.| Stillness in Motion|Tarkir: Dragonstorm|59|R|{1}{U}|Enchantment|||At the beginning of your upkeep, mill three cards. Then if you have no cards in your library, exile this enchantment and put five cards from your graveyard on top of your library in any order.| @@ -57255,9 +57264,14 @@ Taigam, Master Opportunist|Tarkir: Dragonstorm|60|M|{1}{U}|Legendary Creature - Temur Devotee|Tarkir: Dragonstorm|61|C|{1}{U}|Creature - Human Druid|3|3|Defender${1}: Add {G}, {U}, or {R}. Activate only once each turn.| Unending Whisper|Tarkir: Dragonstorm|62|C|{U}|Sorcery|||Draw a card.$Harmonize {5}{U}| Ureni's Rebuff|Tarkir: Dragonstorm|63|U|{1}{U}|Sorcery|||Return target creature to its owner's hand.$Harmonize {5}{U}| +Veteran Ice Climber|Tarkir: Dragonstorm|64|U|{1}{U}|Creature - Human Scout|1|3|Vigilance$This creature can't be blocked.$Whenever this creature attacks, up to one target player mills cards equal to this creature's power.| +Wingblade Disciple|Tarkir: Dragonstorm|65|U|{2}{U}|Creature - Human Monk|2|2|Flying$Flurry -- Whenever you cast your second spell each turn, create a 1/1 white Bird creature token with flying.| Wingspan Stride|Tarkir: Dragonstorm|66|C|{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has flying.${2}{U}: Return this Aura to its owner's hand.| Winternight Stories|Tarkir: Dragonstorm|67|R|{2}{U}|Sorcery|||Draw three cards. Then discard two cards unless you discard a creature card.$Harmonize {4}{U}| Abzan Devotee|Tarkir: Dragonstorm|68|C|{1}{B}|Creature - Dog Cleric|2|1|{1}: Add {W}, {B}, or {G}. Activate only once each turn.${2}{B}: Return this card from your graveyard to your hand.| +Adorned Crocodile|Tarkir: Dragonstorm|69|C|{4}{B}|Creature - Crocodile|5|3|When this creature dies, create a 2/2 black Zombie Druid creature token. Renew -, Exile this card from your graveyard: Put a +1/+1 counter on target creature. Activate only as a sorcery.| +Aggressive Negotiations|Tarkir: Dragonstorm|70|C|{2}{B}|Sorcery|||Target opponent reveals their hand. You choose a nonland card from it and exile that card. Put a +1/+1 counter on up to one target creature you control.| +Alchemist's Assistant|Tarkir: Dragonstorm|71|U|{1}{B}|Creature - Monkey|2|1|Lifelink$Renew -- {1}{B}, Exile this card from your graveyard: Put a lifelink counter on target creature. Activate only as a sorcery.| Alesha's Legacy|Tarkir: Dragonstorm|72|C|{1}{B}|Instant|||Target creature you control gains deathtouch and indestructible until end of turn.| Avenger of the Fallen|Tarkir: Dragonstorm|73|R|{2}{B}|Creature - Human Warrior|2|4|Deathtouch$Mobilize X, where X is the number of creature cards in your graveyard.| Caustic Exhale|Tarkir: Dragonstorm|74|C|{B}|Instant|||As an additional cost to cast this spell, behold a Dragon or pay {1}.$Target creature gets -3/-3 until end of turn.| @@ -57268,14 +57282,22 @@ Desperate Measures|Tarkir: Dragonstorm|78|U|{B}|Instant|||Target creature gets + Dragon's Prey|Tarkir: Dragonstorm|79|C|{2}{B}|Instant|||This spell costs {2} more to cast if it targets a Dragon.$Destroy target creature.| Feral Deathgorger|Tarkir: Dragonstorm|80|C|{5}{B}|Creature - Dragon|3|5|Flying, deathtouch$When this creature enters, exile up to two target cards from a single graveyard.| Dusk Sight|Tarkir: Dragonstorm|80|C|{1}{B}|Sorcery - Omen|3|5|Put a +1/+1 counter on up to one target creature. Draw a card.| +Gurmag Rakshasa|Tarkir: Dragonstorm|81|U|{4}{B}{B}|Creature - Demon|5|5|Menace$When this creature enters, target creature an opponent controls gets -2/-2 until end of turn and target creature you control gets +2/+2 until end of turn.| +Hundred-Battle Veteran|Tarkir: Dragonstorm|82|U|{3}{B}|Creature - Zombie Warrior|4|2|As long as there are three or more different kinds of counters among creatures you control, this creature gets +2/+4.$You may cast this card from your graveyard. If you do, it enters with a finality counter on it.| +Kin-Tree Nurturer|Tarkir: Dragonstorm|83|C|{2}{B}|Creature - Human Druid|2|1|Lifelink$When this creature enters, it endures 1.| Krumar Initiate|Tarkir: Dragonstorm|84|U|{1}{B}|Creature - Human Cleric|2|2|{X}{B}, {T}, Pay X life: This creature endures X. Activate only as a sorcery.| +Nightblade Brigade|Tarkir: Dragonstorm|85|C|{2}{B}|Creature - Goblin Soldier|1|3|Deathtouch$Mobilize 1$When this creature enters, surveil 1.| Qarsi Revenant|Tarkir: Dragonstorm|86|R|{1}{B}{B}|Creature - Vampire|3|3|Flying, deathtouch, lifelink$Renew -- {2}{B}, Exile this card from your graveyard: Put a flying counter, a deathtouch counter, and a lifelink counter on target creature. Activate only as a sorcery.| Rot-Curse Rakshasa|Tarkir: Dragonstorm|87|M|{1}{B}|Creature - Demon|5|5|Trample$Decayed$Renew -- {X}{B}{B}, Exile this card from your graveyard: Put a decayed counter on each of X target creatures. Activate only as a sorcery.| +Salt Road Skirmish|Tarkir: Dragonstorm|88|U|{3}{B}|Sorcery|||Destroy target creature. Create two 1/1 red Warrior creature tokens. They gain haste until end of turn. Sacrifice them at the beginning of the next end step.| +Sandskitter Outrider|Tarkir: Dragonstorm|89|C|{3}{B}|Creature - Goblin Soldier|2|1|Menace$When this creature enters, it endures 2.| Scavenger Regent|Tarkir: Dragonstorm|90|R|{3}{B}|Creature - Dragon|4|4|Flying$Ward--Discard a card.| Exude Toxin|Tarkir: Dragonstorm|90|R|{X}{B}{B}|Sorcery - Omen|4|4|Each non-Dragon creature gets -X/-X until end of turn.| The Sibsig Ceremony|Tarkir: Dragonstorm|91|R|{B}{B}{B}|Legendary Enchantment|||Creature spells you cast cost {2} less to cast.$Whenever a creature you control enters, if you cast it, destroy that creature, then create a 2/2 black Zombie Druid creature token.| Sidisi, Regent of the Mire|Tarkir: Dragonstorm|92|R|{1}{B}|Legendary Creature - Zombie Snake Warlock|1|3|{T}, Sacrifice a creature you control with mana value X other than Sidisi: Return target creature card with mana value X plus 1 from your graveyard to the battlefield. Activate only as a sorcery.| Sinkhole Surveyor|Tarkir: Dragonstorm|93|R|{1}{B}|Creature - Bird Scout|1|3|Flying$Whenever this creature attacks, you lose 1 life and this creature endures 1.| +Strategic Betrayal|Tarkir: Dragonstorm|94|U|{1}{B}|Sorcery|||Target opponent exiles a creature they control and their graveyard.| +Unburied Earthcarver|Tarkir: Dragonstorm|95|C|{1}{B}|Creature - Human Warrior|2|2|{2}, Sacrifice another creature: Put a +1/+1 counter on this creature.| Unrooted Ancestor|Tarkir: Dragonstorm|96|U|{2}{B}|Creature - Spirit Cleric|3|2|Flash${1}, Sacrifice another creature: This creature gains indestructible until end of turn. Tap it.| Venerated Stormsinger|Tarkir: Dragonstorm|97|U|{3}{B}|Creature - Orc Cleric|3|3|Mobilize 1$Whenever this creature or another creature you control dies, each opponent loses 1 life and you gain 1 life.| Wail of War|Tarkir: Dragonstorm|98|U|{2}{B}|Instant|||Choose one --$* Creatures target opponent controls get -1/-1 until end of turn.$* Return up to two target creature cards from your graveyard to your hand.| @@ -57288,41 +57310,57 @@ Devoted Duelist|Tarkir: Dragonstorm|104|C|{1}{R}|Creature - Goblin Monk|2|1|Hast Dracogenesis|Tarkir: Dragonstorm|105|M|{6}{R}{R}|Enchantment|||You may cast Dragon spells without paying their mana costs.| Equilibrium Adept|Tarkir: Dragonstorm|106|U|{3}{R}|Creature - Dog Monk|2|4|When this creature enters, exile the top card of your library. Until the end of your next turn, you may play that card.$Flurry -- Whenever you cast your second spell each turn, this creature gains double strike until end of turn.| Fire-Rim Form|Tarkir: Dragonstorm|107|C|{1}{R}|Enchantment - Aura|||Flash$Enchant creature$When this Aura enters, enchanted creature gains first strike until end of turn.$Enchanted creature gets +2/+0.| +Fleeting Effigy|Tarkir: Dragonstorm|108|U|{R}|Creature - Elemental|2|2|Haste$At the beginning of your end step, return this creature to its owner's hand.$2{R}: This creature gets +2/+0 until end of turn.| +Iridescent Tiger|Tarkir: Dragonstorm|109|U|{4}{R}|Creature - Cat|3|4|When this creature enters, if you cast it, add {W}{U}{B}{R}{G}.| Jeskai Devotee|Tarkir: Dragonstorm|110|C|{1}{R}|Creature - Orc Monk|2|2|Flurry -- Whenever you cast your second spell each turn, this creature gets +1/+1 until end of turn.${1}: Add {U}, {R}, or {W}. Activate only once each turn.| Magmatic Hellkite|Tarkir: Dragonstorm|111|R|{2}{R}{R}|Creature - Dragon|4|5|Flying$When this creature enters, destroy target nonbasic land an opponent controls. Its controller searches their library for a basic land card, puts it onto the battlefield tapped with a stun counter on it, then shuffles.| Meticulous Artisan|Tarkir: Dragonstorm|112|C|{3}{R}|Creature - Djinn Artificer|3|3|Prowess$When this creature enters, create a Treasure token.| Molten Exhale|Tarkir: Dragonstorm|113|C|{1}{R}|Sorcery|||You may cast this spell as though it had flash if you behold a Dragon as an additional cost to cast it.$Molten Exhale deals 4 damage to target creature or planeswalker.| Narset's Rebuke|Tarkir: Dragonstorm|114|C|{4}{R}|Instant|||Narset's Rebuke deals 5 damage to target creature. Add {U}{R}{W}. If that creature would die this turn, exile it instead.| +Overwhelming Surge|Tarkir: Dragonstorm|115|U|{2}{R}|Instant|||Choose one or both --$* Overwhelming Surge deals 3 damage to target creature.$* Destroy target noncreature artifact.| +Rescue Leopard|Tarkir: Dragonstorm|116|C|{2}{R}|Creature - Cat|4|2|Whenever this creature becomes tapped, you may discard a card. If you do, draw a card.| Reverberating Summons|Tarkir: Dragonstorm|117|U|{1}{R}|Enchantment|||At the beginning of each combat, if you've cast two or more spells this turn, this enchantment becomes a 3/3 Monk creature with haste in addition to its other types until end of turn.${1}{R}, Discard your hand, Sacrifice this enchantment: Draw two cards.| Sarkhan, Dragon Ascendant|Tarkir: Dragonstorm|118|R|{1}{R}|Legendary Creature - Human Druid|2|2|When Sarkhan enters, you may behold a Dragon. If you do, create a Treasure token.$Whenever a Dragon you control enters, put a +1/+1 counter on Sarkhan. Until end of turn, Sarkhan becomes a Dragon in addition to its other types and gains flying.| Seize Opportunity|Tarkir: Dragonstorm|119|C|{2}{R}|Instant|||Choose one --$* Exile the top two cards of your library. Until the end of your next turn, you may play those cards.$* Up to two target creatures each get +2/+1 until end of turn.| Shock Brigade|Tarkir: Dragonstorm|120|C|{1}{R}|Creature - Goblin Soldier|1|3|Menace$Mobilize 1| Shocking Sharpshooter|Tarkir: Dragonstorm|121|U|{1}{R}|Creature - Human Archer|1|3|Reach$Whenever another creature you control enters, this creature deals 1 damage to target opponent.| +Stadium Headliner|Tarkir: Dragonstorm|122|R|{R}|Creature - Goblin Warrior|1|1|Mobilize 1${1}{R}, Sacrifice this creature: It deals damage equal to the number of creatures you control to target creature.| Stormscale Scion|Tarkir: Dragonstorm|123|M|{4}{R}{R}|Creature - Dragon|4|4|Flying$Other Dragons you control get +1/+1.$Storm| Stormshriek Feral|Tarkir: Dragonstorm|124|C|{4}{R}|Creature - Dragon|3|3|Flying, haste${1}{R}: This creature gets +1/+0 until end of turn.| Flush Out|Tarkir: Dragonstorm|124|C|{1}{R}|Sorcery - Omen|3|3|Discard a card. If you do, draw two cards.| Summit Intimidator|Tarkir: Dragonstorm|125|C|{3}{R}|Creature - Yeti|4|3|Reach$When this creature enters, target creature can't block this turn.| Sunset Strikemaster|Tarkir: Dragonstorm|126|U|{1}{R}|Creature - Human Monk|3|1|{T}: Add {R}.${2}{R}, {T}, Sacrifice this creature: It deals 6 damage to target creature with flying.| Tersa Lightshatter|Tarkir: Dragonstorm|127|R|{2}{R}|Legendary Creature - Orc Wizard|3|3|Haste$When Tersa Lightshatter enters, discard up to two cards, then draw that many cards.$Whenever Tersa Lightshatter attacks, if there are seven or more cards in your graveyard, exile a card at random from your graveyard. You may play that card this turn.| +Twin Bolt|Tarkir: Dragonstorm|128|C|{1}{R}|Instant|||Twin Bolt deals 2 damage divided as you choose among one or two targets.| Underfoot Underdogs|Tarkir: Dragonstorm|129|C|{2}{R}|Creature - Goblin Warrior|1|2|When this creature enters, create a 1/1 red Goblin creature token.${1}, {T}: Target creature you control with power 2 or less can't be blocked this turn.| Unsparing Boltcaster|Tarkir: Dragonstorm|130|U|{2}{R}|Creature - Ogre Wizard|3|3|When this creature enters, it deals 5 damage to target creature an opponent controls that was dealt damage this turn.| War Effort|Tarkir: Dragonstorm|131|U|{3}{R}|Enchantment|||Creatures you control get +1/+0.$Whenever you attack, create a 1/1 red Warrior creature token that's tapped and attacking. Sacrifice it at the beginning of the next end step.| +Wild Ride|Tarkir: Dragonstorm|132|C|{R}|Sorcery|||Target creature gets +3/+0 and gains haste until end of turn.$Harmonize 4{R}| Zurgo's Vanguard|Tarkir: Dragonstorm|133|U|{2}{R}|Creature - Dog Soldier|*|3|Mobilize 1$This creature's power is equal to the number of creatures you control.| +Ainok Wayfarer|Tarkir: Dragonstorm|134|C|{1}{G}|Creature - Dog Scout|1|1|When this creature enters, mill three cards. You may put a land card from among them into your hand. If you don't, put a +1/+1 counter on this creature.| Attuned Hunter|Tarkir: Dragonstorm|135|U|{2}{G}|Creature - Human Ranger|3|3|Trample$Whenever one or more cards leave your graveyard during your turn, put a +1/+1 counter on this creature.| Bloomvine Regent|Tarkir: Dragonstorm|136|R|{3}{G}{G}|Creature - Dragon|4|5|Flying$Whenever this creature or another Dragon you control enters, you gain 3 life.| Claim Territory|Tarkir: Dragonstorm|136|R|{2}{G}|Sorcery - Omen|4|5|Search your library for up to two basic Forest cards, reveal them, put one onto the battlefield tapped and the other into your hand, then shuffle.| +Champion of Dusan|Tarkir: Dragonstorm|137|C|{2}{G}|Creature - Human Warrior|4|2|Trample$Renew -- {1}{G}, Exile this card from your graveyard: Put a +1/+1 counter and a trample counter on target creature. Activate only as a sorcery.| Craterhoof Behemoth|Tarkir: Dragonstorm|138|M|{5}{G}{G}{G}|Creature - Beast|5|5|Haste$When this creature enters, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control.| +Dragon Sniper|Tarkir: Dragonstorm|139|U|{G}|Creature - Human Archer|1|1|Vigilance, reach, deathtouch| Dragonbroods' Relic|Tarkir: Dragonstorm|140|U|{1}{G}|Artifact|||{T}, Tap an untapped creature you control: Add one mana of any color.${3}{W}{U}{B}{R}{G}, Sacrifice this artifact: Create a 4/4 Dragon creature token named Reliquary Dragon that's all colors. It has flying, lifelink, and "When this token enters, it deals 3 damage to any target." Activate only as a sorcery.| Dusyut Earthcarver|Tarkir: Dragonstorm|141|C|{5}{G}|Creature - Elephant Druid|4|4|Reach$When this creature enters, it endures 3.| Encroaching Dragonstorm|Tarkir: Dragonstorm|142|U|{3}{G}|Enchantment|||When this enchantment enters, search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle.$When a Dragon you control enters, return this enchantment to its owner's hand.| +Formation Breaker|Tarkir: Dragonstorm|143|U|{1}{G}|Creature - Beast|2|1|Creatures with power less than this creature's power can't block it.$As long as you control a creature with a counter on it, this creature gets +1/+2| Herd Heirloom|Tarkir: Dragonstorm|144|R|{1}{G}|Artifact|||{T}: Add one mana of any color. Spend this mana only to cast a creature spell.${T}: Until end of turn, target creature you control with power 4 or greater gains trample and "Whenever this creature deals combat damage to a player, draw a card."| Heritage Reclamation|Tarkir: Dragonstorm|145|C|{1}{G}|Instant|||Choose one --$* Destroy target artifact.$* Destroy target enchantment.$* Exile up to one target card from a graveyard. Draw a card.| +Inspirited Vanguard|Tarkir: Dragonstorm|146|U|{4}{G}|Creature - Human Soldier|3|2|Whenever this creature enters or attacks, it endures 2.| +Knockout Maneuver|Tarkir: Dragonstorm|147|U|{2}{G}|Sorcery|||Put a +1/+1 counter on target creature you control, then it deals damage equal to its power to target creature an opponent controls.| Krotiq Nestguard|Tarkir: Dragonstorm|148|C|{2}{G}|Creature - Insect|4|4|Defender${2}{G}: This creature can attack this turn as though it didn't have defender.| Lasyd Prowler|Tarkir: Dragonstorm|149|R|{2}{G}{G}|Creature - Snake Ranger|5|5|When this creature enters, you may mill cards equal to the number of lands you control.$Renew -- {1}{G}, Exile this card from your graveyard: Put X +1/+1 counters on target creature, where X is the number of land cards in your graveyard. Activate only as a sorcery.| Nature's Rhythm|Tarkir: Dragonstorm|150|R|{X}{G}{G}|Sorcery|||Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle.$Harmonize {X}{G}{G}{G}{G}| Piercing Exhale|Tarkir: Dragonstorm|151|C|{1}{G}|Instant|||As an additional cost to cast this spell, you may behold a Dragon.$Target creature you control deals damage equal to its power to target creature or planeswalker. If a Dragon was beheld, surveil 2.| +Rainveil Rejuvenator|Tarkir: Dragonstorm|152|U|{3}{G}|Creature - Elephant Druid|2|4|When this creature enters, you may mill three cards.${T}: Add an amount of {G} equal to this creature's power.| Rite of Renewal|Tarkir: Dragonstorm|153|U|{3}{G}|Sorcery|||Return up to two target permanent cards from your graveyard to your hand. Target player shuffles up to four target cards from their graveyard into their library. Exile Rite of Renewal.| Roamer's Routine|Tarkir: Dragonstorm|154|C|{2}{G}|Sorcery|||Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.$Harmonize {4}{G}| +Sage of the Fang|Tarkir: Dragonstorm|155|U|{2}{G}|Creature - Human Druid|2|2|When this creature enters, put a +1/+1 counter on target creature.$Renew -- {3}{G}, Exile this card from your graveyard: Put a +1/+1 counter on target creature, then double the number of +1/+1 counters on that creature.| +Sagu Pummeler|Tarkir: Dragonstorm|156|C|{3}{G}|Creature - Beast|4|4|Reach$Renew -- {4}{G}, Exile this card from your graveyard: Put two +1/+1 counters and a reach counter on target creature. Activate only as a sorcery.| Sagu Wildling|Tarkir: Dragonstorm|157|C|{4}{G}|Creature - Dragon|3|3|Flying$When this creature enters, you gain 3 life.| Roost Seek|Tarkir: Dragonstorm|157|C|{G}|Sorcery - Omen|3|3|Search your library for a basic land card, reveal it, put it into your hand, then shuffle.| Sarkhan's Resolve|Tarkir: Dragonstorm|158|C|{1}{G}|Instant|||Choose one --$* Target creature gets +3/+3 until end of turn.$* Destroy target creature with flying.| @@ -57330,11 +57368,12 @@ Snakeskin Veil|Tarkir: Dragonstorm|159|C|{G}|Instant|||Put a +1/+1 counter on ta Sultai Devotee|Tarkir: Dragonstorm|160|C|{1}{G}|Creature - Zombie Snake Druid|2|1|Deathtouch${1}: Add {B}, {G}, or {U}. Activate only once each turn.| Surrak, Elusive Hunter|Tarkir: Dragonstorm|161|R|{2}{G}|Legendary Creature - Human Warrior|4|3|This spell can't be countered.$Trample$Whenever a creature you control or a creature spell you control becomes the target of a spell or ability an opponent controls, draw a card.| Synchronized Charge|Tarkir: Dragonstorm|162|U|{1}{G}|Sorcery|||Distribute two +1/+1 counters among one or two target creatures you control. Creatures you control with counters on them gain vigilance and trample until end of turn.$Harmonize {4}{G}| +Trade Route Envoy|Tarkir: Dragonstorm|163|C|{3}{G}|Creature - Dog Soldier|4|3|When this creature enters, draw a card if you control a creature with a counter on it. If you don't draw a card this way, put a +1/+1 counter on this creature.| Traveling Botanist|Tarkir: Dragonstorm|164|U|{1}{G}|Creature - Dog Scout|2|3|Whenever this creature becomes tapped, look at the top card of your library. If it's a land card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard.| Undergrowth Leopard|Tarkir: Dragonstorm|165|C|{1}{G}|Creature - Cat|2|2|Vigilance${1}, Sacrifice this creature: Destroy target artifact or enchantment.| Warden of the Grove|Tarkir: Dragonstorm|166|R|{2}{G}|Creature - Hydra|2|2|At the beginning of your end step, put a +1/+1 counter on this creature.$Whenever another nontoken creature you control enters, it endures X, where X is the number of counters on this creature.| All-Out Assault|Tarkir: Dragonstorm|167|M|{2}{R}{W}{B}|Enchantment|||Creatures you control get +1/+1 and have deathtouch.$When this enchantment enters, if it's your main phase, there is an additional combat phase after this phase followed by an additional main phase. When you next attack this turn, untap each creature you control.| -Armament Dragon|Tarkir: Dragonstorm|168|U|{3}{W}{B}{G}|Creature -- Dragon|3|4|Flying$When this creature enters, distribute three +1/+1 counters among one, two, or three target creatures you control.| +Armament Dragon|Tarkir: Dragonstorm|168|U|{3}{W}{B}{G}|Creature - Dragon|3|4|Flying$When this creature enters, distribute three +1/+1 counters among one, two, or three target creatures you control.| Auroral Procession|Tarkir: Dragonstorm|169|U|{G}{U}|Instant|||Return target card from your graveyard to your hand.| Awaken the Honored Dead|Tarkir: Dragonstorm|170|R|{B}{G}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Destroy target nonland permanent.$II -- Mill three cards.$III -- You may discard a card. When you do, return target creature or land card from your graveyard to your hand.| Barrensteppe Siege|Tarkir: Dragonstorm|171|R|{2}{W}{B}|Enchantment|||As this enchantment enters, choose Abzan or Mardu.$* Abzan -- At the beginning of your end step, put a +1/+1 counter on each creature you control.$* Mardu -- At the beginning of your end step, if a creature died under your control this turn, each opponent sacrifices a creature of their choice.| @@ -57347,6 +57386,7 @@ Defibrillating Current|Tarkir: Dragonstorm|177|U|{2/R}{2/W}{2/B}|Sorcery|||Defib Disruptive Stormbrood|Tarkir: Dragonstorm|178|U|{4}{G}|Creature - Dragon|3|3|Flying$When this creature enters, destroy up to one target artifact or enchantment.| Petty Revenge|Tarkir: Dragonstorm|178|U|{1}{B}|Sorcery - Omen|3|3|Destroy target creature with power 3 or less.| Dragonback Assault|Tarkir: Dragonstorm|179|M|{3}{G}{U}{R}|Enchantment|||When this enchantment enters, it deals 3 damage to each creature and each planeswalker.$Landfall -- Whenever a land you control enters, create a 4/4 red Dragon creature token with flying.| +Dragonclaw Strike|Tarkir: Dragonstorm|180|U|{2/U}{2/R}{2/G}|Sorcery|||Double the power and toughness of target creature you control until end of turn. Then it fights up to one target creature an opponent controls.| Effortless Master|Tarkir: Dragonstorm|181|U|{2}{U}{R}|Creature - Orc Monk|4|3|Vigilance$Menace$This creature enters with two +1/+1 counters on it if you've cast two or more spells this turn.| Eshki Dragonclaw|Tarkir: Dragonstorm|182|R|{1}{G}{U}{R}|Legendary Creature - Human Warrior|4|4|Vigilance, trample, ward {1}$At the beginning of combat on your turn, if you've cast both a creature spell and a noncreature spell this turn, draw a card and put two +1/+1 counters on Eshki Dragonclaw.| Fangkeeper's Familiar|Tarkir: Dragonstorm|183|R|{1}{B}{G}{U}|Creature - Snake|3|3|Flash$When this creature enters, choose one --$* You gain 3 life and surveil 3.$* Destroy target enchantment.$* Counter target creature spell.| @@ -57384,6 +57424,7 @@ Absorb Essence|Tarkir: Dragonstorm|213|U|{1}{W}|Instant - Omen|4|4|Target creatu Rakshasa's Bargain|Tarkir: Dragonstorm|214|U|{2/B}{2/G}{2/U}|Instant|||Look at the top four cards of your library. Put two of them into your hand and the rest into your graveyard.| Rediscover the Way|Tarkir: Dragonstorm|215|R|{U}{R}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order.$III -- Whenever you cast a noncreature spell this turn, target creature you control gains double strike until end of turn.| Reigning Victor|Tarkir: Dragonstorm|216|C|{2/R}{2/W}{2/B}|Creature - Orc Warrior|3|3|Mobilize 1$When this creature enters, target creature gets +1/+0 and gains indestructible until end of turn.| +Reputable Merchant|Tarkir: Dragonstorm|217|C|{2/W}{2/B}{2/G}|Creature - Human Citizen|2|2|When this creature enters or dies, put a +1/+1 counter on target creature you control.| Revival of the Ancestors|Tarkir: Dragonstorm|218|R|{1}{W}{B}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Create three 1/1 white Spirit creature tokens.$II -- Distribute three +1/+1 counters among one, two, or three target creatures you control.$III -- Creatures you control gain trample and lifelink until end of turn.| Riverwheel Sweep|Tarkir: Dragonstorm|219|U|{2/U}{2/R}{2/W}|Sorcery|||Tap target creature. Put three stun counters on it.$Exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card.| Roar of Endless Song|Tarkir: Dragonstorm|220|R|{2}{G}{U}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Create a 5/5 green Elephant creature token.$III -- Double the power and toughness of each creature you control until end of turn.| @@ -57446,7 +57487,6 @@ Island|Tarkir: Dragonstorm|279|C||Basic Land - Island|||({T}: Add {U}.)| Swamp|Tarkir: Dragonstorm|281|C||Basic Land - Swamp|||({T}: Add {B}.)| Mountain|Tarkir: Dragonstorm|283|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Tarkir: Dragonstorm|285|C||Basic Land - Forest|||({T}: Add {G}.)| -Strategic Betrayal|Tarkir: Dragonstorm|422|U|{1}{B}|Sorcery|||Target opponent exiles a creature they control and their graveyard.| Betor, Ancestor's Voice|Tarkir: Dragonstorm Commander|1|M|{2}{W}{B}{G}|Legendary Creature - Spirit Dragon|3|5|Flying, lifelink$At the beginning of your end step, put a number of +1/+1 counters on up to one other target creature you control equal to the amount of life you gained this turn. Return up to one target creature card with mana value less than or equal to the amount of life you lost this turn from your graveyard to the battlefield.| Elsha, Threefold Master|Tarkir: Dragonstorm Commander|2|M|{U}{R}{W}|Legendary Creature - Djinn Monk|1|1|Trample$Prowess$Whenever Elsha deals combat damage to a player, create that many 1/1 white Monk creature tokens with prowess.| Eshki, Temur's Roar|Tarkir: Dragonstorm Commander|3|M|{G}{U}{R}|Legendary Creature - Human Warrior|2|2|Whenever you cast a creature spell, put a +1/+1 counter on Eshki. If that spell's power is 4 or greater, draw a card. If that spell's power is 6 or greater, Eshki deals damage equal to Eshki's power to each opponent.| From 8a98d2c40fce141c7080e9eb41ac1f31d427de2e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 21:57:18 -0400 Subject: [PATCH 07/71] [TDM] Implement Wingblade Disciple --- .../src/mage/cards/w/WingbladeDisciple.java | 43 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 44 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WingbladeDisciple.java diff --git a/Mage.Sets/src/mage/cards/w/WingbladeDisciple.java b/Mage.Sets/src/mage/cards/w/WingbladeDisciple.java new file mode 100644 index 00000000000..e1c925d30e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WingbladeDisciple.java @@ -0,0 +1,43 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.FlurryAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BirdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WingbladeDisciple extends CardImpl { + + public WingbladeDisciple(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Flurry -- Whenever you cast your second spell each turn, create a 1/1 white Bird creature token with flying. + this.addAbility(new FlurryAbility(new CreateTokenEffect(new BirdToken()))); + } + + private WingbladeDisciple(final WingbladeDisciple card) { + super(card); + } + + @Override + public WingbladeDisciple copy() { + return new WingbladeDisciple(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 9f2391c1230..8d0a49853e2 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -170,6 +170,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 271, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); + cards.add(new SetCardInfo("Wingblade Disciple", 65, Rarity.UNCOMMON, mage.cards.w.WingbladeDisciple.class)); cards.add(new SetCardInfo("Wingspan Stride", 66, Rarity.COMMON, mage.cards.w.WingspanStride.class)); cards.add(new SetCardInfo("Winternight Stories", 67, Rarity.RARE, mage.cards.w.WinternightStories.class)); cards.add(new SetCardInfo("Worthy Cost", 99, Rarity.COMMON, mage.cards.w.WorthyCost.class)); From a09943f25acf3d2c24c59967fecd4c7d4c11668e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 21:58:02 -0400 Subject: [PATCH 08/71] [TDM] Implement Sandskitter Outrider --- .../src/mage/cards/s/SandskitterOutrider.java | 42 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 43 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SandskitterOutrider.java diff --git a/Mage.Sets/src/mage/cards/s/SandskitterOutrider.java b/Mage.Sets/src/mage/cards/s/SandskitterOutrider.java new file mode 100644 index 00000000000..1904c7d10ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SandskitterOutrider.java @@ -0,0 +1,42 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.keyword.EndureSourceEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SandskitterOutrider extends CardImpl { + + public SandskitterOutrider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility()); + + // When this creature enters, it endures 2. + this.addAbility(new EntersBattlefieldTriggeredAbility(new EndureSourceEffect(2))); + } + + private SandskitterOutrider(final SandskitterOutrider card) { + super(card); + } + + @Override + public SandskitterOutrider copy() { + return new SandskitterOutrider(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 8d0a49853e2..0e248caca0c 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -130,6 +130,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class)); cards.add(new SetCardInfo("Rugged Highlands", 265, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); cards.add(new SetCardInfo("Salt Road Packbeast", 23, Rarity.COMMON, mage.cards.s.SaltRoadPackbeast.class)); + cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 266, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); cards.add(new SetCardInfo("Sarkhan's Resolve", 158, Rarity.COMMON, mage.cards.s.SarkhansResolve.class)); cards.add(new SetCardInfo("Scoured Barrens", 267, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); From c77fbe2a888f029a1a26e52d92d7e41a57b68e81 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 21:59:13 -0400 Subject: [PATCH 09/71] [TDM] Implement Sagu Pummeler --- Mage.Sets/src/mage/cards/s/SaguPummeler.java | 45 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 46 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SaguPummeler.java diff --git a/Mage.Sets/src/mage/cards/s/SaguPummeler.java b/Mage.Sets/src/mage/cards/s/SaguPummeler.java new file mode 100644 index 00000000000..dcad1d18f74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaguPummeler.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.RenewAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SaguPummeler extends CardImpl { + + public SaguPummeler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Renew -- {4}{G}, Exile this card from your graveyard: Put two +1/+1 counters and a reach counter on target creature. Activate only as a sorcery. + this.addAbility(new RenewAbility( + "{4}{G}", + CounterType.P1P1.createInstance(2), + CounterType.REACH.createInstance() + )); + } + + private SaguPummeler(final SaguPummeler card) { + super(card); + } + + @Override + public SaguPummeler copy() { + return new SaguPummeler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 0e248caca0c..36f3aeadbf0 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -129,6 +129,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class)); cards.add(new SetCardInfo("Rugged Highlands", 265, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); + cards.add(new SetCardInfo("Sagu Pummeler", 156, Rarity.COMMON, mage.cards.s.SaguPummeler.class)); cards.add(new SetCardInfo("Salt Road Packbeast", 23, Rarity.COMMON, mage.cards.s.SaltRoadPackbeast.class)); cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 266, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); From efa746b8084734306549f6c873a0027dd10d2636 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:01:22 -0400 Subject: [PATCH 10/71] [TDM] Implement Reputable Merchant --- .../src/mage/cards/r/ReputableMerchant.java | 45 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 46 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/ReputableMerchant.java diff --git a/Mage.Sets/src/mage/cards/r/ReputableMerchant.java b/Mage.Sets/src/mage/cards/r/ReputableMerchant.java new file mode 100644 index 00000000000..808a913f665 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReputableMerchant.java @@ -0,0 +1,45 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrDiesSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReputableMerchant extends CardImpl { + + public ReputableMerchant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2/W}{2/B}{2/G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When this creature enters or dies, put a +1/+1 counter on target creature you control. + Ability ability = new EntersBattlefieldOrDiesSourceTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ReputableMerchant(final ReputableMerchant card) { + super(card); + } + + @Override + public ReputableMerchant copy() { + return new ReputableMerchant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 36f3aeadbf0..d6179d7f5b9 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -124,6 +124,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Rally the Monastery", 19, Rarity.UNCOMMON, mage.cards.r.RallyTheMonastery.class)); cards.add(new SetCardInfo("Rebellious Strike", 20, Rarity.COMMON, mage.cards.r.RebelliousStrike.class)); cards.add(new SetCardInfo("Reigning Victor", 216, Rarity.COMMON, mage.cards.r.ReigningVictor.class)); + cards.add(new SetCardInfo("Reputable Merchant", 217, Rarity.COMMON, mage.cards.r.ReputableMerchant.class)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); From e93f48b989762e9f76ea5d4a8477cef0fdc74166 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:02:44 -0400 Subject: [PATCH 11/71] [TDM] Implement Poised Practitioner --- .../src/mage/cards/p/PoisedPractitioner.java | 43 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 44 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/PoisedPractitioner.java diff --git a/Mage.Sets/src/mage/cards/p/PoisedPractitioner.java b/Mage.Sets/src/mage/cards/p/PoisedPractitioner.java new file mode 100644 index 00000000000..5195c3a0a28 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PoisedPractitioner.java @@ -0,0 +1,43 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.FlurryAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PoisedPractitioner extends CardImpl { + + public PoisedPractitioner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flurry -- Whenever you cast your second spell each turn, put a +1/+1 counter on this creature. Scry 1. + Ability ability = new FlurryAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + ability.addEffect(new ScryEffect(1)); + this.addAbility(ability); + } + + private PoisedPractitioner(final PoisedPractitioner card) { + super(card); + } + + @Override + public PoisedPractitioner copy() { + return new PoisedPractitioner(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index d6179d7f5b9..b07b6c3b547 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -119,6 +119,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Osseous Exhale", 17, Rarity.COMMON, mage.cards.o.OsseousExhale.class)); cards.add(new SetCardInfo("Piercing Exhale", 151, Rarity.COMMON, mage.cards.p.PiercingExhale.class)); cards.add(new SetCardInfo("Plains", 277, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Poised Practitioner", 18, Rarity.COMMON, mage.cards.p.PoisedPractitioner.class)); cards.add(new SetCardInfo("Qarsi Revenant", 86, Rarity.RARE, mage.cards.q.QarsiRevenant.class)); cards.add(new SetCardInfo("Rakshasa's Bargain", 214, Rarity.UNCOMMON, mage.cards.r.RakshasasBargain.class)); cards.add(new SetCardInfo("Rally the Monastery", 19, Rarity.UNCOMMON, mage.cards.r.RallyTheMonastery.class)); From 45bd8d4ce616a7b2efc06fd1f75cb5ccf2c03eb8 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:03:14 -0400 Subject: [PATCH 12/71] [TDM] Implement Nightblade Brigade --- .../src/mage/cards/n/NightbladeBrigade.java | 46 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 47 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/n/NightbladeBrigade.java diff --git a/Mage.Sets/src/mage/cards/n/NightbladeBrigade.java b/Mage.Sets/src/mage/cards/n/NightbladeBrigade.java new file mode 100644 index 00000000000..2002ced44ad --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NightbladeBrigade.java @@ -0,0 +1,46 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.MobilizeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NightbladeBrigade extends CardImpl { + + public NightbladeBrigade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Mobilize 1 + this.addAbility(new MobilizeAbility(1)); + + // When this creature enters, surveil 1. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SurveilEffect(1))); + } + + private NightbladeBrigade(final NightbladeBrigade card) { + super(card); + } + + @Override + public NightbladeBrigade copy() { + return new NightbladeBrigade(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index b07b6c3b547..48386dce0f3 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -114,6 +114,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 209, Rarity.RARE, mage.cards.n.NarsetJeskaiWaymaster.class)); cards.add(new SetCardInfo("Nature's Rhythm", 150, Rarity.RARE, mage.cards.n.NaturesRhythm.class)); cards.add(new SetCardInfo("Neriv, Heart of the Storm", 210, Rarity.MYTHIC, mage.cards.n.NerivHeartOfTheStorm.class)); + cards.add(new SetCardInfo("Nightblade Brigade", 85, Rarity.COMMON, mage.cards.n.NightbladeBrigade.class)); cards.add(new SetCardInfo("Nomad Outpost", 263, Rarity.UNCOMMON, mage.cards.n.NomadOutpost.class)); cards.add(new SetCardInfo("Opulent Palace", 264, Rarity.UNCOMMON, mage.cards.o.OpulentPalace.class)); cards.add(new SetCardInfo("Osseous Exhale", 17, Rarity.COMMON, mage.cards.o.OsseousExhale.class)); From 2a4d2cb1c2c75b1640de4dc897763470970cdd04 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:04:05 -0400 Subject: [PATCH 13/71] [TDM] Implement Kin-Tree Nurturer --- .../src/mage/cards/k/KinTreeNurturer.java | 42 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 43 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KinTreeNurturer.java diff --git a/Mage.Sets/src/mage/cards/k/KinTreeNurturer.java b/Mage.Sets/src/mage/cards/k/KinTreeNurturer.java new file mode 100644 index 00000000000..abba7ff3cd9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KinTreeNurturer.java @@ -0,0 +1,42 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.keyword.EndureSourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KinTreeNurturer extends CardImpl { + + public KinTreeNurturer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // When this creature enters, it endures 1. + this.addAbility(new EntersBattlefieldTriggeredAbility(new EndureSourceEffect(1))); + } + + private KinTreeNurturer(final KinTreeNurturer card) { + super(card); + } + + @Override + public KinTreeNurturer copy() { + return new KinTreeNurturer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 48386dce0f3..32206fe66a9 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -95,6 +95,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Jeskai Shrinekeeper", 197, Rarity.UNCOMMON, mage.cards.j.JeskaiShrinekeeper.class)); cards.add(new SetCardInfo("Jungle Hollow", 258, Rarity.COMMON, mage.cards.j.JungleHollow.class)); cards.add(new SetCardInfo("Kheru Goldkeeper", 199, Rarity.UNCOMMON, mage.cards.k.KheruGoldkeeper.class)); + cards.add(new SetCardInfo("Kin-Tree Nurturer", 83, Rarity.COMMON, mage.cards.k.KinTreeNurturer.class)); cards.add(new SetCardInfo("Kin-Tree Severance", 200, Rarity.UNCOMMON, mage.cards.k.KinTreeSeverance.class)); cards.add(new SetCardInfo("Kishla Skimmer", 201, Rarity.UNCOMMON, mage.cards.k.KishlaSkimmer.class)); cards.add(new SetCardInfo("Kishla Trawlers", 50, Rarity.UNCOMMON, mage.cards.k.KishlaTrawlers.class)); From 7a8e3d3c7d1abc4f4605d8f9ce0beae29255311f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:07:37 -0400 Subject: [PATCH 14/71] [TDM] Implement Overwhelming Surge --- .../src/mage/cards/o/OverwhelmingSurge.java | 44 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 45 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/o/OverwhelmingSurge.java diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmingSurge.java b/Mage.Sets/src/mage/cards/o/OverwhelmingSurge.java new file mode 100644 index 00000000000..d7734ac4db0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OverwhelmingSurge.java @@ -0,0 +1,44 @@ +package mage.cards.o; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OverwhelmingSurge extends CardImpl { + + public OverwhelmingSurge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Choose one or both -- + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // * Overwhelming Surge deals 3 damage to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // * Destroy target noncreature artifact. + this.getSpellAbility().addMode(new Mode(new DestroyTargetEffect()) + .addTarget(new TargetPermanent(StaticFilters.FILTER_ARTIFACT_NON_CREATURE))); + } + + private OverwhelmingSurge(final OverwhelmingSurge card) { + super(card); + } + + @Override + public OverwhelmingSurge copy() { + return new OverwhelmingSurge(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 32206fe66a9..77f1b6fb172 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -119,6 +119,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Nomad Outpost", 263, Rarity.UNCOMMON, mage.cards.n.NomadOutpost.class)); cards.add(new SetCardInfo("Opulent Palace", 264, Rarity.UNCOMMON, mage.cards.o.OpulentPalace.class)); cards.add(new SetCardInfo("Osseous Exhale", 17, Rarity.COMMON, mage.cards.o.OsseousExhale.class)); + cards.add(new SetCardInfo("Overwhelming Surge", 115, Rarity.UNCOMMON, mage.cards.o.OverwhelmingSurge.class)); cards.add(new SetCardInfo("Piercing Exhale", 151, Rarity.COMMON, mage.cards.p.PiercingExhale.class)); cards.add(new SetCardInfo("Plains", 277, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Poised Practitioner", 18, Rarity.COMMON, mage.cards.p.PoisedPractitioner.class)); From c1884d156551185c9b9203c93f6eeb05cd78e0ad Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:08:16 -0400 Subject: [PATCH 15/71] [TDM] Implement Inspirited Vanguard --- .../src/mage/cards/i/InspiritedVanguard.java | 38 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 39 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/InspiritedVanguard.java diff --git a/Mage.Sets/src/mage/cards/i/InspiritedVanguard.java b/Mage.Sets/src/mage/cards/i/InspiritedVanguard.java new file mode 100644 index 00000000000..48dc148a261 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InspiritedVanguard.java @@ -0,0 +1,38 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.effects.keyword.EndureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InspiritedVanguard extends CardImpl { + + public InspiritedVanguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever this creature enters or attacks, it endures 2. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new EndureSourceEffect(2))); + } + + private InspiritedVanguard(final InspiritedVanguard card) { + super(card); + } + + @Override + public InspiritedVanguard copy() { + return new InspiritedVanguard(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 77f1b6fb172..637730e3d37 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -87,6 +87,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Hardened Tactician", 191, Rarity.UNCOMMON, mage.cards.h.HardenedTactician.class)); cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class)); + cards.add(new SetCardInfo("Inspirited Vanguard", 146, Rarity.UNCOMMON, mage.cards.i.InspiritedVanguard.class)); cards.add(new SetCardInfo("Island", 279, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jade-Cast Sentinel", 243, Rarity.COMMON, mage.cards.j.JadeCastSentinel.class)); cards.add(new SetCardInfo("Jeskai Brushmaster", 195, Rarity.UNCOMMON, mage.cards.j.JeskaiBrushmaster.class)); From 153925349465c8ee5a8ec23160edbe0170e8ba1d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:09:53 -0400 Subject: [PATCH 16/71] [TDM] Implement Champion of Dusan --- .../src/mage/cards/c/ChampionOfDusan.java | 46 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 47 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/ChampionOfDusan.java diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfDusan.java b/Mage.Sets/src/mage/cards/c/ChampionOfDusan.java new file mode 100644 index 00000000000..ab384f74f2b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChampionOfDusan.java @@ -0,0 +1,46 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.RenewAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChampionOfDusan extends CardImpl { + + public ChampionOfDusan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Renew -- {1}{G}, Exile this card from your graveyard: Put a +1/+1 counter and a trample counter on target creature. Activate only as a sorcery. + this.addAbility(new RenewAbility( + "{1}{G}", + CounterType.P1P1.createInstance(), + CounterType.TRAMPLE.createInstance() + )); + } + + private ChampionOfDusan(final ChampionOfDusan card) { + super(card); + } + + @Override + public ChampionOfDusan copy() { + return new ChampionOfDusan(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 637730e3d37..3ce8f998c08 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -46,6 +46,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Breaching Dragonstorm", 101, Rarity.UNCOMMON, mage.cards.b.BreachingDragonstorm.class)); cards.add(new SetCardInfo("Call the Spirit Dragons", 174, Rarity.MYTHIC, mage.cards.c.CallTheSpiritDragons.class)); cards.add(new SetCardInfo("Caustic Exhale", 74, Rarity.COMMON, mage.cards.c.CausticExhale.class)); + cards.add(new SetCardInfo("Champion of Dusan", 137, Rarity.COMMON, mage.cards.c.ChampionOfDusan.class)); cards.add(new SetCardInfo("Channeled Dragonfire", 102, Rarity.UNCOMMON, mage.cards.c.ChanneledDragonfire.class)); cards.add(new SetCardInfo("Coordinated Maneuver", 6, Rarity.COMMON, mage.cards.c.CoordinatedManeuver.class)); cards.add(new SetCardInfo("Cori Mountain Monastery", 252, Rarity.RARE, mage.cards.c.CoriMountainMonastery.class)); From b1342f33f1244a43681d0e006ad27ca4d321dbdd Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:10:25 -0400 Subject: [PATCH 17/71] [TDM] Implement Dragon Sniper --- Mage.Sets/src/mage/cards/d/DragonSniper.java | 45 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 46 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DragonSniper.java diff --git a/Mage.Sets/src/mage/cards/d/DragonSniper.java b/Mage.Sets/src/mage/cards/d/DragonSniper.java new file mode 100644 index 00000000000..336d59958f4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonSniper.java @@ -0,0 +1,45 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonSniper extends CardImpl { + + public DragonSniper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + } + + private DragonSniper(final DragonSniper card) { + super(card); + } + + @Override + public DragonSniper copy() { + return new DragonSniper(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 3ce8f998c08..cd844840d30 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -63,6 +63,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Dismal Backwater", 254, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); cards.add(new SetCardInfo("Dispelling Exhale", 41, Rarity.COMMON, mage.cards.d.DispellingExhale.class)); cards.add(new SetCardInfo("Dracogenesis", 105, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class)); + cards.add(new SetCardInfo("Dragon Sniper", 139, Rarity.UNCOMMON, mage.cards.d.DragonSniper.class)); cards.add(new SetCardInfo("Dragon's Prey", 79, Rarity.COMMON, mage.cards.d.DragonsPrey.class)); cards.add(new SetCardInfo("Dragonback Assault", 179, Rarity.MYTHIC, mage.cards.d.DragonbackAssault.class)); cards.add(new SetCardInfo("Dragonback Lancer", 9, Rarity.COMMON, mage.cards.d.DragonbackLancer.class)); From baeb75cca7846c7fb576f88e7bb07c9a7ce55663 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:12:36 -0400 Subject: [PATCH 18/71] [TDM] Implement Iridescent Tiger --- .../src/mage/cards/i/IridescentTiger.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/IridescentTiger.java diff --git a/Mage.Sets/src/mage/cards/i/IridescentTiger.java b/Mage.Sets/src/mage/cards/i/IridescentTiger.java new file mode 100644 index 00000000000..d20ec915a85 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IridescentTiger.java @@ -0,0 +1,41 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IridescentTiger extends CardImpl { + + public IridescentTiger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When this creature enters, if you cast it, add {W}{U}{B}{R}{G}. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new BasicManaEffect(new Mana(1, 1, 1, 1, 1, 0, 0, 0)) + ).withInterveningIf(CastFromEverywhereSourceCondition.instance)); + } + + private IridescentTiger(final IridescentTiger card) { + super(card); + } + + @Override + public IridescentTiger copy() { + return new IridescentTiger(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index cd844840d30..2114d2e44ea 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -90,6 +90,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class)); cards.add(new SetCardInfo("Inspirited Vanguard", 146, Rarity.UNCOMMON, mage.cards.i.InspiritedVanguard.class)); + cards.add(new SetCardInfo("Iridescent Tiger", 109, Rarity.UNCOMMON, mage.cards.i.IridescentTiger.class)); cards.add(new SetCardInfo("Island", 279, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jade-Cast Sentinel", 243, Rarity.COMMON, mage.cards.j.JadeCastSentinel.class)); cards.add(new SetCardInfo("Jeskai Brushmaster", 195, Rarity.UNCOMMON, mage.cards.j.JeskaiBrushmaster.class)); From a0ccbc741c47463c3a56b3113de96497c49760d4 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:14:00 -0400 Subject: [PATCH 19/71] [TDM] Implement Wild Ride --- Mage.Sets/src/mage/cards/w/WildRide.java | 44 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 45 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WildRide.java diff --git a/Mage.Sets/src/mage/cards/w/WildRide.java b/Mage.Sets/src/mage/cards/w/WildRide.java new file mode 100644 index 00000000000..c049cc6f6a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WildRide.java @@ -0,0 +1,44 @@ +package mage.cards.w; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HarmonizeAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WildRide extends CardImpl { + + public WildRide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); + + // Target creature gets +3/+0 and gains haste until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 3, 0, Duration.EndOfTurn + ).setText("Target creature gets +3/+0")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains haste until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Harmonize {4}{R} + this.addAbility(new HarmonizeAbility(this, "{4}{R}")); + } + + private WildRide(final WildRide card) { + super(card); + } + + @Override + public WildRide copy() { + return new WildRide(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 2114d2e44ea..0be9593d46d 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -180,6 +180,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Venerated Stormsinger", 97, Rarity.UNCOMMON, mage.cards.v.VeneratedStormsinger.class)); cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); + cards.add(new SetCardInfo("Wild Ride", 132, Rarity.COMMON, mage.cards.w.WildRide.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 271, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); cards.add(new SetCardInfo("Wingblade Disciple", 65, Rarity.UNCOMMON, mage.cards.w.WingbladeDisciple.class)); cards.add(new SetCardInfo("Wingspan Stride", 66, Rarity.COMMON, mage.cards.w.WingspanStride.class)); From 6780f1d004ee21de3ad75e8148f1d195bf403406 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:15:33 -0400 Subject: [PATCH 20/71] [TDM] Implement Adorned Crocodile --- .../src/mage/cards/a/AdornedCrocodile.java | 43 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 44 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AdornedCrocodile.java diff --git a/Mage.Sets/src/mage/cards/a/AdornedCrocodile.java b/Mage.Sets/src/mage/cards/a/AdornedCrocodile.java new file mode 100644 index 00000000000..e50e57f63db --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdornedCrocodile.java @@ -0,0 +1,43 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.RenewAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.permanent.token.ZombieDruidToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AdornedCrocodile extends CardImpl { + + public AdornedCrocodile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.CROCODILE); + this.power = new MageInt(5); + this.toughness = new MageInt(3); + + // When this creature dies, create a 2/2 black Zombie Druid creature token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieDruidToken()))); + + // Renew -- {B}, Exile this card from your graveyard: Put a +1/+1 counter on target creature. Activate only as a sorcery. + this.addAbility(new RenewAbility("{B}", CounterType.P1P1.createInstance())); + } + + private AdornedCrocodile(final AdornedCrocodile card) { + super(card); + } + + @Override + public AdornedCrocodile copy() { + return new AdornedCrocodile(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 0be9593d46d..07e059382e5 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -26,6 +26,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Abzan Devotee", 68, Rarity.COMMON, mage.cards.a.AbzanDevotee.class)); cards.add(new SetCardInfo("Abzan Monument", 238, Rarity.UNCOMMON, mage.cards.a.AbzanMonument.class)); + cards.add(new SetCardInfo("Adorned Crocodile", 69, Rarity.COMMON, mage.cards.a.AdornedCrocodile.class)); cards.add(new SetCardInfo("Aegis Sculptor", 35, Rarity.UNCOMMON, mage.cards.a.AegisSculptor.class)); cards.add(new SetCardInfo("Agent of Kotis", 36, Rarity.COMMON, mage.cards.a.AgentOfKotis.class)); cards.add(new SetCardInfo("Alesha's Legacy", 72, Rarity.COMMON, mage.cards.a.AleshasLegacy.class)); From fce9caccb06ef420479a73e30f9eda90eabce065 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 28 Mar 2025 22:19:06 -0400 Subject: [PATCH 21/71] [TDM] Implement Sibsig Appraiser --- .../src/mage/cards/s/SibsigAppraiser.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SibsigAppraiser.java diff --git a/Mage.Sets/src/mage/cards/s/SibsigAppraiser.java b/Mage.Sets/src/mage/cards/s/SibsigAppraiser.java new file mode 100644 index 00000000000..43af926d85b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SibsigAppraiser.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SibsigAppraiser extends CardImpl { + + public SibsigAppraiser(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When this creature enters, look at the top two cards of your library. Put one of them into your hand and the other into your graveyard. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( + 2, 1, PutCards.HAND, PutCards.GRAVEYARD + ))); + } + + private SibsigAppraiser(final SibsigAppraiser card) { + super(card); + } + + @Override + public SibsigAppraiser copy() { + return new SibsigAppraiser(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 07e059382e5..d4b565c1c25 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -149,6 +149,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class)); cards.add(new SetCardInfo("Shock Brigade", 120, Rarity.COMMON, mage.cards.s.ShockBrigade.class)); cards.add(new SetCardInfo("Shocking Sharpshooter", 121, Rarity.UNCOMMON, mage.cards.s.ShockingSharpshooter.class)); + cards.add(new SetCardInfo("Sibsig Appraiser", 56, Rarity.COMMON, mage.cards.s.SibsigAppraiser.class)); cards.add(new SetCardInfo("Sinkhole Surveyor", 93, Rarity.RARE, mage.cards.s.SinkholeSurveyor.class)); cards.add(new SetCardInfo("Skirmish Rhino", 224, Rarity.UNCOMMON, mage.cards.s.SkirmishRhino.class)); cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class)); From 176c63c5e974ab73d869eb936fc453b19b5737de Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:12:40 +0000 Subject: [PATCH 22/71] [TDC] Implement Teval's Judgement --- .../src/mage/cards/t/TevalsJudgement.java | 47 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 49 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TevalsJudgement.java diff --git a/Mage.Sets/src/mage/cards/t/TevalsJudgement.java b/Mage.Sets/src/mage/cards/t/TevalsJudgement.java new file mode 100644 index 00000000000..b08fa215541 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TevalsJudgement.java @@ -0,0 +1,47 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.TreasureToken; +import mage.game.permanent.token.ZombieDruidToken; + +import java.util.UUID; + +/** + * @author PurpleCrowbar + */ +public final class TevalsJudgement extends CardImpl { + + public TevalsJudgement(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Whenever one or more cards leave your graveyard, choose one that hasn’t been chosen this turn -- + // * Draw a card. + Ability ability = new CardsLeaveGraveyardTriggeredAbility( + new DrawCardSourceControllerEffect(1) + ); + ability.getModes().setLimitUsageByOnce(true); + + // * Create a Treasure token. + ability.addMode(new Mode(new CreateTokenEffect(new TreasureToken()))); + + // * Create a 2/2 black Zombie Druid creature token. + ability.addMode(new Mode(new CreateTokenEffect(new ZombieDruidToken()))); + this.addAbility(ability); + } + + private TevalsJudgement(final TevalsJudgement card) { + super(card); + } + + @Override + public TevalsJudgement copy() { + return new TevalsJudgement(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index f87dc17574a..29bebed6fff 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -304,6 +304,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Temur Ascendancy", 305, Rarity.RARE, mage.cards.t.TemurAscendancy.class)); cards.add(new SetCardInfo("Terramorphic Expanse", 408, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); cards.add(new SetCardInfo("Territorial Hellkite", 240, Rarity.RARE, mage.cards.t.TerritorialHellkite.class)); + cards.add(new SetCardInfo("Teval's Judgement", 28, Rarity.RARE, mage.cards.t.TevalsJudgement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval's Judgement", 68, Rarity.RARE, mage.cards.t.TevalsJudgement.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Teval, the Balanced Scale", 8, Rarity.MYTHIC, mage.cards.t.TevalTheBalancedScale.class)); cards.add(new SetCardInfo("Thalisse, Reverent Medium", 306, Rarity.UNCOMMON, mage.cards.t.ThalisseReverentMedium.class)); cards.add(new SetCardInfo("Think Twice", 168, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); From e45f9494a3eec100d1344b082453cbabf356a33e Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sat, 29 Mar 2025 14:35:18 +0000 Subject: [PATCH 23/71] Fix Glarb, Calamity's Augur rules text --- Mage.Sets/src/mage/cards/g/GlarbCalamitysAugur.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/g/GlarbCalamitysAugur.java b/Mage.Sets/src/mage/cards/g/GlarbCalamitysAugur.java index 164f123c72c..516bf91b1f5 100644 --- a/Mage.Sets/src/mage/cards/g/GlarbCalamitysAugur.java +++ b/Mage.Sets/src/mage/cards/g/GlarbCalamitysAugur.java @@ -54,7 +54,7 @@ public final class GlarbCalamitysAugur extends CardImpl { this.addAbility(new SimpleStaticAbility(new PlayFromTopOfLibraryEffect(filter))); // {T}: Surveil 2. - this.addAbility(new SimpleActivatedAbility(new SurveilEffect(2), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new SurveilEffect(2, false), new TapSourceCost())); } private GlarbCalamitysAugur(final GlarbCalamitysAugur card) { From d9d7dbd9f18ea467f2317b70ed42459def4bb542 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:18:44 -0400 Subject: [PATCH 24/71] [TDM] update spoiler --- Utils/mtg-cards-data.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 9a845ef758c..473865c8d09 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -57237,7 +57237,7 @@ Aegis Sculptor|Tarkir: Dragonstorm|35|U|{3}{U}|Creature - Bird Wizard|2|3|Flying Agent of Kotis|Tarkir: Dragonstorm|36|C|{1}{U}|Creature - Human Rogue|2|1|Renew -- {3}{U}, Exile this card from your graveyard: Put two +1/+1 counters on target creature. Activate only as a sorcery.| Ambling Stormshell|Tarkir: Dragonstorm|37|R|{3}{U}{U}|Creature - Turtle|5|9|Ward {2}$Whenever this creature attacks, put three stun counters on it and draw three cards.$Whenever you cast a Turtle spell, untap this creature.| Bewildering Blizzard|Tarkir: Dragonstorm|38|U|{4}{U}{U}|Instant|||Draw three cards. Creatures your opponents control get -3/-0 until end of turn.| -Constrictor Sage|Tarkir: Dragonstorm|39|U|{4}{U}|Creature - Snake Wizard|4|4|When this creature enters, tap target creature an opponent controls and put a stun counter on it.$Renew -- 2{U}, Exile this card from your graveyard: Tap target creature an opponent controls and put a stun counter on it. Activate only as a sorcery.| +Constrictor Sage|Tarkir: Dragonstorm|39|U|{4}{U}|Creature - Snake Wizard|4|4|When this creature enters, tap target creature an opponent controls and put a stun counter on it.$Renew -- {2}{U}, Exile this card from your graveyard: Tap target creature an opponent controls and put a stun counter on it. Activate only as a sorcery.| Dirgur Island Dragon|Tarkir: Dragonstorm|40|C|{5}{U}|Creature - Dragon|4|4|Flying$Ward {2}| Skimming Strike|Tarkir: Dragonstorm|40|C|{1}{U}|Instant - Omen|4|4|Tap up to one target creature. Draw a card.| Dispelling Exhale|Tarkir: Dragonstorm|41|C|{1}{U}|Instant|||As an additional cost to cast this spell, you may behold a Dragon.$Counter target spell unless its controller pays {2}. If a Dragon was beheld, counter that spell unless its controller pays {4} instead.| @@ -57335,7 +57335,7 @@ Twin Bolt|Tarkir: Dragonstorm|128|C|{1}{R}|Instant|||Twin Bolt deals 2 damage di Underfoot Underdogs|Tarkir: Dragonstorm|129|C|{2}{R}|Creature - Goblin Warrior|1|2|When this creature enters, create a 1/1 red Goblin creature token.${1}, {T}: Target creature you control with power 2 or less can't be blocked this turn.| Unsparing Boltcaster|Tarkir: Dragonstorm|130|U|{2}{R}|Creature - Ogre Wizard|3|3|When this creature enters, it deals 5 damage to target creature an opponent controls that was dealt damage this turn.| War Effort|Tarkir: Dragonstorm|131|U|{3}{R}|Enchantment|||Creatures you control get +1/+0.$Whenever you attack, create a 1/1 red Warrior creature token that's tapped and attacking. Sacrifice it at the beginning of the next end step.| -Wild Ride|Tarkir: Dragonstorm|132|C|{R}|Sorcery|||Target creature gets +3/+0 and gains haste until end of turn.$Harmonize 4{R}| +Wild Ride|Tarkir: Dragonstorm|132|C|{R}|Sorcery|||Target creature gets +3/+0 and gains haste until end of turn.$Harmonize {4}{R}| Zurgo's Vanguard|Tarkir: Dragonstorm|133|U|{2}{R}|Creature - Dog Soldier|*|3|Mobilize 1$This creature's power is equal to the number of creatures you control.| Ainok Wayfarer|Tarkir: Dragonstorm|134|C|{1}{G}|Creature - Dog Scout|1|1|When this creature enters, mill three cards. You may put a land card from among them into your hand. If you don't, put a +1/+1 counter on this creature.| Attuned Hunter|Tarkir: Dragonstorm|135|U|{2}{G}|Creature - Human Ranger|3|3|Trample$Whenever one or more cards leave your graveyard during your turn, put a +1/+1 counter on this creature.| @@ -57347,7 +57347,7 @@ Dragon Sniper|Tarkir: Dragonstorm|139|U|{G}|Creature - Human Archer|1|1|Vigilanc Dragonbroods' Relic|Tarkir: Dragonstorm|140|U|{1}{G}|Artifact|||{T}, Tap an untapped creature you control: Add one mana of any color.${3}{W}{U}{B}{R}{G}, Sacrifice this artifact: Create a 4/4 Dragon creature token named Reliquary Dragon that's all colors. It has flying, lifelink, and "When this token enters, it deals 3 damage to any target." Activate only as a sorcery.| Dusyut Earthcarver|Tarkir: Dragonstorm|141|C|{5}{G}|Creature - Elephant Druid|4|4|Reach$When this creature enters, it endures 3.| Encroaching Dragonstorm|Tarkir: Dragonstorm|142|U|{3}{G}|Enchantment|||When this enchantment enters, search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle.$When a Dragon you control enters, return this enchantment to its owner's hand.| -Formation Breaker|Tarkir: Dragonstorm|143|U|{1}{G}|Creature - Beast|2|1|Creatures with power less than this creature's power can't block it.$As long as you control a creature with a counter on it, this creature gets +1/+2| +Formation Breaker|Tarkir: Dragonstorm|143|U|{1}{G}|Creature - Beast|2|1|Creatures with power less than this creature's power can't block it.$As long as you control a creature with a counter on it, this creature gets +1/+2.| Herd Heirloom|Tarkir: Dragonstorm|144|R|{1}{G}|Artifact|||{T}: Add one mana of any color. Spend this mana only to cast a creature spell.${T}: Until end of turn, target creature you control with power 4 or greater gains trample and "Whenever this creature deals combat damage to a player, draw a card."| Heritage Reclamation|Tarkir: Dragonstorm|145|C|{1}{G}|Instant|||Choose one --$* Destroy target artifact.$* Destroy target enchantment.$* Exile up to one target card from a graveyard. Draw a card.| Inspirited Vanguard|Tarkir: Dragonstorm|146|U|{4}{G}|Creature - Human Soldier|3|2|Whenever this creature enters or attacks, it endures 2.| @@ -57404,7 +57404,7 @@ Inevitable Defeat|Tarkir: Dragonstorm|194|R|{1}{R}{W}{B}|Instant|||This spell ca Jeskai Brushmaster|Tarkir: Dragonstorm|195|U|{1}{U}{R}{W}|Creature - Orc Monk|2|4|Double strike$Prowess| Jeskai Revelation|Tarkir: Dragonstorm|196|M|{4}{U}{R}{W}|Instant|||Return target spell or permanent to its owner's hand. Jeskai Revelation deals 4 damage to any target. Create two 1/1 white Monk creature tokens with prowess. Draw two cards. You gain 4 life.| Jeskai Shrinekeeper|Tarkir: Dragonstorm|197|U|{2}{U}{R}{W}|Creature - Dragon|3|3|Flying, haste$Whenever this creature deals combat damage to a player, you gain 1 life and draw a card.| -Karakyk Guardian|Tarkir: Dragonstorm|198|U|{3}{G}{U}{R}|Creature -- Dragon|6|5|Flying, vigilance, trample$This creature has hexproof if it hasn't dealt damage yet.| +Karakyk Guardian|Tarkir: Dragonstorm|198|U|{3}{G}{U}{R}|Creature - Dragon|6|5|Flying, vigilance, trample$This creature has hexproof if it hasn't dealt damage yet.| Kheru Goldkeeper|Tarkir: Dragonstorm|199|U|{1}{B}{G}{U}|Creature - Dragon|3|3|Flying$Whenever one or more cards leave your graveyard during your turn, create a Treasure token.$Renew -- {2}{B}{G}{U}, Exile this card from your graveyard: Put two +1/+1 counters and a flying counter on target creature. Activate only as a sorcery.| Kin-Tree Severance|Tarkir: Dragonstorm|200|U|{2/W}{2/B}{2/G}|Instant|||Exile target permanent with mana value 3 or greater.| Kishla Skimmer|Tarkir: Dragonstorm|201|U|{G}{U}|Creature - Bird Scout|2|2|Flying$Whenever a card leaves your graveyard during your turn, draw a card. This ability triggers only once each turn.| From eafa45a54910f5938bd8fd03eaf008e34dbc9461 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:21:24 -0400 Subject: [PATCH 25/71] [TDC] fix Teval's Judgment name --- .../t/{TevalsJudgement.java => TevalsJudgment.java} | 10 +++++----- .../src/mage/sets/TarkirDragonstormCommander.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename Mage.Sets/src/mage/cards/t/{TevalsJudgement.java => TevalsJudgment.java} (83%) diff --git a/Mage.Sets/src/mage/cards/t/TevalsJudgement.java b/Mage.Sets/src/mage/cards/t/TevalsJudgment.java similarity index 83% rename from Mage.Sets/src/mage/cards/t/TevalsJudgement.java rename to Mage.Sets/src/mage/cards/t/TevalsJudgment.java index b08fa215541..3f50b543be4 100644 --- a/Mage.Sets/src/mage/cards/t/TevalsJudgement.java +++ b/Mage.Sets/src/mage/cards/t/TevalsJudgment.java @@ -16,9 +16,9 @@ import java.util.UUID; /** * @author PurpleCrowbar */ -public final class TevalsJudgement extends CardImpl { +public final class TevalsJudgment extends CardImpl { - public TevalsJudgement(UUID ownerId, CardSetInfo setInfo) { + public TevalsJudgment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); // Whenever one or more cards leave your graveyard, choose one that hasn’t been chosen this turn -- @@ -36,12 +36,12 @@ public final class TevalsJudgement extends CardImpl { this.addAbility(ability); } - private TevalsJudgement(final TevalsJudgement card) { + private TevalsJudgment(final TevalsJudgment card) { super(card); } @Override - public TevalsJudgement copy() { - return new TevalsJudgement(this); + public TevalsJudgment copy() { + return new TevalsJudgment(this); } } diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 29bebed6fff..15997a8fce0 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -304,8 +304,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Temur Ascendancy", 305, Rarity.RARE, mage.cards.t.TemurAscendancy.class)); cards.add(new SetCardInfo("Terramorphic Expanse", 408, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); cards.add(new SetCardInfo("Territorial Hellkite", 240, Rarity.RARE, mage.cards.t.TerritorialHellkite.class)); - cards.add(new SetCardInfo("Teval's Judgement", 28, Rarity.RARE, mage.cards.t.TevalsJudgement.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Teval's Judgement", 68, Rarity.RARE, mage.cards.t.TevalsJudgement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval's Judgment", 28, Rarity.RARE, mage.cards.t.TevalsJudgment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval's Judgment", 68, Rarity.RARE, mage.cards.t.TevalsJudgment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Teval, the Balanced Scale", 8, Rarity.MYTHIC, mage.cards.t.TevalTheBalancedScale.class)); cards.add(new SetCardInfo("Thalisse, Reverent Medium", 306, Rarity.UNCOMMON, mage.cards.t.ThalisseReverentMedium.class)); cards.add(new SetCardInfo("Think Twice", 168, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); From 164ad388d4d37dcaa630f51aef6f41663e3c0282 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:23:45 -0400 Subject: [PATCH 26/71] [TDM] Implement Alchemist's Assistant --- .../src/mage/cards/a/AlchemistsAssistant.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AlchemistsAssistant.java diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsAssistant.java b/Mage.Sets/src/mage/cards/a/AlchemistsAssistant.java new file mode 100644 index 00000000000..9681199b1be --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlchemistsAssistant.java @@ -0,0 +1,41 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.RenewAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlchemistsAssistant extends CardImpl { + + public AlchemistsAssistant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.MONKEY); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Renew -- {1}{B}, Exile this card from your graveyard: Put a lifelink counter on target creature. Activate only as a sorcery. + this.addAbility(new RenewAbility("{1}{B}", CounterType.LIFELINK.createInstance())); + } + + private AlchemistsAssistant(final AlchemistsAssistant card) { + super(card); + } + + @Override + public AlchemistsAssistant copy() { + return new AlchemistsAssistant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index d4b565c1c25..83c9736810b 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -29,6 +29,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Adorned Crocodile", 69, Rarity.COMMON, mage.cards.a.AdornedCrocodile.class)); cards.add(new SetCardInfo("Aegis Sculptor", 35, Rarity.UNCOMMON, mage.cards.a.AegisSculptor.class)); cards.add(new SetCardInfo("Agent of Kotis", 36, Rarity.COMMON, mage.cards.a.AgentOfKotis.class)); + cards.add(new SetCardInfo("Alchemist's Assistant", 71, Rarity.UNCOMMON, mage.cards.a.AlchemistsAssistant.class)); cards.add(new SetCardInfo("Alesha's Legacy", 72, Rarity.COMMON, mage.cards.a.AleshasLegacy.class)); cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class)); cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 2, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class)); From 68d4fb5e95e1a05a54577fe768ea2e7c812624a8 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:26:08 -0400 Subject: [PATCH 27/71] [TDM] Implement Arashin Sunshield --- .../src/mage/cards/a/ArashinSunshield.java | 54 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 55 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/ArashinSunshield.java diff --git a/Mage.Sets/src/mage/cards/a/ArashinSunshield.java b/Mage.Sets/src/mage/cards/a/ArashinSunshield.java new file mode 100644 index 00000000000..3ea6618a2aa --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArashinSunshield.java @@ -0,0 +1,54 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInASingleGraveyard; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArashinSunshield extends CardImpl { + + public ArashinSunshield(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When this creature enters, exile up to two target cards from a single graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect()); + ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARDS_NON_LAND)); + this.addAbility(ability); + + // {W}, {T}: Tap target creature. + ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{W}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private ArashinSunshield(final ArashinSunshield card) { + super(card); + } + + @Override + public ArashinSunshield copy() { + return new ArashinSunshield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 83c9736810b..011de5447d8 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -33,6 +33,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Alesha's Legacy", 72, Rarity.COMMON, mage.cards.a.AleshasLegacy.class)); cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class)); cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 2, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class)); + cards.add(new SetCardInfo("Arashin Sunshield", 3, Rarity.COMMON, mage.cards.a.ArashinSunshield.class)); cards.add(new SetCardInfo("Armament Dragon", 168, Rarity.UNCOMMON, mage.cards.a.ArmamentDragon.class)); cards.add(new SetCardInfo("Attuned Hunter", 135, Rarity.UNCOMMON, mage.cards.a.AttunedHunter.class)); cards.add(new SetCardInfo("Auroral Procession", 169, Rarity.UNCOMMON, mage.cards.a.AuroralProcession.class)); From d7f9e5951c6b6ee665c869b918084d15a736af8e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:27:42 -0400 Subject: [PATCH 28/71] [TDM] Implement Humbling Elder --- Mage.Sets/src/mage/cards/h/HumblingElder.java | 46 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 47 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HumblingElder.java diff --git a/Mage.Sets/src/mage/cards/h/HumblingElder.java b/Mage.Sets/src/mage/cards/h/HumblingElder.java new file mode 100644 index 00000000000..a79977a93c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HumblingElder.java @@ -0,0 +1,46 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HumblingElder extends CardImpl { + + public HumblingElder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When this creature enters, target creature an opponent controls gets -2/-0 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, 0)); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private HumblingElder(final HumblingElder card) { + super(card); + } + + @Override + public HumblingElder copy() { + return new HumblingElder(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 011de5447d8..786056a3ad4 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -91,6 +91,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Gurmag Nightwatch", 190, Rarity.COMMON, mage.cards.g.GurmagNightwatch.class)); cards.add(new SetCardInfo("Hardened Tactician", 191, Rarity.UNCOMMON, mage.cards.h.HardenedTactician.class)); cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); + cards.add(new SetCardInfo("Humbling Elder", 48, Rarity.COMMON, mage.cards.h.HumblingElder.class)); cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class)); cards.add(new SetCardInfo("Inspirited Vanguard", 146, Rarity.UNCOMMON, mage.cards.i.InspiritedVanguard.class)); cards.add(new SetCardInfo("Iridescent Tiger", 109, Rarity.UNCOMMON, mage.cards.i.IridescentTiger.class)); From f02542efb004870198dffcd03e1026fa0d184813 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:28:51 -0400 Subject: [PATCH 29/71] [TDM] Implement Iceridge Serpent --- .../src/mage/cards/i/IceridgeSerpent.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/IceridgeSerpent.java diff --git a/Mage.Sets/src/mage/cards/i/IceridgeSerpent.java b/Mage.Sets/src/mage/cards/i/IceridgeSerpent.java new file mode 100644 index 00000000000..db31c1be839 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IceridgeSerpent.java @@ -0,0 +1,41 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IceridgeSerpent extends CardImpl { + + public IceridgeSerpent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SERPENT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When this creature enters, return target creature an opponent controls to its owner's hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private IceridgeSerpent(final IceridgeSerpent card) { + super(card); + } + + @Override + public IceridgeSerpent copy() { + return new IceridgeSerpent(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 786056a3ad4..31a424c90ed 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -92,6 +92,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Hardened Tactician", 191, Rarity.UNCOMMON, mage.cards.h.HardenedTactician.class)); cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); cards.add(new SetCardInfo("Humbling Elder", 48, Rarity.COMMON, mage.cards.h.HumblingElder.class)); + cards.add(new SetCardInfo("Iceridge Serpent", 49, Rarity.COMMON, mage.cards.i.IceridgeSerpent.class)); cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class)); cards.add(new SetCardInfo("Inspirited Vanguard", 146, Rarity.UNCOMMON, mage.cards.i.InspiritedVanguard.class)); cards.add(new SetCardInfo("Iridescent Tiger", 109, Rarity.UNCOMMON, mage.cards.i.IridescentTiger.class)); From 5798c361fa46784cc14671cae736ce2e0ac0ea3f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:32:20 -0400 Subject: [PATCH 30/71] [TDM] Implement Rescue Leopard --- Mage.Sets/src/mage/cards/r/RescueLeopard.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RescueLeopard.java diff --git a/Mage.Sets/src/mage/cards/r/RescueLeopard.java b/Mage.Sets/src/mage/cards/r/RescueLeopard.java new file mode 100644 index 00000000000..e74c4610a5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RescueLeopard.java @@ -0,0 +1,41 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.BecomesTappedSourceTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RescueLeopard extends CardImpl { + + public RescueLeopard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Whenever this creature becomes tapped, you may discard a card. If you do, draw a card. + this.addAbility(new BecomesTappedSourceTriggeredAbility( + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()) + )); + } + + private RescueLeopard(final RescueLeopard card) { + super(card); + } + + @Override + public RescueLeopard copy() { + return new RescueLeopard(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 31a424c90ed..1f8d4621959 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -138,6 +138,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Rebellious Strike", 20, Rarity.COMMON, mage.cards.r.RebelliousStrike.class)); cards.add(new SetCardInfo("Reigning Victor", 216, Rarity.COMMON, mage.cards.r.ReigningVictor.class)); cards.add(new SetCardInfo("Reputable Merchant", 217, Rarity.COMMON, mage.cards.r.ReputableMerchant.class)); + cards.add(new SetCardInfo("Rescue Leopard", 116, Rarity.COMMON, mage.cards.r.RescueLeopard.class)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); From 4bc0a755139e203943802270396cfe811cd9e06f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 10:36:58 -0400 Subject: [PATCH 31/71] [TDM] Implement Unburied Earthcarver --- .../src/mage/cards/u/UnburiedEarthcarver.java | 47 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 48 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/u/UnburiedEarthcarver.java diff --git a/Mage.Sets/src/mage/cards/u/UnburiedEarthcarver.java b/Mage.Sets/src/mage/cards/u/UnburiedEarthcarver.java new file mode 100644 index 00000000000..96d624833d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnburiedEarthcarver.java @@ -0,0 +1,47 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnburiedEarthcarver extends CardImpl { + + public UnburiedEarthcarver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {2}, Sacrifice another creature: Put a +1/+1 counter on this creature. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2) + ); + ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_ANOTHER_CREATURE)); + this.addAbility(ability); + } + + private UnburiedEarthcarver(final UnburiedEarthcarver card) { + super(card); + } + + @Override + public UnburiedEarthcarver copy() { + return new UnburiedEarthcarver(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 1f8d4621959..00ac70147a2 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -177,6 +177,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class)); + cards.add(new SetCardInfo("Unburied Earthcarver", 95, Rarity.COMMON, mage.cards.u.UnburiedEarthcarver.class)); cards.add(new SetCardInfo("Underfoot Underdogs", 129, Rarity.COMMON, mage.cards.u.UnderfootUnderdogs.class)); cards.add(new SetCardInfo("Undergrowth Leopard", 165, Rarity.COMMON, mage.cards.u.UndergrowthLeopard.class)); cards.add(new SetCardInfo("Unending Whisper", 62, Rarity.COMMON, mage.cards.u.UnendingWhisper.class)); From 229956f1bcfa1a8d2866bc83c03ab709f87c381c Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:22:22 -0400 Subject: [PATCH 32/71] [TDM] Implement Ainok Wayfarer --- Mage.Sets/src/mage/cards/a/AinokWayfarer.java | 44 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 45 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AinokWayfarer.java diff --git a/Mage.Sets/src/mage/cards/a/AinokWayfarer.java b/Mage.Sets/src/mage/cards/a/AinokWayfarer.java new file mode 100644 index 00000000000..77f093dc213 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AinokWayfarer.java @@ -0,0 +1,44 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.MillThenPutInHandEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AinokWayfarer extends CardImpl { + + public AinokWayfarer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When this creature enters, mill three cards. You may put a land card from among them into your hand. If you don't, put a +1/+1 counter on this creature. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MillThenPutInHandEffect( + 1, StaticFilters.FILTER_CARD_LAND_A, + new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true) + ))); + } + + private AinokWayfarer(final AinokWayfarer card) { + super(card); + } + + @Override + public AinokWayfarer copy() { + return new AinokWayfarer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 00ac70147a2..0681ecaef8d 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -29,6 +29,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Adorned Crocodile", 69, Rarity.COMMON, mage.cards.a.AdornedCrocodile.class)); cards.add(new SetCardInfo("Aegis Sculptor", 35, Rarity.UNCOMMON, mage.cards.a.AegisSculptor.class)); cards.add(new SetCardInfo("Agent of Kotis", 36, Rarity.COMMON, mage.cards.a.AgentOfKotis.class)); + cards.add(new SetCardInfo("Ainok Wayfarer", 134, Rarity.COMMON, mage.cards.a.AinokWayfarer.class)); cards.add(new SetCardInfo("Alchemist's Assistant", 71, Rarity.UNCOMMON, mage.cards.a.AlchemistsAssistant.class)); cards.add(new SetCardInfo("Alesha's Legacy", 72, Rarity.COMMON, mage.cards.a.AleshasLegacy.class)); cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class)); From 46d582e0e63f5d6401c5a462f8d4e073085738c7 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:27:06 -0400 Subject: [PATCH 33/71] [TDM] Implement Constrictor Sage --- .../src/mage/cards/c/ConstrictorSage.java | 59 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 60 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/ConstrictorSage.java diff --git a/Mage.Sets/src/mage/cards/c/ConstrictorSage.java b/Mage.Sets/src/mage/cards/c/ConstrictorSage.java new file mode 100644 index 00000000000..57429482fc1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConstrictorSage.java @@ -0,0 +1,59 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ConstrictorSage extends CardImpl { + + public ConstrictorSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When this creature enters, tap target creature an opponent controls and put a stun counter on it. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()) + .setText("and put a stun counter on it")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + + // Renew -- {2}{U}, Exile this card from your graveyard: Tap target creature an opponent controls and put a stun counter on it. Activate only as a sorcery. + ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new TapTargetEffect(), new ManaCostsImpl<>("{2}{U}")); + ability.addCost(new ExileSourceFromGraveCost()); + ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()) + .setText("and put a stun counter on it")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability.setAbilityWord(AbilityWord.RENEW)); + } + + private ConstrictorSage(final ConstrictorSage card) { + super(card); + } + + @Override + public ConstrictorSage copy() { + return new ConstrictorSage(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 0681ecaef8d..9f782e26d6d 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -52,6 +52,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Caustic Exhale", 74, Rarity.COMMON, mage.cards.c.CausticExhale.class)); cards.add(new SetCardInfo("Champion of Dusan", 137, Rarity.COMMON, mage.cards.c.ChampionOfDusan.class)); cards.add(new SetCardInfo("Channeled Dragonfire", 102, Rarity.UNCOMMON, mage.cards.c.ChanneledDragonfire.class)); + cards.add(new SetCardInfo("Constrictor Sage", 39, Rarity.UNCOMMON, mage.cards.c.ConstrictorSage.class)); cards.add(new SetCardInfo("Coordinated Maneuver", 6, Rarity.COMMON, mage.cards.c.CoordinatedManeuver.class)); cards.add(new SetCardInfo("Cori Mountain Monastery", 252, Rarity.RARE, mage.cards.c.CoriMountainMonastery.class)); cards.add(new SetCardInfo("Cori Mountain Stalwart", 175, Rarity.UNCOMMON, mage.cards.c.CoriMountainStalwart.class)); From 21c5dabb489fab40ede79c5e0424cd9f49bf844f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:39:52 -0400 Subject: [PATCH 34/71] [TDM] Implement Revival of the Ancestors --- .../mage/cards/r/RevivalOfTheAncestors.java | 70 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 71 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RevivalOfTheAncestors.java diff --git a/Mage.Sets/src/mage/cards/r/RevivalOfTheAncestors.java b/Mage.Sets/src/mage/cards/r/RevivalOfTheAncestors.java new file mode 100644 index 00000000000..9f73504a426 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RevivalOfTheAncestors.java @@ -0,0 +1,70 @@ +package mage.cards.r; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.NoFlyingSpiritWhiteToken; +import mage.target.common.TargetCreaturePermanentAmount; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RevivalOfTheAncestors extends CardImpl { + + public RevivalOfTheAncestors(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{B}{G}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Create three 1/1 white Spirit creature tokens. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, + new CreateTokenEffect(new NoFlyingSpiritWhiteToken(), 3) + ); + + // II -- Distribute three +1/+1 counters among one, two, or three target creatures you control. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, + new DistributeCountersEffect(CounterType.P1P1), + new TargetCreaturePermanentAmount(3) + ); + + // III -- Creatures you control gain trample and lifelink until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("creatures you control gain trample"), + new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("and lifelink until end of turn")); + this.addAbility(sagaAbility); + } + + private RevivalOfTheAncestors(final RevivalOfTheAncestors card) { + super(card); + } + + @Override + public RevivalOfTheAncestors copy() { + return new RevivalOfTheAncestors(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 9f782e26d6d..1370212d0e8 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -141,6 +141,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Reigning Victor", 216, Rarity.COMMON, mage.cards.r.ReigningVictor.class)); cards.add(new SetCardInfo("Reputable Merchant", 217, Rarity.COMMON, mage.cards.r.ReputableMerchant.class)); cards.add(new SetCardInfo("Rescue Leopard", 116, Rarity.COMMON, mage.cards.r.RescueLeopard.class)); + cards.add(new SetCardInfo("Revival of the Ancestors", 218, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); From a641f5d05b26909def5edd7865e4b165f328435d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:46:51 -0400 Subject: [PATCH 35/71] [TDM] Implement Sage of the Fang --- Mage.Sets/src/mage/cards/s/SageOfTheFang.java | 51 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 52 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SageOfTheFang.java diff --git a/Mage.Sets/src/mage/cards/s/SageOfTheFang.java b/Mage.Sets/src/mage/cards/s/SageOfTheFang.java new file mode 100644 index 00000000000..12a152c90d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SageOfTheFang.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.RenewAbility; +import mage.abilities.effects.common.DoubleCountersTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SageOfTheFang extends CardImpl { + + public SageOfTheFang(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When this creature enters, put a +1/+1 counter on target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Renew -- {3}{G}, Exile this card from your graveyard: Put a +1/+1 counter on target creature, then double the number of +1/+1 counters on that creature. + ability = new RenewAbility("{3}{G}", CounterType.P1P1.createInstance()); + ability.addEffect(new DoubleCountersTargetEffect(CounterType.P1P1) + .setText(", then double the number of +1/+1 counters on that creature")); + this.addAbility(ability); + } + + private SageOfTheFang(final SageOfTheFang card) { + super(card); + } + + @Override + public SageOfTheFang copy() { + return new SageOfTheFang(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 1370212d0e8..64a3197a6b0 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -147,6 +147,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class)); cards.add(new SetCardInfo("Rugged Highlands", 265, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); + cards.add(new SetCardInfo("Sage of the Fang", 155, Rarity.UNCOMMON, mage.cards.s.SageOfTheFang.class)); cards.add(new SetCardInfo("Sagu Pummeler", 156, Rarity.COMMON, mage.cards.s.SaguPummeler.class)); cards.add(new SetCardInfo("Salt Road Packbeast", 23, Rarity.COMMON, mage.cards.s.SaltRoadPackbeast.class)); cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); From f07c8239afaba464e735068c17c843ea051dffb1 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:49:26 -0400 Subject: [PATCH 36/71] [TDM] Implement Riverwalk Technique --- .../src/mage/cards/r/RiverwalkTechnique.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RiverwalkTechnique.java diff --git a/Mage.Sets/src/mage/cards/r/RiverwalkTechnique.java b/Mage.Sets/src/mage/cards/r/RiverwalkTechnique.java new file mode 100644 index 00000000000..a8547b88b2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RiverwalkTechnique.java @@ -0,0 +1,41 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.PutOnTopOrBottomLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RiverwalkTechnique extends CardImpl { + + public RiverwalkTechnique(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); + + // Choose one -- + // * The owner of target nonland permanent puts it on their choice of the top or bottom of their library. + this.getSpellAbility().addEffect(new PutOnTopOrBottomLibraryTargetEffect(true)); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + + // * Counter target noncreature spell. + this.getSpellAbility().addMode(new Mode(new CounterTargetEffect()) + .addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE))); + } + + private RiverwalkTechnique(final RiverwalkTechnique card) { + super(card); + } + + @Override + public RiverwalkTechnique copy() { + return new RiverwalkTechnique(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 64a3197a6b0..4b59d4b311b 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -143,6 +143,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Rescue Leopard", 116, Rarity.COMMON, mage.cards.r.RescueLeopard.class)); cards.add(new SetCardInfo("Revival of the Ancestors", 218, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); + cards.add(new SetCardInfo("Riverwalk Technique", 54, Rarity.COMMON, mage.cards.r.RiverwalkTechnique.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class)); From a293b77f142709dc6ca1bf402af8b9f2a9018804 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 18:58:31 -0400 Subject: [PATCH 37/71] [TDM] Implement Wayspeaker Bodyguard --- .../src/mage/cards/w/WayspeakerBodyguard.java | 62 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 63 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WayspeakerBodyguard.java diff --git a/Mage.Sets/src/mage/cards/w/WayspeakerBodyguard.java b/Mage.Sets/src/mage/cards/w/WayspeakerBodyguard.java new file mode 100644 index 00000000000..b7f5291cddf --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WayspeakerBodyguard.java @@ -0,0 +1,62 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.FlurryAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WayspeakerBodyguard extends CardImpl { + + private static final FilterCard filter = new FilterNonlandCard("nonland permanent card with mana value 2 or less"); + + static { + filter.add(PermanentPredicate.instance); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public WayspeakerBodyguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.MONK); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When this creature enters, return target nonland permanent card with mana value 2 or less from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Flurry -- Whenever you cast your second spell each turn, tap target creature an opponent controls. + ability = new FlurryAbility(new TapTargetEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private WayspeakerBodyguard(final WayspeakerBodyguard card) { + super(card); + } + + @Override + public WayspeakerBodyguard copy() { + return new WayspeakerBodyguard(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 4b59d4b311b..6e8fdd58aa4 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -193,6 +193,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Venerated Stormsinger", 97, Rarity.UNCOMMON, mage.cards.v.VeneratedStormsinger.class)); cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); + cards.add(new SetCardInfo("Wayspeaker Bodyguard", 34, Rarity.UNCOMMON, mage.cards.w.WayspeakerBodyguard.class)); cards.add(new SetCardInfo("Wild Ride", 132, Rarity.COMMON, mage.cards.w.WildRide.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 271, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); cards.add(new SetCardInfo("Wingblade Disciple", 65, Rarity.UNCOMMON, mage.cards.w.WingbladeDisciple.class)); From a850ce7339ba802f0d3e366afb16d2792ea3282e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Mar 2025 19:04:36 -0400 Subject: [PATCH 38/71] [TDM] Implement Snowmelt Stag --- Mage.Sets/src/mage/cards/s/SnowmeltStag.java | 56 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 57 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SnowmeltStag.java diff --git a/Mage.Sets/src/mage/cards/s/SnowmeltStag.java b/Mage.Sets/src/mage/cards/s/SnowmeltStag.java new file mode 100644 index 00000000000..a72e8ad66f0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnowmeltStag.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.effects.common.continuous.SetBasePowerToughnessSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SnowmeltStag extends CardImpl { + + public SnowmeltStag(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.ELK); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // During your turn, this creature has base power and toughness 5/2. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new SetBasePowerToughnessSourceEffect(5, 2, Duration.WhileOnBattlefield), + MyTurnCondition.instance, "during your turn, this creature has base power and toughness 5/2" + ))); + + // {5}{U}{U}: This creature can't be blocked this turn. + this.addAbility(new SimpleActivatedAbility( + new CantBeBlockedSourceEffect(Duration.EndOfTurn), new ManaCostsImpl<>("{5}{U}{U}") + )); + } + + private SnowmeltStag(final SnowmeltStag card) { + super(card); + } + + @Override + public SnowmeltStag copy() { + return new SnowmeltStag(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 6e8fdd58aa4..71bf4073c12 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -164,6 +164,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Skirmish Rhino", 224, Rarity.UNCOMMON, mage.cards.s.SkirmishRhino.class)); cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class)); cards.add(new SetCardInfo("Snakeskin Veil", 159, Rarity.COMMON, mage.cards.s.SnakeskinVeil.class)); + cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); From 5ac01f259d27e82096373a5064d59732aa6d3f62 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:52:30 -0400 Subject: [PATCH 39/71] fix Veiled Apparition (was missing flying) --- .../src/mage/cards/v/VeiledApparition.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VeiledApparition.java b/Mage.Sets/src/mage/cards/v/VeiledApparition.java index 6d23247eca8..ddb202c6119 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledApparition.java +++ b/Mage.Sets/src/mage/cards/v/VeiledApparition.java @@ -1,17 +1,15 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.condition.common.SourceMatchesFilterCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.DoUnlessControllerPaysEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -21,6 +19,8 @@ import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.permanent.token.TokenImpl; +import java.util.UUID; + /** * * @author jeffwadsworth @@ -33,7 +33,7 @@ public final class VeiledApparition extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // When an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and "At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}." - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilApparitionToken(), null, Duration.WhileOnBattlefield), + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeiledApparitionToken(), null, Duration.WhileOnBattlefield), filter, false); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a spell, if {this} is an enchantment, {this} becomes a 3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}.\"")); @@ -50,23 +50,25 @@ public final class VeiledApparition extends CardImpl { } } -class VeilApparitionToken extends TokenImpl { +class VeiledApparitionToken extends TokenImpl { - public VeilApparitionToken() { + VeiledApparitionToken() { super("Illusion", "3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}."); cardType.add(CardType.CREATURE); subtype.add(SubType.ILLUSION); power = new MageInt(3); toughness = new MageInt(3); - Ability ability = new BeginningOfUpkeepTriggeredAbility(new DoUnlessControllerPaysEffect(new SacrificeSourceEffect(), new ManaCostsImpl<>("{1}{U}"))); - this.addAbility(ability); + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{1}{U}")) + )); } - private VeilApparitionToken(final VeilApparitionToken token) { + private VeiledApparitionToken(final VeiledApparitionToken token) { super(token); } - public VeilApparitionToken copy() { - return new VeilApparitionToken(this); + public VeiledApparitionToken copy() { + return new VeiledApparitionToken(this); } } From 8b52e60b3a59161279fc98344ce57ee7b50d2b3e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 10:17:03 -0400 Subject: [PATCH 40/71] [TDM] Implement Songcrafter Mage --- .../src/mage/cards/s/SongcrafterMage.java | 80 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 81 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SongcrafterMage.java diff --git a/Mage.Sets/src/mage/cards/s/SongcrafterMage.java b/Mage.Sets/src/mage/cards/s/SongcrafterMage.java new file mode 100644 index 00000000000..900b6a01d16 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SongcrafterMage.java @@ -0,0 +1,80 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.HarmonizeAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SongcrafterMage extends CardImpl { + + public SongcrafterMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BARD); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When this creature enters, target instant or sorcery card in your graveyard gains harmonize until end of turn. Its harmonize cost is equal to its mana cost. + Ability ability = new EntersBattlefieldTriggeredAbility(new SongcrafterMageEffect()); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); + this.addAbility(ability); + } + + private SongcrafterMage(final SongcrafterMage card) { + super(card); + } + + @Override + public SongcrafterMage copy() { + return new SongcrafterMage(this); + } +} + +class SongcrafterMageEffect extends ContinuousEffectImpl { + + SongcrafterMageEffect() { + super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.staticText = "target instant or sorcery card in your graveyard gains harmonize until end of turn. " + + "The harmonize cost is equal to its mana cost"; + } + + private SongcrafterMageEffect(final SongcrafterMageEffect effect) { + super(effect); + } + + @Override + public SongcrafterMageEffect copy() { + return new SongcrafterMageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + Ability ability = new HarmonizeAbility(card, card.getManaCost().getText()); + ability.setSourceId(card.getId()); + ability.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card, ability); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 71bf4073c12..6e0d0c3edaf 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -165,6 +165,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class)); cards.add(new SetCardInfo("Snakeskin Veil", 159, Rarity.COMMON, mage.cards.s.SnakeskinVeil.class)); cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); + cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); From 1ceee7b8b409b464c5bfe41d04d3a04cf694e83f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 16:44:35 -0400 Subject: [PATCH 41/71] [TDM] Implement The Sibsig Ceremony --- .../src/mage/cards/t/TheSibsigCeremony.java | 77 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 78 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TheSibsigCeremony.java diff --git a/Mage.Sets/src/mage/cards/t/TheSibsigCeremony.java b/Mage.Sets/src/mage/cards/t/TheSibsigCeremony.java new file mode 100644 index 00000000000..cf5c66c47fc --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSibsigCeremony.java @@ -0,0 +1,77 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ZombieDruidToken; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheSibsigCeremony extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("creature spells"); + private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent(); + + static { + filter2.add(TheSibsigCeremonyPredicate.instance); + } + + public TheSibsigCeremony(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + + // Creature spells you cast cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); + + // Whenever a creature you control enters, if you cast it, destroy that creature, then create a 2/2 black Zombie Druid creature token. + Ability ability = new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, new DestroyTargetEffect() + .setText("if you cast it, destroy that creature"), + filter2, false, SetTargetPointer.PERMANENT + ); + ability.addEffect(new CreateTokenEffect(new ZombieDruidToken()).concatBy(", then")); + this.addAbility(ability); + } + + private TheSibsigCeremony(final TheSibsigCeremony card) { + super(card); + } + + @Override + public TheSibsigCeremony copy() { + return new TheSibsigCeremony(this); + } +} + +enum TheSibsigCeremonyPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + int zcc = input.getZoneChangeCounter(game); + Spell spell = game.getStack().getSpell(input.getId()); + return (spell != null && spell.getZoneChangeCounter(game) == zcc - 1) + || game.getLastKnownInformation(input.getId(), Zone.STACK, zcc - 1) != null; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 6e0d0c3edaf..77db055f0f4 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -180,6 +180,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Temur Devotee", 61, Rarity.COMMON, mage.cards.t.TemurDevotee.class)); cards.add(new SetCardInfo("Temur Monument", 248, Rarity.UNCOMMON, mage.cards.t.TemurMonument.class)); cards.add(new SetCardInfo("Temur Tawnyback", 229, Rarity.COMMON, mage.cards.t.TemurTawnyback.class)); + cards.add(new SetCardInfo("The Sibsig Ceremony", 91, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class)); cards.add(new SetCardInfo("Thornwood Falls", 269, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); From e6f1b944cfed40b6757fcf23513ffaab7f8aa71e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 16:54:30 -0400 Subject: [PATCH 42/71] [TDM] Implement Kotis, the Fangkeeper --- .../src/mage/cards/k/KotisTheFangkeeper.java | 87 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 88 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KotisTheFangkeeper.java diff --git a/Mage.Sets/src/mage/cards/k/KotisTheFangkeeper.java b/Mage.Sets/src/mage/cards/k/KotisTheFangkeeper.java new file mode 100644 index 00000000000..131dce312ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KotisTheFangkeeper.java @@ -0,0 +1,87 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KotisTheFangkeeper extends CardImpl { + + public KotisTheFangkeeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // Whenever Kotis deals combat damage to a player, exile the top X cards of their library, where X is the amount of damage dealt. You may cast any number of spells with mana value X or less from among them without paying their mana costs. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new KotisTheFangkeeperEffect(), false, true + )); + } + + private KotisTheFangkeeper(final KotisTheFangkeeper card) { + super(card); + } + + @Override + public KotisTheFangkeeper copy() { + return new KotisTheFangkeeper(this); + } +} + +class KotisTheFangkeeperEffect extends OneShotEffect { + + KotisTheFangkeeperEffect() { + super(Outcome.Benefit); + staticText = "exile the top X cards of their library, where X is the amount of damage dealt. You may " + + "cast any number of spells with mana value X or less from among them without paying their mana costs"; + } + + private KotisTheFangkeeperEffect(final KotisTheFangkeeperEffect effect) { + super(effect); + } + + @Override + public KotisTheFangkeeperEffect copy() { + return new KotisTheFangkeeperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + int xValue = GetXValue.instance.calculate(game, source, this); + if (controller == null || player == null || xValue < 1) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, xValue)); + controller.moveCards(cards, Zone.EXILED, source, game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 77db055f0f4..3886386d249 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -111,6 +111,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Kishla Skimmer", 201, Rarity.UNCOMMON, mage.cards.k.KishlaSkimmer.class)); cards.add(new SetCardInfo("Kishla Trawlers", 50, Rarity.UNCOMMON, mage.cards.k.KishlaTrawlers.class)); cards.add(new SetCardInfo("Kishla Village", 259, Rarity.RARE, mage.cards.k.KishlaVillage.class)); + cards.add(new SetCardInfo("Kotis, the Fangkeeper", 202, Rarity.RARE, mage.cards.k.KotisTheFangkeeper.class)); cards.add(new SetCardInfo("Krotiq Nestguard", 148, Rarity.COMMON, mage.cards.k.KrotiqNestguard.class)); cards.add(new SetCardInfo("Lightfoot Technique", 14, Rarity.COMMON, mage.cards.l.LightfootTechnique.class)); cards.add(new SetCardInfo("Loxodon Battle Priest", 15, Rarity.UNCOMMON, mage.cards.l.LoxodonBattlePriest.class)); From fa49b5c9b229dde9252ade2b0b8f70d17e0df76b Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 16:58:04 -0400 Subject: [PATCH 43/71] [TDM] Implement Knockout Maneuver --- .../src/mage/cards/k/KnockoutManeuver.java | 37 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 38 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KnockoutManeuver.java diff --git a/Mage.Sets/src/mage/cards/k/KnockoutManeuver.java b/Mage.Sets/src/mage/cards/k/KnockoutManeuver.java new file mode 100644 index 00000000000..528d6ddeddb --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KnockoutManeuver.java @@ -0,0 +1,37 @@ +package mage.cards.k; + +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KnockoutManeuver extends CardImpl { + + public KnockoutManeuver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + // Put a +1/+1 counter on target creature you control, then it deals damage equal to its power to target creature an opponent controls. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect(", then it")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + } + + private KnockoutManeuver(final KnockoutManeuver card) { + super(card); + } + + @Override + public KnockoutManeuver copy() { + return new KnockoutManeuver(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 3886386d249..8ac7d91a4ea 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -111,6 +111,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Kishla Skimmer", 201, Rarity.UNCOMMON, mage.cards.k.KishlaSkimmer.class)); cards.add(new SetCardInfo("Kishla Trawlers", 50, Rarity.UNCOMMON, mage.cards.k.KishlaTrawlers.class)); cards.add(new SetCardInfo("Kishla Village", 259, Rarity.RARE, mage.cards.k.KishlaVillage.class)); + cards.add(new SetCardInfo("Knockout Maneuver", 147, Rarity.UNCOMMON, mage.cards.k.KnockoutManeuver.class)); cards.add(new SetCardInfo("Kotis, the Fangkeeper", 202, Rarity.RARE, mage.cards.k.KotisTheFangkeeper.class)); cards.add(new SetCardInfo("Krotiq Nestguard", 148, Rarity.COMMON, mage.cards.k.KrotiqNestguard.class)); cards.add(new SetCardInfo("Lightfoot Technique", 14, Rarity.COMMON, mage.cards.l.LightfootTechnique.class)); From abcabefbd9e2620e35e80d90c4bd0e57003fc17d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 17:05:20 -0400 Subject: [PATCH 44/71] [TDM] Implement Molten Exhale --- Mage.Sets/src/mage/cards/m/MoltenExhale.java | 40 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + ...yMoreToCastAsThoughtItHadFlashAbility.java | 18 +++++++-- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MoltenExhale.java diff --git a/Mage.Sets/src/mage/cards/m/MoltenExhale.java b/Mage.Sets/src/mage/cards/m/MoltenExhale.java new file mode 100644 index 00000000000..b70d0407772 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoltenExhale.java @@ -0,0 +1,40 @@ +package mage.cards.m; + +import mage.abilities.common.PayMoreToCastAsThoughtItHadFlashAbility; +import mage.abilities.costs.common.BeholdDragonCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoltenExhale extends CardImpl { + + public MoltenExhale(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // You may cast this spell as though it had flash if you behold a Dragon as an additional cost to cast it. + this.addAbility(new PayMoreToCastAsThoughtItHadFlashAbility( + this, new BeholdDragonCost(), "you may cast this spell as though " + + "it had flash if you behold a Dragon as an additional cost to cast it" + )); + + // Molten Exhale deals 4 damage to target creature or planeswalker. + this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private MoltenExhale(final MoltenExhale card) { + super(card); + } + + @Override + public MoltenExhale copy() { + return new MoltenExhale(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 8ac7d91a4ea..30005dc2284 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -120,6 +120,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Mardu Devotee", 16, Rarity.COMMON, mage.cards.m.MarduDevotee.class)); cards.add(new SetCardInfo("Mardu Monument", 245, Rarity.UNCOMMON, mage.cards.m.MarduMonument.class)); cards.add(new SetCardInfo("Meticulous Artisan", 112, Rarity.COMMON, mage.cards.m.MeticulousArtisan.class)); + cards.add(new SetCardInfo("Molten Exhale", 113, Rarity.COMMON, mage.cards.m.MoltenExhale.class)); cards.add(new SetCardInfo("Monastery Messenger", 208, Rarity.COMMON, mage.cards.m.MonasteryMessenger.class)); cards.add(new SetCardInfo("Mountain", 283, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mox Jasper", 246, Rarity.MYTHIC, mage.cards.m.MoxJasper.class)); diff --git a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java index 0ba8afb443a..f27112aba7f 100644 --- a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java @@ -16,24 +16,30 @@ import mage.util.CardUtil; public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { private final Cost costsToAdd; + private final String rule; public PayMoreToCastAsThoughtItHadFlashAbility(Card card, Cost costsToAdd) { + this(card, costsToAdd, null); + } + + public PayMoreToCastAsThoughtItHadFlashAbility(Card card, Cost costsToAdd, String rule) { super(card.getSpellAbility().getManaCosts().copy(), card.getName(), Zone.HAND, SpellAbilityType.BASE_ALTERNATE); this.costsToAdd = costsToAdd; + this.rule = rule; this.timing = TimingRule.INSTANT; this.setRuleAtTheTop(true); - if(costsToAdd instanceof ManaCosts) { + if (costsToAdd instanceof ManaCosts) { ManaCosts manaCosts = (ManaCosts) costsToAdd; CardUtil.increaseCost(this, manaCosts); - } - else { + } else { this.addCost(costsToAdd); } } protected PayMoreToCastAsThoughtItHadFlashAbility(final PayMoreToCastAsThoughtItHadFlashAbility ability) { super(ability); + this.rule = ability.rule; this.costsToAdd = ability.costsToAdd; } @@ -46,8 +52,12 @@ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { public String getRule(boolean all) { return getRule(); } + @Override public String getRule() { + if (rule != null && !rule.isEmpty()) { + return rule; + } if (costsToAdd instanceof ManaCosts) { return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; } else { @@ -55,4 +65,4 @@ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { } } -} \ No newline at end of file +} From e76ee141d7358ee4fed37bc4d9703da99102bc67 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 17:08:49 -0400 Subject: [PATCH 45/71] [TDM] Implement Marshal of the Lost --- .../src/mage/cards/m/MarshalOfTheLost.java | 54 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 55 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MarshalOfTheLost.java diff --git a/Mage.Sets/src/mage/cards/m/MarshalOfTheLost.java b/Mage.Sets/src/mage/cards/m/MarshalOfTheLost.java new file mode 100644 index 00000000000..ad6fcfaf463 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarshalOfTheLost.java @@ -0,0 +1,54 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarshalOfTheLost extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_ATTACKING_CREATURES, null); + private static final Hint hint = new ValueHint("Attacking creatures", xValue); + + public MarshalOfTheLost(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever you attack, target creature gets +X/+X until end of turn, where X is the number of attacking creatures. + Ability ability = new AttacksWithCreaturesTriggeredAbility(new BoostTargetEffect(xValue, xValue), 1); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability.addHint(hint)); + } + + private MarshalOfTheLost(final MarshalOfTheLost card) { + super(card); + } + + @Override + public MarshalOfTheLost copy() { + return new MarshalOfTheLost(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 30005dc2284..e701dac9203 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -119,6 +119,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Mammoth Bellow", 205, Rarity.UNCOMMON, mage.cards.m.MammothBellow.class)); cards.add(new SetCardInfo("Mardu Devotee", 16, Rarity.COMMON, mage.cards.m.MarduDevotee.class)); cards.add(new SetCardInfo("Mardu Monument", 245, Rarity.UNCOMMON, mage.cards.m.MarduMonument.class)); + cards.add(new SetCardInfo("Marshal of the Lost", 207, Rarity.UNCOMMON, mage.cards.m.MarshalOfTheLost.class)); cards.add(new SetCardInfo("Meticulous Artisan", 112, Rarity.COMMON, mage.cards.m.MeticulousArtisan.class)); cards.add(new SetCardInfo("Molten Exhale", 113, Rarity.COMMON, mage.cards.m.MoltenExhale.class)); cards.add(new SetCardInfo("Monastery Messenger", 208, Rarity.COMMON, mage.cards.m.MonasteryMessenger.class)); From f53e43fd464754f629aad0f94007cd233140f782 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 17:13:09 -0400 Subject: [PATCH 46/71] [TDM] Implement Stormbeacon Blade --- .../src/mage/cards/s/StormbeaconBlade.java | 57 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 58 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/StormbeaconBlade.java diff --git a/Mage.Sets/src/mage/cards/s/StormbeaconBlade.java b/Mage.Sets/src/mage/cards/s/StormbeaconBlade.java new file mode 100644 index 00000000000..2b6d9fba1c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormbeaconBlade.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StormbeaconBlade extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_ATTACKING_CREATURE, + ComparisonType.MORE_THAN, 2, true + ); + + public StormbeaconBlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +3/+0. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(3, 0))); + + // Whenever equipped creature attacks, draw a card if you control three or more attacking creatures. + this.addAbility(new AttacksAttachedTriggeredAbility(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), condition, + "draw a card if you control three or more attacking creatures" + ), AttachmentType.EQUIPMENT, false)); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private StormbeaconBlade(final StormbeaconBlade card) { + super(card); + } + + @Override + public StormbeaconBlade copy() { + return new StormbeaconBlade(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index e701dac9203..e2055606f22 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -171,6 +171,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); + cards.add(new SetCardInfo("Stormbeacon Blade", 27, Rarity.UNCOMMON, mage.cards.s.StormbeaconBlade.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); cards.add(new SetCardInfo("Sultai Devotee", 160, Rarity.COMMON, mage.cards.s.SultaiDevotee.class)); From 969ffa1c9848b0e6e42c4df860c43b81f5304a81 Mon Sep 17 00:00:00 2001 From: ssk97 Date: Sun, 30 Mar 2025 14:42:05 -0700 Subject: [PATCH 47/71] FDN New Combat Rules (#13279) * Remove all combat ordering code * Use MultiAmount division for damage * Remove damage selection division skipping in tests * Fix Banding, Multi-block, add tests * Fix test * Fix random iteration order, fix new tests * Add more info to choose dialog, make MultiAmountType class instead of enum * Don't prompt for trample damage assignment if none possible * Mark "Assign default damage" on tests, minor other test improvements --- .../main/java/mage/view/CombatGroupView.java | 2 +- .../ai/ComputerPlayerControllableProxy.java | 20 -- .../java/mage/player/ai/ComputerPlayer.java | 17 +- .../mage/player/ai/SimulatedPlayerMCTS.java | 16 - .../src/mage/player/human/HumanPlayer.java | 52 +-- .../src/mage/cards/b/BalduvianWarlord.java | 15 - .../src/mage/cards/b/BrimazKingOfOreskos.java | 4 +- Mage.Sets/src/mage/cards/f/FalseOrders.java | 15 - Mage.Sets/src/mage/cards/f/FlashFoliage.java | 1 - .../src/mage/cards/g/GeneralJarkeld.java | 19 +- Mage.Sets/src/mage/cards/m/MirrorMatch.java | 10 +- Mage.Sets/src/mage/cards/s/SorrowsPath.java | 6 - .../cards/abilities/keywords/AfflictTest.java | 4 +- .../cards/abilities/keywords/BandingTest.java | 104 ++++++ .../cards/abilities/keywords/BushidoTest.java | 8 +- .../test/cards/damage/ExcessDamageTest.java | 12 +- .../test/cards/emblems/TheRingEmblemTest.java | 7 +- .../PreventDamageRemoveCountersTest.java | 8 +- .../cards/single/fdn/RiteOfPassageTest.java | 9 +- .../cards/single/mir/BindingAgonyTest.java | 5 + .../cards/single/pip/BloatflySwarmTest.java | 8 +- .../cards/single/sth/WallOfEssenceTest.java | 2 +- .../cards/single/tsp/PhantomWurmTest.java | 14 +- .../cards/targets/TargetMultiAmountTest.java | 4 +- .../triggers/damage/DonnaNobleTests.java | 9 +- .../triggers/delayed/DamagedBatchTests.java | 6 +- .../combat/AttackBlockRestrictionsTest.java | 13 +- .../test/combat/DamageDistributionTest.java | 31 ++ .../test/combat/LifelinkInCombatTest.java | 5 +- .../java/org/mage/test/player/TestPlayer.java | 16 +- .../java/mage/constants/MultiAmountType.java | 23 +- .../main/java/mage/game/combat/Combat.java | 24 +- .../java/mage/game/combat/CombatGroup.java | 305 ++++++------------ .../mage/game/turn/DeclareBlockersStep.java | 6 +- Mage/src/main/java/mage/players/Player.java | 14 - .../main/java/mage/players/StubPlayer.java | 12 - 36 files changed, 323 insertions(+), 503 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java diff --git a/Mage.Common/src/main/java/mage/view/CombatGroupView.java b/Mage.Common/src/main/java/mage/view/CombatGroupView.java index e8fc7449d8d..c757ad6ff0d 100644 --- a/Mage.Common/src/main/java/mage/view/CombatGroupView.java +++ b/Mage.Common/src/main/java/mage/view/CombatGroupView.java @@ -40,7 +40,7 @@ public class CombatGroupView implements Serializable { attackers.put(id, new PermanentView(attacker, game.getCard(attacker.getId()),null, game)); } } - for (UUID id: combatGroup.getBlockerOrder()) { + for (UUID id: combatGroup.getBlockers()) { Permanent blocker = game.getPermanent(id); if (blocker != null) { blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game)); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java index dd63da4bdf0..e03ed1d623f 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java @@ -13,10 +13,8 @@ import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.match.Match; -import mage.game.permanent.Permanent; import mage.game.tournament.Tournament; import mage.players.Player; import mage.target.Target; @@ -287,24 +285,6 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 { } } - @Override - public UUID chooseAttackerOrder(java.util.List attackers, Game game) { - if (isUnderMe(game)) { - return super.chooseAttackerOrder(attackers, game); - } else { - return getControllingPlayer(game).chooseAttackerOrder(attackers, game); - } - } - - @Override - public UUID chooseBlockerOrder(java.util.List blockers, CombatGroup combatGroup, java.util.List blockerOrder, Game game) { - if (isUnderMe(game)) { - return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } else { - return getControllingPlayer(game).chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - } - @Override public int getAmount(int min, int max, String message, Game game) { if (isUnderMe(game)) { diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index a938ef24827..d21ecf58811 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2177,9 +2177,6 @@ public class ComputerPlayer extends PlayerImpl { // TODO: add AI support with outcome and replace random with min/max public int getAmount(int min, int max, String message, Game game) { log.debug("getAmount"); - if (message.startsWith("Assign damage to ")) { - return min; - } if (min < max && min == 0) { return RandomUtil.nextInt(CardUtil.overflowInc(max, 1)); } @@ -2192,7 +2189,7 @@ public class ComputerPlayer extends PlayerImpl { log.debug("getMultiAmount"); int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0) { return defaultList; } @@ -2210,18 +2207,6 @@ public class ComputerPlayer extends PlayerImpl { return MultiAmountType.prepareMaxValues(messages, totalMin, totalMax); } - @Override - public UUID chooseAttackerOrder(List attackers, Game game) { - //TODO: improve this - return attackers.iterator().next().getId(); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - //TODO: improve this - return blockers.iterator().next().getId(); - } - @Override public List getAvailableManaProducers(Game game) { return super.getAvailableManaProducers(game); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 3bb1a34df10..5fd1b4ea3b2 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -374,22 +374,6 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer { return super.chooseMode(modes, source, game); } - @Override - public UUID chooseAttackerOrder(List attackers, Game game) { - if (this.isHuman()) { - return attackers.get(RandomUtil.nextInt(attackers.size())).getId(); - } - return super.chooseAttackerOrder(attackers, game); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - if (this.isHuman()) { - return blockers.get(RandomUtil.nextInt(blockers.size())).getId(); - } - return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - @Override public int getAmount(int min, int max, String message, Game game) { if (this.isHuman()) { diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index f35221b49eb..f035a45fe6b 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -2122,56 +2122,6 @@ public class HumanPlayer extends PlayerImpl { } } - @Override - public UUID chooseAttackerOrder(java.util.List attackers, Game game) { - if (gameInCheckPlayableState(game)) { - return null; - } - - while (canRespond()) { - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireSelectTargetEvent(playerId, "Pick attacker", attackers, true); - } - waitForResponse(game); - - UUID responseId = getFixedResponseUUID(game); - if (responseId != null) { - for (Permanent perm : attackers) { - if (perm.getId().equals(responseId)) { - return perm.getId(); - } - } - } - } - return null; - } - - @Override - public UUID chooseBlockerOrder(java.util.List blockers, CombatGroup combatGroup, java.util.List blockerOrder, Game game) { - if (gameInCheckPlayableState(game)) { - return null; - } - - while (canRespond()) { - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireSelectTargetEvent(playerId, "Pick blocker", blockers, true); - } - waitForResponse(game); - - UUID responseId = getFixedResponseUUID(game); - if (responseId != null) { - for (Permanent perm : blockers) { - if (perm.getId().equals(responseId)) { - return perm.getId(); - } - } - } - } - return null; - } - protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) { if (gameInCheckPlayableState(game)) { return; @@ -2260,7 +2210,7 @@ public class HumanPlayer extends PlayerImpl { Game game ) { int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0 || (needCount == 1 && totalMin == totalMax) || messages.stream().map(m -> m.min == m.max).reduce(true, Boolean::logicalAnd)) { // nothing to choose diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index 4b922159db5..55a1fcc34b2 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -153,10 +153,6 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null)); } - CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(permanent.getControllerId(), game); - } } } return true; @@ -164,15 +160,4 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { } return false; } - - private CombatGroup findBlockingGroup(Permanent blocker, Game game) { - if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { - for (CombatGroup group : game.getCombat().getBlockingGroups()) { - if (group.getBlockers().contains(blocker.getId())) { - return group; - } - } - } - return null; - } } diff --git a/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java b/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java index 2e78b4dd69a..cdec812d778 100644 --- a/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java +++ b/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -22,6 +21,8 @@ import mage.game.permanent.token.CatSoldierCreatureToken; import mage.game.permanent.token.Token; import mage.players.Player; +import java.util.UUID; + /** * * @author LevelX2 @@ -99,7 +100,6 @@ class BrimazKingOfOreskosEffect extends OneShotEffect { combatGroup.addBlocker(tokenId, source.getControllerId(), game); game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game); } - combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index cd722afb7dc..4842979f81c 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -169,21 +169,6 @@ class FalseOrdersUnblockEffect extends OneShotEffect { game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null)); } - CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(permanent.getControllerId(), game); - } return true; } - - private CombatGroup findBlockingGroup(Permanent blocker, Game game) { - if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { - for (CombatGroup group : game.getCombat().getBlockingGroups()) { - if (group.getBlockers().contains(blocker.getId())) { - return group; - } - } - } - return null; - } } diff --git a/Mage.Sets/src/mage/cards/f/FlashFoliage.java b/Mage.Sets/src/mage/cards/f/FlashFoliage.java index d85dcb7795c..6730fc8a77c 100644 --- a/Mage.Sets/src/mage/cards/f/FlashFoliage.java +++ b/Mage.Sets/src/mage/cards/f/FlashFoliage.java @@ -90,7 +90,6 @@ class FlashFoliageEffect extends OneShotEffect { game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game); } } - combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java b/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java index e9eee846fe8..f05d68a0bf5 100644 --- a/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java +++ b/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java @@ -1,10 +1,6 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.condition.common.IsStepCondition; @@ -13,18 +9,18 @@ import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAttackingCreature; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** * * @author L_J @@ -138,11 +134,9 @@ class GeneralJarkeldSwitchBlockersEffect extends OneShotEffect { // the ability doesn't unblock a group that loses all blockers, however it will newly block a previously unblocked group if it gains a blocker this way if (!(chosenGroup1.getBlockers().isEmpty())) { chosenGroup1.setBlocked(true, game); - chosenGroup1.pickBlockerOrder(attacker1.getControllerId(), game); } if (!(chosenGroup2.getBlockers().isEmpty())) { chosenGroup2.setBlocked(true, game); - chosenGroup2.pickBlockerOrder(attacker2.getControllerId(), game); } return true; } @@ -197,7 +191,6 @@ class GeneralJarkeldSwitchBlockersEffect extends OneShotEffect { // 10/4/2004 The new blocker does not trigger any abilities which trigger on creatures becoming blockers, because the creatures were already blockers and the simple change of who is blocking does not trigger such abilities. game.getCombat().addBlockingGroup(blocker.getId(), attacker, controller.getId(), game); } - blockGroup.pickAttackerOrder(blocker.getControllerId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/m/MirrorMatch.java b/Mage.Sets/src/mage/cards/m/MirrorMatch.java index c6d0ade8f98..6efa0a8fd1e 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMatch.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMatch.java @@ -1,13 +1,12 @@ package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -20,6 +19,8 @@ import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets; +import java.util.UUID; + /** * * @author LevelX2 @@ -75,20 +76,15 @@ class MirrorMatchEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(attacker, game)); effect.apply(game, source); CombatGroup group = game.getCombat().findGroup(attacker.getId()); - boolean isCreature = false; if (group != null) { for (Permanent addedToken : effect.getAddedPermanents()) { if (addedToken.isCreature(game)) { group.addBlockerToGroup(addedToken.getId(), attackerId, game); - isCreature = true; } } ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat"); exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)); game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); - if (isCreature) { - group.pickBlockerOrder(attacker.getControllerId(), game); - } } } } diff --git a/Mage.Sets/src/mage/cards/s/SorrowsPath.java b/Mage.Sets/src/mage/cards/s/SorrowsPath.java index 82c220c5c8b..19836342a1f 100644 --- a/Mage.Sets/src/mage/cards/s/SorrowsPath.java +++ b/Mage.Sets/src/mage/cards/s/SorrowsPath.java @@ -176,14 +176,8 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect { group.addBlockerToGroup(blocker.getId(), blocker.getControllerId(), game); game.getCombat().addBlockingGroup(blocker.getId(), attacker.getId(), blocker.getControllerId(), game); game.fireEvent(new BlockerDeclaredEvent(attacker.getId(), blocker.getId(), blocker.getControllerId())); - group.pickBlockerOrder(attacker.getControllerId(), game); } } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, blocker.getId(), source, null)); - CombatGroup blockGroup = findBlockingGroup(blocker, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(blocker.getControllerId(), game); - } } - } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java index 25cd66fa768..0ed11cc3005 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class AfflictTest extends CardTestPlayerBase { private final String khenra = "Khenra Eternal"; @@ -35,7 +37,7 @@ public class AfflictTest extends CardTestPlayerBase { attack(1, playerA, khenra); block(1, playerB, elves + ":0", khenra); block(1, playerB, elves + ":1", khenra); - setChoice(playerA, "X=1"); // assign damage + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStrictChooseMode(true); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java new file mode 100644 index 00000000000..ecd2da9036a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java @@ -0,0 +1,104 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author notgreat + */ +public class BandingTest extends CardTestPlayerBase { + + @Test + public void BandingAttackSimple() { + addCard(Zone.BATTLEFIELD, playerA, "Squire"); // 1/2 + addCard(Zone.BATTLEFIELD, playerA, "Benalish Infantry"); // Banding 1/3 + addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet"); // 1/1 + + addCard(Zone.BATTLEFIELD, playerB, "Naga Eternal"); // 3/2 + + attack(1, playerA, "Squire"); + attack(1, playerA, "Benalish Infantry"); + attack(1, playerA, "Eager Cadet"); + setChoice(playerA, true); + setChoice(playerA, "Squire"); + block(1, playerB, "Naga Eternal", "Squire"); + setChoiceAmount(playerA, 1, 2); //1 to Squire, 2 to Infantry, attacking player chooses + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 0); + assertDamageReceived(playerA, "Squire", 1); + assertDamageReceived(playerA, "Benalish Infantry", 2); + assertGraveyardCount(playerB, 1); + assertLife(playerB, 19); // Only Eager Cadet gets through + } + + @Test + public void BandingBlockSimple() { + addCard(Zone.BATTLEFIELD, playerA, "Alpine Grizzly"); // 4/2 + addCard(Zone.BATTLEFIELD, playerB, "Squire"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Sanctuary Cat"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Benalish Infantry"); // Banding 1/3 + + attack(1, playerA, "Alpine Grizzly"); + block(1, playerB, "Squire", "Alpine Grizzly"); + block(1, playerB, "Sanctuary Cat", "Alpine Grizzly"); + block(1, playerB, "Benalish Infantry", "Alpine Grizzly"); + setChoiceAmount(playerB, 1, 1, 2); //1 to Squire, 1 to Cat, 2 to Infantry, defending player chooses + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 0); + assertDamageReceived(playerB, "Squire", 1); + assertDamageReceived(playerB, "Sanctuary Cat", 1); + assertDamageReceived(playerB, "Benalish Infantry", 2); + assertLife(playerB, 20); + + } + + @Test + public void DoubleBanding() { + addCard(Zone.BATTLEFIELD, playerA, "Benalish Infantry"); // Banding 1/3 + addCard(Zone.BATTLEFIELD, playerA, "Fortress Crab"); // 1/6 + addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet"); // 1/1 + + addCard(Zone.BATTLEFIELD, playerB, "Catacomb Slug"); // 2/6 + addCard(Zone.BATTLEFIELD, playerB, "War Elephant"); // Banding 2/2 Trample + + attack(1, playerA, "Benalish Infantry"); + attack(1, playerA, "Fortress Crab"); + attack(1, playerA, "Eager Cadet"); + setChoice(playerA, true); + setChoice(playerA, "Fortress Crab"); + + block(1, playerB, "Catacomb Slug", "Benalish Infantry"); + block(1, playerB, "War Elephant", "Benalish Infantry"); + + setChoiceAmount(playerB, 0, 1); // Damage from Benalish Infantry + setChoiceAmount(playerB, 0, 1); // Damage from Fortress Crab + + setChoiceAmount(playerA, 1, 1); // Damage from War Elephant + setChoiceAmount(playerA, 0, 2); // Damage from Catacomb Slug + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 0); + assertDamageReceived(playerA, "Benalish Infantry", 1); + assertDamageReceived(playerA, "Fortress Crab", 3); + + assertGraveyardCount(playerB, "War Elephant", 1); + assertDamageReceived(playerB, "Catacomb Slug", 0); + + assertLife(playerB, 19); // Only Eager Cadet gets through + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java index d3c9cbd45a4..6d097cd3152 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author noxx */ @@ -21,6 +23,7 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Isao, Enlightened Bushi"); block(2, playerA, "Elite Vanguard", "Isao, Enlightened Bushi"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); @@ -39,6 +42,7 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Elite Vanguard"); block(2, playerA, "Isao, Enlightened Bushi", "Elite Vanguard"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); @@ -58,9 +62,9 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Isao, Enlightened Bushi"); block(2, playerA, "Llanowar Elves", "Isao, Enlightened Bushi"); block(2, playerA, "Elvish Mystic", "Isao, Enlightened Bushi"); - setChoice(playerB, "X=1"); // assign damage - setChoice(playerB, "X=1"); // assign damage + setChoice(playerB, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java index 831b6f77dd8..47e5417a493 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author TheElk801 */ @@ -158,15 +160,7 @@ public class ExcessDamageTest extends CardTestPlayerBase { block(2, playerA, bear, myrSuperion); block(2, playerA, envoy, myrSuperion); block(2, playerA, bondedConstruct, myrSuperion); - - //Assign this much damage to the first blocking creature - setChoice(playerB, "X=2"); - - //Assign this much damage to the second blocking creature - setChoice(playerB, "X=1"); - - //Assign this much damage to the third blocking creature - setChoice(playerB, "X=1"); + setChoice(playerB, CHOICE_SKIP); // Assign default damage setStrictChooseMode(true); setStopAt(2, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java index 4c4afe0424e..e9356c93be4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java @@ -15,6 +15,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author JayDi85 */ @@ -247,9 +249,10 @@ public class TheRingEmblemTest extends CardTestPlayerBase { attack(3, playerA, "Ashiok's Skulker"); block(3, playerB, "Alabaster Kirin", "Ashiok's Skulker"); block(3, playerB, "Alaborn Trooper", "Ashiok's Skulker"); - setChoice(playerA, "Whenever your Ring-bearer becomes blocked"); // 2x triggers from two blockers - setChoice(playerA, "At end of combat, that permanent"); // 2x triggers from two blockers setChoice(playerA, "Mountain"); // draw/discard on attack trigger + setChoice(playerA, "Whenever your Ring-bearer becomes blocked"); // 2x triggers from two blockers + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setChoice(playerA, "At end of combat, that permanent"); // 2x triggers from two blockers checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerA, "Ashiok's Skulker", 1); checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Academy Manufactor", 0); checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Alabaster Kirin", 0); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java index 24c35a7b2be..c8918a46f58 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { @Test @@ -67,7 +69,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(3, playerA, "Magma Pummeler", playerB); block(3, playerB, "Memnite", "Magma Pummeler"); block(3, playerB, "Goblin Piker", "Magma Pummeler"); - setChoice(playerA, "X=5"); // damage for Pummeler, does not really matter for this test. + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, playerB); // For the one trigger setStopAt(3, PhaseStep.END_TURN); @@ -117,7 +119,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(3, playerA, "Magma Pummeler", playerB); block(3, playerB, "Centaur Courser", "Magma Pummeler"); block(3, playerB, "Air Elemental", "Magma Pummeler"); - setChoice(playerA, "X=5"); // damage for Pummeler, does not really matter for this test. + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, playerB); // For the one trigger setStopAt(3, PhaseStep.END_TURN); @@ -148,7 +150,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(1, playerA, "Undergrowth Champion", playerB); block(1, playerB, "Grizzly Bears", "Undergrowth Champion"); block(1, playerB, "Elite Vanguard", "Undergrowth Champion"); - setChoice(playerA, "X=2"); // damage attribution + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java index 6f1035cc025..a2c4f36613f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class RiteOfPassageTest extends CardTestPlayerBase { @@ -38,12 +40,7 @@ public class RiteOfPassageTest extends CardTestPlayerBase { attack(1, playerA, "Watchwolf", playerB); block(1, playerB, "Memnite", "Watchwolf"); block(1, playerB, "Agent of Stromgald", "Watchwolf"); - - // Assign this much damage to Memnite - setChoice(playerA, "X=1"); - - // Assign this much damage to Agent of Stromgald - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); setStopAt(1, PhaseStep.END_TURN); setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java index 9360089029e..70bd6aca5dd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -33,7 +35,9 @@ public class BindingAgonyTest extends CardTestPlayerBase { attack(1, playerA, "Grizzly Bears"); block(1, playerB, "Centaur Courser", "Grizzly Bears"); block(1, playerB, "Memnite", "Grizzly Bears"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -53,6 +57,7 @@ public class BindingAgonyTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, agony, "Grizzly Bears", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java index 18669190a78..e34500b79c8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -32,6 +34,7 @@ public class BloatflySwarmTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swarm, true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", swarm); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -55,7 +58,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase { attack(1, playerA, swarm); block(1, playerB, "Brimstone Dragon", swarm); block(1, playerB, "Giant Spider", swarm); + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -79,8 +84,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase { attack(1, playerA, swarm); block(1, playerB, "Wind Drake", swarm); block(1, playerB, "Giant Spider", swarm); - setChoice(playerA, "X=5"); // damage attribution + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java index 2048a9ea9f7..b2d10bbb30c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java @@ -48,7 +48,7 @@ public class WallOfEssenceTest extends CardTestPlayerBase { block(1, playerB, "Memnite", "Grizzly Bears"); block(1, playerB, wall, "Grizzly Bears"); - setChoice(playerA, "X=2"); // 2 damage on Memnite, no damage to Wall + setChoiceAmount(playerA, 2, 0); // 2 damage on Memnite, no damage to Wall setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java index 0e64acdd57b..4467b893c77 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -31,9 +33,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Memnite", wurm); block(1, playerB, "Eager Cadet", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); @@ -115,9 +115,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Memnite", wurm); block(1, playerB, "Goblin Striker", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); @@ -139,9 +137,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Boros Recruit", wurm); block(1, playerB, "Goblin Striker", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java index 06b8129f704..12ab9202cfe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java @@ -57,7 +57,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps { private void assertDefaultValuesUnconstrained(String need, int count, int min, int max) { List constraints = getUnconstrainedConstraints(count); - List defaultValues = MultiAmountType.prepareDefaltValues(constraints, min, max); + List defaultValues = MultiAmountType.prepareDefaultValues(constraints, min, max); String current = defaultValues .stream() .map(String::valueOf) @@ -122,7 +122,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps { getUnconstrainedConstraints(4)); // good values are checking in test_DefaultValues, it's an additional - List list = MultiAmountType.prepareDefaltValues(constraints.get(3), 0, 0); + List list = MultiAmountType.prepareDefaultValues(constraints.get(3), 0, 0); // count (0, 0, 0) Assert.assertFalse("count", MultiAmountType.isGoodValues(list, constraints.get(0), 0, 0)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java index 826afcc4757..01c72e5f8ef 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommander4Players; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * * @author jimga150 @@ -32,12 +34,7 @@ public class DonnaNobleTests extends CardTestCommander4Players { attack(5, playerA, "Impervious Greatwurm", playerB); block(5, playerB, "Memnite", "Impervious Greatwurm"); block(5, playerB, "Expedition Envoy", "Impervious Greatwurm"); - - //Assign this much damage to the first blocking creature - setChoice(playerA, "X=1"); - - //Assign this much damage to the second blocking creature - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage //Target this player with Donna Noble addTarget(playerA, playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java index e4c30fd1535..2d7f9422924 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java @@ -6,6 +6,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommander4Players; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * * @author jimga150 @@ -25,9 +27,7 @@ public class DamagedBatchTests extends CardTestCommander4Players { attack(1, playerA, "Donna Noble", playerB); block(1, playerB, "Memnite", "Donna Noble"); block(1, playerB, "Expedition Envoy", "Donna Noble"); - - //Assign this much damage to the first blocking creature - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage //Target this player with Donna Noble addTarget(playerA, playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java index a43818f1c90..f64f6bf89a5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java @@ -11,6 +11,7 @@ import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; /** * Test restrictions for choosing attackers and blockers. @@ -753,9 +754,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder"); attack(1, playerA, "Sonorous Howlbonder"); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite"); + setChoice(playerA, CHOICE_SKIP); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -783,7 +784,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { // ai must choose all blockers anyway attack(1, playerA, "Sonorous Howlbonder"); aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites + setChoiceAmount(playerA, 1, 1, 0); // assign damage to blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite"); @@ -813,9 +814,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder"); attack(1, playerA, "Sonorous Howlbonder"); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite"); + setChoice(playerA, CHOICE_SKIP); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -845,7 +846,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { // ai must choose all blockers attack(1, playerA, "Sonorous Howlbonder"); aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites + setChoiceAmount(playerA, 1, 1, 0, 0, 0); // assign damage to blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite"); @@ -879,7 +880,6 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder"); attack(1, playerA, "Sonorous Howlbonder"); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("one possible blocker", 1, playerB, "Memnite"); @@ -998,8 +998,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { addTarget(playerA, "Alley Strangler"); // boost target setChoice(playerA, true); // boost target attack(1, playerA, "Alley Strangler"); - setChoiceAmount(playerA, 1); // assign damage to 1 of 2 blocking memnites - setChoiceAmount(playerA, 1); // assign damage to 2 of 2 blocking memnites + setChoiceAmount(playerA, 1, 1); // assign damage to blocking memnites aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); checkAttackers("x1 attacker", 1, playerA, "Alley Strangler"); checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite"); diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java index c4f4c2218b2..6484c53cf75 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java @@ -197,4 +197,35 @@ public class DamageDistributionTest extends CardTestPlayerBase { assertLife(playerB, 20 - 5); } + + @Test + public void test2x2Block() { + addCard(Zone.BATTLEFIELD, playerA, "Catacomb Slug"); // 2/6 + addCard(Zone.BATTLEFIELD, playerA, "Catacomb Crocodile"); // 3/7 + + addCard(Zone.BATTLEFIELD, playerB, "Brave the Sands"); //can block 2 + addCard(Zone.BATTLEFIELD, playerB, "Marsh Hulk"); // 4/6 + addCard(Zone.BATTLEFIELD, playerB, "Fortress Crab"); // 1/6 + + attack(1, playerA, "Catacomb Slug"); + attack(1, playerA, "Catacomb Crocodile"); + block(1, playerB, "Fortress Crab", "Catacomb Slug"); + block(1, playerB, "Fortress Crab", "Catacomb Crocodile"); + block(1, playerB, "Marsh Hulk", "Catacomb Slug"); + block(1, playerB, "Marsh Hulk", "Catacomb Crocodile"); + + setChoiceAmount(playerA, 1, 1); // Catacomb Slug + setChoiceAmount(playerA, 1, 2); // Catacomb Crocodile + setChoiceAmount(playerB, 1, 0); // Fortress Crab + setChoiceAmount(playerB, 2, 2); // Marsh Hulk + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertDamageReceived(playerA, "Catacomb Slug", 3); + assertDamageReceived(playerA, "Catacomb Crocodile", 2); + assertDamageReceived(playerB, "Fortress Crab", 2); + assertDamageReceived(playerB, "Marsh Hulk", 3); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java index 2881b195b43..aaeb74c2ec2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class LifelinkInCombatTest extends CardTestPlayerBase { @Test public void testOneBlockerTrample() { @@ -43,8 +45,7 @@ public class LifelinkInCombatTest extends CardTestPlayerBase { attack(1, playerA, "Brion Stoutarm"); block(1, playerB, "Boros Recruit", "Brion Stoutarm"); block(1, playerB, "Suntail Hawk", "Brion Stoutarm"); - setChoice(playerA, "X=1"); // Damage assignment - setChoice(playerA, "X=1"); // Damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, "Brion Stoutarm"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 15dfcd42146..e33796e3a0f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2975,11 +2975,12 @@ public class TestPlayer implements Player { assertAliasSupportInChoices(false); int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0) { return defaultList; } + List answer = new ArrayList<>(defaultList); if (!choices.isEmpty()) { // must fill all possible choices or skip it @@ -4477,19 +4478,6 @@ public class TestPlayer implements Player { return computerPlayer.playMana(ability, unpaid, promptText, game); } - @Override - public UUID chooseAttackerOrder(List attacker, Game game - ) { - return computerPlayer.chooseAttackerOrder(attacker, game); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, - List blockerOrder, Game game - ) { - return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - @Override public void sideboard(Match match, Deck deck ) { diff --git a/Mage/src/main/java/mage/constants/MultiAmountType.java b/Mage/src/main/java/mage/constants/MultiAmountType.java index ed5b1d5b34b..398ca7a0fd8 100644 --- a/Mage/src/main/java/mage/constants/MultiAmountType.java +++ b/Mage/src/main/java/mage/constants/MultiAmountType.java @@ -8,23 +8,24 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -public enum MultiAmountType { +public class MultiAmountType { - MANA("Add mana", "Distribute mana among colors"), - DAMAGE("Assign damage", "Assign damage among targets"), - P1P1("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"), - COUNTERS("Choose counters", "Move counters"), - CHEAT_LANDS("Choose lands", "Add lands to your battlefield", true); + public static final MultiAmountType MANA = new MultiAmountType("Add mana", "Distribute mana among colors"); + public static final MultiAmountType DAMAGE = new MultiAmountType("Assign damage", "Assign damage among targets"); + + public static final MultiAmountType P1P1 = new MultiAmountType("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"); + public static final MultiAmountType COUNTERS = new MultiAmountType("Choose counters", "Move counters"); + public static final MultiAmountType CHEAT_LANDS = new MultiAmountType("Choose lands", "Add lands to your battlefield", true); private final String title; private final String header; private final boolean canCancel; // choice dialog will return null instead default values - MultiAmountType(String title, String header) { + public MultiAmountType(String title, String header) { this(title, header, false); } - MultiAmountType(String title, String header, boolean canCancel) { + public MultiAmountType(String title, String header, boolean canCancel) { this.title = title; this.header = header; this.canCancel = canCancel; @@ -42,7 +43,7 @@ public enum MultiAmountType { return canCancel; } - public static List prepareDefaltValues(List constraints, int min, int max) { + public static List prepareDefaultValues(List constraints, int min, int max) { // default values must be assigned from first to last by minimum values List res = constraints.stream().map(m -> m.defaultValue > Integer.MIN_VALUE ? m.defaultValue : Math.min(0, max)) .collect(Collectors.toList()); @@ -50,7 +51,7 @@ public enum MultiAmountType { return res; } - int total = res.stream().mapToInt(x -> x).sum();; + int total = res.stream().mapToInt(x -> x).sum(); // Fill values until we reach the overall minimum. Do this by filling values up until either their max or however much is leftover, starting with the first option. if (min > 0 && total < min) { @@ -174,7 +175,7 @@ public enum MultiAmountType { // data check if (returnDefaultOnError && !isGoodValues(res, constraints, min, max)) { // on broken data - return default - return prepareDefaltValues(constraints, min, max); + return prepareDefaultValues(constraints, min, max); } return res; diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 91637785533..333e786b95a 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -60,7 +60,7 @@ public class Combat implements Serializable, Copyable { protected List groups = new ArrayList<>(); protected List formerGroups = new ArrayList<>(); - protected Map blockingGroups = new HashMap<>(); + protected Map blockingGroups = new LinkedHashMap<>(); // all possible defenders (players, planeswalkers or battle) protected Set defenders = new HashSet<>(); // how many creatures attack defending player @@ -200,7 +200,7 @@ public class Combat implements Serializable, Copyable { StringBuilder sb = new StringBuilder(); sb.append(attackingPlayerId).append(defenders); for (CombatGroup group : groups) { - sb.append(group.defenderId).append(group.attackers).append(group.attackerOrder).append(group.blockers).append(group.blockerOrder); + sb.append(group.defenderId).append(group.attackers).append(group.blockers); } return sb.toString(); } @@ -785,7 +785,7 @@ public class Combat implements Serializable, Copyable { if (attackerExists) { if (!group.getBlockers().isEmpty()) { sb.append("blocked by "); - for (UUID blockingCreatureId : group.getBlockerOrder()) { + for (UUID blockingCreatureId : group.getBlockers()) { Permanent blockingCreature = game.getPermanent(blockingCreatureId); if (blockingCreature != null) { sb.append(blockingCreature.getLogName()).append(" ("); @@ -1799,24 +1799,11 @@ public class Combat implements Serializable, Copyable { return playerDefenders; } - public void damageAssignmentOrder(Game game) { - for (CombatGroup group : groups) { - group.pickBlockerOrder(attackingPlayerId, game); - } - for (Map.Entry blockingGroup : blockingGroups.entrySet()) { - Permanent blocker = game.getPermanent(blockingGroup.getKey()); - if (blocker != null) { - blockingGroup.getValue().pickAttackerOrder(blocker.getControllerId(), game); - } - } - } - @SuppressWarnings("deprecation") public void removeAttacker(UUID attackerId, Game game) { for (CombatGroup group : groups) { if (group.attackers.contains(attackerId)) { group.attackers.remove(attackerId); - group.attackerOrder.remove(attackerId); for (Set attackingCreatures : numberCreaturesDefenderAttackedBy.values()) { attackingCreatures.remove(attackerId); } @@ -1869,7 +1856,6 @@ public class Combat implements Serializable, Copyable { } for (CombatGroup group : groupsToCheck) { group.blockers.remove(blockerId); - group.blockerOrder.remove(blockerId); if (group.blockers.isEmpty()) { group.blocked = false; } @@ -1885,11 +1871,9 @@ public class Combat implements Serializable, Copyable { if (blockGroup.blockers.contains(blockerId)) { for (UUID attackerId : group.getAttackers()) { blockGroup.attackers.remove(attackerId); - blockGroup.attackerOrder.remove(attackerId); } if (creature.getBlocking() == 0) { blockGroup.blockers.remove(blockerId); - blockGroup.attackerOrder.clear(); } } if (blockGroup.blockers.isEmpty()) { @@ -1914,7 +1898,6 @@ public class Combat implements Serializable, Copyable { for (CombatGroup group : groups) { if (group.blockers.contains(blockerId)) { group.blockers.remove(blockerId); - group.blockerOrder.remove(blockerId); if (group.blockers.isEmpty()) { group.blocked = false; } @@ -1924,7 +1907,6 @@ public class Combat implements Serializable, Copyable { for (CombatGroup group : getBlockingGroups()) { if (group.blockers.contains(blockerId)) { group.blockers.remove(blockerId); - group.attackerOrder.clear(); } if (group.blockers.isEmpty()) { canRemove = true; diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index c7caca995fa..03a492dd05f 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -6,6 +6,7 @@ import mage.abilities.common.ControllerDivideCombatDamageAbility; import mage.abilities.common.DamageAsThoughNotBlockedAbility; import mage.abilities.keyword.*; import mage.constants.AsThoughEffectType; +import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; @@ -15,6 +16,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.Copyable; +import mage.util.MultiAmountMessage; import mage.watchers.common.FirstStrikeWatcher; import java.io.Serializable; @@ -29,8 +31,6 @@ public class CombatGroup implements Serializable, Copyable { protected List attackers = new ArrayList<>(); protected List formerAttackers = new ArrayList<>(); protected List blockers = new ArrayList<>(); - protected List blockerOrder = new ArrayList<>(); - protected List attackerOrder = new ArrayList<>(); protected Map players = new HashMap<>(); protected boolean blocked; protected UUID defenderId; // planeswalker, player, or battle id, can be null after remove from combat (e.g. due damage) @@ -52,8 +52,6 @@ public class CombatGroup implements Serializable, Copyable { this.attackers.addAll(group.attackers); this.formerAttackers.addAll(group.formerAttackers); this.blockers.addAll(group.blockers); - this.blockerOrder.addAll(group.blockerOrder); - this.attackerOrder.addAll(group.attackerOrder); this.players.putAll(group.players); this.blocked = group.blocked; this.defenderId = group.defenderId; @@ -91,10 +89,6 @@ public class CombatGroup implements Serializable, Copyable { return blockers; } - public List getBlockerOrder() { - return blockerOrder; - } - private static boolean hasFirstOrDoubleStrike(Permanent perm) { return hasFirstStrike(perm) || hasDoubleStrike(perm); } @@ -175,7 +169,6 @@ public class CombatGroup implements Serializable, Copyable { if (attacker != null && !assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(attacker, attacker.getControllerId(), first, game, true)) { if (blockers.isEmpty()) { unblockedDamage(first, game); - return; } else { Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId()); if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) && @@ -186,11 +179,7 @@ public class CombatGroup implements Serializable, Copyable { blocked = false; unblockedDamage(first, game); } - if (blockers.size() == 1) { - singleBlockerDamage(player, first, game); - } else { - multiBlockerDamage(player, first, game); - } + blockerDamage(player, first, game); } } } @@ -206,9 +195,7 @@ public class CombatGroup implements Serializable, Copyable { } } if (attackers.size() != 1) { - multiAttackerDamage(first, game); - // } else { - // singleAttackerDamage(first, game); + attackerDamage(first, game); } } } @@ -269,51 +256,16 @@ public class CombatGroup implements Serializable, Copyable { } } } - - private void singleBlockerDamage(Player player, boolean first, Game game) { - Permanent blocker = game.getPermanent(blockers.get(0)); - Permanent attacker = game.getPermanent(attackers.get(0)); - if (blocker != null && attacker != null) { - int blockerDamage = getDamageValueFromPermanent(blocker, game); // must be set before attacker damage marking because of effects like Test of Faith - if (blocked && dealsDamageThisStep(attacker, first, game)) { - int damage = getDamageValueFromPermanent(attacker, game); - if (hasTrample(attacker)) { - int lethalDamage = getLethalDamage(blocker, attacker, game); - if (lethalDamage >= damage) { - blocker.markDamage(damage, attacker.getId(), null, game, true, true); - } else { - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); - blocker.markDamage(damageAssigned, attacker.getId(), null, game, true, true); - damage -= damageAssigned; - if (damage > 0) { - defenderDamage(attacker, damage, game, false); - } - } - } else { - blocker.markDamage(damage, attacker.getId(), null, game, true, true); - } - } - if (dealsDamageThisStep(blocker, first, game)) { - if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately - if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { - attacker.markDamage(blockerDamage, blocker.getId(), null, game, true, true); - } - } - } - } - } - - private void multiBlockerDamage(Player player, boolean first, Game game) { + private void blockerDamage(Player player, boolean first, Game game) { Permanent attacker = game.getPermanent(attackers.get(0)); if (attacker == null) { return; } - boolean oldRuleDamage = (Objects.equals(player.getId(), defendingPlayerId)); int damage = getDamageValueFromPermanent(attacker, game); if (dealsDamageThisStep(attacker, first, game)) { // must be set before attacker damage marking because of effects like Test of Faith Map blockerPower = new HashMap<>(); - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately @@ -322,42 +274,62 @@ public class CombatGroup implements Serializable, Copyable { } } Map assigned = new HashMap<>(); + List damageDivision = new ArrayList<>(); + List blockersCopy = new ArrayList<>(blockers); if (blocked) { - boolean excessDamageToDefender = true; - for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException + int remainingDamage = damage; + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { - int lethalDamage = getLethalDamage(blocker, attacker, game); - if (lethalDamage >= damage) { - if (!oldRuleDamage) { - assigned.put(blockerId, damage); - damage = 0; - break; - } else if (damage == 0) { - break; - } - } - int damageAssigned = 0; - if (!oldRuleDamage) { - damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); - } else { - damageAssigned = player.getAmount(0, damage, "Assign damage to " + blocker.getName(), game); - if (damageAssigned < lethalDamage) { - excessDamageToDefender = false; // all blockers need to have lethal damage assigned before it can trample over to the defender - } - } - assigned.put(blockerId, damageAssigned); - damage -= damageAssigned; + int defaultDamage = Math.min(remainingDamage, blocker.getLethalDamage(attacker.getId(), game)); + remainingDamage -= defaultDamage; + String message = String.format("%s, P/T: %d/%d", + blocker.getLogName(), + blocker.getPower().getValue(), + blocker.getToughness().getValue()); + damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage)); } } - if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) { - defenderDamage(attacker, damage, game, false); - } else if (!blockerOrder.isEmpty()) { - // Assign the damage left to first blocker - assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) == null ? 0 : assigned.get(blockerOrder.get(0)) + damage); + List amounts; + if (hasTrample(attacker)){ + if (remainingDamage > 0 || damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign combat damage (with trample)", + String.format("Assign combat damage among creatures blocking %s, P/T: %d/%d (Unassigned damage tramples through)", + attacker.getLogName(), attacker.getPower().getValue(), attacker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage - remainingDamage, damage, dialogue, game); + } else { + amounts = new ArrayList<>(); + if (damageDivision.size() == 1) { // Assign all damage to one blocker + amounts.add(damage); + } + } + int trampleDamage = damage - (amounts.stream().mapToInt(x -> x).sum()); + if (trampleDamage > 0) { + defenderDamage(attacker, trampleDamage, game, false); + } + } else { + if (remainingDamage > 0){ + damageDivision.get(0).defaultValue += remainingDamage; + } + if (damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign combat damage", + String.format("Assign combat damage among creatures blocking %s, P/T: %d/%d", + attacker.getLogName(), attacker.getPower().getValue(), attacker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage, damage, dialogue, game); + } else { + amounts = new LinkedList<>(); + if (damageDivision.size() == 1) { // Assign all damage to one blocker + amounts.add(damage); + } + } + } + if (!damageDivision.isEmpty()){ + for (int i=0; i { } } } else { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { @@ -395,7 +367,7 @@ public class CombatGroup implements Serializable, Copyable { if (dealsDamageThisStep(attacker, first, game)) { // must be set before attacker damage marking because of effects like Test of Faith Map blockerPower = new HashMap<>(); - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately @@ -422,7 +394,7 @@ public class CombatGroup implements Serializable, Copyable { } } if (isAttacking) { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Integer power = blockerPower.get(blockerId); if (power != null) { // might be missing canDamage condition? @@ -439,7 +411,7 @@ public class CombatGroup implements Serializable, Copyable { } } else { if (isAttacking) { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { @@ -473,81 +445,62 @@ public class CombatGroup implements Serializable, Copyable { /** * Damages attacking creatures by a creature that blocked several ones * Damages only attackers as blocker was damage in - * {@link #singleBlockerDamage}. + * {@link #blockerDamage}. *

- * Handles abilities like "{this} an block any number of creatures.". - *

- * Blocker damage for blockers blocking single creatures is handled in the - * single/multi blocker methods, so this shouldn't be used anymore. - * - * @param first - * @param game - * @deprecated - */ - @Deprecated - private void singleAttackerDamage(boolean first, Game game) { - Permanent blocker = game.getPermanent(blockers.get(0)); - Permanent attacker = game.getPermanent(attackers.get(0)); - if (blocker != null && attacker != null) { - if (dealsDamageThisStep(blocker, first, game)) { - int damage = getDamageValueFromPermanent(blocker, game); - attacker.markDamage(damage, blocker.getId(), null, game, true, true); - } - } - } - - /** - * Damages attacking creatures by a creature that blocked several ones - * Damages only attackers as blocker was damage in either - * {@link #singleBlockerDamage} or {@link #multiBlockerDamage}. - *

- * Handles abilities like "{this} an block any number of creatures.". + * Handles abilities like "{this} can block any number of creatures.". * * @param first * @param game */ - private void multiAttackerDamage(boolean first, Game game) { + private void attackerDamage(boolean first, Game game) { Permanent blocker = game.getPermanent(blockers.get(0)); if (blocker == null) { return; } - boolean oldRuleDamage = attackerAssignsCombatDamage(game); // handles banding - Player player = game.getPlayer(oldRuleDamage ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId()); + //Handle Banding + Player player = game.getPlayer(attackerAssignsCombatDamage(game) ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId()); int damage = getDamageValueFromPermanent(blocker, game); if (dealsDamageThisStep(blocker, first, game)) { Map assigned = new HashMap<>(); - for (UUID attackerId : attackerOrder) { + List damageDivision = new ArrayList<>(); + List attackersCopy = new ArrayList<>(attackers); + int remainingDamage = damage; + for (UUID attackerId : attackers) { Permanent attacker = game.getPermanent(attackerId); if (attacker != null) { - int lethalDamage = getLethalDamage(attacker, blocker, game); - if (lethalDamage >= damage) { - if (!oldRuleDamage) { - assigned.put(attackerId, damage); - damage = 0; - break; - } else if (damage == 0) { - break; - } - } - int damageAssigned = 0; - if (!oldRuleDamage) { - damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getName(), game); - } else { - damageAssigned = player.getAmount(0, damage, "Assign damage to " + attacker.getName(), game); - } - assigned.put(attackerId, damageAssigned); - damage -= damageAssigned; + int defaultDamage = Math.min(remainingDamage, attacker.getLethalDamage(blocker.getId(), game)); + remainingDamage -= defaultDamage; + String message = String.format("%s, P/T: %d/%d", + attacker.getLogName(), + attacker.getPower().getValue(), + attacker.getToughness().getValue()); + damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage)); } } - if (damage > 0) { - // Assign the damage left to first attacker - assigned.put(attackerOrder.get(0), assigned.get(attackerOrder.get(0)) + damage); + List amounts; + if (remainingDamage > 0){ + damageDivision.get(0).defaultValue += remainingDamage; + } + if (damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign blocker combat damage", + String.format("Assign combat damage among creatures blocked by %s, P/T: %d/%d", + blocker.getLogName(), blocker.getPower().getValue(), blocker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage, damage, dialogue, game); + } else { + amounts = new LinkedList<>(); + amounts.add(damage); + } + if (!damageDivision.isEmpty()){ + for (int i=0; i entry : assigned.entrySet()) { Permanent attacker = game.getPermanent(entry.getKey()); - attacker.markDamage(entry.getValue(), blocker.getId(), null, game, true, true); + if (attacker != null) { + attacker.markDamage(entry.getValue(), blocker.getId(), null, game, true, true); + } } } } @@ -632,74 +585,11 @@ public class CombatGroup implements Serializable, Copyable { if (blockerId != null && blocker != null) { blocker.setBlocking(blocker.getBlocking() + 1); blockers.add(blockerId); - blockerOrder.add(blockerId); this.blocked = true; this.players.put(blockerId, playerId); } } - public void pickBlockerOrder(UUID playerId, Game game) { - if (blockers.isEmpty()) { - return; - } - Player player = game.getPlayer(playerId); // game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : playerId); // this was incorrect because defenderAssignsCombatDamage might be false by the time damage is dealt - List blockerList = new ArrayList<>(blockers); - blockerOrder.clear(); - while (player.canRespond()) { - if (blockerList.size() == 1) { - blockerOrder.add(blockerList.get(0)); - break; - } else { - List blockerPerms = new ArrayList<>(); - for (UUID blockerId : blockerList) { - blockerPerms.add(game.getPermanent(blockerId)); - } - UUID blockerId = player.chooseBlockerOrder(blockerPerms, this, blockerOrder, game); - blockerOrder.add(blockerId); - blockerList.remove(blockerId); - } - } - if (!game.isSimulation() && blockerOrder.size() > 1) { - logDamageAssignmentOrder("Creatures blocking ", attackers, blockerOrder, game); - } - } - - public void pickAttackerOrder(UUID playerId, Game game) { - Player player = game.getPlayer(playerId); - if (attackers.isEmpty() || player == null) { - return; - } - List attackerList = new ArrayList<>(attackers); - List newAttackerOrder = new ArrayList<>(); - while (true) { - if (attackerList.size() == 1) { - newAttackerOrder.add(attackerList.get(0)); - break; - } else { - List attackerPerms = new ArrayList<>(); - for (UUID attackerId : attackerList) { - attackerPerms.add(game.getPermanent(attackerId)); - } - UUID attackerId = player.chooseAttackerOrder(attackerPerms, game); - if (attackerId == null) { - break; - } - newAttackerOrder.add(attackerId); - attackerList.remove(attackerId); - } - } - if (attackerOrder.isEmpty() || newAttackerOrder.size() == attackerOrder.size()) { - attackerOrder.clear(); - attackerOrder.addAll(newAttackerOrder); - - if (!game.isSimulation() && attackerOrder.size() > 1) { - logDamageAssignmentOrder("Creatures blocked by ", blockers, attackerOrder, game); - } - } else { - game.informPlayers(player.getLogName() + " try to skip choose attacker order"); - } - } - private void logDamageAssignmentOrder(String prefix, List assignedFor, List assignedOrder, Game game) { StringBuilder sb = new StringBuilder(prefix); boolean first = true; @@ -746,12 +636,9 @@ public class CombatGroup implements Serializable, Copyable { formerAttackers.add(creatureId); attackers.remove(creatureId); result = true; - attackerOrder.remove(creatureId); } else if (blockers.contains(creatureId)) { blockers.remove(creatureId); result = true; - //20100423 - 509.2a - blockerOrder.remove(creatureId); } return result; } @@ -825,7 +712,6 @@ public class CombatGroup implements Serializable, Copyable { game.getCombat().removeBlocker(blockerId, game); } blockers.clear(); - blockerOrder.clear(); if (!game.isSimulation()) { game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded."); } @@ -844,7 +730,6 @@ public class CombatGroup implements Serializable, Copyable { game.getCombat().removeBlocker(blockerId, game); } blockers.clear(); - blockerOrder.clear(); if (!game.isSimulation()) { game.informPlayers(new StringBuilder(attacker.getLogName()) .append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy()) diff --git a/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java b/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java index 0e33c98afcb..063670ed7b4 100644 --- a/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java +++ b/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java @@ -1,12 +1,12 @@ package mage.game.turn; -import java.util.UUID; - import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent.EventType; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -37,7 +37,6 @@ public class DeclareBlockersStep extends Step { game.getCombat().selectBlockers(game); if (!game.isPaused() && !game.executingRollback()) { game.getCombat().acceptBlockers(game); - game.getCombat().damageAssignmentOrder(game); } } @@ -46,7 +45,6 @@ public class DeclareBlockersStep extends Step { super.resumeBeginStep(game, activePlayerId); game.getCombat().resumeSelectBlockers(game); game.getCombat().acceptBlockers(game); - game.getCombat().damageAssignmentOrder(game); } @Override diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 5e8286c5b6e..c6c7d14039c 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -23,7 +23,6 @@ import mage.filter.FilterCard; import mage.filter.FilterMana; import mage.filter.FilterPermanent; import mage.game.*; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.events.GameEvent; import mage.game.match.Match; @@ -761,19 +760,6 @@ public interface Player extends MageItem, Copyable { void selectBlockers(Ability source, Game game, UUID defendingPlayerId); - UUID chooseAttackerOrder(List attacker, Game game); - - /** - * Choose the order in which blockers get damage assigned to - * - * @param blockers list of blockers where to choose the next one from - * @param combatGroup the concerning combat group - * @param blockerOrder the already set order of blockers - * @param game - * @return blocker next to add to the blocker order - */ - UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game); - int getAmount(int min, int max, String message, Game game); /** diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java index d1fa0f7d57b..da4d4167d2d 100644 --- a/Mage/src/main/java/mage/players/StubPlayer.java +++ b/Mage/src/main/java/mage/players/StubPlayer.java @@ -18,10 +18,8 @@ import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.filter.FilterMana; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.match.Match; -import mage.game.permanent.Permanent; import mage.game.tournament.Tournament; import mage.target.Target; import mage.target.TargetAmount; @@ -190,16 +188,6 @@ public class StubPlayer extends PlayerImpl { } - @Override - public UUID chooseAttackerOrder(List attacker, Game game) { - return null; - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - return null; - } - @Override public int getAmount(int min, int max, String message, Game game) { return 0; From 776210a6a877969c81c4b2569c6cf89f4c7cdf29 Mon Sep 17 00:00:00 2001 From: padfoothelix Date: Sun, 30 Mar 2025 23:44:44 +0200 Subject: [PATCH 48/71] Implement [WHO] This is How it Ends (#13468) --- .../src/mage/cards/t/ThisIsHowItEnds.java | 108 ++++++++++++++++++ Mage.Sets/src/mage/sets/DoctorWho.java | 8 +- 2 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/ThisIsHowItEnds.java diff --git a/Mage.Sets/src/mage/cards/t/ThisIsHowItEnds.java b/Mage.Sets/src/mage/cards/t/ThisIsHowItEnds.java new file mode 100644 index 00000000000..15b6ed745aa --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThisIsHowItEnds.java @@ -0,0 +1,108 @@ +package mage.cards.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; +import mage.cards.*; +import mage.choices.FaceVillainousChoice; +import mage.choices.VillainousChoice; +import mage.constants.*; +import mage.game.Game; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author padfoothelix + */ +public final class ThisIsHowItEnds extends CardImpl { + + public ThisIsHowItEnds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); + + // Target creature's owner shuffles it into their library, then faces a villainous choice -- They lose 5 life, or they shuffle another creature they own into their library. + this.getSpellAbility().addEffect( + new ShuffleIntoLibraryTargetEffect() + .setText("target creature's owner shuffles it into their library,") + ); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new ThisIsHowItEndsEffect()); + + } + + private ThisIsHowItEnds(final ThisIsHowItEnds card) { + super(card); + } + + @Override + public ThisIsHowItEnds copy() { + return new ThisIsHowItEnds(this); + } +} + +class ThisIsHowItEndsEffect extends OneShotEffect { + + private static final FaceVillainousChoice choice = new FaceVillainousChoice( + Outcome.Removal, new ThisIsHowItEndsFirstChoice(), new ThisIsHowItEndsSecondChoice() + ); + + ThisIsHowItEndsEffect() { + super(Outcome.Benefit); + staticText = "then " + choice.generateRule(); + } + + private ThisIsHowItEndsEffect(final ThisIsHowItEndsEffect effect) { + super(effect); + } + + @Override + public ThisIsHowItEndsEffect copy() { + return new ThisIsHowItEndsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID targetOwnerId = game.getOwnerId(getTargetPointer().getFirst(game, source)); + Player targetOwner = game.getPlayer(targetOwnerId); + choice.faceChoice(targetOwner, game, source); + return true; + } +} + +class ThisIsHowItEndsFirstChoice extends VillainousChoice { + ThisIsHowItEndsFirstChoice() { + super("They lose 5 life","You lose 5 life"); + } + + @Override + public boolean doChoice(Player player, Game game, Ability source) { + player.loseLife(5, game, source, false); + return true; + } +} + +class ThisIsHowItEndsSecondChoice extends VillainousChoice { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you own"); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + } + + ThisIsHowItEndsSecondChoice() { + super("they shuffle another creature they own into their library", "you shuffle a creature you own into your library"); + } + + @Override + public boolean doChoice(Player player, Game game, Ability source) { + TargetCreaturePermanent target = new TargetCreaturePermanent(1, 1, filter, true); + target.withChooseHint("to shuffle into your library"); + player.chooseTarget(Outcome.Detriment, target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + player.shuffleCardsToLibrary(cards, game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index c0009b9bb88..e03ac42718d 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -1093,10 +1093,10 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Thijarian Witness", 716, Rarity.UNCOMMON, mage.cards.t.ThijarianWitness.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Think Twice", 220, Rarity.COMMON, mage.cards.t.ThinkTwice.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Think Twice", 811, Rarity.COMMON, mage.cards.t.ThinkTwice.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("This Is How It Ends", 373, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("This Is How It Ends", 675, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("This Is How It Ends", 70, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("This Is How It Ends", 964, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("This Is How It Ends", 373, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("This Is How It Ends", 675, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("This Is How It Ends", 70, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("This Is How It Ends", 964, Rarity.RARE, mage.cards.t.ThisIsHowItEnds.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thought Vessel", 255, Rarity.UNCOMMON, mage.cards.t.ThoughtVessel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thought Vessel", 846, Rarity.UNCOMMON, mage.cards.t.ThoughtVessel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Three Visits", 235, Rarity.UNCOMMON, mage.cards.t.ThreeVisits.class, NON_FULL_USE_VARIOUS)); From 251b533fcb7c3aa00e4aad13c370cb8e26cc6e43 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 17:47:46 -0400 Subject: [PATCH 49/71] [TDM] Implement Fire-Rim Form --- Mage.Sets/src/mage/cards/f/FireRimForm.java | 55 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 56 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/FireRimForm.java diff --git a/Mage.Sets/src/mage/cards/f/FireRimForm.java b/Mage.Sets/src/mage/cards/f/FireRimForm.java new file mode 100644 index 00000000000..165b1a151ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FireRimForm.java @@ -0,0 +1,55 @@ +package mage.cards.f; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FireRimForm extends CardImpl { + + public FireRimForm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // When this Aura enters, enchanted creature gains first strike until end of turn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.AURA, Duration.EndOfTurn + ))); + + // Enchanted creature gets +2/+0. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(2, 0))); + } + + private FireRimForm(final FireRimForm card) { + super(card); + } + + @Override + public FireRimForm copy() { + return new FireRimForm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index e2055606f22..d19ebb26484 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -84,6 +84,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Evolving Wilds", 255, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Fangkeeper's Familiar", 183, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class)); cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 184, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class)); + cards.add(new SetCardInfo("Fire-Rim Form", 107, Rarity.COMMON, mage.cards.f.FireRimForm.class)); cards.add(new SetCardInfo("Forest", 285, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fortress Kin-Guard", 12, Rarity.COMMON, mage.cards.f.FortressKinGuard.class)); cards.add(new SetCardInfo("Fresh Start", 46, Rarity.UNCOMMON, mage.cards.f.FreshStart.class)); From 0d4c3660276eb36b6a60a422fc8e52a7b3a97145 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 17:49:22 -0400 Subject: [PATCH 50/71] [TDM] Implement Fleeting Effigy --- .../src/mage/cards/f/FleetingEffigy.java | 50 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 51 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/FleetingEffigy.java diff --git a/Mage.Sets/src/mage/cards/f/FleetingEffigy.java b/Mage.Sets/src/mage/cards/f/FleetingEffigy.java new file mode 100644 index 00000000000..46e227d65de --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FleetingEffigy.java @@ -0,0 +1,50 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FleetingEffigy extends CardImpl { + + public FleetingEffigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // At the beginning of your end step, return this creature to its owner's hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ReturnToHandSourceEffect())); + + // {2}{R}: This creature gets +2/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl<>("{2}{R}") + )); + } + + private FleetingEffigy(final FleetingEffigy card) { + super(card); + } + + @Override + public FleetingEffigy copy() { + return new FleetingEffigy(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index d19ebb26484..a44045aaf88 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -85,6 +85,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Fangkeeper's Familiar", 183, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class)); cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 184, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class)); cards.add(new SetCardInfo("Fire-Rim Form", 107, Rarity.COMMON, mage.cards.f.FireRimForm.class)); + cards.add(new SetCardInfo("Fleeting Effigy", 108, Rarity.UNCOMMON, mage.cards.f.FleetingEffigy.class)); cards.add(new SetCardInfo("Forest", 285, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fortress Kin-Guard", 12, Rarity.COMMON, mage.cards.f.FortressKinGuard.class)); cards.add(new SetCardInfo("Fresh Start", 46, Rarity.UNCOMMON, mage.cards.f.FreshStart.class)); From de051a005f86cf800d00befbef8160e98636ea9e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 20:24:38 -0400 Subject: [PATCH 51/71] [TDM] Implement Sarkhan, Dragon Ascendant --- .../mage/cards/s/SarkhanDragonAscendant.java | 69 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 70 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SarkhanDragonAscendant.java diff --git a/Mage.Sets/src/mage/cards/s/SarkhanDragonAscendant.java b/Mage.Sets/src/mage/cards/s/SarkhanDragonAscendant.java new file mode 100644 index 00000000000..555e0f6c8c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SarkhanDragonAscendant.java @@ -0,0 +1,69 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.BeholdDragonCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.AddCardSubTypeSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SarkhanDragonAscendant extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DRAGON, "a Dragon you control"); + + public SarkhanDragonAscendant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Sarkhan enters, you may behold a Dragon. If you do, create a Treasure token. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DoIfCostPaid(new CreateTokenEffect(new TreasureToken()), new BeholdDragonCost()) + )); + + // Whenever a Dragon you control enters, put a +1/+1 counter on Sarkhan. Until end of turn, Sarkhan becomes a Dragon in addition to its other types and gains flying. + Ability ability = new EntersBattlefieldAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter + ); + ability.addEffect(new AddCardSubTypeSourceEffect( + Duration.EndOfTurn, SubType.DRAGON + ).setText("until end of turn, {this} becomes a Dragon in addition to its other types")); + ability.addEffect(new GainAbilitySourceEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains flying")); + this.addAbility(ability); + } + + private SarkhanDragonAscendant(final SarkhanDragonAscendant card) { + super(card); + } + + @Override + public SarkhanDragonAscendant copy() { + return new SarkhanDragonAscendant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index a44045aaf88..a358fd282a4 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -160,6 +160,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 266, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); cards.add(new SetCardInfo("Sarkhan's Resolve", 158, Rarity.COMMON, mage.cards.s.SarkhansResolve.class)); + cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 118, Rarity.RARE, mage.cards.s.SarkhanDragonAscendant.class)); cards.add(new SetCardInfo("Scoured Barrens", 267, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); cards.add(new SetCardInfo("Seize Opportunity", 119, Rarity.COMMON, mage.cards.s.SeizeOpportunity.class)); cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class)); From 70c7b5d32cb8495f12e6a67fdca41014e48c3541 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 20:27:14 -0400 Subject: [PATCH 52/71] [TDM] Implement Stadium Headliner --- .../src/mage/cards/s/StadiumHeadliner.java | 52 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 53 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/StadiumHeadliner.java diff --git a/Mage.Sets/src/mage/cards/s/StadiumHeadliner.java b/Mage.Sets/src/mage/cards/s/StadiumHeadliner.java new file mode 100644 index 00000000000..beb0372451b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StadiumHeadliner.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.MobilizeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StadiumHeadliner extends CardImpl { + + public StadiumHeadliner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Mobilize 1 + this.addAbility(new MobilizeAbility(1)); + + // {1}{R}, Sacrifice this creature: It deals damage equal to the number of creatures you control to target creature. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect( + SourcePermanentPowerValue.NOT_NEGATIVE, "it" + ), new ManaCostsImpl<>("{1}{R}")); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private StadiumHeadliner(final StadiumHeadliner card) { + super(card); + } + + @Override + public StadiumHeadliner copy() { + return new StadiumHeadliner(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index a358fd282a4..2833b2cda80 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -174,6 +174,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); + cards.add(new SetCardInfo("Stadium Headliner", 122, Rarity.RARE, mage.cards.s.StadiumHeadliner.class)); cards.add(new SetCardInfo("Stormbeacon Blade", 27, Rarity.UNCOMMON, mage.cards.s.StormbeaconBlade.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); From a26a13954aebfee49909b94b4fa8c488d1612dbd Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 20:36:21 -0400 Subject: [PATCH 53/71] [TDM] Implement Sonic Shrieker --- Mage.Sets/src/mage/cards/s/SonicShrieker.java | 93 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 94 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SonicShrieker.java diff --git a/Mage.Sets/src/mage/cards/s/SonicShrieker.java b/Mage.Sets/src/mage/cards/s/SonicShrieker.java new file mode 100644 index 00000000000..847e7447909 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SonicShrieker.java @@ -0,0 +1,93 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SonicShrieker extends CardImpl { + + public SonicShrieker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}{B}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature enters, it deals 2 damage to any target and you gain 2 life. If a player is dealt damage this way, they discard a card. + Ability ability = new EntersBattlefieldTriggeredAbility(new SonicShriekerEffect()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private SonicShrieker(final SonicShrieker card) { + super(card); + } + + @Override + public SonicShrieker copy() { + return new SonicShrieker(this); + } +} + +class SonicShriekerEffect extends OneShotEffect { + + SonicShriekerEffect() { + super(Outcome.Benefit); + staticText = "it deals 2 damage to any target and you gain 2 life. " + + "If a player is dealt damage this way, they discard a card"; + } + + private SonicShriekerEffect(final SonicShriekerEffect effect) { + super(effect); + } + + @Override + public SonicShriekerEffect copy() { + return new SonicShriekerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = dealDamage(getTargetPointer().getFirst(game, source), game, source); + Optional.ofNullable(source.getControllerId()) + .map(game::getPlayer) + .ifPresent(controller -> controller.gainLife(2, game, source)); + if (player != null) { + player.discard(1, false, false, source, game); + } + return true; + } + + private static Player dealDamage(UUID targetId, Game game, Ability source) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + permanent.damage(2, source, game); + return null; + } + Player player = game.getPlayer(targetId); + if (player != null && player.damage(2, source, game) > 0) { + return player; + } + return null; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 2833b2cda80..b1b6c9ae8a1 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -173,6 +173,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Snakeskin Veil", 159, Rarity.COMMON, mage.cards.s.SnakeskinVeil.class)); cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class)); + cards.add(new SetCardInfo("Sonic Shrieker", 226, Rarity.UNCOMMON, mage.cards.s.SonicShrieker.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); cards.add(new SetCardInfo("Stadium Headliner", 122, Rarity.RARE, mage.cards.s.StadiumHeadliner.class)); cards.add(new SetCardInfo("Stormbeacon Blade", 27, Rarity.UNCOMMON, mage.cards.s.StormbeaconBlade.class)); From f9ff7e05a1cb5b9a343106f152af37b96cba3d71 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 20:39:09 -0400 Subject: [PATCH 54/71] [TDM] Implement Static Snare --- Mage.Sets/src/mage/cards/s/StaticSnare.java | 55 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 56 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/StaticSnare.java diff --git a/Mage.Sets/src/mage/cards/s/StaticSnare.java b/Mage.Sets/src/mage/cards/s/StaticSnare.java new file mode 100644 index 00000000000..532e26e12b3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StaticSnare.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StaticSnare extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_ATTACKING_CREATURE); + private static final Hint hint = new ValueHint("Attacking creatures", xValue); + + public StaticSnare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // This spell costs {1} less to cast for each attacking creature. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(hint)); + + // When this enchantment enters, exile target artifact or creature an opponent controls until this enchantment leaves the battlefield. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE)); + this.addAbility(ability); + } + + private StaticSnare(final StaticSnare card) { + super(card); + } + + @Override + public StaticSnare copy() { + return new StaticSnare(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index b1b6c9ae8a1..6e6b33810e3 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -176,6 +176,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sonic Shrieker", 226, Rarity.UNCOMMON, mage.cards.s.SonicShrieker.class)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); cards.add(new SetCardInfo("Stadium Headliner", 122, Rarity.RARE, mage.cards.s.StadiumHeadliner.class)); + cards.add(new SetCardInfo("Static Snare", 26, Rarity.UNCOMMON, mage.cards.s.StaticSnare.class)); cards.add(new SetCardInfo("Stormbeacon Blade", 27, Rarity.UNCOMMON, mage.cards.s.StormbeaconBlade.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); From 8bde863b9498bec0ef3ed128c724aa36dd9b7632 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 30 Mar 2025 20:45:36 -0400 Subject: [PATCH 55/71] [TDM] Implement Zurgo, Thunder's Decree --- .../src/mage/cards/z/ZurgoThundersDecree.java | 58 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 59 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/z/ZurgoThundersDecree.java diff --git a/Mage.Sets/src/mage/cards/z/ZurgoThundersDecree.java b/Mage.Sets/src/mage/cards/z/ZurgoThundersDecree.java new file mode 100644 index 00000000000..be9b2e959ff --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZurgoThundersDecree.java @@ -0,0 +1,58 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.CantBeSacrificedSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MobilizeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZurgoThundersDecree extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.WARRIOR, ""); + + static { + filter.add(TokenPredicate.TRUE); + } + + private static final Condition condition = new IsStepCondition(PhaseStep.END_TURN, true); + + public ZurgoThundersDecree(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Mobilize 2 + this.addAbility(new MobilizeAbility(2)); + + // During your end step, Warrior tokens you control have "This token can't be sacrificed." + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilityControlledEffect( + new SimpleStaticAbility(new CantBeSacrificedSourceEffect()), Duration.WhileOnBattlefield + ), condition, "during your end step, Warrior tokens you control have \"This token can't be sacrificed.\""))); + } + + private ZurgoThundersDecree(final ZurgoThundersDecree card) { + super(card); + } + + @Override + public ZurgoThundersDecree copy() { + return new ZurgoThundersDecree(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 6e6b33810e3..9371cf4d2d0 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -216,6 +216,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Worthy Cost", 99, Rarity.COMMON, mage.cards.w.WorthyCost.class)); cards.add(new SetCardInfo("Yathan Tombguard", 100, Rarity.UNCOMMON, mage.cards.y.YathanTombguard.class)); cards.add(new SetCardInfo("Zurgo's Vanguard", 133, Rarity.UNCOMMON, mage.cards.z.ZurgosVanguard.class)); + cards.add(new SetCardInfo("Zurgo, Thunder's Decree", 237, Rarity.RARE, mage.cards.z.ZurgoThundersDecree.class)); cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); } From 2bf680fb2a62032efd4c0311e929b0dc1ebc38d0 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 10:11:44 -0400 Subject: [PATCH 56/71] [TDM] Implement Elspeth, Storm Slayer --- .../src/mage/cards/e/ElspethStormSlayer.java | 74 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java diff --git a/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java b/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java new file mode 100644 index 00000000000..cbf61ff0f3a --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java @@ -0,0 +1,74 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.replacement.CreateTwiceThatManyTokensEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.permanent.token.SoldierToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElspethStormSlayer extends CardImpl { + + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("creature an opponent controls with mana value 3 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); + } + + public ElspethStormSlayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{W}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELSPETH); + this.setStartingLoyalty(5); + + // If one or more tokens would be created under your control, twice that many of those tokens are created instead. + this.addAbility(new SimpleStaticAbility(new CreateTwiceThatManyTokensEffect())); + + // +1: Create a 1/1 white Soldier creature token. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SoldierToken()), 1)); + + // 0: Put a +1/+1 counter on each creature you control. Those creatures gain flying until your next turn. + Ability ability = new LoyaltyAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE + ), 0); + ability.addEffect(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("those creatures gain flying until end of turn")); + this.addAbility(ability); + + // -3: Destroy target creature an opponent controls with mana value 3 or greater. + ability = new LoyaltyAbility(new DestroyTargetEffect(), -3); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private ElspethStormSlayer(final ElspethStormSlayer card) { + super(card); + } + + @Override + public ElspethStormSlayer copy() { + return new ElspethStormSlayer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 9371cf4d2d0..98cf94b8163 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -78,6 +78,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Dragonstorm Globe", 241, Rarity.COMMON, mage.cards.d.DragonstormGlobe.class)); cards.add(new SetCardInfo("Dusyut Earthcarver", 141, Rarity.COMMON, mage.cards.d.DusyutEarthcarver.class)); cards.add(new SetCardInfo("Duty Beyond Death", 10, Rarity.UNCOMMON, mage.cards.d.DutyBeyondDeath.class)); + cards.add(new SetCardInfo("Elspeth, Storm Slayer", 11, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class)); cards.add(new SetCardInfo("Embermouth Sentinel", 242, Rarity.COMMON, mage.cards.e.EmbermouthSentinel.class)); cards.add(new SetCardInfo("Encroaching Dragonstorm", 142, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class)); cards.add(new SetCardInfo("Equilibrium Adept", 106, Rarity.UNCOMMON, mage.cards.e.EquilibriumAdept.class)); From c8504322f1d6636863adbc2a36bffecc9cab9097 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 10:15:34 -0400 Subject: [PATCH 57/71] [TDM] Implement Essence Anchor --- Mage.Sets/src/mage/cards/e/EssenceAnchor.java | 64 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/e/EssenceAnchor.java diff --git a/Mage.Sets/src/mage/cards/e/EssenceAnchor.java b/Mage.Sets/src/mage/cards/e/EssenceAnchor.java new file mode 100644 index 00000000000..503512b1615 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EssenceAnchor.java @@ -0,0 +1,64 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.token.ZombieDruidToken; +import mage.watchers.common.CardsLeftGraveyardWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EssenceAnchor extends CardImpl { + + public EssenceAnchor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + // At the beginning of your upkeep, surveil 1. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SurveilEffect(1))); + + // {T}: Create a 2/2 black Zombie Druid creature token. Activate only during your turn and only if a card left your graveyard this turn. + this.addAbility(new ActivateIfConditionActivatedAbility( + new CreateTokenEffect(new ZombieDruidToken()), + new TapSourceCost(), EssenceAnchorCondition.instance + ), new CardsLeftGraveyardWatcher()); + } + + private EssenceAnchor(final EssenceAnchor card) { + super(card); + } + + @Override + public EssenceAnchor copy() { + return new EssenceAnchor(this); + } +} + +enum EssenceAnchorCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.isActivePlayer(source.getControllerId()) + && !game + .getState() + .getWatcher(CardsLeftGraveyardWatcher.class) + .getCardsThatLeftGraveyard(source.getControllerId(), game) + .isEmpty(); + } + + @Override + public String toString() { + return "during your turn and only if a card left your graveyard this turn"; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 98cf94b8163..b9039e2d8fe 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -82,6 +82,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Embermouth Sentinel", 242, Rarity.COMMON, mage.cards.e.EmbermouthSentinel.class)); cards.add(new SetCardInfo("Encroaching Dragonstorm", 142, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class)); cards.add(new SetCardInfo("Equilibrium Adept", 106, Rarity.UNCOMMON, mage.cards.e.EquilibriumAdept.class)); + cards.add(new SetCardInfo("Essence Anchor", 44, Rarity.UNCOMMON, mage.cards.e.EssenceAnchor.class)); cards.add(new SetCardInfo("Evolving Wilds", 255, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Fangkeeper's Familiar", 183, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class)); cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 184, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class)); From 113d93aef4c11d33241c6873f9613020bab5556e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 10:21:12 -0400 Subject: [PATCH 58/71] [TDM] Implement Frontline Rush --- Mage.Sets/src/mage/cards/f/FrontlineRush.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 42 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/FrontlineRush.java diff --git a/Mage.Sets/src/mage/cards/f/FrontlineRush.java b/Mage.Sets/src/mage/cards/f/FrontlineRush.java new file mode 100644 index 00000000000..4cab716fcb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrontlineRush.java @@ -0,0 +1,41 @@ +package mage.cards.f; + +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.GoblinToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrontlineRush extends CardImpl { + + public FrontlineRush(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{W}"); + + // Choose one -- + // * Create two 1/1 red Goblin creature tokens. + this.getSpellAbility().addEffect(new CreateTokenEffect(new GoblinToken(), 2)); + + // * Target creature gets +X/+X until end of turn, where X is the number of creatures you control. + this.getSpellAbility().addMode(new Mode(new BoostTargetEffect( + CreaturesYouControlCount.instance, CreaturesYouControlCount.instance + )).addTarget(new TargetCreaturePermanent())); + } + + private FrontlineRush(final FrontlineRush card) { + super(card); + } + + @Override + public FrontlineRush copy() { + return new FrontlineRush(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index b9039e2d8fe..7c4c17cbdc3 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -92,6 +92,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Fortress Kin-Guard", 12, Rarity.COMMON, mage.cards.f.FortressKinGuard.class)); cards.add(new SetCardInfo("Fresh Start", 46, Rarity.UNCOMMON, mage.cards.f.FreshStart.class)); cards.add(new SetCardInfo("Frontier Bivouac", 256, Rarity.UNCOMMON, mage.cards.f.FrontierBivouac.class)); + cards.add(new SetCardInfo("Frontline Rush", 186, Rarity.UNCOMMON, mage.cards.f.FrontlineRush.class)); cards.add(new SetCardInfo("Glacial Dragonhunt", 188, Rarity.UNCOMMON, mage.cards.g.GlacialDragonhunt.class)); cards.add(new SetCardInfo("Great Arashin City", 257, Rarity.RARE, mage.cards.g.GreatArashinCity.class)); cards.add(new SetCardInfo("Gurmag Nightwatch", 190, Rarity.COMMON, mage.cards.g.GurmagNightwatch.class)); From 35d03e10f1b820329d0de565bae1b620fb443557 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 10:41:35 -0400 Subject: [PATCH 59/71] [TDM] Implement Trade Route Envoy --- .../src/mage/cards/t/TradeRouteEnvoy.java | 84 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TradeRouteEnvoy.java diff --git a/Mage.Sets/src/mage/cards/t/TradeRouteEnvoy.java b/Mage.Sets/src/mage/cards/t/TradeRouteEnvoy.java new file mode 100644 index 00000000000..28512ab25a8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TradeRouteEnvoy.java @@ -0,0 +1,84 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TradeRouteEnvoy extends CardImpl { + + public TradeRouteEnvoy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // When this creature enters, draw a card if you control a creature with a counter on it. If you don't draw a card this way, put a +1/+1 counter on this creature. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TradeRouteEnvoyEffect())); + } + + private TradeRouteEnvoy(final TradeRouteEnvoy card) { + super(card); + } + + @Override + public TradeRouteEnvoy copy() { + return new TradeRouteEnvoy(this); + } +} + +class TradeRouteEnvoyEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(CounterAnyPredicate.instance); + } + + TradeRouteEnvoyEffect() { + super(Outcome.Benefit); + staticText = "draw a card if you control a creature with a counter on it. " + + "If you don't draw a card this way, put a +1/+1 counter on this creature"; + } + + private TradeRouteEnvoyEffect(final TradeRouteEnvoyEffect effect) { + super(effect); + } + + @Override + public TradeRouteEnvoyEffect copy() { + return new TradeRouteEnvoyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.getBattlefield().contains(filter, source, game, 1)) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null && player.drawCards(1, source, game) > 0) { + return true; + } + } + Optional.ofNullable(source.getSourcePermanentIfItStillExists(game)) + .ifPresent(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 7c4c17cbdc3..e94ce50a506 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -196,6 +196,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Temur Tawnyback", 229, Rarity.COMMON, mage.cards.t.TemurTawnyback.class)); cards.add(new SetCardInfo("The Sibsig Ceremony", 91, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class)); cards.add(new SetCardInfo("Thornwood Falls", 269, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Trade Route Envoy", 163, Rarity.COMMON, mage.cards.t.TradeRouteEnvoy.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class)); From fdfe7c0d6e76b252386692eb6ec5482a76cb7cfb Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 11:48:40 -0400 Subject: [PATCH 60/71] [TDM] Implement Rainveil Rejuvenator --- .../src/mage/cards/r/RainveilRejuvenator.java | 46 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 47 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RainveilRejuvenator.java diff --git a/Mage.Sets/src/mage/cards/r/RainveilRejuvenator.java b/Mage.Sets/src/mage/cards/r/RainveilRejuvenator.java new file mode 100644 index 00000000000..c878316282e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RainveilRejuvenator.java @@ -0,0 +1,46 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.mana.DynamicManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RainveilRejuvenator extends CardImpl { + + public RainveilRejuvenator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.ELEPHANT); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // When this creature enters, you may mill three cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(3), true)); + + // {T}: Add an amount of {G} equal to this creature's power. + this.addAbility(new DynamicManaAbility( + Mana.GreenMana(1), SourcePermanentPowerValue.NOT_NEGATIVE, "Add an amount of {G} equal to {this}'s power." + )); + } + + private RainveilRejuvenator(final RainveilRejuvenator card) { + super(card); + } + + @Override + public RainveilRejuvenator copy() { + return new RainveilRejuvenator(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index e94ce50a506..46766fc91ac 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -144,6 +144,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Plains", 277, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Poised Practitioner", 18, Rarity.COMMON, mage.cards.p.PoisedPractitioner.class)); cards.add(new SetCardInfo("Qarsi Revenant", 86, Rarity.RARE, mage.cards.q.QarsiRevenant.class)); + cards.add(new SetCardInfo("Rainveil Rejuvenator", 152, Rarity.UNCOMMON, mage.cards.r.RainveilRejuvenator.class)); cards.add(new SetCardInfo("Rakshasa's Bargain", 214, Rarity.UNCOMMON, mage.cards.r.RakshasasBargain.class)); cards.add(new SetCardInfo("Rally the Monastery", 19, Rarity.UNCOMMON, mage.cards.r.RallyTheMonastery.class)); cards.add(new SetCardInfo("Rebellious Strike", 20, Rarity.COMMON, mage.cards.r.RebelliousStrike.class)); From 588b0f390f70f4e796df96a5b54cfcc5ec089706 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 14:02:01 -0400 Subject: [PATCH 61/71] [TDM] Implement Gurmag Rakshasa --- .../src/mage/cards/g/GurmagRakshasa.java | 51 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 52 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GurmagRakshasa.java diff --git a/Mage.Sets/src/mage/cards/g/GurmagRakshasa.java b/Mage.Sets/src/mage/cards/g/GurmagRakshasa.java new file mode 100644 index 00000000000..828278fe81f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GurmagRakshasa.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GurmagRakshasa extends CardImpl { + + public GurmagRakshasa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Menace + this.addAbility(new MenaceAbility()); + + // When this creature enters, target creature an opponent controls gets -2/-2 until end of turn and target creature you control gets +2/+2 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, -2)); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + ability.addEffect(new BoostTargetEffect(2, 2) + .setTargetPointer(new SecondTargetPointer()) + .concatBy("and")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private GurmagRakshasa(final GurmagRakshasa card) { + super(card); + } + + @Override + public GurmagRakshasa copy() { + return new GurmagRakshasa(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 46766fc91ac..0f212b98028 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -96,6 +96,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Glacial Dragonhunt", 188, Rarity.UNCOMMON, mage.cards.g.GlacialDragonhunt.class)); cards.add(new SetCardInfo("Great Arashin City", 257, Rarity.RARE, mage.cards.g.GreatArashinCity.class)); cards.add(new SetCardInfo("Gurmag Nightwatch", 190, Rarity.COMMON, mage.cards.g.GurmagNightwatch.class)); + cards.add(new SetCardInfo("Gurmag Rakshasa", 81, Rarity.UNCOMMON, mage.cards.g.GurmagRakshasa.class)); cards.add(new SetCardInfo("Hardened Tactician", 191, Rarity.UNCOMMON, mage.cards.h.HardenedTactician.class)); cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); cards.add(new SetCardInfo("Humbling Elder", 48, Rarity.COMMON, mage.cards.h.HumblingElder.class)); From 4d1f154b13f88fa6c85bbedc16998b16d9cebb82 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 14:12:04 -0400 Subject: [PATCH 62/71] [TDM] Implement Rediscover the Way --- .../src/mage/cards/r/RediscoverTheWay.java | 86 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 87 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RediscoverTheWay.java diff --git a/Mage.Sets/src/mage/cards/r/RediscoverTheWay.java b/Mage.Sets/src/mage/cards/r/RediscoverTheWay.java new file mode 100644 index 00000000000..efab4b20360 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RediscoverTheWay.java @@ -0,0 +1,86 @@ +package mage.cards.r; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RediscoverTheWay extends CardImpl { + + public RediscoverTheWay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}{R}{W}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II -- Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new LookLibraryAndPickControllerEffect(3, 1, PutCards.HAND, PutCards.BOTTOM_ANY) + ); + + // III -- Whenever you cast a noncreature spell this turn, target creature you control gains double strike until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new CreateDelayedTriggeredAbilityEffect(new RediscoverTheWayTriggeredAbility()) + ); + this.addAbility(sagaAbility); + } + + private RediscoverTheWay(final RediscoverTheWay card) { + super(card); + } + + @Override + public RediscoverTheWay copy() { + return new RediscoverTheWay(this); + } +} + +class RediscoverTheWayTriggeredAbility extends DelayedTriggeredAbility { + + RediscoverTheWayTriggeredAbility() { + super(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance()), Duration.EndOfTurn, false, false); + this.addTarget(new TargetControlledCreaturePermanent()); + this.setTriggerPhrase("Whenever you cast a noncreature spell this turn, "); + } + + private RediscoverTheWayTriggeredAbility(final RediscoverTheWayTriggeredAbility ability) { + super(ability); + } + + @Override + public RediscoverTheWayTriggeredAbility copy() { + return new RediscoverTheWayTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpell(event.getTargetId()); + return spell != null && !spell.isCreature(game); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 0f212b98028..3c5f8ded306 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -149,6 +149,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Rakshasa's Bargain", 214, Rarity.UNCOMMON, mage.cards.r.RakshasasBargain.class)); cards.add(new SetCardInfo("Rally the Monastery", 19, Rarity.UNCOMMON, mage.cards.r.RallyTheMonastery.class)); cards.add(new SetCardInfo("Rebellious Strike", 20, Rarity.COMMON, mage.cards.r.RebelliousStrike.class)); + cards.add(new SetCardInfo("Rediscover the Way", 215, Rarity.RARE, mage.cards.r.RediscoverTheWay.class)); cards.add(new SetCardInfo("Reigning Victor", 216, Rarity.COMMON, mage.cards.r.ReigningVictor.class)); cards.add(new SetCardInfo("Reputable Merchant", 217, Rarity.COMMON, mage.cards.r.ReputableMerchant.class)); cards.add(new SetCardInfo("Rescue Leopard", 116, Rarity.COMMON, mage.cards.r.RescueLeopard.class)); From c044593efeb6e81ab2e102555f7f65c8c19547d4 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 14:15:30 -0400 Subject: [PATCH 63/71] [TDM] Implement Salt Road Skirmish --- .../src/mage/cards/s/SaltRoadSkirmish.java | 73 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SaltRoadSkirmish.java diff --git a/Mage.Sets/src/mage/cards/s/SaltRoadSkirmish.java b/Mage.Sets/src/mage/cards/s/SaltRoadSkirmish.java new file mode 100644 index 00000000000..5f8189291b5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaltRoadSkirmish.java @@ -0,0 +1,73 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.token.RedWarriorToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SaltRoadSkirmish extends CardImpl { + + public SaltRoadSkirmish(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); + + // Destroy target creature. Create two 1/1 red Warrior creature tokens. They gain haste until end of turn. Sacrifice them at the beginning of the next end step. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new SaltRoadSkirmishEffect()); + } + + private SaltRoadSkirmish(final SaltRoadSkirmish card) { + super(card); + } + + @Override + public SaltRoadSkirmish copy() { + return new SaltRoadSkirmish(this); + } +} + +class SaltRoadSkirmishEffect extends OneShotEffect { + + SaltRoadSkirmishEffect() { + super(Outcome.Benefit); + staticText = ""; + } + + private SaltRoadSkirmishEffect(final SaltRoadSkirmishEffect effect) { + super(effect); + } + + @Override + public SaltRoadSkirmishEffect copy() { + return new SaltRoadSkirmishEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new RedWarriorToken(); + token.putOntoBattlefield(2, game, source); + game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) + .setTargetPointer(new FixedTargets(token, game)), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new SacrificeTargetEffect("sacrifice them").setTargetPointer(new FixedTargets(token, game)) + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 3c5f8ded306..281702bf3f3 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -163,6 +163,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sage of the Fang", 155, Rarity.UNCOMMON, mage.cards.s.SageOfTheFang.class)); cards.add(new SetCardInfo("Sagu Pummeler", 156, Rarity.COMMON, mage.cards.s.SaguPummeler.class)); cards.add(new SetCardInfo("Salt Road Packbeast", 23, Rarity.COMMON, mage.cards.s.SaltRoadPackbeast.class)); + cards.add(new SetCardInfo("Salt Road Skirmish", 88, Rarity.UNCOMMON, mage.cards.s.SaltRoadSkirmish.class)); cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 266, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); cards.add(new SetCardInfo("Sarkhan's Resolve", 158, Rarity.COMMON, mage.cards.s.SarkhansResolve.class)); From f2ea83f73bf07e1b12485089d570bc0c455e86d9 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:16:40 -0400 Subject: [PATCH 64/71] update ban lists --- .../Mage.Deck.Constructed/src/mage/deck/Legacy.java | 2 ++ .../Mage.Deck.Constructed/src/mage/deck/Modern.java | 1 + .../Mage.Deck.Constructed/src/mage/deck/Pauper.java | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index a0dfd0d541b..a75071e26b9 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -64,6 +64,7 @@ public class Legacy extends Constructed { banned.add("Sensei's Divining Top"); banned.add("Skullclamp"); banned.add("Sol Ring"); + banned.add("Sowing Mycospawn"); banned.add("Strip Mine"); banned.add("Survival of the Fittest"); banned.add("Time Vault"); @@ -72,6 +73,7 @@ public class Legacy extends Constructed { banned.add("Tinker"); banned.add("Tolarian Academy"); banned.add("Treasure Cruise"); + banned.add("Troll of Khazad-dum"); banned.add("Underworld Breach"); banned.add("Vampiric Tutor"); banned.add("Vexing Bauble"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java index f079385508e..cb7a5fa939c 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java @@ -69,6 +69,7 @@ public class Modern extends Constructed { banned.add("Treasure Cruise"); banned.add("Tree of Tales"); banned.add("Umezawa's Jitte"); + banned.add("Underworld Breach"); banned.add("Up the Beanstalk"); banned.add("Uro, Titan of Nature's Wrath"); banned.add("Vault of Whispers"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java index e7ec4d300ce..6dbba75643f 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java @@ -29,6 +29,7 @@ public class Pauper extends Constructed { banned.add("All That Glitters"); banned.add("Arcum's Astrolabe"); banned.add("Atog"); + banned.add("Basking Broodscale"); banned.add("Bonder's Ornament"); banned.add("Chatterstorm"); banned.add("Cloud of Faeries"); @@ -36,6 +37,7 @@ public class Pauper extends Constructed { banned.add("Cranial Plating"); banned.add("Cranial Ram"); banned.add("Daze"); + banned.add("Deadly Dispute"); banned.add("Disciple of the Vault"); banned.add("Empty the Warrens"); banned.add("Fall from Favor"); @@ -44,13 +46,12 @@ public class Pauper extends Constructed { banned.add("Gitaxian Probe"); banned.add("Grapeshot"); banned.add("Gush"); - banned.add("High Tide"); banned.add("Hymn to Tourach"); banned.add("Invigorate"); + banned.add("Kuldotha Rebirth"); banned.add("Monastery Swiftspear"); banned.add("Mystic Sanctuary"); banned.add("Peregrine Drake"); - banned.add("Prophetic Prism"); banned.add("Sinkhole"); banned.add("Stirring Bard"); banned.add("Sojourner's Companion"); From 1da557534eb4f8486a8d36173c715051d30540ef Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:21:41 -0400 Subject: [PATCH 65/71] [TDM] Implement Thunder of Unity --- .../src/mage/cards/t/ThunderOfUnity.java | 83 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 84 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/ThunderOfUnity.java diff --git a/Mage.Sets/src/mage/cards/t/ThunderOfUnity.java b/Mage.Sets/src/mage/cards/t/ThunderOfUnity.java new file mode 100644 index 00000000000..e1619ec66d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderOfUnity.java @@ -0,0 +1,83 @@ +package mage.cards.t; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThunderOfUnity extends CardImpl { + + public ThunderOfUnity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{W}{B}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- You draw two cards and you lose 2 life. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, + new DrawCardSourceControllerEffect(2, true), + new LoseLifeSourceControllerEffect(2).concatBy("and") + ); + + // II, III -- Whenever a creature you control enters this turn, each opponent loses 1 life and you gain 1 life. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_III, + new CreateDelayedTriggeredAbilityEffect(new ThunderOfUnityTriggeredAbility()) + ); + this.addAbility(sagaAbility); + } + + private ThunderOfUnity(final ThunderOfUnity card) { + super(card); + } + + @Override + public ThunderOfUnity copy() { + return new ThunderOfUnity(this); + } +} + +class ThunderOfUnityTriggeredAbility extends DelayedTriggeredAbility { + + ThunderOfUnityTriggeredAbility() { + super(new LoseLifeOpponentsEffect(1), Duration.EndOfTurn, false, false); + this.addEffect(new GainLifeEffect(1).concatBy("and")); + this.setTriggerPhrase("Whenever a creature you control enters this turn, "); + } + + private ThunderOfUnityTriggeredAbility(final ThunderOfUnityTriggeredAbility ability) { + super(ability); + } + + @Override + public ThunderOfUnityTriggeredAbility copy() { + return new ThunderOfUnityTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.isCreature(game) && permanent.isControlledBy(getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 281702bf3f3..c4938d72957 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -200,6 +200,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Temur Tawnyback", 229, Rarity.COMMON, mage.cards.t.TemurTawnyback.class)); cards.add(new SetCardInfo("The Sibsig Ceremony", 91, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class)); cards.add(new SetCardInfo("Thornwood Falls", 269, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Thunder of Unity", 231, Rarity.RARE, mage.cards.t.ThunderOfUnity.class)); cards.add(new SetCardInfo("Trade Route Envoy", 163, Rarity.COMMON, mage.cards.t.TradeRouteEnvoy.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); From 19dfcc4e5c6a29800fda03f6f65193d664c7c5f9 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:34:50 -0400 Subject: [PATCH 66/71] [TDM] Implement Traveling Botanist --- .../src/mage/cards/t/TravelingBotanist.java | 88 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 89 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TravelingBotanist.java diff --git a/Mage.Sets/src/mage/cards/t/TravelingBotanist.java b/Mage.Sets/src/mage/cards/t/TravelingBotanist.java new file mode 100644 index 00000000000..31fac690a1c --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TravelingBotanist.java @@ -0,0 +1,88 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTappedSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TravelingBotanist extends CardImpl { + + public TravelingBotanist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever this creature becomes tapped, look at the top card of your library. If it's a land card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard. + this.addAbility(new BecomesTappedSourceTriggeredAbility(new TravelingBotanistEffect())); + } + + private TravelingBotanist(final TravelingBotanist card) { + super(card); + } + + @Override + public TravelingBotanist copy() { + return new TravelingBotanist(this); + } +} + +class TravelingBotanistEffect extends OneShotEffect { + + TravelingBotanistEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. If it's a land card, you may reveal it and " + + "put it into your hand. If you don't put the card into your hand, you may put it into your graveyard"; + } + + private TravelingBotanistEffect(final TravelingBotanistEffect effect) { + super(effect); + } + + @Override + public TravelingBotanistEffect copy() { + return new TravelingBotanistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.lookAtCards("Top card of library", card, game); + if (card.isLand(game) && player.chooseUse( + Outcome.DrawCard, "Reveal " + card.getName() + + " and put it into your hand?", source, game + )) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); + return true; + } + if (player.chooseUse(Outcome.Neutral, "Put " + card.getName() + " into your graveyard?", source, game)) { + return player.moveCards(card, Zone.GRAVEYARD, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index c4938d72957..95a496a7951 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -203,6 +203,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Thunder of Unity", 231, Rarity.RARE, mage.cards.t.ThunderOfUnity.class)); cards.add(new SetCardInfo("Trade Route Envoy", 163, Rarity.COMMON, mage.cards.t.TradeRouteEnvoy.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Traveling Botanist", 164, Rarity.UNCOMMON, mage.cards.t.TravelingBotanist.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class)); cards.add(new SetCardInfo("Unburied Earthcarver", 95, Rarity.COMMON, mage.cards.u.UnburiedEarthcarver.class)); From 0758118014f05a9c36e99e20cbfb4ab68e4d6618 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:36:37 -0400 Subject: [PATCH 67/71] [TDM] Implement Veteran Ice Climber --- .../src/mage/cards/v/VeteranIceClimber.java | 51 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 52 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VeteranIceClimber.java diff --git a/Mage.Sets/src/mage/cards/v/VeteranIceClimber.java b/Mage.Sets/src/mage/cards/v/VeteranIceClimber.java new file mode 100644 index 00000000000..e4057da3457 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VeteranIceClimber.java @@ -0,0 +1,51 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VeteranIceClimber extends CardImpl { + + public VeteranIceClimber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // This creature can't be blocked. + this.addAbility(new CantBeBlockedSourceAbility()); + + // Whenever this creature attacks, up to one target player mills cards equal to this creature's power. + Ability ability = new AttacksTriggeredAbility(new MillCardsTargetEffect(SourcePermanentPowerValue.NOT_NEGATIVE)); + ability.addTarget(new TargetPlayer(0, 1, false)); + this.addAbility(ability); + } + + private VeteranIceClimber(final VeteranIceClimber card) { + super(card); + } + + @Override + public VeteranIceClimber copy() { + return new VeteranIceClimber(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 95a496a7951..a1d00e2c71e 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -215,6 +215,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Unsparing Boltcaster", 130, Rarity.UNCOMMON, mage.cards.u.UnsparingBoltcaster.class)); cards.add(new SetCardInfo("Ureni's Rebuff", 63, Rarity.UNCOMMON, mage.cards.u.UrenisRebuff.class)); cards.add(new SetCardInfo("Venerated Stormsinger", 97, Rarity.UNCOMMON, mage.cards.v.VeneratedStormsinger.class)); + cards.add(new SetCardInfo("Veteran Ice Climber", 64, Rarity.UNCOMMON, mage.cards.v.VeteranIceClimber.class)); cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); cards.add(new SetCardInfo("Wayspeaker Bodyguard", 34, Rarity.UNCOMMON, mage.cards.w.WayspeakerBodyguard.class)); From ba4f51698d3cce66a08fa20c933533848bd8991b Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:40:12 -0400 Subject: [PATCH 68/71] [TDM] Implement War Effort --- Mage.Sets/src/mage/cards/w/WarEffort.java | 73 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WarEffort.java diff --git a/Mage.Sets/src/mage/cards/w/WarEffort.java b/Mage.Sets/src/mage/cards/w/WarEffort.java new file mode 100644 index 00000000000..3c5b7ee0af5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WarEffort.java @@ -0,0 +1,73 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.token.RedWarriorToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WarEffort extends CardImpl { + + public WarEffort(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); + + // Creatures you control get +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield))); + + // Whenever you attack, create a 1/1 red Warrior creature token that's tapped and attacking. Sacrifice it at the beginning of the next end step. + this.addAbility(new AttacksWithCreaturesTriggeredAbility(new WarEffortEffect(), 1)); + } + + private WarEffort(final WarEffort card) { + super(card); + } + + @Override + public WarEffort copy() { + return new WarEffort(this); + } +} + +class WarEffortEffect extends OneShotEffect { + + WarEffortEffect() { + super(Outcome.Benefit); + staticText = "create a 1/1 red Warrior creature token that's tapped and attacking. " + + "Sacrifice it at the beginning of the next end step"; + } + + private WarEffortEffect(final WarEffortEffect effect) { + super(effect); + } + + @Override + public WarEffortEffect copy() { + return new WarEffortEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new RedWarriorToken(); + token.putOntoBattlefield(1, game, source, source.getControllerId(), true, true); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new SacrificeTargetEffect("sacrifice it").setTargetPointer(new FixedTargets(token, game)) + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index a1d00e2c71e..dc6cc22ae65 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -217,6 +217,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Venerated Stormsinger", 97, Rarity.UNCOMMON, mage.cards.v.VeneratedStormsinger.class)); cards.add(new SetCardInfo("Veteran Ice Climber", 64, Rarity.UNCOMMON, mage.cards.v.VeteranIceClimber.class)); cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); + cards.add(new SetCardInfo("War Effort", 131, Rarity.UNCOMMON, mage.cards.w.WarEffort.class)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); cards.add(new SetCardInfo("Wayspeaker Bodyguard", 34, Rarity.UNCOMMON, mage.cards.w.WayspeakerBodyguard.class)); cards.add(new SetCardInfo("Wild Ride", 132, Rarity.COMMON, mage.cards.w.WildRide.class)); From 08135af525f88dde96dd421907feb06672d702df Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 31 Mar 2025 16:47:22 -0400 Subject: [PATCH 69/71] [TDM] Implement Yathan Roadwatcher --- .../src/mage/cards/y/YathanRoadwatcher.java | 60 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 61 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/y/YathanRoadwatcher.java diff --git a/Mage.Sets/src/mage/cards/y/YathanRoadwatcher.java b/Mage.Sets/src/mage/cards/y/YathanRoadwatcher.java new file mode 100644 index 00000000000..c48e3de7af9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YathanRoadwatcher.java @@ -0,0 +1,60 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.costs.common.MillCardsCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YathanRoadwatcher extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public YathanRoadwatcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When this creature enters, if you cast it, mill four cards. When you do, return target creature card with mana value 3 or less from your graveyard to the battlefield. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(), false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid( + ability, new MillCardsCost(4), "", false + )).withInterveningIf(CastFromEverywhereSourceCondition.instance)); + } + + private YathanRoadwatcher(final YathanRoadwatcher card) { + super(card); + } + + @Override + public YathanRoadwatcher copy() { + return new YathanRoadwatcher(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index dc6cc22ae65..1a0c64cf5d8 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -226,6 +226,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Wingspan Stride", 66, Rarity.COMMON, mage.cards.w.WingspanStride.class)); cards.add(new SetCardInfo("Winternight Stories", 67, Rarity.RARE, mage.cards.w.WinternightStories.class)); cards.add(new SetCardInfo("Worthy Cost", 99, Rarity.COMMON, mage.cards.w.WorthyCost.class)); + cards.add(new SetCardInfo("Yathan Roadwatcher", 236, Rarity.RARE, mage.cards.y.YathanRoadwatcher.class)); cards.add(new SetCardInfo("Yathan Tombguard", 100, Rarity.UNCOMMON, mage.cards.y.YathanTombguard.class)); cards.add(new SetCardInfo("Zurgo's Vanguard", 133, Rarity.UNCOMMON, mage.cards.z.ZurgosVanguard.class)); cards.add(new SetCardInfo("Zurgo, Thunder's Decree", 237, Rarity.RARE, mage.cards.z.ZurgoThundersDecree.class)); From 730bd8e63d20fccbf92c359977bfe40bf6a6bd2b Mon Sep 17 00:00:00 2001 From: androosss <101566943+androosss@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:58:05 +0200 Subject: [PATCH 70/71] refactor: improved tokens structure (#13487) - removed duplicate tokens - fixed names of some tokens - corrected tokens used in tokens database --- .../src/mage/cards/c/CrushOfTentacles.java | 4 +- .../mage/cards/d/DanceOfTheTumbleweeds.java | 4 +- .../src/mage/cards/e/EyesOfTheWisent.java | 4 +- Mage.Sets/src/mage/cards/f/FleshCarver.java | 4 +- .../mage/cards/g/GrismoldTheDreadsower.java | 4 +- .../src/mage/cards/g/GrovetenderDruids.java | 4 +- .../src/mage/cards/i/InfernalGenesis.java | 4 +- .../src/mage/cards/m/MarathWillOfTheWild.java | 12 ++--- .../src/mage/cards/p/PhyrexianProcessor.java | 11 ++--- Mage.Sets/src/mage/cards/r/RallyTheHorde.java | 4 +- Mage.Sets/src/mage/cards/s/SeedGuardian.java | 4 +- .../src/mage/cards/s/SorinSolemnVisitor.java | 4 +- Mage.Sets/src/mage/cards/s/SpoilsOfBlood.java | 4 +- .../src/mage/cards/t/TumbleweedRising.java | 4 +- .../src/mage/cards/w/WalkerOfTheGrove.java | 4 +- .../token/CrushOfTentaclesToken.java | 29 ------------ .../game/permanent/token/DinDragonToken.java | 2 +- ...lToken.java => Elemental44GreenToken.java} | 10 ++--- ...nToken.java => ElementalXXGreenToken.java} | 12 ++--- .../token/GrovetenderDruidsPlantToken.java | 28 ------------ ...rrorToken.java => HorrorXXBlackToken.java} | 12 ++--- .../game/permanent/token/HumanRogueToken.java | 2 +- .../MarathWillOfTheWildElementalToken.java | 30 ------------- .../game/permanent/token/MinionToken.java | 15 +++---- .../game/permanent/token/MinionToken2.java | 28 ------------ .../token/NighteyesTheDesecratorToken.java | 44 ------------------- .../permanent/token/PhyrexianMinionToken.java | 33 ++++++++++++++ ...smoldPlantToken.java => Plant11Token.java} | 13 +++--- .../token/RallyTheHordeWarriorToken.java | 29 ------------ .../token/SorinSolemnVisitorVampireToken.java | 30 ------------- .../token/SpoilsOfBloodHorrorToken.java | 32 -------------- .../token/WalkerOfTheGroveToken.java | 29 ------------ Mage/src/main/resources/tokens-database.txt | 26 +++++------ 33 files changed, 112 insertions(+), 367 deletions(-) delete mode 100644 Mage/src/main/java/mage/game/permanent/token/CrushOfTentaclesToken.java rename Mage/src/main/java/mage/game/permanent/token/{EyesOfTheWisentElementalToken.java => Elemental44GreenToken.java} (59%) rename Mage/src/main/java/mage/game/permanent/token/{SeedGuardianToken.java => ElementalXXGreenToken.java} (60%) delete mode 100644 Mage/src/main/java/mage/game/permanent/token/GrovetenderDruidsPlantToken.java rename Mage/src/main/java/mage/game/permanent/token/{FleshCarverHorrorToken.java => HorrorXXBlackToken.java} (59%) delete mode 100644 Mage/src/main/java/mage/game/permanent/token/MarathWillOfTheWildElementalToken.java delete mode 100644 Mage/src/main/java/mage/game/permanent/token/MinionToken2.java delete mode 100644 Mage/src/main/java/mage/game/permanent/token/NighteyesTheDesecratorToken.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/PhyrexianMinionToken.java rename Mage/src/main/java/mage/game/permanent/token/{GrismoldPlantToken.java => Plant11Token.java} (62%) delete mode 100644 Mage/src/main/java/mage/game/permanent/token/RallyTheHordeWarriorToken.java delete mode 100644 Mage/src/main/java/mage/game/permanent/token/SorinSolemnVisitorVampireToken.java delete mode 100644 Mage/src/main/java/mage/game/permanent/token/SpoilsOfBloodHorrorToken.java delete mode 100644 Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java diff --git a/Mage.Sets/src/mage/cards/c/CrushOfTentacles.java b/Mage.Sets/src/mage/cards/c/CrushOfTentacles.java index 933d08d7330..2e2f579b3c0 100644 --- a/Mage.Sets/src/mage/cards/c/CrushOfTentacles.java +++ b/Mage.Sets/src/mage/cards/c/CrushOfTentacles.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterNonlandPermanent; -import mage.game.permanent.token.CrushOfTentaclesToken; +import mage.game.permanent.token.OctopusToken; /** * @@ -25,7 +25,7 @@ public final class CrushOfTentacles extends CardImpl { // Return all nonland permanents to their owners' hands. If Crush of Tentacles surge cost was paid, create an 8/8 blue Octopus creature token. getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(new FilterNonlandPermanent("nonland permanents"))); - Effect effect = new ConditionalOneShotEffect(new CreateTokenEffect(new CrushOfTentaclesToken()), SurgedCondition.instance); + Effect effect = new ConditionalOneShotEffect(new CreateTokenEffect(new OctopusToken()), SurgedCondition.instance); effect.setText("If this spell's surge cost was paid, create an 8/8 blue Octopus creature token"); getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/d/DanceOfTheTumbleweeds.java b/Mage.Sets/src/mage/cards/d/DanceOfTheTumbleweeds.java index 9a158a24685..0d6f28edb95 100644 --- a/Mage.Sets/src/mage/cards/d/DanceOfTheTumbleweeds.java +++ b/Mage.Sets/src/mage/cards/d/DanceOfTheTumbleweeds.java @@ -17,7 +17,7 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.permanent.token.SeedGuardianToken; +import mage.game.permanent.token.ElementalXXGreenToken; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -83,7 +83,7 @@ class DanceOfTheTumbleweedsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - return new SeedGuardianToken(LandsYouControlCount.instance.calculate(game, source, this)) + return new ElementalXXGreenToken(LandsYouControlCount.instance.calculate(game, source, this)) .putOntoBattlefield(1, game, source); } } diff --git a/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java b/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java index 5da9e6b315e..4a97eda2c78 100644 --- a/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java +++ b/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.permanent.token.EyesOfTheWisentElementalToken; +import mage.game.permanent.token.Elemental44GreenToken; import java.util.UUID; @@ -33,7 +33,7 @@ public final class EyesOfTheWisent extends CardImpl { // Whenever an opponent casts a blue spell during your turn, you may create a 4/4 green Elemental creature token. this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new CreateTokenEffect(new EyesOfTheWisentElementalToken()), filter, true), + new SpellCastOpponentTriggeredAbility(new CreateTokenEffect(new Elemental44GreenToken()), filter, true), MyTurnCondition.instance, "Whenever an opponent casts a blue spell during your turn, you may create a 4/4 green Elemental creature token." ).addHint(MyTurnHint.instance)); diff --git a/Mage.Sets/src/mage/cards/f/FleshCarver.java b/Mage.Sets/src/mage/cards/f/FleshCarver.java index 8247d4cbb23..7ba5d59fdcd 100644 --- a/Mage.Sets/src/mage/cards/f/FleshCarver.java +++ b/Mage.Sets/src/mage/cards/f/FleshCarver.java @@ -24,7 +24,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.token.FleshCarverHorrorToken; +import mage.game.permanent.token.HorrorXXBlackToken; import mage.players.Player; import mage.target.common.TargetControlledPermanent; @@ -110,7 +110,7 @@ class FleshCarverEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int xValue = (Integer) getValue("power"); - return new CreateTokenEffect(new FleshCarverHorrorToken(xValue)).apply(game, source); + return new CreateTokenEffect(new HorrorXXBlackToken(xValue)).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/g/GrismoldTheDreadsower.java b/Mage.Sets/src/mage/cards/g/GrismoldTheDreadsower.java index 5ea3e290dc9..ab06afa01bb 100644 --- a/Mage.Sets/src/mage/cards/g/GrismoldTheDreadsower.java +++ b/Mage.Sets/src/mage/cards/g/GrismoldTheDreadsower.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.permanent.token.GrismoldPlantToken; +import mage.game.permanent.token.Plant11Token; import java.util.UUID; @@ -45,7 +45,7 @@ public final class GrismoldTheDreadsower extends CardImpl { // At the beginning of your end step, each player creates a 1/1 green Plant creature token. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new CreateTokenAllEffect(new GrismoldPlantToken(), TargetController.EACH_PLAYER) + new CreateTokenAllEffect(new Plant11Token(), TargetController.EACH_PLAYER) )); // Whenever a creature token dies, put a +1/+1 counter on Grismold, the Dreadsower. diff --git a/Mage.Sets/src/mage/cards/g/GrovetenderDruids.java b/Mage.Sets/src/mage/cards/g/GrovetenderDruids.java index 9964ddf09b5..aedf0f54914 100644 --- a/Mage.Sets/src/mage/cards/g/GrovetenderDruids.java +++ b/Mage.Sets/src/mage/cards/g/GrovetenderDruids.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.GrovetenderDruidsPlantToken; +import mage.game.permanent.token.Plant11Token; import java.util.UUID; @@ -29,7 +29,7 @@ public final class GrovetenderDruids extends CardImpl { // Rally-Whenever Grovetender Druids or another Ally you control enters, you may pay {1}. // If you do, create a 1/1 green Plant creature token. this.addAbility(new AllyEntersBattlefieldTriggeredAbility(new DoIfCostPaid( - new CreateTokenEffect(new GrovetenderDruidsPlantToken()), new GenericManaCost(1) + new CreateTokenEffect(new Plant11Token()), new GenericManaCost(1) ), false)); } diff --git a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java index 36d9fea4d6c..b2bf94bdd3c 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java +++ b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.game.Game; -import mage.game.permanent.token.MinionToken2; +import mage.game.permanent.token.MinionToken; import mage.game.permanent.token.Token; import mage.players.Player; @@ -42,7 +42,7 @@ public final class InfernalGenesis extends CardImpl { class InfernalGenesisEffect extends OneShotEffect { - private static final Token token = new MinionToken2(); + private static final Token token = new MinionToken(); InfernalGenesisEffect() { super(Outcome.PutCreatureInPlay); diff --git a/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java b/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java index c6b1fb11ff2..d8709770525 100644 --- a/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java +++ b/Mage.Sets/src/mage/cards/m/MarathWillOfTheWild.java @@ -15,6 +15,7 @@ import mage.abilities.dynamicvalue.common.ManaSpentToCastCount; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -27,11 +28,10 @@ import mage.constants.SuperType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.MarathWillOfTheWildElementalToken; -import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCreaturePermanent; +import mage.game.permanent.token.ElementalXXGreenToken; import java.util.UUID; @@ -110,12 +110,8 @@ class MarathWillOfTheWildCreateTokenEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - int amount = GetXValue.instance.calculate(game, source, this); - Token token = new MarathWillOfTheWildElementalToken(); - token.setPower(amount); - token.setToughness(amount); - token.putOntoBattlefield(1, game, source, source.getControllerId()); - return true; + int xvalue = GetXValue.instance.calculate(game, source, this); + return new CreateTokenEffect(new ElementalXXGreenToken(xvalue)).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java b/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java index 95d06e5249f..011c55daf31 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java @@ -8,6 +8,7 @@ import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,7 +16,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.MinionToken; +import mage.game.permanent.token.PhyrexianMinionToken; import mage.players.Player; import mage.util.CardUtil; @@ -110,12 +111,8 @@ class PhyrexianProcessorCreateTokenEffect extends OneShotEffect { String key = CardUtil.getCardZoneString("lifePaid", source.getSourceId(), game, true); Object object = game.getState().getValue(key); if (object instanceof Integer) { - int lifePaid = (int) object; - MinionToken token = new MinionToken(); - token.setPower(lifePaid); - token.setToughness(lifePaid); - token.putOntoBattlefield(1, game, source, source.getControllerId()); - return true; + int xvalue = (int) object; + return new CreateTokenEffect(new PhyrexianMinionToken(xvalue)).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/r/RallyTheHorde.java b/Mage.Sets/src/mage/cards/r/RallyTheHorde.java index af7546bc266..e407da6cb8c 100644 --- a/Mage.Sets/src/mage/cards/r/RallyTheHorde.java +++ b/Mage.Sets/src/mage/cards/r/RallyTheHorde.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.token.RallyTheHordeWarriorToken; +import mage.game.permanent.token.RedWarriorToken; import mage.players.Player; /** @@ -72,7 +72,7 @@ class RallyTheHordeEffect extends OneShotEffect { nonLandCardsExiled += nonLands; } } - return new CreateTokenEffect(new RallyTheHordeWarriorToken(), nonLandCardsExiled).apply(game, source); + return new CreateTokenEffect(new RedWarriorToken(), nonLandCardsExiled).apply(game, source); } return false; diff --git a/Mage.Sets/src/mage/cards/s/SeedGuardian.java b/Mage.Sets/src/mage/cards/s/SeedGuardian.java index d1dec07dcb9..fb4935b78d2 100644 --- a/Mage.Sets/src/mage/cards/s/SeedGuardian.java +++ b/Mage.Sets/src/mage/cards/s/SeedGuardian.java @@ -14,7 +14,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.token.SeedGuardianToken; +import mage.game.permanent.token.ElementalXXGreenToken; import mage.players.Player; /** @@ -66,7 +66,7 @@ class SeedGuardianEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int creaturesInGraveyard = controller.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); - return new CreateTokenEffect(new SeedGuardianToken(creaturesInGraveyard)).apply(game, source); + return new CreateTokenEffect(new ElementalXXGreenToken(creaturesInGraveyard)).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java b/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java index ae295bea696..59e399ad294 100644 --- a/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java +++ b/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java @@ -17,7 +17,7 @@ import mage.constants.Duration; import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.game.command.emblems.SorinSolemnVisitorEmblem; -import mage.game.permanent.token.SorinSolemnVisitorVampireToken; +import mage.game.permanent.token.VampireToken; /** * @@ -42,7 +42,7 @@ public final class SorinSolemnVisitor extends CardImpl { this.addAbility(loyaltyAbility); // -2: Create a 2/2 black Vampire creature token with flying. - this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SorinSolemnVisitorVampireToken()), -2)); + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new VampireToken()), -2)); // -6: You get an emblem with "At the beginning of each opponent's upkeep, that player sacrifices a creature." this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new SorinSolemnVisitorEmblem()), -6)); diff --git a/Mage.Sets/src/mage/cards/s/SpoilsOfBlood.java b/Mage.Sets/src/mage/cards/s/SpoilsOfBlood.java index 4964fb93945..1a091662826 100644 --- a/Mage.Sets/src/mage/cards/s/SpoilsOfBlood.java +++ b/Mage.Sets/src/mage/cards/s/SpoilsOfBlood.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; -import mage.game.permanent.token.SpoilsOfBloodHorrorToken; +import mage.game.permanent.token.HorrorXXBlackToken; import mage.players.Player; /** @@ -54,7 +54,7 @@ class SpoilsOfBloodEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { new CreateTokenEffect( - new SpoilsOfBloodHorrorToken(CreaturesDiedThisTurnCount.instance.calculate(game, source, this))) + new HorrorXXBlackToken(CreaturesDiedThisTurnCount.instance.calculate(game, source, this))) .apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TumbleweedRising.java b/Mage.Sets/src/mage/cards/t/TumbleweedRising.java index a553fc1d14b..fa0239749b6 100644 --- a/Mage.Sets/src/mage/cards/t/TumbleweedRising.java +++ b/Mage.Sets/src/mage/cards/t/TumbleweedRising.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; -import mage.game.permanent.token.SeedGuardianToken; +import mage.game.permanent.token.ElementalXXGreenToken; import java.util.UUID; @@ -61,6 +61,6 @@ class TumbleweedRisingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int xvalue = GreatestPowerAmongControlledCreaturesValue.instance.calculate(game, source, this); - return new CreateTokenEffect(new SeedGuardianToken(xvalue)).apply(game, source); + return new CreateTokenEffect(new ElementalXXGreenToken(xvalue)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/w/WalkerOfTheGrove.java b/Mage.Sets/src/mage/cards/w/WalkerOfTheGrove.java index 98f5d3eba23..22005807dbf 100644 --- a/Mage.Sets/src/mage/cards/w/WalkerOfTheGrove.java +++ b/Mage.Sets/src/mage/cards/w/WalkerOfTheGrove.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.WalkerOfTheGroveToken; +import mage.game.permanent.token.Elemental44GreenToken; /** * @@ -26,7 +26,7 @@ public final class WalkerOfTheGrove extends CardImpl { this.toughness = new MageInt(7); // When Walker of the Grove leaves the battlefield, create a 4/4 green Elemental creature token. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new WalkerOfTheGroveToken(), 1), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new Elemental44GreenToken(), 1), false)); // Evoke {4}{G} this.addAbility(new EvokeAbility("{4}{G}")); } diff --git a/Mage/src/main/java/mage/game/permanent/token/CrushOfTentaclesToken.java b/Mage/src/main/java/mage/game/permanent/token/CrushOfTentaclesToken.java deleted file mode 100644 index 831465f93fc..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/CrushOfTentaclesToken.java +++ /dev/null @@ -1,29 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author spjspj - */ -public final class CrushOfTentaclesToken extends TokenImpl { - - public CrushOfTentaclesToken() { - super("Octopus Token", "8/8 blue Octopus creature"); - this.cardType.add(CardType.CREATURE); - this.color.setBlue(true); - this.subtype.add(SubType.OCTOPUS); - this.power = new MageInt(8); - this.toughness = new MageInt(8); - } - - private CrushOfTentaclesToken(final CrushOfTentaclesToken token) { - super(token); - } - - public CrushOfTentaclesToken copy() { - return new CrushOfTentaclesToken(this); - } - -} diff --git a/Mage/src/main/java/mage/game/permanent/token/DinDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/DinDragonToken.java index c833b1d4418..87cac3c1118 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DinDragonToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DinDragonToken.java @@ -11,7 +11,7 @@ import mage.constants.SubType; public final class DinDragonToken extends TokenImpl { public DinDragonToken() { - super("Dragon Token", "4/4 red Dinosaur Dragon creature token with flying"); + super("Dinosaur Dragon Token", "4/4 red Dinosaur Dragon creature token with flying"); cardType.add(CardType.CREATURE); color.setRed(true); subtype.add(SubType.DINOSAUR); diff --git a/Mage/src/main/java/mage/game/permanent/token/EyesOfTheWisentElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/Elemental44GreenToken.java similarity index 59% rename from Mage/src/main/java/mage/game/permanent/token/EyesOfTheWisentElementalToken.java rename to Mage/src/main/java/mage/game/permanent/token/Elemental44GreenToken.java index 15f41debc84..2d6ed9040f5 100644 --- a/Mage/src/main/java/mage/game/permanent/token/EyesOfTheWisentElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/Elemental44GreenToken.java @@ -7,9 +7,9 @@ import mage.constants.SubType; /** * @author spjspj */ -public final class EyesOfTheWisentElementalToken extends TokenImpl { +public final class Elemental44GreenToken extends TokenImpl { - public EyesOfTheWisentElementalToken() { + public Elemental44GreenToken() { super("Elemental Token", "4/4 green Elemental creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -18,11 +18,11 @@ public final class EyesOfTheWisentElementalToken extends TokenImpl { toughness = new MageInt(4); } - private EyesOfTheWisentElementalToken(final EyesOfTheWisentElementalToken token) { + private Elemental44GreenToken(final Elemental44GreenToken token) { super(token); } - public EyesOfTheWisentElementalToken copy() { - return new EyesOfTheWisentElementalToken(this); + public Elemental44GreenToken copy() { + return new Elemental44GreenToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java b/Mage/src/main/java/mage/game/permanent/token/ElementalXXGreenToken.java similarity index 60% rename from Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java rename to Mage/src/main/java/mage/game/permanent/token/ElementalXXGreenToken.java index f0fa5a71677..3e1cfa21e86 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ElementalXXGreenToken.java @@ -7,13 +7,13 @@ import mage.constants.SubType; /** * @author spjspj */ -public final class SeedGuardianToken extends TokenImpl { +public final class ElementalXXGreenToken extends TokenImpl { - public SeedGuardianToken() { + public ElementalXXGreenToken() { this(1); } - public SeedGuardianToken(int xValue) { + public ElementalXXGreenToken(int xValue) { super("Elemental Token", "X/X green Elemental creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -22,11 +22,11 @@ public final class SeedGuardianToken extends TokenImpl { toughness = new MageInt(xValue); } - private SeedGuardianToken(final SeedGuardianToken token) { + private ElementalXXGreenToken(final ElementalXXGreenToken token) { super(token); } - public SeedGuardianToken copy() { - return new SeedGuardianToken(this); + public ElementalXXGreenToken copy() { + return new ElementalXXGreenToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/GrovetenderDruidsPlantToken.java b/Mage/src/main/java/mage/game/permanent/token/GrovetenderDruidsPlantToken.java deleted file mode 100644 index cc98203651e..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/GrovetenderDruidsPlantToken.java +++ /dev/null @@ -1,28 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author spjspj - */ -public final class GrovetenderDruidsPlantToken extends TokenImpl { - - public GrovetenderDruidsPlantToken() { - super("Plant Token", "1/1 green Plant creature token"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add(SubType.PLANT); - power = new MageInt(1); - toughness = new MageInt(1); - } - - private GrovetenderDruidsPlantToken(final GrovetenderDruidsPlantToken token) { - super(token); - } - - public GrovetenderDruidsPlantToken copy() { - return new GrovetenderDruidsPlantToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/FleshCarverHorrorToken.java b/Mage/src/main/java/mage/game/permanent/token/HorrorXXBlackToken.java similarity index 59% rename from Mage/src/main/java/mage/game/permanent/token/FleshCarverHorrorToken.java rename to Mage/src/main/java/mage/game/permanent/token/HorrorXXBlackToken.java index a91f64a5a8f..478a123e926 100644 --- a/Mage/src/main/java/mage/game/permanent/token/FleshCarverHorrorToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HorrorXXBlackToken.java @@ -7,13 +7,13 @@ import mage.constants.SubType; /** * @author spjspj */ -public final class FleshCarverHorrorToken extends TokenImpl { +public final class HorrorXXBlackToken extends TokenImpl { - public FleshCarverHorrorToken() { + public HorrorXXBlackToken() { this(1); } - public FleshCarverHorrorToken(int xValue) { + public HorrorXXBlackToken(int xValue) { super("Horror Token", "X/X black Horror creature token"); cardType.add(CardType.CREATURE); color.setBlack(true); @@ -22,11 +22,11 @@ public final class FleshCarverHorrorToken extends TokenImpl { toughness = new MageInt(xValue); } - private FleshCarverHorrorToken(final FleshCarverHorrorToken token) { + private HorrorXXBlackToken(final HorrorXXBlackToken token) { super(token); } - public FleshCarverHorrorToken copy() { - return new FleshCarverHorrorToken(this); + public HorrorXXBlackToken copy() { + return new HorrorXXBlackToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanRogueToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanRogueToken.java index a28573e5d3c..05e3708d76e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HumanRogueToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HumanRogueToken.java @@ -10,7 +10,7 @@ import mage.constants.SubType; public final class HumanRogueToken extends TokenImpl { public HumanRogueToken() { - super("Human Token", "1/1 white Human Rogue creature token"); + super("Human Rogue Token", "1/1 white Human Rogue creature token"); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.HUMAN); diff --git a/Mage/src/main/java/mage/game/permanent/token/MarathWillOfTheWildElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/MarathWillOfTheWildElementalToken.java deleted file mode 100644 index ee2ed8f36b1..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/MarathWillOfTheWildElementalToken.java +++ /dev/null @@ -1,30 +0,0 @@ - - -package mage.game.permanent.token; - -import mage.constants.CardType; -import mage.constants.SubType; -import mage.MageInt; - -/** - * @author spjspj - */ -public final class MarathWillOfTheWildElementalToken extends TokenImpl { - - public MarathWillOfTheWildElementalToken() { - super("Elemental Token", "X/X green Elemental creature token"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.ELEMENTAL); - color.setGreen(true); - power = new MageInt(0); - toughness = new MageInt(0); - } - - private MarathWillOfTheWildElementalToken(final MarathWillOfTheWildElementalToken token) { - super(token); - } - - public MarathWillOfTheWildElementalToken copy() { - return new MarathWillOfTheWildElementalToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/MinionToken.java b/Mage/src/main/java/mage/game/permanent/token/MinionToken.java index 5b3862b9562..7d1c5f5b5ce 100644 --- a/Mage/src/main/java/mage/game/permanent/token/MinionToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/MinionToken.java @@ -5,25 +5,20 @@ import mage.constants.CardType; import mage.constants.SubType; /** - * @author FenrisulfrX + * @author Quercitron */ public final class MinionToken extends TokenImpl { public MinionToken() { - this("DDE"); - } - - public MinionToken(String setCode) { - super("Phyrexian Minion Token", "X/X black Phyrexian Minion creature token"); + super("Minion Token", "1/1 black Minion creature token"); cardType.add(CardType.CREATURE); - subtype.add(SubType.PHYREXIAN); subtype.add(SubType.MINION); color.setBlack(true); - power = new MageInt(0); - toughness = new MageInt(0); + power = new MageInt(1); + toughness = new MageInt(1); } - private MinionToken(final MinionToken token) { + protected MinionToken(final MinionToken token) { super(token); } diff --git a/Mage/src/main/java/mage/game/permanent/token/MinionToken2.java b/Mage/src/main/java/mage/game/permanent/token/MinionToken2.java deleted file mode 100644 index b35acab5e35..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/MinionToken2.java +++ /dev/null @@ -1,28 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author Quercitron - */ -public final class MinionToken2 extends TokenImpl { - - public MinionToken2() { - super("Minion Token", "1/1 black Minion creature token"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.MINION); - color.setBlack(true); - power = new MageInt(1); - toughness = new MageInt(1); - } - - protected MinionToken2(final MinionToken2 token) { - super(token); - } - - public MinionToken2 copy() { - return new MinionToken2(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/NighteyesTheDesecratorToken.java b/Mage/src/main/java/mage/game/permanent/token/NighteyesTheDesecratorToken.java deleted file mode 100644 index dc11eaf6383..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/NighteyesTheDesecratorToken.java +++ /dev/null @@ -1,44 +0,0 @@ - - -package mage.game.permanent.token; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.target.common.TargetCardInGraveyard; - -/** - * @author spjspj - */ -public final class NighteyesTheDesecratorToken extends TokenImpl { - - public NighteyesTheDesecratorToken() { - super("Nighteyes the Desecrator Token", ""); - this.supertype.add(SuperType.LEGENDARY); - cardType.add(CardType.CREATURE); - color.setBlack(true); - subtype.add(SubType.RAT); - subtype.add(SubType.WIZARD); - power = new MageInt(4); - toughness = new MageInt(2); - // {4}{B}: Put target creature card from a graveyard onto the battlefield under your control. - Ability ability = new SimpleActivatedAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl<>("{4}{B}")); - ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD)); - this.addAbility(ability); - } - - private NighteyesTheDesecratorToken(final NighteyesTheDesecratorToken token) { - super(token); - } - - public NighteyesTheDesecratorToken copy() { - return new NighteyesTheDesecratorToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/PhyrexianMinionToken.java b/Mage/src/main/java/mage/game/permanent/token/PhyrexianMinionToken.java new file mode 100644 index 00000000000..488d2dd80f8 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/PhyrexianMinionToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author FenrisulfrX + */ +public final class PhyrexianMinionToken extends TokenImpl { + + public PhyrexianMinionToken() { + this(1); + } + + public PhyrexianMinionToken(int xValue) { + super("Phyrexian Minion Token", "X/X black Phyrexian Minion creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.PHYREXIAN); + subtype.add(SubType.MINION); + color.setBlack(true); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + } + + private PhyrexianMinionToken(final PhyrexianMinionToken token) { + super(token); + } + + public PhyrexianMinionToken copy() { + return new PhyrexianMinionToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GrismoldPlantToken.java b/Mage/src/main/java/mage/game/permanent/token/Plant11Token.java similarity index 62% rename from Mage/src/main/java/mage/game/permanent/token/GrismoldPlantToken.java rename to Mage/src/main/java/mage/game/permanent/token/Plant11Token.java index 67f3f33d09d..6967daf5c7b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GrismoldPlantToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/Plant11Token.java @@ -4,9 +4,12 @@ import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -public final class GrismoldPlantToken extends TokenImpl { +/** + * @author spjspj + */ +public final class Plant11Token extends TokenImpl { - public GrismoldPlantToken() { + public Plant11Token() { super("Plant Token", "1/1 green Plant creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -15,11 +18,11 @@ public final class GrismoldPlantToken extends TokenImpl { toughness = new MageInt(1); } - private GrismoldPlantToken(final GrismoldPlantToken token) { + private Plant11Token(final Plant11Token token) { super(token); } - public GrismoldPlantToken copy() { - return new GrismoldPlantToken(this); + public Plant11Token copy() { + return new Plant11Token(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/RallyTheHordeWarriorToken.java b/Mage/src/main/java/mage/game/permanent/token/RallyTheHordeWarriorToken.java deleted file mode 100644 index 6a1d3310496..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/RallyTheHordeWarriorToken.java +++ /dev/null @@ -1,29 +0,0 @@ - -package mage.game.permanent.token; - -import mage.constants.CardType; -import mage.constants.SubType; -import mage.MageInt; - -/** - * @author spjspj - */ -public final class RallyTheHordeWarriorToken extends TokenImpl { - - public RallyTheHordeWarriorToken() { - super("Warrior Token", "1/1 red Warrior creature token"); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add(SubType.WARRIOR); - power = new MageInt(1); - toughness = new MageInt(1); - } - - private RallyTheHordeWarriorToken(final RallyTheHordeWarriorToken token) { - super(token); - } - - public RallyTheHordeWarriorToken copy() { - return new RallyTheHordeWarriorToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/SorinSolemnVisitorVampireToken.java b/Mage/src/main/java/mage/game/permanent/token/SorinSolemnVisitorVampireToken.java deleted file mode 100644 index 8ad1c68fe31..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/SorinSolemnVisitorVampireToken.java +++ /dev/null @@ -1,30 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.abilities.keyword.FlyingAbility; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author spjspj - */ -public final class SorinSolemnVisitorVampireToken extends TokenImpl { - - public SorinSolemnVisitorVampireToken() { - super("Vampire Token", "2/2 black Vampire creature token with flying"); - cardType.add(CardType.CREATURE); - color.setBlack(true); - subtype.add(SubType.VAMPIRE); - power = new MageInt(2); - toughness = new MageInt(2); - addAbility(FlyingAbility.getInstance()); - } - - private SorinSolemnVisitorVampireToken(final SorinSolemnVisitorVampireToken token) { - super(token); - } - - public SorinSolemnVisitorVampireToken copy() { - return new SorinSolemnVisitorVampireToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/SpoilsOfBloodHorrorToken.java b/Mage/src/main/java/mage/game/permanent/token/SpoilsOfBloodHorrorToken.java deleted file mode 100644 index db8966a8181..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/SpoilsOfBloodHorrorToken.java +++ /dev/null @@ -1,32 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author spjspj - */ -public final class SpoilsOfBloodHorrorToken extends TokenImpl { - - public SpoilsOfBloodHorrorToken() { - this(1); - } - - public SpoilsOfBloodHorrorToken(int xValue) { - super("Horror Token", "X/X black Horror creature token"); - cardType.add(CardType.CREATURE); - color.setBlack(true); - subtype.add(SubType.HORROR); - power = new MageInt(xValue); - toughness = new MageInt(xValue); - } - - private SpoilsOfBloodHorrorToken(final SpoilsOfBloodHorrorToken token) { - super(token); - } - - public SpoilsOfBloodHorrorToken copy() { - return new SpoilsOfBloodHorrorToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java b/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java deleted file mode 100644 index e660b616776..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java +++ /dev/null @@ -1,29 +0,0 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * @author spjspj - */ - -public final class WalkerOfTheGroveToken extends TokenImpl { - - public WalkerOfTheGroveToken() { - super("Elemental Token", "4/4 green Elemental creature token"); - cardType.add(CardType.CREATURE); - this.subtype.add(SubType.ELEMENTAL); - this.color.setGreen(true); - power = new MageInt(4); - toughness = new MageInt(4); - } - - private WalkerOfTheGroveToken(final WalkerOfTheGroveToken token) { - super(token); - } - - public WalkerOfTheGroveToken copy() { - return new WalkerOfTheGroveToken(this); - } -} diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index 39c9c643fd3..a4275e34e4c 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -242,7 +242,7 @@ |Generate|TOK:ARN|Djinn|||DjinnToken| |Generate|TOK:AVR|Angel|||AngelToken| |Generate|TOK:AVR|Demon|||DemonToken| -|Generate|TOK:AVR|Human|1||RedHumanToken| +|Generate|TOK:AVR|Human|1||ThatcherHumanToken| |Generate|TOK:AVR|Human|2||HumanToken| |Generate|TOK:AVR|Spirit|1||SpiritBlueToken| |Generate|TOK:AVR|Spirit|2||SpiritWhiteToken| @@ -257,7 +257,7 @@ |Generate|TOK:BFZ|Knight Ally|||KnightAllyToken| |Generate|TOK:BFZ|Kor Ally|||KorAllyToken| |Generate|TOK:BFZ|Octopus|||OctopusToken| -|Generate|TOK:BFZ|Plant|||GrovetenderDruidsPlantToken| +|Generate|TOK:BFZ|Plant|||Plant11Token| |Generate|TOK:BNG|Bird|1||EnchantmentBirdToken| |Generate|TOK:BNG|Bird|2||BirdToken| |Generate|TOK:BNG|Cat Soldier|||CatSoldierCreatureToken| @@ -284,7 +284,7 @@ |Generate|TOK:C14|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:C14|Goat|||GoatToken| |Generate|TOK:C14|Goblin|||GoblinToken| -|Generate|TOK:C14|Horror|||SpoilsOfBloodHorrorToken| +|Generate|TOK:C14|Horror|||HorrorXXBlackToken| |Generate|TOK:C14|Kor Soldier|||KorSoldierToken| |Generate|TOK:C14|Kraken|||Kraken99Token| |Generate|TOK:C14|Myr|||MyrToken| @@ -395,7 +395,7 @@ |Generate|TOK:C19|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:C19|Human|||HumanToken| |Generate|TOK:C19|Pegasus|||PegasusToken| -|Generate|TOK:C19|Plant|||GrismoldPlantToken| +|Generate|TOK:C19|Plant|||Plant11Token| |Generate|TOK:C19|Rhino|||RhinoToken| |Generate|TOK:C19|Saproling|||SaprolingToken| |Generate|TOK:C19|Sculpture|||DoomedArtisanToken| @@ -457,7 +457,7 @@ |Generate|TOK:DDD|Beast|2||BeastToken2| |Generate|TOK:DDD|Elephant|||ElephantToken| |Generate|TOK:DDE|Hornet|||HornetToken| -|Generate|TOK:DDE|Minion|||MinionToken| +|Generate|TOK:DDE|Phyrexian Minion|||PhyrexianMinionToken| |Generate|TOK:DDE|Saproling|||SaprolingToken| |Generate|TOK:DDF|Soldier|||SoldierToken| |Generate|TOK:DDG|Goblin|||GoblinToken| @@ -612,7 +612,7 @@ # LGN don't have tokens, from wiki: A Sliver token for Brood Sliver and a Goblin token for Warbreak Trumpeter were featured as a Magic Player Reward. |Generate|TOK:LRW|Avatar|||AvatarToken| |Generate|TOK:LRW|Beast|||BeastToken| -|Generate|TOK:LRW|Elemental|1||WalkerOfTheGroveToken| +|Generate|TOK:LRW|Elemental|1||Elemental44GreenToken| |Generate|TOK:LRW|Elemental|2||WhiteElementalToken| |Generate|TOK:LRW|Elemental Shaman|||ElementalShamanToken| |Generate|TOK:LRW|Elf Warrior|||ElfWarriorToken| @@ -724,7 +724,7 @@ |Generate|TOK:MM3|Zombie|||ZombieToken| |Generate|TOK:MMA|Bat|||BatToken| |Generate|TOK:MMA|Dragon|||DragonToken| -|Generate|TOK:MMA|Elemental|||WalkerOfTheGroveToken| +|Generate|TOK:MMA|Elemental|||Elemental44GreenToken| |Generate|TOK:MMA|Faerie Rogue|||OonaQueenFaerieRogueToken| |Generate|TOK:MMA|Giant Warrior|||GiantWarriorToken| |Generate|TOK:MMA|Goblin Rogue|||GoblinRogueToken| @@ -757,7 +757,7 @@ |Generate|TOK:OGW|Eldrazi Scion|4||EldraziScionToken| |Generate|TOK:OGW|Eldrazi Scion|5||EldraziScionToken| |Generate|TOK:OGW|Eldrazi Scion|6||EldraziScionToken| -|Generate|TOK:OGW|Elemental|1||SeedGuardianToken| +|Generate|TOK:OGW|Elemental|1||ElementalXXGreenToken| |Generate|TOK:OGW|Elemental|2||ElementalTokenWithHaste| |Generate|TOK:OGW|Plant|||PlantToken| |Generate|TOK:OGW|Zombie|||ZombieToken| @@ -1073,7 +1073,7 @@ # OonaQueenFaerieRogueToken is FaerieRogueToken with additional blue color, but ZNC contains only one token - so don't use normal token for it #|Generate|TOK:ZNC|Faerie Rogue|||OonaQueenFaerieRogueToken| # Germ token uses in chest and antology, but scryfall put it here -#|Generate|TOK:ZNC|Phyrexian Germ|||PhyrexianGermToken| +|Generate|TOK:ZNC|Phyrexian Germ|||PhyrexianGermToken| # |Generate|TOK:ZNC|Goblin Rogue|||GoblinRogueToken| |Generate|TOK:ZNC|Kor Ally|||KorAllyToken| @@ -1308,7 +1308,7 @@ # UMA |Generate|TOK:UMA|Citizen|||CitizenToken| |Generate|TOK:UMA|Drake|||DrakeToken| -|Generate|TOK:UMA|Elemental|1||WalkerOfTheGroveToken| +|Generate|TOK:UMA|Elemental|1||Elemental44GreenToken| |Generate|TOK:UMA|Elemental|2||RedElementalToken| |Generate|TOK:UMA|Elemental|3||RedElementalToken| |Generate|TOK:UMA|Faerie Rogue|||FaerieRogueToken| @@ -1536,7 +1536,7 @@ |Generate|TOK:DDR|Eldrazi Scion|||EldraziScionToken| |Generate|TOK:DDR|Demon|||DemonToken| |Generate|TOK:DDR|Zombie Giant|||QuestForTheGravelordZombieToken| -|Generate|TOK:DDR|Elemental|||WalkerOfTheGroveToken| +|Generate|TOK:DDR|Elemental|||Elemental44GreenToken| |Generate|TOK:DDR|Plant|||PlantToken| # DDS @@ -2176,7 +2176,7 @@ |Generate|TOK:OTJ|Bird|||BlueBirdToken| |Generate|TOK:OTJ|Clue|||ClueArtifactToken| |Generate|TOK:OTJ|Dinosaur|||Dinosaur31Token| -|Generate|TOK:OTJ|Elemental|||SeedGuardianToken| +|Generate|TOK:OTJ|Elemental|||ElementalXXGreenToken| |Generate|TOK:OTJ|Elk|||ElkToken| |Generate|TOK:OTJ|Mercenary|||MercenaryToken| |Generate|TOK:OTJ|Meteorite|||MeteoriteToken| @@ -2460,7 +2460,7 @@ |Generate|TOK:DFT|Elephant|||ElephantToken| |Generate|TOK:DFT|Goblin|||GoblinToken| |Generate|TOK:DFT|Insect|||InsectToken| -|Generate|TOK:DFT|Pilot|||PilotCrewToken| +|Generate|TOK:DFT|Pilot|||PilotSaddleCrewToken| |Generate|TOK:DFT|Servo|||ServoToken| |Generate|TOK:DFT|Thopter|1||ThopterColorlessToken| |Generate|TOK:DFT|Thopter|2||ThopterColorlessToken| From b6421e4b6cfb6c094374352f5e08ab7ea6ce0a67 Mon Sep 17 00:00:00 2001 From: androosss <101566943+androosss@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:13:25 +0200 Subject: [PATCH 71/71] images: added new tokens from promo and other sets, fixed miss images (#13492) * add missing tokens images * fix broken card images * remove tokens without image * remove copy tokens references (embalm, eternalize) --- .../card/dl/sources/GrabbagImageSource.java | 2 +- .../dl/sources/ScryfallImageSupportCards.java | 14 +- .../sources/ScryfallImageSupportTokens.java | 223 +++++++- .../game/ScryfallImagesDownloadTest.java | 4 +- .../src/mage/sets/AetherdriftCommander.java | 2 +- .../sets/MediaAndCollaborationPromos.java | 96 ++-- Mage/src/main/resources/tokens-database.txt | 525 ++++++++++++++---- 7 files changed, 703 insertions(+), 163 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java index 7db9fa2970e..ab56977114d 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java @@ -204,7 +204,7 @@ public enum GrabbagImageSource implements CardImageSource { singleLinks.put("SWS/Hazard Trooper", "ZOutamG.jpeg"); singleLinks.put("SWS/Head Hunting", "7OT1bGZ.jpeg"); singleLinks.put("SWS/Heavy Trooper", "HhZWs2N.jpeg"); - singleLinks.put("SWS/Hot Pursuit", "ih1GT5Z.jpeg"); + singleLinks.put("SWS/Hot Pursuit (Star Wars)", "ih1GT5Z.jpeg"); singleLinks.put("SWS/Hungry Dragonsnake", "23v7RTm.jpeg"); singleLinks.put("SWS/Hunt to Extinction", "3eJyfzZ.jpeg"); singleLinks.put("SWS/Hutt Crime Lord", "NAzK7Hp.jpeg"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index 1c64712ad5a..8ca97816777 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -439,7 +439,7 @@ public class ScryfallImageSupportCards { add("ELD"); // Throne of Eldraine //add("PTG"); // Ponies: The Galloping add("CMB1"); // Mystery Booster Playtest Cards 2019 - //add("MB1"); // Mystery Booster + add("MB1"); // Mystery Booster add("GN2"); // Game Night 2019 add("HA1"); // Historic Anthology 1 //add("HHO"); // Happy Holidays @@ -534,7 +534,7 @@ public class ScryfallImageSupportCards { add("ONE"); // Phyrexia: All Will Be One add("ONC"); // Phyrexia: All Will Be One Commander add("PL23"); // Year of the Rabbit 2023 - add("DA1"); // Unknown Event + add("UNK"); // Unknown Event add("SIS"); // Shadows of the Past add("SIR"); // Shadows over Innistrad Remastered add("SLP"); // Secret Lair Showdown @@ -683,8 +683,16 @@ public class ScryfallImageSupportCards { // CALC - custom alchemy version of cards. put("CALC/C-Pillar of the Paruns", "https://api.scryfall.com/cards/dis/176/"); + // MB1 + put("MB1/Goblin Trenches", "https://api.scryfall.com/cards/plst/EMA-203/"); + put("MB1/Prophetic Bolt", "https://api.scryfall.com/cards/plst/C15-231/"); + // LTR - 0 number for tokens only - put("LTR/The One Ring/001", "https://api.scryfall.com/cards/ltr/0/"); + // Scryfall has a bug, for some reason this link doesn't work with ?format=image even though it works with ?format=json + // and ?format=text. Base url fails because language is qya and not en and alternate url fails because of this bug + // TODO: This should be reverted when Scryfall fixes the bug + // put("LTR/The One Ring/001", "https://api.scryfall.com/cards/ltr/0/"); + put("LTR/The One Ring/001", "https://api.scryfall.com/cards/ltr/0/qya?format=image"); // REX - double faced lands (xmage uses two diff lands for it) put("REX/Command Tower/26b", "https://api.scryfall.com/cards/rex/26/en?format=image&face=back"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index 55602d438ad..9441b6aa677 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -39,7 +39,8 @@ public class ScryfallImageSupportTokens { putAll(TokenRepository.instance.prepareScryfallDownloadList()); // RIX - put("RIX/City's Blessing", "https://api.scryfall.com/cards/trix/6/en?format=image"); // TODO: missing from tokens data + // TODO: this should be readded when condition tokens are implemented + // put("RIX/City's Blessing", "https://api.scryfall.com/cards/trix/6/en?format=image"); put("RIX/Elemental/1", "https://api.scryfall.com/cards/trix/1/en?format=image"); put("RIX/Elemental/2", "https://api.scryfall.com/cards/trix/2/en?format=image"); put("RIX/Golem", "https://api.scryfall.com/cards/trix/4/en?format=image"); @@ -117,22 +118,6 @@ public class ScryfallImageSupportTokens { put("AKH/Warrior", "https://api.scryfall.com/cards/takh/17/en?format=image"); put("AKH/Wurm", "https://api.scryfall.com/cards/takh/24/en?format=image"); put("AKH/Zombie", "https://api.scryfall.com/cards/takh/20/en?format=image"); - // AKH - embalm ability (token from card) - put("AKH/Angel of Sanctions", "https://api.scryfall.com/cards/takh/1/en?format=image"); - put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image"); - put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image"); - put("AKH/Aven Wind Guide", "https://api.scryfall.com/cards/takh/4/en?format=image"); - put("AKH/Glyph Keeper", "https://api.scryfall.com/cards/takh/5/en?format=image"); - put("AKH/Heart-Piercer Manticore", "https://api.scryfall.com/cards/takh/6/en?format=image"); - put("AKH/Honored Hydra", "https://api.scryfall.com/cards/takh/7/en?format=image"); - put("AKH/Labyrinth Guardian", "https://api.scryfall.com/cards/takh/8/en?format=image"); - put("AKH/Oketra's Attendant", "https://api.scryfall.com/cards/takh/9/en?format=image"); - put("AKH/Sacred Cat", "https://api.scryfall.com/cards/takh/10/en?format=image"); - put("AKH/Tah-Crop Skirmisher", "https://api.scryfall.com/cards/takh/11/en?format=image"); - put("AKH/Temmet, Vizier of Naktamun", "https://api.scryfall.com/cards/takh/12/en?format=image"); - put("AKH/Trueheart Duelist", "https://api.scryfall.com/cards/takh/13/en?format=image"); - put("AKH/Unwavering Initiate", "https://api.scryfall.com/cards/takh/14/en?format=image"); - put("AKH/Vizier of Many Faces", "https://api.scryfall.com/cards/takh/15/en?format=image"); // AER put("AER/Etherium Cell", "https://api.scryfall.com/cards/taer/3/en?format=image"); @@ -501,7 +486,7 @@ public class ScryfallImageSupportTokens { put("ZNC/Elemental/1", "https://api.scryfall.com/cards/tznc/10/en?format=image"); // 5/5 put("ZNC/Elemental/2", "https://api.scryfall.com/cards/tznc/8/en?format=image"); // 2/2 put("ZNC/Faerie Rogue", "https://api.scryfall.com/cards/tznc/3/en?format=image"); - put("ZNC/Germ", "https://api.scryfall.com/cards/tznc/4/en?format=image"); // must be in chest or antology + put("ZNC/Phyrexian Germ", "https://api.scryfall.com/cards/tznc/4/en?format=image"); // must be in chest or antology put("ZNC/Goblin Rogue", "https://api.scryfall.com/cards/tznc/5/en?format=image"); put("ZNC/Kor Ally", "https://api.scryfall.com/cards/tznc/2/en?format=image"); put("ZNC/Rat", "https://api.scryfall.com/cards/tznc/6/en?format=image"); @@ -596,7 +581,6 @@ public class ScryfallImageSupportTokens { put("C21/Beast/1", "https://api.scryfall.com/cards/tc21/10/en?format=image"); // 3/3 put("C21/Beast/2", "https://api.scryfall.com/cards/tc21/11/en?format=image"); // 4/4 put("C21/Boar", "https://api.scryfall.com/cards/tc21/12/en?format=image"); - put("C21/Champion of Wits", "https://api.scryfall.com/cards/tc21/6/en?format=image"); put("C21/Construct/1", "https://api.scryfall.com/cards/tc21/22/en?format=image"); // x/x put("C21/Construct/2", "https://api.scryfall.com/cards/tc21/23/en?format=image"); // 0/0 put("C21/Demon", "https://api.scryfall.com/cards/tc21/7/en?format=image"); @@ -829,17 +813,45 @@ public class ScryfallImageSupportTokens { put("NEC/Thopter", "https://api.scryfall.com/cards/tnec/12/en?format=image"); // SLD + put("SLD/Angel", "https://api.scryfall.com/cards/sld/1340?format=image"); + put("SLD/Cat/1", "https://api.scryfall.com/cards/sld/1517?format=image"); + put("SLD/Cat/2", "https://api.scryfall.com/cards/sld/27?format=image"); + put("SLD/Cat/3", "https://api.scryfall.com/cards/sld/28?format=image"); put("SLD/Clue", "https://api.scryfall.com/cards/sld/348/en?format=image"); + put("SLD/Dog", "https://api.scryfall.com/cards/sld/1516?format=image"); + put("SLD/Egg", "https://api.scryfall.com/cards/sld/1398?format=image"); put("SLD/Faerie Rogue/1", "https://api.scryfall.com/cards/sld/13/en?format=image"); put("SLD/Faerie Rogue/2", "https://api.scryfall.com/cards/sld/14/en?format=image"); put("SLD/Faerie Rogue/3", "https://api.scryfall.com/cards/sld/15/en?format=image"); put("SLD/Faerie Rogue/4", "https://api.scryfall.com/cards/sld/16/en?format=image"); - put("SLD/Treasure", "https://api.scryfall.com/cards/sld/153/en?format=image"); + put("SLD/Food/1", "https://api.scryfall.com/cards/sld/1938?format=image"); + put("SLD/Food/2", "https://api.scryfall.com/cards/sld/2010?format=image"); + put("SLD/Food/3", "https://api.scryfall.com/cards/sld/2011?format=image"); + put("SLD/Food/4", "https://api.scryfall.com/cards/sld/2012?format=image"); + put("SLD/Food/5", "https://api.scryfall.com/cards/sld/2013?format=image"); + put("SLD/Goblin", "https://api.scryfall.com/cards/sld/219?format=image"); + put("SLD/Hydra", "https://api.scryfall.com/cards/sld/1334?format=image"); + put("SLD/Icingdeath, Frost Tongue", "https://api.scryfall.com/cards/sld/1018?format=image"); + put("SLD/Marit Lage", "https://api.scryfall.com/cards/sld/1681?format=image"); + put("SLD/Mechtitan", "https://api.scryfall.com/cards/sld/1969?format=image"); + put("SLD/Saproling", "https://api.scryfall.com/cards/sld/1139?format=image"); + put("SLD/Shrine", "https://api.scryfall.com/cards/sld/1835?format=image"); + put("SLD/Spirit/1", "https://api.scryfall.com/cards/sld/1341?format=image"); + put("SLD/Spirit/2", "https://api.scryfall.com/cards/sld/1852?format=image"); + put("SLD/Squirrel", "https://api.scryfall.com/cards/sld/200?format=image"); + put("SLD/Treasure/1", "https://api.scryfall.com/cards/sld/1432/en?format=image"); + put("SLD/Treasure/2", "https://api.scryfall.com/cards/sld/1736/en?format=image"); + put("SLD/Treasure/3", "https://api.scryfall.com/cards/sld/1507/en?format=image"); + put("SLD/Treasure/4", "https://api.scryfall.com/cards/sld/153/en?format=image"); put("SLD/Walker/1", "https://api.scryfall.com/cards/sld/148/en?format=image"); put("SLD/Walker/2", "https://api.scryfall.com/cards/sld/149/en?format=image"); put("SLD/Walker/3", "https://api.scryfall.com/cards/sld/150/en?format=image"); put("SLD/Walker/4", "https://api.scryfall.com/cards/sld/151/en?format=image"); put("SLD/Walker/5", "https://api.scryfall.com/cards/sld/152/en?format=image"); + put("SLD/Warrior", "https://api.scryfall.com/cards/sld/1752?format=image"); + put("SLD/Wolf", "https://api.scryfall.com/cards/sld/1613?format=image"); + put("SLD/Wurm", "https://api.scryfall.com/cards/sld/1306?format=image"); + put("SLD/Zombie", "https://api.scryfall.com/cards/sld/1357?format=image"); // 2XM put("2XM/Angel", "https://api.scryfall.com/cards/t2xm/3/en?format=image"); @@ -1707,6 +1719,7 @@ public class ScryfallImageSupportTokens { put("CLB/Squid", "https://api.scryfall.com/cards/tclb/29/en?format=image"); put("CLB/Squirrel", "https://api.scryfall.com/cards/tclb/15/en?format=image"); put("CLB/Treasure", "https://api.scryfall.com/cards/tclb/17/en?format=image"); + put("CLB/Undercity", "https://api.scryfall.com/cards/tclb/20/en?format=image"); put("CLB/Volo's Journal", "https://api.scryfall.com/cards/tclb/18/en?format=image"); put("CLB/Warrior", "https://api.scryfall.com/cards/tclb/32/en?format=image"); put("CLB/Emblem Will Kenrith", "https://api.scryfall.com/cards/tclb/50/en?format=image"); @@ -2168,12 +2181,33 @@ public class ScryfallImageSupportTokens { put("WOC/Virtuous", "https://api.scryfall.com/cards/twoc/3/en?format=image"); // WHO + put("WHO/Alien", "https://api.scryfall.com/cards/twho/2?format=image"); put("WHO/Alien Insect", "https://api.scryfall.com/cards/twho/19/en?format=image"); - put("WHO/Human Noble", "https://api.scryfall.com/cards/twho/7/en?format=image"); + put("WHO/Alien Salamander", "https://api.scryfall.com/cards/twho/16?format=image"); + put("WHO/Alien Warrior", "https://api.scryfall.com/cards/twho/14?format=image"); + put("WHO/Beast", "https://api.scryfall.com/cards/twho/17?format=image"); + put("WHO/Clue/1", "https://api.scryfall.com/cards/twho/21?format=image"); + put("WHO/Clue/2", "https://api.scryfall.com/cards/twho/22?format=image"); + put("WHO/Clue/3", "https://api.scryfall.com/cards/twho/23?format=image"); + put("WHO/Dalek", "https://api.scryfall.com/cards/twho/12?format=image"); + put("WHO/Dinosaur", "https://api.scryfall.com/cards/twho/20?format=image"); + put("WHO/Fish", "https://api.scryfall.com/cards/twho/10?format=image"); + put("WHO/Food/1", "https://api.scryfall.com/cards/twho/25?format=image"); + put("WHO/Food/2", "https://api.scryfall.com/cards/twho/26?format=image"); + put("WHO/Food/3", "https://api.scryfall.com/cards/twho/27?format=image"); put("WHO/Horse", "https://api.scryfall.com/cards/twho/4/en?format=image"); + put("WHO/Human", "https://api.scryfall.com/cards/twho/5?format=image"); + put("WHO/Human Noble", "https://api.scryfall.com/cards/twho/7/en?format=image"); + put("WHO/Mark of the Rani", "https://api.scryfall.com/cards/twho/15?format=image"); + put("WHO/Soldier", "https://api.scryfall.com/cards/twho/8?format=image"); + put("WHO/Treasure/1", "https://api.scryfall.com/cards/twho/28?format=image"); + put("WHO/Treasure/2", "https://api.scryfall.com/cards/twho/29?format=image"); + put("WHO/Treasure/3", "https://api.scryfall.com/cards/twho/30?format=image"); + put("WHO/Treasure/4", "https://api.scryfall.com/cards/twho/31?format=image"); + put("WHO/Warrior", "https://api.scryfall.com/cards/twho/9?format=image"); // 8ED - put("8ED/Rukh", "https://api.scryfall.com/cards/p03/7/en?format=image"); + put("8ED/Bird", "https://api.scryfall.com/cards/p03/7/en?format=image"); // LCI put("LCI/Angel", "https://api.scryfall.com/cards/tlci/2/en?format=image"); @@ -2494,7 +2528,18 @@ public class ScryfallImageSupportTokens { put("BLC/Wolf/2", "https://api.scryfall.com/cards/tblc/32/en?format=image"); // DSK + put("DSK/Beast", "https://api.scryfall.com/cards/tdsk/3?format=image"); put("DSK/Emblem Kaito", "https://api.scryfall.com/cards/tdsk/17/en?format=image"); + put("DSK/Everywhere", "https://api.scryfall.com/cards/tdsk/16?format=image"); + put("DSK/Glimmer", "https://api.scryfall.com/cards/tdsk/4?format=image"); + put("DSK/Gremlin", "https://api.scryfall.com/cards/tdsk/11?format=image"); + put("DSK/Insect/1", "https://api.scryfall.com/cards/tdsk/13?format=image"); + put("DSK/Insect/2", "https://api.scryfall.com/cards/tdsk/5?format=image"); + put("DSK/Primo, the Indivisible", "https://api.scryfall.com/cards/tdsk/14?format=image"); + put("DSK/Shard", "https://api.scryfall.com/cards/tdsk/2?format=image"); + put("DSK/Spider", "https://api.scryfall.com/cards/tdsk/12?format=image"); + put("DSK/Spirit", "https://api.scryfall.com/cards/tdsk/8?format=image"); + put("DSK/Treasure", "https://api.scryfall.com/cards/tdsk/15?format=image"); // DSC put("DSC/Angel", "https://api.scryfall.com/cards/tdsc/2/en?format=image"); @@ -2633,18 +2678,25 @@ public class ScryfallImageSupportTokens { // TDC put("TDC/Angel", "https://api.scryfall.com/cards/ttdc/2/en?format=image"); + put("TDC/Beast", "https://api.scryfall.com/cards/ttdc/20?format=image"); put("TDC/Citizen", "https://api.scryfall.com/cards/ttdc/26/en?format=image"); put("TDC/Dog", "https://api.scryfall.com/cards/ttdc/3/en?format=image"); + put("TDC/Dragon/1", "https://api.scryfall.com/cards/ttdc/13?format=image"); + put("TDC/Dragon/2", "https://api.scryfall.com/cards/ttdc/14?format=image"); + put("TDC/Dragon Egg", "https://api.scryfall.com/cards/ttdc/12?format=image"); put("TDC/Dragon Illusion", "https://api.scryfall.com/cards/ttdc/15/en?format=image"); put("TDC/Eldrazi", "https://api.scryfall.com/cards/ttdc/1/en?format=image"); put("TDC/Elemental/1", "https://api.scryfall.com/cards/ttdc/16/en?format=image"); put("TDC/Elemental/2", "https://api.scryfall.com/cards/ttdc/17/en?format=image"); put("TDC/Elemental/3", "https://api.scryfall.com/cards/ttdc/27/en?format=image"); put("TDC/First Mate Ragavan", "https://api.scryfall.com/cards/ttdc/18/en?format=image"); + put("TDC/Frog Lizard", "https://api.scryfall.com/cards/ttdc/21?format=image"); put("TDC/Goat", "https://api.scryfall.com/cards/ttdc/4/en?format=image"); put("TDC/Gold", "https://api.scryfall.com/cards/ttdc/29/en?format=image"); put("TDC/Human", "https://api.scryfall.com/cards/ttdc/5/en?format=image"); + put("TDC/Inkling", "https://api.scryfall.com/cards/ttdc/28?format=image"); put("TDC/Insect", "https://api.scryfall.com/cards/ttdc/22/en?format=image"); + put("TDC/Karox Bladewing", "https://api.scryfall.com/cards/ttdc/19?format=image"); put("TDC/Myr", "https://api.scryfall.com/cards/ttdc/30/en?format=image"); put("TDC/Plant", "https://api.scryfall.com/cards/ttdc/24/en?format=image"); put("TDC/Rat", "https://api.scryfall.com/cards/ttdc/9/en?format=image"); @@ -2652,9 +2704,136 @@ public class ScryfallImageSupportTokens { put("TDC/Servo", "https://api.scryfall.com/cards/ttdc/31/en?format=image"); put("TDC/Snake", "https://api.scryfall.com/cards/ttdc/10/en?format=image"); put("TDC/Soldier", "https://api.scryfall.com/cards/ttdc/32/en?format=image"); + put("TDC/Spider", "https://api.scryfall.com/cards/ttdc/25?format=image"); put("TDC/Spirit", "https://api.scryfall.com/cards/ttdc/6/en?format=image"); put("TDC/Thopter", "https://api.scryfall.com/cards/ttdc/33/en?format=image"); + // ACR + put("ACR/Assassin", "https://api.scryfall.com/cards/tacr/4?format=image"); + put("ACR/Emblem Capitoline Triad", "https://api.scryfall.com/cards/tacr/7/en?format=image"); + put("ACR/Human Rogue", "https://api.scryfall.com/cards/tacr/3?format=image"); + put("ACR/Phobos", "https://api.scryfall.com/cards/tacr/5?format=image"); + put("ACR/Shapeshifter", "https://api.scryfall.com/cards/tacr/2?format=image"); + put("ACR/Treasure", "https://api.scryfall.com/cards/tacr/6?format=image"); + + // DD2 + put("DD2/Elemental Shaman", "https://api.scryfall.com/cards/tdd2/1?format=image"); + + // FIN + put("FIN/Food", "https://api.scryfall.com/cards/tfin/22?format=image"); + + // JVC + put("JVC/Elemental Shaman", "https://api.scryfall.com/cards/tjvc/4?format=image"); + + // PIP + put("PIP/Alien", "https://api.scryfall.com/cards/tpip/6?format=image"); + put("PIP/Clue", "https://api.scryfall.com/cards/tpip/11?format=image"); + put("PIP/Food/1", "https://api.scryfall.com/cards/tpip/12?format=image"); + put("PIP/Food/2", "https://api.scryfall.com/cards/tpip/13?format=image"); + put("PIP/Food/3", "https://api.scryfall.com/cards/tpip/14?format=image"); + put("PIP/Human Knight", "https://api.scryfall.com/cards/tpip/2?format=image"); + put("PIP/Human Soldier", "https://api.scryfall.com/cards/tpip/3?format=image"); + put("PIP/Junk", "https://api.scryfall.com/cards/tpip/15?format=image"); + put("PIP/Robot", "https://api.scryfall.com/cards/tpip/16?format=image"); + put("PIP/Settlement", "https://api.scryfall.com/cards/tpip/8?format=image"); + put("PIP/Soldier/1", "https://api.scryfall.com/cards/tpip/10?format=image"); + put("PIP/Soldier/2", "https://api.scryfall.com/cards/tpip/4?format=image"); + put("PIP/Squirrel", "https://api.scryfall.com/cards/tpip/9?format=image"); + put("PIP/Thopter", "https://api.scryfall.com/cards/tpip/17?format=image"); + put("PIP/Treasure/1", "https://api.scryfall.com/cards/tpip/18?format=image"); + put("PIP/Treasure/2", "https://api.scryfall.com/cards/tpip/19?format=image"); + put("PIP/Warrior", "https://api.scryfall.com/cards/tpip/5?format=image"); + put("PIP/Wasteland Survival Guide", "https://api.scryfall.com/cards/tpip/20?format=image"); + put("PIP/Zombie Mutant", "https://api.scryfall.com/cards/tpip/7?format=image"); + + // REX + put("REX/Dinosaur", "https://api.scryfall.com/cards/trex/1?format=image"); + put("REX/Treasure", "https://api.scryfall.com/cards/trex/2?format=image"); + + // UGL + put("UGL/Goblin", "https://api.scryfall.com/cards/tugl/4?format=image"); + put("UGL/Pegasus", "https://api.scryfall.com/cards/tugl/1?format=image"); + put("UGL/Soldier", "https://api.scryfall.com/cards/tugl/2?format=image"); + put("UGL/Squirrel", "https://api.scryfall.com/cards/tugl/6?format=image"); + put("UGL/Zombie", "https://api.scryfall.com/cards/tugl/3?format=image"); + + // UST + put("UST/Angel", "https://api.scryfall.com/cards/tust/1?format=image"); + put("UST/Beast", "https://api.scryfall.com/cards/tust/13?format=image"); + put("UST/Brainiac", "https://api.scryfall.com/cards/tust/10?format=image"); + put("UST/Clue", "https://api.scryfall.com/cards/tust/18?format=image"); + put("UST/Dragon", "https://api.scryfall.com/cards/tust/16?format=image"); + put("UST/Elemental/1", "https://api.scryfall.com/cards/tust/11?format=image"); + put("UST/Elemental/2", "https://api.scryfall.com/cards/tust/17?format=image"); + put("UST/Gnome", "https://api.scryfall.com/cards/tust/20?format=image"); + put("UST/Goat", "https://api.scryfall.com/cards/tust/2?format=image"); + put("UST/Goblin", "https://api.scryfall.com/cards/tust/12?format=image"); + put("UST/Saproling", "https://api.scryfall.com/cards/tust/14?format=image"); + put("UST/Spirit", "https://api.scryfall.com/cards/tust/3?format=image"); + put("UST/Squirrel", "https://api.scryfall.com/cards/tust/15?format=image"); + put("UST/Storm Crow", "https://api.scryfall.com/cards/tust/5?format=image"); + put("UST/Thopter", "https://api.scryfall.com/cards/tust/6?format=image"); + put("UST/Vampire", "https://api.scryfall.com/cards/tust/8?format=image"); + put("UST/Zombie", "https://api.scryfall.com/cards/tust/9?format=image"); + + // F12 + put("F12/Human", "https://api.scryfall.com/cards/f12/1a?format=image"); + put("F12/Wolf", "https://api.scryfall.com/cards/f12/1a?format=image&face=back"); + + // F17 + put("F17/Dinosaur", "https://api.scryfall.com/cards/f17/11?format=image"); + put("F17/Pirate", "https://api.scryfall.com/cards/f17/12?format=image"); + put("F17/Vampire", "https://api.scryfall.com/cards/f17/10?format=image"); + put("F17/Treasure/1", "https://api.scryfall.com/cards/f17/11?format=image&face=back"); + put("F17/Treasure/2", "https://api.scryfall.com/cards/f17/12?format=image&face=back"); + put("F17/Treasure/3", "https://api.scryfall.com/cards/f17/10?format=image&face=back"); + + // HHO + put("HHO/Treasure", "https://api.scryfall.com/cards/hho/21★?format=image"); + + // J12 + put("J12/Centaur", "https://api.scryfall.com/cards/j12/9?format=image"); + + // J13 + put("J13/Golem", "https://api.scryfall.com/cards/j13/9?format=image"); + + // MPR + put("MPR/Bear", "https://api.scryfall.com/cards/mpr/7?format=image"); + put("MPR/Beast", "https://api.scryfall.com/cards/mpr/8?format=image"); + put("MPR/Bird", "https://api.scryfall.com/cards/mpr/4?format=image"); + put("MPR/Elephant", "https://api.scryfall.com/cards/mpr/3?format=image"); + put("MPR/Goblin Soldier", "https://api.scryfall.com/cards/mpr/6?format=image"); + put("MPR/Saproling", "https://api.scryfall.com/cards/mpr/2?format=image"); + put("MPR/Spirit", "https://api.scryfall.com/cards/mpr/5?format=image"); + + // P03 + put("P03/Bear", "https://api.scryfall.com/cards/p03/4?format=image"); + put("P03/Demon", "https://api.scryfall.com/cards/p03/6?format=image"); + put("P03/Goblin", "https://api.scryfall.com/cards/p03/5?format=image"); + put("P03/Insect", "https://api.scryfall.com/cards/p03/2?format=image"); + put("P03/Bird", "https://api.scryfall.com/cards/p03/7?format=image"); + put("P03/Sliver", "https://api.scryfall.com/cards/p03/3?format=image"); + + // P04 + put("P04/Angel", "https://api.scryfall.com/cards/p04/2?format=image"); + put("P04/Beast", "https://api.scryfall.com/cards/p04/5?format=image"); + put("P04/Myr", "https://api.scryfall.com/cards/p04/4?format=image"); + put("P04/Pentavite", "https://api.scryfall.com/cards/p04/3?format=image"); + put("P04/Spirit", "https://api.scryfall.com/cards/p04/6?format=image"); + + // PEMN + put("PEMN/Zombie/1", "https://api.scryfall.com/cards/pemn/1Z?format=image"); + put("PEMN/Zombie/2", "https://api.scryfall.com/cards/pemn/1Z?format=image&face=back"); + + // PHEL + put("PHEL/Angel", "https://api.scryfall.com/cards/phel/1★?format=image"); + + // PL21 + put("PL21/Minotaur", "https://api.scryfall.com/cards/pl21/2★?format=image"); + + // PL23 + put("PL23/Food", "https://api.scryfall.com/cards/pl23/2?format=image"); + // generate supported sets supportedSets.clear(); for (String cardName : this.keySet()) { diff --git a/Mage.Client/src/test/java/mage/client/game/ScryfallImagesDownloadTest.java b/Mage.Client/src/test/java/mage/client/game/ScryfallImagesDownloadTest.java index 003db308954..25b1455fe36 100644 --- a/Mage.Client/src/test/java/mage/client/game/ScryfallImagesDownloadTest.java +++ b/Mage.Client/src/test/java/mage/client/game/ScryfallImagesDownloadTest.java @@ -43,7 +43,7 @@ public class ScryfallImagesDownloadTest { .anyMatch(c -> c.getCardNumber().equals("001")) ); urls = imageSource.generateCardUrl(new CardDownloadData("The One Ring", "LTR", "001", false, 0)); - Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/en?format=image", urls.getBaseUrl()); + Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/qya?format=image", urls.getBaseUrl()); // added same tests for small images @@ -74,6 +74,6 @@ public class ScryfallImagesDownloadTest { .anyMatch(c -> c.getCardNumber().equals("001")) ); urls = imageSourceSmall.generateCardUrl(new CardDownloadData("The One Ring", "LTR", "001", false, 0)); - Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/en?format=image&version=small", urls.getBaseUrl()); + Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/qya?format=image&version=small", urls.getBaseUrl()); } } diff --git a/Mage.Sets/src/mage/sets/AetherdriftCommander.java b/Mage.Sets/src/mage/sets/AetherdriftCommander.java index 5707ceedfc3..5cb696fed4c 100644 --- a/Mage.Sets/src/mage/sets/AetherdriftCommander.java +++ b/Mage.Sets/src/mage/sets/AetherdriftCommander.java @@ -118,7 +118,7 @@ public final class AetherdriftCommander extends ExpansionSet { cards.add(new SetCardInfo("Maskwood Nexus", 132, Rarity.RARE, mage.cards.m.MaskwoodNexus.class)); cards.add(new SetCardInfo("Midnight Clock", 79, Rarity.RARE, mage.cards.m.MidnightClock.class)); cards.add(new SetCardInfo("Midnight Reaper", 44, Rarity.RARE, mage.cards.m.MidnightReaper.class)); - cards.add(new SetCardInfo("Murderous Rider // Swift End", 45, Rarity.RARE, mage.cards.m.MurderousRider.class)); + cards.add(new SetCardInfo("Murderous Rider", 45, Rarity.RARE, mage.cards.m.MurderousRider.class)); cards.add(new SetCardInfo("Never // Return", 96, Rarity.RARE, mage.cards.n.NeverReturn.class)); cards.add(new SetCardInfo("Nissa, Worldsoul Speaker", 13, Rarity.RARE, mage.cards.n.NissaWorldsoulSpeaker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nissa, Worldsoul Speaker", 29, Rarity.RARE, mage.cards.n.NissaWorldsoulSpeaker.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/MediaAndCollaborationPromos.java b/Mage.Sets/src/mage/sets/MediaAndCollaborationPromos.java index 1454b11516d..e867776dd97 100644 --- a/Mage.Sets/src/mage/sets/MediaAndCollaborationPromos.java +++ b/Mage.Sets/src/mage/sets/MediaAndCollaborationPromos.java @@ -22,37 +22,69 @@ public class MediaAndCollaborationPromos extends ExpansionSet { // some cards are non-English (most are Japanese, Jamuraan Lion has a German printing), but it's ok - scryfall can download it - cards.add(new SetCardInfo("Archangel", 29, Rarity.RARE, mage.cards.a.Archangel.class)); - cards.add(new SetCardInfo("Ascendant Evincar", 28, Rarity.RARE, mage.cards.a.AscendantEvincar.class)); - cards.add(new SetCardInfo("Blue Elemental Blast", 5, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); - cards.add(new SetCardInfo("Cast Down", 30, Rarity.UNCOMMON, mage.cards.c.CastDown.class)); - cards.add(new SetCardInfo("Chandra's Outrage", 18, Rarity.COMMON, mage.cards.c.ChandrasOutrage.class)); - cards.add(new SetCardInfo("Chandra's Spitfire", 19, Rarity.UNCOMMON, mage.cards.c.ChandrasSpitfire.class)); - cards.add(new SetCardInfo("Cunning Sparkmage", 17, Rarity.UNCOMMON, mage.cards.c.CunningSparkmage.class)); - cards.add(new SetCardInfo("Darksteel Juggernaut", 16, Rarity.RARE, mage.cards.d.DarksteelJuggernaut.class)); - cards.add(new SetCardInfo("Daxos, Blessed by the Sun", 36, Rarity.UNCOMMON, mage.cards.d.DaxosBlessedByTheSun.class)); - cards.add(new SetCardInfo("Diabolic Edict", 31, Rarity.RARE, mage.cards.d.DiabolicEdict.class)); - cards.add(new SetCardInfo("Duress", 34, Rarity.RARE, mage.cards.d.Duress.class)); - cards.add(new SetCardInfo("Fireball", 4, Rarity.COMMON, mage.cards.f.Fireball.class)); - cards.add(new SetCardInfo("Jamuraan Lion", "10*", Rarity.COMMON, mage.cards.j.JamuraanLion.class)); - cards.add(new SetCardInfo("Kuldotha Phoenix", 20, Rarity.RARE, mage.cards.k.KuldothaPhoenix.class)); - cards.add(new SetCardInfo("Lava Coil", 33, Rarity.UNCOMMON, mage.cards.l.LavaCoil.class)); - cards.add(new SetCardInfo("Lightning Hounds", 10, Rarity.COMMON, mage.cards.l.LightningHounds.class)); - cards.add(new SetCardInfo("Parallax Dementia", 27, Rarity.COMMON, mage.cards.p.ParallaxDementia.class)); - cards.add(new SetCardInfo("Phantasmal Dragon", 21, Rarity.UNCOMMON, mage.cards.p.PhantasmalDragon.class)); - cards.add(new SetCardInfo("Phyrexian Rager", 14, Rarity.COMMON, mage.cards.p.PhyrexianRager.class)); - cards.add(new SetCardInfo("Sandbar Crocodile", 22, Rarity.COMMON, mage.cards.s.SandbarCrocodile.class)); - cards.add(new SetCardInfo("Scent of Cinder", 9, Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); - cards.add(new SetCardInfo("Shivan Dragon", 15, Rarity.RARE, mage.cards.s.ShivanDragon.class)); - cards.add(new SetCardInfo("Shock", 32, Rarity.RARE, mage.cards.s.Shock.class)); - cards.add(new SetCardInfo("Shrieking Drake", 24, Rarity.COMMON, mage.cards.s.ShriekingDrake.class)); - cards.add(new SetCardInfo("Silver Drake", 13, Rarity.COMMON, mage.cards.s.SilverDrake.class)); - cards.add(new SetCardInfo("Spined Wurm", 11, Rarity.COMMON, mage.cards.s.SpinedWurm.class)); - cards.add(new SetCardInfo("Staggering Insight", 37, Rarity.RARE, mage.cards.s.StaggeringInsight.class)); - cards.add(new SetCardInfo("Stream of Life", 25, Rarity.COMMON, mage.cards.s.StreamOfLife.class)); - cards.add(new SetCardInfo("Thorn Elemental", 26, Rarity.RARE, mage.cards.t.ThornElemental.class)); - cards.add(new SetCardInfo("Voltaic Key", 35, Rarity.RARE, mage.cards.v.VoltaicKey.class)); - cards.add(new SetCardInfo("Warmonger", 12, Rarity.UNCOMMON, mage.cards.w.Warmonger.class)); - cards.add(new SetCardInfo("Zhalfirin Knight", 23, Rarity.COMMON, mage.cards.z.ZhalfirinKnight.class)); + cards.add(new SetCardInfo("Ajani, Mentor of Heroes", "2024-3", Rarity.MYTHIC, mage.cards.a.AjaniMentorOfHeroes.class)); + cards.add(new SetCardInfo("Ancestral Mask", "2025-2", Rarity.RARE, mage.cards.a.AncestralMask.class)); + cards.add(new SetCardInfo("Archangel", "2000-4", Rarity.RARE, mage.cards.a.Archangel.class)); + cards.add(new SetCardInfo("Ascendant Evincar", "2000-7", Rarity.RARE, mage.cards.a.AscendantEvincar.class)); + cards.add(new SetCardInfo("Avalanche Riders", "2023-5", Rarity.RARE, mage.cards.a.AvalancheRiders.class)); + cards.add(new SetCardInfo("Blue Elemental Blast", "1995-2", Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); + cards.add(new SetCardInfo("Bone Shredder", "2021-2", Rarity.RARE, mage.cards.b.BoneShredder.class)); + cards.add(new SetCardInfo("Cast Down", "2019-1", Rarity.UNCOMMON, mage.cards.c.CastDown.class)); + cards.add(new SetCardInfo("Chandra's Outrage", "2010-3", Rarity.COMMON, mage.cards.c.ChandrasOutrage.class)); + cards.add(new SetCardInfo("Chandra's Spitfire", "2010-4", Rarity.UNCOMMON, mage.cards.c.ChandrasSpitfire.class)); + cards.add(new SetCardInfo("Counterspell", "2021-1", Rarity.RARE, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crop Rotation", "2020-7ww", Rarity.RARE, mage.cards.c.CropRotation.class)); + cards.add(new SetCardInfo("Culling the Weak", "2023-8", Rarity.RARE, mage.cards.c.CullingTheWeak.class)); + cards.add(new SetCardInfo("Cunning Sparkmage", "2010-2", Rarity.UNCOMMON, mage.cards.c.CunningSparkmage.class)); + cards.add(new SetCardInfo("Dark Ritual", "2020-4", Rarity.RARE, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Darksteel Juggernaut", "2010-1", Rarity.RARE, mage.cards.d.DarksteelJuggernaut.class)); + cards.add(new SetCardInfo("Daxos, Blessed by the Sun", "2020-2", Rarity.UNCOMMON, mage.cards.d.DaxosBlessedByTheSun.class)); + cards.add(new SetCardInfo("Diabolic Edict", "2024-5", Rarity.RARE, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Disenchant", "2022-1", Rarity.RARE, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Duress", "2025-7", Rarity.RARE, mage.cards.d.Duress.class)); + cards.add(new SetCardInfo("Fireball", "1995-1", Rarity.COMMON, mage.cards.f.Fireball.class)); + cards.add(new SetCardInfo("Frantic Search", "2022-4", Rarity.RARE, mage.cards.f.FranticSearch.class)); + cards.add(new SetCardInfo("Gingerbrute", "2023-3", Rarity.RARE, mage.cards.g.Gingerbrute.class)); + cards.add(new SetCardInfo("Gush", "2024-4", Rarity.RARE, mage.cards.g.Gush.class)); + cards.add(new SetCardInfo("Harald, King of Skemfar", "2021-3", Rarity.RARE, mage.cards.h.HaraldKingOfSkemfar.class)); + cards.add(new SetCardInfo("Heliod's Pilgrim", "2020-6", Rarity.RARE, mage.cards.h.HeliodsPilgrim.class)); + cards.add(new SetCardInfo("Hypnotic Sprite", "2019-5", Rarity.RARE, mage.cards.h.HypnoticSprite.class)); + cards.add(new SetCardInfo("Jace Beleren", "2009-1", Rarity.MYTHIC, mage.cards.j.JaceBeleren.class)); + cards.add(new SetCardInfo("Jace, Memory Adept", "2024-2", Rarity.MYTHIC, mage.cards.j.JaceMemoryAdept.class)); + cards.add(new SetCardInfo("Jamuraan Lion", "1996-3", Rarity.COMMON, mage.cards.j.JamuraanLion.class)); + cards.add(new SetCardInfo("Kuldotha Phoenix", "2010-5", Rarity.RARE, mage.cards.k.KuldothaPhoenix.class)); + cards.add(new SetCardInfo("Lava Coil", "2019-4", Rarity.UNCOMMON, mage.cards.l.LavaCoil.class)); + cards.add(new SetCardInfo("Lightning Hounds", "2000-1", Rarity.COMMON, mage.cards.l.LightningHounds.class)); + cards.add(new SetCardInfo("Liliana of the Dark Realms", "2024-8", Rarity.MYTHIC, mage.cards.l.LilianaOfTheDarkRealms.class)); + cards.add(new SetCardInfo("Mental Misstep", "2023-1", Rarity.RARE, mage.cards.m.MentalMisstep.class)); + cards.add(new SetCardInfo("Nicol Bolas, Planeswalker", "2025-10", Rarity.MYTHIC, mage.cards.n.NicolBolasPlaneswalker.class)); + cards.add(new SetCardInfo("Parallax Dementia", "2000-6", Rarity.COMMON, mage.cards.p.ParallaxDementia.class)); + cards.add(new SetCardInfo("Patchwork Banner", "2024-7", Rarity.RARE, mage.cards.p.PatchworkBanner.class)); + cards.add(new SetCardInfo("Phantasmal Dragon", "2011-1", Rarity.UNCOMMON, mage.cards.p.PhantasmalDragon.class)); + cards.add(new SetCardInfo("Phyrexian Rager", "2000-5", Rarity.COMMON, mage.cards.p.PhyrexianRager.class)); + cards.add(new SetCardInfo("Pyromancer's Gauntlet", "2023-6", Rarity.RARE, mage.cards.p.PyromancersGauntlet.class)); + cards.add(new SetCardInfo("Ruin Crab", "2023-4", Rarity.RARE, mage.cards.r.RuinCrab.class)); + cards.add(new SetCardInfo("Sandbar Crocodile", "1996-1", Rarity.COMMON, mage.cards.s.SandbarCrocodile.class)); + cards.add(new SetCardInfo("Scent of Cinder", "1999-1", Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); + cards.add(new SetCardInfo("Shield Wall", "1997-3", Rarity.COMMON, mage.cards.s.ShieldWall.class)); + cards.add(new SetCardInfo("Shivan Dragon", "2001-2", Rarity.RARE, mage.cards.s.ShivanDragon.class)); + cards.add(new SetCardInfo("Shock", "2025-1", Rarity.RARE, mage.cards.s.Shock.class)); + cards.add(new SetCardInfo("Shrieking Drake", "1997-2", Rarity.COMMON, mage.cards.s.ShriekingDrake.class)); + cards.add(new SetCardInfo("Silver Drake", "2000-2", Rarity.COMMON, mage.cards.s.SilverDrake.class)); + cards.add(new SetCardInfo("Snuff Out", "2024-1", Rarity.RARE, mage.cards.s.SnuffOut.class)); + cards.add(new SetCardInfo("Spined Wurm", "2001-1", Rarity.COMMON, mage.cards.s.SpinedWurm.class)); + cards.add(new SetCardInfo("Sprite Dragon", "2020-5", Rarity.RARE, mage.cards.s.SpriteDragon.class)); + cards.add(new SetCardInfo("Staggering Insight", "2020-3", Rarity.RARE, mage.cards.s.StaggeringInsight.class)); + cards.add(new SetCardInfo("Stream of Life", "1997-4", Rarity.COMMON, mage.cards.s.StreamOfLife.class)); + cards.add(new SetCardInfo("Talruum Champion", "1997-1", Rarity.COMMON, mage.cards.t.TalruumChampion.class)); + cards.add(new SetCardInfo("Tangled Florahedron", "2020-8", Rarity.UNCOMMON, mage.cards.t.TangledFlorahedron.class)); + cards.add(new SetCardInfo("Thorn Elemental", "2000-3", Rarity.RARE, mage.cards.t.ThornElemental.class)); + cards.add(new SetCardInfo("Usher of the Fallen", "2022-3", Rarity.RARE, mage.cards.u.UsherOfTheFallen.class)); + cards.add(new SetCardInfo("Voltaic Key", "2024-6", Rarity.RARE, mage.cards.v.VoltaicKey.class)); + cards.add(new SetCardInfo("Warmonger", "1999-2", Rarity.UNCOMMON, mage.cards.w.Warmonger.class)); + cards.add(new SetCardInfo("Wild Growth", "2022-2", Rarity.RARE, mage.cards.w.WildGrowth.class)); + cards.add(new SetCardInfo("Winged Boots", "2023-7", Rarity.RARE, mage.cards.w.WingedBoots.class)); + cards.add(new SetCardInfo("Worn Powerstone", "2023-2", Rarity.RARE, mage.cards.w.WornPowerstone.class)); + cards.add(new SetCardInfo("Zhalfirin Knight", "1996-2", Rarity.COMMON, mage.cards.z.ZhalfirinKnight.class)); } } diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index a4275e34e4c..0b77e47469c 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -178,6 +178,7 @@ |Generate|DUNGEON:AFR|Tomb of Annihilation|||TombOfAnnihilationDungeon| |Generate|DUNGEON:AFR|Lost Mine of Phandelver|||LostMineOfPhandelverDungeon| |Generate|DUNGEON:AFR|Dungeon of the Mad Mage|||DungeonOfTheMadMageDungeon| +|Generate|DUNGEON:CLB|Undercity|||UndercityDungeon| # ALL TOKENS # Usage hints: @@ -209,23 +210,8 @@ |Generate|TOK:AKH|Warrior|||WarriorVigilantToken| |Generate|TOK:AKH|Wurm|||Wurm55Token| |Generate|TOK:AKH|Zombie|||ZombieToken| -#TOK:AKH - some tokens from real cards (see Embalm ability) -#|Generate|TOK:AKH|Angel of Sanctions|| -#|Generate|TOK:AKH|Anointer Priest|| -#|Generate|TOK:AKH|Aven Initiate|| -#|Generate|TOK:AKH|Aven Wind Guide|| -#|Generate|TOK:AKH|Glyph Keeper|| -#|Generate|TOK:AKH|Heart-Piercer Manticore|| -#|Generate|TOK:AKH|Honored Hydra|| -#|Generate|TOK:AKH|Labyrinth Guardian|| -#|Generate|TOK:AKH|Oketra's Attendant|| -#|Generate|TOK:AKH|Sacred Cat|| -#|Generate|TOK:AKH|Tah-Crop Skirmisher|| -#|Generate|TOK:AKH|Temmet, Vizier of Naktamun|| -#|Generate|TOK:AKH|Trueheart Duelist|| -#|Generate|TOK:AKH|Unwavering Initiate|| -#|Generate|TOK:AKH|Vizier of Many Faces|| +# ALA |Generate|TOK:ALA|Beast|||GodSireBeastToken| |Generate|TOK:ALA|Dragon|||DragonToken| |Generate|TOK:ALA|Goblin|||GoblinToken| @@ -236,10 +222,14 @@ |Generate|TOK:ALA|Soldier|||SoldierToken| |Generate|TOK:ALA|Thopter|||ThopterToken| |Generate|TOK:ALA|Zombie|||ZombieToken| + +# ARB |Generate|TOK:ARB|Bird Soldier|||BirdSoldierToken| |Generate|TOK:ARB|Dragon|||DragonBroodmotherDragonToken| |Generate|TOK:ARB|Lizard|||LizardToken| -|Generate|TOK:ARN|Djinn|||DjinnToken| +|Generate|TOK:ARB|Zombie Wizard|||ZombieWizardToken| + +# AVR |Generate|TOK:AVR|Angel|||AngelToken| |Generate|TOK:AVR|Demon|||DemonToken| |Generate|TOK:AVR|Human|1||ThatcherHumanToken| @@ -247,17 +237,21 @@ |Generate|TOK:AVR|Spirit|1||SpiritBlueToken| |Generate|TOK:AVR|Spirit|2||SpiritWhiteToken| |Generate|TOK:AVR|Zombie|||ZombieToken| + +# BFZ |Generate|TOK:BFZ|Dragon|||DragonToken2| +|Generate|TOK:BFZ|Eldrazi|||EldraziToken| |Generate|TOK:BFZ|Eldrazi Scion|1||EldraziScionToken| |Generate|TOK:BFZ|Eldrazi Scion|2||EldraziScionToken| |Generate|TOK:BFZ|Eldrazi Scion|3||EldraziScionToken| -|Generate|TOK:BFZ|Eldrazi|||EldraziToken| |Generate|TOK:BFZ|Elemental|1||OmnathElementalToken| |Generate|TOK:BFZ|Elemental|2||Elemental31TrampleHasteToken| |Generate|TOK:BFZ|Knight Ally|||KnightAllyToken| |Generate|TOK:BFZ|Kor Ally|||KorAllyToken| |Generate|TOK:BFZ|Octopus|||OctopusToken| |Generate|TOK:BFZ|Plant|||Plant11Token| + +# BNG |Generate|TOK:BNG|Bird|1||EnchantmentBirdToken| |Generate|TOK:BNG|Bird|2||BirdToken| |Generate|TOK:BNG|Cat Soldier|||CatSoldierCreatureToken| @@ -268,6 +262,8 @@ |Generate|TOK:BNG|Soldier|||GodFavoredGeneralSoldierToken| |Generate|TOK:BNG|Wolf|||WolfToken| |Generate|TOK:BNG|Zombie|||ForlornPseudammaZombieToken| + +# C14 |Generate|TOK:C14|Angel|||AngelToken| |Generate|TOK:C14|Ape|||ApeToken| |Generate|TOK:C14|Beast|1||BeastToken| @@ -281,7 +277,6 @@ |Generate|TOK:C14|Elf Warrior|||ElfWarriorToken| |Generate|TOK:C14|Fish|||ReefWormFishToken| |Generate|TOK:C14|Gargoyle|||GargoyleToken| -|Generate|TOK:C14|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:C14|Goat|||GoatToken| |Generate|TOK:C14|Goblin|||GoblinToken| |Generate|TOK:C14|Horror|||HorrorXXBlackToken| @@ -290,6 +285,9 @@ |Generate|TOK:C14|Myr|||MyrToken| |Generate|TOK:C14|Pegasus|||PegasusToken| |Generate|TOK:C14|Pentavite|||PentaviteToken| +|Generate|TOK:C14|Phyrexian Germ|||PhyrexianGermToken| +|Generate|TOK:C14|Phyrexian Wurm|1||WurmWithDeathtouchToken| +|Generate|TOK:C14|Phyrexian Wurm|2||WurmWithLifelinkToken| |Generate|TOK:C14|Soldier|||SoldierToken| |Generate|TOK:C14|Spirit|||SpiritWhiteToken| |Generate|TOK:C14|Stoneforged Blade|||NahiriTheLithomancerEquipmentToken| @@ -297,10 +295,10 @@ |Generate|TOK:C14|Tuktuk the Returned|||TuktukTheReturnedToken| |Generate|TOK:C14|Whale|||ReefWormWhaleToken| |Generate|TOK:C14|Wolf|||WolfToken| -|Generate|TOK:C14|Phyrexian Wurm|1||WurmWithDeathtouchToken| -|Generate|TOK:C14|Phyrexian Wurm|2||WurmWithLifelinkToken| |Generate|TOK:C14|Zombie|1||ZombieToken| |Generate|TOK:C14|Zombie|2||StitcherGeralfZombieToken| + +# C15 |Generate|TOK:C15|Angel|||AngelToken| |Generate|TOK:C15|Bear|||BearToken| |Generate|TOK:C15|Beast|||BeastToken2| @@ -311,11 +309,11 @@ |Generate|TOK:C15|Elemental Shaman|||ElementalShamanToken| |Generate|TOK:C15|Elephant|||ElephantToken| |Generate|TOK:C15|Frog Lizard|||FrogLizardToken| -|Generate|TOK:C15|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:C15|Gold|||GoldToken| |Generate|TOK:C15|Knight|1||HuntedDragonKnightToken| |Generate|TOK:C15|Knight|2||KnightToken| |Generate|TOK:C15|Lightning Rager|||LightningRagerToken| +|Generate|TOK:C15|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:C15|Saproling|||SaprolingToken| |Generate|TOK:C15|Shapeshifter|||CribSwapShapeshifterWhiteToken| |Generate|TOK:C15|Snake|1||SnakeToken| @@ -325,17 +323,19 @@ |Generate|TOK:C15|Spirit|2||WhiteBlackSpiritToken| |Generate|TOK:C15|Wolf|||WolfToken| |Generate|TOK:C15|Zombie|||ZombieToken| + +# C16 |Generate|TOK:C16|Beast|||BeastToken| |Generate|TOK:C16|Bird|1||SwanSongBirdToken| |Generate|TOK:C16|Bird|2||BirdToken| |Generate|TOK:C16|Elemental|||WhiteElementalToken| |Generate|TOK:C16|Elf Warrior|||ElfWarriorToken| -|Generate|TOK:C16|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:C16|Goat|||GoatToken| |Generate|TOK:C16|Goblin|||SpyMasterGoblinToken| -|Generate|TOK:C16|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:C16|Myr|||MyrToken| |Generate|TOK:C16|Ogre|||OgreToken| +|Generate|TOK:C16|Phyrexian Germ|||PhyrexianGermToken| +|Generate|TOK:C16|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:C16|Saproling|1||SaprolingToken| |Generate|TOK:C16|Saproling|2||SaprolingToken| |Generate|TOK:C16|Soldier|||SoldierToken| @@ -345,6 +345,8 @@ |Generate|TOK:C16|Thopter|||ThopterToken| |Generate|TOK:C16|Worm|||BlackGreenWormToken| |Generate|TOK:C16|Zombie|||ZombieToken| + +# C17 |Generate|TOK:C17|Bat|||BatToken| |Generate|TOK:C17|Cat|||CatToken| |Generate|TOK:C17|Cat Dragon|||WasitoraCatDragonToken| @@ -356,6 +358,8 @@ |Generate|TOK:C17|Rat|||DeathtouchRatToken| |Generate|TOK:C17|Vampire|||EdgarMarkovToken| |Generate|TOK:C17|Zombie|||ZombieToken| + +# C18 |Generate|TOK:C18|Angel|||AngelToken| |Generate|TOK:C18|Beast|1||BeastToken2| |Generate|TOK:C18|Beast|2||SpawningGroundsBeastToken| @@ -381,6 +385,8 @@ |Generate|TOK:C18|Thopter|3||ThopterToken| |Generate|TOK:C18|Worm|||BlackGreenWormToken| |Generate|TOK:C18|Zombie|||ZombieToken| + +# C19 |Generate|TOK:C19|Assassin|||AssassinToken| |Generate|TOK:C19|Beast|1||BeastToken| |Generate|TOK:C19|Beast|2||BeastToken2| @@ -392,9 +398,9 @@ |Generate|TOK:C19|Egg|||AtlaPalaniToken| |Generate|TOK:C19|Eldrazi|||EldraziToken| |Generate|TOK:C19|Gargoyle|||GargoyleToken| -|Generate|TOK:C19|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:C19|Human|||HumanToken| |Generate|TOK:C19|Pegasus|||PegasusToken| +|Generate|TOK:C19|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:C19|Plant|||Plant11Token| |Generate|TOK:C19|Rhino|||RhinoToken| |Generate|TOK:C19|Saproling|||SaprolingToken| @@ -405,6 +411,8 @@ |Generate|TOK:C19|Wurm|||WurmToken| |Generate|TOK:C19|Zombie|1||ZombieToken| |Generate|TOK:C19|Zombie|2||ZombieToken| + +# CMA |Generate|TOK:CMA|Beast|1||BeastToken| |Generate|TOK:CMA|Beast|2||BeastToken2| |Generate|TOK:CMA|Dragon|||DragonToken2| @@ -414,9 +422,9 @@ |Generate|TOK:CMA|Elf Druid|||ElfDruidToken| |Generate|TOK:CMA|Elf Warrior|||ElfWarriorToken| |Generate|TOK:CMA|Gargoyle|||GargoyleToken| -|Generate|TOK:CMA|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:CMA|Kithkin Soldier|||KithkinSoldierToken| |Generate|TOK:CMA|Knight|||KnightToken| +|Generate|TOK:CMA|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:CMA|Saproling|||SaprolingToken| |Generate|TOK:CMA|Spider|||SpiderToken| |Generate|TOK:CMA|Spirit|||SpiritWhiteToken| @@ -424,6 +432,8 @@ |Generate|TOK:CMA|Wolf|1||WolfToken| |Generate|TOK:CMA|Wolf|2||WolfToken| |Generate|TOK:CMA|Zombie|||ZombieToken| + +# CN2 |Generate|TOK:CN2|Assassin|||QueenMarchesaAssassinToken| |Generate|TOK:CN2|Beast|||BeastToken| |Generate|TOK:CN2|Construct|||DarettiConstructToken| @@ -433,6 +443,8 @@ |Generate|TOK:CN2|Soldier|||SoldierToken| |Generate|TOK:CN2|Spirit|||SpiritWhiteToken| |Generate|TOK:CN2|Zombie|||ZombieToken| + +# CNS |Generate|TOK:CNS|Construct|||DarettiConstructToken| |Generate|TOK:CNS|Demon|||DemonFlyingToken| |Generate|TOK:CNS|Elephant|||ElephantToken| @@ -441,48 +453,88 @@ |Generate|TOK:CNS|Squirrel|||SquirrelToken| |Generate|TOK:CNS|Wolf|||WolfToken| |Generate|TOK:CNS|Zombie|||ZombieToken| + +# CON |Generate|TOK:CON|Angel|||AngelToken| |Generate|TOK:CON|Elemental|||ElementalTokenWithHaste| + +# DVD |Generate|TOK:DVD|Demon|||DemonFlyingToken| |Generate|TOK:DVD|Spirit|||SpiritWhiteToken| |Generate|TOK:DVD|Thrull|||BreedingPitThrullToken| + +# GVL |Generate|TOK:GVL|Bat|||BatToken| |Generate|TOK:GVL|Beast|1||BeastToken| |Generate|TOK:GVL|Beast|2||BeastToken2| |Generate|TOK:GVL|Elephant|||ElephantToken| + +# DDC |Generate|TOK:DDC|Demon|||DemonFlyingToken| |Generate|TOK:DDC|Spirit|||SpiritWhiteToken| |Generate|TOK:DDC|Thrull|||BreedingPitThrullToken| + +# DDD |Generate|TOK:DDD|Beast|1||BeastToken| |Generate|TOK:DDD|Beast|2||BeastToken2| |Generate|TOK:DDD|Elephant|||ElephantToken| + +# DDE |Generate|TOK:DDE|Hornet|||HornetToken| |Generate|TOK:DDE|Phyrexian Minion|||PhyrexianMinionToken| |Generate|TOK:DDE|Saproling|||SaprolingToken| + +# DDF |Generate|TOK:DDF|Soldier|||SoldierToken| + +# DDG |Generate|TOK:DDG|Goblin|||GoblinToken| + +# DDH |Generate|TOK:DDH|Griffin|||GriffinToken| |Generate|TOK:DDH|Saproling|||SaprolingToken| + +# DDJ |Generate|TOK:DDJ|Saproling|||SaprolingToken| + +# DDK |Generate|TOK:DDK|Spirit|||SpiritWhiteToken| + +# DDL |Generate|TOK:DDL|Beast|||BeastToken| |Generate|TOK:DDL|Griffin|||GriffinToken| + +# DDM |Generate|TOK:DDM|Assassin|||AssassinToken| + +# DDN |Generate|TOK:DDN|Goblin|||GoblinToken| + +# DDO |Generate|TOK:DDO|Kraken|||Kraken99Token| |Generate|TOK:DDO|Soldier|||SoldierToken| + +# DDP |Generate|TOK:DDP|Eldrazi Spawn|1||EldraziSpawnToken| |Generate|TOK:DDP|Eldrazi Spawn|2||EldraziSpawnToken| |Generate|TOK:DDP|Eldrazi Spawn|3||EldraziSpawnToken| |Generate|TOK:DDP|Hellion|||HellionToken| |Generate|TOK:DDP|Plant|||PlantToken| + +# DDQ |Generate|TOK:DDQ|Angel|||AngelToken| |Generate|TOK:DDQ|Human|||HumanToken| |Generate|TOK:DDQ|Spirit|||SpiritWhiteToken| |Generate|TOK:DDQ|Zombie|||ZombieToken| + +# DGM |Generate|TOK:DGM|Elemental|||VoiceOfResurgenceToken| + +# DKA |Generate|TOK:DKA|Human|||HumanToken| |Generate|TOK:DKA|Vampire|||SorinLordOfInnistradVampireToken| + +# DOM |Generate|TOK:DOM|Cleric|||BelzenlokClericToken| |Generate|TOK:DOM|Construct|||KarnConstructToken| |Generate|TOK:DOM|Demon|||BelzenlokDemonToken| @@ -497,25 +549,33 @@ |Generate|TOK:DOM|Saproling|3||SaprolingToken| |Generate|TOK:DOM|Soldier|||SoldierToken| |Generate|TOK:DOM|Zombie Knight|||ZombieKnightToken| + +# DTK |Generate|TOK:DTK|Djinn Monk|||DjinnMonkToken| |Generate|TOK:DTK|Dragon|||DragonToken| |Generate|TOK:DTK|Goblin|||GoblinToken| |Generate|TOK:DTK|Warrior|||WarriorToken| -|Generate|TOK:DTK|Zombie Horror|||CorpseweftZombieToken| |Generate|TOK:DTK|Zombie|||ZombieToken| +|Generate|TOK:DTK|Zombie Horror|||CorpseweftZombieToken| + +# E01 |Generate|TOK:E01|Beast|1||BeastToken| |Generate|TOK:E01|Beast|2||BeastToken2| |Generate|TOK:E01|Soldier|||SoldierToken| |Generate|TOK:E01|Spirit|||SpiritWhiteToken| + +# E02 |Generate|TOK:E02|Saproling|||SaprolingToken| + +# EMA |Generate|TOK:EMA|Carnivore|||CarnivoreToken| |Generate|TOK:EMA|Dragon|||DragonEggDragonToken| |Generate|TOK:EMA|Elemental|1||RedElementalToken| |Generate|TOK:EMA|Elemental|2||CallTheSkyBreakerElementalToken| |Generate|TOK:EMA|Elephant|||ElephantToken| |Generate|TOK:EMA|Elf Warrior|||ElfWarriorToken| -|Generate|TOK:EMA|Goblin Soldier|||GoblinSoldierToken| |Generate|TOK:EMA|Goblin|||GoblinToken| +|Generate|TOK:EMA|Goblin Soldier|||GoblinSoldierToken| |Generate|TOK:EMA|Serf|||SerfToken| |Generate|TOK:EMA|Soldier|||SoldierToken| |Generate|TOK:EMA|Spirit|1||SpiritToken| @@ -523,20 +583,24 @@ |Generate|TOK:EMA|Wall|||TidalWaveWallToken| |Generate|TOK:EMA|Wurm|||WurmToken| |Generate|TOK:EMA|Zombie|||ZombieToken| + +# EMN +|Generate|TOK:EMN|Eldrazi Horror|||EldraziHorrorToken| +|Generate|TOK:EMN|Human|||RedHumanToken| +|Generate|TOK:EMN|Human Wizard|||HumanWizardToken| +|Generate|TOK:EMN|Spider|||SpiderToken| +|Generate|TOK:EMN|Zombie|1||ZombieToken| +|Generate|TOK:EMN|Zombie|2||ZombieToken| +|Generate|TOK:EMN|Zombie|3||ZombieToken| +|Generate|TOK:EMN|Zombie|4||ZombieToken2| #TOK:EMN - Human Soldier, Spirit, Devil, Insect and Wolf tokens from SOI set #|Generate|TOK:EMN|Devil|||DevilToken| #|Generate|TOK:EMN|Human Soldier|||HumanSoldierToken| #|Generate|TOK:EMN|Insect|||InsectToken| #|Generate|TOK:EMN|Spirit|||SpiritWhiteToken| #|Generate|TOK:EMN|Wolf|||WolfToken| -|Generate|TOK:EMN|Eldrazi Horror|||EldraziHorrorToken| -|Generate|TOK:EMN|Human Wizard|||HumanWizardToken| -|Generate|TOK:EMN|Human|||RedHumanToken| -|Generate|TOK:EMN|Spider|||SpiderToken| -|Generate|TOK:EMN|Zombie|1||ZombieToken| -|Generate|TOK:EMN|Zombie|2||ZombieToken| -|Generate|TOK:EMN|Zombie|3||ZombieToken| -|Generate|TOK:EMN|Zombie|4||ZombieToken2| + +# EVE |Generate|TOK:EVE|Beast|||BeastToken| |Generate|TOK:EVE|Bird|||BlueBirdToken| |Generate|TOK:EVE|Elemental|||CallTheSkyBreakerElementalToken| @@ -544,12 +608,18 @@ |Generate|TOK:EVE|Goblin Soldier|||GoblinSoldierToken| |Generate|TOK:EVE|Spirit|||WhiteBlackSpiritToken| |Generate|TOK:EVE|Worm|||BlackGreenWormToken| + +# EVG |Generate|TOK:EVG|Elemental|||VoiceOfTheWoodsElementalToken| |Generate|TOK:EVG|Elf Warrior|||ElfWarriorToken| |Generate|TOK:EVG|Goblin|||GoblinToken| + +# FRF |Generate|TOK:FRF|Monk|||MonasteryMentorToken| |Generate|TOK:FRF|Spirit|||SpiritWhiteToken| |Generate|TOK:FRF|Warrior|||MarduStrikeLeaderWarriorToken| + +# GTC |Generate|TOK:GTC|Angel|||AngelToken| |Generate|TOK:GTC|Cleric|||DeathpactAngelToken| |Generate|TOK:GTC|Frog Lizard|||FrogLizardToken| @@ -557,21 +627,16 @@ |Generate|TOK:GTC|Rat|||RatToken| |Generate|TOK:GTC|Soldier|||SoldierTokenWithHaste| |Generate|TOK:GTC|Spirit|||WhiteBlackSpiritToken| + +# H17 |Generate|TOK:H17|Dragon|||DragonTokenGold| + +# HOU |Generate|TOK:HOU|Horse|||CrestedSunmareToken| |Generate|TOK:HOU|Insect|||TheLocustGodInsectToken| |Generate|TOK:HOU|Snake|||RhonassLastStandToken| -#TOK:HOU - some tokens from real cards (see Eternalize ability) -#TOK:HOU - Cat, Warrior and Zombie tokens from AKH set -#|Generate|TOK:HOU|Adorned Pouncer|||| -#|Generate|TOK:HOU|Champion of Wits|||| -#|Generate|TOK:HOU|Dreamstealer|||| -#|Generate|TOK:HOU|Earthshaker Khenra|||| -#|Generate|TOK:HOU|Proven Combatant|||| -#|Generate|TOK:HOU|Resilient Khenra|||| -#|Generate|TOK:HOU|Sinuous Striker|||| -#|Generate|TOK:HOU|Steadfast Sentinel|||| -#|Generate|TOK:HOU|Sunscourge Champion|||| + +# ISD |Generate|TOK:ISD|Angel|||AngelToken| |Generate|TOK:ISD|Demon|||DemonToken| |Generate|TOK:ISD|Homunculus|||StitchersApprenticeHomunculusToken| @@ -584,12 +649,16 @@ |Generate|TOK:ISD|Zombie|1||ZombieToken| |Generate|TOK:ISD|Zombie|2||ZombieToken| |Generate|TOK:ISD|Zombie|3||ZombieToken| + +# JOU |Generate|TOK:JOU|Hydra|||HydraBroodmasterToken| |Generate|TOK:JOU|Minotaur|||MinotaurToken| |Generate|TOK:JOU|Snake|||PharikaSnakeToken| |Generate|TOK:JOU|Sphinx|||HourOfNeedSphinxToken| |Generate|TOK:JOU|Spider|||RenownedWeaverSpiderToken| |Generate|TOK:JOU|Zombie|||RitualOfTheReturnedZombieToken| + +# KLD |Generate|TOK:KLD|Beast|||ArchitectOfTheUntamedBeastToken| |Generate|TOK:KLD|Construct|1||OviyaPashiriSageLifecrafterToken| |Generate|TOK:KLD|Construct|2||MetallurgicSummoningsConstructToken| @@ -599,17 +668,20 @@ |Generate|TOK:KLD|Thopter|1||ThopterColorlessToken| |Generate|TOK:KLD|Thopter|2||ThopterColorlessToken| |Generate|TOK:KLD|Thopter|3||ThopterColorlessToken| + +# KTK |Generate|TOK:KTK|Bear|||BearsCompanionBearToken| |Generate|TOK:KTK|Bird|||WingmateRocToken| |Generate|TOK:KTK|Goblin|||GoblinToken| |Generate|TOK:KTK|Snake|||SnakeToken| -|Generate|TOK:KTK|Spirit Warrior|||SpiritWarriorToken| |Generate|TOK:KTK|Spirit|||SpiritWhiteToken| +|Generate|TOK:KTK|Spirit Warrior|||SpiritWarriorToken| |Generate|TOK:KTK|Vampire|||VampireToken| |Generate|TOK:KTK|Warrior|1||WarriorToken| |Generate|TOK:KTK|Warrior|2||WarriorToken| |Generate|TOK:KTK|Zombie|||ZombieToken| -# LGN don't have tokens, from wiki: A Sliver token for Brood Sliver and a Goblin token for Warbreak Trumpeter were featured as a Magic Player Reward. + +# LRW |Generate|TOK:LRW|Avatar|||AvatarToken| |Generate|TOK:LRW|Beast|||BeastToken| |Generate|TOK:LRW|Elemental|1||Elemental44GreenToken| @@ -621,6 +693,8 @@ |Generate|TOK:LRW|Merfolk Wizard|||MerfolkWizardToken| |Generate|TOK:LRW|Shapeshifter|||CribSwapShapeshifterWhiteToken| |Generate|TOK:LRW|Wolf|||WolfToken| + +# M10 |Generate|TOK:M10|Avatar|||AvatarToken| |Generate|TOK:M10|Beast|||BeastToken| |Generate|TOK:M10|Gargoyle|||GargoyleToken| @@ -629,12 +703,16 @@ |Generate|TOK:M10|Soldier|||SoldierToken| |Generate|TOK:M10|Wolf|||WolfToken| |Generate|TOK:M10|Zombie|||ZombieToken| + +# M11 |Generate|TOK:M11|Avatar|||AvatarToken| |Generate|TOK:M11|Beast|||BeastToken| |Generate|TOK:M11|Bird|||RocEggToken| |Generate|TOK:M11|Ooze|1||MitoticSlimeOozeToken| |Generate|TOK:M11|Ooze|2||OozeToken| |Generate|TOK:M11|Zombie|||ZombieToken| + +# M12 |Generate|TOK:M12|Beast|||BeastToken| |Generate|TOK:M12|Bird|||RocEggToken| |Generate|TOK:M12|Pentavite|||PentaviteToken| @@ -642,6 +720,8 @@ |Generate|TOK:M12|Soldier|||SoldierToken| |Generate|TOK:M12|Wurm|||WurmToken| |Generate|TOK:M12|Zombie|||ZombieToken| + +# M13 |Generate|TOK:M13|Beast|||BeastToken| |Generate|TOK:M13|Cat|||CatToken| |Generate|TOK:M13|Drake|||DrakeToken| @@ -652,6 +732,8 @@ |Generate|TOK:M13|Soldier|||SoldierToken| |Generate|TOK:M13|Wurm|||WurmToken| |Generate|TOK:M13|Zombie|||ZombieToken| + +# M14 |Generate|TOK:M14|Angel|||AngelToken| |Generate|TOK:M14|Beast|||BeastToken| |Generate|TOK:M14|Cat|||CatToken| @@ -663,6 +745,8 @@ |Generate|TOK:M14|Sliver|||SliverToken| |Generate|TOK:M14|Wolf|||WolfToken| |Generate|TOK:M14|Zombie|||ZombieToken| + +# M15 |Generate|TOK:M15|Beast|1||GarrukApexPredatorBeastToken| |Generate|TOK:M15|Beast|2||BeastToken| |Generate|TOK:M15|Dragon|||DragonEggDragonToken| @@ -675,26 +759,32 @@ |Generate|TOK:M15|Squid|||SquidToken| |Generate|TOK:M15|Treefolk Warrior|||KalonianTwingroveTreefolkWarriorToken| |Generate|TOK:M15|Zombie|||ZombieToken| -|Generate|TOK:MBS|Phyrexian Germ|||PhyrexianGermToken| + +# MBS |Generate|TOK:MBS|Golem|||TitanForgeGolemToken| +|Generate|TOK:MBS|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:MBS|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:MBS|Thopter|||ThopterColorlessToken| |Generate|TOK:MBS|Zombie|||ZombieToken| + +# MED |Generate|TOK:MED|Beast|||GarrukApexPredatorBeastToken| |Generate|TOK:MED|Construct|1||KarnConstructToken| |Generate|TOK:MED|Construct|2||DarettiConstructToken| |Generate|TOK:MED|Dragon|||DragonToken| |Generate|TOK:MED|Soldier|||SoldierToken| |Generate|TOK:MED|Zombie|||ZombieToken| + +# MM2 |Generate|TOK:MM2|Eldrazi Spawn|1||EldraziSpawnToken| |Generate|TOK:MM2|Eldrazi Spawn|2||EldraziSpawnToken| |Generate|TOK:MM2|Eldrazi Spawn|3||EldraziSpawnToken| |Generate|TOK:MM2|Elephant|||ElephantToken| |Generate|TOK:MM2|Faerie Rogue|||FaerieRogueToken| -|Generate|TOK:MM2|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:MM2|Golem|||GolemToken| |Generate|TOK:MM2|Insect|||InsectToken| |Generate|TOK:MM2|Myr|||MyrToken| +|Generate|TOK:MM2|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:MM2|Saproling|||SaprolingToken| |Generate|TOK:MM2|Snake|||SnakeToken| |Generate|TOK:MM2|Soldier|||SoldierToken| @@ -702,6 +792,8 @@ |Generate|TOK:MM2|Thrull|||ThrullToken| |Generate|TOK:MM2|Wolf|||WolfToken| |Generate|TOK:MM2|Worm|||BlackGreenWormToken| + +# MM3 |Generate|TOK:MM3|Angel|||AngelToken| |Generate|TOK:MM3|Beast|1||BeastToken| |Generate|TOK:MM3|Beast|2||BeastToken2| @@ -713,8 +805,8 @@ |Generate|TOK:MM3|Giant Warrior|||GiantBaitingGiantWarriorToken| |Generate|TOK:MM3|Goblin|||GoblinToken| |Generate|TOK:MM3|Goblin Warrior|||GoblinWarriorToken| -|Generate|TOK:MM3|Phyrexian Golem|||PhyrexianGolemToken| |Generate|TOK:MM3|Ooze|||OozeToken| +|Generate|TOK:MM3|Phyrexian Golem|||PhyrexianGolemToken| |Generate|TOK:MM3|Saproling|||SaprolingToken| |Generate|TOK:MM3|Soldier|1||SoldierToken| |Generate|TOK:MM3|Soldier|2||SoldierTokenWithHaste| @@ -722,13 +814,15 @@ |Generate|TOK:MM3|Spirit|||SpiritWhiteToken| |Generate|TOK:MM3|Wurm|||WurmWithTrampleToken| |Generate|TOK:MM3|Zombie|||ZombieToken| + +# MMA |Generate|TOK:MMA|Bat|||BatToken| |Generate|TOK:MMA|Dragon|||DragonToken| |Generate|TOK:MMA|Elemental|||Elemental44GreenToken| |Generate|TOK:MMA|Faerie Rogue|||OonaQueenFaerieRogueToken| |Generate|TOK:MMA|Giant Warrior|||GiantWarriorToken| -|Generate|TOK:MMA|Goblin Rogue|||GoblinRogueToken| |Generate|TOK:MMA|Goblin|||GoblinToken| +|Generate|TOK:MMA|Goblin Rogue|||GoblinRogueToken| |Generate|TOK:MMA|Illusion|||MelokuTheCloudedMirrorToken| |Generate|TOK:MMA|Kithkin Soldier|||KithkinSoldierToken| |Generate|TOK:MMA|Saproling|||SaprolingToken| @@ -737,19 +831,27 @@ |Generate|TOK:MMA|Treefolk Shaman|||TreefolkShamanToken| |Generate|TOK:MMA|Worm|||BlackGreenWormToken| |Generate|TOK:MMA|Zombie|||ZombieToken| + +# MOR |Generate|TOK:MOR|Faerie Rogue|||FaerieRogueToken| |Generate|TOK:MOR|Giant Warrior|||GiantWarriorToken| |Generate|TOK:MOR|Treefolk Shaman|||TreefolkShamanToken| + +# NPH |Generate|TOK:NPH|Beast|||BeastToken| |Generate|TOK:NPH|Phyrexian Goblin|||PhyrexianGoblinHasteToken| |Generate|TOK:NPH|Phyrexian Golem|||PhyrexianGolemToken| |Generate|TOK:NPH|Phyrexian Myr|||PhyrexianMyrToken| + +# ODY |Generate|TOK:ODY|Bear|||BearToken| |Generate|TOK:ODY|Beast|||BeastToken2| |Generate|TOK:ODY|Elephant|||ElephantToken| |Generate|TOK:ODY|Squirrel|||SquirrelToken| |Generate|TOK:ODY|Wurm|||WurmToken| |Generate|TOK:ODY|Zombie|||ZombieToken| + +# OGW |Generate|TOK:OGW|Angel|||Angel33Token| |Generate|TOK:OGW|Eldrazi Scion|1||EldraziScionToken| |Generate|TOK:OGW|Eldrazi Scion|2||EldraziScionToken| @@ -761,6 +863,8 @@ |Generate|TOK:OGW|Elemental|2||ElementalTokenWithHaste| |Generate|TOK:OGW|Plant|||PlantToken| |Generate|TOK:OGW|Zombie|||ZombieToken| + +# ORI |Generate|TOK:ORI|Angel|||AngelToken| |Generate|TOK:ORI|Ashaya, the Awoken World|||NissaSageAnimistToken| |Generate|TOK:ORI|Demon|||DemonToken| @@ -772,10 +876,14 @@ |Generate|TOK:ORI|Thopter|1||ThopterColorlessToken| |Generate|TOK:ORI|Thopter|2||ThopterColorlessToken| |Generate|TOK:ORI|Zombie|||ZombieToken| + +# RIX |Generate|TOK:RIX|Elemental|1||RekindlingPhoenixToken| |Generate|TOK:RIX|Elemental|2||RedElementalToken| |Generate|TOK:RIX|Golem|||GoldForgeGarrisonGolemToken| |Generate|TOK:RIX|Saproling|||SaprolingToken| + +# ROE |Generate|TOK:ROE|Eldrazi Spawn|1||EldraziSpawnToken| |Generate|TOK:ROE|Eldrazi Spawn|2||EldraziSpawnToken| |Generate|TOK:ROE|Eldrazi Spawn|3||EldraziSpawnToken| @@ -783,6 +891,8 @@ |Generate|TOK:ROE|Hellion|||HellionToken| |Generate|TOK:ROE|Ooze|||OozeToken| |Generate|TOK:ROE|Tuktuk the Returned|||TuktukTheReturnedToken| + +# RTR |Generate|TOK:RTR|Assassin|||AssassinToken| |Generate|TOK:RTR|Bird|||BirdToken| |Generate|TOK:RTR|Centaur|||CentaurToken| @@ -795,6 +905,8 @@ |Generate|TOK:RTR|Saproling|||SaprolingToken| |Generate|TOK:RTR|Soldier|||SoldierToken| |Generate|TOK:RTR|Wurm|||WurmWithTrampleToken| + +# SHM |Generate|TOK:SHM|Elemental|1||DinOfTheFireherdToken| |Generate|TOK:SHM|Elemental|2||Elemental11HasteToken| |Generate|TOK:SHM|Elf Warrior|1||ElfWarriorToken| @@ -807,6 +919,8 @@ |Generate|TOK:SHM|Spider|||SpiderToken| |Generate|TOK:SHM|Spirit|||SpiritWhiteToken| |Generate|TOK:SHM|Wolf|||WolfToken| + +# SOI |Generate|TOK:SOI|Angel|||AngelToken| |Generate|TOK:SOI|Clue|1||ClueArtifactToken| |Generate|TOK:SOI|Clue|2||ClueArtifactToken| @@ -823,25 +937,31 @@ |Generate|TOK:SOI|Vampire Knight|||VampireKnightToken| |Generate|TOK:SOI|Wolf|||WolfToken| |Generate|TOK:SOI|Zombie|||ZombieToken| + +# SOM |Generate|TOK:SOM|Cat|||CatToken| |Generate|TOK:SOM|Goblin|||GoblinToken| |Generate|TOK:SOM|Golem|||GolemToken| -|Generate|TOK:SOM|Phyrexian Insect|||InsectInfectToken| |Generate|TOK:SOM|Myr|||MyrToken| -|Generate|TOK:SOM|Soldier|||SoldierToken| -|Generate|TOK:SOM|Wolf|||WolfToken| +|Generate|TOK:SOM|Phyrexian Insect|||InsectInfectToken| |Generate|TOK:SOM|Phyrexian Wurm|1||WurmWithDeathtouchToken| |Generate|TOK:SOM|Phyrexian Wurm|2||WurmWithLifelinkToken| -|Generate|TOK:SWS|Ewok|||EwokToken| -|Generate|TOK:SWS|B-Wing|||RebelStarshipToken| -|Generate|TOK:SWS|Hunter|||HunterToken| -|Generate|TOK:SWS|TIE Fighter|||TIEFighterToken| -|Generate|TOK:SWS|Trooper|||TrooperToken| +|Generate|TOK:SOM|Soldier|||SoldierToken| +|Generate|TOK:SOM|Wolf|||WolfToken| + +# SWS |Generate|TOK:SWS|AT-AT|||ATATToken| +|Generate|TOK:SWS|B-Wing|||RebelStarshipToken| +|Generate|TOK:SWS|Droid|||DroidToken| +|Generate|TOK:SWS|Ewok|||EwokToken| +|Generate|TOK:SWS|Hunter|||HunterToken| |Generate|TOK:SWS|Rebel|||RebelToken| |Generate|TOK:SWS|Royal Guard|||RoyalGuardToken| +|Generate|TOK:SWS|TIE Fighter|||TIEFighterToken| +|Generate|TOK:SWS|Trooper|||TrooperToken| |Generate|TOK:SWS|Tusken Raider|||TuskenRaiderToken| -|Generate|TOK:SWS|Droid|||DroidToken| + +# THS |Generate|TOK:THS|Bird|||SwanSongBirdToken| |Generate|TOK:THS|Boar|||Boar2Token| |Generate|TOK:THS|Cleric|||HeliodGodOfTheSunToken| @@ -852,14 +972,35 @@ |Generate|TOK:THS|Soldier|1||SoldierToken| |Generate|TOK:THS|Soldier|2||SoldierToken| |Generate|TOK:THS|Soldier|3||AkroanSoldierToken| + +# UST +|Generate|TOK:UST|Angel|||AngelToken| +|Generate|TOK:UST|Beast|||BeastToken| +|Generate|TOK:UST|Brainiac|||BrainiacToken| +|Generate|TOK:UST|Clue|||ClueToken| |Generate|TOK:UST|Dragon|||DragonTokenGold| +|Generate|TOK:UST|Elemental|1||RedElementalToken| +|Generate|TOK:UST|Elemental|2||VoiceOfResurgenceToken| +|Generate|TOK:UST|Gnome|||GnomeToken| +|Generate|TOK:UST|Goat|||GoatToken| +|Generate|TOK:UST|Goblin|||GoblinToken| +|Generate|TOK:UST|Saproling|||SaprolingToken| +|Generate|TOK:UST|Spirit|||SpiritWhiteToken| +|Generate|TOK:UST|Squirrel|||SquirrelToken| |Generate|TOK:UST|Storm Crow|||StormCrowToken| +|Generate|TOK:UST|Thopter|||ThopterToken| +|Generate|TOK:UST|Vampire|||VampireToken| +|Generate|TOK:UST|Zombie|||ZombieToken| + +# WWK |Generate|TOK:WWK|Construct|||StoneIdolToken| |Generate|TOK:WWK|Dragon|||DragonToken2| |Generate|TOK:WWK|Elephant|||ElephantToken| |Generate|TOK:WWK|Ogre|||OgreToken| |Generate|TOK:WWK|Plant|||PlantToken| |Generate|TOK:WWK|Soldier Ally|||JoinTheRanksSoldierToken| + +# XLN |Generate|TOK:XLN|Dinosaur|||DinosaurToken| |Generate|TOK:XLN|Illusion|||JaceCunningCastawayIllusionToken| |Generate|TOK:XLN|Merfolk|||MerfolkHexproofToken| @@ -870,6 +1011,8 @@ |Generate|TOK:XLN|Treasure|3||TreasureToken| |Generate|TOK:XLN|Treasure|4||TreasureToken| |Generate|TOK:XLN|Vampire|||IxalanVampireToken| + +# ZEN |Generate|TOK:ZEN|Angel|||AngelToken| |Generate|TOK:ZEN|Beast|||BeastToken2| |Generate|TOK:ZEN|Bird|||BirdToken| @@ -881,6 +1024,8 @@ |Generate|TOK:ZEN|Vampire|||KalitasVampireToken| |Generate|TOK:ZEN|Wolf|||WolfToken| |Generate|TOK:ZEN|Zombie Giant|||QuestForTheGravelordZombieToken| + +# RNA |Generate|TOK:RNA|Beast|||RedGreenBeastToken| |Generate|TOK:RNA|Centaur|||CentaurToken| |Generate|TOK:RNA|Frog Lizard|||FrogLizardToken| @@ -893,12 +1038,16 @@ |Generate|TOK:RNA|Thopter|||ThopterColorlessToken| |Generate|TOK:RNA|Treasure|||TreasureToken| |Generate|TOK:RNA|Zombie|||ZombieToken| + +# GRN |Generate|TOK:GRN|Angel|||AngelVigilanceToken| |Generate|TOK:GRN|Bird Illusion|||BirdIllusionToken| |Generate|TOK:GRN|Elf Knight|||ElfKnightToken| |Generate|TOK:GRN|Goblin|||GoblinToken| |Generate|TOK:GRN|Insect|||IzoniInsectToken| |Generate|TOK:GRN|Soldier|||SoldierLifelinkToken| + +# WAR |Generate|TOK:WAR|Angel|||AngelVigilanceToken| |Generate|TOK:WAR|Assassin|||AssassinToken2| |Generate|TOK:WAR|Citizen|||PlanewideCelebrationToken| @@ -913,11 +1062,12 @@ |Generate|TOK:WAR|Wizard|||WizardToken| |Generate|TOK:WAR|Wolf|||WolfToken| |Generate|TOK:WAR|Zombie|||ZombieToken| -|Generate|TOK:WAR|Zombie Warrior|||GodEternalOketraToken| |Generate|TOK:WAR|Zombie Army|1||ZombieArmyToken| |Generate|TOK:WAR|Zombie Army|2||ZombieArmyToken| |Generate|TOK:WAR|Zombie Army|3||ZombieArmyToken| -|Generate|TOK:MH1|Shapeshifter|||ShapeshifterToken| +|Generate|TOK:WAR|Zombie Warrior|||GodEternalOketraToken| + +# MH1 |Generate|TOK:MH1|Angel|||AngelVigilanceToken| |Generate|TOK:MH1|Bear|||BearToken| |Generate|TOK:MH1|Bird|||BirdToken| @@ -931,11 +1081,14 @@ |Generate|TOK:MH1|Marit Lage|||MaritLageToken| |Generate|TOK:MH1|Myr|||MyrToken| |Generate|TOK:MH1|Rhino|||RhinoToken| +|Generate|TOK:MH1|Shapeshifter|||ShapeshifterToken| |Generate|TOK:MH1|Soldier|||SoldierToken| |Generate|TOK:MH1|Spider|||SpiderToken| |Generate|TOK:MH1|Spirit|||WhiteBlackSpiritToken| |Generate|TOK:MH1|Squirrel|||SquirrelToken| |Generate|TOK:MH1|Zombie|||ZombieToken| + +# M19 |Generate|TOK:M19|Angel|||AngelVigilanceToken| |Generate|TOK:M19|Avatar|||AvatarToken2| |Generate|TOK:M19|Bat|||BatToken| @@ -950,6 +1103,8 @@ |Generate|TOK:M19|Soldier|||SoldierToken| |Generate|TOK:M19|Thopter|||ThopterColorlessToken| |Generate|TOK:M19|Zombie|||ZombieToken| + +# M20 |Generate|TOK:M20|Ajani's Pridemate|||AjanisPridemateToken| |Generate|TOK:M20|Demon|||DemonToken| |Generate|TOK:M20|Elemental|||RedElementalToken| @@ -982,27 +1137,27 @@ |Generate|TOK:ELD|Wolf|||GarrukCursedHuntsmanToken| # THB -|Generate|TOK:THB|Goat|||GoatToken| -|Generate|TOK:THB|Human Soldier|||HumanSoldierToken| -|Generate|TOK:THB|Pegasus|||PegasusToken2| -|Generate|TOK:THB|Kraken|||KrakenHexproofToken| -|Generate|TOK:THB|Reflection|||ReflectionBlueToken| -|Generate|TOK:THB|Tentacle|||TentacleToken| -|Generate|TOK:THB|Zombie|||ZombieToken| |Generate|TOK:THB|Elemental|||PurphorossInterventionToken| +|Generate|TOK:THB|Goat|||GoatToken| +|Generate|TOK:THB|Gold|||GoldToken| +|Generate|TOK:THB|Human Soldier|||HumanSoldierToken| +|Generate|TOK:THB|Kraken|||KrakenHexproofToken| +|Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken| +|Generate|TOK:THB|Pegasus|||PegasusToken2| +|Generate|TOK:THB|Reflection|||ReflectionBlueToken| |Generate|TOK:THB|Satyr|||SatyrCantBlockToken| |Generate|TOK:THB|Spider|||SpiderToken| -|Generate|TOK:THB|Wolf|||WolfToken| -|Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken| -|Generate|TOK:THB|Gold|||GoldToken| +|Generate|TOK:THB|Tentacle|||TentacleToken| |Generate|TOK:THB|Wall|||ArtifactWallToken| +|Generate|TOK:THB|Wolf|||WolfToken| +|Generate|TOK:THB|Zombie|||ZombieToken| # IKO |Generate|TOK:IKO|Beast|||BeastToken| -|Generate|TOK:IKO|Cat Bird|||CatBirdToken| |Generate|TOK:IKO|Cat|||CatToken2| -|Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken| +|Generate|TOK:IKO|Cat Bird|||CatBirdToken| |Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken| +|Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken| |Generate|TOK:IKO|Feather|||FeatherToken| |Generate|TOK:IKO|Human Soldier|1||HumanSoldierToken| |Generate|TOK:IKO|Human Soldier|2||HumanSoldierToken| @@ -1055,8 +1210,8 @@ |Generate|TOK:ZNR|Cat|||CatToken3| |Generate|TOK:ZNR|Cat Beast|||CatBeastToken| |Generate|TOK:ZNR|Construct|||ConstructToken| -|Generate|TOK:ZNR|Goblin Construct|||RelicRobberToken| |Generate|TOK:ZNR|Drake|||DrakeToken| +|Generate|TOK:ZNR|Goblin Construct|||RelicRobberToken| |Generate|TOK:ZNR|Hydra|||GrakmawSkyclaveRavagerHydraToken| |Generate|TOK:ZNR|Illusion|||CustomIllusionToken| |Generate|TOK:ZNR|Insect|||InsectToken| @@ -1064,19 +1219,16 @@ |Generate|TOK:ZNR|Plant|||PlantToken| # ZNC -|Generate|TOK:ZNC|Bird|||BirdToken| |Generate|TOK:ZNC|Beast|||BeastToken2| +|Generate|TOK:ZNC|Bird|||BirdToken| |Generate|TOK:ZNC|Elemental|1||OmnathElementalToken| |Generate|TOK:ZNC|Elemental|2||ZendikarsRoilElementalToken| |Generate|TOK:ZNC|Faerie Rogue|||FaerieRogueToken| -# # OonaQueenFaerieRogueToken is FaerieRogueToken with additional blue color, but ZNC contains only one token - so don't use normal token for it #|Generate|TOK:ZNC|Faerie Rogue|||OonaQueenFaerieRogueToken| -# Germ token uses in chest and antology, but scryfall put it here -|Generate|TOK:ZNC|Phyrexian Germ|||PhyrexianGermToken| -# |Generate|TOK:ZNC|Goblin Rogue|||GoblinRogueToken| |Generate|TOK:ZNC|Kor Ally|||KorAllyToken| +|Generate|TOK:ZNC|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:ZNC|Rat|||RatToken| |Generate|TOK:ZNC|Saproling|||SaprolingToken| |Generate|TOK:ZNC|Thopter|||ThopterColorlessToken| @@ -1090,8 +1242,8 @@ |Generate|TOK:CMR|Elephant|||ElephantToken| |Generate|TOK:CMR|Elf Warrior|||ElfWarriorToken| |Generate|TOK:CMR|Golem|||GolemToken| -|Generate|TOK:CMR|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:CMR|Illusion|||MelokuTheCloudedMirrorToken| +|Generate|TOK:CMR|Phyrexian Horror|||PhyrexianRebirthHorrorToken| |Generate|TOK:CMR|Plant|||PlantToken| |Generate|TOK:CMR|Rock|||RockToken| |Generate|TOK:CMR|Salamander Warrior|||SalamanderWarriorToken| @@ -1151,7 +1303,7 @@ |Generate|TOK:TSR|Soldier|||SoldierToken| |Generate|TOK:TSR|Spider|||PenumbraSpiderToken| -// STX +# STX |Generate|TOK:STX|Avatar|||BloodAvatarToken| |Generate|TOK:STX|Elemental|||Elemental44Token| |Generate|TOK:STX|Fractal|||FractalToken| @@ -1164,8 +1316,6 @@ |Generate|TOK:C21|Beast|1||BeastToken| |Generate|TOK:C21|Beast|2||BeastToken2| |Generate|TOK:C21|Boar|||Boar2Token| -# no need tokens for Eternalize ability, but scryfall have it: https://scryfall.com/card/tc21/6/champion-of-wits -# no need tokens for Copy, but scryfall have it: https://scryfall.com/card/tc21/30/copy |Generate|TOK:C21|Construct|1||MetallurgicSummoningsConstructToken| |Generate|TOK:C21|Construct|2||KarnConstructToken| |Generate|TOK:C21|Demon|||DemonFlyingToken| @@ -1192,7 +1342,7 @@ |Generate|TOK:C21|Wurm|||WurmToken| |Generate|TOK:C21|Zombie|||ZombieToken| -// MH2 +# MH2 |Generate|TOK:MH2|Beast|||BeastToken2| |Generate|TOK:MH2|Bird|||BirdToken| |Generate|TOK:MH2|Clue|1||ClueArtifactToken| @@ -1208,8 +1358,6 @@ |Generate|TOK:MH2|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:MH2|Squirrel|||SquirrelToken| |Generate|TOK:MH2|Thopter|||ThopterColorlessToken| -# no need tokens for Eternalize ability, but scryfall have it: https://scryfall.com/card/tmh2/4/timeless-dragon -# no need tokens for Eternalize ability, but scryfall have it: https://scryfall.com/card/tmh2/5/timeless-witness |Generate|TOK:MH2|Treasure|1||TreasureToken| |Generate|TOK:MH2|Treasure|2||TreasureToken| |Generate|TOK:MH2|Zombie|||ZombieToken| @@ -1235,7 +1383,6 @@ # AFC |Generate|TOK:AFC|Angel|||AngelToken| |Generate|TOK:AFC|Beast|||BeastToken| -# no need tokens for Eternalize ability, but scryfall have it: https://scryfall.com/card/tafc/4/champion-of-wits |Generate|TOK:AFC|Clue|||ClueArtifactToken| |Generate|TOK:AFC|Dragon|||DragonToken2| |Generate|TOK:AFC|Dragon Spirit|||VrondissRageOfAncientsToken| @@ -1357,18 +1504,45 @@ |Generate|TOK:NEC|Thopter|||ThopterColorlessToken| # SLD +|Generate|TOK:SLD|Angel|||AngelToken| +|Generate|TOK:SLD|Cat|1||GreenCatToken| +|Generate|TOK:SLD|Cat|2||CatToken2| +|Generate|TOK:SLD|Cat|3||CatToken2| |Generate|TOK:SLD|Clue|||ClueArtifactToken| +|Generate|TOK:SLD|Dog|||WhiteDogToken| +|Generate|TOK:SLD|Egg|||AtlaPalaniToken| |Generate|TOK:SLD|Faerie Rogue|1||FaerieRogueToken| |Generate|TOK:SLD|Faerie Rogue|2||FaerieRogueToken| |Generate|TOK:SLD|Faerie Rogue|3||FaerieRogueToken| |Generate|TOK:SLD|Faerie Rogue|4||FaerieRogueToken| -|Generate|TOK:SLD|Treasure|||TreasureToken| +|Generate|TOK:SLD|Food|1||FoodToken| +|Generate|TOK:SLD|Food|2||FoodToken| +|Generate|TOK:SLD|Food|3||FoodToken| +|Generate|TOK:SLD|Food|4||FoodToken| +|Generate|TOK:SLD|Food|5||FoodToken| +|Generate|TOK:SLD|Goblin|||GoblinToken| +|Generate|TOK:SLD|Hydra|||ZaxaraTheExemplaryHydraToken| +|Generate|TOK:SLD|Icingdeath, Frost Tongue|||IcingdeathFrostTongueToken| +|Generate|TOK:SLD|Marit Lage|||MaritLageToken| +|Generate|TOK:SLD|Mechtitan|||MechtitanToken| +|Generate|TOK:SLD|Saproling|||SaprolingToken| +|Generate|TOK:SLD|Shrine|||ShrineToken| +|Generate|TOK:SLD|Spirit|1||SpiritWhiteToken| +|Generate|TOK:SLD|Spirit|2||SpiritToken| +|Generate|TOK:SLD|Squirrel|||SquirrelToken| +|Generate|TOK:SLD|Treasure|1||TreasureToken| +|Generate|TOK:SLD|Treasure|2||TreasureToken| +|Generate|TOK:SLD|Treasure|3||TreasureToken| +|Generate|TOK:SLD|Treasure|4||TreasureToken| |Generate|TOK:SLD|Walker|1||WalkerToken| |Generate|TOK:SLD|Walker|2||WalkerToken| |Generate|TOK:SLD|Walker|3||WalkerToken| |Generate|TOK:SLD|Walker|4||WalkerToken| |Generate|TOK:SLD|Walker|5||WalkerToken| -# TODO: Add new SLD token images +|Generate|TOK:SLD|Warrior|||WarriorToken| +|Generate|TOK:SLD|Wolf|||WolfToken| +|Generate|TOK:SLD|Wurm|||WurmWithTrampleToken| +|Generate|TOK:SLD|Zombie|||ZombieToken| # 2XM |Generate|TOK:2XM|Angel|||AngelToken| @@ -1381,13 +1555,15 @@ |Generate|TOK:2XM|Elemental|||VoiceOfResurgenceToken| |Generate|TOK:2XM|Elephant|||ElephantToken| |Generate|TOK:2XM|Elf Warrior|||GreenWhiteElfWarriorToken| -|Generate|TOK:2XM|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:2XM|Golem|||GolemToken| |Generate|TOK:2XM|Human Soldier|||HumanSoldierToken| |Generate|TOK:2XM|Marit Lage|||MaritLageToken| |Generate|TOK:2XM|Myr|||MyrToken| -|Generate|TOK:2XM|Phyrexian Myr|||BrudicladTelchorMyrToken| |Generate|TOK:2XM|Ooze|||OozeToken| +|Generate|TOK:2XM|Phyrexian Germ|||PhyrexianGermToken| +|Generate|TOK:2XM|Phyrexian Myr|||BrudicladTelchorMyrToken| +|Generate|TOK:2XM|Phyrexian Wurm|1||WurmWithDeathtouchToken| +|Generate|TOK:2XM|Phyrexian Wurm|2||WurmWithLifelinkToken| |Generate|TOK:2XM|Plant|||PlantToken| |Generate|TOK:2XM|Saproling|||SaprolingToken| |Generate|TOK:2XM|Servo|||ServoToken| @@ -1399,8 +1575,6 @@ |Generate|TOK:2XM|Treasure|||TreasureToken| |Generate|TOK:2XM|Tuktuk the Returned|||TuktukTheReturnedToken| |Generate|TOK:2XM|Wolf|||WolfToken| -|Generate|TOK:2XM|Phyrexian Wurm|1||WurmWithDeathtouchToken| -|Generate|TOK:2XM|Phyrexian Wurm|2||WurmWithLifelinkToken| # SNC |Generate|TOK:SNC|Angel|||Angel33Token| @@ -1482,20 +1656,20 @@ # CM2 |Generate|TOK:CM2|Bird|||BirdToken| |Generate|TOK:CM2|Elemental Shaman|||ElementalShamanToken| -|Generate|TOK:CM2|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:CM2|Goat|||GoatToken| |Generate|TOK:CM2|Goblin|||GoblinToken| |Generate|TOK:CM2|Knight|||HuntedDragonKnightToken| |Generate|TOK:CM2|Lightning Rager|||LightningRagerToken| |Generate|TOK:CM2|Myr|||MyrToken| |Generate|TOK:CM2|Pentavite|||PentaviteToken| +|Generate|TOK:CM2|Phyrexian Germ|||PhyrexianGermToken| +|Generate|TOK:CM2|Phyrexian Wurm|1||WurmWithDeathtouchToken| +|Generate|TOK:CM2|Phyrexian Wurm|2||WurmWithLifelinkToken| |Generate|TOK:CM2|Saproling|||SaprolingToken| |Generate|TOK:CM2|Shapeshifter|||CribSwapShapeshifterWhiteToken| |Generate|TOK:CM2|Spirit|||SpiritWhiteToken| |Generate|TOK:CM2|Triskelavite|||TriskelaviteToken| |Generate|TOK:CM2|Tuktuk the Returned|||TuktukTheReturnedToken| -|Generate|TOK:CM2|Phyrexian Wurm|1||WurmWithDeathtouchToken| -|Generate|TOK:CM2|Phyrexian Wurm|2||WurmWithLifelinkToken| |Generate|TOK:CM2|Zombie|||ZombieToken| # PCA @@ -1507,13 +1681,13 @@ |Generate|TOK:PCA|Eldrazi Spawn|1||EldraziSpawnToken| |Generate|TOK:PCA|Eldrazi Spawn|2||EldraziSpawnToken| |Generate|TOK:PCA|Eldrazi Spawn|3||EldraziSpawnToken| -|Generate|TOK:PCA|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:PCA|Goat|||GoatToken| |Generate|TOK:PCA|Goblin|||GoblinToken| |Generate|TOK:PCA|Hellion|||HellionToken| |Generate|TOK:PCA|Insect|||InsectToken| |Generate|TOK:PCA|Ooze|1||OozeToken| |Generate|TOK:PCA|Ooze|2||MitoticSlimeOozeToken| +|Generate|TOK:PCA|Phyrexian Germ|||PhyrexianGermToken| |Generate|TOK:PCA|Plant|||PlantToken| |Generate|TOK:PCA|Saproling|||SaprolingToken| |Generate|TOK:PCA|Spider|||PenumbraSpiderToken| @@ -1533,11 +1707,11 @@ |Generate|TOK:V16|Marit Lage|||MaritLageToken| # DDR -|Generate|TOK:DDR|Eldrazi Scion|||EldraziScionToken| |Generate|TOK:DDR|Demon|||DemonToken| -|Generate|TOK:DDR|Zombie Giant|||QuestForTheGravelordZombieToken| +|Generate|TOK:DDR|Eldrazi Scion|||EldraziScionToken| |Generate|TOK:DDR|Elemental|||Elemental44GreenToken| |Generate|TOK:DDR|Plant|||PlantToken| +|Generate|TOK:DDR|Zombie Giant|||QuestForTheGravelordZombieToken| # DDS |Generate|TOK:DDS|Beast|||BeastToken2| @@ -2059,10 +2233,51 @@ |Generate|TOK:WOC|Virtuous|||VirtuousRoleToken| # WHO +|Generate|TOK:WHO|Alien|||AlienToken| |Generate|TOK:WHO|Alien Insect|||AlienInsectToken| +|Generate|TOK:WHO|Alien Salamander|||AlienSalamanderToken| +|Generate|TOK:WHO|Alien Warrior|||AlienWarriorToken| +|Generate|TOK:WHO|Beast|||BeastToken| +|Generate|TOK:WHO|Clue|1||ClueToken| +|Generate|TOK:WHO|Clue|2||ClueToken| +|Generate|TOK:WHO|Clue|3||ClueToken| +|Generate|TOK:WHO|Dalek|||DalekToken| +|Generate|TOK:WHO|Dinosaur|||DinosaurFlyingHasteToken| +|Generate|TOK:WHO|Fish|||FishNoAbilityToken| +|Generate|TOK:WHO|Food|1||FoodToken| +|Generate|TOK:WHO|Food|2||FoodToken| +|Generate|TOK:WHO|Food|3||FoodToken| +|Generate|TOK:WHO|Horse|||TheGirlInTheFireplaceHorseToken| +|Generate|TOK:WHO|Human|||TheEleventhHourToken| +|Generate|TOK:WHO|Human Noble|||TheGirlInTheFireplaceHumanNobleToken| +|Generate|TOK:WHO|Mark of the Rani|||MarkOfTheRaniToken| +|Generate|TOK:WHO|Soldier|||SoldierToken| +|Generate|TOK:WHO|Treasure|1||TreasureToken| +|Generate|TOK:WHO|Treasure|2||TreasureToken| +|Generate|TOK:WHO|Treasure|3||TreasureToken| +|Generate|TOK:WHO|Treasure|4||TreasureToken| +|Generate|TOK:WHO|Warrior|||WarriorToken| # PIP +|Generate|TOK:PIP|Alien|||Alien00Token| +|Generate|TOK:PIP|Clue|||ClueToken| +|Generate|TOK:PIP|Food|1||FoodToken| +|Generate|TOK:PIP|Food|2||FoodToken| +|Generate|TOK:PIP|Food|3||FoodToken| +|Generate|TOK:PIP|Human Knight|||ThePrydwenSteelFlagshipHumanKnightToken| +|Generate|TOK:PIP|Human Soldier|||HumanSoldierToken| +|Generate|TOK:PIP|Junk|||JunkToken| |Generate|TOK:PIP|Robot|||RobotToken| +|Generate|TOK:PIP|Settlement|||SettlementToken| +|Generate|TOK:PIP|Soldier|1||SoldierTokenWithHaste| +|Generate|TOK:PIP|Soldier|2||SoldierToken| +|Generate|TOK:PIP|Squirrel|||SquirrelToken| +|Generate|TOK:PIP|Thopter|||ThopterToken| +|Generate|TOK:PIP|Treasure|1||TreasureToken| +|Generate|TOK:PIP|Treasure|2||TreasureToken| +|Generate|TOK:PIP|Warrior|||WarriorToken| +|Generate|TOK:PIP|Wasteland Survival Guide|||WastelandSurvivalGuideToken| +|Generate|TOK:PIP|Zombie Mutant|||ZombieMutantToken| # LCI |Generate|TOK:LCI|Angel|||AngelVigilanceToken| @@ -2501,18 +2716,25 @@ # TDC |Generate|TOK:TDC|Angel|||AngelVigilanceToken| +|Generate|TOK:TDC|Beast|||BeastToken| |Generate|TOK:TDC|Citizen|||CitizenGreenWhiteToken| |Generate|TOK:TDC|Dog|||WhiteDogToken| +|Generate|TOK:TDC|Dragon|1||DragonEggDragonToken| +|Generate|TOK:TDC|Dragon|2||DragonToken2| +|Generate|TOK:TDC|Dragon Egg|||NestingDragonToken| |Generate|TOK:TDC|Dragon Illusion|||DragonIllusionToken| |Generate|TOK:TDC|Eldrazi|||EldraziToken| |Generate|TOK:TDC|Elemental|1||RedElementalToken| |Generate|TOK:TDC|Elemental|2||Elemental11HasteToken| |Generate|TOK:TDC|Elemental|3||Elemental44Token| |Generate|TOK:TDC|First Mate Ragavan|||FirstMateRagavanToken| +|Generate|TOK:TDC|Frog Lizard|||FrogLizardToken| |Generate|TOK:TDC|Goat|||GoatToken| |Generate|TOK:TDC|Gold|||GoldToken| |Generate|TOK:TDC|Human|||HumanToken| +|Generate|TOK:TDC|Inkling|||InklingToken| |Generate|TOK:TDC|Insect|||InsectToken| +|Generate|TOK:TDC|Karox Bladewing|||KaroxBladewingToken| |Generate|TOK:TDC|Myr|||MyrToken| |Generate|TOK:TDC|Plant|||PlantToken| |Generate|TOK:TDC|Rat|||RatToken| @@ -2520,5 +2742,104 @@ |Generate|TOK:TDC|Servo|||ServoToken| |Generate|TOK:TDC|Snake|||OphiomancerSnakeToken| |Generate|TOK:TDC|Soldier|||SoldierArtifactToken| +|Generate|TOK:TDC|Spider|||SpiderToken| |Generate|TOK:TDC|Spirit|||SpiritWhiteToken| |Generate|TOK:TDC|Thopter|||ThopterColorlessToken| + +# ACR +|Generate|TOK:ACR|Assassin|||AssassinMenaceToken| +|Generate|TOK:ACR|Human Rogue|||HumanRogueToken| +|Generate|TOK:ACR|Phobos|||PhobosToken| +|Generate|TOK:ACR|Shapeshifter|||Shapeshifter32Token| +|Generate|TOK:ACR|Treasure|||TreasureToken| + +# DD2 +|Generate|TOK:DD2|Elemental Shaman|||ElementalShamanToken| + +# DSK +|Generate|TOK:DSK|Beast|||BeastieToken| +|Generate|TOK:DSK|Everywhere|||EverywhereToken| +|Generate|TOK:DSK|Glimmer|||GlimmerToken| +|Generate|TOK:DSK|Gremlin|||Gremlin11Token| +|Generate|TOK:DSK|Insect|1||InsectBlackGreenFlyingToken| +|Generate|TOK:DSK|Insect|2||InsectWhiteToken| +|Generate|TOK:DSK|Primo, the Indivisible|||PrimoTheIndivisibleToken| +|Generate|TOK:DSK|Shard|||ShardToken| +|Generate|TOK:DSK|Spider|||Spider22Token| +|Generate|TOK:DSK|Spirit|||SpiritBlueToken| +|Generate|TOK:DSK|Treasure|||TreasureToken| + +# FIN +|Generate|TOK:FIN|Food|||FoodToken| + +# JVC +|Generate|TOK:JVC|Elemental Shaman|||ElementalShamanToken| + +# REX +|Generate|TOK:REX|Dinosaur|||DinosaurToken| +|Generate|TOK:REX|Treasure|||TreasureToken| + +# UGL +|Generate|TOK:UGL|Goblin|||GoblinToken| +|Generate|TOK:UGL|Pegasus|||PegasusToken| +|Generate|TOK:UGL|Soldier|||SoldierToken| +|Generate|TOK:UGL|Squirrel|||SquirrelToken| +|Generate|TOK:UGL|Zombie|||ZombieToken| + +# F12 +|Generate|TOK:F12|Human|||HumanToken| +|Generate|TOK:F12|Wolf|||WolfToken| + +# F17 +|Generate|TOK:F17|Dinosaur|||DinosaurToken| +|Generate|TOK:F17|Pirate|||PirateToken| +|Generate|TOK:F17|Treasure|1||TreasureToken| +|Generate|TOK:F17|Treasure|2||TreasureToken| +|Generate|TOK:F17|Treasure|3||TreasureToken| +|Generate|TOK:F17|Vampire|||IxalanVampireToken| + +# HHO +|Generate|TOK:HHO|Treasure|||TreasureToken| + +# J12 +|Generate|TOK:J12|Centaur|||CentaurToken| + +# J13 +|Generate|TOK:J13|Golem|||HammerOfPurphorosGolemToken| + +# MPR +|Generate|TOK:MPR|Bear|||BearToken| +|Generate|TOK:MPR|Beast|||BeastToken2| +|Generate|TOK:MPR|Bird|||BlueBirdToken| +|Generate|TOK:MPR|Elephant|||ElephantToken| +|Generate|TOK:MPR|Goblin Soldier|||GoblinSoldierToken| +|Generate|TOK:MPR|Saproling|||SaprolingToken| +|Generate|TOK:MPR|Spirit|||SpiritWhiteToken| + +# P03 +|Generate|TOK:P03|Bear|||BearToken| +|Generate|TOK:P03|Bird|||RukhEggBirdToken| +|Generate|TOK:P03|Demon|||DemonFlyingToken| +|Generate|TOK:P03|Goblin|||GoblinToken| +|Generate|TOK:P03|Insect|||InsectToken| +|Generate|TOK:P03|Sliver|||SliverToken| + +# P04 +|Generate|TOK:P04|Angel|||AngelToken| +|Generate|TOK:P04|Beast|||BeastToken| +|Generate|TOK:P04|Myr|||MyrToken| +|Generate|TOK:P04|Pentavite|||PentaviteToken| +|Generate|TOK:P04|Spirit|||SpiritToken| + +# PEMN +|Generate|TOK:PEMN|Zombie|1||ZombieToken| +|Generate|TOK:PEMN|Zombie|2||ZombieToken| + +# PHEL +|Generate|TOK:PHEL|Angel|||AngelToken| + +# PL21 +|Generate|TOK:PL21|Minotaur|||MinotaurToken| + +# PL23 +|Generate|TOK:PL23|Food|||FoodToken|