Refactored Apex Observatory to work correctly with blink effects.

This commit is contained in:
Jeff Wadsworth 2024-09-18 17:05:58 -05:00
parent e7dc75d5f6
commit 5abf295ba2

View file

@ -5,19 +5,14 @@ import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AddContinuousEffectToGame;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.choices.Choice; import mage.choices.Choice;
import mage.choices.ChoiceCardType; import mage.choices.ChoiceCardType;
import mage.constants.*; import mage.constants.*;
import mage.filter.common.FilterOwnedCard;
import mage.game.ExileZone; import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -26,6 +21,8 @@ import mage.util.CardUtil;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import mage.abilities.SpellAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
/** /**
* @author jeffwadsworth * @author jeffwadsworth
@ -40,11 +37,11 @@ public class ApexObservatory extends CardImpl {
// Apex Observatory enters the battlefield tapped. // Apex Observatory enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility()); this.addAbility(new EntersBattlefieldTappedAbility());
// Apex Observatory enters the battlefield tapped. As it enters, choose a card type shared among two exiled cards used to craft it. // As it enters, choose a card type shared among two exiled cards used to craft it.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCardTypeEffect())); this.addAbility(new AsEntersBattlefieldAbility(new ChooseCardTypeEffect()));
// The next spell you cast this turn of the chosen type can be cast without paying its mana cost. // The next spell you cast this turn of the chosen type can be cast without paying its mana cost.
this.addAbility(new SimpleActivatedAbility(new AddContinuousEffectToGame(new ApexObservatoryEffect()), new TapSourceCost())); this.addAbility(new SimpleActivatedAbility(new ApexObservatoryEffect(), new TapSourceCost()));
} }
private ApexObservatory(final ApexObservatory card) { private ApexObservatory(final ApexObservatory card) {
@ -141,10 +138,10 @@ class ChooseCardTypeEffect extends OneShotEffect {
} }
} }
class ApexObservatoryEffect extends ContinuousEffectImpl { class ApexObservatoryEffect extends OneShotEffect {
ApexObservatoryEffect() { ApexObservatoryEffect() {
super(Duration.EndOfTurn, Outcome.Benefit); super(Outcome.Benefit);
staticText = "The next spell you cast this turn of the chosen type can be cast without paying its mana cost."; staticText = "The next spell you cast this turn of the chosen type can be cast without paying its mana cost.";
} }
@ -158,86 +155,85 @@ class ApexObservatoryEffect extends ContinuousEffectImpl {
} }
@Override @Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
String chosenCardType = (String) game.getState().getValue("ApexObservatoryType_" + source.getSourceId().toString()); String chosenCardType = (String) game.getState().getValue("ApexObservatoryType_" + source.getSourceId().toString());
if (controller != null if (chosenCardType == null) {
&& chosenCardType != null) {
Card apexObservatory = game.getCard(source.getSourceId());
if (apexObservatory != null) {
Boolean wasItUsed = (Boolean) game.getState().getValue(
apexObservatory.getId().toString() + game.getTurnNum());
if (wasItUsed == null) {
ApexObservatoryAlternativeCostAbility alternateCostAbility = new ApexObservatoryAlternativeCostAbility(chosenCardType);
alternateCostAbility.setSourceId(source.getSourceId());
controller.getAlternativeSourceCosts().add(alternateCostAbility);
}
return true;
}
}
return false; return false;
} }
game.addEffect(new ApexObservatoryCastWithoutManaEffect(chosenCardType, source.getControllerId()), source);
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}
class ApexObservatoryAlternativeCostAbility extends AlternativeCostSourceAbility {
private boolean wasActivated;
ApexObservatoryAlternativeCostAbility(String chosenCardType) {
super(new SpellMatchesChosenTypeCondition(chosenCardType), null, new FilterOwnedCard(), true, null);
}
private ApexObservatoryAlternativeCostAbility(final ApexObservatoryAlternativeCostAbility ability) {
super(ability);
this.wasActivated = ability.wasActivated;
}
@Override
public ApexObservatoryAlternativeCostAbility copy() {
return new ApexObservatoryAlternativeCostAbility(this);
}
@Override
public boolean activateAlternativeCosts(Ability ability, Game game) {
if (!super.activateAlternativeCosts(ability, game)) {
return false;
}
Card apexObservatory = game.getCard(this.getSourceId());
if (apexObservatory != null) {
game.getState().setValue(apexObservatory.getId().toString() + game.getTurnNum(), true);
}
return true; return true;
} }
} }
class SpellMatchesChosenTypeCondition implements Condition { class ApexObservatoryCastWithoutManaEffect extends CostModificationEffectImpl {
final private String chosenCardType; private final String chosenCardType;
private final UUID playerId;
private boolean used = false;
public SpellMatchesChosenTypeCondition(String chosenCardType) { ApexObservatoryCastWithoutManaEffect(String chosenCardType, UUID playerId) {
super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.SET_COST);
this.chosenCardType = chosenCardType; this.chosenCardType = chosenCardType;
this.playerId = playerId;
staticText = "The next spell you cast this turn of the chosen type can be cast without paying its mana cost";
}
private ApexObservatoryCastWithoutManaEffect(final ApexObservatoryCastWithoutManaEffect effect) {
super(effect);
this.chosenCardType = effect.chosenCardType;
this.playerId = effect.playerId;
this.used = effect.used;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source, Ability abilityToModify) {
return checkSpell(game.getObject(source), game, chosenCardType); // Ask the player if they want to use the effect
Player controller = game.getPlayer(playerId);
if (controller != null) {
MageObject spell = abilityToModify.getSourceObject(game);
if (spell != null && !game.isSimulation()) {
String message = "Cast " + spell.getIdName() + " without paying its mana cost?";
if (controller.chooseUse(Outcome.Benefit, message, source, game)) {
// Set the cost to zero
abilityToModify.getManaCostsToPay().clear();
// Mark as used
used = true;
game.informPlayers(controller.getLogName() + " casts " + spell.getIdName() + " without paying its mana cost.");
return true;
} else {
// Player chose not to use the effect
return false;
}
}
}
return false;
} }
public static boolean checkSpell(MageObject spell, Game game, String chosenCardType) { @Override
if (spell instanceof Card) { public ApexObservatoryCastWithoutManaEffect copy() {
Card card = (Card) spell; return new ApexObservatoryCastWithoutManaEffect(this);
return chosenCardType != null }
&& card.getCardType(game).toString().contains(chosenCardType);
@Override
public boolean isInactive(Ability source, Game game) {
return used || super.isInactive(source, game);
}
@Override
public boolean applies(Ability ability, Ability source, Game game) {
if (used) {
return false;
}
if (!ability.isControlledBy(playerId)) {
return false;
}
if (!(ability instanceof SpellAbility)) {
return false;
}
MageObject object = game.getObject(ability.getSourceId());
if (object != null && object.getCardType(game).stream()
.anyMatch(cardType -> cardType.toString().equals(chosenCardType))) {
return true;
} }
return false; return false;
} }