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.
This commit is contained in:
Maxmtg
2013-06-01 12:55:08 +00:00
parent 877aec25b0
commit 38d1744f16
19 changed files with 135 additions and 85 deletions

View File

@@ -1102,22 +1102,20 @@ public class AbilityUtils {
} }
final Cost cost = new Cost(unlessCost, true); 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; boolean paid = false;
for (Player payer : payers) { for (Player payer : payers) {
final Ability ability = new AbilityStatic(source, cost, sa.getTarget()) { @Override public void resolve() { } };
ability.setActivatingPlayer(payer); ability.setActivatingPlayer(payer);
if (payer.isComputer()) { 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 ComputerUtil.playNoStack(payer, ability, game); // Unless cost was payed - no resolve
paid = true; paid = true;
} }
} else { } else {
// if it's paid by the AI already the human can pay, but it won't change anything // 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);
} }
} }

View File

@@ -242,7 +242,6 @@ public enum ApiType {
public SpellAbilityEffect getSpellEffect() { public SpellAbilityEffect getSpellEffect() {
return clsEffect == null ? null : ReflectionUtil.makeDefaultInstanceOf(clsEffect); return clsEffect == null ? null : ReflectionUtil.makeDefaultInstanceOf(clsEffect);
} }
public SpellAbilityAi getAi() { public SpellAbilityAi getAi() {

View File

@@ -7,6 +7,7 @@ import forge.game.ai.ComputerUtilCost;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
public abstract class SpellAbilityAi { public abstract class SpellAbilityAi {
@@ -103,4 +104,9 @@ public abstract class SpellAbilityAi {
final AbilitySub subAb = ab.getSubAbility(); final AbilitySub subAb = ab.getSubAbility();
return ab.getAi().chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb)); 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;
}
} }

View File

@@ -15,6 +15,7 @@ import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -112,4 +113,13 @@ public class CopyPermanentAi extends SpellAbilityAi {
return true; 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;
}
} }

View File

@@ -2,11 +2,13 @@ package forge.card.ability.ai;
import java.util.Random; import java.util.Random;
import forge.Card;
import forge.card.ability.SpellAbilityAi; import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -65,4 +67,13 @@ public class DigAi extends SpellAbilityAi {
return true; 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();
}
} }

View File

@@ -14,6 +14,7 @@ import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana; import forge.game.ai.ComputerUtilMana;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -165,4 +166,13 @@ public class DiscardAi extends SpellAbilityAi {
// TODO: check for some extra things // TODO: check for some extra things
return true; return true;
} // discardCheckDrawbackAI() } // 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);
}
} }

View File

@@ -37,6 +37,7 @@ import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana; import forge.game.ai.ComputerUtilMana;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -267,4 +268,14 @@ public class DrawAi extends SpellAbilityAi {
return targetAI(ai, sa, mandatory); 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();
}
} }

View File

