diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index c1e436731b9..b045a5627bc 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -511,6 +511,7 @@ public final class AbilityFactory { totalCost.add(parseAbilityCost(rightState, rightMap, rightType)); final SpellAbility left = getAbility(leftType, leftApi, leftMap, totalCost, leftState, leftState); + left.setCardState(card.getState(CardStateName.Original)); final AbilitySub right = (AbilitySub) getAbility(AbilityRecordType.SubAbility, rightApi, rightMap, null, rightState, rightState); left.appendSubAbility(right); return left; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index b802b5c9faa..eda2d0236a0 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -753,6 +753,19 @@ public class CardFactory { final CardState ret2 = new CardState(out, CardStateName.Transformed); ret2.copyFrom(in.getState(CardStateName.Transformed), false, sa); result.put(CardStateName.Transformed, ret2); + } else if (in.isSplitCard()) { + // for split cards, copy all three states + final CardState ret1 = new CardState(out, CardStateName.Original); + ret1.copyFrom(in.getState(CardStateName.Original), false, sa); + result.put(CardStateName.Original, ret1); + + final CardState ret2 = new CardState(out, CardStateName.LeftSplit); + ret2.copyFrom(in.getState(CardStateName.LeftSplit), false, sa); + result.put(CardStateName.LeftSplit, ret2); + + final CardState ret3 = new CardState(out, CardStateName.RightSplit); + ret3.copyFrom(in.getState(CardStateName.RightSplit), false, sa); + result.put(CardStateName.RightSplit, ret3); } else { // in all other cases just copy the current state to original final CardState ret = new CardState(out, CardStateName.Original); diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 97d82a2f379..01ed5ffbbb9 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -194,24 +194,49 @@ public final class CardUtil { newCopy.setController(in.getController(), 0); newCopy.setCommander(in.isCommander()); + newCopy.setRules(in.getRules()); + // needed to ensure that the LKI object has correct CMC info no matter what state the original card was in // (e.g. Scrap Trawler + transformed Harvest Hand) newCopy.setLKICMC(in.getCMC()); // used for the purpose of cards that care about the zone the card was known to be in last newCopy.setLastKnownZone(in.getLastKnownZone()); + // copy EffectSource for description + newCopy.setEffectSource(getLKICopy(in.getEffectSource(), cachedMap)); - newCopy.getCurrentState().copyFrom(in.getState(in.getFaceupCardStateName()), true); + if (in.isFlipCard()) { + newCopy.getState(CardStateName.Original).copyFrom(in.getState(CardStateName.Original), true); + newCopy.addAlternateState(CardStateName.Flipped, false); + newCopy.getState(CardStateName.Flipped).copyFrom(in.getState(CardStateName.Flipped), true); + } else if (in.isTransformable()) { + newCopy.getState(CardStateName.Original).copyFrom(in.getState(CardStateName.Original), true); + newCopy.addAlternateState(CardStateName.Transformed, false); + newCopy.getState(CardStateName.Transformed).copyFrom(in.getState(CardStateName.Transformed), true); + } else if (in.isAdventureCard()) { + newCopy.getState(CardStateName.Original).copyFrom(in.getState(CardStateName.Original), true); + newCopy.addAlternateState(CardStateName.Adventure, false); + newCopy.getState(CardStateName.Adventure).copyFrom(in.getState(CardStateName.Adventure), true); + } else if (in.isSplitCard()) { + newCopy.getState(CardStateName.Original).copyFrom(in.getState(CardStateName.Original), true); + newCopy.addAlternateState(CardStateName.LeftSplit, false); + newCopy.getState(CardStateName.LeftSplit).copyFrom(in.getState(CardStateName.LeftSplit), true); + newCopy.addAlternateState(CardStateName.RightSplit, false); + newCopy.getState(CardStateName.RightSplit).copyFrom(in.getState(CardStateName.RightSplit), true); + } else { + newCopy.getCurrentState().copyFrom(in.getState(in.getFaceupCardStateName()), true); + } + newCopy.setFlipped(in.isFlipped()); + newCopy.setBackSide(in.isBackSide()); + if (in.isTransformed()) { + newCopy.incrementTransformedTimestamp(); + } + newCopy.setState(newCopy.getFaceupCardStateName(), false, true); if (in.isFaceDown()) { newCopy.turnFaceDownNoUpdate(); newCopy.setType(new CardType(in.getFaceDownState().getType())); - // prevent StackDescription from revealing face - newCopy.updateStateForView(); - } - - if (in.isAdventureCard() && in.getFaceupCardStateName().equals(CardStateName.Original)) { - newCopy.addAlternateState(CardStateName.Adventure, false); - newCopy.getState(CardStateName.Adventure).copyFrom(in.getState(CardStateName.Adventure), true); } + // prevent StackDescription from revealing face + newCopy.updateStateForView(); /* if (in.isCloned()) { @@ -300,9 +325,6 @@ public final class CardUtil { newCopy.setTimestamp(in.getTimestamp()); newCopy.setBestowTimestamp(in.getBestowTimestamp()); - if (in.isTransformed()) { - newCopy.incrementTransformedTimestamp(); - } newCopy.setForetold(in.isForetold()); newCopy.setForetoldThisTurn(in.isForetoldThisTurn()); diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java index 0e925297912..10f021f7a5d 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java @@ -1779,7 +1779,7 @@ public class GameSimulationTest extends SimulationTest { AssertJUnit.assertTrue(clonedOutLaw.isCloned()); AssertJUnit.assertTrue(clonedOutLaw.isTransformable()); - AssertJUnit.assertFalse(clonedOutLaw.hasState(CardStateName.Transformed)); + AssertJUnit.assertTrue(clonedOutLaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(clonedOutLaw.canTransform(null)); AssertJUnit.assertFalse(clonedOutLaw.isBackSide()); @@ -1800,7 +1800,7 @@ public class GameSimulationTest extends SimulationTest { AssertJUnit.assertTrue(transformOutLaw.isCloned()); AssertJUnit.assertTrue(transformOutLaw.isTransformable()); - AssertJUnit.assertFalse(transformOutLaw.hasState(CardStateName.Transformed)); + AssertJUnit.assertTrue(transformOutLaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(transformOutLaw.canTransform(null)); AssertJUnit.assertTrue(transformOutLaw.isBackSide()); @@ -2451,13 +2451,9 @@ public class GameSimulationTest extends SimulationTest { // whereas Naban doesn't see Memnarch to double the trigger addCardToZone("Memnarch", p, ZoneType.Library); - addCard("Forest", p); - addCard("Forest", p); - addCard("Island", p); - addCard("Island", p); - addCard("Island", p); - addCard("Mountain", p); - addCard("Mountain", p); + addCards("Forest", 2, p); + addCards("Island", 3, p); + addCards("Mountain", 2, p); Card genesis = addCardToZone("Genesis Ultimatum", p, ZoneType.Hand); @@ -2473,4 +2469,46 @@ public class GameSimulationTest extends SimulationTest { // 2 damage dealt for 2 artifacts AssertJUnit.assertEquals(18, simGame.getPlayers().get(1).getLife()); } + + @Test + public void testLKITransformableTokenCopy() { + Game game = initAndCreateGame(); + Player p = game.getPlayers().get(0); + + String untransformedName = "Heliod, the Radiant Dawn"; + String transformedName = "Heliod, the Warped Eclipse"; + + addCard("Ratadrabik of Urborg", p); + Card heliod = addCard(untransformedName, p); + + addCards("Island", 4, p); + addCards("Swamp", 3, p); + + Card murder = addCardToZone("Murder", p, ZoneType.Hand); + SpellAbility murderSA = murder.getFirstSpellAbility(); + game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); + game.getAction().checkStateEffects(true); + + SpellAbility transformSA = findSAWithPrefix(heliod, "{3}{U/P}: Transform"); + + GameSimulator sim = createSimulator(game, p); + AssertJUnit.assertNotNull(transformSA); + sim.simulateSpellAbility(transformSA); + + Game simGame = sim.getSimulatedGameState(); + + Card transformedHeliod = findCardWithName(simGame, transformedName); + AssertJUnit.assertNotNull(transformedHeliod); + murderSA.getTargets().add(transformedHeliod); + + sim.simulateSpellAbility(murderSA); + simGame = sim.getSimulatedGameState(); + + Card transformedHeliodToken = findCardWithName(simGame, transformedName); + AssertJUnit.assertNotNull(transformedHeliodToken); + AssertJUnit.assertTrue(transformedHeliodToken.isToken()); + AssertJUnit.assertTrue(transformedHeliodToken.isTransformable()); + AssertJUnit.assertTrue(transformedHeliodToken.isTransformed()); + AssertJUnit.assertTrue(transformedHeliodToken.isBackSide()); + } }