From 4ab36880fe75c7ca8f4f96b55d74067163b7f857 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:20:12 -0400 Subject: [PATCH] new common BecomesTargetSourceFirstTimeTriggeredAbility --- Mage.Sets/src/mage/cards/a/AngelicCub.java | 54 +++------------ Mage.Sets/src/mage/cards/g/GlyphKeeper.java | 67 +++--------------- .../src/mage/cards/j/JettingGlasskite.java | 63 +++-------------- .../mage/cards/k/KiraGreatGlassSpinner.java | 68 +++---------------- .../src/mage/cards/s/ShimmeringGlasskite.java | 64 +++-------------- ...TargetSourceFirstTimeTriggeredAbility.java | 41 +++++++++++ ...rOfTimesPermanentTargetedATurnWatcher.java | 38 +++++------ 7 files changed, 108 insertions(+), 287 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/a/AngelicCub.java b/Mage.Sets/src/mage/cards/a/AngelicCub.java index 460320f9aaa..a95f7ccb184 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicCub.java +++ b/Mage.Sets/src/mage/cards/a/AngelicCub.java @@ -1,7 +1,7 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -12,18 +12,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; import java.util.UUID; /** - * @author AustinYQM + * @author AustinYQM, xenohedron */ public final class AngelicCub extends CardImpl { @@ -36,7 +33,10 @@ public final class AngelicCub extends CardImpl { this.toughness = new MageInt(1); // Whenever Angelic Cub becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it. - this.addAbility(new AngelicCubAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false + )); // As long as Angelic Cub has three or more +1/+1 counters on it, it has flying. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.P1P1, 3), "As long as {this} has three or more +1/+1 counters on it, it has flying."))); } @@ -50,41 +50,3 @@ public final class AngelicCub extends CardImpl { return new AngelicCub(this); } } - -class AngelicCubAbility extends TriggeredAbilityImpl { - - public AngelicCubAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - public AngelicCubAbility(final mage.cards.a.AngelicCubAbility ability) { - super(ability); - } - - @Override - public mage.cards.a.AngelicCubAbility copy() { - return new mage.cards.a.AngelicCubAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - return watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game); - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java index 2644ded03be..5572b1f7ec4 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java +++ b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java @@ -1,28 +1,23 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.EmbalmAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author Styxo + * @author Styxo, xenohedron */ public final class GlyphKeeper extends CardImpl { @@ -37,7 +32,10 @@ public final class GlyphKeeper extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Glyph Keeper becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new GlyphKeeperAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); // Embalm {5}{U}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl<>("{5}{U}{U}"), this)); @@ -53,48 +51,3 @@ public final class GlyphKeeper extends CardImpl { return new GlyphKeeper(this); } } - -class GlyphKeeperAbility extends TriggeredAbilityImpl { - - public GlyphKeeperAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private GlyphKeeperAbility(final GlyphKeeperAbility ability) { - super(ability); - } - - @Override - public GlyphKeeperAbility copy() { - return new GlyphKeeperAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null - && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java index 419194b95ed..ab68e05e1fe 100644 --- a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java +++ b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class JettingGlasskite extends CardImpl { @@ -34,7 +31,10 @@ public final class JettingGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Jetting Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new JettingGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +47,3 @@ public final class JettingGlasskite extends CardImpl { return new JettingGlasskite(this); } } - -class JettingGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public JettingGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private JettingGlasskiteAbility(final JettingGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public JettingGlasskiteAbility copy() { - return new JettingGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java index ad727125a6b..09faf71ee3b 100644 --- a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java +++ b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java @@ -1,10 +1,9 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.FlyingAbility; @@ -12,11 +11,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +import java.util.UUID; /** * @@ -36,12 +32,12 @@ public final class KiraGreatGlassSpinner extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability." - Effect effect = new CounterTargetEffect(); - effect.setText("counter that spell or ability"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(new KiraGreatGlassSpinnerAbility(effect), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURES)), - new NumberOfTimesPermanentTargetedATurnWatcher()); + TriggeredAbility gainedAbility = new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + ).setTriggerPhrase("Whenever this creature becomes the target of a spell or ability for the first time each turn, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + gainedAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES))); } @@ -54,47 +50,3 @@ public final class KiraGreatGlassSpinner extends CardImpl { return new KiraGreatGlassSpinner(this); } } - -class KiraGreatGlassSpinnerAbility extends TriggeredAbilityImpl { - - public KiraGreatGlassSpinnerAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - private KiraGreatGlassSpinnerAbility(final KiraGreatGlassSpinnerAbility ability) { - super(ability); - } - - @Override - public KiraGreatGlassSpinnerAbility copy() { - return new KiraGreatGlassSpinnerAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java index c2e8b828d92..c32f0be66df 100644 --- a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java +++ b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class ShimmeringGlasskite extends CardImpl { @@ -26,7 +23,6 @@ public final class ShimmeringGlasskite extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); this.subtype.add(SubType.SPIRIT); - this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -34,7 +30,10 @@ public final class ShimmeringGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Shimmering Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new ShimmeringGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +46,3 @@ public final class ShimmeringGlasskite extends CardImpl { return new ShimmeringGlasskite(this); } } - -class ShimmeringGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public ShimmeringGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private ShimmeringGlasskiteAbility(final ShimmeringGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public ShimmeringGlasskiteAbility copy() { - return new ShimmeringGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java new file mode 100644 index 00000000000..bf895161c74 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java @@ -0,0 +1,41 @@ +package mage.abilities.common; + +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterStackObject; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +/** + * @author xenohedron + */ +public class BecomesTargetSourceFirstTimeTriggeredAbility extends BecomesTargetSourceTriggeredAbility { + + public BecomesTargetSourceFirstTimeTriggeredAbility(Effect effect, FilterStackObject filter, + SetTargetPointer setTargetPointer, boolean optional) { + super(effect, filter, setTargetPointer, optional); + this.addWatcher(new NumberOfTimesPermanentTargetedATurnWatcher()); + setTriggerPhrase("Whenever {this} becomes the target of " + filter.getMessage() + " for the first time each turn, "); + } + + protected BecomesTargetSourceFirstTimeTriggeredAbility(final BecomesTargetSourceFirstTimeTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesTargetSourceFirstTimeTriggeredAbility copy() { + return new BecomesTargetSourceFirstTimeTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); + if (permanent == null || watcher == null || watcher.numTimesTargetedThisTurn(permanent, game) > 1) { + return false; + } + return super.checkTrigger(event, game); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index 1df8fa84d1f..55e1b0419e0 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -1,16 +1,17 @@ - - package mage.watchers.common; -import java.util.HashMap; -import java.util.Map; import mage.MageObjectReference; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; + /** * * @author LevelX2 @@ -25,24 +26,23 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TARGETED) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - MageObjectReference mor = new MageObjectReference(permanent, game); - int amount = 0; - if (permanentsTargeted.containsKey(mor)) { - amount = permanentsTargeted.get(mor); - } - permanentsTargeted.put(mor, ++amount); - } + if (event.getType() != GameEvent.EventType.TARGETED) { + return; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !CardUtil.checkTargetMap(null, targetingObject, event, game)) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + MageObjectReference mor = new MageObjectReference(permanent, game); + int nTimes = permanentsTargeted.computeIfAbsent(mor, k -> 0); + permanentsTargeted.put(mor, nTimes + 1); } } - public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) { - if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) { - return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2; - } - return true; + public int numTimesTargetedThisTurn(Permanent permanent, Game game) { + return permanentsTargeted.computeIfAbsent(new MageObjectReference(permanent, game), k -> 0); } @Override