@@ -20,6 +20,7 @@ package forge.card.ability.ai;
import forge.card.ability.SpellAbilityAi; import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
/** /**
* <p> * <p>
@@ -51,4 +52,10 @@ public final class EncodeAi extends SpellAbilityAi {
public boolean chkAIDrawback(SpellAbility sa, Player ai) { public boolean chkAIDrawback(SpellAbility sa, Player ai) {
return true; return true;
} }
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return true;
}
} }

View File

@@ -1,8 +1,10 @@
package forge.card.ability.ai; package forge.card.ability.ai;
import forge.card.ability.SpellAbilityAi; import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
@@ -20,4 +22,13 @@ public class PeekAndRevealAi extends SpellAbilityAi {
return true; 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);
}
} }

View File

@@ -25,6 +25,7 @@ import forge.game.ai.ComputerUtilMana;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
public class PumpAi extends PumpAiBase { public class PumpAi extends PumpAiBase {
@@ -446,4 +447,14 @@ public class PumpAi extends PumpAiBase {
return true; return true;
} // pumpDrawbackAI() } // 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;
}
} }

View File

@@ -5,6 +5,7 @@ import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
public class RepeatAi extends SpellAbilityAi { public class RepeatAi extends SpellAbilityAi {
@@ -21,4 +22,11 @@ public class RepeatAi extends SpellAbilityAi {
} }
return true; 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;
}
} }

View File

@@ -3,6 +3,7 @@ package forge.card.ability.ai;
import forge.card.ability.SpellAbilityAi; import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
public class ShuffleAi extends SpellAbilityAi { public class ShuffleAi extends SpellAbilityAi {
@Override @Override
@@ -47,4 +48,12 @@ public class ShuffleAi extends SpellAbilityAi {
return true; 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;
}
} }

View File

@@ -506,9 +506,7 @@ public class GameAction {
private static final long serialVersionUID = 8858061639236920054L; private static final long serialVersionUID = 8858061639236920054L;
@Override @Override
public void resolve() { public void resolve() {}
}
@Override @Override
public String getStackDescription() { public String getStackDescription() {
@@ -533,7 +531,7 @@ public class GameAction {
Player p = recoverable.getController(); Player p = recoverable.getController();
boolean hasPaid = false; boolean hasPaid = false;
if (p.isHuman()) { if (p.isHuman()) {
hasPaid = HumanPlay.payCostDuringAbilityResolve(abRecover, abRecover.getPayCosts(), null); hasPaid = HumanPlay.payCostDuringAbilityResolve(p, recoverable, cost, null);
} else { // computer } else { // computer
if (ComputerUtilCost.canPayCost(abRecover, p)) { if (ComputerUtilCost.canPayCost(abRecover, p)) {
ComputerUtil.playNoStack(p, abRecover, game); ComputerUtil.playNoStack(p, abRecover, game);

View File

@@ -35,12 +35,10 @@ import forge.CardPredicates;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.Constant; import forge.Constant;
import forge.GameEntity; import forge.GameEntity;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.CostDiscard; import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart; import forge.card.cost.CostPart;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent; import forge.card.spellability.SpellPermanent;
@@ -711,50 +709,12 @@ public class AiController {
if( mode != null ) switch (mode) { if( mode != null ) switch (mode) {
case BraidOfFire: return true; 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: String exMsg = String.format("AI confirmAction does not know what to decide about %s mode (api is null).", mode);
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);
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); throw new InvalidParameterException(exMsg);
} else
return api.getAi().confirmAction(player, sa, mode, message);
} }
public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) {

View File

@@ -312,8 +312,7 @@ public class ComputerUtilCost {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a boolean. * @return a boolean.
*/ */
public static boolean shouldPayCost(final Player ai, final Card hostCard, final String costString) { public static boolean shouldPayCost(final Player ai, final Card hostCard, final Cost cost) {
final Cost cost = new Cost(costString, false);
for (final CostPart part : cost.getCostParts()) { for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostPayLife) { if (part instanceof CostPayLife) {
@@ -368,7 +367,7 @@ public class ComputerUtilCost {
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa); && CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
} // canPayCost() } // canPayCost()
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, SpellAbility ability, boolean alreadyPaid, List<Player> payers) { public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, List<Player> payers) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
boolean payForOwnOnly = "OnlyOwn".equals(sa.getParam("UnlessAI")); boolean payForOwnOnly = "OnlyOwn".equals(sa.getParam("UnlessAI"));
boolean payOwner = sa.hasParam("UnlessAI") ? sa.getParam("UnlessAI").startsWith("Defined") : false; 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 // 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 // Didn't have any of the data on the original SA to pay dependant costs
return checkLifeCost(payer, ability.getPayCosts(), source, 4, sa) return checkLifeCost(payer, cost, source, 4, sa)
&& checkDamageCost(payer, ability.getPayCosts(), source, 4) && checkDamageCost(payer, cost, source, 4)
&& (isMine || checkDiscardCost(payer, ability.getPayCosts(), source)) && (isMine || checkDiscardCost(payer, cost, source))
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2) && (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
&& (!source.getName().equals("Perplex") || 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) && (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)

View File

@@ -1005,7 +1005,7 @@ public class CombatUtil {
ability.setActivatingPlayer(c.getController()); ability.setActivatingPlayer(c.getController());
if (c.getController().isHuman()) { if (c.getController().isHuman()) {
isPaid = HumanPlay.payCostDuringAbilityResolve(ability, attackCost, null); isPaid = HumanPlay.payCostDuringAbilityResolve(c.getController(), c, attackCost, null);
} else { // computer } else { // computer
if (ComputerUtilCost.canPayCost(ability, c.getController())) { if (ComputerUtilCost.canPayCost(ability, c.getController())) {
ComputerUtil.playNoStack(c.getController(), ability, game); ComputerUtil.playNoStack(c.getController(), ability, game);

View File

@@ -181,7 +181,7 @@ public class PhaseUtil {
ability.setActivatingPlayer(blocker.getController()); ability.setActivatingPlayer(blocker.getController());
if (blocker.getController().isHuman()) { if (blocker.getController().isHuman()) {
hasPaid = HumanPlay.payCostDuringAbilityResolve(ability, blockCost, null); hasPaid = HumanPlay.payCostDuringAbilityResolve(blocker.getController(), blocker, blockCost, null);
} else { // computer } else { // computer
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) { if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
ComputerUtil.playNoStack(blocker.getController(), ability, game); ComputerUtil.playNoStack(blocker.getController(), ability, game);

View File

@@ -165,7 +165,8 @@ public class Upkeep extends Phase {
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
final Card c = list.get(i); final Card c = list.get(i);
if (c.hasStartOfKeyword("(Echo unpaid)")) { 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()); blankAbility.setActivatingPlayer(c.getController());
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@@ -178,7 +179,7 @@ public class Upkeep extends Phase {
Player controller = c.getController(); Player controller = c.getController();
if (controller.isHuman()) { if (controller.isHuman()) {
Cost cost = new Cost(c.getEchoCost().trim(), true); Cost cost = new Cost(c.getEchoCost().trim(), true);
hasPaid = HumanPlay.payCostDuringAbilityResolve(blankAbility, cost, null); hasPaid = HumanPlay.payCostDuringAbilityResolve(controller, c, cost, null);
} else { // computer } else { // computer
if (ComputerUtilCost.canPayCost(blankAbility, controller)) { if (ComputerUtilCost.canPayCost(blankAbility, controller)) {
ComputerUtil.playNoStack(controller, blankAbility, game); ComputerUtil.playNoStack(controller, blankAbility, game);
@@ -230,7 +231,7 @@ public class Upkeep extends Phase {
Singletons.getControl().getInputQueue().setInputAndWait(inp); Singletons.getControl().getInputQueue().setInputAndWait(inp);
isUpkeepPaid = inp.isPaid(); isUpkeepPaid = inp.isPaid();
} else { // computer } 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"); isUpkeepPaid = ComputerUtilCost.canPayCost(aiPaid, controller) && !c.hasKeyword("Indestructible");
if (isUpkeepPaid) { if (isUpkeepPaid) {
ComputerUtil.playNoStack(controller, aiPaid, game); ComputerUtil.playNoStack(controller, aiPaid, game);
@@ -271,24 +272,27 @@ public class Upkeep extends Phase {
sb.append("Cumulative upkeep for ").append(c).append("\n"); 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); final Ability blankAbility = Upkeep.getBlankAbility(c, upkeepCost);
blankAbility.setActivatingPlayer(controller); blankAbility.setActivatingPlayer(controller);
final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
boolean isPaid = false;
if (controller.isHuman()) { if (controller.isHuman()) {
if ( !HumanPlay.payCostDuringAbilityResolve(blankAbility, blankAbility.getPayCosts(), this)) isPaid = HumanPlay.payCostDuringAbilityResolve(controller, c, upkeepCost, this);
game.getAction().sacrifice(c, null);
} else { // computer } else { // computer
if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) { if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) {
ComputerUtil.playNoStack(controller, blankAbility, game); // this makes AI pay ComputerUtil.playNoStack(controller, blankAbility, game); // this makes AI pay
} else { isPaid = true;
}
}
if(!isPaid)
game.getAction().sacrifice(c, null); game.getAction().sacrifice(c, null);
} }
}
}
}; };
upkeepAbility.setActivatingPlayer(controller); upkeepAbility.setActivatingPlayer(controller);
upkeepAbility.setStackDescription(sb.toString()); upkeepAbility.setStackDescription(sb.toString());
@@ -315,7 +319,7 @@ public class Upkeep extends Phase {
Singletons.getControl().getInputQueue().setInputAndWait(inp); Singletons.getControl().getInputQueue().setInputAndWait(inp);
isUpkeepPaid = inp.isPaid(); isUpkeepPaid = inp.isPaid();
} else { // computers } 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) { if (ComputerUtilCost.canPayCost(aiPaid, controller) && ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0) {
ComputerUtil.playNoStack(controller, aiPaid, game); ComputerUtil.playNoStack(controller, aiPaid, game);
isUpkeepPaid = true; isUpkeepPaid = true;
@@ -350,8 +354,8 @@ public class Upkeep extends Phase {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.Ability} object. * @return a {@link forge.card.spellability.Ability} object.
*/ */
public static Ability getBlankAbility(final Card c, final String costString) { public static Ability getBlankAbility(final Card c, final Cost cost) {
return new AbilityStatic(c, new Cost(costString, true), null) { return new AbilityStatic(c, cost, null) {
@Override @Override
public void resolve() {} public void resolve() {}
}; };

View File

@@ -49,6 +49,7 @@ import forge.gui.input.InputPayManaExecuteCommands;
import forge.gui.input.InputPayManaSimple; import forge.gui.input.InputPayManaSimple;
import forge.gui.input.InputSelectCards; import forge.gui.input.InputSelectCards;
import forge.gui.input.InputSelectCardsFromList; import forge.gui.input.InputSelectCardsFromList;
import forge.util.Lang;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
@@ -280,11 +281,9 @@ public class HumanPlay {
* a {@link forge.Command} object. * a {@link forge.Command} object.
* @param sourceAbility TODO * @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 // 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 Card current = null; // Used in spells with RepeatEach effect to distinguish cards, Cut the Tethers
if (!source.getRemembered().isEmpty()) { if (!source.getRemembered().isEmpty()) {
if (source.getRemembered().get(0) instanceof Card) { if (source.getRemembered().get(0) instanceof Card) {
@@ -295,6 +294,8 @@ public class HumanPlay {
current = source.getImprinted().get(0); current = source.getImprinted().get(0);
} }
String promptCurrent = current == null ? "" : "Current Card: " + current + "\r\n";
final List<CostPart> parts = cost.getCostParts(); final List<CostPart> parts = cost.getCostParts();
ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts()); ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts());
CostPart costPart = null; CostPart costPart = null;
@@ -353,8 +354,7 @@ public class HumanPlay {
return false; return false;
} }
String plural = amount > 1 ? "s" : ""; if (false == GuiDialog.confirm(source, "Do you want to put " + Lang.nounWithAmount(amount, counterType.getName() + " counter") + " on " + source + "?"))
if (false == GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() + " counter" + plural + " on " + source + "?"))
return false; return false;
source.addCounter(counterType, amount, false); source.addCounter(counterType, amount, false);
@@ -363,12 +363,11 @@ public class HumanPlay {
else if (part instanceof CostRemoveCounter) { else if (part instanceof CostRemoveCounter) {
CounterType counterType = ((CostRemoveCounter) part).getCounter(); CounterType counterType = ((CostRemoveCounter) part).getCounter();
int amount = getAmountFromPartX(part, source, sourceAbility); int amount = getAmountFromPartX(part, source, sourceAbility);
String plural = amount > 1 ? "s" : "";
if (!part.canPay(sourceAbility)) if (!part.canPay(sourceAbility))
return false; 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; return false;
source.subtractCounter(counterType, amount); source.subtractCounter(counterType, amount);
@@ -387,7 +386,7 @@ public class HumanPlay {
CostExile costExile = (CostExile) part; CostExile costExile = (CostExile) part;
ZoneType from = costExile.getFrom(); ZoneType from = costExile.getFrom();
List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source); List<Card> 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) if (list.size() < nNeeded)
return false; return false;
@@ -453,9 +452,8 @@ public class HumanPlay {
if (!(costPart instanceof CostPartMana )) if (!(costPart instanceof CostPartMana ))
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana."); throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana.");
InputPayMana toSet = current == null String prompt = source + "\r\n" + promptCurrent;
? new InputPayManaExecuteCommands(p, source + "\r\n", cost.getCostMana().getManaToPay()) InputPayMana toSet = new InputPayManaExecuteCommands(p, prompt, cost.getCostMana().getManaToPay());
: new InputPayManaExecuteCommands(p, source + "\r\n" + "Current Card: " + current + "\r\n" , cost.getCostMana().getManaToPay());
Singletons.getControl().getInputQueue().setInputAndWait(toSet); Singletons.getControl().getInputQueue().setInputAndWait(toSet);
return toSet.isPaid(); return toSet.isPaid();
} }