From c67ce93ec4b1170bc53b1b9d321a731dfbdf1bc1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 8 Jan 2021 11:03:39 +0100 Subject: [PATCH] * Fixed some problems with color changes of cards and spells - e.g. Painter's Servant (fixes #7325 fixes #6487). --- .../src/mage/cards/a/ArcaneAdaptation.java | 29 ++-- .../src/mage/cards/a/AshesOfTheFallen.java | 2 +- Mage.Sets/src/mage/cards/c/Conspiracy.java | 46 +++---- .../src/mage/cards/m/MycosynthLattice.java | 18 ++- .../src/mage/cards/p/PaintersServant.java | 23 ++-- .../cards/continuous/PaintersServantTest.java | 52 +++++++ .../duel/CommanderColorChangeTest.java | 127 ++++++++++++++++++ Mage/src/main/java/mage/MageObjectImpl.java | 16 ++- .../mage/abilities/keyword/MorphAbility.java | 4 +- Mage/src/main/java/mage/cards/CardImpl.java | 30 +---- Mage/src/main/java/mage/game/GameState.java | 30 ++--- ...ttribute.java => MageObjectAttribute.java} | 22 +-- Mage/src/main/java/mage/game/stack/Spell.java | 10 +- 13 files changed, 294 insertions(+), 115 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java rename Mage/src/main/java/mage/game/{CardAttribute.java => MageObjectAttribute.java} (50%) diff --git a/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java b/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java index a788844dcbf..236f5ca99c7 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java @@ -1,5 +1,8 @@ package mage.cards.a; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -11,15 +14,13 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; +import mage.game.command.CommandObject; +import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; - /** * @author TheElk801 */ @@ -70,37 +71,37 @@ class ConspyEffect extends ContinuousEffectImpl { for (UUID cardId : controller.getGraveyard()) { Card card = game.getCard(cardId); if (card != null && card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } // on Hand for (UUID cardId : controller.getHand()) { Card card = game.getCard(cardId); if (card != null && card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } // in Exile for (Card card : game.getState().getExile().getAllCards(game)) { if (card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } // in Library (e.g. for Mystical Teachings) for (Card card : controller.getLibrary().getCards(game)) { if (card.isOwnedBy(controller.getId()) && card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } // commander in command zone - for (UUID commanderId : game.getCommandersIds(controller)) { - if (game.getState().getZone(commanderId) == Zone.COMMAND) { - Card card = game.getCard(commanderId); + for (CommandObject commandObject : game.getState().getCommand()) { + if (commandObject instanceof Commander) { + Card card = game.getCard(((Commander) commandObject).getId()); if (card != null && card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } - } + } // creature spells you control for (Iterator iterator = game.getStack().iterator(); iterator.hasNext(); ) { StackObject stackObject = iterator.next(); @@ -109,7 +110,7 @@ class ConspyEffect extends ContinuousEffectImpl { && stackObject.isCreature() && !stackObject.hasSubtype(subType, game)) { Card card = ((Spell) stackObject).getCard(); - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } // creatures you control diff --git a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java index 8af731bee2b..25f2014d46d 100644 --- a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java @@ -62,7 +62,7 @@ class AshesOfTheFallenEffect extends ContinuousEffectImpl { for (UUID cardId : controller.getGraveyard()) { Card card = game.getCard(cardId); if (card != null && card.isCreature() && !card.hasSubtype(subType, game)) { - game.getState().getCreateCardAttribute(card, game).getSubtype().add(subType); + game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } } } else { diff --git a/Mage.Sets/src/mage/cards/c/Conspiracy.java b/Mage.Sets/src/mage/cards/c/Conspiracy.java index 4c34b8c5bc2..69f6bd22b41 100644 --- a/Mage.Sets/src/mage/cards/c/Conspiracy.java +++ b/Mage.Sets/src/mage/cards/c/Conspiracy.java @@ -1,5 +1,8 @@ package mage.cards.c; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; @@ -10,16 +13,14 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; +import mage.game.command.CommandObject; +import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; import mage.util.SubTypeList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; /** * @author bunchOfDevs @@ -101,30 +102,31 @@ public final class Conspiracy extends CardImpl { setCreatureSubtype(card, subType, game); } } - // commander in command zone - for (UUID commanderId : game.getCommandersIds(controller)) { - if (game.getState().getZone(commanderId) == Zone.COMMAND) { - Card card = game.getCard(commanderId); - if (card != null && card.isCreature()) { + // in command zone + for (CommandObject commandObject : game.getState().getCommand()) { + if (commandObject instanceof Commander) { + Card card = game.getCard(((Commander) commandObject).getId()); + if (card != null && card.isCreature() && card.isOwnedBy(controller.getId())) { setCreatureSubtype(card, subType, game); } } - } + } // creature spells you control for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { StackObject stackObject = iterator.next(); if (stackObject instanceof Spell - && stackObject.isControlledBy(source.getControllerId()) + && stackObject.isControlledBy(controller.getId()) && stackObject.isCreature()) { - Card card = ((Spell) stackObject).getCard(); - setCreatureSubtype(card, subType, game); + setCreatureSubtype(stackObject, subType, game); + setCreatureSubtype(((Spell) stackObject).getCard(), subType, game); } } // creatures you control - List creatures = game.getState().getBattlefield().getAllActivePermanents( - new FilterControlledCreaturePermanent(), source.getControllerId(), game); - for (Permanent creature : creatures) { - setCreatureSubtype(creature, subType, game); + List permanents = game.getState().getBattlefield().getAllActivePermanents(controller.getId()); + for (Permanent permanent : permanents) { + if (permanent.isCreature()) { + setCreatureSubtype(permanent, subType, game); + } } } return true; @@ -134,14 +136,8 @@ public final class Conspiracy extends CardImpl { private void setCreatureSubtype(MageObject object, SubType subtype, Game game) { if (object != null) { - if (object instanceof Card) { - Card card = (Card) object; - setChosenSubtype( - game.getState().getCreateCardAttribute(card, game).getSubtype(), - subtype); - } else { - setChosenSubtype(object.getSubtype(game), subtype); - } + setChosenSubtype(game.getState() + .getCreateMageObjectAttribute(object, game).getSubtype(), subtype); } } diff --git a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java index 7696c63df90..95a6f1a1f2a 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java @@ -15,6 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.command.CommandObject; +import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.ManaPoolItem; @@ -95,31 +96,36 @@ class EverythingIsColorlessEffect extends ContinuousEffectImpl { // spells for (MageObject object : game.getStack()) { if (object instanceof Spell) { - object.getColor(game).setColor(colorless); + game.getState().getCreateMageObjectAttribute(object, game).getColor().setColor(colorless); } } // exile for (Card card : game.getExile().getAllCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().setColor(colorless); + game.getState().getCreateMageObjectAttribute(card, game).getColor().setColor(colorless); } // command for (CommandObject commandObject : game.getState().getCommand()) { - commandObject.getColor(game).setColor(colorless); + if (commandObject instanceof Commander) { + Card card = game.getCard(((Commander) commandObject).getId()); + if (card != null) { + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(colorless); + } + } } for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { // hand for (Card card : player.getHand().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().setColor(colorless); + game.getState().getCreateMageObjectAttribute(card, game).getColor().setColor(colorless); } // library for (Card card : player.getLibrary().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().setColor(colorless); + game.getState().getCreateMageObjectAttribute(card, game).getColor().setColor(colorless); } // graveyard for (Card card : player.getGraveyard().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().setColor(colorless); + game.getState().getCreateMageObjectAttribute(card, game).getColor().setColor(colorless); } } } diff --git a/Mage.Sets/src/mage/cards/p/PaintersServant.java b/Mage.Sets/src/mage/cards/p/PaintersServant.java index 2afe5f848b7..f1a5627bd4d 100644 --- a/Mage.Sets/src/mage/cards/p/PaintersServant.java +++ b/Mage.Sets/src/mage/cards/p/PaintersServant.java @@ -14,18 +14,18 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.command.CommandObject; +import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; -import mage.game.command.Commander; /** * @@ -78,17 +78,22 @@ class PaintersServantEffect extends ContinuousEffectImpl { // Stack for (MageObject object : game.getStack()) { if (object instanceof Spell) { - object.getColor(game).addColor(color); + game.getState().getCreateMageObjectAttribute(object, game).getColor().addColor(color); + Card card = ((Spell) object).getCard(); + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); } - } + } // Exile for (Card card : game.getExile().getAllCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().addColor(color); + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); } // Command for (CommandObject commandObject : game.getState().getCommand()) { if (commandObject instanceof Commander) { - commandObject.getColor(game).addColor(color); + Card card = game.getCard(((Commander) commandObject).getId()); + if (card != null) { + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); + } } } @@ -97,15 +102,15 @@ class PaintersServantEffect extends ContinuousEffectImpl { if (player != null) { // Hand for (Card card : player.getHand().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().addColor(color); + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); } // Library for (Card card : player.getLibrary().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().addColor(color); + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); } // Graveyard for (Card card : player.getGraveyard().getCards(game)) { - game.getState().getCreateCardAttribute(card, game).getColor().addColor(color); + game.getState().getCreateMageObjectAttribute(card, game).getColor().addColor(color); } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java index 627ff17cd90..7b6c33d264d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java @@ -219,5 +219,57 @@ public class PaintersServantTest extends CardTestPlayerBase { } + /** + * https://github.com/magefree/mage/issues/6487 + * I was playing mtg and had a Painter's Servant on the board, naming blue. + * A player tried to cast a green sun's zenith, it was targeted by pyroblast + * and in response the painter was exiled but the zenith was still countered. + */ + + @Test + public void testColorSpellEnds() { + setStrictChooseMode(true); + + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); // Artifact Creature {2} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // Choose one - Counter target spell if it's blue; or destroy target permanent if it's blue. + addCard(Zone.HAND, playerA, "Pyroblast", 1); // Instant {R} + + // Search your library for a green creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. + // Shuffle Green Sun's Zenith into its owner's library. + addCard(Zone.HAND, playerB, "Green Sun's Zenith", 1); // Sorcery {X}{G} + addTarget(playerB, "Ambush Viper"); + // Exile target artifact or enchantment. + addCard(Zone.HAND, playerB, "Altar's Light", 1); // Instant {2}{W}{W} + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 6); + // Flash, Deathtouch + addCard(Zone.LIBRARY, playerB, "Ambush Viper", 2); // Creature 2/1 {1}{G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Green Sun's Zenith"); + setChoice(playerB, "X=2"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyroblast", "Green Sun's Zenith"); + setModeChoice(playerA, "1"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Altar's Light", "Painter's Servant", "Pyroblast"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Pyroblast", 1); + assertGraveyardCount(playerB, "Altar's Light", 1); + assertLibraryCount(playerB, "Green Sun's Zenith", 1); + + assertExileCount(playerA, "Painter's Servant", 1); + + + assertPermanentCount(playerB, "Ambush Viper", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java new file mode 100644 index 00000000000..b28101e754a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderColorChangeTest.java @@ -0,0 +1,127 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.commander.duel; + +import java.io.FileNotFoundException; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.GameException; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ + +public class CommanderColorChangeTest extends CardTestCommanderDuelBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + // When a player casts a spell or a creature attacks, exile Norin the Wary. Return it to the battlefield under its owner's control at the beginning of the next end step. + setDecknamePlayerA("CMDNorinTheWary.dck"); // Commander = Norin the Wary {R} + return super.createNewGameAndPlayers(); + } + + @Test + public void castCommanderWithAddedBlueColor() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); // Artifact Creature {2} + + // Whenever a player casts a blue spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Kraken's Eye", 1); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + // When a player casts a spell or a creature attacks, exile Norin the Wary. + // Return it to the battlefield under its owner's control at the beginning of the next end step. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + setChoice(playerA, "Yes"); // Whenever a player casts a blue spell, you may gain 1 life. Choices: Yes - No + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Norin the Wary", 1); + + Permanent norin = getPermanent("Norin the Wary", playerA); + Assert.assertEquals(true, norin.getColor(currentGame).isBlue()); + Assert.assertEquals(true, norin.getColor(currentGame).isRed()); + + Permanent kraken = getPermanent("Kraken's Eye", playerA); + Assert.assertEquals(true, kraken.getColor(currentGame).isBlue()); + + assertLife(playerA, 41); + assertLife(playerB, 40); + + } + + + /** + * I played a Painter's Servant, named black, but the other commanders get a extra colors + * Later it got removed but the commanders and some cards still have the extra color + * I played it again later, named green, and the previously affected cards get the extra color + * so now they have 2 extra colors and the commander get and additional color on top of that + * And finally I got the empty hand error #6738 on my turn for what I assume is the Painter's Servant + Grindstone combo I have, + * but nonetheless manage to tie the game so it go into a second game and the issue carry over, + * all the commanders have all the extra colors they gain from the first game + */ + + @Test + public void castCommanderWithoutAddedBlueColor() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); // Artifact Creature {2} + + // Whenever a player casts a blue spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Kraken's Eye", 1); + + + // Exile target artifact or enchantment. + addCard(Zone.HAND, playerB, "Altar's Light", 1); // Instant {2}{W}{W} + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + // When a player casts a spell or a creature attacks, exile Norin the Wary. + // Return it to the battlefield under its owner's control at the beginning of the next end step. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + setChoice(playerA, "Yes"); // Whenever a player casts a blue spell, you may gain 1 life. Choices: Yes - No + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Altar's Light", "Painter's Servant", "Norin the Wary"); + setChoice(playerA, "Yes"); // Whenever a player casts a blue spell, you may gain 1 life. Choices: Yes - No + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Norin the Wary", 1); + assertAllCommandsUsed(); + + Permanent norin = getPermanent("Norin the Wary", playerA); + Assert.assertEquals(false, norin.getColor(currentGame).isBlue()); + Assert.assertEquals(true, norin.getColor(currentGame).isRed()); + + Permanent kraken = getPermanent("Kraken's Eye", playerA); + Assert.assertEquals(false, kraken.getColor(currentGame).isBlue()); + + assertLife(playerA, 42); + assertLife(playerB, 40); + + } +} diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index 9c737da8947..c2da4643a9d 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -1,5 +1,6 @@ package mage; +import java.util.*; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -16,12 +17,11 @@ import mage.cards.FrameStyle; import mage.cards.mock.MockCard; import mage.constants.*; import mage.game.Game; +import mage.game.MageObjectAttribute; import mage.game.events.ZoneChangeEvent; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.*; - public abstract class MageObjectImpl implements MageObject { protected UUID objectId; @@ -117,6 +117,12 @@ public abstract class MageObjectImpl implements MageObject { @Override public SubTypeList getSubtype(Game game) { + if (game != null) { + MageObjectAttribute mageObjectAttribute = game.getState().getMageObjectAttribute(getId()); + if (mageObjectAttribute != null) { + return mageObjectAttribute.getSubtype(); + } + } return subtype; } @@ -170,6 +176,12 @@ public abstract class MageObjectImpl implements MageObject { @Override public ObjectColor getColor(Game game) { + if (game != null) { + MageObjectAttribute mageObjectAttribute = game.getState().getMageObjectAttribute(getId()); + if (mageObjectAttribute != null) { + return mageObjectAttribute.getColor(); + } + } return color; } diff --git a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java index 2c638e9869a..6e7d82d9656 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java @@ -207,14 +207,14 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost ability.getCosts().add(cost.copy()); } } - // change spell colors + // change spell colors and subtype *TODO probably this needs to be done by continuous effect (while on the stack) ObjectColor spellColor = spell.getColor(game); spellColor.setBlack(false); spellColor.setRed(false); spellColor.setGreen(false); spellColor.setWhite(false); spellColor.setBlue(false); - game.getState().getCreateCardAttribute(spell.getCard(), game).getSubtype().clear(); + game.getState().getCreateMageObjectAttribute(spell.getCard(), game).getSubtype().clear(); } else { spell.setFaceDown(false, game); } diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index d7da027ca87..0a9c8eba509 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -1,5 +1,8 @@ package mage.cards; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; @@ -20,14 +23,9 @@ import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.util.CardUtil; import mage.util.GameLog; -import mage.util.SubTypeList; import mage.watchers.Watcher; import org.apache.log4j.Logger; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - public abstract class CardImpl extends MageObjectImpl implements Card { private static final long serialVersionUID = 1L; @@ -869,28 +867,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { spellAbility = ability; } - @Override - public ObjectColor getColor(Game game) { - if (game != null) { - CardAttribute cardAttribute = game.getState().getCardAttribute(getId()); - if (cardAttribute != null) { - return cardAttribute.getColor(); - } - } - return super.getColor(game); - } - - @Override - public SubTypeList getSubtype(Game game) { - if (game != null) { - CardAttribute cardAttribute = game.getState().getCardAttribute(getId()); - if (cardAttribute != null) { - return cardAttribute.getSubtype(); - } - } - return super.getSubtype(game); - } - @Override public List getAttachments() { return attachments; diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 8a858e60453..7ad046582c5 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -1,5 +1,9 @@ package mage.game; +import java.io.Serializable; +import java.util.*; +import static java.util.Collections.emptyList; +import java.util.stream.Collectors; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.*; @@ -35,12 +39,6 @@ import mage.util.ThreadLocalStringBuilder; import mage.watchers.Watcher; import mage.watchers.Watchers; -import java.io.Serializable; -import java.util.*; -import java.util.stream.Collectors; - -import static java.util.Collections.emptyList; - /** * @author BetaSteward_at_googlemail.com *

@@ -95,7 +93,7 @@ public class GameState implements Serializable, Copyable { private Map zones = new HashMap<>(); private List simultaneousEvents = new ArrayList<>(); private Map cardState = new HashMap<>(); - private Map cardAttribute = new HashMap<>(); + private Map mageObjectAttribute = new HashMap<>(); private Map zoneChangeCounter = new HashMap<>(); private Map copiedCards = new HashMap<>(); private int permanentOrderNumber; @@ -178,8 +176,8 @@ public class GameState implements Serializable, Copyable { for (Map.Entry entry : state.cardState.entrySet()) { cardState.put(entry.getKey(), entry.getValue().copy()); } - for (Map.Entry entry : state.cardAttribute.entrySet()) { - cardAttribute.put(entry.getKey(), entry.getValue().copy()); + for (Map.Entry entry : state.mageObjectAttribute.entrySet()) { + mageObjectAttribute.put(entry.getKey(), entry.getValue().copy()); } this.zoneChangeCounter.putAll(state.zoneChangeCounter); this.copiedCards.putAll(state.copiedCards); @@ -228,7 +226,7 @@ public class GameState implements Serializable, Copyable { this.zones = state.zones; this.simultaneousEvents = state.simultaneousEvents; this.cardState = state.cardState; - this.cardAttribute = state.cardAttribute; + this.mageObjectAttribute = state.mageObjectAttribute; this.zoneChangeCounter = state.zoneChangeCounter; this.copiedCards = state.copiedCards; this.permanentOrderNumber = state.permanentOrderNumber; @@ -1117,7 +1115,7 @@ public class GameState implements Serializable, Copyable { for (CardState state : cardState.values()) { state.clearAbilities(); } - cardAttribute.clear(); + mageObjectAttribute.clear(); this.setManaBurn(false); } @@ -1188,13 +1186,13 @@ public class GameState implements Serializable, Copyable { return cardState.get(cardId); } - public CardAttribute getCardAttribute(UUID cardId) { - return cardAttribute.get(cardId); + public MageObjectAttribute getMageObjectAttribute(UUID cardId) { + return mageObjectAttribute.get(cardId); } - public CardAttribute getCreateCardAttribute(Card card, Game game) { - CardAttribute cardAtt = cardAttribute.computeIfAbsent(card.getId(), k -> new CardAttribute(card, game)); - return cardAtt; + public MageObjectAttribute getCreateMageObjectAttribute(MageObject mageObject, Game game) { + MageObjectAttribute mageObjectAtt = mageObjectAttribute.computeIfAbsent(mageObject.getId(), k -> new MageObjectAttribute(mageObject, game)); + return mageObjectAtt; } public void addWatcher(Watcher watcher) { diff --git a/Mage/src/main/java/mage/game/CardAttribute.java b/Mage/src/main/java/mage/game/MageObjectAttribute.java similarity index 50% rename from Mage/src/main/java/mage/game/CardAttribute.java rename to Mage/src/main/java/mage/game/MageObjectAttribute.java index e0f9945ec5a..66da3d0a60c 100644 --- a/Mage/src/main/java/mage/game/CardAttribute.java +++ b/Mage/src/main/java/mage/game/MageObjectAttribute.java @@ -6,34 +6,34 @@ package mage.game; import java.io.Serializable; +import mage.MageObject; import mage.ObjectColor; -import mage.cards.Card; import mage.util.SubTypeList; /** - * This class saves changed attributes of cards (e.g. in graveyard, exile or + * This class saves changed attributes of mage objects (e.g. in command zone, graveyard, exile or * player hands or libraries). * * @author LevelX2 */ -public class CardAttribute implements Serializable { +public class MageObjectAttribute implements Serializable { protected ObjectColor color; protected SubTypeList subtype; - public CardAttribute(Card card, Game game) { - color = card.getColor(null).copy(); + public MageObjectAttribute(MageObject mageObject, Game game) { + color = mageObject.getColor(null).copy(); subtype = new SubTypeList(); - subtype.addAll(card.getSubtype(game)); + subtype.addAll(mageObject.getSubtype(game)); } - public CardAttribute(CardAttribute cardAttribute) { - this.color = cardAttribute.color; - this.subtype = cardAttribute.subtype; + public MageObjectAttribute(MageObjectAttribute mageObjectAttribute) { + this.color = mageObjectAttribute.color; + this.subtype = mageObjectAttribute.subtype; } - public CardAttribute copy() { - return new CardAttribute(this); + public MageObjectAttribute copy() { + return new MageObjectAttribute(this); } public ObjectColor getColor() { diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 7fe2b19a668..452a62e7d09 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1,5 +1,6 @@ package mage.game.stack; +import java.util.*; import mage.MageInt; import mage.MageObject; import mage.Mana; @@ -22,6 +23,7 @@ import mage.counters.Counters; import mage.filter.FilterMana; import mage.game.Game; import mage.game.GameState; +import mage.game.MageObjectAttribute; import mage.game.events.CopiedStackObjectEvent; import mage.game.events.CopyStackObjectEvent; import mage.game.events.GameEvent; @@ -34,8 +36,6 @@ import mage.util.CardUtil; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.*; - /** * @author BetaSteward_at_googlemail.com */ @@ -575,6 +575,12 @@ public class Spell extends StackObjImpl implements Card { @Override public ObjectColor getColor(Game game) { + if (game != null) { + MageObjectAttribute mageObjectAttribute = game.getState().getMageObjectAttribute(getId()); + if (mageObjectAttribute != null) { + return mageObjectAttribute.getColor(); + } + } return color; }