From ae255a8e8fee4344846c9dfb276324be3b602ef4 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Sat, 22 Oct 2022 14:10:39 +0200 Subject: [PATCH] Fix Bargaining Table (#1727) --- .../src/main/java/forge/ai/AiController.java | 34 +++++++++---------- .../java/forge/ai/PlayerControllerAi.java | 10 +++--- .../ability/effects/ChooseNumberEffect.java | 2 +- .../src/main/java/forge/game/cost/Cost.java | 2 +- .../res/cardsfolder/b/bargaining_table.txt | 6 ++-- forge-gui/res/cardsfolder/b/blizzard.txt | 3 +- .../b/bloodline_keeper_lord_of_lineage.txt | 2 +- .../java/forge/player/HumanCostDecision.java | 12 +++++-- .../forge/player/HumanPlaySpellAbility.java | 7 +++- 9 files changed, 45 insertions(+), 33 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index a8fa6b77591..a66f3792296 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1086,7 +1086,7 @@ public class AiController { if (source.hasSVar("AIPriorityModifier")) { p += Integer.parseInt(source.getSVar("AIPriorityModifier")); } - if (ComputerUtilCard.isCardRemAIDeck(source)) { + if (ComputerUtilCard.isCardRemAIDeck(sa.getOriginalHost() != null ? sa.getOriginalHost() : source)) { p -= 10; } // don't play equipments before having any creatures @@ -1955,6 +1955,22 @@ public class AiController { return max; } + public int chooseNumber(SpellAbility sa, String title, List options, Player relatedPlayer) { + switch(sa.getApi()) + { + case SetLife: // Reverse the Sands + if (relatedPlayer.equals(sa.getHostCard().getController())) { + return Collections.max(options); + } else if (relatedPlayer.isOpponentOf(sa.getHostCard().getController())) { + return Collections.min(options); + } else { + return options.get(0); + } + default: + return options.get(0); + } + } + public boolean confirmPayment(CostPart costPart) { throw new UnsupportedOperationException("AI is not supposed to reach this code at the moment"); } @@ -2120,22 +2136,6 @@ public class AiController { return library; } // smoothComputerManaCurve() - public int chooseNumber(SpellAbility sa, String title, List options, Player relatedPlayer) { - switch(sa.getApi()) - { - case SetLife: // Reverse the Sands - if (relatedPlayer.equals(sa.getHostCard().getController())) { - return Collections.max(options); - } else if (relatedPlayer.isOpponentOf(sa.getHostCard().getController())) { - return Collections.min(options); - } else { - return options.get(0); - } - default: - return 0; - } - } - public boolean chooseDirection(SpellAbility sa) { if (sa == null || sa.getApi() == null) { throw new UnsupportedOperationException(); diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index fcfbbf61d3e..0d06433d2c5 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -591,11 +591,6 @@ public class PlayerControllerAi extends PlayerController { return ComputerUtil.vote(player, options, sa, votes, forPlayer); } - @Override - public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question) { - return brains.aiShouldRun(replacementEffect, effectSA, affected); - } - @Override public boolean mulliganKeepHand(Player firstPlayer, int cardsToReturn) { return !ComputerUtil.wantMulligan(player, cardsToReturn); @@ -1012,6 +1007,11 @@ public class PlayerControllerAi extends PlayerController { return brains.confirmPayment(costPart); // AI is expected to know what it is paying for at the moment (otherwise add another parameter to this method) } + @Override + public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question) { + return brains.aiShouldRun(replacementEffect, effectSA, affected); + } + @Override public ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers) { return brains.chooseSingleReplacementEffect(possibleReplacers); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java index 63e99413a36..6ac1e42e252 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java @@ -61,7 +61,7 @@ public class ChooseNumberEffect extends SpellAbilityEffect { String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : Localizer.getInstance().getMessage("lblChooseNumber"); if (anyNumber) { Integer value = p.getController().announceRequirements(sa, title); - chosen = (value == null ? 0 : value); + chosen = value == null ? 0 : value; } else { chosen = p.getController().chooseNumber(sa, title, min, max); } diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index 731e36e9e1a..548ea4ed3e5 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -245,7 +245,7 @@ public class Cost implements Serializable { this.isMandatory = true; } else { CostPart cp = parseCostPart(part, tapCost, untapCost); - if (null != cp ) + if (null != cp) if (cp instanceof CostPartMana) { parsedMana = (CostPartMana) cp; } else { diff --git a/forge-gui/res/cardsfolder/b/bargaining_table.txt b/forge-gui/res/cardsfolder/b/bargaining_table.txt index 97cf668e3c0..1b83145cb10 100644 --- a/forge-gui/res/cardsfolder/b/bargaining_table.txt +++ b/forge-gui/res/cardsfolder/b/bargaining_table.txt @@ -1,7 +1,7 @@ Name:Bargaining Table ManaCost:5 Types:Artifact -# The ability is untargeted (see: http://magiccards.info/mm/en/288.html), so the opponent is automaticaly chosen. This has to be modified as soon as multiplayer formats get supported. -A:AB$ Draw | Cost$ X T | NumCards$ 1 | SpellDescription$ Draw a card. X is the number of cards in an opponent's hand. -SVar:X:Count$InOppHand +A:AB$ Draw | Cost$ X T | NumCards$ 1 | AnnounceType$ Opponent | SpellDescription$ Draw a card. X is the number of cards in an opponent's hand. +SVar:X:Count$InChosenHand +AI:RemoveDeck:All Oracle:{X}, {T}: Draw a card. X is the number of cards in an opponent's hand. diff --git a/forge-gui/res/cardsfolder/b/blizzard.txt b/forge-gui/res/cardsfolder/b/blizzard.txt index fd73dfaf9df..d8667268a04 100644 --- a/forge-gui/res/cardsfolder/b/blizzard.txt +++ b/forge-gui/res/cardsfolder/b/blizzard.txt @@ -3,7 +3,6 @@ ManaCost:G G Types:Enchantment K:Cumulative upkeep:2 S:Mode$ Continuous | Affected$ Creature.withFlying | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Creatures with flying don't untap during their controllers' untap steps. -S:Mode$ CantBeCast | ValidCard$ Card.Self | EffectZone$ All | CheckSVar$ X | SVarCompare$ EQ0 | Description$ Cast CARDNAME only if you control a snow land. -SVar:X:Count$Valid Snow.Land+YouCtrl +S:Mode$ CantBeCast | ValidCard$ Card.Self | EffectZone$ All | IsPresent$ Snow.Land+YouCtrl | PresentCompare$ EQ0 | Description$ Cast CARDNAME only if you control a snow land. AI:RemoveDeck:Random Oracle:Cast this spell only if you control a snow land.\nCumulative 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.)\nCreatures with flying don't untap during their controllers' untap steps. diff --git a/forge-gui/res/cardsfolder/b/bloodline_keeper_lord_of_lineage.txt b/forge-gui/res/cardsfolder/b/bloodline_keeper_lord_of_lineage.txt index 3ae4c561c84..f53bdb0f136 100644 --- a/forge-gui/res/cardsfolder/b/bloodline_keeper_lord_of_lineage.txt +++ b/forge-gui/res/cardsfolder/b/bloodline_keeper_lord_of_lineage.txt @@ -4,7 +4,7 @@ Types:Creature Vampire PT:3/3 K:Flying A:AB$ Token | Cost$ T | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ b_2_2_vampire_flying | SpellDescription$ Create a 2/2 black Vampire creature token with flying. -A:AB$ SetState | Cost$ B | Defined$ Self | IsPresent$ Card.Vampire+YouCtrl | PresentCompare$ GE5 | Mode$ Transform | SpellDescription$ Transform CARDNAME.Activate only if you control five or more Vampires. +A:AB$ SetState | Cost$ B | Defined$ Self | IsPresent$ Card.Vampire+YouCtrl | PresentCompare$ GE5 | Mode$ Transform | SpellDescription$ Transform CARDNAME. Activate only if you control five or more Vampires. AlternateMode:DoubleFaced Oracle:Flying\n{T}: Create a 2/2 black Vampire creature token with flying.\n{B}: Transform Bloodline Keeper. Activate only if you control five or more Vampires. diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index b0d21c28d6b..9e939aae3c4 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -689,8 +689,13 @@ public class HumanCostDecision extends CostDecisionMakerBase { } // Cards to use this branch: Scarscale Ritual, Wandering Mage - each adds only one counter - final CardCollectionView typeList = CardLists.getValidCards(source.getGame().getCardsIn(ZoneType.Battlefield), + CardCollectionView typeList = CardLists.getValidCards(source.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, ability.getHostCard(), ability); + typeList = CardLists.filter(typeList, CardPredicates.canReceiveCounters(cost.getCounter())); + + if (typeList.isEmpty()) { + return null; + } final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, 1, typeList, ability); inp.setMessage(Localizer.getInstance().getMessage("lblPutNTypeCounterOnTarget", String.valueOf(c), cost.getCounter().getName(), cost.getDescriptiveType())); @@ -717,6 +722,10 @@ public class HumanCostDecision extends CostDecisionMakerBase { final CardCollectionView validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source, ability); + if (validCards.size() < c) { + return null; + } + final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, validCards, ability); inp.setCancelAllowed(!mandatory); inp.setMessage(Localizer.getInstance().getMessage("lblNTypeCardsToHand", "%d", cost.getDescriptiveType())); @@ -804,7 +813,6 @@ public class HumanCostDecision extends CostDecisionMakerBase { int c = cost.getAbilityAmount(ability); final String type = cost.getType(); - CardCollectionView list = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), player, source, ability); list = CardLists.filter(list, CardPredicates.hasCounters()); diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index bd61c42ef3a..c0565cec9fe 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -41,6 +41,7 @@ import forge.game.player.Player; import forge.game.player.PlayerController; import forge.game.spellability.SpellAbility; import forge.game.zone.Zone; +import forge.util.Localizer; /** *

@@ -281,9 +282,13 @@ public class HumanPlaySpellAbility { final int min = Integer.parseInt(ability.getParam("Min")); final int max = Integer.parseInt(ability.getParam("Max")); final int i = ability.getActivatingPlayer().getController().chooseNumber(ability, - "Choose a number", min, max); + Localizer.getInstance().getMessage("lblChooseNumber") , min, max); ability.getHostCard().setChosenNumber(i); } + if ("Opponent".equals(varName)) { + Player opp = ability.getActivatingPlayer().getController().chooseSingleEntityForEffect(ability.getActivatingPlayer().getOpponents(), ability, Localizer.getInstance().getMessage("lblChooseAnOpponent"), null); + ability.getHostCard().setChosenPlayer(opp); + } } } return true;