From 898952515e1270544b0d1f7ba63e81f97d7f2dab Mon Sep 17 00:00:00 2001 From: ssk97 Date: Tue, 5 Dec 2023 17:27:49 -0800 Subject: [PATCH] [LCI] Implement Intrepid Paleontologist (#11508) * Improve some "mana spent -> give effect to permanent spell" cards make effect source be the SpellAbility itself * Implement Intrepid Paleontologist and tests * Use a common class for the "specific MOR ETBs with counter" effect * Prevent casting non-owned dinosuars * Rename AddCounterEnteringMOR to AddCounterEnteringCreature * fixes from review --- Mage.Sets/src/mage/cards/b/Biophagus.java | 74 ++-------- .../src/mage/cards/g/GuildmagesForum.java | 68 ++------- .../src/mage/cards/h/HallOfTheBanditLord.java | 10 +- .../mage/cards/i/IntrepidPaleontologist.java | 131 ++++++++++++++++++ .../src/mage/sets/TheLostCavernsOfIxalan.java | 1 + .../lci/IntrepidPaleontologistTest.java | 97 +++++++++++++ .../watchers/HallOfTheBanditLordTest.java | 26 ++++ Mage/src/main/java/mage/MageIdentifier.java | 1 + .../AddCounterEnteringCreatureEffect.java | 70 ++++++++++ 9 files changed, 349 insertions(+), 129 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/i/IntrepidPaleontologist.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/lci/IntrepidPaleontologistTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterEnteringCreatureEffect.java diff --git a/Mage.Sets/src/mage/cards/b/Biophagus.java b/Mage.Sets/src/mage/cards/b/Biophagus.java index 0b6a5da50cb..1f6cd3ffb9a 100644 --- a/Mage.Sets/src/mage/cards/b/Biophagus.java +++ b/Mage.Sets/src/mage/cards/b/Biophagus.java @@ -1,20 +1,18 @@ package mage.cards.b; import mage.MageInt; -import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.counters.CounterType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.watchers.Watcher; @@ -36,7 +34,7 @@ public final class Biophagus extends CardImpl { Ability ability = new AnyColorManaAbility(new TapSourceCost(), true).withFlavorWord("Genomic Enhancement"); ability.getEffects().get(0).setText("Add one mana of any color. If this mana is spent to cast a creature spell, " + "that creature enters the battlefield with an additional +1/+1 counter on it."); - this.addAbility(ability, new BiophagusWatcher(ability.getId())); + this.addAbility(ability, new BiophagusWatcher()); } private Biophagus(final Biophagus card) { @@ -50,69 +48,19 @@ public final class Biophagus extends CardImpl { } class BiophagusWatcher extends Watcher { - - private final UUID sourceAbilityID; - - BiophagusWatcher(UUID sourceAbilityID) { + BiophagusWatcher() { super(WatcherScope.CARD); - this.sourceAbilityID = sourceAbilityID; } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAID) { - MageObject target = game.getObject(event.getTargetId()); - if (event.getSourceId() != null - && event.getSourceId().equals(this.getSourceId()) - && target != null && target.isCreature(game) - && event.getFlag()) { - if (target instanceof Spell) { - game.getState().addEffect(new BiophagusEntersBattlefieldEffect( - new MageObjectReference(((Spell) target).getSourceId(), target.getZoneChangeCounter(game), game)), - game.getAbility(sourceAbilityID, this.getSourceId()).orElse(null)); //null will cause an immediate crash - } + Spell target = game.getSpell(event.getTargetId()); + if (event.getSourceId() != null && event.getSourceId().equals(this.getSourceId()) + && target != null && target.isCreature(game) && event.getFlag()) { + game.getState().addEffect(new AddCounterEnteringCreatureEffect(new MageObjectReference(target.getCard(), game)), + target.getSpellAbility()); } } } } - -class BiophagusEntersBattlefieldEffect extends ReplacementEffectImpl { - - private final MageObjectReference mor; - - public BiophagusEntersBattlefieldEffect(MageObjectReference mor) { - super(Duration.EndOfTurn, Outcome.BoostCreature); - this.staticText = "If that mana is spent on a multicolored creature spell, that creature enters the battlefield with an additional +1/+1 counter on it"; - this.mor = mor; - } - - private BiophagusEntersBattlefieldEffect(final BiophagusEntersBattlefieldEffect effect) { - super(effect); - this.mor = effect.mor; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && mor.refersTo(permanent, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); - } - return false; - } - - @Override - public BiophagusEntersBattlefieldEffect copy() { - return new BiophagusEntersBattlefieldEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GuildmagesForum.java b/Mage.Sets/src/mage/cards/g/GuildmagesForum.java index 18241cbeb89..68542dda2b4 100644 --- a/Mage.Sets/src/mage/cards/g/GuildmagesForum.java +++ b/Mage.Sets/src/mage/cards/g/GuildmagesForum.java @@ -1,28 +1,23 @@ package mage.cards.g; -import java.util.UUID; -import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.WatcherScope; -import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.UUID; + /** * * @author LevelX2 @@ -39,7 +34,7 @@ public final class GuildmagesForum extends CardImpl { Ability ability = new AnyColorManaAbility(new GenericManaCost(1), true); ability.getEffects().get(0).setText("Add one mana of any color. If that mana is spent on a multicolored creature spell, that creature enters the battlefield with an additional +1/+1 counter on it"); ability.addCost(new TapSourceCost()); - this.addAbility(ability, new GuildmagesForumWatcher(ability.getId())); + this.addAbility(ability, new GuildmagesForumWatcher()); } private GuildmagesForum(final GuildmagesForum card) { @@ -54,69 +49,22 @@ public final class GuildmagesForum extends CardImpl { class GuildmagesForumWatcher extends Watcher { - private final UUID sourceAbilityID; - GuildmagesForumWatcher(UUID sourceAbilityID) { + GuildmagesForumWatcher() { super(WatcherScope.CARD); - this.sourceAbilityID = sourceAbilityID; } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAID) { - MageObject target = game.getObject(event.getTargetId()); + Spell target = game.getSpell(event.getTargetId()); if (event.getSourceId() != null && event.getSourceId().equals(this.getSourceId()) && target != null && target.isCreature(game) && target.getColor(game).isMulticolored() && event.getFlag()) { - if (target instanceof Spell) { - game.getState().addEffect(new GuildmagesForumEntersBattlefieldEffect( - new MageObjectReference(((Spell) target).getSourceId(), target.getZoneChangeCounter(game), game)), - game.getAbility(sourceAbilityID, this.getSourceId()).orElse(null)); //null will cause an immediate crash - } + game.getState().addEffect(new AddCounterEnteringCreatureEffect(new MageObjectReference(target.getCard(), game)), + target.getSpellAbility()); } } } - -} - -class GuildmagesForumEntersBattlefieldEffect extends ReplacementEffectImpl { - - private final MageObjectReference mor; - - public GuildmagesForumEntersBattlefieldEffect(MageObjectReference mor) { - super(Duration.EndOfTurn, Outcome.BoostCreature); - this.staticText = "If that mana is spent on a multicolored creature spell, that creature enters the battlefield with an additional +1/+1 counter on it"; - this.mor = mor; - } - - private GuildmagesForumEntersBattlefieldEffect(final GuildmagesForumEntersBattlefieldEffect effect) { - super(effect); - this.mor = effect.mor; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && mor.refersTo(permanent, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); - } - return false; - } - - @Override - public GuildmagesForumEntersBattlefieldEffect copy() { - return new GuildmagesForumEntersBattlefieldEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java b/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java index 2ef0ebce41f..ac0fd77d93d 100644 --- a/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java +++ b/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java @@ -2,7 +2,6 @@ package mage.cards.h; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.UUID; import mage.MageObject; import mage.Mana; @@ -46,7 +45,7 @@ public final class HallOfTheBanditLord extends CardImpl { effect.setText("Add {C}. If that mana is spent on a creature spell, it gains haste"); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); ability.addCost(new PayLifeCost(3)); - this.addAbility(ability, new HallOfTheBanditLordWatcher(ability.getId())); + this.addAbility(ability, new HallOfTheBanditLordWatcher()); } private HallOfTheBanditLord(final HallOfTheBanditLord card) { @@ -61,12 +60,10 @@ public final class HallOfTheBanditLord extends CardImpl { class HallOfTheBanditLordWatcher extends Watcher { - private final UUID sourceAbilityID; private final List creatures = new ArrayList<>(); - HallOfTheBanditLordWatcher(UUID sourceAbilityID) { + HallOfTheBanditLordWatcher() { super(WatcherScope.CARD); - this.sourceAbilityID = sourceAbilityID; } @Override @@ -100,7 +97,8 @@ class HallOfTheBanditLordWatcher extends Watcher { if (creatures.contains(event.getSourceId())) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - game.addEffect(effect, game.getAbility(sourceAbilityID, this.getSourceId()).orElse(null)); //null will cause an immediate crash + Ability source = game.getPermanent(event.getSourceId()).getSpellAbility(); + game.addEffect(effect, source); creatures.remove(event.getSourceId()); } } diff --git a/Mage.Sets/src/mage/cards/i/IntrepidPaleontologist.java b/Mage.Sets/src/mage/cards/i/IntrepidPaleontologist.java new file mode 100644 index 00000000000..159c0d59f8b --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntrepidPaleontologist.java @@ -0,0 +1,131 @@ +package mage.cards.i; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.UUID; + +/** + * + * @author notgreat + */ +public final class IntrepidPaleontologist extends CardImpl { + + public IntrepidPaleontologist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {2}: Exile target card from a graveyard. + Ability exileAbility = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new GenericManaCost(2)); + exileAbility.addTarget(new TargetCardInGraveyard()); + this.addAbility(exileAbility); + + // You may cast Dinosaur creature spells from among cards you own exiled with Intrepid Paleontologist. If you cast a spell this way, that creature enters the battlefield with a finality counter on it. + Ability castAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new IntrepidPaleontologistPlayEffect()); + castAbility.setIdentifier(MageIdentifier.IntrepidPaleontologistWatcher); + castAbility.addWatcher(new IntrepidPaleontologistWatcher()); + this.addAbility(castAbility); + } + + private IntrepidPaleontologist(final IntrepidPaleontologist card) { + super(card); + } + + @Override + public IntrepidPaleontologist copy() { + return new IntrepidPaleontologist(this); + } +} + +class IntrepidPaleontologistPlayEffect extends AsThoughEffectImpl { + + public IntrepidPaleontologistPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "You may cast Dinosaur creature spells from among cards you own exiled with {this}. If you cast a spell this way, that creature enters the battlefield with a finality counter on it."; + } + + private IntrepidPaleontologistPlayEffect(final IntrepidPaleontologistPlayEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public IntrepidPaleontologistPlayEffect copy() { + return new IntrepidPaleontologistPlayEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + if (playerId.equals(source.getControllerId())) { + Card card = game.getCard(objectId); + MageObject sourceObject = game.getObject(source); + if (card != null && sourceObject != null && affectedAbility instanceof SpellAbility) { + Card characteristics = ((SpellAbility)affectedAbility).getCharacteristics(game); + if (card.getOwnerId().equals(playerId) && characteristics.isCreature(game) && characteristics.hasSubtype(SubType.DINOSAUR, game)) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)); + ExileZone exileZone = game.getState().getExile().getExileZone(exileId); + return exileZone != null && exileZone.contains(objectId); + } + } + } + return false; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return false; + } +} + +class IntrepidPaleontologistWatcher extends Watcher { + IntrepidPaleontologistWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (GameEvent.EventType.SPELL_CAST.equals(event.getType()) + && event.hasApprovingIdentifier(MageIdentifier.IntrepidPaleontologistWatcher)) { + Spell target = game.getSpell(event.getTargetId()); + if (target != null) { + game.getState().addEffect(new AddCounterEnteringCreatureEffect(new MageObjectReference(target.getCard(), game), + CounterType.FINALITY.createInstance(), Outcome.UnboostCreature), + target.getSpellAbility()); + } + } + } +} diff --git a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java index 8b1e13882d5..e6f7a0bd76f 100644 --- a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java +++ b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java @@ -176,6 +176,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet { cards.add(new SetCardInfo("Idol of the Deep King", 155, Rarity.COMMON, mage.cards.i.IdolOfTheDeepKing.class)); cards.add(new SetCardInfo("In the Presence of Ages", 192, Rarity.COMMON, mage.cards.i.InThePresenceOfAges.class)); cards.add(new SetCardInfo("Inti, Seneschal of the Sun", 156, Rarity.RARE, mage.cards.i.IntiSeneschalOfTheSun.class)); + cards.add(new SetCardInfo("Intrepid Paleontologist", 193, Rarity.RARE, mage.cards.i.IntrepidPaleontologist.class)); cards.add(new SetCardInfo("Inverted Iceberg", 60, Rarity.COMMON, mage.cards.i.InvertedIceberg.class)); cards.add(new SetCardInfo("Ironpaw Aspirant", 18, Rarity.COMMON, mage.cards.i.IronpawAspirant.class)); cards.add(new SetCardInfo("Island", 288, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_UST_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/IntrepidPaleontologistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/IntrepidPaleontologistTest.java new file mode 100644 index 00000000000..713f29771e0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/IntrepidPaleontologistTest.java @@ -0,0 +1,97 @@ +package org.mage.test.cards.single.lci; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author notgreat + */ +public class IntrepidPaleontologistTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.i.IntrepidPaleontologist}
+ * Intrepid Paleontologist {1}{G}
+ * Creature - Human Druid

+ * {T}: Add one mana of any color.

+ * {2}: Exile target card from a graveyard.

+ * You may cast Dinosaur creature spells from among cards you own exiled with Intrepid Paleontologist. + * If you cast a spell this way, that creature enters the battlefield with a finality counter on it. + **/ + private static final String paleo = "Intrepid Paleontologist"; + private static final String dino = "Frenzied Raptor"; + private static final String dino2 = "Scytheclaw Raptor"; + + // You can't cast an enemy dinosaur, but can cast your own and that the dino enters with the finality counter + @Test + public void test_basic() { + addCard(Zone.BATTLEFIELD, playerA, paleo); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2+2+3); + addCard(Zone.GRAVEYARD, playerA, dino); + addCard(Zone.GRAVEYARD, playerB, dino2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: ",dino); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: ",dino2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("Can't cast enemy-owned dino", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast "+dino2, false); + checkPlayableAbility("Can cast own dino", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast "+dino, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dino, true); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, dino, 1); + assertCounterCount(dino, CounterType.FINALITY, 1); + assertExileCount(dino2, 1); + } + // Make sure that re-casting the dino does not come with the counter again + @Test + public void test_recast() { + addCard(Zone.BATTLEFIELD, playerA, paleo); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 2+3+1+3); + addCard(Zone.HAND, playerA, "Unsummon", 1); + addCard(Zone.GRAVEYARD, playerA, dino); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: ",dino); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dino, true); + + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Unsummon", dino,true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, dino, true); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, dino, 1); + assertCounterCount(dino, CounterType.FINALITY, 0); + } + + // Make sure that having two paleos out doesn't break anything + @Test + public void test_double() { + addCard(Zone.BATTLEFIELD, playerA, paleo, 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", (2+3)*2); + addCard(Zone.GRAVEYARD, playerA, dino); + addCard(Zone.GRAVEYARD, playerA, dino2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: ",dino); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: ",dino2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dino, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dino2, true); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, dino, 1); + assertPermanentCount(playerA, dino2, 1); + assertCounterCount(dino, CounterType.FINALITY, 1); + assertCounterCount(dino2, CounterType.FINALITY, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java index 70eba9adbb1..0e8f7bebef8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java @@ -50,4 +50,30 @@ public class HallOfTheBanditLordTest extends CardTestPlayerBase { this.assertAbility(playerA, "Ember Hauler", HasteAbility.getInstance(), false); } + //Test that a copied Hall of the Bandit Lord works properly + @Test + public void testGainsHasteCopied() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Hall of the Bandit Lord"); + addCard(Zone.BATTLEFIELD, playerA, "Mirror Gallery"); + addCard(Zone.HAND, playerA, "Vesuva"); + addCard(Zone.HAND, playerA, "Goblin Piker"); // Creature 2/1 - {1}{R} + addCard(Zone.HAND, playerA, "Feral Maaka"); // Creature 2/2 - {1}{R} + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva"); + setChoice(playerA, true); + setChoice(playerA, "Hall of the Bandit Lord"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Piker"); + playLand(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Feral Maaka"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + + this.assertAbility(playerA, "Goblin Piker", HasteAbility.getInstance(), true); + this.assertAbility(playerA, "Feral Maaka", HasteAbility.getInstance(), true); + } } diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index 1e3fa947d61..4db6d63ec78 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -24,6 +24,7 @@ public enum MageIdentifier { GisaAndGeralfWatcher, DanithaNewBenaliasLightWatcher, HaukensInsightWatcher, + IntrepidPaleontologistWatcher, KaradorGhostChieftainWatcher, KessDissidentMageWatcher, MuldrothaTheGravetideWatcher, diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterEnteringCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterEnteringCreatureEffect.java new file mode 100644 index 00000000000..80fa22f5777 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterEnteringCreatureEffect.java @@ -0,0 +1,70 @@ +package mage.abilities.effects.common.counter; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author notgreat + */ +public class AddCounterEnteringCreatureEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + private final Counter counters; + + public AddCounterEnteringCreatureEffect(MageObjectReference mor, Counter counters, Outcome outcome, String text) { + super(Duration.EndOfTurn, outcome); + this.staticText = "That creature enters the battlefield with " + text + " on it"; + this.mor = mor; + this.counters = counters; + } + public AddCounterEnteringCreatureEffect(MageObjectReference mor, Counter counters, Outcome outcome){ + this(mor, counters, outcome, counters.getDescription()); + } + + /** + * defaults to a single +1/+1 counter + */ + public AddCounterEnteringCreatureEffect(MageObjectReference mor){ + this(mor, CounterType.P1P1.createInstance(), Outcome.BoostCreature, "an additional +1/+1 counter"); + } + + private AddCounterEnteringCreatureEffect(final AddCounterEnteringCreatureEffect effect) { + super(effect); + this.mor = effect.mor; + this.counters = effect.counters; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + return permanent != null && mor.refersTo(permanent, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + if (target != null) { + target.addCounters(counters, source.getControllerId(), source, game, event.getAppliedEffects()); + } + return false; + } + + @Override + public AddCounterEnteringCreatureEffect copy() { + return new AddCounterEnteringCreatureEffect(this); + } +}