From 54a8e8ef317fe32f258c637780699ce2181c800f Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Thu, 4 Sep 2025 15:35:07 -0500 Subject: [PATCH] [SPM] implement Maximum Carnage --- .../src/mage/cards/m/MaximumCarnage.java | 90 +++++++++++++++++++ Mage.Sets/src/mage/sets/MarvelsSpiderMan.java | 2 + .../cards/single/spm/MaximumCarnageTest.java | 69 ++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MaximumCarnage.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java diff --git a/Mage.Sets/src/mage/cards/m/MaximumCarnage.java b/Mage.Sets/src/mage/cards/m/MaximumCarnage.java new file mode 100644 index 00000000000..ead24418699 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaximumCarnage.java @@ -0,0 +1,90 @@ +package mage.cards.m; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MaximumCarnage extends CardImpl { + + public MaximumCarnage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new AttacksIfAbleAllEffect( + new FilterOpponentsCreaturePermanent("each creature an opponent controls"), Duration.UntilYourNextTurn + ), new MaximumCarnageEffect()); + + // II -- Add {R}{R}{R}. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new AddManaToManaPoolSourceControllerEffect(new Mana(ManaType.RED, 3))); + + // III -- This Saga deals 5 damage to each opponent. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new DamagePlayersEffect(5, TargetController.OPPONENT)); + } + + private MaximumCarnage(final MaximumCarnage card) { + super(card); + } + + @Override + public MaximumCarnage copy() { + return new MaximumCarnage(this); + } +} +class MaximumCarnageEffect extends RestrictionEffect { + + MaximumCarnageEffect() { + super(Duration.UntilYourNextTurn); + staticText = "and attacks a player other than you if able"; + } + + private MaximumCarnageEffect(final MaximumCarnageEffect effect) { + super(effect); + } + + @Override + public MaximumCarnageEffect copy() { + return new MaximumCarnageEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return game.getOpponents(permanent.getControllerId()).contains(source.getControllerId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null + || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you + return true; + } + // A planeswalker controlled by the controller is the defender + if (game.getPermanent(defenderId) != null) { + return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); + } + // The controller is the defender + return !defenderId.equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java index 5775573ffe9..683d5c85b95 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java @@ -120,6 +120,8 @@ public final class MarvelsSpiderMan extends ExpansionSet { cards.add(new SetCardInfo("Mary Jane Watson", 134, Rarity.RARE, mage.cards.m.MaryJaneWatson.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mary Jane Watson", 229, Rarity.RARE, mage.cards.m.MaryJaneWatson.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Masked Meower", 82, Rarity.COMMON, mage.cards.m.MaskedMeower.class)); + cards.add(new SetCardInfo("Maximum Carnage", 225, Rarity.RARE, mage.cards.m.MaximumCarnage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maximum Carnage", 83, Rarity.RARE, mage.cards.m.MaximumCarnage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mechanical Mobster", 168, Rarity.COMMON, mage.cards.m.MechanicalMobster.class)); cards.add(new SetCardInfo("Merciless Enforcers", 58, Rarity.COMMON, mage.cards.m.MercilessEnforcers.class)); cards.add(new SetCardInfo("Miles Morales", 108, Rarity.MYTHIC, mage.cards.m.MilesMorales.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java new file mode 100644 index 00000000000..7807ecc4f90 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java @@ -0,0 +1,69 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Jmlundeen + */ +public class MaximumCarnageTest extends CardTestCommander4Players { + + /* + Maximum Carnage + {4}{R} + Enchantment - Saga + (As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.) + I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able. + II -- Add {R}{R}{R}. + III -- This Saga deals 5 damage to each opponent. + */ + private static final String maximumCarnage = "Maximum Carnage"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testMaximumCarnage() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, maximumCarnage); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerD, bearCub); + + addTarget(playerD, playerC); // must attack + addTarget(playerB, playerD); // must attack + + setStopAt(4, PhaseStep.END_TURN); + execute(); + } + + @Test + public void testMaximumCarnageCantAttackController() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, maximumCarnage); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerD, bearCub); + + addTarget(playerD, playerA); // must attack + + setStopAt(4, PhaseStep.END_TURN); + try { + execute(); + } catch (AssertionError e) { + assertTrue("Shouldn't be able to attack playerA", + e.getMessage().contains("[targetPlayer=PlayerA], but not used")); + } + } +} \ No newline at end of file