From 36c6210ceeac12ab816619fae36202c0ec69af44 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 20 Nov 2025 13:09:38 -0500 Subject: [PATCH] [TLE] Implement Tale of Momo --- .../src/mage/cards/k/KutzilsFlanker.java | 55 ++++--------------- Mage.Sets/src/mage/cards/t/TaleOfMomo.java | 48 ++++++++++++++++ .../sets/AvatarTheLastAirbenderEternal.java | 2 + .../common/CreatureLeftThisTurnCondition.java | 30 ++++++++++ .../CreatureLeftBattlefieldWatcher.java | 51 +++++++++++++++++ 5 files changed, 141 insertions(+), 45 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TaleOfMomo.java create mode 100644 Mage/src/main/java/mage/abilities/condition/common/CreatureLeftThisTurnCondition.java create mode 100644 Mage/src/main/java/mage/watchers/common/CreatureLeftBattlefieldWatcher.java diff --git a/Mage.Sets/src/mage/cards/k/KutzilsFlanker.java b/Mage.Sets/src/mage/cards/k/KutzilsFlanker.java index 79ecf0f0963..f9f53fe1a50 100644 --- a/Mage.Sets/src/mage/cards/k/KutzilsFlanker.java +++ b/Mage.Sets/src/mage/cards/k/KutzilsFlanker.java @@ -10,24 +10,18 @@ import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.WatcherScope; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.target.TargetPlayer; -import mage.util.CardUtil; -import mage.watchers.Watcher; +import mage.watchers.common.CreatureLeftBattlefieldWatcher; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** @@ -52,8 +46,8 @@ public final class KutzilsFlanker extends CardImpl { new AddCountersSourceEffect(CounterType.P1P1.createInstance(), KutzilsFlankerValue.instance, true) .setText("put a +1/+1 counter on {this} for each creature that left the battlefield under your control this turn") ); - ability.addHint(new ValueHint("Number of creatures that left", KutzilsFlankerValue.instance)); - ability.addWatcher(new KutzilsFlankerWatcher()); + ability.addHint(KutzilsFlankerValue.getHint()); + ability.addWatcher(new CreatureLeftBattlefieldWatcher()); // * You gain 2 life and scry 2. ability.addMode(new Mode( @@ -82,10 +76,15 @@ public final class KutzilsFlanker extends CardImpl { enum KutzilsFlankerValue implements DynamicValue { instance; + private static final Hint hint = new ValueHint("Number of creatures that left", KutzilsFlankerValue.instance); + + public static Hint getHint() { + return hint; + } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return KutzilsFlankerWatcher.getNumberCreatureLeft(sourceAbility.getControllerId(), game); + return CreatureLeftBattlefieldWatcher.getNumberCreatureLeft(sourceAbility.getControllerId(), game); } @Override @@ -103,37 +102,3 @@ enum KutzilsFlankerValue implements DynamicValue { return ""; } } - - -class KutzilsFlankerWatcher extends Watcher { - - // player -> number of creatures that left the battlefield under that player's control this turn - private final Map mapCreaturesLeft = new HashMap<>(); - - KutzilsFlankerWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { - return; - } - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() != Zone.BATTLEFIELD || !zEvent.getTarget().isCreature(game)) { - return; - } - mapCreaturesLeft.compute(zEvent.getTarget().getControllerId(), CardUtil::setOrIncrementValue); - } - - @Override - public void reset() { - super.reset(); - mapCreaturesLeft.clear(); - } - - public static int getNumberCreatureLeft(UUID playerId, Game game) { - KutzilsFlankerWatcher watcher = game.getState().getWatcher(KutzilsFlankerWatcher.class); - return watcher == null ? 0 : watcher.mapCreaturesLeft.getOrDefault(playerId, 0); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TaleOfMomo.java b/Mage.Sets/src/mage/cards/t/TaleOfMomo.java new file mode 100644 index 00000000000..b7f19660166 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TaleOfMomo.java @@ -0,0 +1,48 @@ +package mage.cards.t; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.CreatureLeftThisTurnCondition; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.watchers.common.CreatureLeftBattlefieldWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TaleOfMomo extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("an Ally creature card"); + + static { + filter.add(SubType.ALLY.getPredicate()); + } + + public TaleOfMomo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}"); + + // This spell costs {2} less to cast if a creature left the battlefield under your control this turn. + this.addAbility(new SimpleStaticAbility( + new SpellCostReductionSourceEffect(2, CreatureLeftThisTurnCondition.instance) + ).addHint(CreatureLeftThisTurnCondition.getHint()), new CreatureLeftBattlefieldWatcher()); + + // Search your library and/or graveyard for an Ally creature card, reveal it, and put it into your hand. If you search your library this way, shuffle. + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); + } + + private TaleOfMomo(final TaleOfMomo card) { + super(card); + } + + @Override + public TaleOfMomo copy() { + return new TaleOfMomo(this); + } +} diff --git a/Mage.Sets/src/mage/sets/AvatarTheLastAirbenderEternal.java b/Mage.Sets/src/mage/sets/AvatarTheLastAirbenderEternal.java index 423f7ff4698..e12e5227fad 100644 --- a/Mage.Sets/src/mage/sets/AvatarTheLastAirbenderEternal.java +++ b/Mage.Sets/src/mage/sets/AvatarTheLastAirbenderEternal.java @@ -269,6 +269,8 @@ public final class AvatarTheLastAirbenderEternal extends ExpansionSet { cards.add(new SetCardInfo("Suspicious Bookcase", 170, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class)); cards.add(new SetCardInfo("Swampbenders", 65, Rarity.RARE, mage.cards.s.Swampbenders.class)); cards.add(new SetCardInfo("Swiftfoot Boots", 317, Rarity.RARE, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Tale of Momo", 176, Rarity.RARE, mage.cards.t.TaleOfMomo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tale of Momo", 86, Rarity.RARE, mage.cards.t.TaleOfMomo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tarnished Citadel", 59, Rarity.MYTHIC, mage.cards.t.TarnishedCitadel.class)); cards.add(new SetCardInfo("Taunting Challenge", 46, Rarity.MYTHIC, mage.cards.t.TauntingChallenge.class)); cards.add(new SetCardInfo("Tectonic Split", 144, Rarity.RARE, mage.cards.t.TectonicSplit.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage/src/main/java/mage/abilities/condition/common/CreatureLeftThisTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CreatureLeftThisTurnCondition.java new file mode 100644 index 00000000000..b8356474519 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/CreatureLeftThisTurnCondition.java @@ -0,0 +1,30 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; +import mage.watchers.common.CreatureLeftBattlefieldWatcher; + +/** + * @author TheElk801 + */ +public enum CreatureLeftThisTurnCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return CreatureLeftBattlefieldWatcher.getNumberCreatureLeft(source.getControllerId(), game) > 0; + } + + @Override + public String toString() { + return "a creature left the battlefield under your control this turn"; + } +} diff --git a/Mage/src/main/java/mage/watchers/common/CreatureLeftBattlefieldWatcher.java b/Mage/src/main/java/mage/watchers/common/CreatureLeftBattlefieldWatcher.java new file mode 100644 index 00000000000..8fb44dac0a0 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/CreatureLeftBattlefieldWatcher.java @@ -0,0 +1,51 @@ +package mage.watchers.common; + +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author Susucr + */ +public class CreatureLeftBattlefieldWatcher extends Watcher { + + // player -> number of creatures that left the battlefield under that player's control this turn + private final Map mapCreaturesLeft = new HashMap<>(); + + public CreatureLeftBattlefieldWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (Zone.BATTLEFIELD.match(zEvent.getFromZone()) && zEvent.getTarget().isCreature(game)) { + mapCreaturesLeft.compute(zEvent.getTarget().getControllerId(), CardUtil::setOrIncrementValue); + } + } + + @Override + public void reset() { + super.reset(); + mapCreaturesLeft.clear(); + } + + public static int getNumberCreatureLeft(UUID playerId, Game game) { + return game + .getState() + .getWatcher(CreatureLeftBattlefieldWatcher.class) + .mapCreaturesLeft + .getOrDefault(playerId, 0); + } +}