From f60718b7b5dac1a2a12e6aca795e86a7deee6962 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Wed, 8 May 2024 21:06:04 +0200 Subject: [PATCH] Some fixes (#5219) Co-authored-by: tool4EvEr --- .../main/java/forge/ai/ComputerUtilMana.java | 2 +- .../java/forge/ai/ability/PermanentAi.java | 3 +- .../game/ability/effects/BranchEffect.java | 12 +-- .../ability/effects/TapOrUntapEffect.java | 17 ++-- .../src/main/java/forge/game/card/Card.java | 81 ++++++++----------- .../java/forge/game/spellability/Spell.java | 14 ++-- .../StaticAbilityContinuous.java | 8 +- forge-gui/res/cardsfolder/n/nightsnare.txt | 2 +- .../res/cardsfolder/r/reckoner_shakedown.txt | 2 +- .../res/cardsfolder/s/siphon_insight.txt | 2 +- forge-gui/res/lists/TypeLists.txt | 1 + 11 files changed, 60 insertions(+), 84 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 397428b8540..2eea71baf9a 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -1352,7 +1352,7 @@ public class ComputerUtilMana { CostAdjustment.adjust(cost, sa, null, test); int timesMultikicked = card.getKickerMagnitude(); - if (timesMultikicked > 0 && sa.hasParam("Announce") && sa.getParam("Announce").startsWith("Multikicker")) { + if (timesMultikicked > 0 && sa.isAnnouncing("Multikicker")) { ManaCost mkCost = sa.getMultiKickerManaCost(); for (int i = 0; i < timesMultikicked; i++) { cost.addManaCost(mkCost); diff --git a/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java b/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java index 4da029d281b..dfbdadbbc3b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PermanentAi.java @@ -153,8 +153,7 @@ public class PermanentAi extends SpellAbilityAi { return false; } - if (sa.hasParam("Announce") && sa.getParam("Announce").startsWith("Multikicker")) { - // String announce = sa.getParam("Announce"); + if (sa.isAnnouncing("Multikicker")) { ManaCost mkCost = sa.getMultiKickerManaCost(); ManaCost mCost = sa.getPayCosts().getTotalMana(); boolean isZeroCost = mCost.isZero(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/BranchEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BranchEffect.java index a8f5cb2dedc..dce42d273d5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/BranchEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/BranchEffect.java @@ -13,23 +13,17 @@ public class BranchEffect extends SpellAbilityEffect { // TODO Reuse SpellAbilityCondition and areMet() here instead of repeating each - int value = 0; - if (sa.hasParam("BranchCondition")) { - if (sa.getParam("BranchCondition").equals("ChosenCard")) { - value = host.getChosenCards().size(); - } - } else { - value = AbilityUtils.calculateAmount(host, sa.getParam("BranchConditionSVar"), sa); - } + String branchSVar = sa.getParam("BranchConditionSVar"); String branchCompare = sa.getParamOrDefault("BranchConditionSVarCompare", "GE1"); String operator = branchCompare.substring(0, 2); String operand = branchCompare.substring(2); + final int svarValue = AbilityUtils.calculateAmount(host, branchSVar, sa); final int operandValue = AbilityUtils.calculateAmount(host, operand, sa); SpellAbility sub = null; - if (Expressions.compare(value, operator, operandValue)) { + if (Expressions.compare(svarValue, operator, operandValue)) { sub = sa.getAdditionalAbility("TrueSubAbility"); } else { sub = sa.getAdditionalAbility("FalseSubAbility"); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java index 8fc700e29ed..903cb8b24c6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java @@ -35,8 +35,11 @@ public class TapOrUntapEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - Player activator = sa.getActivatingPlayer(); - PlayerController pc = activator.getController(); + Player tapper = sa.getActivatingPlayer(); + if (sa.hasParam("Tapper")) { + tapper = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Tapper"), sa).getFirst(); + } + PlayerController pc = tapper.getController(); CardCollection tapped = new CardCollection(); final Map untapMap = Maps.newHashMap(); @@ -50,12 +53,8 @@ public class TapOrUntapEffect extends SpellAbilityEffect { // If the effected card is controlled by the same controller of the SA, default to untap. boolean tap = pc.chooseBinary(sa, Localizer.getInstance().getMessage("lblTapOrUntapTarget", CardTranslation.getTranslatedName(tgtC.getName())), PlayerController.BinaryChoiceType.TapOrUntap, - !tgtC.getController().equals(activator) ); + !tgtC.getController().equals(tapper) ); - Player tapper = activator; - if (sa.hasParam("Tapper")) { - tapper = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Tapper"), sa).getFirst(); - } if (tap) { if (tgtC.tap(true, sa, tapper)) tapped.add(tgtC); } else if (tgtC.untap(true)) { @@ -65,12 +64,12 @@ public class TapOrUntapEffect extends SpellAbilityEffect { if (!untapMap.isEmpty()) { final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Map, untapMap); - activator.getGame().getTriggerHandler().runTrigger(TriggerType.UntapAll, runParams, false); + tapper.getGame().getTriggerHandler().runTrigger(TriggerType.UntapAll, runParams, false); } if (!tapped.isEmpty()) { final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Cards, tapped); - activator.getGame().getTriggerHandler().runTrigger(TriggerType.TapAll, runParams, false); + tapper.getGame().getTriggerHandler().runTrigger(TriggerType.TapAll, runParams, false); } } 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 06349a4fa23..1489dec177c 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2290,12 +2290,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { ? ", or " : n + 2 == costs.length ? " or " : ", "); } } else if (keyword.startsWith("Multikicker")) { - if (!keyword.endsWith("Generic")) { - final String[] n = keyword.split(":"); - final Cost cost = new Cost(n[1], false); - sbLong.append("Multikicker ").append(cost.toSimpleString()); - sbLong.append(" (").append(inst.getReminderText()).append(")").append("\r\n"); - } + final String[] n = keyword.split(":"); + final Cost cost = new Cost(n[1], false); + sbLong.append("Multikicker ").append(cost.toSimpleString()); + sbLong.append(" (").append(inst.getReminderText()).append(")").append("\r\n"); } else if (keyword.startsWith("Kicker")) { sbLong.append(kickerDesc(keyword, inst.getReminderText())).append("\r\n"); } else if (keyword.startsWith("Trample:")) { @@ -2583,41 +2581,34 @@ public class Card extends GameEntity implements Comparable, IHasSVars { final Cost cost = new Cost(n[1], false); final String costStr = cost.toSimpleString(); final boolean manaOnly = cost.isOnlyManaCost(); - if (!keyword.endsWith("Generic")) { - sbx.append("Kicker").append(manaOnly ? " " + costStr : "—" + costStr + "."); - if (Lists.newArrayList(n).size() > 2) { - sbx.append(" and/or "); - final Cost cost2 = new Cost(n[2], false); - sbx.append(cost2.toSimpleString()); - } - if (!manaOnly) { - if (cost.hasNoManaCost()) { - remText = remText.replaceFirst(" pay an additional", ""); - remText = remText.replace(remText.charAt(8), Character.toLowerCase(remText.charAt(8))); - } else { - remText = remText.replaceFirst(" an additional", ""); - char c = remText.charAt(remText.indexOf(",") + 2); - remText = remText.replace(c, Character.toLowerCase(c)); - remText = remText.replaceFirst(", ", " and "); - } - remText = remText.replaceFirst("as", "in addition to any other costs as"); - if (remText.contains(" tap ")) { - if (remText.contains("tap a")) { - String noun = remText.substring(remText.indexOf("untapped") + 9, remText.indexOf(" in ")); - remText = remText.replace(remText.substring(12, remText.indexOf(" in ")), - Lang.nounWithNumeralExceptOne(1, noun) + " "); - } else { - remText = remText.replaceFirst(" untapped ", ""); - } - } - } - sbx.append(" (").append(remText).append(")\r\n"); - } else { - sbx.append("As an additional cost to cast this spell, you may "); - String costS = StringUtils.uncapitalize(costStr); - sbx.append(cost.hasManaCost() ? "pay " + costS : costS); - sbx.append(".").append("\r\n"); + sbx.append("Kicker").append(manaOnly ? " " + costStr : "—" + costStr + "."); + if (Lists.newArrayList(n).size() > 2) { + sbx.append(" and/or "); + final Cost cost2 = new Cost(n[2], false); + sbx.append(cost2.toSimpleString()); } + if (!manaOnly) { + if (cost.hasNoManaCost()) { + remText = remText.replaceFirst(" pay an additional", ""); + remText = remText.replace(remText.charAt(8), Character.toLowerCase(remText.charAt(8))); + } else { + remText = remText.replaceFirst(" an additional", ""); + char c = remText.charAt(remText.indexOf(",") + 2); + remText = remText.replace(c, Character.toLowerCase(c)); + remText = remText.replaceFirst(", ", " and "); + } + remText = remText.replaceFirst("as", "in addition to any other costs as"); + if (remText.contains(" tap ")) { + if (remText.contains("tap a")) { + String noun = remText.substring(remText.indexOf("untapped") + 9, remText.indexOf(" in ")); + remText = remText.replace(remText.substring(12, remText.indexOf(" in ")), + Lang.nounWithNumeralExceptOne(1, noun) + " "); + } else { + remText = remText.replaceFirst(" untapped ", ""); + } + } + } + sbx.append(" (").append(remText).append(")\r\n"); return sbx.toString(); } @@ -3028,11 +3019,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbBefore.append(sbCost).append(" (").append(inst.getReminderText()).append(")"); sbBefore.append("\r\n"); } else if (keyword.startsWith("Multikicker")) { - if (!keyword.endsWith("Generic")) { - final String[] n = keyword.split(":"); - final Cost cost = new Cost(n[1], false); - sbBefore.append("Multikicker ").append(cost.toSimpleString()).append(" (").append(inst.getReminderText()).append(")").append("\r\n"); - } + final String[] n = keyword.split(":"); + final Cost cost = new Cost(n[1], false); + sbBefore.append("Multikicker ").append(cost.toSimpleString()).append(" (").append(inst.getReminderText()).append(")").append("\r\n"); } else if (keyword.startsWith("Kicker")) { sbBefore.append(kickerDesc(keyword, inst.getReminderText())).append("\r\n"); } else if (keyword.startsWith("AlternateAdditionalCost")) { @@ -4622,7 +4611,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } private int multiKickerMagnitude = 0; - public final void addMultiKickerMagnitude(final int n) { multiKickerMagnitude += n; } public final void setKickerMagnitude(final int n) { multiKickerMagnitude = n; } public final int getKickerMagnitude() { if (multiKickerMagnitude > 0) { @@ -4673,7 +4661,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final boolean isUntapped() { return !tapped; } - public final boolean isTapped() { return tapped; } 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 d4ac459c2ab..dec93b53856 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -74,9 +74,6 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable return false; } - // Save the original cost and the face down info for a later check since the LKI copy will overwrite them - ManaCost origCost = card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost(); - Player activator = this.getActivatingPlayer(); if (activator == null) { activator = card.getController(); @@ -90,6 +87,9 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable return false; } + // Save the original cost and the face down info for a later check since the LKI copy will overwrite them + ManaCost origCost = card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost(); + // do performanceMode only for cases where the activator is different than controller if (!Spell.performanceMode && !card.getController().equals(activator)) { // always make a lki copy in this case? @@ -105,10 +105,8 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable // for uncastables like lotus bloom, check if manaCost is blank (except for morph spells) // but ignore if it comes from PlayEffect - if (!isCastFaceDown() - && !isCastFromPlayEffect() - && isBasicSpell() - && origCost.isNoCost()) { + if (!isCastFaceDown() && !isCastFromPlayEffect() + && isBasicSpell() && origCost.isNoCost()) { return false; } @@ -117,7 +115,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable } return true; - } // canPlay() + } /** {@inheritDoc} */ @Override 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 6be0d46bd3d..00897a8d025 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -753,10 +753,8 @@ public final class StaticAbilityContinuous { Iterables.removeIf(newKeywords, new Predicate() { @Override public boolean apply(String input) { - if (input.contains("CardManaCost")) { - if (affectedCard.getManaCost().isNoCost()) { - return true; - } + if (input.contains("CardManaCost") && affectedCard.getManaCost().isNoCost()) { + return true; } // replace one Keyword with list of keywords if (input.startsWith("Protection") && input.contains("CardColors")) { @@ -897,7 +895,7 @@ public final class StaticAbilityContinuous { } } - if (!addedAbilities.isEmpty() || addReplacements != null || addTriggers != null || addStatics != null + if (!addedAbilities.isEmpty() || !addedTrigger.isEmpty() || addReplacements != null || addStatics != null || removeAllAbilities) { affectedCard.addChangedCardTraits( addedAbilities, null, addedTrigger, addedReplacementEffects, addedStaticAbility, removeAllAbilities, removeNonMana, diff --git a/forge-gui/res/cardsfolder/n/nightsnare.txt b/forge-gui/res/cardsfolder/n/nightsnare.txt index ea18f730757..07d1ec463ab 100644 --- a/forge-gui/res/cardsfolder/n/nightsnare.txt +++ b/forge-gui/res/cardsfolder/n/nightsnare.txt @@ -3,7 +3,7 @@ ManaCost:3 B Types:Sorcery A:SP$ RevealHand | ValidTgts$ Opponent | RememberRevealed$ True | SubAbility$ DBChoose | SpellDescription$ Target opponent reveals their hand. SVar:DBChoose:DB$ ChooseCard | ChoiceZone$ Hand | Amount$ 1 | Choices$ Card.nonLand+IsRemembered | ChoiceDesc$ nonland | FromDesc$ it | SubAbility$ DBBranch | ChoiceTitle$ You may choose a nonland card | SpellDescription$ You may choose a nonland card from it. -SVar:DBBranch:DB$ Branch | BranchCondition$ ChosenCard | TrueSubAbility$ DBDiscard | FalseSubAbility$ DBDiscard2 | StackDescription$ If they do, {p:Targeted} discards that card. If they don't, {p:Targeted} discards two cards. | SpellDescription$ If you do, that player discards that card. If you don't, that player discards two cards. +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ Count$ChosenSize | TrueSubAbility$ DBDiscard | FalseSubAbility$ DBDiscard2 | StackDescription$ If they do, {p:Targeted} discards that card. If they don't, {p:Targeted} discards two cards. | SpellDescription$ If you do, that player discards that card. If you don't, that player discards two cards. SVar:DBDiscard:DB$ Discard | DefinedCards$ ChosenCard | Defined$ Targeted | Mode$ Defined | SubAbility$ DBCleanup SVar:DBDiscard2:DB$ Discard | Defined$ Targeted | NumCards$ 2 | Mode$ TgtChoose | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True diff --git a/forge-gui/res/cardsfolder/r/reckoner_shakedown.txt b/forge-gui/res/cardsfolder/r/reckoner_shakedown.txt index 7e16e0b6c6d..9853f85f85d 100644 --- a/forge-gui/res/cardsfolder/r/reckoner_shakedown.txt +++ b/forge-gui/res/cardsfolder/r/reckoner_shakedown.txt @@ -3,7 +3,7 @@ ManaCost:2 B Types:Sorcery A:SP$ RevealHand | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | RememberRevealed$ True | SubAbility$ DBChoose | SpellDescription$ Target opponent reveals their hand. SVar:DBChoose:DB$ ChooseCard | ChoiceZone$ Hand | Amount$ 1 | Choices$ Card.nonLand+IsRemembered | ChoiceDesc$ nonland | FromDesc$ it | SubAbility$ DBBranch | ChoiceTitle$ You may choose a nonland card | SpellDescription$ You may choose a nonland card from it. -SVar:DBBranch:DB$ Branch | BranchCondition$ ChosenCard | TrueSubAbility$ DBDiscard | FalseSubAbility$ DBPutCounter | StackDescription$ If they do, {p:Targeted} discards that card. If they don't, they put two +1/+1 counters on a creature or Vehicle they control. | SpellDescription$ If you do, that player discards that card. If you don't, put two +1/+1 counters on a creature or Vehicle you control. +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ Count$ChosenSize | TrueSubAbility$ DBDiscard | FalseSubAbility$ DBPutCounter | StackDescription$ If they do, {p:Targeted} discards that card. If they don't, they put two +1/+1 counters on a creature or Vehicle they control. | SpellDescription$ If you do, that player discards that card. If you don't, put two +1/+1 counters on a creature or Vehicle you control. SVar:DBDiscard:DB$ Discard | DefinedCards$ ChosenCard | Defined$ Targeted | Mode$ Defined | SubAbility$ DBCleanup SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.YouCtrl,Vehicle.YouCtrl | ChoiceTitle$ Choose a creature or Vehicle you control | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True diff --git a/forge-gui/res/cardsfolder/s/siphon_insight.txt b/forge-gui/res/cardsfolder/s/siphon_insight.txt index 4148241c617..a54d8e3db98 100644 --- a/forge-gui/res/cardsfolder/s/siphon_insight.txt +++ b/forge-gui/res/cardsfolder/s/siphon_insight.txt @@ -3,7 +3,7 @@ ManaCost:U B Types:Instant A:SP$ Dig | ValidTgts$ Opponent | DigNum$ 2 | ChangeNum$ 1 | DestinationZone$ Exile | DestinationZone2$ Library | ExileFaceDown$ True | ChangeValid$ Card | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Look at the top two cards of target opponent's library. Exile one of them face down and put the other on the bottom of that library. You may look at and play the exiled card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup | StackDescription$ Exile one of them face down and put the other on the bottom of that library. You may look at and play the exiled card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. -SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | MayPlayIgnoreType$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Secondary$ True | Description$ You may look at and play the exiled card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. +SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | MayPlayIgnoreColor$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Secondary$ True | Description$ You may look at and play the exiled card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True K:Flashback:1 U B DeckHas:Ability$Graveyard diff --git a/forge-gui/res/lists/TypeLists.txt b/forge-gui/res/lists/TypeLists.txt index e6b09adb153..c36425e1fd5 100644 --- a/forge-gui/res/lists/TypeLists.txt +++ b/forge-gui/res/lists/TypeLists.txt @@ -385,6 +385,7 @@ Guff Huatli Inzerva Jace +Jared Jaya Jeska Kaito