diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index be35a09c363..a8008e8a5a5 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -2316,7 +2316,7 @@ public class ComputerUtilCombat { * @return transform creature if possible, original creature otherwise */ private final static Card canTransform(Card original) { - if (original.isDoubleFaced() && !original.isInAlternateState()) { + if (original.isTransformable() && !original.isInAlternateState()) { for (SpellAbility sa : original.getSpellAbilities()) { if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController(), false)) { Card transformed = CardUtil.getLKICopy(original); diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index c9a778e26d5..02967040fa9 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -46,7 +46,7 @@ public class CreatureEvaluator implements Function { value += addValue(toughness * 10, "toughness: " + toughness); // because backside is always stronger the potential makes it better than a single faced card - if (c.hasKeyword(Keyword.DAYBOUND) && c.hasBackSide()) { + if (c.hasKeyword(Keyword.DAYBOUND) && c.isDoubleFaced()) { value += addValue(power * 10, "transforming"); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java index 921a6d361d1..cdba01f3f99 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java @@ -173,7 +173,7 @@ public class SetStateAi extends SpellAbilityAi { } } else { // doublefaced or meld cards can't be turned face down - if (card.isDoubleFaced() || card.isMeldable()) { + if (card.isTransformable() || card.isMeldable()) { return false; } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 6f3fb7591ff..6068a34149a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -592,7 +592,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { gameCard.setTapped(false); } if (sa.hasParam("Transformed")) { - if (gameCard.isDoubleFaced()) { + if (gameCard.isTransformable()) { // need LKI before Animate does apply if (!moveParams.containsKey(AbilityKey.CardLKI)) { moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard)); @@ -1320,7 +1320,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { c.addEtbCounter(cType, cAmount, player); } if (sa.hasParam("Transformed")) { - if (c.isDoubleFaced()) { + if (c.isTransformable()) { // need LKI before Animate does apply if (!moveParams.containsKey(AbilityKey.CardLKI)) { moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index ad1085d9abc..4069a6caf8e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -129,7 +129,7 @@ public class SetStateEffect extends SpellAbilityEffect { && gameCard.hasMergedCard()) { boolean hasBackSide = false; for (final Card c : gameCard.getMergedCards()) { - if (c.hasBackSide()) { + if (c.isDoubleFaced()) { hasBackSide = true; break; } @@ -200,7 +200,7 @@ public class SetStateEffect extends SpellAbilityEffect { if (remChanged) { host.addRemembered(gameCard); } - if (!gameCard.isDoubleFaced()) + if (!gameCard.isTransformable()) transformedCards.add(gameCard); if ("Specialize".equals(mode)) { gameCard.setSpecialized(true); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 245b075439a..9f476b69649 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -596,7 +596,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // and then any effect have it turn upface again and demand its former flip state to be restored // Proof: Morph cards never have ability that makes them flip, Ixidron does not suppose cards to be turned face up again, // Illusionary Mask affects cards in hand. - if (mode.equals("Transform") && (isDoubleFaced() || hasMergedCard())) { + if (mode.equals("Transform") && (isTransformable() || hasMergedCard())) { if (!canTransform(cause)) { return false; } @@ -608,7 +608,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this); boolean retResult = false; for (final Card c : cards) { - if (!c.isDoubleFaced()) { + if (!c.isTransformable()) { continue; } c.backside = !c.backside; @@ -726,7 +726,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this); boolean retResult = false; for (final Card c : cards) { - if (override || !c.hasBackSide()) { + if (override || !c.isDoubleFaced()) { c.facedown = true; if (c.setState(CardStateName.FaceDown, true)) { c.runFacedownCommands(); @@ -810,7 +810,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (hasMergedCard()) { boolean hasTransformCard = false; for (final Card c : getMergedCards()) { - if (c.isDoubleFaced()) { + if (c.isTransformable()) { hasTransformCard = true; transformCard = c; break; @@ -819,7 +819,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (!hasTransformCard) { return false; } - } else if (!isDoubleFaced()) { + } else if (!isTransformable()) { return false; } @@ -931,7 +931,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return numStates > threshold; } - public final boolean isDoubleFaced() { + public final boolean isTransformable() { return getRules() != null && getRules().getSplitType() == CardSplitType.Transform; } @@ -943,8 +943,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return getRules() != null && getRules().getSplitType() == CardSplitType.Modal; } - public final boolean hasBackSide() { - return isDoubleFaced() || isMeldable() || isModal(); + public final boolean isDoubleFaced() { + return isTransformable() || isMeldable() || isModal(); } public final boolean isFlipCard() { @@ -2467,7 +2467,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // Check if the saga card does not have the keyword Read ahead if (type.hasSubtype("Saga") && !this.hasStartOfKeyword("Read ahead")) { sb.append("(").append(Localizer.getInstance().getMessage("lblSagaHeader")); - if (!state.getCard().isDoubleFaced()) { + if (!state.getCard().isTransformable()) { sb.append(" ").append(Localizer.getInstance().getMessage("lblSagaFooter")).append(" ").append(TextUtil.toRoman(getFinalChapterNr())).append("."); } sb.append(")").append(linebreak); @@ -4106,7 +4106,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return CardStateName.Flipped; } else if (isSpecialized()) { return getCurrentStateName(); - } else if (backside && hasBackSide()) { + } else if (backside && isDoubleFaced()) { CardStateName stateName = getRules().getSplitType().getChangedStateName(); if (hasState(stateName)) { return stateName; @@ -6667,7 +6667,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (stateName != null && hasState(stateName) && this.getCurrentStateName() != stateName) { setState(stateName, true); // need to set backSide value according to the SplitType - if (hasBackSide()) { + if (isDoubleFaced()) { setBackSide(getRules().getSplitType().getChangedStateName().equals(stateName)); } } 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 3b9e05422ac..56110cfc56d 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -253,7 +253,7 @@ public class CardFactory { c.setState(CardStateName.Flipped, false); c.setImageKey(cp.getImageKey(true)); } - else if (c.hasBackSide() && cardRules != null) { + else if (c.isDoubleFaced() && cardRules != null) { c.setState(cardRules.getSplitType().getChangedStateName(), false); c.setImageKey(cp.getImageKey(true)); } @@ -300,7 +300,7 @@ public class CardFactory { private static void buildAbilities(final Card card) { for (final CardStateName state : card.getStates()) { - if (card.hasBackSide() && state == CardStateName.FaceDown) { + if (card.isDoubleFaced() && state == CardStateName.FaceDown) { continue; // Ignore FaceDown for DFC since they have none. } card.setState(state, false); diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index d2761f7605a..4695ff593e7 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -143,6 +143,10 @@ public class CardProperty { if (!card.isBackSide()) { return false; } + } else if (property.equals("CanTransform")) { + if (!card.isTransformable()) { + return false; + } } else if (property.equals("Transformed")) { if (!card.isTransformed()) { return false; diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 85ded527dc6..7c41d0fa702 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -922,7 +922,7 @@ public class CardView extends GameEntityView { updateIntensity(c); } - if (getBackup() == null && !c.isFaceDown() && (c.hasBackSide()||c.isFlipCard()||c.isAdventureCard())) { + if (getBackup() == null && !c.isFaceDown() && (c.isDoubleFaced()||c.isFlipCard()||c.isAdventureCard())) { set(TrackableProperty.PaperCardBackup, c.getPaperCard()); } @@ -939,7 +939,7 @@ public class CardView extends GameEntityView { //backside if (c.getAlternateState() != null) - updateBackSide(c.getAlternateState().getName(), c.hasBackSide()); + updateBackSide(c.getAlternateState().getName(), c.isDoubleFaced()); final Card cloner = c.getCloner(); @@ -1005,7 +1005,7 @@ public class CardView extends GameEntityView { alternateState = c.getState(CardStateName.Original); } - if (c.hasBackSide() && isFaceDown()) //fixes facedown cards with backside... + if (c.isDoubleFaced() && isFaceDown()) //fixes facedown cards with backside... alternateState = c.getState(CardStateName.Original); if (alternateState == null) { diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java index 50825af66a1..c6ed2b4e778 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -186,7 +186,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable } source.setState(stateName, false); - if (getHostCard().hasBackSide()) { + if (getHostCard().isDoubleFaced()) { source.setBackSide(getHostCard().getRules().getSplitType().getChangedStateName().equals(stateName)); } 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 fec79f0c14b..0e925297912 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 @@ -1745,12 +1745,12 @@ public class GameSimulationTest extends SimulationTest { Card giant = addCard(hillGiantName, p); AssertJUnit.assertFalse(outlaw.isCloned()); - AssertJUnit.assertTrue(outlaw.isDoubleFaced()); + AssertJUnit.assertTrue(outlaw.isTransformable()); AssertJUnit.assertTrue(outlaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(outlaw.canTransform(null)); AssertJUnit.assertFalse(outlaw.isBackSide()); - AssertJUnit.assertFalse(giant.isDoubleFaced()); + AssertJUnit.assertFalse(giant.isTransformable()); AssertJUnit.assertFalse(giant.canTransform(null)); addCards("Forest", 4, p); @@ -1778,14 +1778,14 @@ public class GameSimulationTest extends SimulationTest { Card clonedOutLaw = (Card) sim.getGameCopier().find(outlaw); AssertJUnit.assertTrue(clonedOutLaw.isCloned()); - AssertJUnit.assertTrue(clonedOutLaw.isDoubleFaced()); + AssertJUnit.assertTrue(clonedOutLaw.isTransformable()); AssertJUnit.assertFalse(clonedOutLaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(clonedOutLaw.canTransform(null)); AssertJUnit.assertFalse(clonedOutLaw.isBackSide()); AssertJUnit.assertEquals(clonedOutLaw.getName(), hillGiantName); - AssertJUnit.assertTrue(clonedOutLaw.isDoubleFaced()); + AssertJUnit.assertTrue(clonedOutLaw.isTransformable()); score = sim.simulateSpellAbility(moonmistSA).value; AssertJUnit.assertTrue(score > 0); @@ -1799,7 +1799,7 @@ public class GameSimulationTest extends SimulationTest { Card transformOutLaw = (Card) sim.getGameCopier().find(outlaw); AssertJUnit.assertTrue(transformOutLaw.isCloned()); - AssertJUnit.assertTrue(transformOutLaw.isDoubleFaced()); + AssertJUnit.assertTrue(transformOutLaw.isTransformable()); AssertJUnit.assertFalse(transformOutLaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(transformOutLaw.canTransform(null)); AssertJUnit.assertTrue(transformOutLaw.isBackSide()); @@ -1814,7 +1814,7 @@ public class GameSimulationTest extends SimulationTest { AssertJUnit.assertEquals(1, countCardsWithName(simGame, terrorName)); AssertJUnit.assertFalse(transformOutLaw.isCloned()); - AssertJUnit.assertTrue(transformOutLaw.isDoubleFaced()); + AssertJUnit.assertTrue(transformOutLaw.isTransformable()); AssertJUnit.assertTrue(transformOutLaw.hasState(CardStateName.Transformed)); AssertJUnit.assertTrue(transformOutLaw.canTransform(null)); AssertJUnit.assertTrue(transformOutLaw.isBackSide()); diff --git a/forge-gui/res/cardsfolder/t/tovolar_dire_overlord_tovolar_the_midnight_scourge.txt b/forge-gui/res/cardsfolder/t/tovolar_dire_overlord_tovolar_the_midnight_scourge.txt index 3ea13b280bd..db2caca4443 100644 --- a/forge-gui/res/cardsfolder/t/tovolar_dire_overlord_tovolar_the_midnight_scourge.txt +++ b/forge-gui/res/cardsfolder/t/tovolar_dire_overlord_tovolar_the_midnight_scourge.txt @@ -6,9 +6,9 @@ T:Mode$ DamageDone | ValidSource$ Wolf.YouCtrl,Werewolf.YouCtrl | ValidTarget$ P SVar:TrigDrawD:DB$ Draw | Defined$ You | NumCards$ 1 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE3 | Execute$ TrigNight | TriggerDescription$ At the beginning of your upkeep, if you control three or more Wolves and/or Werewolves, it becomes night. Then transform any number of Human Werewolves you control. SVar:TrigNight:DB$ DayTime | Value$ Night | SubAbility$ DBTransform -SVar:DBTransform:DB$ SetState | MinAmount$ 0 | Amount$ NumHumanWerewolves | Choices$ Human.Werewolf+YouCtrl+DoubleFaced | ChoiceTitle$ Choose any number of Human Werewolves you control | Mode$ Transform +SVar:DBTransform:DB$ SetState | MinAmount$ 0 | Amount$ NumHumanWerewolves | Choices$ Human.Werewolf+YouCtrl+CanTransform | ChoiceTitle$ Choose any number of Human Werewolves you control | Mode$ Transform K:Daybound -SVar:NumHumanWerewolves:Count$Valid Human.Werewolf+YouCtrl+DoubleFaced +SVar:NumHumanWerewolves:Count$Valid Human.Werewolf+YouCtrl+CanTransform SVar:X:Count$Valid Wolf.YouCtrl,Werewolf.YouCtrl DeckHints:Type$Wolf|Werewolf AlternateMode:DoubleFaced diff --git a/forge-gui/res/cardsfolder/upcoming/overgrown_pest.txt b/forge-gui/res/cardsfolder/upcoming/overgrown_pest.txt new file mode 100644 index 00000000000..0ded6f11277 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/overgrown_pest.txt @@ -0,0 +1,7 @@ +Name:Overgrown Pest +ManaCost:2 G +Types:Creature Pest +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, look at the top five cards of your library. You may reveal a land or double-faced card from among them and put that card into your hand. Put the rest on the bottom of your library in a random order. +SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ForceRevealToController$ True | ChangeValid$ Land,Card.DoubleFaced | RestRandomOrder$ True +Oracle:When Overgrown Pest enters the battlefield, look at the top five cards of your library. You may reveal a land or double-faced card from among them and put that card into your hand. Put the rest on the bottom of your library in a random order.