diff --git a/Mage.Sets/src/mage/cards/a/ApexObservatory.java b/Mage.Sets/src/mage/cards/a/ApexObservatory.java index 99c13583faa..3d3cf06b9e7 100644 --- a/Mage.Sets/src/mage/cards/a/ApexObservatory.java +++ b/Mage.Sets/src/mage/cards/a/ApexObservatory.java @@ -13,10 +13,10 @@ import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.AlternativeCostSourceAbility; -import mage.abilities.costs.DynamicCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AddContinuousEffectToGame; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; -import mage.filter.FilterCard; +import mage.filter.common.FilterOwnedCard; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -52,7 +52,7 @@ public class ApexObservatory extends CardImpl { 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. - this.addAbility(new SimpleActivatedAbility(new ApexObservatoryEffect(), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new AddContinuousEffectToGame(new ApexObservatoryEffect()), new TapSourceCost())); } private ApexObservatory(final ApexObservatory card) { @@ -126,8 +126,7 @@ class ChooseCardTypeEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + cardTypeChoice.getChoice()); } - // non-unique identifier for now until global one is setup. source won't work for the conditional check - game.getState().setValue("ApexObservatory" + source.getControllerId().toString(), cardTypeChoice.getChoice()); + game.getState().setValue("ApexObservatoryType_" + source.getSourceId().toString(), cardTypeChoice.getChoice()); if (mageObject instanceof Permanent) { ((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen card type: " + cardTypeChoice.getChoice()), game); } @@ -145,13 +144,8 @@ class ChooseCardTypeEffect extends OneShotEffect { class ApexObservatoryEffect extends ContinuousEffectImpl { - private static final FilterCard filter = new FilterCard("a spell"); - private final AlternativeCostSourceAbility alternativeCastingCostAbility = new ApexObservatoryAlternativeCostAbility( - new SpellMatchesChosenTypeCondition(), null, filter, true, null - ); - - public ApexObservatoryEffect() { - super(Duration.EndOfTurn, Layer.RulesEffects, SubLayer.NA, Outcome.Neutral); + ApexObservatoryEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); staticText = "The next spell you cast this turn of the chosen type can be cast without paying its mana cost."; } @@ -165,46 +159,91 @@ class ApexObservatoryEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game, Ability source) { + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; + String chosenCardType = (String) game.getState().getValue("ApexObservatoryType_" + source.getSourceId().toString()); + if (controller != null + && chosenCardType != null) { + Card apexObservatory = game.getCard(source.getSourceId()); + if (apexObservatory != null) { + Boolean wasItUsed = (Boolean) game.getState().getValue( + apexObservatory.getId().toString()); + if (wasItUsed == null) { + ApexObservatoryAlternativeCostAbility alternateCostAbility = new ApexObservatoryAlternativeCostAbility(chosenCardType); + alternateCostAbility.setSourceId(source.getSourceId()); + controller.getAlternativeSourceCosts().add(alternateCostAbility); + } + return true; + } } - controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility); - return true; + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; } } class ApexObservatoryAlternativeCostAbility extends AlternativeCostSourceAbility { - public ApexObservatoryAlternativeCostAbility(Condition condition, String rule, FilterCard filter, boolean onlyMana, DynamicCost dynamicCost) { - super(condition, rule, filter, onlyMana, dynamicCost); + private boolean wasActivated; + + ApexObservatoryAlternativeCostAbility(String chosenCardType) { + super(new SpellMatchesChosenTypeCondition(chosenCardType), null, new FilterOwnedCard(), true, null); } - @Override - public boolean isAvailable(Ability source, Game game) { - return super.isAvailable(source, game) && !AlternativeCostSourceAbility.getActivatedStatus(game, source, getOriginalId(), false); + private ApexObservatoryAlternativeCostAbility(final ApexObservatoryAlternativeCostAbility ability) { + super(ability); + this.wasActivated = ability.wasActivated; } @Override public ApexObservatoryAlternativeCostAbility copy() { - return new ApexObservatoryAlternativeCostAbility(condition, rule, filter, onlyMana, dynamicCost); + return new ApexObservatoryAlternativeCostAbility(this); + } + + @Override + public boolean askToActivateAlternativeCosts(Ability ability, Game game) { + Player controller = game.getPlayer(ability.getControllerId()); + Card apexObservatory = game.getCard(this.getSourceId()); + if (controller != null + && apexObservatory != null) { + if (controller.chooseUse(Outcome.Benefit, "Use " + + apexObservatory.getLogName() + " to pay no mana costs for this spell?", ability, game)) { + wasActivated = super.askToActivateAlternativeCosts(ability, game); + if (wasActivated) { + game.getState().setValue(apexObservatory.getId().toString(), true); + } + } + } + return wasActivated; } } class SpellMatchesChosenTypeCondition implements Condition { - @Override - public boolean apply(Game game, Ability source) { - // investigating a global unique identifier for situations like this; source doesn't refer to the main card - return checkSpell(game.getObject(source), game, "ApexObservatory" + source.getControllerId().toString()); + final private String chosenCardType; + + public SpellMatchesChosenTypeCondition(String chosenCardType) { + this.chosenCardType = chosenCardType; } - public static boolean checkSpell(MageObject spell, Game game, String key) { + @Override + public boolean apply(Game game, Ability source) { + return checkSpell(game.getObject(source), game, chosenCardType); + } + + public static boolean checkSpell(MageObject spell, Game game, String chosenCardType) { if (spell instanceof Card) { Card card = (Card) spell; - String chosenType = (String) game.getState().getValue(key); - return chosenType != null && card.getCardType(game).toString().contains(chosenType); + return chosenCardType != null + && card.getCardType(game).toString().contains(chosenCardType); } return false; } diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index 0e2e4c1370b..631ea69b8e4 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -50,13 +50,13 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } /** - * @param cost alternate cost to pay + * @param cost alternate cost to pay * @param condition only if the condition is true it's possible to use the - * alternate costs - * @param rule if != null used as rule text - * @param filter filters the cards this alternate cost can be applied to - * @param onlyMana if true only the mana costs are replaced by this costs, - * other costs stay untouched + * alternate costs + * @param rule if != null used as rule text + * @param filter filters the cards this alternate cost can be applied to + * @param onlyMana if true only the mana costs are replaced by this costs, + * other costs stay untouched */ public AlternativeCostSourceAbility(Cost cost, Condition condition, String rule, FilterCard filter, boolean onlyMana) { super(Zone.ALL, null); @@ -153,7 +153,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } for (AlternativeCost alternateCost : alternativeCostsToCheck) { alternateCost.activate(); - for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext(); ) { + for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext();) { Cost costDetailed = (Cost) it.next(); if (costDetailed instanceof ManaCost) { ability.addManaCostsToPay((ManaCost) costDetailed.copy()); @@ -211,13 +211,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter /** * Search activated status of alternative cost. *

