From 38d1744f16967753fb97dc8edb75c4a4d27d1c91 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 1 Jun 2013 12:55:08 +0000 Subject: [PATCH] changed parameters consumed by HumanPlay.payCostDuringAbilityResolve - it does not really need a surrogate ability. AiController.confirmAction - spread choices made for specific apis among their matching XxxxxAi classes - to avoid a huge switch and keep all related code in a single place. --- .../java/forge/card/ability/AbilityUtils.java | 10 ++-- src/main/java/forge/card/ability/ApiType.java | 1 - .../forge/card/ability/SpellAbilityAi.java | 6 +++ .../card/ability/ai/CopyPermanentAi.java | 10 ++++ .../java/forge/card/ability/ai/DigAi.java | 11 +++++ .../java/forge/card/ability/ai/DiscardAi.java | 10 ++++ .../java/forge/card/ability/ai/DrawAi.java | 11 +++++ .../java/forge/card/ability/ai/EncodeAi.java | 7 +++ .../card/ability/ai/PeekAndRevealAi.java | 11 +++++ .../java/forge/card/ability/ai/PumpAi.java | 11 +++++ .../java/forge/card/ability/ai/RepeatAi.java | 8 ++++ .../java/forge/card/ability/ai/ShuffleAi.java | 9 ++++ src/main/java/forge/game/GameAction.java | 6 +-- src/main/java/forge/game/ai/AiController.java | 48 ++----------------- .../java/forge/game/ai/ComputerUtilCost.java | 11 ++--- .../java/forge/game/phase/CombatUtil.java | 2 +- src/main/java/forge/game/phase/PhaseUtil.java | 2 +- src/main/java/forge/game/phase/Upkeep.java | 26 +++++----- .../java/forge/game/player/HumanPlay.java | 20 ++++---- 19 files changed, 135 insertions(+), 85 deletions(-) diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index c24027d6a61..dd036c9553d 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -1102,22 +1102,20 @@ public class AbilityUtils { } final Cost cost = new Cost(unlessCost, true); - final Ability ability = new AbilityStatic(source, cost, sa.getTarget()) { - @Override - public void resolve() { /* nothing to do here */ } - }; + boolean paid = false; for (Player payer : payers) { + final Ability ability = new AbilityStatic(source, cost, sa.getTarget()) { @Override public void resolve() { } }; ability.setActivatingPlayer(payer); if (payer.isComputer()) { - if (ComputerUtilCost.willPayUnlessCost(sa, payer, ability, paid, payers) && ComputerUtilCost.canPayCost(ability, payer)) { + if (ComputerUtilCost.willPayUnlessCost(sa, payer, cost, paid, payers) && ComputerUtilCost.canPayCost(ability, payer)) { ComputerUtil.playNoStack(payer, ability, game); // Unless cost was payed - no resolve paid = true; } } else { // if it's paid by the AI already the human can pay, but it won't change anything - paid |= HumanPlay.payCostDuringAbilityResolve(ability, cost, sa); + paid |= HumanPlay.payCostDuringAbilityResolve(payer, source, cost, sa); } } diff --git a/src/main/java/forge/card/ability/ApiType.java b/src/main/java/forge/card/ability/ApiType.java index 6e8bcbe739a..d0ca6aeb841 100644 --- a/src/main/java/forge/card/ability/ApiType.java +++ b/src/main/java/forge/card/ability/ApiType.java @@ -242,7 +242,6 @@ public enum ApiType { public SpellAbilityEffect getSpellEffect() { return clsEffect == null ? null : ReflectionUtil.makeDefaultInstanceOf(clsEffect); - } public SpellAbilityAi getAi() { diff --git a/src/main/java/forge/card/ability/SpellAbilityAi.java b/src/main/java/forge/card/ability/SpellAbilityAi.java index 8262b3673a2..9cf43e0c972 100644 --- a/src/main/java/forge/card/ability/SpellAbilityAi.java +++ b/src/main/java/forge/card/ability/SpellAbilityAi.java @@ -7,6 +7,7 @@ import forge.game.ai.ComputerUtilCost; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; public abstract class SpellAbilityAi { @@ -103,4 +104,9 @@ public abstract class SpellAbilityAi { final AbilitySub subAb = ab.getSubAbility(); return ab.getAi().chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb)); } + + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); + return true; + } } diff --git a/src/main/java/forge/card/ability/ai/CopyPermanentAi.java b/src/main/java/forge/card/ability/ai/CopyPermanentAi.java index bccecd8994d..226b7154690 100644 --- a/src/main/java/forge/card/ability/ai/CopyPermanentAi.java +++ b/src/main/java/forge/card/ability/ai/CopyPermanentAi.java @@ -15,6 +15,7 @@ import forge.card.spellability.Target; import forge.game.ai.ComputerUtilCard; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -111,5 +112,14 @@ public class CopyPermanentAi extends SpellAbilityAi { return true; } + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + //TODO: add logic here + return true; + } } diff --git a/src/main/java/forge/card/ability/ai/DigAi.java b/src/main/java/forge/card/ability/ai/DigAi.java index 1259a5eb76b..c3796ae4e33 100644 --- a/src/main/java/forge/card/ability/ai/DigAi.java +++ b/src/main/java/forge/card/ability/ai/DigAi.java @@ -2,11 +2,13 @@ package forge.card.ability.ai; import java.util.Random; +import forge.Card; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -65,4 +67,13 @@ public class DigAi extends SpellAbilityAi { return true; } + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + // looks like perfect code for Delver of Secrets, but what about other cards? + Card topc = player.getZone(ZoneType.Library).get(0); + return topc.isInstant() || topc.isSorcery(); + } } diff --git a/src/main/java/forge/card/ability/ai/DiscardAi.java b/src/main/java/forge/card/ability/ai/DiscardAi.java index 7402e1dc3f3..2c5c794ce9c 100644 --- a/src/main/java/forge/card/ability/ai/DiscardAi.java +++ b/src/main/java/forge/card/ability/ai/DiscardAi.java @@ -14,6 +14,7 @@ import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilMana; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -165,4 +166,13 @@ public class DiscardAi extends SpellAbilityAi { // TODO: check for some extra things return true; } // discardCheckDrawbackAI() + + + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + if ( mode == PlayerActionConfirmMode.Random ) { // + // TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards + return true; + } + return super.confirmAction(player, sa, mode, message); + } } diff --git a/src/main/java/forge/card/ability/ai/DrawAi.java b/src/main/java/forge/card/ability/ai/DrawAi.java index 704c6f767b7..c8693c5e800 100644 --- a/src/main/java/forge/card/ability/ai/DrawAi.java +++ b/src/main/java/forge/card/ability/ai/DrawAi.java @@ -37,6 +37,7 @@ import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilMana; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -267,4 +268,14 @@ public class DrawAi extends SpellAbilityAi { return targetAI(ai, sa, mandatory); } + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa) : 1; + // AI shouldn't mill itself + return numCards < player.getZone(ZoneType.Library).size(); + } } diff --git a/src/main/java/forge/card/ability/ai/EncodeAi.java b/src/main/java/forge/card/ability/ai/EncodeAi.java index a57c3c9bdf8..50f64a9a2d4 100644 --- a/src/main/java/forge/card/ability/ai/EncodeAi.java +++ b/src/main/java/forge/card/ability/ai/EncodeAi.java @@ -20,6 +20,7 @@ package forge.card.ability.ai; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; /** *

@@ -51,4 +52,10 @@ public final class EncodeAi extends SpellAbilityAi { public boolean chkAIDrawback(SpellAbility sa, Player ai) { return true; } + + + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + return true; + } } diff --git a/src/main/java/forge/card/ability/ai/PeekAndRevealAi.java b/src/main/java/forge/card/ability/ai/PeekAndRevealAi.java index 79f410d40d2..a6ada4cb287 100644 --- a/src/main/java/forge/card/ability/ai/PeekAndRevealAi.java +++ b/src/main/java/forge/card/ability/ai/PeekAndRevealAi.java @@ -1,8 +1,10 @@ package forge.card.ability.ai; import forge.card.ability.SpellAbilityAi; +import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; /** * TODO: Write javadoc for this type. @@ -19,5 +21,14 @@ public class PeekAndRevealAi extends SpellAbilityAi { // once things get converted from Dig + NoMove return true; } + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + AbilitySub subAb = sa.getSubAbility(); + return subAb != null && subAb.getAi().chkDrawbackWithSubs(player, subAb); + } } diff --git a/src/main/java/forge/card/ability/ai/PumpAi.java b/src/main/java/forge/card/ability/ai/PumpAi.java index 175a35a63f8..11de5a2fd29 100644 --- a/src/main/java/forge/card/ability/ai/PumpAi.java +++ b/src/main/java/forge/card/ability/ai/PumpAi.java @@ -25,6 +25,7 @@ import forge.game.ai.ComputerUtilMana; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; public class PumpAi extends PumpAiBase { @@ -446,4 +447,14 @@ public class PumpAi extends PumpAiBase { return true; } // pumpDrawbackAI() + + + + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + //TODO Add logic here if necessary but I think the AI won't cast + //the spell in the first place if it would curse its own creature + //and the pump isn't mandatory + return true; + } } diff --git a/src/main/java/forge/card/ability/ai/RepeatAi.java b/src/main/java/forge/card/ability/ai/RepeatAi.java index 0e1f74fc3aa..0af52f14586 100644 --- a/src/main/java/forge/card/ability/ai/RepeatAi.java +++ b/src/main/java/forge/card/ability/ai/RepeatAi.java @@ -5,6 +5,7 @@ import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; public class RepeatAi extends SpellAbilityAi { @@ -21,4 +22,11 @@ public class RepeatAi extends SpellAbilityAi { } return true; } + + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + //TODO add logic to have computer make better choice (ArsenalNut) + return false; + } + } diff --git a/src/main/java/forge/card/ability/ai/ShuffleAi.java b/src/main/java/forge/card/ability/ai/ShuffleAi.java index 4b82e6b4074..06737476cf5 100644 --- a/src/main/java/forge/card/ability/ai/ShuffleAi.java +++ b/src/main/java/forge/card/ability/ai/ShuffleAi.java @@ -3,6 +3,7 @@ package forge.card.ability.ai; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; public class ShuffleAi extends SpellAbilityAi { @Override @@ -47,4 +48,12 @@ public class ShuffleAi extends SpellAbilityAi { return true; } + + + + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + // ai could analyze parameter denoting the player to shuffle + return true; + } } diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index b171a217dc9..a6af3ac9a3e 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -506,9 +506,7 @@ public class GameAction { private static final long serialVersionUID = 8858061639236920054L; @Override - public void resolve() { - - } + public void resolve() {} @Override public String getStackDescription() { @@ -533,7 +531,7 @@ public class GameAction { Player p = recoverable.getController(); boolean hasPaid = false; if (p.isHuman()) { - hasPaid = HumanPlay.payCostDuringAbilityResolve(abRecover, abRecover.getPayCosts(), null); + hasPaid = HumanPlay.payCostDuringAbilityResolve(p, recoverable, cost, null); } else { // computer if (ComputerUtilCost.canPayCost(abRecover, p)) { ComputerUtil.playNoStack(p, abRecover, game); diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index 5787921df74..4742839a551 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -35,12 +35,10 @@ import forge.CardPredicates; import forge.CardPredicates.Presets; import forge.Constant; import forge.GameEntity; -import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.CostDiscard; import forge.card.cost.CostPart; -import forge.card.spellability.AbilitySub; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellPermanent; @@ -711,50 +709,12 @@ public class AiController { if( mode != null ) switch (mode) { case BraidOfFire: return true; } - } else switch(api) { - case Discard: - if ( mode == PlayerActionConfirmMode.Random ) { // - // TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards - return true; - } - break; - - case Encode: - return true; - - case Dig: - Card topc = player.getZone(ZoneType.Library).get(0); - return topc.isInstant() || topc.isSorcery(); - - case Repeat: - //TODO add logic to have computer make better choice (ArsenalNut) - return false; - case PeekAndReveal: - AbilitySub subAb = sa.getSubAbility(); - return subAb != null && subAb.getAi().chkDrawbackWithSubs(player, subAb); + String exMsg = String.format("AI confirmAction does not know what to decide about %s mode (api is null).", mode); + throw new InvalidParameterException(exMsg); - case Shuffle: // ai could analyze parameter denoting the player to shuffle - return true; - - case Pump: //TODO Add logic here if necessary but I think the AI won't cast - //the spell in the first place if it would curse its own creature - //and the pump isn't mandatory - return true; - - case Draw: - int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa) : 1; - // AI shouldn't mill itself - return numCards < player.getZone(ZoneType.Library).size(); - - case CopyPermanent: - //TODO: add logic here - return true; - - default: - } - String exMsg = String.format("AI confirmAction does not know what to decide about %s API with %s mode.", api, mode); - throw new InvalidParameterException(exMsg); + } else + return api.getAi().confirmAction(player, sa, mode, message); } public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { diff --git a/src/main/java/forge/game/ai/ComputerUtilCost.java b/src/main/java/forge/game/ai/ComputerUtilCost.java index 8dc760ae724..c7aca5f5a3a 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCost.java +++ b/src/main/java/forge/game/ai/ComputerUtilCost.java @@ -312,8 +312,7 @@ public class ComputerUtilCost { * a {@link java.lang.String} object. * @return a boolean. */ - public static boolean shouldPayCost(final Player ai, final Card hostCard, final String costString) { - final Cost cost = new Cost(costString, false); + public static boolean shouldPayCost(final Player ai, final Card hostCard, final Cost cost) { for (final CostPart part : cost.getCostParts()) { if (part instanceof CostPayLife) { @@ -368,7 +367,7 @@ public class ComputerUtilCost { && CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa); } // canPayCost() - public static boolean willPayUnlessCost(SpellAbility sa, Player payer, SpellAbility ability, boolean alreadyPaid, List payers) { + public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, List payers) { final Card source = sa.getSourceCard(); boolean payForOwnOnly = "OnlyOwn".equals(sa.getParam("UnlessAI")); boolean payOwner = sa.hasParam("UnlessAI") ? sa.getParam("UnlessAI").startsWith("Defined") : false; @@ -409,9 +408,9 @@ public class ComputerUtilCost { // AI was crashing because the blank ability used to pay costs // Didn't have any of the data on the original SA to pay dependant costs - return checkLifeCost(payer, ability.getPayCosts(), source, 4, sa) - && checkDamageCost(payer, ability.getPayCosts(), source, 4) - && (isMine || checkDiscardCost(payer, ability.getPayCosts(), source)) + return checkLifeCost(payer, cost, source, 4, sa) + && checkDamageCost(payer, cost, source, 4) + && (isMine || checkDiscardCost(payer, cost, source)) && (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2) && (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2) && (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1) diff --git a/src/main/java/forge/game/phase/CombatUtil.java b/src/main/java/forge/game/phase/CombatUtil.java index 6087124fd7b..1be0e33aec0 100644 --- a/src/main/java/forge/game/phase/CombatUtil.java +++ b/src/main/java/forge/game/phase/CombatUtil.java @@ -1005,7 +1005,7 @@ public class CombatUtil { ability.setActivatingPlayer(c.getController()); if (c.getController().isHuman()) { - isPaid = HumanPlay.payCostDuringAbilityResolve(ability, attackCost, null); + isPaid = HumanPlay.payCostDuringAbilityResolve(c.getController(), c, attackCost, null); } else { // computer if (ComputerUtilCost.canPayCost(ability, c.getController())) { ComputerUtil.playNoStack(c.getController(), ability, game); diff --git a/src/main/java/forge/game/phase/PhaseUtil.java b/src/main/java/forge/game/phase/PhaseUtil.java index 44ea5fb322f..0877e3723c2 100644 --- a/src/main/java/forge/game/phase/PhaseUtil.java +++ b/src/main/java/forge/game/phase/PhaseUtil.java @@ -181,7 +181,7 @@ public class PhaseUtil { ability.setActivatingPlayer(blocker.getController()); if (blocker.getController().isHuman()) { - hasPaid = HumanPlay.payCostDuringAbilityResolve(ability, blockCost, null); + hasPaid = HumanPlay.payCostDuringAbilityResolve(blocker.getController(), blocker, blockCost, null); } else { // computer if (ComputerUtilCost.canPayCost(ability, blocker.getController())) { ComputerUtil.playNoStack(blocker.getController(), ability, game); diff --git a/src/main/java/forge/game/phase/Upkeep.java b/src/main/java/forge/game/phase/Upkeep.java index fcdad38982c..077bd972085 100644 --- a/src/main/java/forge/game/phase/Upkeep.java +++ b/src/main/java/forge/game/phase/Upkeep.java @@ -165,7 +165,8 @@ public class Upkeep extends Phase { for (int i = 0; i < list.size(); i++) { final Card c = list.get(i); if (c.hasStartOfKeyword("(Echo unpaid)")) { - final Ability blankAbility = Upkeep.getBlankAbility(c, c.getEchoCost()); + Cost cost = new Cost(c.getEchoCost(), true); + final Ability blankAbility = Upkeep.getBlankAbility(c, cost); blankAbility.setActivatingPlayer(c.getController()); final StringBuilder sb = new StringBuilder(); @@ -178,7 +179,7 @@ public class Upkeep extends Phase { Player controller = c.getController(); if (controller.isHuman()) { Cost cost = new Cost(c.getEchoCost().trim(), true); - hasPaid = HumanPlay.payCostDuringAbilityResolve(blankAbility, cost, null); + hasPaid = HumanPlay.payCostDuringAbilityResolve(controller, c, cost, null); } else { // computer if (ComputerUtilCost.canPayCost(blankAbility, controller)) { ComputerUtil.playNoStack(controller, blankAbility, game); @@ -230,7 +231,7 @@ public class Upkeep extends Phase { Singletons.getControl().getInputQueue().setInputAndWait(inp); isUpkeepPaid = inp.isPaid(); } else { // computer - Ability aiPaid = Upkeep.getBlankAbility(c, upkeepCost.toString()); + Ability aiPaid = Upkeep.getBlankAbility(c, new Cost(upkeepCost, true)); isUpkeepPaid = ComputerUtilCost.canPayCost(aiPaid, controller) && !c.hasKeyword("Indestructible"); if (isUpkeepPaid) { ComputerUtil.playNoStack(controller, aiPaid, game); @@ -271,23 +272,26 @@ public class Upkeep extends Phase { sb.append("Cumulative upkeep for ").append(c).append("\n"); } - final String upkeepCost = cost; + final Cost upkeepCost = new Cost(cost, true); final Ability blankAbility = Upkeep.getBlankAbility(c, upkeepCost); blankAbility.setActivatingPlayer(controller); final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { @Override public void resolve() { + boolean isPaid = false; if (controller.isHuman()) { - if ( !HumanPlay.payCostDuringAbilityResolve(blankAbility, blankAbility.getPayCosts(), this)) - game.getAction().sacrifice(c, null); + isPaid = HumanPlay.payCostDuringAbilityResolve(controller, c, upkeepCost, this); + } else { // computer if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) { ComputerUtil.playNoStack(controller, blankAbility, game); // this makes AI pay - } else { - game.getAction().sacrifice(c, null); + isPaid = true; } } + + if(!isPaid) + game.getAction().sacrifice(c, null); } }; upkeepAbility.setActivatingPlayer(controller); @@ -315,7 +319,7 @@ public class Upkeep extends Phase { Singletons.getControl().getInputQueue().setInputAndWait(inp); isUpkeepPaid = inp.isPaid(); } else { // computers - final Ability aiPaid = Upkeep.getBlankAbility(c, upkeepCost.toString()); + final Ability aiPaid = Upkeep.getBlankAbility(c, new Cost(upkeepCost, true)); if (ComputerUtilCost.canPayCost(aiPaid, controller) && ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0) { ComputerUtil.playNoStack(controller, aiPaid, game); isUpkeepPaid = true; @@ -350,8 +354,8 @@ public class Upkeep extends Phase { * a {@link java.lang.String} object. * @return a {@link forge.card.spellability.Ability} object. */ - public static Ability getBlankAbility(final Card c, final String costString) { - return new AbilityStatic(c, new Cost(costString, true), null) { + public static Ability getBlankAbility(final Card c, final Cost cost) { + return new AbilityStatic(c, cost, null) { @Override public void resolve() {} }; diff --git a/src/main/java/forge/game/player/HumanPlay.java b/src/main/java/forge/game/player/HumanPlay.java index 5ae22898cbc..c603bd2e372 100644 --- a/src/main/java/forge/game/player/HumanPlay.java +++ b/src/main/java/forge/game/player/HumanPlay.java @@ -49,6 +49,7 @@ import forge.gui.input.InputPayManaExecuteCommands; import forge.gui.input.InputPayManaSimple; import forge.gui.input.InputSelectCards; import forge.gui.input.InputSelectCardsFromList; +import forge.util.Lang; /** * TODO: Write javadoc for this type. @@ -280,11 +281,9 @@ public class HumanPlay { * a {@link forge.Command} object. * @param sourceAbility TODO */ - public static boolean payCostDuringAbilityResolve(final SpellAbility ability, final Cost cost, SpellAbility sourceAbility) { + public static boolean payCostDuringAbilityResolve(final Player p, final Card source, final Cost cost, SpellAbility sourceAbility) { // Only human player pays this way - final Player p = ability.getActivatingPlayer(); - final Card source = ability.getSourceCard(); Card current = null; // Used in spells with RepeatEach effect to distinguish cards, Cut the Tethers if (!source.getRemembered().isEmpty()) { if (source.getRemembered().get(0) instanceof Card) { @@ -295,6 +294,8 @@ public class HumanPlay { current = source.getImprinted().get(0); } + String promptCurrent = current == null ? "" : "Current Card: " + current + "\r\n"; + final List parts = cost.getCostParts(); ArrayList remainingParts = new ArrayList(cost.getCostParts()); CostPart costPart = null; @@ -353,8 +354,7 @@ public class HumanPlay { return false; } - String plural = amount > 1 ? "s" : ""; - if (false == GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() + " counter" + plural + " on " + source + "?")) + if (false == GuiDialog.confirm(source, "Do you want to put " + Lang.nounWithAmount(amount, counterType.getName() + " counter") + " on " + source + "?")) return false; source.addCounter(counterType, amount, false); @@ -363,12 +363,11 @@ public class HumanPlay { else if (part instanceof CostRemoveCounter) { CounterType counterType = ((CostRemoveCounter) part).getCounter(); int amount = getAmountFromPartX(part, source, sourceAbility); - String plural = amount > 1 ? "s" : ""; if (!part.canPay(sourceAbility)) return false; - if ( false == GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName() + " counter" + plural + " from " + source + "?")) + if ( false == GuiDialog.confirm(source, "Do you want to remove " + Lang.nounWithAmount(amount, counterType.getName() + " counter") + " from " + source + "?")) return false; source.subtractCounter(counterType, amount); @@ -387,7 +386,7 @@ public class HumanPlay { CostExile costExile = (CostExile) part; ZoneType from = costExile.getFrom(); List list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source); - final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability); + final int nNeeded = getAmountFromPart(costPart, source, sourceAbility); if (list.size() < nNeeded) return false; @@ -453,9 +452,8 @@ public class HumanPlay { if (!(costPart instanceof CostPartMana )) throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana."); - InputPayMana toSet = current == null - ? new InputPayManaExecuteCommands(p, source + "\r\n", cost.getCostMana().getManaToPay()) - : new InputPayManaExecuteCommands(p, source + "\r\n" + "Current Card: " + current + "\r\n" , cost.getCostMana().getManaToPay()); + String prompt = source + "\r\n" + promptCurrent; + InputPayMana toSet = new InputPayManaExecuteCommands(p, prompt, cost.getCostMana().getManaToPay()); Singletons.getControl().getInputQueue().setInputAndWait(toSet); return toSet.isPaid(); }