From 0bba44b54fcb8bab8dc6f333906422381345d5f9 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sat, 7 Sep 2024 00:21:14 -0400 Subject: [PATCH] fix #10607 (Maskwood Nexus) closes #12554 --- .../src/mage/cards/a/AshesOfTheFallen.java | 15 +++- Mage.Sets/src/mage/cards/m/MaskwoodNexus.java | 87 +++++++++++-------- .../cards/single/khm/MaskwoodNexusTest.java | 71 +++++++++++++++ 3 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MaskwoodNexusTest.java diff --git a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java index 3a5906ce7d3..cc46bd2a3e4 100644 --- a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java @@ -1,7 +1,6 @@ - package mage.cards.a; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,6 +13,9 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; /** * @@ -62,7 +64,14 @@ class AshesOfTheFallenEffect extends ContinuousEffectImpl { for (UUID cardId : controller.getGraveyard()) { Card card = game.getCard(cardId); if (card != null && card.isCreature(game) && !card.hasSubtype(subType, game)) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); + MageObject mageObject = game.getObject(card.getId()); + if (mageObject != null) { + CardUtil.getObjectPartsAsObjects(mageObject).forEach(objectPart ->{ + if (objectPart.isCreature(game)) { + game.getState().getCreateMageObjectAttribute(objectPart, game).getSubtype().add(subType); + } + }); + } } } } else { diff --git a/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java b/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java index 57621cbad9c..eb4110d319d 100644 --- a/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java +++ b/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java @@ -1,5 +1,7 @@ package mage.cards.m; +import mage.MageItem; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,17 +15,19 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.command.CommandObject; import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.permanent.token.ShapeshifterBlueToken; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; +import mage.util.CardUtil; -import java.util.Iterator; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author TheElk801 @@ -81,46 +85,53 @@ class MaskwoodNexusEffect extends ContinuousEffectImpl { } // Creature cards you own that aren't on the battlefield // in graveyard - for (UUID cardId : controller.getGraveyard()) { - Card card = game.getCard(cardId); - if (card != null && card.isCreature(game)) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); - } - } + Set affectedCards = controller.getGraveyard().stream() + .map(game::getCard) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); // on Hand - for (UUID cardId : controller.getHand()) { - Card card = game.getCard(cardId); - if (card != null && card.isCreature(game)) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); - } - } + controller.getHand().stream() + .map(game::getCard) + .filter(Objects::nonNull) + .forEach(affectedCards::add); + // in Exile - for (Card card : game.getState().getExile().getAllCards(game)) { - if (card.isCreature(game) && card.isOwnedBy(controller.getId())) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); - } - } + game.getState().getExile().getAllCards(game, controller.getId()).stream() + .filter(card -> card.isOwnedBy(controller.getId())) + .forEach(affectedCards::add); + // in Library (e.g. for Mystical Teachings) - for (Card card : controller.getLibrary().getCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); - } - } + affectedCards.addAll(controller.getLibrary().getCards(game)); + // commander in command zone - for (CommandObject commandObject : game.getState().getCommand()) { - if (!(commandObject instanceof Commander)) { - continue; - } - Card card = game.getCard(((Commander) commandObject).getId()); - if (card != null - && card.isOwnedBy(controller.getId()) - && card.isCreature(game)) { - game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); - } - } + game.getState().getCommand().stream() + .filter(Commander.class::isInstance) + .map(MageItem::getId) + .map(game::getCard) + .filter(Objects::nonNull) + .filter(c -> c.isOwnedBy(controller.getId())) + .forEach(affectedCards::add); + + // Apply to all affected cards by object parts + affectedCards.stream() + .map(card -> game.getObject(card.getId())) + .filter(Objects::nonNull) + .forEach(mageObject -> { + if (mageObject.isCreature(game)) { + game.getState().getCreateMageObjectAttribute(mageObject, game).getSubtype().setIsAllCreatureTypes(true); + } + CardUtil.getObjectParts(mageObject).stream() + .filter(Objects::nonNull) + .forEach(objectId -> { + MageObject partObject = game.getObject(objectId); + if (partObject != null && partObject.isCreature(game)) { + game.getState().getCreateMageObjectAttribute(partObject, game).getSubtype().setIsAllCreatureTypes(true); + } + }); + }); + // creature spells you control - for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { - StackObject stackObject = iterator.next(); + for (StackObject stackObject : game.getStack()) { if (stackObject instanceof Spell && stackObject.isControlledBy(source.getControllerId()) && stackObject.isCreature(game)) { @@ -128,6 +139,7 @@ class MaskwoodNexusEffect extends ContinuousEffectImpl { game.getState().getCreateMageObjectAttribute(card, game).getSubtype().setIsAllCreatureTypes(true); } } + // creatures you control List creatures = game.getBattlefield().getAllActivePermanents( StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), game); @@ -136,6 +148,7 @@ class MaskwoodNexusEffect extends ContinuousEffectImpl { creature.setIsAllCreatureTypes(game, true); } } + return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MaskwoodNexusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MaskwoodNexusTest.java new file mode 100644 index 00000000000..b206475168b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/MaskwoodNexusTest.java @@ -0,0 +1,71 @@ +package org.mage.test.cards.single.khm; + +import mage.abilities.keyword.HexproofAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * {@link mage.cards.m.MaskwoodNexus Maskwood Nexus} + * {4} + * Artifact + * Creatures you control are every creature type. The same is true for creature spells you control and creature cards you own that aren't on the battlefield. + * {3}, {T}: Create a 2/2 blue Shapeshifter creature token with changeling. + * + * @author grimreap142 + */ + +public class MaskwoodNexusTest extends CardTestPlayerBase { + + private static final String maskwood = "Maskwood Nexus"; + private static final String magda = "Magda, Brazen Outlaw"; + private static final String pMonk = "Pinnacle Monk"; + private static final String jadeOrb = "Jade Orb of Dragonkind"; + + @Test + public void magdaSearch() { + addCard(Zone.BATTLEFIELD, playerA, magda); + addCard(Zone.LIBRARY, playerA, "Sol Ring"); + addCard(Zone.BATTLEFIELD, playerA, maskwood); + addCard(Zone.LIBRARY, playerA, pMonk); + // create 5 treasures + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + addCard(Zone.BATTLEFIELD, playerA, "Treasure Vault", 1); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}{X}, {T}, Sacrifice"); + setChoice(playerA, "X=5"); + + // activate Magda and select Pinnacle Monk + setChoice(playerA, "Treasure Token", 5); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice five Treasures:"); + addTarget(playerA, pMonk); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, pMonk, 1); + assertPermanentCount(playerA, magda, 1); + + } + + @Test + public void maskwoodAndJadeOrbCounterTest() { + addCard(Zone.HAND, playerA, pMonk); + addCard(Zone.BATTLEFIELD, playerA, maskwood, 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, jadeOrb); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pMonk); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertTapped(jadeOrb, true); + assertCounterCount(pMonk, CounterType.P1P1, 1); + assertAbility(playerA, pMonk, HexproofAbility.getInstance(), true); + } + +}