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;
This commit is contained in:
Oleg Agafonov 2021-08-21 01:44:00 +04:00
parent bdeb0dde66
commit 33380f09c2
8 changed files with 33 additions and 29 deletions

View file

@ -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

View file

@ -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

View file

@ -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);
/**

View file

@ -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.
* <p>
* WARNING, don't forget to call super.canActivate on override in card's code in most cases.
*

View file

@ -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 players 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();

View file

@ -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());

View file

@ -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);
}
/**

View file

@ -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);