cleanup AbilityType for easier check that an ability is an activated ability (#12153)

* cleanup AbilityType

* further cleanup

* cleanup instanceof

* tweak formatting

* rework Rex

* fix mistake in PlayerImpl

* cleanup 'instanceof' for 'ActivatedManaAbilityImpl'
This commit is contained in:
Susucre 2024-05-04 04:13:34 +02:00 committed by GitHub
parent 62cc49ffa3
commit 85cad4ff1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
118 changed files with 486 additions and 574 deletions

View file

@ -5,7 +5,6 @@ import mage.abilities.costs.Cost;
import mage.abilities.keyword.ProtectionAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.abilities.mana.ManaAbility;
import mage.constants.AbilityType;
import mage.constants.Zone;
import mage.game.Game;
import mage.util.ThreadLocalStringBuilder;
@ -103,6 +102,11 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
return rules;
}
/**
* Activated Ability in the engine are broader than in the rules.
* Notably SpellAbility & PlayLandAbility are ActivatedAbility,
* as they can be activated by a player (the engine meaning).
*/
@Override
public Abilities<ActivatedAbility> getActivatedAbilities(Zone zone) {
return stream()
@ -161,7 +165,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
public Abilities<TriggeredAbility> getTriggeredAbilities(Zone zone) {
Abilities<TriggeredAbility> zonedAbilities = new AbilitiesImpl<>();
for (T ability : this) {
if (ability instanceof TriggeredAbility && ability.getZone().match(zone)) {
if (ability.isTriggeredAbility() && ability.getZone().match(zone)) {
zonedAbilities.add((TriggeredAbility) ability);
} else if (ability instanceof ZoneChangeTriggeredAbility) {
ZoneChangeTriggeredAbility zcAbility = (ZoneChangeTriggeredAbility) ability;
@ -176,7 +180,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
@Override
public boolean hasPoolDependantAbilities() {
return stream()
.filter(ability -> ability.getAbilityType() == AbilityType.MANA)
.filter(Ability::isManaAbility)
.map(ManaAbility.class::cast)
.anyMatch(ManaAbility::isPoolDependant);
}

View file

@ -62,6 +62,31 @@ public interface Ability extends Controllable, Serializable {
*/
AbilityType getAbilityType();
/**
* If this ability is an activated one (mana included).
*/
boolean isActivatedAbility();
/**
* If this ability is a triggered one (mana included).
*/
boolean isTriggeredAbility();
/**
* If this ability is an activated one, excluding mana.
*/
boolean isNonManaActivatedAbility();
/**
* If this ability is a mana activated one.
*/
boolean isManaActivatedAbility();
/**
* If this ability is a mana ability, (both triggered and activated can be mana abilities).
*/
boolean isManaAbility();
/**
* Sets the id of the controller of this ability.
*

View file

@ -162,6 +162,31 @@ public abstract class AbilityImpl implements Ability {
return this.abilityType;
}
@Override
public boolean isActivatedAbility() {
return this.abilityType.isActivatedAbility();
}
@Override
public boolean isTriggeredAbility() {
return this.abilityType.isTriggeredAbility();
}
@Override
public boolean isNonManaActivatedAbility() {
return this.abilityType.isNonManaActivatedAbility();
}
@Override
public boolean isManaActivatedAbility() {
return this.abilityType.isManaActivatedAbility();
}
@Override
public boolean isManaAbility() {
return this.abilityType.isManaAbility();
}
@Override
public boolean resolve(Game game) {
boolean result = true;
@ -193,7 +218,7 @@ public abstract class AbilityImpl implements Ability {
boolean effectResult = effect.apply(game, this);
result &= effectResult;
if (logger.isDebugEnabled()) {
if (this.getAbilityType() != AbilityType.MANA) {
if (!this.isManaAbility()) {
if (!effectResult) {
if (this.getSourceId() != null) {
MageObject mageObject = game.getObject(this.getSourceId());
@ -351,7 +376,7 @@ public abstract class AbilityImpl implements Ability {
// and/or zones become the target of a spell trigger at this point; they'll wait to be put on
// the stack until the spell has finished being cast.)
if (this.getAbilityType() != AbilityType.TRIGGERED) { // triggered abilities check this already in playerImpl.triggerAbility
if (!this.getAbilityType().isTriggeredAbility()) { // triggered abilities check this already in playerImpl.triggerAbility
adjustTargets(game);
}
@ -359,6 +384,7 @@ public abstract class AbilityImpl implements Ability {
Outcome outcome = getEffects().getOutcome(this);
// only activated abilities can be canceled by human user (not triggered)
// Note: ActivatedAbility does include SpellAbility & PlayLandAbility, but those should be able to be canceled too.
boolean canCancel = this instanceof ActivatedAbility && controller.isHuman();
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, canCancel)) {
// was canceled during targer selection

View file

@ -57,7 +57,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
}
protected ActivatedAbilityImpl(Zone zone, Effect effect, Cost cost) {
super(AbilityType.ACTIVATED, zone);
super(AbilityType.ACTIVATED_NONMANA, zone);
this.addEffect(effect);
this.addCost(cost);
}
@ -147,8 +147,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
if (approvingObjects.isEmpty()) {
return ActivationStatus.withoutApprovingObject(true);
}
else {
} else {
return new ActivationStatus(approvingObjects);
}
}

View file

@ -36,7 +36,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
}
protected TriggeredAbilityImpl(Zone zone, Effect effect, boolean optional) {
super(AbilityType.TRIGGERED, zone);
super(AbilityType.TRIGGERED_NONMANA, zone);
setLeavesTheBattlefieldTrigger(false);
if (effect != null) {
addEffect(effect);
@ -265,6 +265,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
/**
* For use in generating trigger phrases with correct text
*
* @return "When " for an effect that always removes the source from the battlefield, otherwise "Whenever "
*/
protected final String getWhen() {

View file

@ -54,19 +54,22 @@ public class FinalChapterAbilityResolvesTriggeredAbility extends TriggeredAbilit
// the ID of the original ability (on the permanent) that the resolving ability
// came from.
Optional<Ability> ability_opt = game.getAbility(event.getTargetId(), event.getSourceId());
if (!ability_opt.isPresent())
if (!ability_opt.isPresent()) {
return false;
}
// Make sure it was a triggered ability (needed for checking if it's a chapter
// ability)
Ability ability = ability_opt.get();
if (!(ability instanceof TriggeredAbility))
if (!(ability.isTriggeredAbility())) {
return false;
}
// Make sure it was a chapter ability
TriggeredAbility triggeredAbility = (TriggeredAbility) ability;
if (!SagaAbility.isChapterAbility(triggeredAbility))
if (!SagaAbility.isChapterAbility(triggeredAbility)) {
return false;
}
// There's a chance that the permanent that this abiltiy came from no longer
// exists, so try and find it on the battlefield or check last known
@ -75,17 +78,17 @@ public class FinalChapterAbilityResolvesTriggeredAbility extends TriggeredAbilit
// chapter ability on that permanent.
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent == null
|| !permanent.isControlledBy(getControllerId())
|| !permanent.hasSubtype(SubType.SAGA, game)) {
|| !permanent.isControlledBy(getControllerId())
|| !permanent.hasSubtype(SubType.SAGA, game)) {
return false;
}
// Find the max chapter number from that permanent
int maxChapter = CardUtil
.castStream(permanent.getAbilities(game).stream(), SagaAbility.class)
.map(SagaAbility::getMaxChapter)
.mapToInt(SagaChapter::getNumber)
.sum();
.castStream(permanent.getAbilities(game).stream(), SagaAbility.class)
.map(SagaAbility::getMaxChapter)
.mapToInt(SagaChapter::getNumber)
.sum();
// Check if the ability was the last one
if (!SagaAbility.isFinalAbility(triggeredAbility, maxChapter)) {

View file

@ -47,7 +47,7 @@ public class SacrificeAllCost extends CostImpl implements SacrificeCost {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
UUID activator = controllerId;
if (ability.getAbilityType() == AbilityType.ACTIVATED || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.getAbilityType().isActivatedAbility() || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (((ActivatedAbilityImpl) ability).getActivatorId() != null) {
activator = ((ActivatedAbilityImpl) ability).getActivatorId();
} // else, Activator not filled?

View file

@ -27,6 +27,7 @@ public class SacrificeTargetCost extends CostImpl implements SacrificeCost {
/**
* Sacrifice a permanent matching the filter:
*
* @param filter can be generic, will automatically add article and sacrifice predicates
*/
public SacrificeTargetCost(FilterPermanent filter) {
@ -35,6 +36,7 @@ public class SacrificeTargetCost extends CostImpl implements SacrificeCost {
/**
* Sacrifice N permanents matching the filter:
*
* @param filter can be generic, will automatically add sacrifice predicates
*/
public SacrificeTargetCost(int numToSac, FilterPermanent filter) {
@ -57,7 +59,7 @@ public class SacrificeTargetCost extends CostImpl implements SacrificeCost {
@Override
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
UUID activator = controllerId;
if (ability.getAbilityType() == AbilityType.ACTIVATED || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.getAbilityType().isActivatedAbility() || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
activator = ((ActivatedAbilityImpl) ability).getActivatorId();
}
// can be cancel by user
@ -87,7 +89,7 @@ public class SacrificeTargetCost extends CostImpl implements SacrificeCost {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
UUID activator = controllerId;
if (ability.getAbilityType() == AbilityType.ACTIVATED || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.getAbilityType().isActivatedAbility() || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (((ActivatedAbilityImpl) ability).getActivatorId() != null) {
activator = ((ActivatedAbilityImpl) ability).getActivatorId();
} // else, Activator not filled?

View file

@ -1,7 +1,6 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
@ -42,7 +41,7 @@ public class GainActivatedAbilitiesOfTopCardEffect extends ContinuousEffectImpl
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ActivatedAbility) {
if (ability.isActivatedAbility()) {
permanent.addAbility(ability, source.getSourceId(), game, true);
}
}

View file

@ -1,7 +1,6 @@
package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.Mode;
import mage.abilities.condition.Condition;
import mage.abilities.effects.ContinuousEffectImpl;
@ -106,7 +105,7 @@ public class GainControlTargetEffect extends ContinuousEffectImpl {
controlChanged = true;
}
}
if (source instanceof ActivatedAbility
if (source.isActivatedAbility()
&& firstControlChange && !controlChanged) {
// If it was not possible to get control of target permanent by the activated ability the first time it took place
// the effect failed (e.g. because of Guardian Beast) and must be discarded

View file

@ -2,7 +2,6 @@ package mage.abilities.effects.common.ruleModifying;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.constants.Duration;
@ -86,7 +85,7 @@ public class TargetsHaveToTargetPermanentIfAbleEffect extends ContinuousRuleModi
}
Ability stackAbility = stackObject.getStackAbility();
// Ensure that this ability is activated or a cast spell, because Flag Bearer effects don't require triggered abilities to choose a Standard Bearer
if (!(stackAbility instanceof ActivatedAbility) &&
if (!(stackAbility.isActivatedAbility()) &&
!(stackAbility instanceof SpellAbility)) {
return false;
}

View file

@ -2,9 +2,7 @@ package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.game.Game;
@ -39,7 +37,7 @@ public abstract class ManaEffect extends OneShotEffect {
// During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production.
// By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana
// So it's important if ManaEffects overwrite the apply method to take care for this.
if (source instanceof TriggeredAbility) {
if (source.isTriggeredAbility()) {
player.addAvailableTriggeredMana(getNetMana(game, source));
}
return true; // No need to add mana to pool during checkPlayable
@ -132,7 +130,7 @@ public abstract class ManaEffect extends OneShotEffect {
* @param source
*/
public void checkToFirePossibleEvents(Mana mana, Game game, Ability source) {
if (source.getAbilityType() == AbilityType.MANA && source.hasTapCost()) {
if (source.getAbilityType().isManaAbility() && source.hasTapCost()) {
ManaEvent event = new TappedForManaEvent(source.getSourceId(), source, source.getControllerId(), mana, game);
if (!game.replaceEvent(event)) {
game.fireEvent(event);

View file

@ -3,7 +3,6 @@ package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -14,11 +13,11 @@ import java.util.Optional;
/**
* Split Second
*
* <p>
* As long as this spell is on the stack, players can't cast other spells or activate abilities that aren't mana abilities.
*/
public class SplitSecondAbility extends SimpleStaticAbility {
public class SplitSecondAbility extends SimpleStaticAbility {
public SplitSecondAbility() {
super(Zone.STACK, new SplitSecondEffect());
@ -70,7 +69,7 @@ class SplitSecondEffect extends ContinuousRuleModifyingEffectImpl {
}
if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY) {
Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
if (ability.isPresent() && !(ability.get() instanceof ActivatedManaAbilityImpl)) {
if (ability.isPresent() && !ability.get().isManaActivatedAbility()) {
return true;
}
}

View file

@ -22,7 +22,7 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
protected boolean poolDependant;
public ActivatedManaAbilityImpl(Zone zone, ManaEffect effect, Cost cost) {
super(AbilityType.MANA, zone);
super(AbilityType.ACTIVATED_MANA, zone);
this.usesStack = false;
this.undoPossible = true;
if (effect != null) {

View file

@ -5,6 +5,7 @@ import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.mana.ManaEffect;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Zone;
import mage.game.Game;
@ -13,8 +14,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mage.constants.ManaType;
/**
* see 20110715 - 605.1b
*
@ -32,8 +31,7 @@ public abstract class TriggeredManaAbility extends TriggeredAbilityImpl implemen
public TriggeredManaAbility(Zone zone, ManaEffect effect, boolean optional) {
super(zone, effect, optional);
this.usesStack = false;
this.abilityType = AbilityType.MANA;
this.abilityType = AbilityType.TRIGGERED_MANA;
}
protected TriggeredManaAbility(final TriggeredManaAbility ability) {

View file

@ -1,26 +1,32 @@
package mage.constants;
/**
* @author North
* @author North, Susucr
*/
public enum AbilityType {
PLAY_LAND("Play land", true),
MANA("Mana", false),
SPELL("Spell", true),
ACTIVATED("Activated", false),
STATIC("Static", false),
TRIGGERED("Triggered", false),
EVASION("Evasion", false),
LOYALTY("Loyalty", false),
SPECIAL_ACTION("Special Action", false),
SPECIAL_MANA_PAYMENT("Special Mana Payment", false); // No activated ability and no special action. (e.g. Improvise, Delve)
PLAY_LAND("Play land", true, false, false, false),
SPELL("Spell", true, false, false, false),
STATIC("Static", false, false, false, false),
EVASION("Evasion", false, false, false, false),
ACTIVATED_NONMANA("Activated", false, true, false, false),
ACTIVATED_MANA("Mana", false, true, false, true),
TRIGGERED_NONMANA("Triggered", false, false, true, false),
TRIGGERED_MANA("Triggered Mana", false, false, true, true),
SPECIAL_ACTION("Special Action", false, false, false, false),
SPECIAL_MANA_PAYMENT("Special Mana Payment", false, false, false, false); // No activated ability and no special action. (e.g. Improvise, Delve)
private final String text;
private final boolean playCardAbility;
private final boolean activatedAbility;
private final boolean triggeredAbility;
private final boolean manaAbility;
AbilityType(String text, boolean playCardAbility) {
AbilityType(String text, boolean playCardAbility, boolean activatedAbility, boolean triggeredAbility, boolean manaAbility) {
this.text = text;
this.playCardAbility = playCardAbility;
this.activatedAbility = activatedAbility;
this.triggeredAbility = triggeredAbility;
this.manaAbility = manaAbility;
}
@Override
@ -31,4 +37,24 @@ public enum AbilityType {
public boolean isPlayCardAbility() {
return playCardAbility;
}
public boolean isActivatedAbility() {
return activatedAbility;
}
public boolean isTriggeredAbility() {
return triggeredAbility;
}
public boolean isNonManaActivatedAbility() {
return activatedAbility && !manaAbility;
}
public boolean isManaActivatedAbility() {
return activatedAbility && manaAbility;
}
public boolean isManaAbility() {
return manaAbility;
}
}

View file

@ -1,13 +1,12 @@
package mage.filter.common;
import mage.abilities.Ability;
import mage.constants.AbilityType;
import mage.filter.FilterStackObject;
import mage.filter.predicate.other.ActivatedOrTriggeredAbilityPredicate;
import mage.game.Game;
import mage.game.stack.StackObject;
import java.util.UUID;
import mage.filter.predicate.other.ActivatedOrTriggeredAbilityPredicate;
/**
* @author TheElk801
@ -29,25 +28,23 @@ public class FilterActivatedOrTriggeredAbility extends FilterStackObject {
@Override
public boolean match(StackObject stackObject, UUID playerId, Ability source, Game game) {
if (!super.match(stackObject, playerId, source, game)
|| !(stackObject instanceof Ability)) {
return false;
}
Ability ability = (Ability) stackObject;
return ability.getAbilityType() == AbilityType.TRIGGERED
|| ability.getAbilityType() == AbilityType.ACTIVATED;
return ability.isTriggeredAbility() || ability.isActivatedAbility();
}
@Override
public boolean match(StackObject stackObject, Game game) {
if (!super.match(stackObject, game)
|| !(stackObject instanceof Ability)) {
return false;
}
Ability ability = (Ability) stackObject;
return ability.getAbilityType() == AbilityType.TRIGGERED
|| ability.getAbilityType() == AbilityType.ACTIVATED;
return ability.getAbilityType().isTriggeredAbility() || ability.isActivatedAbility();
}
}

View file

@ -1,13 +1,11 @@
package mage.filter.predicate.other;
import mage.abilities.Ability;
import mage.constants.AbilityType;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.game.stack.StackObject;
/**
*
* @author jeffwadsworth
*/
public enum ActivatedOrTriggeredAbilityPredicate implements Predicate<StackObject> {
@ -19,8 +17,7 @@ public enum ActivatedOrTriggeredAbilityPredicate implements Predicate<StackObjec
return false;
}
Ability ability = ((Ability) input);
return ability.getAbilityType() == AbilityType.TRIGGERED
|| ability.getAbilityType() == AbilityType.ACTIVATED;
return ability.isTriggeredAbility() || ability.isActivatedAbility();
}
@Override

View file

@ -2,7 +2,6 @@ package mage.game.command.emblems;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.CopyStackObjectEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.command.Emblem;
@ -56,7 +55,7 @@ class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl {
return false;
}
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (stackAbility == null || stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl) {
if (stackAbility == null || stackAbility.getStackAbility().isManaActivatedAbility()) {
return false;
}
this.getEffects().setValue("stackObject", stackAbility);

View file

@ -430,6 +430,31 @@ public class StackAbility extends StackObjectImpl implements Ability {
return ability.getAbilityType();
}
@Override
public boolean isActivatedAbility() {
return ability.isActivatedAbility();
}
@Override
public boolean isTriggeredAbility() {
return ability.isTriggeredAbility();
}
@Override
public boolean isNonManaActivatedAbility() {
return ability.isNonManaActivatedAbility();
}
@Override
public boolean isManaActivatedAbility() {
return ability.isManaActivatedAbility();
}
@Override
public boolean isManaAbility() {
return ability.isManaAbility();
}
@Override
public boolean isUsesStack() {
return true;

View file

@ -1592,7 +1592,7 @@ public abstract class PlayerImpl implements Player, Serializable {
case SPECIAL_MANA_PAYMENT:
result = specialManaPayment((SpecialAction) ability.copy(), game);
break;
case MANA:
case ACTIVATED_MANA:
result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game);
break;
case SPELL:
@ -1616,9 +1616,7 @@ public abstract class PlayerImpl implements Player, Serializable {
//if player has taken an action then reset all player passed flags
justActivatedType = null;
if (result) {
if (isHuman()
&& (ability.getAbilityType() == AbilityType.SPELL
|| ability.getAbilityType() == AbilityType.ACTIVATED)) {
if (isHuman() && (ability.getAbilityType() == AbilityType.SPELL || ability.getAbilityType().isActivatedAbility())) {
if (ability.isUsesStack()) { // if the ability does not use the stack (e.g. Suspend) auto pass would go to next phase unintended
setJustActivatedType(ability.getAbilityType());
}
@ -2400,7 +2398,7 @@ public abstract class PlayerImpl implements Player, Serializable {
public void removeCounters(String name, int amount, Ability source, Game game) {
GameEvent removeCountersEvent = new RemoveCountersEvent(name, this, source, amount, false);
if (game.replaceEvent(removeCountersEvent)){
if (game.replaceEvent(removeCountersEvent)) {
return;
}
@ -2408,7 +2406,7 @@ public abstract class PlayerImpl implements Player, Serializable {
for (int i = 0; i < amount; i++) {
GameEvent event = new RemoveCounterEvent(name, this, source, false);
if (game.replaceEvent(event)){
if (game.replaceEvent(event)) {
continue;
}
@ -3582,7 +3580,7 @@ public abstract class PlayerImpl implements Player, Serializable {
* @return
*/
protected boolean canPlay(ActivatedAbility ability, ManaOptions availableMana, MageObject sourceObject, Game game) {
if (!(ability instanceof ActivatedManaAbilityImpl)) {
if (!ability.isManaActivatedAbility()) {
ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
if (!copy.canActivate(playerId, game).canActivate()) {
return false;
@ -3885,7 +3883,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// alternative cost must be replaced by real play ability
return findActivatedAbilityFromAlternativeSourceCost(object, manaFull, ability, game);
} else if (ability instanceof ActivatedAbility) {
// all other activated ability
// all other abilities (include PlayLandAbility & SpellAbility)
if (canPlay((ActivatedAbility) ability, manaFull, object, game)) {
return (ActivatedAbility) ability;
}
@ -3964,6 +3962,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
// must check all abilities, not activated only
for (Ability ability : candidateAbilities) {
// Note: SpellAbility and PlayLandAbility are ActivatedAbility
if (!(ability instanceof ActivatedAbility)) {
continue;
}
@ -4493,9 +4492,10 @@ public abstract class PlayerImpl implements Player, Serializable {
case allAbilities:
return true;
case onlyManaAbilities:
return ability.getAbilityType() == AbilityType.MANA;
return ability.isManaAbility();
case nonSpellnonActivatedAbilities:
return ability.getAbilityType() != AbilityType.ACTIVATED && ability.getAbilityType() != AbilityType.SPELL;
return !ability.getAbilityType().isActivatedAbility()
&& ability.getAbilityType() != AbilityType.SPELL;
case none:
default:
return false;

View file

@ -203,7 +203,9 @@ public abstract class TargetImpl implements Target {
@Override
public boolean isRequired(Ability ability) {
return ability == null || ability.isActivated() || !(ability.getAbilityType() == AbilityType.SPELL || ability.getAbilityType() == AbilityType.ACTIVATED);
return ability == null
|| ability.isActivated()
|| !(ability.getAbilityType() == AbilityType.SPELL || ability.getAbilityType().isActivatedAbility());
}
@Override
@ -675,10 +677,10 @@ public abstract class TargetImpl implements Target {
String abilityText = source.getRule(true).toLowerCase();
boolean strictModeEnabled = player.getStrictChooseMode();
boolean canAutoChoose = this.getMinNumberOfTargets() == this.getMaxNumberOfTargets() // Targets must be picked
&& possibleTargets.size() == this.getNumberOfTargets() - this.getSize() // Available targets are equal to the number that must be picked
&& !strictModeEnabled // Test AI is not set to strictChooseMode(true)
&& playerAutoTargetLevel > 0 // Human player has enabled auto-choose in settings
&& !abilityText.contains("search"); // Do not autochoose for any effects which involve searching
&& possibleTargets.size() == this.getNumberOfTargets() - this.getSize() // Available targets are equal to the number that must be picked
&& !strictModeEnabled // Test AI is not set to strictChooseMode(true)
&& playerAutoTargetLevel > 0 // Human player has enabled auto-choose in settings
&& !abilityText.contains("search"); // Do not autochoose for any effects which involve searching
if (canAutoChoose) {

View file

@ -1,7 +1,6 @@
package mage.target.common;
import mage.abilities.Ability;
import mage.constants.AbilityType;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.FilterAbility;
@ -48,7 +47,7 @@ public class TargetActivatedAbility extends TargetObject {
StackObject stackObject = game.getStack().getStackObject(id);
return stackObject != null
&& stackObject.getStackAbility() != null
&& stackObject.getStackAbility().getAbilityType() == AbilityType.ACTIVATED
&& stackObject.getStackAbility().isActivatedAbility()
&& source != null
&& filter.match(stackObject, source.getControllerId(), source, game);
}
@ -62,8 +61,9 @@ public class TargetActivatedAbility extends TargetObject {
public boolean canChoose(UUID sourceControllerId, Game game) {
for (StackObject stackObject : game.getStack()) {
if (stackObject.getStackAbility() != null
&& stackObject.getStackAbility().getAbilityType() == AbilityType.ACTIVATED
&& game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getStackAbility().getControllerId())) {
&& stackObject.getStackAbility().isActivatedAbility()
&& game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getStackAbility().getControllerId())
) {
return true;
}
}
@ -79,7 +79,7 @@ public class TargetActivatedAbility extends TargetObject {
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
Set<UUID> possibleTargets = new HashSet<>();
for (StackObject stackObject : game.getStack()) {
if (stackObject.getStackAbility().getAbilityType() == AbilityType.ACTIVATED
if (stackObject.getStackAbility().isActivatedAbility()
&& game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getStackAbility().getControllerId())
&& filter.match(stackObject, game)) {
possibleTargets.add(stackObject.getStackAbility().getId());

View file

@ -1,7 +1,6 @@
package mage.target.common;
import mage.abilities.Ability;
import mage.constants.AbilityType;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.FilterStackObject;
@ -93,7 +92,7 @@ public class TargetTriggeredAbility extends TargetObject {
}
if (stackObject instanceof Ability) {
Ability ability = (Ability) stackObject;
return ability.getAbilityType() == AbilityType.TRIGGERED;
return ability.getAbilityType().isTriggeredAbility();
}
return false;
}