From 3dd7a6aa5801c08160d35a539ef5c4dff851bb2c Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 21 Jan 2024 22:26:01 +0100 Subject: [PATCH] Card: add Suspected Flag --- .../src/main/java/forge/ai/GameState.java | 7 +++ .../java/forge/ai/simulation/GameCopier.java | 3 ++ .../ability/effects/AlterAttributeEffect.java | 19 +++++++- .../src/main/java/forge/game/card/Card.java | 43 ++++++++++++++++--- .../java/forge/game/card/CardProperty.java | 8 ++++ .../main/java/forge/game/card/CardUtil.java | 1 + .../java/forge/game/combat/CombatUtil.java | 4 ++ .../StaticAbilityCantBeSuspected.java | 32 ++++++++++++++ 8 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeSuspected.java diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 2739f2bf4e0..b51591d63d4 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -295,6 +295,9 @@ private void addCard(ZoneType zoneType, Map cardTexts, Card c) if (c.isSolved()) { newText.append("|Solved"); } + if (c.isSuspected()) { + newText.append("|Suspected"); + } if (c.isMonstrous()) { newText.append("|Monstrous"); } @@ -1259,6 +1262,10 @@ private CardCollectionView processCardsForZone(final String[] data, final Player c.tap(false, null, null); } else if (info.startsWith("Renowned")) { c.setRenowned(true); + } else if (info.startsWith("Solved")) { + c.setSolved(true); + } else if (info.startsWith("Suspected")) { + c.setSuspected(true); } else if (info.startsWith("Monstrous")) { c.setMonstrous(true); } else if (info.startsWith("PhasedOut")) { 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 5b2546b883c..29e83ed8f75 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -399,6 +399,9 @@ private void addCard(Game newGame, ZoneType zone, Card c, Player aiPlayer) { if (c.isSolved()) { newCard.setSolved(true); } + if (c.isSuspected()) { + newCard.setSuspected(true); + } if (c.isPlaneswalker()) { for (SpellAbility sa : c.getAllSpellAbilities()) { int active = sa.getActivationsThisTurn(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/AlterAttributeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AlterAttributeEffect.java index 43169b1fa7d..fd196e14ca8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AlterAttributeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AlterAttributeEffect.java @@ -5,6 +5,8 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.spellability.SpellAbility; +import forge.util.Lang; +import forge.util.TextUtil; import java.util.ArrayList; @@ -15,6 +17,17 @@ public void resolve(SpellAbility sa) { ArrayList attributes = Lists.newArrayList(sa.getParam("Attributes").split(",")); CardCollection defined = getDefinedCardsOrTargeted(sa, "Defined"); + if (sa.hasParam("Optional")) { + final String targets = Lang.joinHomogenous(defined); + final String message = sa.hasParam("OptionQuestion") + ? TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets) + : getStackDescription(sa); + + if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message, null)) { + return; + } + } + for(Card c : defined) { for(String attr : attributes) { switch(attr.trim()) { @@ -22,8 +35,12 @@ public void resolve(SpellAbility sa) { case "Solved": c.setSolved(activate); break; + case "Suspect": + case "Suspected": + c.setSuspected(activate); + break; - // Other attributes: renown, monstrous, suspected, etc + // Other attributes: renown, monstrous, suspected, etc default: break; 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 6e585649092..7c606eb5f61 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -140,6 +140,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { protected KeywordsChange changedCardKeywordsByWord = new KeywordsChange(ImmutableList.of(), ImmutableList.of(), false); // Layer 3 by Word Change private final Table changedCardKeywords = TreeBasedTable.create(); // Layer 6 + protected KeywordsChange suspectedKeywordChange = null; + // stores the keywords created by static abilities private final Map, KeywordInterface> storedKeywords = Maps.newHashMap(); @@ -216,6 +218,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private boolean renowned; private boolean solved = false; + private boolean suspected = false; private boolean manifested; @@ -2599,6 +2602,9 @@ else if (o.grantsZonePermissions()) if (solved) { sb.append("Solved\r\n"); } + if (suspected) { + sb.append("Suspected\r\n"); + } if (manifested) { sb.append("Manifested\r\n"); } @@ -3935,7 +3941,8 @@ public Iterable getChangedCardKeywordsList() { changedCardKeywordsByText.values(), // Layer 3 ImmutableList.of(changedCardKeywordsByWord), // Layer 3 ImmutableList.of(new KeywordsChange(ImmutableList.of(), ImmutableList.of(), this.hasRemoveIntrinsic())), // Layer 4 - changedCardKeywords.values() // Layer 6 + changedCardKeywords.values(), // Layer 6 + getSuspectedKeywordChange() // Menace by Suspected ); } @@ -4414,21 +4421,21 @@ public final void addPerpetual(Map p) { public final void executePerpetual(Map p) { final String category = (String) p.get("Category"); if (category.equals("NewPT")) { - addNewPT((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) + addNewPT((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) p.get("Timestamp"), (long) 0); } else if (category.equals("PTBoost")) { - addPTBoost((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) + addPTBoost((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) p.get("Timestamp"), (long) 0); } else if (category.equals("Keywords")) { boolean removeAll = p.containsKey("RemoveAll") && (boolean) p.get("RemoveAll") == true; - addChangedCardKeywords((List) p.get("AddKeywords"), Lists.newArrayList(), removeAll, + addChangedCardKeywords((List) p.get("AddKeywords"), Lists.newArrayList(), removeAll, (long) p.get("Timestamp"), (long) 0); } else if (category.equals("Types")) { - addChangedCardTypes((CardType) p.get("AddTypes"), (CardType) p.get("RemoveTypes"), - false, (Set) p.get("RemoveXTypes"), + addChangedCardTypes((CardType) p.get("AddTypes"), (CardType) p.get("RemoveTypes"), + false, (Set) p.get("RemoveXTypes"), (long) p.get("Timestamp"), (long) 0, true, false); } else if (category.equals("Colors")) { - addColor((ColorSet) p.get("Colors"), !(boolean) p.get("Overwrite"), (long) p.get("Timestamp"), + addColor((ColorSet) p.get("Colors"), !(boolean) p.get("Overwrite"), (long) p.get("Timestamp"), (long) 0, false); } } @@ -6226,6 +6233,28 @@ public final void setSolved(final boolean solved) { this.solved = solved; } + public final boolean isSuspected() { + return suspected; + } + + public final void setSuspected(final boolean suspected) { + if (suspected && StaticAbilityCantBeSuspected.cantBeSuspected(this)) { + return; + } + this.suspected = suspected; + if (suspected) { + KeywordInterface kw = Keyword.getInstance("Menace"); + kw.createTraits(this, false); + suspectedKeywordChange = new KeywordsChange(ImmutableList.of(kw), ImmutableList.of(), false); + } + updateKeywords(); + } + + protected Iterable getSuspectedKeywordChange() + { + return isSuspected() ? ImmutableList.of(this.suspectedKeywordChange) : ImmutableList.of(); + } + public final boolean isManifested() { return manifested; } 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 749b795a16e..67ecd0a5941 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1908,6 +1908,14 @@ else if (property.equals("blocked")) { if (card.isSolved()) { return false; } + } else if (property.equals("IsSuspected")) { + if (!card.isSuspected()) { + return false; + } + } else if (property.equals("IsUnsuspected")) { + if (card.isSuspected()) { + return false; + } } else if (property.equals("IsRemembered")) { if (!source.isRemembered(card)) { return false; 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 c1f43a06d5f..b8127609a3e 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -268,6 +268,7 @@ public static Card getLKICopy(final Card in, Map cachedMap) { newCopy.setMonstrous(in.isMonstrous()); newCopy.setRenowned(in.isRenowned()); newCopy.setSolved(in.isSolved()); + newCopy.setSuspected(in.isSuspected()); newCopy.setColor(in.getColor().getColor()); newCopy.setPhasedOut(in.getPhasedOut()); diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index 8555544404b..74f79cfb5a6 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -507,6 +507,10 @@ public static boolean canBlock(final Card blocker, final boolean nextTurn) { return false; } + if (blocker.isSuspected()) { + return false; + } + if (blocker.hasKeyword("CARDNAME can't block.") || blocker.hasKeyword("CARDNAME can't attack or block.") || blocker.isPhasedOut()) { return false; diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeSuspected.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeSuspected.java new file mode 100644 index 00000000000..f7de559b93f --- /dev/null +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeSuspected.java @@ -0,0 +1,32 @@ +package forge.game.staticability; + +import forge.game.Game; +import forge.game.card.Card; +import forge.game.zone.ZoneType; + +public class StaticAbilityCantBeSuspected { + + static String MODE = "CantBeSuspected"; + + public static boolean cantBeSuspected(final Card c) { + final Game game = c.getGame(); + for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) { + for (final StaticAbility stAb : ca.getStaticAbilities()) { + if (!stAb.checkConditions(MODE)) { + continue; + } + if (cantBeSuspectedCheck(stAb, c)) { + return true; + } + } + } + return false; + } + + private static boolean cantBeSuspectedCheck(final StaticAbility stAb, final Card card) { + if (stAb.matchesValidParam("ValidCard", card)) { + return true; + } + return false; + } +}