diff --git a/Mage.Sets/src/mage/cards/e/ElvishRefueler.java b/Mage.Sets/src/mage/cards/e/ElvishRefueler.java new file mode 100644 index 00000000000..e184cc92e0c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElvishRefueler.java @@ -0,0 +1,151 @@ +package mage.cards.e; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.constants.*; +import mage.abilities.keyword.ExhaustAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.watchers.Watcher; + +/** + * + * @author Jmlundeen + */ +public final class ElvishRefueler extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(); + private static final Hint hint = new ConditionHint(ActivatedExhaustCondition.instance, + "You haven't activated an exhaust ability this turn", + null, + "You have activated an exhaust ability this turn", + null, + true); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public ElvishRefueler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // During your turn, as long as you haven't activated an exhaust ability this turn, you may activate exhaust abilities as though they haven't been activated. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + new ElvishRefuelerEffect(filter), ActivatedExhaustCondition.instance) + .setText("During your turn, as long as you haven't activated an exhaust ability this turn, " + + "you may activate exhaust abilities as though they haven't been activated") + ).addHint(hint), new ElvishRefuelerWatcher()); + + // Exhaust -- {1}{G}: Put a +1/+1 counter on this creature. + this.addAbility(new ExhaustAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{1}{G}"))); + + } + + private ElvishRefueler(final ElvishRefueler card) { + super(card); + } + + @Override + public ElvishRefueler copy() { + return new ElvishRefueler(this); + } + +} + +enum ActivatedExhaustCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + ElvishRefuelerWatcher watcher = game.getState().getWatcher(ElvishRefuelerWatcher.class); + return watcher != null && !watcher.checkActivatedExhaust(source.getControllerId()); + } +} + +class ElvishRefuelerWatcher extends Watcher { + + private final Map activatedExhaustAbilities = new HashMap<>(); + + public ElvishRefuelerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY) { + return; + } + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (stackObject != null && stackObject.getStackAbility() instanceof ExhaustAbility) { + activatedExhaustAbilities.put(event.getPlayerId(), true); + } + } + + @Override + public void reset() { + super.reset(); + activatedExhaustAbilities.clear(); + } + + public boolean checkActivatedExhaust(UUID playerId) { + return activatedExhaustAbilities.getOrDefault(playerId, false); + } +} + +class ElvishRefuelerEffect extends AsThoughEffectImpl { + private final FilterPermanent filter; + + ElvishRefuelerEffect(FilterPermanent filter) { + super(AsThoughEffectType.ALLOW_EXHAUST_PER_TURN, Duration.WhileOnBattlefield, Outcome.Benefit); + this.filter = filter; + staticText = "During your turn, as long as you haven't activated an exhaust ability this turn, " + + "you may activate exhaust abilities as though they haven't been activated"; + } + + ElvishRefuelerEffect(final ElvishRefuelerEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ElvishRefuelerEffect copy() { + return new ElvishRefuelerEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + ElvishRefuelerWatcher watcher = game.getState().getWatcher(ElvishRefuelerWatcher.class); + if (!game.isActivePlayer(affectedControllerId) || watcher == null || watcher.checkActivatedExhaust(affectedControllerId)) { + return false; + } + Permanent permanent = game.getPermanent(objectId); + return filter.match(permanent, source.getControllerId(), source, game); + } +} diff --git a/Mage.Sets/src/mage/sets/Aetherdrift.java b/Mage.Sets/src/mage/sets/Aetherdrift.java index d76124abaab..5e2dbd77c71 100644 --- a/Mage.Sets/src/mage/sets/Aetherdrift.java +++ b/Mage.Sets/src/mage/sets/Aetherdrift.java @@ -107,6 +107,7 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Dune Drifter", 200, Rarity.UNCOMMON, mage.cards.d.DuneDrifter.class)); cards.add(new SetCardInfo("Dynamite Diver", 123, Rarity.COMMON, mage.cards.d.DynamiteDiver.class)); cards.add(new SetCardInfo("Earthrumbler", 160, Rarity.UNCOMMON, mage.cards.e.Earthrumbler.class)); + cards.add(new SetCardInfo("Elvish Refueler", 161, Rarity.UNCOMMON, mage.cards.e.ElvishRefueler.class)); cards.add(new SetCardInfo("Embalmed Ascendant", 201, Rarity.UNCOMMON, mage.cards.e.EmbalmedAscendant.class)); cards.add(new SetCardInfo("Endrider Catalyzer", 124, Rarity.COMMON, mage.cards.e.EndriderCatalyzer.class)); cards.add(new SetCardInfo("Endrider Spikespitter", 125, Rarity.UNCOMMON, mage.cards.e.EndriderSpikespitter.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dft/ElvishRefuelerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dft/ElvishRefuelerTest.java new file mode 100644 index 00000000000..56c9cde2498 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dft/ElvishRefuelerTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.single.dft; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author jimga150 + */ +public class ElvishRefuelerTest extends CardTestPlayerBase { + + @Test + public void testExhaustPrevention() { + // During your turn, as long as you haven’t activated an exhaust ability this turn, + // you may activate exhaust abilities as though they haven’t been activated. + String refueler = "Elvish Refueler"; + addCard(Zone.BATTLEFIELD, playerA, refueler); + addCard(Zone.HAND, playerA, refueler); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 7); + + // Should be able to activate refueler's exhaust ability + checkPlayableAbility("Should be able to activate exhaust", + 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhaust", true); + // Activate refueler's exhaust ability + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhaust"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // Should not be able to activate refueler's exhaust ability + checkPlayableAbility("Should not be able to activate exhaust after activating", + 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhaust", false); + // Casting a second refueler should not allow activating the exhaust ability + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, refueler); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + // Activating second refueler's exhaust ability + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhaust"); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + // Should be no exhaust left + checkPlayableAbility("All exhausts should be used from both refuelers", + 1, PhaseStep.END_TURN, playerA, "Exhaust", false); + + // Confirm on opponent's turn that exhaust is still not available + checkPlayableAbility("Opponent's turn, effect should not apply", + 2, PhaseStep.END_TURN, playerA, "Exhaust", false); + + // Should be able to activate refueler's exhaust ability on next turn + checkPlayableAbility("Should be able to activate exhaust on our next turn", + 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhaust", true); + // Activate refueler's exhaust ability + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhaust"); + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN); + // Only one exhaust should have been available + checkPlayableAbility("Already activated exhaust, should not be able to activate again", + 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhaust", false); + setStopAt(3, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + } + + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java b/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java index 4b325da69ed..7528788e1b1 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java @@ -3,7 +3,9 @@ package mage.abilities.keyword; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.effects.Effect; +import mage.constants.AsThoughEffectType; import mage.constants.Zone; +import mage.game.Game; /** * @author TheElk801 @@ -24,6 +26,20 @@ public class ExhaustAbility extends ActivatedAbilityImpl { return new ExhaustAbility(this); } + @Override + public boolean hasMoreActivationsThisTurn(Game game) { + ActivationInfo info = getActivationInfo(game); + if (info != null && info.totalActivations >= maxActivationsPerGame) { + boolean canActivate = !game.getContinuousEffects() + .asThough(sourceId, AsThoughEffectType.ALLOW_EXHAUST_PER_TURN, this, controllerId, game) + .isEmpty(); + if (canActivate) { + return true; + } + } + return super.hasMoreActivationsThisTurn(game); + } + @Override public String getRule() { return "Exhaust — " + super.getRule() + " (Activate each exhaust ability only once.)"; diff --git a/Mage/src/main/java/mage/constants/AsThoughEffectType.java b/Mage/src/main/java/mage/constants/AsThoughEffectType.java index e6b6b5e8c02..03fefec570f 100644 --- a/Mage/src/main/java/mage/constants/AsThoughEffectType.java +++ b/Mage/src/main/java/mage/constants/AsThoughEffectType.java @@ -59,7 +59,10 @@ public enum AsThoughEffectType { // // ALLOW_FORETELL_ANYTIME: // For Cosmos Charger effect - ALLOW_FORETELL_ANYTIME; + ALLOW_FORETELL_ANYTIME, + // ALLOW_EXHAUST_ACTIVE_ABILITY: + // Elvish Refueler effect allows Exhaust on your turn as though it hasn't been activated + ALLOW_EXHAUST_PER_TURN(true, false); private final boolean needAffectedAbility; // mark what AsThough check must be called for specific ability, not full object (example: spell check) private final boolean needPlayCardAbility; // mark what AsThough check must be called for play/cast abilities