From 94db57e9d4409092ad172b13e18067cf40ae6f2c Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 8 Dec 2024 19:04:59 -0500 Subject: [PATCH 1/8] cleanup obsolete comment --- .../effects/common/UntapAllThatAttackedEffect.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapAllThatAttackedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapAllThatAttackedEffect.java index ba6707d9aee..3070c20ed70 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapAllThatAttackedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapAllThatAttackedEffect.java @@ -1,8 +1,5 @@ - package mage.abilities.effects.common; -import java.util.Set; - import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -11,11 +8,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.watchers.common.AttackedThisTurnWatcher; +import java.util.Set; + /** - * !!!! This effect needs the adding of the watcher in the using card class - *

- * this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); - * * @author LevelX2 */ public class UntapAllThatAttackedEffect extends OneShotEffect { -- 2.47.2 From 24687eb4afdcdb8ea893b6be84bed16f8e5696a1 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 8 Dec 2024 20:25:51 -0500 Subject: [PATCH 2/8] implement [CLB] Hezrou --- Mage.Sets/src/mage/cards/h/Hezrou.java | 121 ++++++++++++++++++ .../src/mage/cards/s/SizzlingBarrage.java | 52 +------- .../CommanderLegendsBattleForBaldursGate.java | 1 + .../test/cards/single/clb/HezrouTest.java | 84 ++++++++++++ .../permanent/BlockedThisTurnPredicate.java | 26 ++++ .../common/BlockedThisTurnWatcher.java | 8 +- 6 files changed, 237 insertions(+), 55 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/h/Hezrou.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/clb/HezrouTest.java create mode 100644 Mage/src/main/java/mage/filter/predicate/permanent/BlockedThisTurnPredicate.java diff --git a/Mage.Sets/src/mage/cards/h/Hezrou.java b/Mage.Sets/src/mage/cards/h/Hezrou.java new file mode 100644 index 00000000000..97033f959ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/Hezrou.java @@ -0,0 +1,121 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.AdventureCard; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.BlockedThisTurnPredicate; +import mage.filter.predicate.permanent.BlockingPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.watchers.common.BlockedThisTurnWatcher; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author xenohedron + */ +public final class Hezrou extends AdventureCard { + + private static final FilterCreaturePermanent filterBlocked = new FilterCreaturePermanent("each creature that blocked this turn"); + private static final FilterCreaturePermanent filterBlocking = new FilterCreaturePermanent("each blocking creature"); + static { + filterBlocked.add(BlockedThisTurnPredicate.instance); + filterBlocking.add(BlockingPredicate.instance); + } + + + public Hezrou(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new CardType[]{CardType.INSTANT}, "{5}{B}{B}", "Demonic Stench", "{B}"); + + this.subtype.add(SubType.FROG); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Whenever one or more creatures you control become blocked, each blocking creature gets -1/-1 until end of turn. + this.addAbility(new HezrouTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filterBlocking, false))); + + // Demonic Stench + // Each creature that blocked this turn gets -1/-1 until end of turn. + this.getSpellCard().getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filterBlocked, false)); + this.getSpellCard().getSpellAbility().addWatcher(new BlockedThisTurnWatcher()); + + this.finalizeAdventure(); + } + + private Hezrou(final Hezrou card) { + super(card); + } + + @Override + public Hezrou copy() { + return new Hezrou(this); + } +} + +class HezrouTriggeredAbility extends TriggeredAbilityImpl { + + HezrouTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + setTriggerPhrase("Whenever one or more creatures you control become blocked, "); + } + + private HezrouTriggeredAbility(final HezrouTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP + || event.getType() == GameEvent.EventType.BATCH_BLOCK_NONCOMBAT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (event.getType()) { + case DECLARE_BLOCKERS_STEP: + return game.getCombat() + .getGroups() + .stream() + .filter(CombatGroup::getBlocked) + .map(CombatGroup::getAttackers) + .flatMap(Collection::stream) + .map(game::getControllerId) + .anyMatch(this.getControllerId()::equals); + case BATCH_BLOCK_NONCOMBAT: + Object value = game.getState().getValue("becameBlocked_" + event.getData()); + if (!(value instanceof Set)) { + return false; + } + Set permanents = (Set) value; + return permanents + .stream() + .map(mor -> mor.getPermanentOrLKIBattlefield(game)) + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .anyMatch(this.getControllerId()::equals); + default: + return false; + } + } + + @Override + public HezrouTriggeredAbility copy() { + return new HezrouTriggeredAbility(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/s/SizzlingBarrage.java b/Mage.Sets/src/mage/cards/s/SizzlingBarrage.java index b814d6ea560..4c5732ea0be 100644 --- a/Mage.Sets/src/mage/cards/s/SizzlingBarrage.java +++ b/Mage.Sets/src/mage/cards/s/SizzlingBarrage.java @@ -1,22 +1,15 @@ package mage.cards.s; -import mage.MageObjectReference; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.WatcherScope; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.predicate.permanent.BlockedThisTurnPredicate; import mage.target.TargetPermanent; -import mage.watchers.Watcher; +import mage.watchers.common.BlockedThisTurnWatcher; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -27,7 +20,7 @@ public final class SizzlingBarrage extends CardImpl { private static final FilterPermanent filter = new FilterCreaturePermanent("creature that blocked this turn"); static { - filter.add(SizzlingBarragePredicate.instance); + filter.add(BlockedThisTurnPredicate.instance); } public SizzlingBarrage(UUID ownerId, CardSetInfo setInfo) { @@ -36,7 +29,7 @@ public final class SizzlingBarrage extends CardImpl { // Sizzling Barrage deals 4 damage to target creature that blocked this turn. this.getSpellAbility().addEffect(new DamageTargetEffect(4)); this.getSpellAbility().addTarget(new TargetPermanent(filter)); - this.getSpellAbility().addWatcher(new SizzlingBarrageWatcher()); + this.getSpellAbility().addWatcher(new BlockedThisTurnWatcher()); } private SizzlingBarrage(final SizzlingBarrage card) { @@ -48,40 +41,3 @@ public final class SizzlingBarrage extends CardImpl { return new SizzlingBarrage(this); } } - -enum SizzlingBarragePredicate implements Predicate { - instance; - - @Override - public boolean apply(Permanent input, Game game) { - SizzlingBarrageWatcher watcher = game.getState().getWatcher(SizzlingBarrageWatcher.class); - return watcher != null && watcher.checkCreature(input, game); - } -} - -class SizzlingBarrageWatcher extends Watcher { - - private final Set blockers = new HashSet<>(); - - SizzlingBarrageWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.BLOCKER_DECLARED) { - return; - } - blockers.add(new MageObjectReference(event.getSourceId(), game)); - } - - @Override - public void reset() { - super.reset(); - this.blockers.clear(); - } - - boolean checkCreature(Permanent permanent, Game game) { - return blockers.stream().anyMatch(mor -> mor.refersTo(permanent, game)); - } -} diff --git a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java index e0647542c95..504c284a888 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java +++ b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java @@ -305,6 +305,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Hedron Archive", 861, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class)); cards.add(new SetCardInfo("Herald's Horn", 862, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class)); cards.add(new SetCardInfo("Hex", 757, Rarity.RARE, mage.cards.h.Hex.class)); + cards.add(new SetCardInfo("Hezrou", 131, Rarity.COMMON, mage.cards.h.Hezrou.class)); cards.add(new SetCardInfo("High Priest of Penance", 848, Rarity.RARE, mage.cards.h.HighPriestOfPenance.class)); cards.add(new SetCardInfo("Highland Forest", 896, Rarity.COMMON, mage.cards.h.HighlandForest.class)); cards.add(new SetCardInfo("Hoarding Ogre", 181, Rarity.COMMON, mage.cards.h.HoardingOgre.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/clb/HezrouTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/clb/HezrouTest.java new file mode 100644 index 00000000000..08e44084059 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/clb/HezrouTest.java @@ -0,0 +1,84 @@ +package org.mage.test.cards.single.clb; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class HezrouTest extends CardTestPlayerBase { + + private static final String hezrou = "Hezrou"; // 6/6 + // Whenever one or more creatures you control become blocked, each blocking creature gets -1/-1 until end of turn. + private static final String stench = "Demonic Stench"; // {B} Instant + // Each creature that blocked this turn gets -1/-1 until end of turn. + + private static final String kraken = "Kraken Hatchling"; // 0/4 + private static final String guard = "Maritime Guard"; // 1/3 + private static final String fortress = "Fortress Crab"; // 1/6 + private static final String turtle = "Aegis Turtle"; // 0/5 + private static final String pangolin = "Gloom Pangolin"; // 1/5 + private static final String wishcoin = "Wishcoin Crab"; // 2/5 + + private void setupCreatures() { + addCard(Zone.BATTLEFIELD, playerA, kraken); + addCard(Zone.BATTLEFIELD, playerA, guard); + addCard(Zone.BATTLEFIELD, playerA, fortress); + addCard(Zone.BATTLEFIELD, playerB, turtle); + addCard(Zone.BATTLEFIELD, playerB, pangolin); + addCard(Zone.BATTLEFIELD, playerB, wishcoin); + } + + @Test + public void testTrigger() { + setupCreatures(); + addCard(Zone.BATTLEFIELD, playerA, hezrou); + + attack(1, playerA, kraken, playerB); + attack(1, playerA, guard, playerB); + attack(1, playerA, fortress, playerB); + block(1, playerB, turtle, kraken); + block(1, playerB, pangolin, guard); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + assertPowerToughness(playerA, kraken, 0, 4); + assertPowerToughness(playerA, guard, 1, 3); + assertPowerToughness(playerA, fortress, 1, 6); + assertPowerToughness(playerB, turtle, -1, 4); + assertPowerToughness(playerB, pangolin, 0, 4); + assertPowerToughness(playerB, wishcoin, 2, 5); + } + + @Test + public void testAdventure() { + setupCreatures(); + addCard(Zone.HAND, playerA, hezrou); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + attack(1, playerA, kraken, playerB); + attack(1, playerA, guard, playerB); + attack(1, playerA, fortress, playerB); + block(1, playerB, turtle, kraken); + block(1, playerB, pangolin, guard); + + castSpell(1, PhaseStep.END_COMBAT, playerA, stench); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + assertPowerToughness(playerA, kraken, 0, 4); + assertPowerToughness(playerA, guard, 1, 3); + assertPowerToughness(playerA, fortress, 1, 6); + assertPowerToughness(playerB, turtle, -1, 4); + assertPowerToughness(playerB, pangolin, 0, 4); + assertPowerToughness(playerB, wishcoin, 2, 5); + } + +} diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/BlockedThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/BlockedThisTurnPredicate.java new file mode 100644 index 00000000000..30718c1742f --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/permanent/BlockedThisTurnPredicate.java @@ -0,0 +1,26 @@ +package mage.filter.predicate.permanent; + +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.BlockedThisTurnWatcher; + +/** + * Requires BlockedThisTurnWatcher to be added to the card + * + * @author xenohedron + */ +public enum BlockedThisTurnPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + BlockedThisTurnWatcher watcher = game.getState().getWatcher(BlockedThisTurnWatcher.class); + return watcher != null && watcher.checkIfBlocked(input, game); + } + + @Override + public String toString() { + return "blocked this turn"; + } +} diff --git a/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java index 70b08e29c3d..62804a17a2a 100644 --- a/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java @@ -1,4 +1,3 @@ - package mage.watchers.common; import mage.MageObjectReference; @@ -34,12 +33,7 @@ public class BlockedThisTurnWatcher extends Watcher { } public boolean checkIfBlocked(Permanent permanent, Game game) { - for (MageObjectReference mor : blockedThisTurnCreatures) { - if (mor.refersTo(permanent, game)) { - return true; - } - } - return false; + return blockedThisTurnCreatures.stream().anyMatch(mor -> mor.refersTo(permanent, game)); } @Override -- 2.47.2 From e9b29f7776a53e6cfc074bb5113abdb7a52c5396 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 8 Dec 2024 20:28:06 -0500 Subject: [PATCH 3/8] real fix for b5710802 --- Mage.Sets/src/mage/cards/c/ComeBackWrong.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ComeBackWrong.java b/Mage.Sets/src/mage/cards/c/ComeBackWrong.java index 8ec12a76218..6a055cfcfaf 100644 --- a/Mage.Sets/src/mage/cards/c/ComeBackWrong.java +++ b/Mage.Sets/src/mage/cards/c/ComeBackWrong.java @@ -68,7 +68,7 @@ class ComeBackWrongEffect extends OneShotEffect { permanent.destroy(source, game); // tokens are not creature cards if (permanent instanceof PermanentToken) { - return false; + return true; } Card card = permanent.getMainCard(); if (card == null @@ -78,11 +78,11 @@ class ComeBackWrongEffect extends OneShotEffect { } Player player = game.getPlayer(source.getControllerId()); if (player == null) { - return false; + return true; } player.moveCards(card, Zone.BATTLEFIELD, source, game); Permanent creature = game.getPermanent(card.getId()); - if (permanent != null) { + if (creature != null) { game.addDelayedTriggeredAbility(new AtTheBeginOfPlayersNextEndStepDelayedTriggeredAbility( new SacrificeTargetEffect("sacrifice it") .setTargetPointer(new FixedTarget(creature, game)), -- 2.47.2 From 272a189e8bf9d42215d0be6d85e2a938a5af0609 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 8 Dec 2024 20:46:56 -0500 Subject: [PATCH 4/8] update Portal Mage to common class --- Mage.Sets/src/mage/cards/p/PortalMage.java | 94 ++-------------------- 1 file changed, 6 insertions(+), 88 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/PortalMage.java b/Mage.Sets/src/mage/cards/p/PortalMage.java index a5b88d854fa..2f2c2c130c2 100644 --- a/Mage.Sets/src/mage/cards/p/PortalMage.java +++ b/Mage.Sets/src/mage/cards/p/PortalMage.java @@ -1,4 +1,3 @@ - package mage.cards.p; import mage.MageInt; @@ -6,29 +5,18 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.IsStepCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.ReselectDefenderAttackedByTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.SubType; -import mage.filter.StaticFilters; -import mage.filter.common.FilterAttackingCreature; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; -import mage.target.common.TargetDefender; +import mage.target.common.TargetAttackingCreature; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.UUID; /** - * * @author LevelX2 */ public final class PortalMage extends CardImpl { @@ -46,11 +34,11 @@ public final class PortalMage extends CardImpl { // When Portal Mage enters the battlefield during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking. Ability ability = new ConditionalTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new PortalMageEffect(), true), + new EntersBattlefieldTriggeredAbility(new ReselectDefenderAttackedByTargetEffect(true), true), new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), - "When {this} enters during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking. " - + "(It can't attack its controller or its controller's planeswalkers.)"); - ability.addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + "When {this} enters during the declare attackers step, you may reselect which player or permanent target attacking creature is attacking. " + + "(It can't attack its controller or their permanents)"); + ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability); } @@ -63,73 +51,3 @@ public final class PortalMage extends CardImpl { return new PortalMage(this); } } - -class PortalMageEffect extends OneShotEffect { - - PortalMageEffect() { - super(Outcome.Benefit); - this.staticText = "you may reselect which player or planeswalker target attacking creature is attacking"; - } - - private PortalMageEffect(final PortalMageEffect effect) { - super(effect); - } - - @Override - public PortalMageEffect copy() { - return new PortalMageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent attackingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (attackingCreature != null) { - CombatGroup combatGroupTarget = null; - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getAttackers().contains(attackingCreature.getId())) { - combatGroupTarget = combatGroup; - break; - } - } - if (combatGroupTarget == null) { - return false; - } - // Reselecting which player or planeswalker a creature is attacking ignores all requirements, - // restrictions, and costs associated with attacking. - - // Update possible defender - Set defenders = new LinkedHashSet<>(); - for (UUID playerId : game.getCombat().getAttackablePlayers(game)) { - defenders.add(playerId); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, playerId, game)) { - defenders.add(permanent.getId()); - } - } - // Select the new defender - TargetDefender target = new TargetDefender(defenders); - if (controller.chooseTarget(Outcome.Damage, target, source, game)) { - if (combatGroupTarget.getDefenderId() != null && !combatGroupTarget.getDefenderId().equals(target.getFirstTarget())) { - if (combatGroupTarget.changeDefenderPostDeclaration(target.getFirstTarget(), game)) { - String attacked = ""; - Player player = game.getPlayer(target.getFirstTarget()); - if (player != null) { - attacked = player.getLogName(); - } else { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - attacked = permanent.getLogName(); - } - } - game.informPlayers(attackingCreature.getLogName() + " now attacks " + attacked); - return true; - } - } - } - } - return true; - } - return false; - } -} -- 2.47.2 From 08934f3f947d66f4d4a75feab34f02323f803a98 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 8 Dec 2024 21:11:16 -0500 Subject: [PATCH 5/8] implement [CLB] Mirror of Life Trapping --- .../mage/cards/l/LightPawsEmperorsVoice.java | 2 +- .../mage/cards/m/MirrorOfLifeTrapping.java | 112 ++++++++++++++++++ .../CommanderLegendsBattleForBaldursGate.java | 1 + 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java diff --git a/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java b/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java index 07cfe7d7ddd..f477729d9bc 100644 --- a/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java +++ b/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java @@ -74,7 +74,7 @@ enum LightPawsEmperorsVoicePredicate implements Predicate { class LightPawsEmperorsVoiceEffect extends OneShotEffect { - private static enum LightPawsEmperorsVoiceEffectPredicate implements ObjectSourcePlayerPredicate { + private enum LightPawsEmperorsVoiceEffectPredicate implements ObjectSourcePlayerPredicate { instance; private static final FilterPermanent filter = new FilterControlledPermanent(SubType.AURA); diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java b/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java new file mode 100644 index 00000000000..8e6e36841d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java @@ -0,0 +1,112 @@ +package mage.cards.m; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author xenohedron + */ +public final class MirrorOfLifeTrapping extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); + static { + filter.add(MirrorOfLifeTrappingCastPredicate.instance); + } + + public MirrorOfLifeTrapping(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // Whenever a creature enters, if it was cast, exile it, then return all other permanent cards exiled with Mirror of Life Trapping to the battlefield under their owners' control. + this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new MirrorOfLifeTrappingEffect(), + filter, false, SetTargetPointer.PERMANENT)); + } + + private MirrorOfLifeTrapping(final MirrorOfLifeTrapping card) { + super(card); + } + + @Override + public MirrorOfLifeTrapping copy() { + return new MirrorOfLifeTrapping(this); + } +} + +enum MirrorOfLifeTrappingCastPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + int zcc = input.getZoneChangeCounter(game); + Spell spell = game.getStack().getSpell(input.getId()); + return (spell != null && spell.getZoneChangeCounter(game) == zcc - 1) + || game.getLastKnownInformation(input.getId(), Zone.STACK, zcc - 1) != null; + } +} + +class MirrorOfLifeTrappingEffect extends OneShotEffect { + + MirrorOfLifeTrappingEffect() { + super(Outcome.Benefit); + this.staticText = "exile it, then return all other permanent cards exiled with {this} to the battlefield under their owners' control"; + } + + private MirrorOfLifeTrappingEffect(final MirrorOfLifeTrappingEffect effect) { + super(effect); + } + + @Override + public MirrorOfLifeTrappingEffect copy() { + return new MirrorOfLifeTrappingEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { + return false; + } + + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ExileZone exileZone = game.getExile().getExileZone(exileZoneId); + + Cards toBattlefield = null; + if (exileZone != null && !exileZone.isEmpty()) { + toBattlefield = new CardsImpl(exileZone); + } + + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + controller.moveCardsToExile(permanent, source, game, true, exileZoneId, sourceObject.getIdName()); + } + + if (toBattlefield != null) { + game.processAction(); + controller.moveCards(toBattlefield.getCards(StaticFilters.FILTER_CARD_PERMANENT, game), + Zone.BATTLEFIELD, source, game, false, true, true, null); + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java index 504c284a888..da21969f229 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java +++ b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java @@ -404,6 +404,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 285, Rarity.MYTHIC, mage.cards.m.MinscBooTimelessHeroes.class)); cards.add(new SetCardInfo("Minthara, Merciless Soul", 286, Rarity.UNCOMMON, mage.cards.m.MintharaMercilessSoul.class)); cards.add(new SetCardInfo("Mirror Entity", 701, Rarity.RARE, mage.cards.m.MirrorEntity.class)); + cards.add(new SetCardInfo("Mirror of Life Trapping", 326, Rarity.RARE, mage.cards.m.MirrorOfLifeTrapping.class)); cards.add(new SetCardInfo("Mizzium Mortars", 803, Rarity.RARE, mage.cards.m.MizziumMortars.class)); cards.add(new SetCardInfo("Mocking Doppelganger", 667, Rarity.RARE, mage.cards.m.MockingDoppelganger.class)); cards.add(new SetCardInfo("Modify Memory", 83, Rarity.UNCOMMON, mage.cards.m.ModifyMemory.class)); -- 2.47.2 From 6aabc6c9c9243103105c017cd93b71f0e7110fc1 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 9 Dec 2024 19:49:07 -0500 Subject: [PATCH 6/8] fix First Responder effect --- Mage.Sets/src/mage/cards/f/FirstResponder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/f/FirstResponder.java b/Mage.Sets/src/mage/cards/f/FirstResponder.java index 46a55e0c1dc..b509be1e801 100644 --- a/Mage.Sets/src/mage/cards/f/FirstResponder.java +++ b/Mage.Sets/src/mage/cards/f/FirstResponder.java @@ -84,7 +84,7 @@ class FirstResponderEffect extends OneShotEffect { player.moveCards(permanent, Zone.HAND, source, game); int power = permanent.getPower().getValue(); Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); - if (power < 1 || sourcePermanent == null) { + if (power > 0 && sourcePermanent != null) { sourcePermanent.addCounters(CounterType.P1P1.createInstance(power), source, game); } return true; -- 2.47.2 From 15a1e618fab11bcd46dbfe1285b5dabee084e5dd Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 10 Dec 2024 18:17:55 +0400 Subject: [PATCH 7/8] client: improved default run scripts for better compatibility with non-ascii paths, increased default memory limit to 2 GB (#13121) --- Mage.Client/release/startClient.bat | 2 +- Mage.Client/release/startClient.command | 2 +- Mage.Client/release/startClient.sh | 2 +- Mage.Client/release/startClientWin7.bat | 2 +- Mage.Client/src/main/java/mage/client/MageFrame.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage.Client/release/startClient.bat b/Mage.Client/release/startClient.bat index 6ebff32baba..22acbfcd3b7 100644 --- a/Mage.Client/release/startClient.bat +++ b/Mage.Client/release/startClient.bat @@ -4,4 +4,4 @@ set JAVA_HOME="C:\Program Files\Java\jre7\" set CLASSPATH=%JAVA_HOME%/bin;%CLASSPATH% set PATH=%JAVA_HOME%/bin;%PATH% :NOJAVADIR -java -Xmx1024m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar .\lib\mage-client-${project.version}.jar \ No newline at end of file +java -Xmx2000m -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar .\lib\mage-client-${project.version}.jar \ No newline at end of file diff --git a/Mage.Client/release/startClient.command b/Mage.Client/release/startClient.command index 1c259967a9a..effd525f0b5 100644 --- a/Mage.Client/release/startClient.command +++ b/Mage.Client/release/startClient.command @@ -2,4 +2,4 @@ cd "`dirname "$0"`" -java -Xmx1024m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar ./lib/mage-client-${project.version}.jar & \ No newline at end of file +java -Xmx2000m -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar ./lib/mage-client-${project.version}.jar & \ No newline at end of file diff --git a/Mage.Client/release/startClient.sh b/Mage.Client/release/startClient.sh index e9f64a2c8d3..b5028fb28d2 100644 --- a/Mage.Client/release/startClient.sh +++ b/Mage.Client/release/startClient.sh @@ -1,3 +1,3 @@ #!/bin/sh -java -Xmx1024m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar ./lib/mage-client-${project.version}.jar & \ No newline at end of file +java -Xmx2000m -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -jar ./lib/mage-client-${project.version}.jar & \ No newline at end of file diff --git a/Mage.Client/release/startClientWin7.bat b/Mage.Client/release/startClientWin7.bat index 1c8a443a22f..5008d8cb19a 100644 --- a/Mage.Client/release/startClientWin7.bat +++ b/Mage.Client/release/startClientWin7.bat @@ -4,4 +4,4 @@ set JAVA_HOME="C:\Program Files (x86)\Java\jre7\" set CLASSPATH=%JAVA_HOME%/bin;%CLASSPATH% set PATH=%JAVA_HOME%/bin;%PATH% :NOJAVADIR -java -Xmx1024m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -jar .\lib\mage-client-${project.version}.jar \ No newline at end of file +java -Xmx2000m -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -jar .\lib\mage-client-${project.version}.jar \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 376f7c40bf6..07ce7d54e7d 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -1502,7 +1502,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { if (!Charset.defaultCharset().toString().equals("UTF-8")) { LOGGER.warn("WARNING, bad charset. Some images will not be downloaded. You must:"); LOGGER.warn("* Open launcher -> settings -> java -> client java options"); - LOGGER.warn("* Insert additional command at the the end: -Dfile.encoding=UTF-8"); + LOGGER.warn("* Insert at the the end: -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"); } startTime = System.currentTimeMillis(); -- 2.47.2 From 8f809cab3c0457c4187ba42535bc6b71fba1f6d5 Mon Sep 17 00:00:00 2001 From: Steven Knipe Date: Tue, 10 Dec 2024 15:46:23 -0800 Subject: [PATCH 8/8] Fix Orim's Chant (#13130) --- Mage.Sets/src/mage/cards/o/OrimsThunder.java | 45 +++++--------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OrimsThunder.java b/Mage.Sets/src/mage/cards/o/OrimsThunder.java index d19a4b374ff..78e00bd54c1 100644 --- a/Mage.Sets/src/mage/cards/o/OrimsThunder.java +++ b/Mage.Sets/src/mage/cards/o/OrimsThunder.java @@ -5,6 +5,7 @@ import mage.abilities.Ability; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -32,15 +33,15 @@ public final class OrimsThunder extends CardImpl { this.addAbility(new KickerAbility("{R}")); // Destroy target artifact or enchantment. If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature. - this.getSpellAbility().addEffect(new OrimsThunderEffect()); + this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new OrimsThunderEffect2(), + new OrimsThunderDamageEffect(), KickedCondition.ONCE, "If this spell was kicked, it deals damage equal to that permanent's mana value to target creature") ); - this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetCreaturePermanent())); + this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster( + KickedCondition.ONCE, true, new TargetCreaturePermanent())); } private OrimsThunder(final OrimsThunder card) { @@ -53,13 +54,13 @@ public final class OrimsThunder extends CardImpl { } } -class OrimsThunderEffect2 extends OneShotEffect { +class OrimsThunderDamageEffect extends OneShotEffect { - OrimsThunderEffect2() { + OrimsThunderDamageEffect() { super(Outcome.Damage); } - private OrimsThunderEffect2(final OrimsThunderEffect2 effect) { + private OrimsThunderDamageEffect(final OrimsThunderDamageEffect effect) { super(effect); } @@ -80,33 +81,7 @@ class OrimsThunderEffect2 extends OneShotEffect { } @Override - public OrimsThunderEffect2 copy() { - return new OrimsThunderEffect2(this); + public OrimsThunderDamageEffect copy() { + return new OrimsThunderDamageEffect(this); } } - -class OrimsThunderEffect extends OneShotEffect { - - OrimsThunderEffect() { - super(Outcome.DestroyPermanent); - staticText = "Destroy target artifact or enchantment"; - } - - private OrimsThunderEffect(final OrimsThunderEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(source.getFirstTarget()); - if (target != null) { - return target.destroy(source, game, false); - } - return false; - } - - @Override - public OrimsThunderEffect copy() { - return new OrimsThunderEffect(this); - } -} \ No newline at end of file -- 2.47.2