From 104387c15a6555cf4f641cd68d65964823b58e77 Mon Sep 17 00:00:00 2001 From: Lyu Zong-Hong Date: Sun, 24 Jan 2021 18:46:44 +0900 Subject: [PATCH] Implement Illusionary Terrain --- forge-ai/src/main/java/forge/ai/GameState.java | 13 +++++++++++++ .../main/java/forge/ai/simulation/GameCopier.java | 3 +++ .../src/main/java/forge/game/ForgeScript.java | 4 ++++ .../game/ability/effects/ChooseTypeEffect.java | 6 +++++- .../forge/game/ability/effects/CleanUpEffect.java | 1 + .../src/main/java/forge/game/card/Card.java | 15 +++++++++++++++ .../src/main/java/forge/game/card/CardView.java | 7 +++++++ .../staticability/StaticAbilityContinuous.java | 6 ++++++ .../java/forge/trackable/TrackableProperty.java | 1 + .../res/cardsfolder/i/illusionary_terrain.txt | 10 ++++++++++ .../src/main/java/forge/card/CardDetailUtil.java | 3 +++ 11 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 forge-gui/res/cardsfolder/i/illusionary_terrain.txt diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 6dd774ab0c8..b5a45786d58 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -76,6 +76,7 @@ public abstract class GameState { private final Map> cardToChosenClrs = new HashMap<>(); private final Map cardToChosenCards = new HashMap<>(); private final Map cardToChosenType = new HashMap<>(); + private final Map cardToChosenType2 = new HashMap<>(); private final Map> cardToRememberedId = new HashMap<>(); private final Map> cardToImprintedId = new HashMap<>(); private final Map cardToNamedCard = new HashMap<>(); @@ -323,6 +324,9 @@ public abstract class GameState { if (!c.getChosenType().isEmpty()) { newText.append("|ChosenType:").append(c.getChosenType()); } + if (!c.getChosenType2().isEmpty()) { + newText.append("|ChosenType2:").append(c.getChosenType2()); + } if (!c.getNamedCard().isEmpty()) { newText.append("|NamedCard:").append(c.getNamedCard()); } @@ -601,6 +605,7 @@ public abstract class GameState { cardToChosenClrs.clear(); cardToChosenCards.clear(); cardToChosenType.clear(); + cardToChosenType2.clear(); cardToScript.clear(); cardAttackMap.clear(); @@ -1055,6 +1060,12 @@ public abstract class GameState { c.setChosenType(entry.getValue()); } + // Chosen type 2 + for (Entry entry : cardToChosenType2.entrySet()) { + Card c = entry.getKey(); + c.setChosenType2(entry.getValue()); + } + // Named card for (Entry entry : cardToNamedCard.entrySet()) { Card c = entry.getKey(); @@ -1286,6 +1297,8 @@ public abstract class GameState { cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(","))); } else if (info.startsWith("ChosenType:")) { cardToChosenType.put(c, info.substring(info.indexOf(':') + 1)); + } else if (info.startsWith("ChosenType2:")) { + cardToChosenType2.put(c, info.substring(info.indexOf(':') + 1)); } else if (info.startsWith("ChosenCards:")) { CardCollection chosen = new CardCollection(); String[] idlist = info.substring(info.indexOf(':') + 1).split(","); diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 671dc4f40b9..9c743f30653 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -336,6 +336,9 @@ public class GameCopier { if (!c.getChosenType().isEmpty()) { newCard.setChosenType(c.getChosenType()); } + if (!c.getChosenType2().isEmpty()) { + newCard.setChosenType2(c.getChosenType2()); + } if (c.getChosenColors() != null) { newCard.setChosenColors(Lists.newArrayList(c.getChosenColors())); } diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index ea8f199a1ae..ee2aa04bcb0 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -73,6 +73,10 @@ public class ForgeScript { return cardState.getTypeWithChanges().hasStringType(source.getChosenType()); } else if (property.equals("IsNotChosenType")) { return !cardState.getTypeWithChanges().hasStringType(source.getChosenType()); + } else if (property.equals("ChosenType2")) { + return cardState.getTypeWithChanges().hasStringType(source.getChosenType2()); + } else if (property.equals("IsNotChosenType2")) { + return !cardState.getTypeWithChanges().hasStringType(source.getChosenType2()); } else if (property.startsWith("HasSubtype")) { final String subType = property.substring(11); return cardState.getTypeWithChanges().hasSubtype(subType); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java index 36ac0d24e5c..d8c6f7d819c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java @@ -64,7 +64,11 @@ public class ChooseTypeEffect extends SpellAbilityEffect { for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); - card.setChosenType(choice); + if (!sa.hasParam("Secondary")) { + card.setChosenType(choice); + } else { + card.setChosenType2(choice); + } } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java index 1011d9293ee..1dcbbcd1d42 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java @@ -53,6 +53,7 @@ public class CleanUpEffect extends SpellAbilityEffect { } if (sa.hasParam("ClearChosenType")) { source.setChosenType(""); + source.setChosenType2(""); } if (sa.hasParam("ClearChosenColor")) { source.setChosenColors(null); 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 e3177cd1b04..06162c1add4 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -228,6 +228,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private String originalText = "", text = ""; private String chosenType = ""; + private String chosenType2 = ""; private List chosenColors; private String chosenName = ""; private String chosenName2 = ""; @@ -1469,6 +1470,20 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return chosenType != null && !chosenType.isEmpty(); } + // used by card Illusionary Terrain + public final String getChosenType2() { + return chosenType2; + } + + public final void setChosenType2(final String s) { + chosenType2 = s; + view.updateChosenType2(this); + } + + public final boolean hasChosenType2() { + return chosenType2 != null && !chosenType2.isEmpty(); + } + public final String getChosenColor() { if (hasChosenColor()) { return chosenColors.get(0); 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 53bb83e47b1..2b0a1e35608 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -306,6 +306,13 @@ public class CardView extends GameEntityView { set(TrackableProperty.ChosenType, c.getChosenType()); } + public String getChosenType2() { + return get(TrackableProperty.ChosenType2); + } + void updateChosenType2(Card c) { + set(TrackableProperty.ChosenType2, c.getChosenType2()); + } + public String getChosenNumber() { return get(TrackableProperty.ChosenNumber); } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 48676613425..b0ae7dfe94a 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -367,6 +367,9 @@ public final class StaticAbilityContinuous { if (input.equals("ChosenType") && !hostCard.hasChosenType()) { return true; } + if (input.equals("ChosenType2") && !hostCard.hasChosenType2()) { + return true; + } if (input.equals("ImprintedCreatureType")) { if (hostCard.hasImprintedCard()) { newTypes.addAll(hostCard.getImprintedCards().getLast().getType().getCreatureTypes()); @@ -385,6 +388,9 @@ public final class StaticAbilityContinuous { addTypes = Lists.transform(addTypes, new Function() { @Override public String apply(String input) { + if (hostCard.hasChosenType2()) { + input = input.replaceAll("ChosenType2", hostCard.getChosenType2()); + } if (hostCard.hasChosenType()) { input = input.replaceAll("ChosenType", hostCard.getChosenType()); } diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 9b46a396fd5..4b47a091fef 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -48,6 +48,7 @@ public enum TrackableProperty { LethalDamage(TrackableTypes.IntegerType), ShieldCount(TrackableTypes.IntegerType), ChosenType(TrackableTypes.StringType), + ChosenType2(TrackableTypes.StringType), ChosenColors(TrackableTypes.StringListType), ChosenCards(TrackableTypes.CardViewCollectionType), ChosenNumber(TrackableTypes.StringType), diff --git a/forge-gui/res/cardsfolder/i/illusionary_terrain.txt b/forge-gui/res/cardsfolder/i/illusionary_terrain.txt new file mode 100644 index 00000000000..0abfe4fde94 --- /dev/null +++ b/forge-gui/res/cardsfolder/i/illusionary_terrain.txt @@ -0,0 +1,10 @@ +Name:Illusionary Terrain +ManaCost:U U +Types:Enchantment +K:Cumulative upkeep:2 +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigChooseType1 | TriggerDescription$ As Illusionary Terrain enters the battlefield, choose two basic land types. +SVar:TrigChooseType1:DB$ ChooseType | Defined$ You | Type$ Basic Land | SubAbility$ ChooseType2 +SVar:ChooseType2:DB$ ChooseType | Defined$ You | Type$ Basic Land | Secondary$ True +S:Mode$ Continuous | Affected$ Land.Basic+ChosenType | AddType$ ChosenType2 | RemoveLandTypes$ True | Description$ Basic lands of the first chosen type are the second chosen type. +AI:RemoveDeck:All +Oracle:Cumulative upkeep {2} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nAs Illusionary Terrain enters the battlefield, choose two basic land types.\nBasic lands of the first chosen type are the second chosen type. diff --git a/forge-gui/src/main/java/forge/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/card/CardDetailUtil.java index 18597a7e7e4..9708175d5d1 100644 --- a/forge-gui/src/main/java/forge/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/card/CardDetailUtil.java @@ -393,6 +393,9 @@ public class CardDetailUtil { } area.append("(chosen type: "); area.append(card.getChosenType()); + if (!card.getChosenType2().isEmpty()) { + area.append(", ").append(card.getChosenType2()); + } area.append(")"); }