rework triggered ability class for putting counters on permanents

fix #13870 (Kate Stewart; All Will Be One)
This commit is contained in:
xenohedron 2025-08-09 19:25:28 -04:00
parent 8994c8e7aa
commit 6f398ca2fc
11 changed files with 122 additions and 171 deletions

View file

@ -12,6 +12,8 @@ import mage.filter.common.FilterCreaturePlayerOrPlaneswalker;
import mage.filter.common.FilterPermanentOrPlayer;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetPermanentOrPlayer;
import java.util.UUID;
@ -68,6 +70,16 @@ class AllWillBeOneTriggeredAbility extends TriggeredAbilityImpl {
if (!isControlledBy(event.getPlayerId())) {
return false;
}
Player player = game.getPlayer(event.getTargetId());
if (player == null) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
if (permanent == null) {
return false;
}
}
}
getEffects().setValue("damage", event.getAmount());
return true;
}

View file

@ -1,16 +1,14 @@
package mage.cards.g;
import mage.MageInt;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.filter.StaticFilters;
import java.util.UUID;
@ -19,13 +17,6 @@ import java.util.UUID;
*/
public final class GenerousPatron extends CardImpl {
private static final FilterPermanent filter
= new FilterCreaturePermanent("creature you don't control");
static {
filter.add(TargetController.NOT_YOU.getControllerPredicate());
}
public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
@ -38,7 +29,8 @@ public final class GenerousPatron extends CardImpl {
this.addAbility(new SupportAbility(this, 2));
// Whenever you put one or more counters on a creature you don't control, draw a card.
this.addAbility(new PutCounterOnCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), filter));
this.addAbility(new PutCounterOnPermanentTriggeredAbility(new DrawCardSourceControllerEffect(1),
null, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
}
private GenerousPatron(final GenerousPatron card) {

View file

@ -3,7 +3,7 @@ package mage.cards.h;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
@ -12,6 +12,7 @@ import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.permanent.token.DeathtouchSnakeToken;
import mage.target.common.TargetCreaturePermanent;
@ -37,7 +38,8 @@ public final class HapatraVizierOfPoisons extends CardImpl {
this.addAbility(ability);
// Whenever you put one or more -1/-1 counters on a creature, create a 1/1 green Snake creature token with deathtouch.
this.addAbility(new PutCounterOnCreatureTriggeredAbility(new CreateTokenEffect(new DeathtouchSnakeToken()), CounterType.M1M1.createInstance()));
this.addAbility(new PutCounterOnPermanentTriggeredAbility(new CreateTokenEffect(new DeathtouchSnakeToken()),
CounterType.M1M1, StaticFilters.FILTER_PERMANENT_CREATURE));
}
private HapatraVizierOfPoisons(final HapatraVizierOfPoisons card) {

View file

@ -2,8 +2,8 @@ package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
@ -18,7 +18,6 @@ import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.SoldierToken;
import java.util.UUID;
@ -38,7 +37,8 @@ public final class KateStewart extends CardImpl {
this.toughness = new MageInt(3);
// Whenever you put one or more time counters on a permanent you control, create a 1/1 white Soldier creature token.
this.addAbility(new KateStewartTriggeredAbility());
this.addAbility(new PutCounterOnPermanentTriggeredAbility(new CreateTokenEffect(new SoldierToken()),
CounterType.TIME, StaticFilters.FILTER_CONTROLLED_PERMANENT));
// Whenever Kate Stewart attacks, you may pay {8}. If you do, attacking creatures get +X/+X until end of turn, where X is the number of time counters among permanents you control.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new BoostAllEffect(
@ -57,35 +57,6 @@ public final class KateStewart extends CardImpl {
}
}
class KateStewartTriggeredAbility extends TriggeredAbilityImpl {
KateStewartTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SoldierToken()));
setTriggerPhrase("Whenever you put one or more time counters on a permanent you control, ");
}
private KateStewartTriggeredAbility(final KateStewartTriggeredAbility ability) {
super(ability);
}
@Override
public KateStewartTriggeredAbility copy() {
return new KateStewartTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return CounterType.TIME.getName().equals(event.getData())
&& this.isControlledBy(event.getPlayerId())
&& this.isControlledBy(game.getControllerId(event.getTargetId()));
}
}
enum KateStewartValue implements DynamicValue {
instance;
private static final Hint hint = new ValueHint("Time counters among permanents you control", instance);

View file

@ -3,7 +3,7 @@ package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.combat.GoadTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@ -42,7 +42,8 @@ public final class KrosDefenseContractor extends CardImpl {
this.addAbility(ability);
// Whenever you put one or more counters on a creature you don't control, tap that creature and goad it. It gains trample until your next turn.
this.addAbility(new PutCounterOnCreatureTriggeredAbility(new KrosDefenseContractorEffect(), null, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, true));
this.addAbility(new PutCounterOnPermanentTriggeredAbility(new KrosDefenseContractorEffect(),
null, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, true, false));
}
private KrosDefenseContractor(final KrosDefenseContractor card) {

View file

@ -1,12 +1,13 @@
package mage.cards.n;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.dynamicvalue.common.EffectKeyValue;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.permanent.token.NestOfScarabsBlackInsectToken;
import java.util.UUID;
@ -20,10 +21,10 @@ public final class NestOfScarabs extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
// Whenever you put one or more -1/-1 counters on a creature, create that many 1/1 black Insect creature tokens.
this.addAbility(new PutCounterOnCreatureTriggeredAbility(
this.addAbility(new PutCounterOnPermanentTriggeredAbility(
new CreateTokenEffect(new NestOfScarabsBlackInsectToken(),
new EffectKeyValue("countersAdded", "that many")),
CounterType.M1M1.createInstance()));
CounterType.M1M1, StaticFilters.FILTER_PERMANENT_CREATURE));
}
private NestOfScarabs(final NestOfScarabs card) {

View file

@ -1,11 +1,9 @@
package mage.cards.o;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsDamageToACreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
@ -15,6 +13,9 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import java.util.UUID;
/**
*
@ -36,10 +37,9 @@ public final class ObeliskSpider extends CardImpl {
this.addAbility(new DealsDamageToACreatureTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)), true, false, true));
// Whenever you put one or more -1/-1 counters on a creature, each opponent loses 1 life and you gain 1 life.
Ability ability = new PutCounterOnCreatureTriggeredAbility(new LoseLifeOpponentsEffect(1), CounterType.M1M1.createInstance());
Effect effect = new GainLifeEffect(1);
effect.setText("and you gain 1 life");
ability.addEffect(effect);
Ability ability = new PutCounterOnPermanentTriggeredAbility(new LoseLifeOpponentsEffect(1),
CounterType.M1M1, StaticFilters.FILTER_PERMANENT_CREATURE);
ability.addEffect(new GainLifeEffect(1).concatBy("and"));
this.addAbility(ability);
}

View file

@ -3,7 +3,7 @@ package mage.cards.o;
import mage.MageInt;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.dynamicvalue.common.CountersSourceCount;
import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@ -49,10 +49,9 @@ public final class OmarthisGhostfireInitiate extends CardImpl {
// Whenever you put one or more +1/+1 counters on another colorless creature, you may put a +1/+1 counter on Omarthis.
this.addAbility(
new PutCounterOnCreatureTriggeredAbility(
new PutCounterOnPermanentTriggeredAbility(
new AddCountersSourceEffect(CounterType.P1P1.createInstance()),
CounterType.P1P1.createInstance(), filter,
false, true
CounterType.P1P1, filter, false, true
)
);

View file

@ -1,6 +1,6 @@
package mage.cards.t;
import mage.abilities.common.PutCounterOnCreatureTriggeredAbility;
import mage.abilities.common.PutCounterOnPermanentTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.EffectKeyValue;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@ -23,9 +23,9 @@ public final class Terrasymbiosis extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
// Whenever you put one or more +1/+1 counters on a creature you control, you may draw that many cards. Do this only once each turn.
this.addAbility(new PutCounterOnCreatureTriggeredAbility(
this.addAbility(new PutCounterOnPermanentTriggeredAbility(
new DrawCardSourceControllerEffect(xValue),
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE
CounterType.P1P1, StaticFilters.FILTER_CONTROLLED_CREATURE
).setDoOnlyOnceEachTurn(true));
}

View file

@ -1,106 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
* "Whenever you put one or more counters on a creature " triggered ability
*
* @author PurpleCrowbar
*/
public class PutCounterOnCreatureTriggeredAbility extends TriggeredAbilityImpl {
private final Counter counterType; // when null, any counter type is accepted
private final FilterPermanent filter;
private final boolean setTargetPointer;
public PutCounterOnCreatureTriggeredAbility(Effect effect) {
this(effect, (Counter) null);
}
public PutCounterOnCreatureTriggeredAbility(Effect effect, Counter counter) {
this(effect, counter, new FilterCreaturePermanent());
}
public PutCounterOnCreatureTriggeredAbility(Effect effect, FilterPermanent filter) {
this(effect, null, filter);
}
public PutCounterOnCreatureTriggeredAbility(Effect effect, Counter counter, FilterPermanent filter) {
this(effect, counter, filter, false);
}
public PutCounterOnCreatureTriggeredAbility(Effect effect, Counter counter, FilterPermanent filter, boolean setTargetPointer) {
this(effect, counter, filter, setTargetPointer, false);
}
public PutCounterOnCreatureTriggeredAbility(Effect effect, Counter counter, FilterPermanent filter, boolean setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.counterType = counter;
this.filter = filter;
this.setTargetPointer = setTargetPointer;
setFilterMessage();
}
protected PutCounterOnCreatureTriggeredAbility(final PutCounterOnCreatureTriggeredAbility ability) {
super(ability);
this.counterType = ability.counterType;
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public PutCounterOnCreatureTriggeredAbility copy() {
return new PutCounterOnCreatureTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!isControlledBy(event.getPlayerId())) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
}
if (permanent == null || !filter.match(permanent, controllerId, this, game)) {
return false;
}
if (counterType != null && !event.getData().equals(counterType.getName())) {
return false;
}
if (setTargetPointer) {
getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
}
getEffects().setValue("countersAdded", event.getAmount());
return true;
}
private void setFilterMessage() {
String filterMessage = filter.getMessage();
if (!filterMessage.startsWith("another")) {
filterMessage = "a " + filterMessage;
}
if (this.counterType == null) {
setTriggerPhrase("Whenever you put one or more counters on " + filterMessage + ", ");
} else {
setTriggerPhrase("Whenever you put one or more " + this.counterType.getName() + " counters on " + filterMessage + ", ");
}
}
}

View file

@ -0,0 +1,79 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* "Whenever you put one or more [] counters on a []" triggered ability
*
* @author PurpleCrowbar
*/
public class PutCounterOnPermanentTriggeredAbility extends TriggeredAbilityImpl {
private final CounterType counterType; // when null, any counter type is accepted
private final FilterPermanent filter;
private final boolean setTargetPointer;
public PutCounterOnPermanentTriggeredAbility(Effect effect, CounterType counterType, FilterPermanent filter) {
this(effect, counterType, filter, false, false);
}
public PutCounterOnPermanentTriggeredAbility(Effect effect, CounterType counterType, FilterPermanent filter,
boolean setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.counterType = counterType;
this.filter = filter;
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever you put one or more "
+ (this.counterType == null ? "" : this.counterType.getName() + " ")
+ "counters on " + CardUtil.addArticle(filter.getMessage()) + ", ");
}
protected PutCounterOnPermanentTriggeredAbility(final PutCounterOnPermanentTriggeredAbility ability) {
super(ability);
this.counterType = ability.counterType;
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public PutCounterOnPermanentTriggeredAbility copy() {
return new PutCounterOnPermanentTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!isControlledBy(event.getPlayerId())) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
}
if (permanent == null || !filter.match(permanent, controllerId, this, game)) {
return false;
}
if (counterType != null && !event.getData().equals(counterType.getName())) {
return false;
}
if (setTargetPointer) {
getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
}
getEffects().setValue("countersAdded", event.getAmount());
return true;
}
}