From 33380f09c2d83e25aa3e535967e93ceca12872c0 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 21 Aug 2021 01:44:00 +0400 Subject: [PATCH] Improved canActivate support: * added support of non controller activates in ActivatedManaAbility (mayActivate); * removed custom code from ActivatedManaAbility; * removed custom code from Mana Cache; * added additional comments; --- .../src/mage/cards/g/GlimpseTheCosmos.java | 2 ++ Mage.Sets/src/mage/cards/m/ManaCache.java | 22 ++++++++----------- .../java/mage/abilities/ActivatedAbility.java | 5 +++++ .../mage/abilities/ActivatedAbilityImpl.java | 2 +- .../java/mage/abilities/PlayLandAbility.java | 8 +++++++ .../java/mage/abilities/SpellAbility.java | 5 ++++- .../mana/ActivatedManaAbilityImpl.java | 15 +------------ .../main/java/mage/players/PlayerImpl.java | 3 +++ 8 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java b/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java index 8a46054c527..e40b1525b49 100644 --- a/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java +++ b/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java @@ -53,6 +53,7 @@ public class GlimpseTheCosmos extends CardImpl { } +// TODO: card can't use custom SpellAbility, must be reworked to PLAY_FROM_NOT_OWN_HAND_ZONE (example: Worldheart Phoenix) class GlimpseTheCosmosAbility extends SpellAbility { private String abilityName; @@ -74,6 +75,7 @@ class GlimpseTheCosmosAbility extends SpellAbility { @Override public ActivationStatus canActivate(UUID playerId, Game game) { + // TODO: miss super.canActivate? Card card = game.getCard(getSourceId()); if (card != null) { // Card must be in the graveyard zone diff --git a/Mage.Sets/src/mage/cards/m/ManaCache.java b/Mage.Sets/src/mage/cards/m/ManaCache.java index 2ee9c317a73..716ae7d3050 100644 --- a/Mage.Sets/src/mage/cards/m/ManaCache.java +++ b/Mage.Sets/src/mage/cards/m/ManaCache.java @@ -11,10 +11,7 @@ import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledLandPermanent; @@ -94,6 +91,7 @@ class ManaCacheManaAbility extends ActivatedManaAbilityImpl { super(Zone.BATTLEFIELD, new BasicManaEffect(Mana.ColorlessMana(1), new CountersSourceCount(CounterType.CHARGE)), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1))); this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 0, 1)); + this.setMayActivate(TargetController.ANY); } public ManaCacheManaAbility(final ManaCacheManaAbility ability) { @@ -102,17 +100,15 @@ class ManaCacheManaAbility extends ActivatedManaAbilityImpl { @Override public ActivationStatus canActivate(UUID playerId, Game game) { - if (!super.hasMoreActivationsThisTurn(game) || !(condition == null || condition.apply(game, this))) { + // any player, but only during their turn before the end step + Player player = game.getPlayer(playerId); + if (player == null + || !playerId.equals(game.getActivePlayerId()) + || !game.getStep().getType().isBefore(PhaseStep.END_TURN)) { return ActivationStatus.getFalse(); } - Player player = game.getPlayer(playerId); - if (player != null && playerId.equals(game.getActivePlayerId()) && game.getStep().getType().isBefore(PhaseStep.END_TURN)) { - if (costs.canPay(this, this, playerId, game)) { - this.setControllerId(playerId); - return ActivationStatus.getTrue(this, game); - } - } - return ActivationStatus.getFalse(); + + return super.canActivate(playerId, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbility.java b/Mage/src/main/java/mage/abilities/ActivatedAbility.java index 29e8d90bace..61fe629ab16 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbility.java @@ -53,6 +53,11 @@ public interface ActivatedAbility extends Ability { */ ActivationStatus canActivate(UUID playerId, Game game); // has to return a reference to the permitting ability/source + /** + * Who can activate an ability. By default, only you (the controller/owner). + * + * @param mayActivate + */ void setMayActivate(TargetController mayActivate); /** diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java index 08d9f33d140..2149d7fe87d 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java @@ -173,7 +173,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } /** - * Basic activation check. It contains costs and targets legality too. + * Activated ability check, not spells. It contains costs and targets legality too. *

* WARNING, don't forget to call super.canActivate on override in card's code in most cases. * diff --git a/Mage/src/main/java/mage/abilities/PlayLandAbility.java b/Mage/src/main/java/mage/abilities/PlayLandAbility.java index 0b8c3df6167..750ba6dc543 100644 --- a/Mage/src/main/java/mage/abilities/PlayLandAbility.java +++ b/Mage/src/main/java/mage/abilities/PlayLandAbility.java @@ -25,6 +25,14 @@ public class PlayLandAbility extends ActivatedAbilityImpl { @Override public ActivationStatus canActivate(UUID playerId, Game game) { + // 20210723 - 116.2a + // Playing a land is a special action. To play a land, a player puts that land onto the battlefield + // from the zone it was in (usually that player’s hand). By default, a player can take this action + // only once during each of their turns. A player can take this action any time they have priority + // and the stack is empty during a main phase of their turn. See rule 305, “Lands.” + + // no super.canActivate() call + ApprovingObject approvingObject = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this, playerId, game); if (!controlsAbility(playerId, game) && null == approvingObject) { return ActivationStatus.getFalse(); diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index 368af5b155f..4ab4c6cecf9 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -87,6 +87,9 @@ public class SpellAbility extends ActivatedAbilityImpl { @Override public ActivationStatus canActivate(UUID playerId, Game game) { + // spells can be cast from non hand zones, so must use custom check + // no super.canActivate() call + if (this.spellCanBeActivatedRegularlyNow(playerId, game)) { if (spellAbilityType == SpellAbilityType.SPLIT || spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) { @@ -121,7 +124,7 @@ public class SpellAbility extends ActivatedAbilityImpl { } } - // can pay all costs + // can pay all costs and choose targets if (costs.canPay(this, this, playerId, game)) { if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard splitCard = (SplitCard) game.getCard(getSourceId()); diff --git a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java index 53ebed55211..f6fd9becc71 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java @@ -42,18 +42,6 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl @Override public ActivationStatus canActivate(UUID playerId, Game game) { - if (!super.hasMoreActivationsThisTurn(game) || !(condition == null || condition.apply(game, this))) { - return ActivationStatus.getFalse(); - } - if (!controlsAbility(playerId, game)) { - return ActivationStatus.getFalse(); - } - if (timing == TimingRule.SORCERY - && !game.canPlaySorcery(playerId) - && null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) { - return ActivationStatus.getFalse(); - } - // check if player is in the process of playing spell costs and they are no longer allowed to use // activated mana abilities (e.g. because they started to use improvise or convoke) if (!game.getStack().isEmpty()) { @@ -69,8 +57,7 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl } } - //20091005 - 605.3a - return new ActivationStatus(costs.canPay(this, this, controllerId, game), null); + return super.canActivate(playerId, game); } /** diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index fccfebad493..4dc9bbc5630 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3706,6 +3706,9 @@ public abstract class PlayerImpl implements Player, Serializable { } // check the hand zone (Sen Triplets) + // TODO: remove direct hand check (reveal fix in Sen Triplets)? + // human games: cards from opponent's hand must be revealed before play + // AI games: computer can see and play cards from opponent's hand without reveal if (fromAll || fromZone == Zone.HAND) { for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) { Player player = game.getPlayer(playerInRangeId);