From 4772658527452bef1b235e96b77f99973bf0af01 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Wed, 19 Nov 2025 13:19:11 -0500 Subject: [PATCH] [TLA] Implement The Last Agni Kai --- .../src/mage/cards/e/EzurisPredation.java | 2 +- Mage.Sets/src/mage/cards/r/RhinosRampage.java | 49 +++++----- .../src/mage/cards/t/TheLastAgniKai.java | 92 +++++++++++++++++++ .../src/mage/sets/AvatarTheLastAirbender.java | 2 + .../continuous/YouDontLoseManaEffect.java | 11 ++- .../java/mage/game/permanent/Permanent.java | 9 +- .../mage/game/permanent/PermanentImpl.java | 11 ++- 7 files changed, 141 insertions(+), 35 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TheLastAgniKai.java diff --git a/Mage.Sets/src/mage/cards/e/EzurisPredation.java b/Mage.Sets/src/mage/cards/e/EzurisPredation.java index dec66cf05f3..a478c3ce212 100644 --- a/Mage.Sets/src/mage/cards/e/EzurisPredation.java +++ b/Mage.Sets/src/mage/cards/e/EzurisPredation.java @@ -92,7 +92,7 @@ class EzurisPredationEffect extends OneShotEffect { Permanent opponentCreature = creaturesOfOpponents.iterator().next(); creaturesOfOpponents.remove(opponentCreature); // can be multiple tokens, so must be used custom BATCH_FIGHT event - token.fight(opponentCreature, source, game, false); + token.fightWithExcess(opponentCreature, source, game, false); morSet.add(new MageObjectReference(token, game)); morSet.add(new MageObjectReference(opponentCreature, game)); game.informPlayers(token.getLogName() + " fights " + opponentCreature.getLogName()); diff --git a/Mage.Sets/src/mage/cards/r/RhinosRampage.java b/Mage.Sets/src/mage/cards/r/RhinosRampage.java index 8ed68d09334..47840bb8e46 100644 --- a/Mage.Sets/src/mage/cards/r/RhinosRampage.java +++ b/Mage.Sets/src/mage/cards/r/RhinosRampage.java @@ -4,50 +4,40 @@ import mage.abilities.Ability; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; +import java.util.List; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** - * * @author Jmlundeen */ public final class RhinosRampage extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public RhinosRampage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R/G}"); - // Target creature you control gets +1/+0 until end of turn. It fights target creature an opponent controls. When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature artifact with mana value 3 or less. this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); - this.getSpellAbility().addEffect(new FightTargetsEffect() - .setText("It fights target creature an opponent controls")); this.getSpellAbility().addEffect(new RhinosRampageEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); - + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); } private RhinosRampage(final RhinosRampage card) { @@ -62,10 +52,17 @@ public final class RhinosRampage extends CardImpl { class RhinosRampageEffect extends OneShotEffect { + private static final FilterPermanent filter = new FilterArtifactPermanent("noncreature artifact with mana value 3 or less"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); + } + RhinosRampageEffect() { super(Outcome.BoostCreature); - staticText = "When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature " + - "artifact with mana value 3 or less"; + staticText = "it fights target creature an opponent controls. When excess damage is dealt to the creature " + + "an opponent controls this way, destroy up to one target noncreature artifact with mana value 3 or less."; } protected RhinosRampageEffect(final RhinosRampageEffect effect) { @@ -79,14 +76,20 @@ class RhinosRampageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getTargets().get(1).getFirstTarget()); - if (permanent == null || permanent.getDamage() <= permanent.getToughness().getBaseValue()) { + List permanents = this + .getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.size() < 2) { return false; } - FilterPermanent filter = new FilterArtifactPermanent("noncreature artifact with mana value 3 or less"); - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); - + int excess = permanents.get(0).fightWithExcess(permanents.get(1), source, game, true); + if (excess < 1) { + return true; + } ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetPermanent(0, 1, filter)); game.fireReflexiveTriggeredAbility(ability, source); diff --git a/Mage.Sets/src/mage/cards/t/TheLastAgniKai.java b/Mage.Sets/src/mage/cards/t/TheLastAgniKai.java new file mode 100644 index 00000000000..a35026f1559 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheLastAgniKai.java @@ -0,0 +1,92 @@ +package mage.cards.t; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.YouDontLoseManaEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.ManaType; +import mage.constants.Outcome; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class TheLastAgniKai extends CardImpl { + + public TheLastAgniKai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Target creature you control fights target creature an opponent controls. If the creature the opponent controls is dealt excess damage this way, add that much {R}. + this.getSpellAbility().addEffect(new TheLastAgniKaiEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + + // Until end of turn, you don't lose unspent red mana as steps and phases end. + this.getSpellAbility().addEffect(new YouDontLoseManaEffect(Duration.EndOfTurn, ManaType.RED).concatBy("
")); + } + + private TheLastAgniKai(final TheLastAgniKai card) { + super(card); + } + + @Override + public TheLastAgniKai copy() { + return new TheLastAgniKai(this); + } +} + +class TheLastAgniKaiEffect extends OneShotEffect { + + TheLastAgniKaiEffect() { + super(Outcome.Benefit); + staticText = "target creature you control fights target creature an opponent controls. " + + "If the creature the opponent controls is dealt excess damage this way, add that much {R}"; + this.setTargetPointer(new EachTargetPointer()); + } + + private TheLastAgniKaiEffect(final TheLastAgniKaiEffect effect) { + super(effect); + } + + @Override + public TheLastAgniKaiEffect copy() { + return new TheLastAgniKaiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = this + .getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.size() < 2) { + return false; + } + int excess = permanents.get(0).fightWithExcess(permanents.get(1), source, game, true); + if (excess > 0) { + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.getManaPool().addMana(Mana.RedMana(excess), game, source)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java b/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java index faedc678e60..5ce5843a5a1 100644 --- a/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java +++ b/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java @@ -347,6 +347,8 @@ public final class AvatarTheLastAirbender extends ExpansionSet { cards.add(new SetCardInfo("The Earth King", 344, Rarity.RARE, mage.cards.t.TheEarthKing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fire Nation Drill", 321, Rarity.RARE, mage.cards.t.TheFireNationDrill.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fire Nation Drill", 98, Rarity.RARE, mage.cards.t.TheFireNationDrill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Last Agni Kai", 144, Rarity.RARE, mage.cards.t.TheLastAgniKai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Last Agni Kai", 314, Rarity.RARE, mage.cards.t.TheLastAgniKai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Legend of Kuruk", 355, Rarity.MYTHIC, mage.cards.t.TheLegendOfKuruk.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Legend of Kuruk", 61, Rarity.MYTHIC, mage.cards.t.TheLegendOfKuruk.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Legend of Kyoshi", 186, Rarity.MYTHIC, mage.cards.t.TheLegendOfKyoshi.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/YouDontLoseManaEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/YouDontLoseManaEffect.java index 2646319f1da..1f3afd4589e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/YouDontLoseManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/YouDontLoseManaEffect.java @@ -14,8 +14,13 @@ public class YouDontLoseManaEffect extends ContinuousEffectImpl { private final ManaType manaType; public YouDontLoseManaEffect(ManaType manaType) { - super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); - staticText = "you don't lose unspent " + manaType + " mana as steps and phases end"; + this(Duration.WhileOnBattlefield, manaType); + } + + public YouDontLoseManaEffect(Duration duration, ManaType manaType) { + super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); + staticText = (duration == Duration.EndOfTurn ? "until end of turn, " : "") + + "you don't lose unspent " + manaType + " mana as steps and phases end"; this.manaType = manaType; } @@ -37,4 +42,4 @@ public class YouDontLoseManaEffect extends ContinuousEffectImpl { } return false; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index f73c83bf1a0..d043c659e5c 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -223,7 +223,10 @@ public interface Permanent extends Card, Controllable { boolean fight(Permanent fightTarget, Ability source, Game game); - boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger); + /** + * Resolves a fight and returns the amount of excess damage dealt to fightTarget + */ + int fightWithExcess(Permanent fightTarget, Ability source, Game game, boolean batchTrigger); boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent); @@ -475,7 +478,7 @@ public interface Permanent extends Card, Controllable { void setHarnessed(boolean value); boolean wasRoomUnlockedOnCast(); - + boolean isLeftDoorUnlocked(); boolean isRightDoorUnlocked(); @@ -483,7 +486,7 @@ public interface Permanent extends Card, Controllable { boolean unlockRoomOnCast(Game game); boolean unlockDoor(Game game, Ability source, boolean isLeftDoor); - + @Override Permanent copy(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 1e094470adf..e76497c4a9c 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -2024,16 +2024,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean fight(Permanent fightTarget, Ability source, Game game) { - return this.fight(fightTarget, source, game, true); + this.fightWithExcess(fightTarget, source, game, true); + return true; } @Override - public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) { + public int fightWithExcess(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) { // double fight events for each creature game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, fightTarget.getId(), source, source.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, getId(), source, source.getControllerId())); damage(fightTarget.getPower().getValue(), fightTarget.getId(), source, game); - fightTarget.damage(getPower().getValue(), getId(), source, game); + int excess = fightTarget.damageWithExcess(getPower().getValue(), getId(), source, game); if (batchTrigger) { Set morSet = new HashSet<>(); @@ -2044,7 +2045,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BATCH_FIGHT, getId(), source, source.getControllerId(), data, 0)); } - return true; + return excess; } @Override @@ -2153,4 +2154,4 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return true; } -} \ No newline at end of file +}