reimplement "don't cause abilities to trigger" effects (#11242)

This commit is contained in:
xenohedron 2023-10-01 22:50:14 -04:00 committed by GitHub
parent 2ec532b157
commit bfbdf6b103
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 171 additions and 289 deletions

View file

@ -96,7 +96,7 @@ public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbili
if (ability.checkTrigger(event, game) && ability.checkTriggeredAlready(game) && !ability.checkUsedAlready(game)) {
NumberOfTriggersEvent numberOfTriggersEvent = new NumberOfTriggersEvent(ability, event);
// event == null - state based triggers like StateTriggeredAbility, must be ignored for number event
if (event == null || !game.replaceEvent(numberOfTriggersEvent)) {
if (event == null || !game.replaceEvent(numberOfTriggersEvent, ability)) {
for (int i = 0; i < numberOfTriggersEvent.getAmount(); i++) {
ability.trigger(game, ability.getControllerId(), event);
}

View file

@ -21,11 +21,9 @@ public interface TriggeredAbility extends Ability {
boolean checkEventType(GameEvent event, Game game);
/**
* This method checks if the event has to trigger the ability. It's
* important to do nothing unique within this method, that can't be done
* multiple times. Because some abilities call this to check if an ability
* is relevant (e.g. Torpor Orb), so the method is called multiple times for
* the same event.
* This method checks if the event has to trigger the ability,
* and if it does trigger, may set targets and other values in associated effects
* before returning true.
*/
boolean checkTrigger(GameEvent event, Game game);

View file

@ -0,0 +1,116 @@
package mage.abilities.effects.common.ruleModifying;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.events.NumberOfTriggersEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
/**
* @author xenohedron
*/
public class DontCauseTriggerEffect extends ContinuousRuleModifyingEffectImpl {
private final FilterPermanent filterEntering; // (or dying, if applicable)
private final boolean orDying;
private final FilterPermanent filterTriggering; // can be null if any abilities
/**
* "Creatures entering the battlefield don't cause abilities to trigger."
*/
public DontCauseTriggerEffect() {
this(StaticFilters.FILTER_PERMANENT_CREATURES, false, null);
}
/**
*
* @param filterEntering which permanents don't cause triggers on entering (or dying if applicable)
* @param orDying false = only ETB, true = also applies to dying
* @param filterTriggering if not all abilities e.g. "of permanents your opponents control", or null for all
*/
public DontCauseTriggerEffect(FilterPermanent filterEntering, boolean orDying, FilterPermanent filterTriggering) {
super(Duration.WhileOnBattlefield, Outcome.Neutral);
this.filterEntering = filterEntering;
this.orDying = orDying;
this.filterTriggering = filterTriggering;
staticText = filterEntering.getMessage() + " entering the battlefield"
+ (orDying ? " or dying" : "") + " don't cause abilities"
+ (filterTriggering == null ? "" : " of " + filterTriggering.getMessage())
+ " to trigger";
}
protected DontCauseTriggerEffect(final DontCauseTriggerEffect effect) {
super(effect);
this.filterEntering = effect.filterEntering;
this.orDying = effect.orDying;
this.filterTriggering = effect.filterTriggering;
}
@Override
public DontCauseTriggerEffect copy() {
return new DontCauseTriggerEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (filterTriggering != null) {
Ability ability = (Ability) getValue("targetAbility");
if (ability == null) {
return false;
}
Permanent triggeringPermanent = ability.getSourcePermanentOrLKI(game);
if (triggeringPermanent == null || !filterTriggering.match(triggeringPermanent, source.getControllerId(), source, game)) {
return false;
}
}
GameEvent sourceEvent = ((NumberOfTriggersEvent) event).getSourceEvent();
if (sourceEvent == null) {
return false;
}
switch (sourceEvent.getType()) {
case ENTERS_THE_BATTLEFIELD:
Permanent enteringPermanent = ((EntersTheBattlefieldEvent) sourceEvent).getTarget();
if (enteringPermanent != null && filterEntering.match(enteringPermanent, source.getControllerId(), source, game)) {
break; // Effect applies as permanent matches filter
}
return false;
case ZONE_CHANGE:
if (orDying && ((ZoneChangeEvent) sourceEvent).isDiesEvent()) {
Permanent dyingPermanent = ((ZoneChangeEvent) sourceEvent).getTarget();
if (dyingPermanent != null && filterEntering.match(dyingPermanent, source.getControllerId(), source, game)) {
break; // Effect applies as permanent matches filter
}
}
return false;
default:
return false;
}
return true; // NumberOfTriggers event fully replaced, no further replacement effects, no triggers
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
Ability ability = (Ability) getValue("targetAbility");
if (ability == null) {
return null;
}
MageObject abilityObject = game.getObject(ability.getSourceId());
if (abilityObject == null) {
return null;
}
return "Prevented ability of " + abilityObject.getLogName() + " from triggering.";
}
}