[DFT] Implement Elvish Refueler (#13392)

This commit is contained in:
Jmlundeen 2025-03-13 22:24:26 -05:00 committed by GitHub
parent cd8cb6afe5
commit 9aaad5193f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 232 additions and 1 deletions

View file

@ -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<UUID, Boolean> 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);
}
}

View file

@ -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("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("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("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("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 Catalyzer", 124, Rarity.COMMON, mage.cards.e.EndriderCatalyzer.class));
cards.add(new SetCardInfo("Endrider Spikespitter", 125, Rarity.UNCOMMON, mage.cards.e.EndriderSpikespitter.class)); cards.add(new SetCardInfo("Endrider Spikespitter", 125, Rarity.UNCOMMON, mage.cards.e.EndriderSpikespitter.class));

View file

@ -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 havent activated an exhaust ability this turn,
// you may activate exhaust abilities as though they havent 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();
}
}

View file

@ -3,7 +3,9 @@ package mage.abilities.keyword;
import mage.abilities.ActivatedAbilityImpl; import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost; import mage.abilities.costs.Cost;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.constants.AsThoughEffectType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game;
/** /**
* @author TheElk801 * @author TheElk801
@ -24,6 +26,20 @@ public class ExhaustAbility extends ActivatedAbilityImpl {
return new ExhaustAbility(this); 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 @Override
public String getRule() { public String getRule() {
return "Exhaust &mdash; " + super.getRule() + " <i>(Activate each exhaust ability only once.)</i>"; return "Exhaust &mdash; " + super.getRule() + " <i>(Activate each exhaust ability only once.)</i>";

View file

@ -59,7 +59,10 @@ public enum AsThoughEffectType {
// //
// ALLOW_FORETELL_ANYTIME: // ALLOW_FORETELL_ANYTIME:
// For Cosmos Charger effect // 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 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 private final boolean needPlayCardAbility; // mark what AsThough check must be called for play/cast abilities