diff --git a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java index af1641e22af..edf24e4da50 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java +++ b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -11,21 +9,26 @@ import mage.abilities.effects.common.DamageEverythingEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class AshlingThePilgrim extends CardImpl { public AshlingThePilgrim(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.SHAMAN); @@ -34,12 +37,14 @@ public final class AshlingThePilgrim extends CardImpl { this.toughness = new MageInt(1); // {1}{R}: Put a +1/+1 counter on Ashling the Pilgrim. If this is the third time this ability has resolved this turn, remove all +1/+1 counters from Ashling the Pilgrim, and it deals that much damage to each creature and each player. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), new ManaCostsImpl("{1}{R}")); + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{R}") + ); ability.addEffect(new AshlingThePilgrimEffect()); - this.addAbility(ability); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public AshlingThePilgrim(final AshlingThePilgrim card) { + private AshlingThePilgrim(final AshlingThePilgrim card) { super(card); } @@ -51,19 +56,14 @@ public final class AshlingThePilgrim extends CardImpl { class AshlingThePilgrimEffect extends OneShotEffect { - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; - } - - public AshlingThePilgrimEffect() { + AshlingThePilgrimEffect() { super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player"; + this.staticText = "If this is the third time this ability has resolved this turn, " + + "remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player"; } - public AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) { - super(effect); + private AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) { + super(effect); } @Override @@ -75,32 +75,18 @@ class AshlingThePilgrimEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); - } - info.activations++; - if (info.activations == 3) { - int damage = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (damage > 0) { - sourcePermanent.removeCounters(CounterType.P1P1.getName(), damage, game); - return new DamageEverythingEffect(damage, new FilterCreaturePermanent()).apply(game, source); - } - } - return true; + AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); + if (controller == null + || sourcePermanent == null + || watcher == null + || !watcher.checkActivations(source, game)) { + return false; } - return false; + int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); + if (counters < 1) { + return false; + } + sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game); + return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java index 60f71b540e4..86ff0ed941c 100644 --- a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java +++ b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -13,34 +11,37 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class InnerFlameIgniter extends CardImpl { public InnerFlameIgniter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(2); // {2}{R}: Creatures you control get +1/+0 until end of turn. If this is the third time this ability has resolved this turn, creatures you control gain first strike until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1,0,Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")); + Ability ability = new SimpleActivatedAbility( + new BoostControlledEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}") + ); ability.addEffect(new InnerFlameIgniterEffect()); - this.addAbility(ability); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public InnerFlameIgniter(final InnerFlameIgniter card) { + private InnerFlameIgniter(final InnerFlameIgniter card) { super(card); } @@ -52,19 +53,14 @@ public final class InnerFlameIgniter extends CardImpl { class InnerFlameIgniterEffect extends OneShotEffect { - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; + InnerFlameIgniterEffect() { + super(Outcome.AddAbility); + this.staticText = "If this is the third time this ability has resolved this turn, " + + "creatures you control gain first strike until end of turn"; } - public InnerFlameIgniterEffect() { - super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, creatures you control gain first strike until end of turn"; - } - - public InnerFlameIgniterEffect(final InnerFlameIgniterEffect effect) { - super(effect); + private InnerFlameIgniterEffect(final InnerFlameIgniterEffect effect) { + super(effect); } @Override @@ -75,29 +71,16 @@ class InnerFlameIgniterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); - } - info.activations++; - if (info.activations == 3) { - game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source); - } - return true; + AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); + if (controller == null + || watcher == null + || !watcher.checkActivations(source, game)) { + return false; } - return false; + game.addEffect(new GainAbilityControlledEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ), source); + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java index d26f0a5fddb..c4fa17cb3af 100644 --- a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java +++ b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; @@ -13,36 +11,38 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class SoulbrightFlamekin extends CardImpl { public SoulbrightFlamekin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(1); // {2}: Target creature gains trample until end of turn. If this is the third time this ability has resolved this turn, you may add {R}{R}{R}{R}{R}{R}{R}{R}. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl("{2}")); ability.addEffect(new SoulbrightFlamekinEffect()); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public SoulbrightFlamekin(final SoulbrightFlamekin card) { + private SoulbrightFlamekin(final SoulbrightFlamekin card) { super(card); } @@ -54,19 +54,14 @@ public final class SoulbrightFlamekin extends CardImpl { class SoulbrightFlamekinEffect extends OneShotEffect { - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; - } - - public SoulbrightFlamekinEffect() { + SoulbrightFlamekinEffect() { super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, you may add {R}{R}{R}{R}{R}{R}{R}{R}"; + this.staticText = "If this is the third time this ability has resolved this turn, " + + "you may add {R}{R}{R}{R}{R}{R}{R}{R}"; } - public SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) { - super(effect); + private SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) { + super(effect); } @Override @@ -77,29 +72,14 @@ class SoulbrightFlamekinEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); - } - info.activations++; - if (info.activations == 3) { - controller.getManaPool().addMana(Mana.RedMana(8), game, source); - } - return true; + AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); + if (controller == null + || watcher == null + || !watcher.checkActivations(source, game) + || !controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}}?", source, game)) { + return false; } - return false; + controller.getManaPool().addMana(Mana.RedMana(8), game, source); + return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java new file mode 100644 index 00000000000..ba25535c3ad --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java @@ -0,0 +1,118 @@ +package org.mage.test.cards.single.lrw; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ + +public class AshlingThePilgrimTest extends CardTestPlayerBase { + + private static final String ashling = "Ashling the Pilgrim"; + private static final String ashAbility = "{1}{R}: "; + private static final String cshift = "Cloudshift"; + + @Test + public void testAshling() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 17); + } + + @Test + public void testAshlingMultipleTurns() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 1); + assertCounterCount(ashling, CounterType.P1P1, 4); + assertLife(playerA, 20); + } + + @Test + public void testAshlingMultipleTurns2() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 14); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 13); + } + + @Test + public void testBlinkedAshling() { + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 7); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.HAND, playerA, cshift); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cshift, ashling); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 1); + assertCounterCount(ashling, CounterType.P1P1, 2); + assertLife(playerA, 20); + } + + @Test + public void testBlinkedAshling2() { + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 9); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.HAND, playerA, cshift); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cshift, ashling); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 17); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java new file mode 100644 index 00000000000..ccb1f75c81c --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java @@ -0,0 +1,40 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class AbilityResolvedWatcher extends Watcher { + + private final Map> activationMap = new HashMap<>(); + + public AbilityResolvedWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + } + + @Override + public void reset() { + super.reset(); + activationMap.clear(); + } + + public boolean checkActivations(Ability source, Game game) { + return activationMap.computeIfAbsent(new MageObjectReference( + source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + ), x -> new HashMap<>()).compute(source.getOriginalId(), (u, i) -> i == null ? 1 : i + 1).intValue() == 3; + } +}