- * If you need it on resolve then use current ZCC (on stack) - * If you need it on battlefield then use previous ZCC (-1) + * If you need it on resolve then use current ZCC (on stack) If you need it + * on battlefield then use previous ZCC (-1) * * @param game * @param source - * @param alternativeCostOriginalId you must save originalId on card's creation - * @param searchPrevZCC true on battlefield, false on stack + * @param alternativeCostOriginalId you must save originalId on card's + * creation + * @param searchPrevZCC true on battlefield, false on stack * @return */ public static boolean getActivatedStatus(Game game, Ability source, UUID alternativeCostOriginalId, boolean searchPrevZCC) { @@ -232,21 +233,20 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter @Override public boolean isActivated(Ability source, Game game) { - Costs alternativeCostsToCheck; - if (dynamicCost != null) { - alternativeCostsToCheck = (Costs) game.getState().getValue(getDynamicCostActivatedKey(source)); - if (alternativeCostsToCheck == null) { - return false; - } - } else { - alternativeCostsToCheck = this.alternateCosts; + Costs alternativeCostsToCheck = dynamicCost != null + ? (Costs) game.getState().getValue(getDynamicCostActivatedKey(source)) + : this.alternateCosts; + + if (alternativeCostsToCheck == null) { + return false; } + for (AlternativeCost cost : alternativeCostsToCheck) { if (cost.isActivated(game)) { return true; } } - return false; + return onlyMana && alternativeCostsToCheck.isEmpty(); } @Override @@ -280,8 +280,8 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter sb.append(CardUtil.concatWithAnd(alternateCosts .stream() .map(cost -> cost.getCost() instanceof ManaCost - ? "pay " + cost.getText(true) - : cost.getText(true)) + ? "pay " + cost.getText(true) + : cost.getText(true)) .map(CardUtil::getTextWithFirstCharLowerCase) .collect(Collectors.toList()))); if (condition == null || alternateCosts.size() == 1) {