mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Cost: rework param that says if it's by effect or by Spell/ActivatedAbility cost
This commit is contained in:
committed by
Michael Kamensky
parent
c18f7221c6
commit
aa6f2e3b6c
@@ -131,7 +131,7 @@ public class AiAttackController {
|
||||
}
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
if (ComputerUtilCost.canPayCost(sa, defender)
|
||||
if (ComputerUtilCost.canPayCost(sa, defender, false)
|
||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||
Card animatedCopy = AnimateAi.becomeAnimated(c, sa);
|
||||
defenders.add(animatedCopy);
|
||||
@@ -1159,8 +1159,8 @@ public class AiAttackController {
|
||||
// Check if the card actually has an ability the AI can and wants to play, if not, attacking is fine!
|
||||
for (SpellAbility sa : attacker.getSpellAbilities()) {
|
||||
// Do not attack if we can afford using the ability.
|
||||
if (sa.isAbility()) {
|
||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (sa.isActivatedAbility()) {
|
||||
if (ComputerUtilCost.canPayCost(sa, ai, false)) {
|
||||
return false;
|
||||
}
|
||||
// TODO Eventually The Ai will need to learn to predict if they have any use for the ability before next untap or not.
|
||||
|
||||
@@ -276,7 +276,7 @@ public class AiController {
|
||||
}
|
||||
rightapi = true;
|
||||
if (!(exSA instanceof AbilitySub)) {
|
||||
if (!ComputerUtilCost.canPayCost(exSA, player)) {
|
||||
if (!ComputerUtilCost.canPayCost(exSA, player, true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -648,7 +648,7 @@ public class AiController {
|
||||
// Ideally this should cast canPlaySa to determine that the AI is truly able/willing to cast a spell,
|
||||
// but that is currently difficult to implement due to various side effects leading to stack overflow.
|
||||
Card host = sa.getHostCard();
|
||||
if (!ComputerUtil.castPermanentInMain1(player, sa) && host != null && !host.isLand() && ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (!ComputerUtil.castPermanentInMain1(player, sa) && host != null && !host.isLand() && ComputerUtilCost.canPayCost(sa, player, false)) {
|
||||
if (sa instanceof SpellPermanent) {
|
||||
return sa;
|
||||
}
|
||||
@@ -744,7 +744,7 @@ public class AiController {
|
||||
int oldCMC = -1;
|
||||
boolean xCost = sa.costHasX() || sa.getHostCard().hasStartOfKeyword("Strive");
|
||||
if (!xCost) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||
// for most costs, it's OK to check if they can be paid early in order to avoid running a heavy API check
|
||||
// when the AI won't even be able to play the spell in the first place (even if it could afford it)
|
||||
return AiPlayDecision.CantAfford;
|
||||
@@ -782,7 +782,7 @@ public class AiController {
|
||||
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
||||
if (wardCost.hasManaCost()) {
|
||||
amount = wardCost.getTotalMana().getCMC();
|
||||
if (amount > 0 && !ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (amount > 0 && !ComputerUtilCost.canPayCost(sa, player, true)) {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
}
|
||||
@@ -808,7 +808,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
if (xCost && !ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (xCost && !ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||
// for dependent costs with X, e.g. Repeal, which require a valid target to be specified before a decision can be made
|
||||
// on whether the cost can be paid, this can only be checked late after canPlaySa has been run (or the AI will misplay)
|
||||
return AiPlayDecision.CantAfford;
|
||||
@@ -871,7 +871,7 @@ public class AiController {
|
||||
if (mana != null) {
|
||||
if (mana.countX() > 0) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, player);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, player, sa.isTrigger());
|
||||
if (xPay <= 0) {
|
||||
return AiPlayDecision.CantAffordX;
|
||||
}
|
||||
@@ -2310,11 +2310,11 @@ public class AiController {
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardCollectionView chooseSacrificeType(String type, SpellAbility ability, int amount, final CardCollectionView exclude) {
|
||||
public CardCollectionView chooseSacrificeType(String type, SpellAbility ability, boolean effect, int amount, final CardCollectionView exclude) {
|
||||
if (simPicker != null) {
|
||||
return simPicker.chooseSacrificeType(type, ability, amount, exclude);
|
||||
return simPicker.chooseSacrificeType(type, ability, effect, amount, exclude);
|
||||
}
|
||||
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), amount, exclude);
|
||||
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), effect, amount, exclude);
|
||||
}
|
||||
|
||||
private boolean checkAiSpecificRestrictions(final SpellAbility sa) {
|
||||
|
||||
@@ -16,7 +16,6 @@ import com.google.common.collect.Lists;
|
||||
import forge.card.CardType;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
@@ -40,8 +39,8 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
private final CardCollection discarded;
|
||||
private final CardCollection tapped;
|
||||
|
||||
public AiCostDecision(Player ai0, SpellAbility sa) {
|
||||
super(ai0);
|
||||
public AiCostDecision(Player ai0, SpellAbility sa, final boolean effect) {
|
||||
super(ai0, effect);
|
||||
ability = sa;
|
||||
source = ability.getHostCard();
|
||||
|
||||
@@ -51,11 +50,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostAddMana cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
@@ -93,10 +88,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
if (type.contains("WithSameName")) {
|
||||
return null;
|
||||
}
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
if (type.equals("Random")) {
|
||||
CardCollectionView randomSubset = CardLists.getRandomSubList(new CardCollection(hand), c);
|
||||
@@ -134,25 +126,17 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostDamage cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostDraw cost) {
|
||||
if (!cost.canPay(ability, player)) {
|
||||
if (!cost.canPay(ability, player, isEffect())) {
|
||||
return null;
|
||||
}
|
||||
Integer c = cost.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
List<Player> res = cost.getPotentialPlayers(player, ability);
|
||||
|
||||
@@ -174,10 +158,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
if (cost.getFrom().equals(ZoneType.Library)) {
|
||||
return PaymentDecision.card(player.getCardsIn(ZoneType.Library, c));
|
||||
@@ -193,10 +174,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostExileFromStack cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
List<SpellAbility> chosen = Lists.newArrayList();
|
||||
for (SpellAbilityStackInstance si :source.getGame().getStack()) {
|
||||
SpellAbility sp = si.getSpellAbility(true).getRootAbility();
|
||||
@@ -209,12 +186,9 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostExiledMoveToGrave cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
CardCollection chosen = new CardCollection();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
CardCollection typeList = CardLists.getValidCards(player.getGame().getCardsIn(ZoneType.Exile), cost.getType().split(";"), player, source, ability);
|
||||
|
||||
@@ -238,10 +212,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(source);
|
||||
}
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
final CardCollection typeList = CardLists.getValidCards(player.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source, ability);
|
||||
|
||||
@@ -260,19 +231,13 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostFlipCoin cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostRollDice cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
|
||||
@@ -282,10 +247,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(source);
|
||||
}
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
final CardCollection typeList = CardLists.getValidCards(player.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source, ability);
|
||||
|
||||
@@ -307,7 +269,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
public PaymentDecision visit(CostGainLife cost) {
|
||||
final List<Player> oppsThatCanGainLife = Lists.newArrayList();
|
||||
|
||||
for (final Player opp : cost.getPotentialTargets(player, source)) {
|
||||
for (final Player opp : cost.getPotentialTargets(player, ability)) {
|
||||
if (opp.canGainLife()) {
|
||||
oppsThatCanGainLife.add(opp);
|
||||
}
|
||||
@@ -323,10 +285,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostMill cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
CardCollectionView topLib = player.getCardsIn(ZoneType.Library, c);
|
||||
return topLib.size() < c ? null : PaymentDecision.number(c);
|
||||
@@ -339,12 +298,8 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostPayLife cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
// Generalize cost
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
if (!player.canPayLife(c)) {
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
if (!player.canPayLife(c, isEffect())) {
|
||||
return null;
|
||||
}
|
||||
// activator.payLife(c, null);
|
||||
@@ -353,10 +308,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostPayEnergy cost) {
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
if (!player.canPayEnergy(c)) {
|
||||
return null;
|
||||
}
|
||||
@@ -368,7 +320,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
if (cost.payCostFromSource()) {
|
||||
return PaymentDecision.card(source);
|
||||
}
|
||||
Integer c = cost.convertAmount();
|
||||
final Game game = player.getGame();
|
||||
CardCollection chosen = new CardCollection();
|
||||
CardCollectionView list;
|
||||
@@ -379,9 +330,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
list = new CardCollection(player.getCardsIn(cost.getFrom()));
|
||||
}
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
list = CardLists.getValidCards(list, cost.getType().split(";"), player, source, ability);
|
||||
|
||||
@@ -430,17 +379,12 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostTapType cost) {
|
||||
final String amount = cost.getAmount();
|
||||
Integer c = cost.convertAmount();
|
||||
String type = cost.getType();
|
||||
boolean isVehicle = type.contains("+withTotalPowerGE");
|
||||
|
||||
CardCollection exclude = new CardCollection();
|
||||
exclude.addAll(tapped);
|
||||
|
||||
if (c == null && !isVehicle) {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
if (type.contains("sharesCreatureTypeWith")) {
|
||||
return null;
|
||||
}
|
||||
@@ -470,6 +414,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
type = TextUtil.fastReplace(type, "+withTotalPowerGE", "");
|
||||
totap = ComputerUtil.chooseTapTypeAccumulatePower(player, type, ability, !cost.canTapSource, Integer.parseInt(totalP), exclude);
|
||||
} else {
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
totap = ComputerUtil.chooseTapType(player, type, source, !cost.canTapSource, c, exclude, ability);
|
||||
}
|
||||
|
||||
@@ -494,14 +439,10 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String amount = cost.getAmount();
|
||||
Integer c = cost.convertAmount();
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
|
||||
CardCollectionView list = aic.chooseSacrificeType(cost.getType(), ability, c, null);
|
||||
CardCollectionView list = aic.chooseSacrificeType(cost.getType(), ability, isEffect(), c, null);
|
||||
return PaymentDecision.card(list);
|
||||
}
|
||||
|
||||
@@ -510,10 +451,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
if (cost.payCostFromSource())
|
||||
return PaymentDecision.card(source);
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
CardCollectionView res = ComputerUtil.chooseReturnType(player, cost.getType(), source, ability.getTargetCard(), c, ability);
|
||||
return res.isEmpty() ? null : PaymentDecision.card(res);
|
||||
@@ -544,10 +482,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(getBestCreatureAI(hand));
|
||||
}
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
|
||||
return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
|
||||
@@ -580,8 +515,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostRemoveAnyCounter cost) {
|
||||
final String amount = cost.getAmount();
|
||||
final int c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
final int c = cost.getAbilityAmount(ability);
|
||||
final Card originalHost = ObjectUtils.defaultIfNull(ability.getOriginalHost(), source);
|
||||
|
||||
if (c <= 0) {
|
||||
@@ -785,25 +719,24 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public PaymentDecision visit(CostRemoveCounter cost) {
|
||||
final String amount = cost.getAmount();
|
||||
Integer c = cost.convertAmount();
|
||||
final String type = cost.getType();
|
||||
|
||||
if (c == null) {
|
||||
final String sVar = ability.getSVar(amount);
|
||||
if (amount.equals("All")) {
|
||||
c = source.getCounters(cost.counter);
|
||||
} else if (sVar.equals("Targeted$CardManaCost")) {
|
||||
c = 0;
|
||||
if (ability.getTargets().size() > 0) {
|
||||
for (Card tgt : ability.getTargets().getTargetCards()) {
|
||||
if (tgt.getManaCost() != null) {
|
||||
c += tgt.getManaCost().getCMC();
|
||||
}
|
||||
int c;
|
||||
|
||||
final String sVar = ability.getSVar(amount);
|
||||
if (amount.equals("All")) {
|
||||
c = source.getCounters(cost.counter);
|
||||
} else if (sVar.equals("Targeted$CardManaCost")) {
|
||||
c = 0;
|
||||
if (ability.getTargets().size() > 0) {
|
||||
for (Card tgt : ability.getTargets().getTargetCards()) {
|
||||
if (tgt.getManaCost() != null) {
|
||||
c += tgt.getManaCost().getCMC();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
} else {
|
||||
c = cost.getAbilityAmount(ability);
|
||||
}
|
||||
|
||||
if (!cost.payCostFromSource()) {
|
||||
@@ -831,11 +764,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(CostUntapType cost) {
|
||||
final String amount = cost.getAmount();
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
|
||||
CardCollectionView list = ComputerUtil.chooseUntapType(player, cost.getType(), source, cost.canUntapSource, c, ability);
|
||||
|
||||
|
||||
@@ -149,13 +149,13 @@ public class ComputerUtil {
|
||||
|
||||
// TODO: update mana color conversion for Daxos of Meletis
|
||||
if (cost == null) {
|
||||
if (ComputerUtilMana.payManaCost(ai, sa)) {
|
||||
if (ComputerUtilMana.payManaCost(ai, sa, false)) {
|
||||
game.getStack().addAndUnfreeze(sa);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
final CostPayment pay = new CostPayment(cost, sa);
|
||||
if (pay.payComputerCosts(new AiCostDecision(ai, sa))) {
|
||||
if (pay.payComputerCosts(new AiCostDecision(ai, sa, false))) {
|
||||
game.getStack().addAndUnfreeze(sa);
|
||||
if (sa.getSplicedCards() != null && !sa.getSplicedCards().isEmpty()) {
|
||||
game.getAction().reveal(sa.getSplicedCards(), ai, true, "Computer reveals spliced cards from ");
|
||||
@@ -202,7 +202,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// Abilities before Spells (card advantage)
|
||||
if (sa.isAbility()) {
|
||||
if (sa.isActivatedAbility()) {
|
||||
restrict += 40;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ public class ComputerUtil {
|
||||
// this is used for AI's counterspells
|
||||
public static final boolean playStack(SpellAbility sa, final Player ai, final Game game) {
|
||||
sa.setActivatingPlayer(ai);
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai))
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai, false))
|
||||
return false;
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
@@ -257,11 +257,11 @@ public class ComputerUtil {
|
||||
|
||||
final Cost cost = sa.getPayCosts();
|
||||
if (cost == null) {
|
||||
ComputerUtilMana.payManaCost(ai, sa);
|
||||
ComputerUtilMana.payManaCost(ai, sa, false);
|
||||
game.getStack().add(sa);
|
||||
} else {
|
||||
final CostPayment pay = new CostPayment(cost, sa);
|
||||
if (pay.payComputerCosts(new AiCostDecision(ai, sa))) {
|
||||
if (pay.payComputerCosts(new AiCostDecision(ai, sa, false))) {
|
||||
game.getStack().add(sa);
|
||||
}
|
||||
}
|
||||
@@ -284,7 +284,7 @@ public class ComputerUtil {
|
||||
SpellAbility newSA = sa.copyWithNoManaCost();
|
||||
newSA.setActivatingPlayer(ai);
|
||||
|
||||
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA) || !ComputerUtilMana.canPayManaCost(newSA, ai, 0)) {
|
||||
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA) || !ComputerUtilMana.canPayManaCost(newSA, ai, 0, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -302,16 +302,16 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
final CostPayment pay = new CostPayment(newSA.getPayCosts(), newSA);
|
||||
pay.payComputerCosts(new AiCostDecision(ai, newSA));
|
||||
pay.payComputerCosts(new AiCostDecision(ai, newSA, false));
|
||||
|
||||
game.getStack().add(newSA);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final void playNoStack(final Player ai, SpellAbility sa, final Game game) {
|
||||
public static final void playNoStack(final Player ai, SpellAbility sa, final Game game, final boolean effect) {
|
||||
sa.setActivatingPlayer(ai);
|
||||
// TODO: We should really restrict what doesn't use the Stack
|
||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (ComputerUtilCost.canPayCost(sa, ai, effect)) {
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
@@ -321,10 +321,10 @@ public class ComputerUtil {
|
||||
|
||||
final Cost cost = sa.getPayCosts();
|
||||
if (cost == null) {
|
||||
ComputerUtilMana.payManaCost(ai, sa);
|
||||
ComputerUtilMana.payManaCost(ai, sa, effect);
|
||||
} else {
|
||||
final CostPayment pay = new CostPayment(cost, sa);
|
||||
pay.payComputerCosts(new AiCostDecision(ai, sa));
|
||||
pay.payComputerCosts(new AiCostDecision(ai, sa, effect));
|
||||
}
|
||||
|
||||
AbilityUtils.resolve(sa);
|
||||
@@ -564,7 +564,7 @@ public class ComputerUtil {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static CardCollection chooseSacrificeType(final Player ai, final String type, final SpellAbility ability, final Card target, final int amount, final CardCollectionView exclude) {
|
||||
public static CardCollection chooseSacrificeType(final Player ai, final String type, final SpellAbility ability, final Card target, final boolean effect, final int amount, final CardCollectionView exclude) {
|
||||
final Card source = ability.getHostCard();
|
||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, ability);
|
||||
|
||||
@@ -572,7 +572,7 @@ public class ComputerUtil {
|
||||
typeList.removeAll(exclude);
|
||||
}
|
||||
|
||||
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
|
||||
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability, effect));
|
||||
|
||||
// don't sacrifice the card we're pumping
|
||||
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, ability, ai);
|
||||
@@ -927,11 +927,11 @@ public class ComputerUtil {
|
||||
// This try/catch should fix the "computer is thinking" bug
|
||||
try {
|
||||
|
||||
if (!sa.isAbility() || sa.getApi() != ApiType.Regenerate) {
|
||||
if (!sa.isActivatedAbility() || sa.getApi() != ApiType.Regenerate) {
|
||||
continue; // Not a Regenerate ability
|
||||
}
|
||||
sa.setActivatingPlayer(controller);
|
||||
if (!(sa.canPlay() && ComputerUtilCost.canPayCost(sa, controller))) {
|
||||
if (!(sa.canPlay() && ComputerUtilCost.canPayCost(sa, controller, false))) {
|
||||
continue; // Can't play ability
|
||||
}
|
||||
|
||||
@@ -983,12 +983,12 @@ public class ComputerUtil {
|
||||
// if SA is from AF_Counter don't add to getPlayable
|
||||
// This try/catch should fix the "computer is thinking" bug
|
||||
try {
|
||||
if (sa.getApi() == null || !sa.isAbility()) {
|
||||
if (sa.getApi() == null || !sa.isActivatedAbility()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getApi() == ApiType.PreventDamage && sa.canPlay()
|
||||
&& ComputerUtilCost.canPayCost(sa, controller)) {
|
||||
&& ComputerUtilCost.canPayCost(sa, controller, false)) {
|
||||
if (AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).contains(card)) {
|
||||
prevented += AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa);
|
||||
}
|
||||
@@ -1472,7 +1472,7 @@ public class ComputerUtil {
|
||||
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY);
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai, false)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
@@ -1504,7 +1504,7 @@ public class ComputerUtil {
|
||||
if (!sa.canTarget(enemy)) {
|
||||
continue;
|
||||
}
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai, false)) {
|
||||
continue;
|
||||
}
|
||||
if (!GameActionUtil.getOptionalCostValues(sa).isEmpty()) {
|
||||
@@ -2918,7 +2918,7 @@ public class ComputerUtil {
|
||||
// only API-based SAs are supported, other things may lead to a NPE (e.g. Ancestral Vision Suspend SA)
|
||||
continue;
|
||||
} else if (ab.getApi() == ApiType.Mana && "ManaRitual".equals(ab.getParam("AILogic"))) {
|
||||
// Mana Ritual cards are too complex for the AI to consider casting through a spell effect and will
|
||||
// TODO Mana Ritual cards are too complex for the AI to consider casting through a spell effect and will
|
||||
// lead to a stack overflow. Consider improving.
|
||||
continue;
|
||||
}
|
||||
@@ -2926,7 +2926,7 @@ public class ComputerUtil {
|
||||
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
||||
abTest.setActivatingPlayer(ai);
|
||||
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(abTest) && ComputerUtilCost.canPayCost(abTest, ai)) {
|
||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(abTest) && ComputerUtilCost.canPayCost(abTest, ai, false)) {
|
||||
targets.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,32 +618,32 @@ public class ComputerUtilCard {
|
||||
}
|
||||
|
||||
public static boolean canBeKilledByRoyalAssassin(final Player ai, final Card card) {
|
||||
boolean wasTapped = card.isTapped();
|
||||
for (Player opp : ai.getOpponents()) {
|
||||
for (Card c : opp.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
boolean wasTapped = card.isTapped();
|
||||
for (Player opp : ai.getOpponents()) {
|
||||
for (Card c : opp.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() != ApiType.Destroy) {
|
||||
continue;
|
||||
}
|
||||
if (!ComputerUtilCost.canPayCost(sa, opp)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, opp, sa.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
sa.setActivatingPlayer(opp);
|
||||
if (sa.canTarget(card)) {
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
// check whether the ability can only target tapped creatures
|
||||
card.setTapped(true);
|
||||
card.setTapped(true);
|
||||
if (!sa.canTarget(card)) {
|
||||
card.setTapped(wasTapped);
|
||||
continue;
|
||||
card.setTapped(wasTapped);
|
||||
continue;
|
||||
}
|
||||
card.setTapped(wasTapped);
|
||||
card.setTapped(wasTapped);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1398,7 +1398,7 @@ public class ComputerUtilCard {
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
Cost abCost = ab.getPayCosts();
|
||||
if (abCost != null && abCost.hasTapCost()
|
||||
&& (!abCost.hasManaCost() || ComputerUtilMana.canPayManaCost(ab, ai, 0))) {
|
||||
&& (!abCost.hasManaCost() || ComputerUtilMana.canPayManaCost(ab, ai, 0, false))) {
|
||||
nonCombatChance += 0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1020,7 +1020,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
@@ -1039,7 +1039,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
@@ -1155,7 +1155,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
|
||||
if (tBonus > 0) {
|
||||
toughness += tBonus;
|
||||
@@ -1174,7 +1174,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
|
||||
if (tBonus > 0) {
|
||||
toughness += tBonus;
|
||||
@@ -1352,7 +1352,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
@@ -1371,7 +1371,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
@@ -1570,7 +1570,7 @@ public class ComputerUtilCombat {
|
||||
if (ability.getPayCosts().hasTapCost() && !attacker.hasKeyword(Keyword.VIGILANCE)) {
|
||||
continue;
|
||||
}
|
||||
if (!ComputerUtilCost.canPayCost(ability, attacker.getController())) {
|
||||
if (!ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2312,7 +2312,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.hasParam("KW") || !ComputerUtilCost.canPayCost(ability, controller)) {
|
||||
if (!ability.hasParam("KW") || !ComputerUtilCost.canPayCost(ability, controller, false)) {
|
||||
continue;
|
||||
}
|
||||
if (c != combatant) {
|
||||
@@ -2346,7 +2346,7 @@ public class ComputerUtilCombat {
|
||||
private final static Card canTransform(Card original) {
|
||||
if (original.isDoubleFaced() && !original.isInAlternateState()) {
|
||||
for (SpellAbility sa : original.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController())) {
|
||||
if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController(), false)) {
|
||||
Card transformed = CardUtil.getLKICopy(original);
|
||||
transformed.getCurrentState().copyFrom(original.getAlternateState(), true);
|
||||
transformed.updateStateForView();
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ComputerUtilCost {
|
||||
if (cost == null) {
|
||||
return true;
|
||||
}
|
||||
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa);
|
||||
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa, false);
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostRemoveCounter) {
|
||||
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
|
||||
@@ -233,7 +233,7 @@ public class ComputerUtilCost {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility, final boolean effect) {
|
||||
// TODO cheating via autopay can still happen, need to get the real ai player from controlledBy
|
||||
if (cost == null || !ai.isAI()) {
|
||||
return true;
|
||||
@@ -260,7 +260,7 @@ public class ComputerUtilCost {
|
||||
c = AbilityUtils.calculateAmount(source, amount, sourceAbility);
|
||||
}
|
||||
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
CardCollectionView choices = aic.chooseSacrificeType(part.getType(), sourceAbility, c, exclude);
|
||||
CardCollectionView choices = aic.chooseSacrificeType(part.getType(), sourceAbility, effect, c, exclude);
|
||||
if (choices != null) {
|
||||
list.addAll(choices);
|
||||
}
|
||||
@@ -523,7 +523,7 @@ public class ComputerUtilCost {
|
||||
* a {@link forge.game.player.Player} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canPayCost(final SpellAbility sa, final Player player) {
|
||||
public static boolean canPayCost(final SpellAbility sa, final Player player, final boolean effect) {
|
||||
if (sa.getActivatingPlayer() == null) {
|
||||
sa.setActivatingPlayer(player); // complaints on NPE had came before this line was added.
|
||||
}
|
||||
@@ -585,7 +585,7 @@ public class ComputerUtilCost {
|
||||
if (sa.hasParam("Crew")) { // put under checkTapTypeCost?
|
||||
for (final CostPart part : sa.getPayCosts().getCostParts()) {
|
||||
if (part instanceof CostTapType && part.getType().contains("+withTotalPowerGE")) {
|
||||
return new AiCostDecision(player, sa).visit((CostTapType)part) != null;
|
||||
return new AiCostDecision(player, sa, false).visit((CostTapType)part) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,7 +632,7 @@ public class ComputerUtilCost {
|
||||
}
|
||||
}
|
||||
|
||||
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded)
|
||||
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded, effect)
|
||||
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
} // canPayCost()
|
||||
|
||||
@@ -699,7 +699,7 @@ public class ComputerUtilCost {
|
||||
if (part instanceof CostPayLife) {
|
||||
final CostPayLife lifeCost = (CostPayLife) part;
|
||||
Integer amount = lifeCost.convertAmount();
|
||||
if (payer.getLife() > (amount + 1) && payer.canPayLife(amount)) {
|
||||
if (payer.getLife() > (amount + 1) && payer.canPayLife(amount, true)) {
|
||||
final int landsize = payer.getLandsInPlay().size() + 1;
|
||||
for (Card c : payer.getCardsIn(ZoneType.Hand)) {
|
||||
// Check if the AI has enough lands to play the card
|
||||
@@ -767,7 +767,7 @@ public class ComputerUtilCost {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getMaxXValue(SpellAbility sa, Player ai) {
|
||||
public static int getMaxXValue(SpellAbility sa, Player ai, final boolean effect) {
|
||||
final Card source = sa.getHostCard();
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
final Cost abCost = root.getPayCosts();
|
||||
@@ -779,7 +779,7 @@ public class ComputerUtilCost {
|
||||
Integer val = null;
|
||||
|
||||
if (root.costHasManaX()) {
|
||||
val = ComputerUtilMana.determineLeftoverMana(root, ai);
|
||||
val = ComputerUtilMana.determineLeftoverMana(root, ai, effect);
|
||||
}
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
@@ -795,7 +795,7 @@ public class ComputerUtilCost {
|
||||
}
|
||||
}
|
||||
|
||||
val = ObjectUtils.min(val, abCost.getMaxForNonManaX(root, ai));
|
||||
val = ObjectUtils.min(val, abCost.getMaxForNonManaX(root, ai, false));
|
||||
|
||||
if (val != null && val > 0) {
|
||||
// filter cost parts for preferences, don't choose X > than possible preferences
|
||||
|
||||
@@ -48,23 +48,23 @@ import java.util.*;
|
||||
public class ComputerUtilMana {
|
||||
private final static boolean DEBUG_MANA_PAYMENT = false;
|
||||
|
||||
public static boolean canPayManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
|
||||
public static boolean canPayManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean effect) {
|
||||
cost = new ManaCostBeingPaid(cost); //check copy of cost so it doesn't modify the exist cost being paid
|
||||
return payManaCost(cost, sa, ai, true, true);
|
||||
return payManaCost(cost, sa, ai, true, true, effect);
|
||||
}
|
||||
public static boolean canPayManaCost(final SpellAbility sa, final Player ai, final int extraMana) {
|
||||
return payManaCost(sa, ai, true, extraMana, true);
|
||||
public static boolean canPayManaCost(final SpellAbility sa, final Player ai, final int extraMana, final boolean effect) {
|
||||
return payManaCost(sa, ai, true, extraMana, true, effect);
|
||||
}
|
||||
|
||||
public static boolean payManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
|
||||
return payManaCost(cost, sa, ai, false, true);
|
||||
public static boolean payManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean effect) {
|
||||
return payManaCost(cost, sa, ai, false, true, effect);
|
||||
}
|
||||
public static boolean payManaCost(final Player ai, final SpellAbility sa) {
|
||||
return payManaCost(sa, ai, false, 0, true);
|
||||
public static boolean payManaCost(final Player ai, final SpellAbility sa, final boolean effect) {
|
||||
return payManaCost(sa, ai, false, 0, true, effect);
|
||||
}
|
||||
private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) {
|
||||
private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable, final boolean effect) {
|
||||
ManaCostBeingPaid cost = calculateManaCost(sa, test, extraMana);
|
||||
return payManaCost(cost, sa, ai, test, checkPlayable);
|
||||
return payManaCost(cost, sa, ai, test, checkPlayable, effect);
|
||||
}
|
||||
|
||||
private static void refundMana(List<Mana> manaSpent, Player ai, SpellAbility sa) {
|
||||
@@ -82,7 +82,7 @@ public class ComputerUtilMana {
|
||||
*/
|
||||
public static int getConvergeCount(final SpellAbility sa, final Player ai) {
|
||||
ManaCostBeingPaid cost = calculateManaCost(sa, true, 0);
|
||||
if (payManaCost(cost, sa, ai, true, true)) {
|
||||
if (payManaCost(cost, sa, ai, true, true, false)) {
|
||||
return cost.getSunburst();
|
||||
}
|
||||
return 0;
|
||||
@@ -93,7 +93,7 @@ public class ComputerUtilMana {
|
||||
if (ai == null || sa == null)
|
||||
return false;
|
||||
sa.setActivatingPlayer(ai);
|
||||
return payManaCost(sa, ai, true, 0, false);
|
||||
return payManaCost(sa, ai, true, 0, false, false);
|
||||
}
|
||||
|
||||
private static Integer scoreManaProducingCard(final Card card) {
|
||||
@@ -326,7 +326,7 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma.getHostCard(), ma)) {
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma.getHostCard(), ma, ma.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ public class ComputerUtilMana {
|
||||
SpellAbility saPayment = chooseManaAbility(cost, sa, ai, toPay, saList, true);
|
||||
if (saPayment == null) {
|
||||
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(2)) {
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(2, false)) {
|
||||
break; // cannot pay
|
||||
}
|
||||
|
||||
@@ -673,7 +673,7 @@ public class ComputerUtilMana {
|
||||
return manaSources;
|
||||
} // getManaSourcesToPayCost()
|
||||
|
||||
private static boolean payManaCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean test, boolean checkPlayable) {
|
||||
private static boolean payManaCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean test, boolean checkPlayable, boolean effect) {
|
||||
AiCardMemory.clearMemorySet(ai, MemorySet.PAYS_TAP_COST);
|
||||
AiCardMemory.clearMemorySet(ai, MemorySet.PAYS_SAC_COST);
|
||||
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
|
||||
@@ -793,7 +793,7 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
if (saPayment == null) {
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(2)
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(2, false)
|
||||
|| (ai.getLife() <= 2 && !ai.cantLoseForZeroOrLessLife())) {
|
||||
break; // cannot pay
|
||||
}
|
||||
@@ -816,7 +816,7 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
ai.payLife(2, sa.getHostCard());
|
||||
ai.payLife(2, sa.getHostCard(), false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -852,7 +852,7 @@ public class ComputerUtilMana {
|
||||
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||
} else {
|
||||
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
|
||||
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) {
|
||||
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment, effect))) {
|
||||
saList.remove(saPayment);
|
||||
continue;
|
||||
}
|
||||
@@ -1547,7 +1547,7 @@ public class ComputerUtilMana {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mCost = ManaCost.combine(mCost, mkCost);
|
||||
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
|
||||
if (!canPayManaCost(mcbp, sa, sa.getActivatingPlayer())) {
|
||||
if (!canPayManaCost(mcbp, sa, sa.getActivatingPlayer(), true)) {
|
||||
sa.getHostCard().setSVar("NumTimes", "Number$" + i);
|
||||
break;
|
||||
}
|
||||
@@ -1866,9 +1866,9 @@ public class ComputerUtilMana {
|
||||
* @return a int.
|
||||
* @since 1.0.15
|
||||
*/
|
||||
public static int determineLeftoverMana(final SpellAbility sa, final Player player) {
|
||||
public static int determineLeftoverMana(final SpellAbility sa, final Player player, final boolean effect) {
|
||||
for (int i = 1; i < 100; i++) {
|
||||
if (!canPayManaCost(sa.getRootAbility(), player, i)) {
|
||||
if (!canPayManaCost(sa.getRootAbility(), player, i, effect)) {
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
@@ -1889,13 +1889,13 @@ public class ComputerUtilMana {
|
||||
* @return a int.
|
||||
* @since 1.5.59
|
||||
*/
|
||||
public static int determineLeftoverMana(final SpellAbility sa, final Player player, final String shardColor) {
|
||||
public static int determineLeftoverMana(final SpellAbility sa, final Player player, final String shardColor, final boolean effect) {
|
||||
ManaCost origCost = sa.getRootAbility().getPayCosts().getTotalMana();
|
||||
|
||||
String shardSurplus = shardColor;
|
||||
for (int i = 1; i < 100; i++) {
|
||||
ManaCost extra = new ManaCost(new ManaCostParser(shardSurplus));
|
||||
if (!canPayManaCost(new ManaCostBeingPaid(ManaCost.combine(origCost, extra)), sa, player)) {
|
||||
if (!canPayManaCost(new ManaCostBeingPaid(ManaCost.combine(origCost, extra)), sa, player, effect)) {
|
||||
return i - 1;
|
||||
}
|
||||
shardSurplus += " " + shardColor;
|
||||
@@ -1941,7 +1941,7 @@ public class ComputerUtilMana {
|
||||
final Card offering = sa.getSacrificedAsOffering();
|
||||
offering.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null, null);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, false, null, null);
|
||||
}
|
||||
sa.resetSacrificedAsOffering();
|
||||
}
|
||||
@@ -1949,7 +1949,7 @@ public class ComputerUtilMana {
|
||||
final Card emerge = sa.getSacrificedAsEmerge();
|
||||
emerge.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null, null);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, false, null, null);
|
||||
}
|
||||
sa.resetSacrificedAsEmerge();
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
payingPlayer = ability.getHostCard().getEnchantingCard().getController();
|
||||
}
|
||||
|
||||
int number = ComputerUtilMana.determineLeftoverMana(ability, player);
|
||||
int number = ComputerUtilMana.determineLeftoverMana(ability, player, false);
|
||||
|
||||
if (logic.startsWith("MaxMana.") || logic.startsWith("PowerLeakMaxMana.")) {
|
||||
number = Math.min(number, Integer.parseInt(logic.substring(logic.indexOf(".") + 1)));
|
||||
@@ -535,7 +535,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
public void playSpellAbilityNoStack(SpellAbility effectSA, boolean canSetupTargets) {
|
||||
if (canSetupTargets)
|
||||
brains.doTrigger(effectSA, true); // first parameter does not matter, since return value won't be used
|
||||
ComputerUtil.playNoStack(player, effectSA, getGame());
|
||||
ComputerUtil.playNoStack(player, effectSA, getGame(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -692,6 +692,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public boolean payManaOptional(Card c, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) {
|
||||
// TODO replace with EmptySa
|
||||
final Ability ability = new AbilityStatic(c, cost, null) { @Override public void resolve() {} };
|
||||
ability.setActivatingPlayer(c.getController());
|
||||
|
||||
@@ -713,8 +714,8 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
// - End of hack for Exile a card from library Cumulative Upkeep -
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, c.getController())) {
|
||||
ComputerUtil.playNoStack(c.getController(), ability, getGame());
|
||||
if (ComputerUtilCost.canPayCost(ability, c.getController(), true)) {
|
||||
ComputerUtil.playNoStack(c.getController(), ability, getGame(), true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1009,13 +1010,14 @@ public class PlayerControllerAi extends PlayerController {
|
||||
@Override
|
||||
public boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers) {
|
||||
final Card source = sa.getHostCard();
|
||||
// TODO replace with EmptySa
|
||||
final Ability emptyAbility = new AbilityStatic(source, cost, sa.getTargetRestrictions()) { @Override public void resolve() { } };
|
||||
emptyAbility.setActivatingPlayer(player);
|
||||
emptyAbility.setTriggeringObjects(sa.getTriggeringObjects());
|
||||
emptyAbility.setSVars(sa.getSVars());
|
||||
emptyAbility.setXManaCostPaid(sa.getRootAbility().getXManaCostPaid());
|
||||
if (ComputerUtilCost.willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers) && ComputerUtilCost.canPayCost(emptyAbility, player)) {
|
||||
ComputerUtil.playNoStack(player, emptyAbility, getGame()); // AI needs something to resolve to pay that cost
|
||||
if (ComputerUtilCost.willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers) && ComputerUtilCost.canPayCost(emptyAbility, player, true)) {
|
||||
ComputerUtil.playNoStack(player, emptyAbility, getGame(), true); // AI needs something to resolve to pay that cost
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1069,7 +1071,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
@Override
|
||||
public void playTrigger(Card host, WrappedAbility wrapperAbility, boolean isMandatory) {
|
||||
if (prepareSingleSa(host, wrapperAbility, isMandatory)) {
|
||||
ComputerUtil.playNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility, getGame());
|
||||
ComputerUtil.playNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility, getGame(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,10 +1140,10 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, ManaConversionMatrix matrix, boolean isActivatedSa) {
|
||||
public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, ManaConversionMatrix matrix, boolean effect) {
|
||||
// TODO Auto-generated method stub
|
||||
ManaCostBeingPaid cost = isActivatedSa ? ComputerUtilMana.calculateManaCost(sa, false, 0) : new ManaCostBeingPaid(toPay);
|
||||
return ComputerUtilMana.payManaCost(cost, sa, player);
|
||||
ManaCostBeingPaid cost = !effect ? ComputerUtilMana.calculateManaCost(sa, false, 0) : new ManaCostBeingPaid(toPay);
|
||||
return ComputerUtilMana.payManaCost(cost, sa, player, effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1347,7 +1349,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player, false)) {
|
||||
chosenOptCosts.add(opt);
|
||||
costSoFar.add(opt.getCost());
|
||||
}
|
||||
@@ -1372,7 +1374,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
for (int i = 0; i < max; i++) {
|
||||
costSoFar.add(cost);
|
||||
SpellAbility fullCostSa = sa.copyWithDefinedCost(costSoFar);
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player, sa.isTrigger())) {
|
||||
chosenAmount++;
|
||||
} else {
|
||||
break;
|
||||
|
||||
@@ -647,7 +647,7 @@ public class SpecialCardAi {
|
||||
public static class GoblinPolkaBand {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
int maxPotentialTgts = Lists.newArrayList(Iterables.filter(ai.getOpponents().getCreaturesInPlay(), CardPredicates.Presets.UNTAPPED)).size();
|
||||
int maxPotentialPayment = ComputerUtilMana.determineLeftoverMana(sa, ai, "R");
|
||||
int maxPotentialPayment = ComputerUtilMana.determineLeftoverMana(sa, ai, "R", false);
|
||||
|
||||
int numTgts = Math.min(maxPotentialPayment, maxPotentialTgts);
|
||||
if (numTgts == 0) {
|
||||
@@ -924,7 +924,7 @@ public class SpecialCardAi {
|
||||
public static Card considerCardFromList(final CardCollection fetchList) {
|
||||
for (Card c : CardLists.filter(fetchList, Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.CREATURES))) {
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
if (ab.isAbility() && !ab.isTrigger()) {
|
||||
if (ab.isActivatedAbility()) {
|
||||
Player controller = c.getController();
|
||||
boolean wasCaged = false;
|
||||
for (Card caged : CardLists.filter(controller.getCardsIn(ZoneType.Exile),
|
||||
@@ -994,7 +994,7 @@ public class SpecialCardAi {
|
||||
}
|
||||
|
||||
// Set PayX here to maximum value.
|
||||
int tokenSize = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int tokenSize = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
||||
|
||||
// Some basic strategy for Momir
|
||||
if (tokenSize < 2) {
|
||||
@@ -1014,7 +1014,7 @@ public class SpecialCardAi {
|
||||
// Multiple Choice
|
||||
public static class MultipleChoice {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
int maxX = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int maxX = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
||||
|
||||
if (maxX == 0) {
|
||||
return false;
|
||||
@@ -1604,7 +1604,7 @@ public class SpecialCardAi {
|
||||
if (topGY == null
|
||||
|| !topGY.isCreature()
|
||||
|| ComputerUtilCard.evaluateCreature(creatHand) > ComputerUtilCard.evaluateCreature(topGY) + 80) {
|
||||
return numCreatsInHand > 1 || !ComputerUtilMana.canPayManaCost(creatHand.getSpellPermanent(), ai, 0);
|
||||
return numCreatsInHand > 1 || !ComputerUtilMana.canPayManaCost(creatHand.getSpellPermanent(), ai, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1769,7 +1769,7 @@ public class SpecialCardAi {
|
||||
int CMC = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().getCMC() : 0;
|
||||
int Xcount = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().countX() : 0;
|
||||
|
||||
if ((Xcount == 0 && CMC == 0) || ComputerUtilMana.canPayManaCost(ab, ai, selfCMC + minManaAdj)) {
|
||||
if ((Xcount == 0 && CMC == 0) || ComputerUtilMana.canPayManaCost(ab, ai, selfCMC + minManaAdj, false)) {
|
||||
if (src.isInstant() || src.isSorcery()) {
|
||||
// instants and sorceries are one-shot, so only treat them as 1/2 value for the purpose of meeting minimum
|
||||
// castable cards in graveyard requirements
|
||||
|
||||
@@ -60,7 +60,7 @@ public abstract class SpellAbilityAi {
|
||||
|
||||
if (sa.hasParam("AICheckCanPlayWithDefinedX")) {
|
||||
// FIXME: can this somehow be simplified without the need for an extra AI hint?
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, false));
|
||||
}
|
||||
|
||||
if (!checkConditions(ai, sa, sa.getConditions())) {
|
||||
@@ -104,7 +104,7 @@ public abstract class SpellAbilityAi {
|
||||
if (!con.getManaSpent().isEmpty()) {
|
||||
// need to use ManaCostBeingPaid check, can't use Cost#canPay
|
||||
ManaCostBeingPaid paid = new ManaCostBeingPaid(new ManaCost(new ManaCostParser(con.getManaSpent())));
|
||||
if (ComputerUtilMana.canPayManaCost(paid, sa, ai)) {
|
||||
if (ComputerUtilMana.canPayManaCost(paid, sa, ai, sa.isTrigger())) {
|
||||
con.setManaSpent("");
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public abstract class SpellAbilityAi {
|
||||
|
||||
public final boolean doTriggerAI(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||
// this evaluation order is currently intentional as it does more stuff that helps avoiding some crashes
|
||||
if (!ComputerUtilCost.canPayCost(sa, aiPlayer) && !mandatory) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, aiPlayer, true) && !mandatory) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public abstract class SpellAbilityAi {
|
||||
*/
|
||||
protected static boolean isSorcerySpeed(final SpellAbility sa) {
|
||||
return (sa.getRootAbility().isSpell() && sa.getHostCard().isSorcery())
|
||||
|| (sa.getRootAbility().isAbility() && sa.getRestrictions().isSorcerySpeed())
|
||||
|| (sa.getRootAbility().isActivatedAbility() && sa.getRestrictions().isSorcerySpeed())
|
||||
|| (sa.getRootAbility().isAdventure() && sa.getHostCard().getState(CardStateName.Adventure).getType().isSorcery())
|
||||
|| (sa.isPwAbility() && !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed."));
|
||||
}
|
||||
|
||||
@@ -71,14 +71,14 @@ public class AnimateAi extends SpellAbilityAi {
|
||||
final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack);
|
||||
CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
|
||||
ai.getWeakestOpponent(), topStack.getHostCard(), topStack);
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack, true));
|
||||
ComputerUtilCard.sortByEvaluateCreature(list);
|
||||
if (!list.isEmpty() && list.size() == nToSac && ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (!list.isEmpty() && list.size() == nToSac && ComputerUtilCost.canPayCost(sa, ai, sa.isTrigger())) {
|
||||
Card animatedCopy = becomeAnimated(source, sa);
|
||||
list.add(animatedCopy);
|
||||
list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(),
|
||||
topStack);
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack, true));
|
||||
if (ComputerUtilCard.evaluateCreature(animatedCopy) < ComputerUtilCard.evaluateCreature(list.get(0))
|
||||
&& list.contains(animatedCopy)) {
|
||||
return true;
|
||||
@@ -137,7 +137,7 @@ public class AnimateAi extends SpellAbilityAi {
|
||||
|
||||
if (sa.costHasManaX() && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer, sa.isTrigger());
|
||||
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
@@ -343,7 +343,7 @@ public class AnimateAi extends SpellAbilityAi {
|
||||
if (worst.isLand()) {
|
||||
// e.g. Clan Guildmage, make sure we're not using the same land we want to animate to activate the ability
|
||||
holdAnimatedTillMain2(ai, worst);
|
||||
if (!ComputerUtilMana.canPayManaCost(sa, ai, 0)) {
|
||||
if (!ComputerUtilMana.canPayManaCost(sa, ai, 0, sa.isTrigger())) {
|
||||
releaseHeldTillMain2(ai, worst);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
if (abCost.getTotalMana().countX() > 0 && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value. (Endless Scream and Venarian Gold)
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
if (xPay == 0) {
|
||||
return false;
|
||||
@@ -353,14 +353,14 @@ public class AttachAi extends SpellAbilityAi {
|
||||
public boolean apply(final Card c) {
|
||||
//Check for cards that can be sacrificed in response
|
||||
for (final SpellAbility ability : c.getAllSpellAbilities()) {
|
||||
if (ability.isAbility()) {
|
||||
if (ability.isActivatedAbility()) {
|
||||
final Cost cost = ability.getPayCosts();
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (!(part instanceof CostSacrifice)) {
|
||||
continue;
|
||||
}
|
||||
CostSacrifice sacCost = (CostSacrifice) part;
|
||||
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController())) {
|
||||
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class ChangeTargetsAi extends SpellAbilityAi {
|
||||
// e.g. Spellskite or a creature receiving its ability that requires Phyrexian mana P/U
|
||||
int potentialDmg = ComputerUtil.predictDamageFromSpell(topSa, aiPlayer);
|
||||
ManaCost normalizedMana = manaCost.getNormalizedMana();
|
||||
boolean canPay = ComputerUtilMana.canPayManaCost(new ManaCostBeingPaid(normalizedMana), sa, aiPlayer);
|
||||
boolean canPay = ComputerUtilMana.canPayManaCost(new ManaCostBeingPaid(normalizedMana), sa, aiPlayer, false);
|
||||
if (potentialDmg != -1 && potentialDmg <= payDamage && !canPay
|
||||
&& topTargets.contains(aiPlayer)) {
|
||||
// do not pay Phyrexian mana if the spell is a damaging one but it deals less damage or the same damage as we'll pay life
|
||||
|
||||
@@ -347,7 +347,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (type != null) {
|
||||
if (type.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
type = type.replace("X", Integer.toString(xPay));
|
||||
}
|
||||
@@ -392,7 +392,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (num != null) {
|
||||
if (num.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
if (xPay == 0) return false;
|
||||
xPay = Math.min(xPay, list.size());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
@@ -502,7 +502,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
final String type = sa.getParam("ChangeType");
|
||||
if (type != null && type.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
@@ -870,7 +870,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
// X controls the minimum targets
|
||||
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
|
||||
sa.setXManaCostPaid(xPay);
|
||||
@@ -2171,7 +2171,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && sa.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||
setPayX = true;
|
||||
toPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
toPay = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
} else {
|
||||
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ChooseColorAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
// Set PayX here to maximum value.
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player);
|
||||
paycost.setPayCosts(unless);
|
||||
if (ComputerUtilCost.willPayUnlessCost(sp, player, unless, false, new FCollection<>(player))
|
||||
&& ComputerUtilCost.canPayCost(paycost, player)) {
|
||||
&& ComputerUtilCost.canPayCost(paycost, player, true)) {
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ChooseTypeAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxX = ComputerUtilMana.determineLeftoverMana(sa, aiPlayer);
|
||||
int maxX = ComputerUtilMana.determineLeftoverMana(sa, aiPlayer, false);
|
||||
int avgPower = 0;
|
||||
|
||||
// predict the opposition
|
||||
|
||||
@@ -82,7 +82,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
|
||||
if (sa.costHasManaX() && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value. (Osgir)
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer, sa.isTrigger());
|
||||
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && sa.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||
setPayX = true;
|
||||
toPay = Math.min(ComputerUtilCost.getMaxXValue(sa, ai), usableManaSources + 1);
|
||||
toPay = Math.min(ComputerUtilCost.getMaxXValue(sa, ai, true), usableManaSources + 1);
|
||||
} else {
|
||||
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && sa.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||
setPayX = true;
|
||||
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
toPay = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
} else {
|
||||
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
// check if Spell with Strive is still playable
|
||||
if (sa.isSpell() && sa.getHostCard().hasStartOfKeyword("Strive")) {
|
||||
// if not remove target again and break list
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai, false)) {
|
||||
sa.getTargets().remove(c);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ public class CountersPutAi extends CountersAi {
|
||||
if (amountStr.equals("X")) {
|
||||
if (sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// By default, set PayX here to maximum value (used for most SAs of this type).
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
if (isClockwork) {
|
||||
// Clockwork Avian and other similar cards: do not tap all mana for X,
|
||||
@@ -769,7 +769,7 @@ public class CountersPutAi extends CountersAi {
|
||||
}
|
||||
|
||||
// Spend all remaining mana to add X counters (eg. Hero of Leina Tower)
|
||||
int payX = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int payX = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
|
||||
// Account for the possible presence of additional glyphs in cost (e.g. Mikaeus, the Lunarch; Primordial Hydra)
|
||||
payX -= nonXGlyphs;
|
||||
|
||||
@@ -83,7 +83,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
|
||||
final int amount;
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(amount);
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
|
||||
@@ -149,7 +149,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
int amount;
|
||||
boolean xPay = false;
|
||||
if (amountStr.equals("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
final int manaLeft = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int manaLeft = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
if (manaLeft == 0) {
|
||||
return false;
|
||||
@@ -301,7 +301,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
boolean xPay = false;
|
||||
// Timecrafting has X R
|
||||
if (amountStr.equals("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
final int manaLeft = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int manaLeft = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
if (manaLeft == 0) {
|
||||
return false;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||
}
|
||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
}
|
||||
if (x == -1) {
|
||||
if (determineOppToKill(ai, sa, source, dmg) != null) {
|
||||
@@ -197,7 +197,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
int dmg;
|
||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(dmg);
|
||||
} else {
|
||||
dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||
@@ -276,7 +276,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
|
||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(dmg);
|
||||
} else {
|
||||
dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||
|
||||
@@ -91,7 +91,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
}
|
||||
|
||||
// Set PayX here to maximum value.
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(dmg);
|
||||
} else if (sa.getSVar(damage).equals("Count$CardsInYourHand") && source.isInZone(ZoneType.Hand)) {
|
||||
dmg--; // the card will be spent casting the spell, so actual damage is 1 less
|
||||
@@ -111,7 +111,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
if (damage.equals("X")) {
|
||||
if (sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
// Try not to waste spells like Blaze or Fireball on early targets, try to do more damage with them if possible
|
||||
if (ai.getController().isAI()) {
|
||||
@@ -959,7 +959,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(dmg);
|
||||
}
|
||||
|
||||
@@ -1007,9 +1007,9 @@ public class DamageDealAi extends DamageAiBase {
|
||||
Player opponent = ai.getWeakestOpponent();
|
||||
|
||||
// TODO: somehow account for the possible cost reduction?
|
||||
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"));
|
||||
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"), false);
|
||||
|
||||
while (!ComputerUtilMana.canPayManaCost(sa, ai, dmg) && dmg > 0) {
|
||||
while (!ComputerUtilMana.canPayManaCost(sa, ai, dmg, false) && dmg > 0) {
|
||||
// TODO: ideally should never get here, currently put here as a precaution for complex mana base cases where the miscalculation might occur. Will remove later if it proves to never trigger.
|
||||
dmg--;
|
||||
System.out.println("Warning: AI could not pay mana cost for a XLifeDrain logic spell. Reducing X value to "+dmg);
|
||||
@@ -1019,7 +1019,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
// TODO: somehow generalize this calculation to allow other potential similar cards to function in the future
|
||||
if ("Soul Burn".equals(sourceName)) {
|
||||
Map<String, Integer> xByColor = Maps.newHashMap();
|
||||
xByColor.put("B", dmg - ComputerUtilMana.determineLeftoverMana(sa, ai, "R"));
|
||||
xByColor.put("B", dmg - ComputerUtilMana.determineLeftoverMana(sa, ai, "R", false));
|
||||
source.setXManaCostPaidByColor(xByColor);
|
||||
}
|
||||
|
||||
@@ -1123,7 +1123,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
ManaCost total = ManaCost.combine(costSa, costAb);
|
||||
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
|
||||
// can we pay both costs?
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0)) {
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0, false)) {
|
||||
return Pair.of(ab, Integer.parseInt(dmgDef));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
||||
ManaCost total = ManaCost.combine(costSa, costAb);
|
||||
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
|
||||
// can we pay both costs?
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0)) {
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
||||
}
|
||||
AiPlayDecision decision = ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(ab);
|
||||
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
|
||||
if (ComputerUtilMana.canPayManaCost(ab, ai, 0)) {
|
||||
if (ComputerUtilMana.canPayManaCost(ab, ai, 0, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
// If there's X in payment costs and it's tied to targeting, make sure we set the XManaCostPaid first
|
||||
// (e.g. Heliod's Intervention)
|
||||
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.getRootAbility().setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
|
||||
if (sa.getRootAbility().costHasManaX()) {
|
||||
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
|
||||
maxTargets = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
maxTargets = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
// need to set XPaid to get the right number for
|
||||
sa.getRootAbility().setXManaCostPaid(maxTargets);
|
||||
// need to check for maxTargets
|
||||
@@ -179,14 +179,14 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
public boolean apply(final Card c) {
|
||||
//Check for cards that can be sacrificed in response
|
||||
for (final SpellAbility ability : c.getAllSpellAbilities()) {
|
||||
if (ability.isAbility()) {
|
||||
if (ability.isActivatedAbility()) {
|
||||
final Cost cost = ability.getPayCosts();
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (!(part instanceof CostSacrifice)) {
|
||||
continue;
|
||||
}
|
||||
CostSacrifice sacCost = (CostSacrifice) part;
|
||||
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController())) {
|
||||
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class DestroyAllAi extends SpellAbilityAi {
|
||||
|
||||
if (valid.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
valid = valid.replace("X", Integer.toString(xPay));
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class DigAi extends SpellAbilityAi {
|
||||
manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
||||
}
|
||||
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai) - manaToSave;
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()) - manaToSave;
|
||||
if (numCards <= 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ public class DigAi extends SpellAbilityAi {
|
||||
// Triggers that ask to pay {X} (e.g. Depala, Pilot Exemplar).
|
||||
if (sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("PayXButSaveMana")) {
|
||||
int manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai) - manaToSave;
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai, true) - manaToSave;
|
||||
if (numCards <= 0) {
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class DigUntilAi extends SpellAbilityAi {
|
||||
// Set PayX here to maximum value.
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int numCards = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
if (numCards <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class DiscardAi extends SpellAbilityAi {
|
||||
if (sa.hasParam("NumCards")) {
|
||||
if (sa.getParam("NumCards").equals("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int cardsToDiscard = Math.min(ComputerUtilCost.getMaxXValue(sa, ai), ai.getWeakestOpponent()
|
||||
final int cardsToDiscard = Math.min(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()), ai.getWeakestOpponent()
|
||||
.getCardsIn(ZoneType.Hand).size());
|
||||
if (cardsToDiscard < 1) {
|
||||
return false;
|
||||
@@ -151,7 +151,7 @@ public class DiscardAi extends SpellAbilityAi {
|
||||
for (Player opp : opps) {
|
||||
if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) {
|
||||
continue;
|
||||
} else if (!opp.canDiscardBy(sa)) { // e.g. Tamiyo, Collector of Tales
|
||||
} else if (!opp.canDiscardBy(sa, true)) { // e.g. Tamiyo, Collector of Tales
|
||||
continue;
|
||||
}
|
||||
if (sa.usesTargeting()) {
|
||||
@@ -190,7 +190,7 @@ public class DiscardAi extends SpellAbilityAi {
|
||||
}
|
||||
if ("X".equals(sa.getParam("RevealNumber")) && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int cardsToDiscard = Math.min(ComputerUtilCost.getMaxXValue(sa, ai), ai.getWeakestOpponent()
|
||||
final int cardsToDiscard = Math.min(ComputerUtilCost.getMaxXValue(sa, ai, true), ai.getWeakestOpponent()
|
||||
.getCardsIn(ZoneType.Hand).size());
|
||||
sa.setXManaCostPaid(cardsToDiscard);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source,sa)) {
|
||||
AiCostDecision aiDecisions = new AiCostDecision(ai, sa);
|
||||
AiCostDecision aiDecisions = new AiCostDecision(ai, sa, false);
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostDiscard) {
|
||||
PaymentDecision decision = part.accept(aiDecisions);
|
||||
@@ -255,7 +255,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
if (drawback && root.getXManaCostPaid() != null) {
|
||||
numCards = root.getXManaCostPaid();
|
||||
} else {
|
||||
numCards = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
numCards = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
// try not to overdraw
|
||||
int safeDraw = Math.abs(Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3));
|
||||
if (source.isInstant() || source.isSorcery()) { safeDraw++; } // card will be spent
|
||||
|
||||
@@ -123,7 +123,7 @@ public class EffectAi extends SpellAbilityAi {
|
||||
} else if (logic.equals("WillCastCreature") && ai.isAI()) {
|
||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
SpellAbility saCreature = aic.predictSpellToCastInMain2(ApiType.PermanentCreature);
|
||||
randomReturn = saCreature != null && ComputerUtilMana.canPayManaCost(saCreature, ai, 0);
|
||||
randomReturn = saCreature != null && ComputerUtilMana.canPayManaCost(saCreature, ai, 0, false);
|
||||
} else if (logic.equals("Always")) {
|
||||
randomReturn = true;
|
||||
} else if (logic.equals("Main1")) {
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (logic.equals("MaxX")) {
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, true));
|
||||
}
|
||||
|
||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
|
||||
@@ -131,7 +131,7 @@ public class LifeGainAi extends SpellAbilityAi {
|
||||
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
lifeAmount = xPay;
|
||||
} else {
|
||||
@@ -218,7 +218,7 @@ public class LifeGainAi extends SpellAbilityAi {
|
||||
final String amountStr = sa.getParam("LifeAmount");
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.google.common.base.Predicates;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilCost;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
@@ -40,7 +39,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
amount = root.getXManaCostPaid();
|
||||
} else if (root.getPayCosts() != null && root.getPayCosts().hasXInAnyCostPart()) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
root.setXManaCostPaid(xPay);
|
||||
amount = xPay;
|
||||
}
|
||||
@@ -73,7 +72,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
}
|
||||
@@ -107,7 +106,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(amount);
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
@@ -173,7 +172,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
int amount = 0;
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(xPay);
|
||||
amount = xPay;
|
||||
} else {
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LifeSetAi extends SpellAbilityAi {
|
||||
// we shouldn't have to worry too much about PayX for SetLife
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
amount = xPay;
|
||||
} else {
|
||||
@@ -114,7 +114,7 @@ public class LifeSetAi extends SpellAbilityAi {
|
||||
int amount;
|
||||
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(xPay);
|
||||
amount = xPay;
|
||||
} else {
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ManifestAi extends SpellAbilityAi {
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Handle either Manifest X cards, or Manifest 1 card and give it X P1P1s
|
||||
// Set PayX here to maximum value.
|
||||
int x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int x = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(x);
|
||||
if (x <= 0) {
|
||||
return false;
|
||||
|
||||
@@ -221,6 +221,6 @@ public class MillAi extends SpellAbilityAi {
|
||||
cardsToDiscard = Math.min(ai.getCardsIn(ZoneType.Library).size() - 5, cardsToDiscard);
|
||||
}
|
||||
|
||||
return Math.min(ComputerUtilCost.getMaxXValue(sa, ai), cardsToDiscard);
|
||||
return Math.min(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()), cardsToDiscard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class PermanentAi extends SpellAbilityAi {
|
||||
ManaCost mana = sa.getPayCosts().getTotalMana();
|
||||
if (mana.countX() > 0) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
||||
final Card source = sa.getHostCard();
|
||||
if (source.hasConverge()) {
|
||||
int nColors = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||
@@ -141,7 +141,7 @@ public class PermanentAi extends SpellAbilityAi {
|
||||
|
||||
int generic = paidCost.getGenericManaAmount();
|
||||
// Set PayX here to maximum value.
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
||||
// currently cards with SacToReduceCost reduce by 2 generic
|
||||
xPay = Math.min(xPay, generic / 2);
|
||||
sa.setXManaCostPaid(xPay);
|
||||
@@ -155,7 +155,7 @@ public class PermanentAi extends SpellAbilityAi {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mCost = ManaCost.combine(mCost, mkCost);
|
||||
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
|
||||
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, ai)) {
|
||||
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, ai, false)) {
|
||||
card.setKickerMagnitude(i);
|
||||
sa.setSVar("Multikicker", String.valueOf(i));
|
||||
break;
|
||||
@@ -181,7 +181,7 @@ public class PermanentAi extends SpellAbilityAi {
|
||||
emptyAbility.setTargetRestrictions(sa.getTargetRestrictions());
|
||||
|
||||
emptyAbility.setActivatingPlayer(ai);
|
||||
if (!ComputerUtilCost.canPayCost(emptyAbility, ai)) {
|
||||
if (!ComputerUtilCost.canPayCost(emptyAbility, ai, true)) {
|
||||
// AiPlayDecision.AnotherTime
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class PermanentCreatureAi extends PermanentAi {
|
||||
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
if (game.getReplacementHandler().wouldPhaseBeSkipped(ai, "BeginCombat"))
|
||||
return false;
|
||||
if (ComputerUtilCost.canPayCost(sa.getHostCard().getSpellPermanent(), ai)) {
|
||||
if (ComputerUtilCost.canPayCost(sa.getHostCard().getSpellPermanent(), ai, false)) {
|
||||
//do not dash if creature can be played normally
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
return false;
|
||||
ManaCost mana = sa.getPayCosts().getTotalMana();
|
||||
if (mana.countX() > 0) {
|
||||
int amount = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
if (amount < ComputerUtilCard.getBestAI(cards).getCMC())
|
||||
return false;
|
||||
int totalCMC = 0;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class PumpAi extends PumpAiBase {
|
||||
return SpecialAiLogic.doAristocratWithCountersLogic(ai, sa);
|
||||
} else if (aiLogic.equals("SwitchPT")) {
|
||||
// Some more AI would be even better, but this is a good start to prevent spamming
|
||||
if (sa.isAbility() && sa.getActivationsThisTurn() > 0 && !sa.usesTargeting()) {
|
||||
if (sa.isActivatedAbility() && sa.getActivationsThisTurn() > 0 && !sa.usesTargeting()) {
|
||||
// Will prevent flipping back and forth
|
||||
return false;
|
||||
}
|
||||
@@ -253,7 +253,7 @@ public class PumpAi extends PumpAiBase {
|
||||
// Donate step 1 - try to target an opponent, preferably one who does not have a donate target yet
|
||||
return SpecialCardAi.Donate.considerTargetingOpponent(ai, sa);
|
||||
} else if (aiLogic.equals("InfernoOfTheStarMounts")) {
|
||||
int numRedMana = ComputerUtilMana.determineLeftoverMana(sa, ai, "R");
|
||||
int numRedMana = ComputerUtilMana.determineLeftoverMana(sa, ai, "R", false);
|
||||
int currentPower = source.getNetPower();
|
||||
if (currentPower < 20 && currentPower + numRedMana >= 20) {
|
||||
return true;
|
||||
@@ -284,7 +284,7 @@ public class PumpAi extends PumpAiBase {
|
||||
int defense;
|
||||
if (numDefense.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
if (sourceName.equals("Necropolis Fiend")) {
|
||||
xPay = Math.min(xPay, sa.getActivatingPlayer().getCardsIn(ZoneType.Graveyard).size());
|
||||
sa.setSVar("X", Integer.toString(xPay));
|
||||
@@ -305,7 +305,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (numAttack.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai, sa.isTrigger());
|
||||
root.setXManaCostPaid(xPay);
|
||||
attack = xPay;
|
||||
} else {
|
||||
@@ -531,7 +531,7 @@ public class PumpAi extends PumpAiBase {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
for (SpellAbility sa : card.getSpellAbilities()) {
|
||||
if (sa.isAbility()) {
|
||||
if (sa.isActivatedAbility()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -681,7 +681,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (numDefense.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai, true);
|
||||
root.setXManaCostPaid(xPay);
|
||||
defense = xPay;
|
||||
} else {
|
||||
@@ -695,7 +695,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (numAttack.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(root, ai, true);
|
||||
root.setXManaCostPaid(xPay);
|
||||
attack = xPay;
|
||||
} else {
|
||||
@@ -750,7 +750,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (numAttack.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
// X is not set yet
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
root.setXManaCostPaid(xPay);
|
||||
attack = xPay;
|
||||
} else {
|
||||
@@ -764,7 +764,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (numDefense.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
// X is not set yet
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
root.setXManaCostPaid(xPay);
|
||||
defense = xPay;
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RepeatAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
// Set PayX here to maximum value.
|
||||
final int max = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int max = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.setXManaCostPaid(max);
|
||||
return max > 0;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.util.List;
|
||||
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.ComputerUtilCost;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -88,7 +87,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
if (!destroy) {
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(sa));
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(sa, true));
|
||||
} else {
|
||||
if (!CardLists.getKeyword(list, Keyword.INDESTRUCTIBLE).isEmpty()) {
|
||||
// human can choose to destroy indestructibles
|
||||
@@ -102,7 +101,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
|
||||
if (num.equals("X") && sa.getSVar(num).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
sa.setXManaCostPaid(Math.min(ComputerUtilCost.getMaxXValue(sa, ai), amount));
|
||||
sa.setXManaCostPaid(Math.min(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()), amount));
|
||||
}
|
||||
|
||||
final int half = (amount / 2) + (amount % 2); // Half of amount rounded up
|
||||
@@ -131,7 +130,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
|
||||
if (num.equals("X") && sa.getSVar(num).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount);
|
||||
amount = Math.min(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()), amount);
|
||||
}
|
||||
|
||||
List<Card> humanList = null;
|
||||
@@ -183,7 +182,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
if (!targetable.isEmpty()) {
|
||||
CardCollection priorityTgts = new CardCollection();
|
||||
if (p.isOpponentOf(ai)) {
|
||||
priorityTgts.addAll(CardLists.filter(targetable, CardPredicates.canBeSacrificedBy(sa)));
|
||||
priorityTgts.addAll(CardLists.filter(targetable, CardPredicates.canBeSacrificedBy(sa, true)));
|
||||
if (!priorityTgts.isEmpty()) {
|
||||
sa.getTargets().add(ComputerUtilCard.getBestAI(priorityTgts));
|
||||
} else {
|
||||
@@ -191,7 +190,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
}
|
||||
} else {
|
||||
for (Card c : targetable) {
|
||||
if (c.canBeSacrificedBy(sa) && (c.hasSVar("SacMe") || (c.isCreature() && ComputerUtilCard.evaluateCreature(c) <= 135)) && !c.equals(sa.getHostCard())) {
|
||||
if (c.canBeSacrificedBy(sa, true) && (c.hasSVar("SacMe") || (c.isCreature() && ComputerUtilCard.evaluateCreature(c) <= 135)) && !c.equals(sa.getHostCard())) {
|
||||
priorityTgts.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SetStateAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, aiPlayer, sa.isTrigger());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class StoreSVarAi extends SpellAbilityAi {
|
||||
if (sa.hasParam("AILogic")) {
|
||||
if (sa.getPayCosts().getTotalMana().countX() > 0 && source.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to half the remaining mana to allow for Main 2 and other combat shenanigans.
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai) / 2;
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai, sa.isTrigger()) / 2;
|
||||
if (xPay == 0) { return false; }
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TapAi extends TapAiBase {
|
||||
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()));
|
||||
}
|
||||
|
||||
sa.resetTargets();
|
||||
|
||||
@@ -98,7 +98,7 @@ public class TokenAi extends SpellAbilityAi {
|
||||
}
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
sa.getRootAbility().setXManaCostPaid(x);
|
||||
}
|
||||
if (x <= 0) {
|
||||
@@ -243,13 +243,13 @@ public class TokenAi extends SpellAbilityAi {
|
||||
final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack);
|
||||
CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
|
||||
ai.getWeakestOpponent(), topStack.getHostCard(), sa);
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack, true));
|
||||
// only care about saving single creature for now
|
||||
if (!list.isEmpty() && nTokens > 0 && list.size() == nToSac) {
|
||||
ComputerUtilCard.sortByEvaluateCreature(list);
|
||||
list.add(token);
|
||||
list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(), sa);
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack, true));
|
||||
return ComputerUtilCard.evaluateCreature(token) < ComputerUtilCard.evaluateCreature(list.get(0))
|
||||
&& list.contains(token);
|
||||
}
|
||||
@@ -284,7 +284,7 @@ public class TokenAi extends SpellAbilityAi {
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
if (x == 0) { // already paid outside trigger
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai, true);
|
||||
sa.setXManaCostPaid(x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class UnattachAllAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
if (xPay == 0) {
|
||||
return false;
|
||||
|
||||
@@ -367,7 +367,7 @@ public class UntapAi extends SpellAbilityAi {
|
||||
// can ideally be improved to work by color.
|
||||
ManaCostBeingPaid reduced = new ManaCostBeingPaid(ab.getPayCosts().getCostMana().getManaCostFor(ab), ab.getPayCosts().getCostMana().getRestriction());
|
||||
reduced.decreaseShard(ManaCostShard.GENERIC, untappingCards.size());
|
||||
if (ComputerUtilMana.canPayManaCost(reduced, ab, ai)) {
|
||||
if (ComputerUtilMana.canPayManaCost(reduced, ab, ai, false)) {
|
||||
CardCollection manaLandsTapped = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
||||
Predicates.and(Presets.LANDS_PRODUCING_MANA, Presets.TAPPED));
|
||||
manaLandsTapped = CardLists.filter(manaLandsTapped, new Predicate<Card>() {
|
||||
@@ -400,7 +400,7 @@ public class UntapAi extends SpellAbilityAi {
|
||||
Card landToPool = manaLands.getFirst();
|
||||
SpellAbility manaAb = landToPool.getManaAbilities().getFirst();
|
||||
|
||||
ComputerUtil.playNoStack(ai, manaAb, game);
|
||||
ComputerUtil.playNoStack(ai, manaAb, game, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ public class SpellAbilityChoicesIterator {
|
||||
// TODO this should also iterate over all possible values
|
||||
// (currently no additional complexity to keep performance reasonable)
|
||||
if (sa.costHasManaX()) {
|
||||
Integer x = ComputerUtilCost.getMaxXValue(sa, sa.getActivatingPlayer());
|
||||
Integer x = ComputerUtilCost.getMaxXValue(sa, sa.getActivatingPlayer(), sa.isTrigger());
|
||||
sa.setXManaCostPaid(x);
|
||||
controller.getLastDecision().xMana = x;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ public class SpellAbilityPicker {
|
||||
if (sa.isPwAbility()) {
|
||||
return !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed.");
|
||||
}
|
||||
return sa.isAbility() && sa.getRestrictions().isSorcerySpeed();
|
||||
return sa.isActivatedAbility() && sa.getRestrictions().isSorcerySpeed();
|
||||
}
|
||||
|
||||
private void createNewPlan(Score origGameScore, List<SpellAbility> candidateSAs) {
|
||||
@@ -348,7 +348,7 @@ public class SpellAbilityPicker {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (!ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
|
||||
@@ -434,11 +434,11 @@ public class SpellAbilityPicker {
|
||||
}
|
||||
}
|
||||
|
||||
public CardCollectionView chooseSacrificeType(String type, SpellAbility ability, int amount, final CardCollectionView exclude) {
|
||||
public CardCollectionView chooseSacrificeType(String type, SpellAbility ability, final boolean effect, int amount, final CardCollectionView exclude) {
|
||||
if (amount == 1) {
|
||||
Card source = ability.getHostCard();
|
||||
CardCollection cardList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, null);
|
||||
cardList = CardLists.filter(cardList, CardPredicates.canBeSacrificedBy(ability));
|
||||
cardList = CardLists.filter(cardList, CardPredicates.canBeSacrificedBy(ability, effect));
|
||||
if (cardList.size() >= 2) {
|
||||
if (interceptor != null) {
|
||||
return new CardCollection(interceptor.chooseCard(cardList));
|
||||
@@ -450,7 +450,7 @@ public class SpellAbilityPicker {
|
||||
}
|
||||
}
|
||||
}
|
||||
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), amount, exclude);
|
||||
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), effect, amount, exclude);
|
||||
}
|
||||
|
||||
public static class PlayLandAbility extends LandAbility {
|
||||
|
||||
@@ -93,14 +93,14 @@ public class ForgeScript {
|
||||
return !cardState.getTypeWithChanges().hasSubtype(subType);
|
||||
} else if (property.equals("hasActivatedAbilityWithTapCost")) {
|
||||
for (final SpellAbility sa : cardState.getSpellAbilities()) {
|
||||
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
|
||||
if (sa.isActivatedAbility() && sa.getPayCosts().hasTapCost()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (property.equals("hasActivatedAbility")) {
|
||||
for (final SpellAbility sa : cardState.getSpellAbilities()) {
|
||||
if (sa.isAbility()) {
|
||||
if (sa.isActivatedAbility()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -116,8 +116,8 @@ public class ForgeScript {
|
||||
}
|
||||
return false;
|
||||
} else if (property.equals("hasNonManaActivatedAbility")) {
|
||||
for (final SpellAbility sa : cardState.getSpellAbilities()) {
|
||||
if (sa.isAbility() && !sa.isManaAbility()) {
|
||||
for (final SpellAbility sa : cardState.getNonManaAbilities()) {
|
||||
if (sa.isActivatedAbility()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,14 +1452,15 @@ public class GameAction {
|
||||
if (!c.getType().hasSubtype("Saga")) {
|
||||
return false;
|
||||
}
|
||||
if (!c.canBeSacrificed()) {
|
||||
if (!c.canBeSacrificedBy(null, true)) {
|
||||
return false;
|
||||
}
|
||||
if (c.getCounters(CounterEnumType.LORE) < c.getFinalChapterNr()) {
|
||||
return false;
|
||||
}
|
||||
if (!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) {
|
||||
sacrifice(c, null, table, null);
|
||||
// needs to be effect, because otherwise it might be a cost?
|
||||
sacrifice(c, null, true, table, null);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -1759,8 +1760,8 @@ public class GameAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||
if (!c.canBeSacrificedBy(source)) {
|
||||
public final Card sacrifice(final Card c, final SpellAbility source, final boolean effect, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||
if (!c.canBeSacrificedBy(source, effect)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -682,13 +682,13 @@ public abstract class SpellAbilityEffect {
|
||||
};
|
||||
}
|
||||
|
||||
protected static void discard(SpellAbility sa, CardZoneTable table, Map<Player, CardCollectionView> discardedMap) {
|
||||
protected static void discard(SpellAbility sa, CardZoneTable table, final boolean effect, Map<Player, CardCollectionView> discardedMap) {
|
||||
Set<Player> discarders = discardedMap.keySet();
|
||||
for (Player p : discarders) {
|
||||
final CardCollection discardedByPlayer = new CardCollection();
|
||||
for (Card card : Lists.newArrayList(discardedMap.get(p))) { // without copying will get concurrent modification exception
|
||||
if (card == null) { continue; }
|
||||
if (p.discard(card, sa, table) != null) {
|
||||
if (p.discard(card, sa, effect, table) != null) {
|
||||
discardedByPlayer.add(card);
|
||||
|
||||
if (sa.hasParam("RememberDiscarded")) {
|
||||
|
||||
@@ -61,13 +61,13 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
} else { // Battlefield
|
||||
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||
if ( null == card ) continue;
|
||||
game.getAction().sacrifice(card, sa, table, params);
|
||||
game.getAction().sacrifice(card, sa, true, table, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (zone.equals(ZoneType.Hand)) {
|
||||
discard(sa, table, discardedMap);
|
||||
discard(sa, table, true, discardedMap);
|
||||
}
|
||||
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.event.GameEventCardModeChosen;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -52,17 +53,10 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
if (!saChoice.getRestrictions().checkOtherRestrictions(host, saChoice, sa.getActivatingPlayer()) ) {
|
||||
saToRemove.add(saChoice);
|
||||
} else if (saChoice.hasParam("UnlessCost")) {
|
||||
String unlessCost = saChoice.getParam("UnlessCost");
|
||||
// Sac a permanent in presence of Sigarda, Host of Herons
|
||||
// TODO: generalize this by testing if the unless cost can be paid
|
||||
if (unlessCost.startsWith("Sac<")) {
|
||||
if (!p.canSacrificeBy(saChoice)) {
|
||||
saToRemove.add(saChoice);
|
||||
}
|
||||
} else if (unlessCost.startsWith("Discard<")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
saToRemove.add(saChoice);
|
||||
}
|
||||
// generic check for if the cost can be paid
|
||||
Cost unlessCost = new Cost(saChoice.getParam("UnlessCost"), false);
|
||||
if (!unlessCost.canPay(sa, p, true)) {
|
||||
saToRemove.add(saChoice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
card.addRemembered(gameCard.getAttachedCards());
|
||||
}
|
||||
if (sac) {
|
||||
destroyed = game.getAction().sacrifice(gameCard, sa, table, params) != null;
|
||||
destroyed = game.getAction().sacrifice(gameCard, sa, true, table, params) != null;
|
||||
} else {
|
||||
destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table, params);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
final String mode = sa.getParam("Mode");
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final Iterable<Player> tgtPlayers = Iterables.filter(getTargetPlayers(sa), PlayerPredicates.canDiscardBy(sa));
|
||||
final Iterable<Player> tgtPlayers = Iterables.filter(getTargetPlayers(sa), PlayerPredicates.canDiscardBy(sa, true));
|
||||
|
||||
if (!Iterables.isEmpty(tgtPlayers)) {
|
||||
sb.append(Lang.joinHomogenous(tgtPlayers)).append(" ");
|
||||
@@ -127,12 +127,12 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
for (final Player p : discarders) {
|
||||
CardCollectionView toBeDiscarded = new CardCollection();
|
||||
if ((mode.equals("RevealTgtChoose") && firstTarget != null) || !sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||
if (sa.hasParam("RememberDiscarder") && p.canDiscardBy(sa)) {
|
||||
if (sa.hasParam("RememberDiscarder") && p.canDiscardBy(sa, true)) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
final int numCardsInHand = p.getCardsIn(ZoneType.Hand).size();
|
||||
if (mode.equals("Defined")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,10 +148,12 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (mode.equals("Hand")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
toBeDiscarded = p.getCardsIn(ZoneType.Hand);
|
||||
|
||||
// Empty hand can still be discarded
|
||||
if (!toBeDiscarded.isEmpty() && !p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
toBeDiscarded = p.getCardsIn(ZoneType.Hand);
|
||||
|
||||
if (toBeDiscarded.size() > 1) {
|
||||
toBeDiscarded = GameActionUtil.orderCardsByTheirOwners(game, toBeDiscarded, ZoneType.Graveyard, sa);
|
||||
@@ -159,7 +161,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (mode.equals("NotRemembered")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
toBeDiscarded = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), "Card.IsNotRemembered", p, source, sa);
|
||||
@@ -175,7 +177,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (mode.equals("Random")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
String message = Localizer.getInstance().getMessage("lblWouldYouLikeRandomDiscardTargetCard", String.valueOf(numCards));
|
||||
@@ -202,7 +204,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
else if (mode.equals("TgtChoose") && sa.hasParam("UnlessType")) {
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
if (numCardsInHand > 0) {
|
||||
@@ -223,7 +225,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
opp.getController().reveal(dPHand, ZoneType.Hand, p, Localizer.getInstance().getMessage("lblReveal") + " ");
|
||||
}
|
||||
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -265,7 +267,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
game.getAction().reveal(dPHand, p);
|
||||
}
|
||||
|
||||
if (!p.canDiscardBy(sa)) {
|
||||
if (!p.canDiscardBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -286,7 +288,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
discardedMap.put(p, toBeDiscarded);
|
||||
}
|
||||
|
||||
discard(sa, table, discardedMap);
|
||||
discard(sa, table, true, discardedMap);
|
||||
|
||||
// run trigger if something got milled
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
|
||||
@@ -68,7 +68,7 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
||||
// gameCard is LKI in that case, the card is not in game anymore
|
||||
// or the timestamp did change
|
||||
// this should check Self too
|
||||
if (gameCard == null || !sac.equalsWithTimestamp(gameCard) || !gameCard.canBeSacrificedBy(sa)) {
|
||||
if (gameCard == null || !sac.equalsWithTimestamp(gameCard) || !gameCard.canBeSacrificedBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
gameList.add(gameCard);
|
||||
@@ -92,7 +92,7 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
||||
|
||||
for (Card sac : list) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||
if (game.getAction().sacrifice(sac, sa, table, params) != null) {
|
||||
if (game.getAction().sacrifice(sac, sa, true, table, params) != null) {
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(lKICopy);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
||||
if (!optional || activator.getController().confirmAction(sa, null,
|
||||
Localizer.getInstance().getMessage("lblDoYouWantSacrificeThis", card.getName()))) {
|
||||
if (game.getAction().sacrifice(card, sa, table, params) != null) {
|
||||
if (game.getAction().sacrifice(card, sa, true, table, params) != null) {
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(card);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
List<CardCollection> validTargetsList = new ArrayList<>(validArray.length);
|
||||
for (String subValid : validArray) {
|
||||
CardCollectionView validTargets = AbilityUtils.filterListByType(battlefield, subValid, sa);
|
||||
validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa));
|
||||
validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa, true));
|
||||
validTargetsList.add(new CardCollection(validTargets));
|
||||
}
|
||||
CardCollection chosenCards = new CardCollection();
|
||||
@@ -146,7 +146,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
} else {
|
||||
CardCollectionView validTargets = AbilityUtils.filterListByType(battlefield, valid, sa);
|
||||
if (!destroy) {
|
||||
validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa));
|
||||
validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa, true));
|
||||
}
|
||||
|
||||
if (sa.hasParam("Random")) {
|
||||
@@ -175,13 +175,13 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (Card sac : choosenToSacrifice) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table, params) != null;
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, true, table, params) != null;
|
||||
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table, params);
|
||||
// Run Devour Trigger
|
||||
if (devour) {
|
||||
card.addDevoured(lKICopy);
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.Devoured, sac);
|
||||
runParams.put(AbilityKey.Devoured, lKICopy);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Devoured, runParams, false);
|
||||
}
|
||||
if (exploit) {
|
||||
|
||||
@@ -43,7 +43,6 @@ import forge.game.ability.ApiType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatLki;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.event.*;
|
||||
import forge.game.event.GameEventCardDamaged.DamageType;
|
||||
import forge.game.keyword.*;
|
||||
@@ -57,6 +56,7 @@ import forge.game.spellability.*;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityCantAttackBlock;
|
||||
import forge.game.staticability.StaticAbilityCantPutCounter;
|
||||
import forge.game.staticability.StaticAbilityCantSacrifice;
|
||||
import forge.game.staticability.StaticAbilityCantTransform;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -5946,10 +5946,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return isInPlay() && !isPhasedOut() && (!hasKeyword(Keyword.INDESTRUCTIBLE) || (isCreature() && getNetToughness() <= 0));
|
||||
}
|
||||
|
||||
public final boolean canBeSacrificed() {
|
||||
return isInPlay() && !isPhasedOut() && !hasKeyword("CARDNAME can't be sacrificed.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canBeTargetedBy(final SpellAbility sa) {
|
||||
if (getOwner().hasLost()) {
|
||||
@@ -6284,29 +6280,16 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return this.lkiCMC >= 0;
|
||||
}
|
||||
|
||||
public final boolean canBeSacrificedBy(final SpellAbility source) {
|
||||
public final boolean canBeSacrificedBy(final SpellAbility source, final boolean effect) {
|
||||
if (isImmutable()) {
|
||||
System.out.println("Trying to sacrifice immutables: " + this);
|
||||
return false;
|
||||
}
|
||||
if (!canBeSacrificed()) {
|
||||
if (!isInPlay() || isPhasedOut()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((source.isSpell() || source.isActivatedAbility()) && source.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
|
||||
if (isCreature() && source.getActivatingPlayer().hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPermanent() && !isLand() && source.getActivatingPlayer().hasKeyword("You can't sacrifice nonland permanents to cast spells or activate abilities.")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return getController().canSacrificeBy(source);
|
||||
return !StaticAbilityCantSacrifice.cantSacrifice(this, source, effect);
|
||||
}
|
||||
|
||||
public CardRules getRules() {
|
||||
@@ -6828,12 +6811,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return n;
|
||||
}
|
||||
|
||||
public boolean canBeDiscardedBy(SpellAbility sa) {
|
||||
public boolean canBeDiscardedBy(SpellAbility sa, final boolean effect) {
|
||||
if (!isInZone(ZoneType.Hand)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getOwner().canDiscardBy(sa);
|
||||
return getOwner().canDiscardBy(sa, effect);
|
||||
}
|
||||
|
||||
public void addAbilityActivated(SpellAbility ability) {
|
||||
|
||||
@@ -251,11 +251,11 @@ public final class CardPredicates {
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> canBeSacrificedBy(final SpellAbility sa) {
|
||||
public static final Predicate<Card> canBeSacrificedBy(final SpellAbility sa, final boolean effect) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.canBeSacrificedBy(sa);
|
||||
return c.canBeSacrificedBy(sa, effect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -392,7 +392,8 @@ public class CardProperty {
|
||||
}
|
||||
}
|
||||
} else if (property.equals("CanBeSacrificedBy") && spellAbility instanceof SpellAbility) {
|
||||
if (!card.canBeSacrificedBy((SpellAbility) spellAbility)) {
|
||||
// used for Emerge and Offering, these are SpellCost, not effect
|
||||
if (!card.canBeSacrificedBy((SpellAbility) spellAbility, false)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("AttachedBy")) {
|
||||
|
||||
@@ -939,12 +939,12 @@ public class Cost implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPay(SpellAbility sa) {
|
||||
return canPay(sa, sa.getActivatingPlayer());
|
||||
public boolean canPay(SpellAbility sa, final boolean effect) {
|
||||
return canPay(sa, sa.getActivatingPlayer(), effect);
|
||||
}
|
||||
public boolean canPay(SpellAbility sa, Player payer) {
|
||||
public boolean canPay(SpellAbility sa, Player payer, final boolean effect) {
|
||||
for (final CostPart part : this.getCostParts()) {
|
||||
if (!part.canPay(sa, payer)) {
|
||||
if (!part.canPay(sa, payer, effect)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -968,14 +968,14 @@ public class Cost implements Serializable {
|
||||
return xCost;
|
||||
}
|
||||
|
||||
public Integer getMaxForNonManaX(final SpellAbility ability, final Player payer) {
|
||||
public Integer getMaxForNonManaX(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
Integer val = null;
|
||||
for (CostPart p : getCostParts()) {
|
||||
if (!p.getAmount().equals("X")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val = ObjectUtils.min(val, p.getMaxAmountX(ability, payer));
|
||||
val = ObjectUtils.min(val, p.getMaxAmountX(ability, payer, effect));
|
||||
}
|
||||
// extra 0 check
|
||||
if (val != null && val <= 0 && hasManaCost() && !getCostMana().canXbe0()) {
|
||||
|
||||
@@ -68,12 +68,12 @@ public class CostAddMana extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility sa, final boolean effect) {
|
||||
Card source = sa.getHostCard();
|
||||
|
||||
List<Mana> manaProduced = new ArrayList<>();
|
||||
|
||||
@@ -292,7 +292,7 @@ public class CostAdjustment {
|
||||
|
||||
Card toSac = null;
|
||||
CardCollectionView canOffer = CardLists.filter(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.isType(offeringType), CardPredicates.canBeSacrificedBy(sa));
|
||||
CardPredicates.isType(offeringType), CardPredicates.canBeSacrificedBy(sa, false));
|
||||
|
||||
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canOffer, offeringType);
|
||||
|
||||
@@ -309,7 +309,7 @@ public class CostAdjustment {
|
||||
|
||||
private static void adjustCostByEmerge(final ManaCostBeingPaid cost, final SpellAbility sa) {
|
||||
Card toSac = null;
|
||||
CardCollectionView canEmerge = CardLists.filter(sa.getActivatingPlayer().getCreaturesInPlay(), CardPredicates.canBeSacrificedBy(sa));
|
||||
CardCollectionView canEmerge = CardLists.filter(sa.getActivatingPlayer().getCreaturesInPlay(), CardPredicates.canBeSacrificedBy(sa, false));
|
||||
|
||||
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, "Creature");
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ public class CostChooseCreatureType extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa, final boolean effect) {
|
||||
sa.getHostCard().setChosenType(pd.type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,12 +60,12 @@ public class CostDamage extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision decision, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player payer, PaymentDecision decision, SpellAbility sa, final boolean effect) {
|
||||
final Card source = sa.getHostCard();
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
CardDamageMap preventMap = new CardDamageMap();
|
||||
|
||||
@@ -5,9 +5,14 @@ import forge.game.player.Player;
|
||||
public abstract class CostDecisionMakerBase implements ICostVisitor<PaymentDecision> {
|
||||
|
||||
protected final Player player;
|
||||
public CostDecisionMakerBase(Player player0) {
|
||||
private boolean effect;
|
||||
public CostDecisionMakerBase(Player player0, boolean effect0) {
|
||||
player = player0;
|
||||
effect = effect0;
|
||||
}
|
||||
public Player getPlayer() { return player; }
|
||||
public abstract boolean paysRightAfterDecision();
|
||||
public boolean isEffect() {
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@ public class CostDiscard extends CostPartWithList {
|
||||
|
||||
protected boolean firstTime = false;
|
||||
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@@ -66,10 +63,10 @@ public class CostDiscard extends CostPartWithList {
|
||||
public int paymentOrder() { return 10; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
String type = this.getType();
|
||||
CardCollectionView handList = payer.canDiscardBy(ability) ? payer.getCardsIn(ZoneType.Hand) : CardCollection.EMPTY;
|
||||
CardCollectionView handList = payer.canDiscardBy(ability, effect) ? payer.getCardsIn(ZoneType.Hand) : CardCollection.EMPTY;
|
||||
|
||||
if (!type.equals("Random")) {
|
||||
handList = CardLists.getValidCards(handList, type.split(";"), payer, source, ability);
|
||||
@@ -128,19 +125,23 @@ public class CostDiscard extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
CardCollectionView handList = payer.canDiscardBy(ability) ? payer.getCardsIn(ZoneType.Hand) : CardCollection.EMPTY;
|
||||
CardCollectionView handList = payer.canDiscardBy(ability, effect) ? payer.getCardsIn(ZoneType.Hand) : CardCollection.EMPTY;
|
||||
String type = this.getType();
|
||||
final Integer amount = this.convertAmount();
|
||||
final int amount = getAbilityAmount(ability);
|
||||
|
||||
if (this.payCostFromSource()) {
|
||||
return source.canBeDiscardedBy(ability);
|
||||
return source.canBeDiscardedBy(ability, effect);
|
||||
}
|
||||
else {
|
||||
if (type.equals("Hand")) {
|
||||
return payer.canDiscardBy(ability);
|
||||
// trying to discard an empty hand always work even with Tamiyo
|
||||
if (payer.getZone(ZoneType.Hand).isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return payer.canDiscardBy(ability, effect);
|
||||
// this will always work
|
||||
}
|
||||
else if (type.equals("LastDrawn")) {
|
||||
@@ -152,7 +153,7 @@ public class CostDiscard extends CostPartWithList {
|
||||
for (Card c : handList) {
|
||||
cardNames.add(c.getName());
|
||||
}
|
||||
return amount != null && cardNames.size() >= amount;
|
||||
return cardNames.size() >= amount;
|
||||
}
|
||||
else {
|
||||
boolean sameName = false;
|
||||
@@ -180,7 +181,7 @@ public class CostDiscard extends CostPartWithList {
|
||||
}
|
||||
}
|
||||
|
||||
if ((amount != null) && (amount > handList.size() - adjustment)) {
|
||||
if (amount > handList.size() - adjustment) {
|
||||
// not enough cards in hand to pay
|
||||
return false;
|
||||
}
|
||||
@@ -193,7 +194,7 @@ public class CostDiscard extends CostPartWithList {
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
if (ability.isCycling() && targetCard.equals(ability.getHostCard())) {
|
||||
// discard itself for cycling cost
|
||||
@@ -201,7 +202,7 @@ public class CostDiscard extends CostPartWithList {
|
||||
}
|
||||
// if this is caused by 118.12 it's also an effect
|
||||
SpellAbility cause = targetCard.getGame().getStack().isResolving(ability.getHostCard()) ? ability : null;
|
||||
return targetCard.getController().discard(targetCard, cause, null, runParams);
|
||||
return targetCard.getController().discard(targetCard, cause, effect, null, runParams);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
@@ -27,9 +26,7 @@ import forge.game.spellability.SpellAbility;
|
||||
* The Class CostDraw.
|
||||
*/
|
||||
public class CostDraw extends CostPart {
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@@ -66,10 +63,8 @@ public class CostDraw extends CostPart {
|
||||
PlayerCollection res = new PlayerCollection();
|
||||
String type = this.getType();
|
||||
final Card source = ability.getHostCard();
|
||||
Integer c = convertAmount();
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, getAmount(), ability);
|
||||
}
|
||||
|
||||
int c = this.getAbilityAmount(ability);
|
||||
|
||||
for (Player p : payer.getGame().getPlayers()) {
|
||||
if (p.isValid(type, payer, source, ability) && p.canDrawAmount(c)) {
|
||||
@@ -87,7 +82,7 @@ public class CostDraw extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return !getPotentialPlayers(payer, ability).isEmpty();
|
||||
}
|
||||
|
||||
@@ -98,7 +93,7 @@ public class CostDraw extends CostPart {
|
||||
* forge.Card, forge.card.cost.Cost_Payment)
|
||||
*/
|
||||
@Override
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability) {
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
for (final Player p : decision.players) {
|
||||
p.drawCards(decision.c, ability);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,6 @@ import forge.game.zone.ZoneType;
|
||||
*/
|
||||
public class CostExert extends CostPartWithList {
|
||||
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@@ -81,7 +78,7 @@ public class CostExert extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
if (!this.payCostFromSource()) {
|
||||
@@ -89,17 +86,17 @@ public class CostExert extends CostPartWithList {
|
||||
|
||||
CardCollectionView typeList = payer.getCardsIn(ZoneType.Battlefield);
|
||||
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), payer, source, ability);
|
||||
final Integer amount = this.convertAmount();
|
||||
final int amount = this.getAbilityAmount(ability);
|
||||
|
||||
|
||||
return needsAnnoucement || (amount == null) || (typeList.size() >= amount);
|
||||
return needsAnnoucement || (typeList.size() >= amount);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
targetCard.exert();
|
||||
return targetCard;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -61,7 +60,7 @@ public class CostExile extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
@@ -126,7 +125,7 @@ public class CostExile extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
@@ -153,22 +152,18 @@ public class CostExile extends CostPartWithList {
|
||||
list = CardLists.getValidCards(list, type.split(";"), payer, source, ability);
|
||||
}
|
||||
|
||||
Integer amount = this.convertAmount();
|
||||
|
||||
if (amount == null) { // try to calculate when it's defined.
|
||||
amount = AbilityUtils.calculateAmount(ability.getHostCard(), getAmount(), ability);
|
||||
}
|
||||
int amount = this.getAbilityAmount(ability);
|
||||
|
||||
// for cards like Allosaurus Rider, do not count it
|
||||
if (this.from == ZoneType.Hand && source.isInZone(ZoneType.Hand) && list.contains(source)) {
|
||||
amount++;
|
||||
}
|
||||
|
||||
if (amount != null && list.size() < amount) {
|
||||
if (list.size() < amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.sameZone && amount != null) {
|
||||
if (this.sameZone) {
|
||||
boolean foundPayable = false;
|
||||
FCollectionView<Player> players = game.getPlayers();
|
||||
for (Player p : players) {
|
||||
@@ -183,7 +178,7 @@ public class CostExile extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
final Game game = targetCard.getGame();
|
||||
Card newCard = game.getAction().exile(targetCard, null);
|
||||
newCard.setExiledWith(ability.getHostCard());
|
||||
|
||||
@@ -32,9 +32,6 @@ import forge.game.zone.ZoneType;
|
||||
public class CostExileFromStack extends CostPart {
|
||||
// ExileFromStack<Num/Type{/TypeDescription}>
|
||||
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@@ -81,7 +78,7 @@ public class CostExileFromStack extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
String type = this.getType();
|
||||
@@ -93,8 +90,8 @@ public class CostExileFromStack extends CostPart {
|
||||
|
||||
list = CardLists.getValidCards(list, type.split(";"), payer, source, ability);
|
||||
|
||||
final Integer amount = this.convertAmount();
|
||||
return (amount == null) || (list.size() >= amount);
|
||||
final int amount = this.getAbilityAmount(ability);
|
||||
return list.size() >= amount;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -104,7 +101,7 @@ public class CostExileFromStack extends CostPart {
|
||||
* forge.Card, forge.card.cost.Cost_Payment)
|
||||
*/
|
||||
@Override
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability) {
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
Game game = ai.getGame();
|
||||
for (final SpellAbility sa : decision.sp) {
|
||||
SpellAbilityStackInstance si = game.getStack().getInstanceFromSpellAbility(sa);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -43,7 +42,7 @@ public class CostExiledMoveToGrave extends CostPartWithList {
|
||||
public int paymentOrder() { return 15; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
CardCollectionView typeList = payer.getGame().getCardsIn(ZoneType.Exile);
|
||||
|
||||
@@ -76,20 +75,15 @@ public class CostExiledMoveToGrave extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
final Card source = ability.getHostCard();
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
|
||||
Integer i = convertAmount();
|
||||
int i = getAbilityAmount(ability);
|
||||
|
||||
if (i == null) {
|
||||
i = AbilityUtils.calculateAmount(source, getAmount(), ability);
|
||||
}
|
||||
|
||||
return getMaxAmountX(ability, payer) >= i;
|
||||
return getMaxAmountX(ability, payer, effect) >= i;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
return targetCard.getGame().getAction().moveToGraveyard(targetCard, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public class CostFlipCoin extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class CostFlipCoin extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa, final boolean effect) {
|
||||
int m = FlipCoinEffect.getFlipMultiplier(payer);
|
||||
for (int i = 0; i < pd.c; i++) {
|
||||
FlipCoinEffect.flipCoinCall(payer, sa, m);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -74,23 +73,19 @@ public class CostGainControl extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
CardCollectionView typeList = payer.getGame().getCardsIn(ZoneType.Battlefield);
|
||||
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), payer, source, ability);
|
||||
|
||||
Integer amount = this.convertAmount();
|
||||
if (amount == null) {
|
||||
amount = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
|
||||
}
|
||||
return typeList.size() >= amount;
|
||||
return typeList.size() >= getAbilityAmount(ability);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
targetCard.addTempController(ability.getActivatingPlayer(), ability.getActivatingPlayer().getGame().getNextTimestamp());
|
||||
return targetCard;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -28,9 +28,7 @@ import forge.game.spellability.SpellAbility;
|
||||
* The Class CostGainLife.
|
||||
*/
|
||||
public class CostGainLife extends CostPart {
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final int cntPlayers; // MAX_VALUE means ALL/EACH PLAYERS
|
||||
|
||||
@@ -64,29 +62,19 @@ public class CostGainLife extends CostPart {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public List<Player> getPotentialTargets(final Player payer, final Card source) {
|
||||
List<Player> res = new ArrayList<>();
|
||||
public List<Player> getPotentialTargets(final Player payer, final SpellAbility ability) {
|
||||
List<Player> res = Lists.newArrayList();
|
||||
for (Player p : payer.getGame().getPlayers()) {
|
||||
if (p.isValid(getType(), payer, source, null))
|
||||
if (p.isValid(getType(), payer, ability.getHostCard(), ability))
|
||||
res.add(p);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
final Integer amount = this.convertAmount();
|
||||
if (amount == null) return false;
|
||||
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
int cntAbleToGainLife = 0;
|
||||
List<Player> possibleTargets = getPotentialTargets(payer, ability.getHostCard());
|
||||
List<Player> possibleTargets = getPotentialTargets(payer, ability);
|
||||
|
||||
for (final Player opp : possibleTargets) {
|
||||
if (opp.canGainLife()) {
|
||||
@@ -97,15 +85,9 @@ public class CostGainLife extends CostPart {
|
||||
return cntAbleToGainLife >= cntPlayers || cntPlayers == Integer.MAX_VALUE && cntAbleToGainLife == possibleTargets.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
|
||||
* forge.Card, forge.card.cost.Cost_Payment)
|
||||
*/
|
||||
@Override
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability) {
|
||||
Integer c = this.convertAmount();
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
Integer c = this.getAbilityAmount(ability);
|
||||
|
||||
int playersLeft = cntPlayers;
|
||||
for (final Player opp : decision.players) {
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
@@ -59,17 +56,8 @@ public class CostMill extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
final Card source = ability.getHostCard();
|
||||
final PlayerZone zone = payer.getZone(ZoneType.Library);
|
||||
|
||||
Integer i = this.convertAmount();
|
||||
|
||||
if (i == null) {
|
||||
i = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
|
||||
}
|
||||
|
||||
return i < zone.size();
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return getAbilityAmount(ability) < payer.getZone(ZoneType.Library).size();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,7 +86,7 @@ public class CostMill extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability) {
|
||||
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
ability.getPaidHash().put("Milled", (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table));
|
||||
table.triggerChangesZoneAll(ai.getGame(), ability);
|
||||
|
||||
@@ -74,7 +74,7 @@ public abstract class CostPart implements Comparable<CostPart>, Cloneable, Seria
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer) {
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
@@ -145,6 +145,10 @@ public abstract class CostPart implements Comparable<CostPart>, Cloneable, Seria
|
||||
return StringUtils.isNumeric(amount) ? Integer.parseInt(amount) : null;
|
||||
}
|
||||
|
||||
public final int getAbilityAmount(SpellAbility ability) {
|
||||
return AbilityUtils.calculateAmount(ability.getHostCard(), getAmount(), ability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can pay.
|
||||
*
|
||||
@@ -153,7 +157,7 @@ public abstract class CostPart implements Comparable<CostPart>, Cloneable, Seria
|
||||
* @param payer
|
||||
* @return true, if successful
|
||||
*/
|
||||
public abstract boolean canPay(SpellAbility ability, Player payer);
|
||||
public abstract boolean canPay(SpellAbility ability, Player payer, boolean effect);
|
||||
|
||||
public abstract <T> T accept(final ICostVisitor<T> visitor);
|
||||
|
||||
@@ -191,7 +195,7 @@ public abstract class CostPart implements Comparable<CostPart>, Cloneable, Seria
|
||||
this.typeDescription = AbilityUtils.applyDescriptionTextChangeEffects(this.originalTypeDescription, trait);
|
||||
}
|
||||
|
||||
public abstract boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa);
|
||||
public abstract boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa, final boolean effect);
|
||||
|
||||
public int paymentOrder() { return 5; }
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ public class CostPartMana extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
// For now, this will always return true. But this should probably be
|
||||
// checked at some point
|
||||
return true;
|
||||
@@ -170,11 +170,11 @@ public class CostPartMana extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa, final boolean effect) {
|
||||
sa.clearManaPaid();
|
||||
|
||||
// decision not used here, the whole payment is interactive!
|
||||
return payer.getController().payManaCost(this, sa, null, cardMatrix, true);
|
||||
return payer.getController().payManaCost(this, sa, null, cardMatrix, effect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -101,10 +101,10 @@ public abstract class CostPartWithList extends CostPart {
|
||||
super(amount, type, description);
|
||||
}
|
||||
|
||||
public final boolean executePayment(SpellAbility ability, Card targetCard) {
|
||||
public final boolean executePayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
lkiList.add(CardUtil.getLKICopy(targetCard));
|
||||
final Zone origin = targetCard.getZone();
|
||||
final Card newCard = doPayment(ability, targetCard);
|
||||
final Card newCard = doPayment(ability, targetCard, effect);
|
||||
|
||||
// need to update the LKI info to ensure correct interaction with cards which may trigger on this
|
||||
// (e.g. Necroskitter + a creature dying from a -1/-1 counter on a cost payment).
|
||||
@@ -122,16 +122,16 @@ public abstract class CostPartWithList extends CostPart {
|
||||
}
|
||||
|
||||
// always returns true, made this to inline with return
|
||||
protected boolean executePayment(Player payer, SpellAbility ability, CardCollectionView targetCards) {
|
||||
protected boolean executePayment(Player payer, SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
|
||||
handleBeforePayment(payer, ability, targetCards);
|
||||
if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes.
|
||||
for (Card c: targetCards) {
|
||||
lkiList.add(CardUtil.getLKICopy(c));
|
||||
}
|
||||
cardList.addAll(doListPayment(ability, targetCards));
|
||||
cardList.addAll(doListPayment(ability, targetCards, effect));
|
||||
} else {
|
||||
for (Card c : targetCards) {
|
||||
executePayment(ability, c);
|
||||
executePayment(ability, c, effect);
|
||||
}
|
||||
}
|
||||
handleChangeZoneTrigger(payer, ability, targetCards);
|
||||
@@ -144,10 +144,10 @@ public abstract class CostPartWithList extends CostPart {
|
||||
* @param targetCard the {@link Card} to pay with.
|
||||
* @return The physical card after the payment.
|
||||
*/
|
||||
protected abstract Card doPayment(SpellAbility ability, Card targetCard);
|
||||
protected abstract Card doPayment(SpellAbility ability, Card targetCard, final boolean effect);
|
||||
// Overload these two only together, set to true and perform payment on list
|
||||
protected boolean canPayListAtOnce() { return false; }
|
||||
protected CardCollectionView doListPayment(SpellAbility ability, CardCollectionView targetCards) { return CardCollection.EMPTY; }
|
||||
protected CardCollectionView doListPayment(SpellAbility ability, CardCollectionView targetCards, final boolean effect) { return CardCollection.EMPTY; }
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
@@ -157,8 +157,8 @@ public abstract class CostPartWithList extends CostPart {
|
||||
public abstract String getHashForCardList();
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
executePayment(ai, ability, decision.cards);
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
executePayment(ai, ability, decision.cards, effect);
|
||||
reportPaidCardsTo(ability);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package forge.game.cost;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.player.Player;
|
||||
@@ -47,7 +46,8 @@ public class CostPayEnergy extends CostPart {
|
||||
@Override
|
||||
public int paymentOrder() { return 7; }
|
||||
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer) {
|
||||
@Override
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return payer.getCounters(CounterEnumType.ENERGY);
|
||||
}
|
||||
|
||||
@@ -83,17 +83,12 @@ public class CostPayEnergy extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
Integer amount = this.convertAmount();
|
||||
if (amount == null) { // try to calculate when it's defined.
|
||||
amount = AbilityUtils.calculateAmount(ability.getHostCard(), getAmount(), ability);
|
||||
}
|
||||
|
||||
return payer.getCounters(CounterEnumType.ENERGY) >= amount;
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return payer.getCounters(CounterEnumType.ENERGY) >= this.getAbilityAmount(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
paidAmount = decision.c;
|
||||
return ai.payEnergy(paidAmount, null);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -62,36 +61,16 @@ public class CostPayLife extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
if (!payer.canPayLife(1)) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
if (!payer.canPayLife(1, effect)) {
|
||||
return 0;
|
||||
}
|
||||
return payer.getLife();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
Integer amount = this.convertAmount();
|
||||
if (amount == null) { // try to calculate when it's defined.
|
||||
amount = AbilityUtils.calculateAmount(ability.getHostCard(), getAmount(), ability);
|
||||
// CR 107.1b
|
||||
if (getAmount().contains("/Half")) {
|
||||
amount = Math.max(amount, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (amount != null && !payer.canPayLife(amount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ability.isTrigger() && payer.hasKeyword("You can't pay life to cast spells or activate abilities.")) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
if (!payer.canPayLife(this.getAbilityAmount(ability), effect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -99,8 +78,8 @@ public class CostPayLife extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
return ai.payLife(decision.c, null);
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
return ai.payLife(decision.c, null, effect);
|
||||
}
|
||||
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
|
||||
@@ -92,7 +92,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
}
|
||||
|
||||
cost = CostAdjustment.adjust(cost, ability);
|
||||
return cost.canPay(ability);
|
||||
return cost.canPay(ability, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +146,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
((CostPartMana)part).setCardMatrix(this);
|
||||
}
|
||||
|
||||
if (pd == null || !part.payAsDecided(decisionMaker.getPlayer(), pd, ability)) {
|
||||
if (pd == null || !part.payAsDecided(decisionMaker.getPlayer(), pd, ability, decisionMaker.isEffect())) {
|
||||
if (part instanceof CostPartMana) {
|
||||
((CostPartMana)part).setCardMatrix(null);
|
||||
}
|
||||
@@ -194,7 +194,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
|
||||
// wrap the payment and push onto the cost stack
|
||||
game.costPaymentStack.push(part, this);
|
||||
if ((decisionMaker.paysRightAfterDecision() || payImmediately) && !part.payAsDecided(decisionMaker.getPlayer(), decision, ability)) {
|
||||
if ((decisionMaker.paysRightAfterDecision() || payImmediately) && !part.payAsDecided(decisionMaker.getPlayer(), decision, ability, decisionMaker.isEffect())) {
|
||||
game.costPaymentStack.pop(); // cost is resolved
|
||||
return false;
|
||||
}
|
||||
@@ -207,7 +207,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
// wrap the payment and push onto the cost stack
|
||||
game.costPaymentStack.push(part, this);
|
||||
|
||||
if (!part.payAsDecided(decisionMaker.getPlayer(), decisions.get(part), this.ability)) {
|
||||
if (!part.payAsDecided(decisionMaker.getPlayer(), decisions.get(part), this.ability, decisionMaker.isEffect())) {
|
||||
game.costPaymentStack.pop(); // cost is resolved
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -119,15 +118,11 @@ public class CostPutCardToLib extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
Integer i = convertAmount();
|
||||
|
||||
if (i == null) {
|
||||
i = AbilityUtils.calculateAmount(source, getAmount(), ability);
|
||||
}
|
||||
int i = getAbilityAmount(ability);
|
||||
|
||||
CardCollectionView typeList;
|
||||
if (sameZone) {
|
||||
@@ -162,7 +157,7 @@ public class CostPutCardToLib extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
return targetCard.getGame().getAction().moveToLibrary(targetCard, Integer.parseInt(getLibPos()),null);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class CostPutCounter extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
if (this.payCostFromSource()) {
|
||||
return source.canReceiveCounters(this.counter);
|
||||
@@ -160,23 +160,20 @@ public class CostPutCounter extends CostPartWithList {
|
||||
* forge.Card, forge.card.cost.Cost_Payment)
|
||||
*/
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
if (this.payCostFromSource()) {
|
||||
executePayment(ability, ability.getHostCard());
|
||||
executePayment(ability, ability.getHostCard(), effect);
|
||||
} else {
|
||||
executePayment(ai, ability, decision.cards);
|
||||
executePayment(ai, ability, decision.cards, effect);
|
||||
}
|
||||
triggerCounterPutAll(ability);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
final Integer i = this.convertAmount();
|
||||
targetCard.addCounter(this.getCounter(), i, ability.getActivatingPlayer(), null, ability.getRootAbility().isTrigger(), counterTable);
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
final int i = this.getAbilityAmount(ability);
|
||||
targetCard.addCounter(this.getCounter(), i, ability.getActivatingPlayer(), null, effect, counterTable);
|
||||
return targetCard;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class CostRemoveAnyCounter extends CostPart {
|
||||
public int paymentOrder() { return 8; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer) {
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
CardCollectionView validCards = CardLists.getValidCards(payer.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), payer, source, ability);
|
||||
@@ -86,8 +86,8 @@ public class CostRemoveAnyCounter extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
return AbilityUtils.calculateAmount(ability.getHostCard(), this.getAmount(), ability) <= getMaxAmountX(ability, payer);
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return AbilityUtils.calculateAmount(ability.getHostCard(), this.getAmount(), ability) <= getMaxAmountX(ability, payer, effect);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -111,7 +111,7 @@ public class CostRemoveAnyCounter extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
int removed = 0;
|
||||
for (Entry<GameEntity, Map<CounterType, Integer>> e : decision.counterTable.row(Optional.absent()).entrySet()) {
|
||||
for (Entry<CounterType, Integer> v : e.getValue().entrySet()) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public class CostRemoveCounter extends CostPart {
|
||||
public int paymentOrder() { return 8; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer) {
|
||||
public Integer getMaxAmountX(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final CounterType cntrs = this.counter;
|
||||
final Card source = ability.getHostCard();
|
||||
final String type = this.getType();
|
||||
@@ -107,12 +107,12 @@ public class CostRemoveCounter extends CostPart {
|
||||
sb.append("-").append(this.getAmount());
|
||||
} else {
|
||||
sb.append("Remove ");
|
||||
final Integer i = this.convertAmount();
|
||||
if (this.getAmount().equals("X")) {
|
||||
sb.append("any number of counters");
|
||||
} else if (this.getAmount().equals("All")) {
|
||||
sb.append("all ").append(this.counter.getName().toLowerCase()).append(" counters");
|
||||
} else {
|
||||
final Integer i = this.convertAmount();
|
||||
sb.append(Cost.convertAmountTypeToWords(i, this.getAmount(),
|
||||
this.counter.getName().toLowerCase() + " counter"));
|
||||
}
|
||||
@@ -137,20 +137,20 @@ public class CostRemoveCounter extends CostPart {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final CounterType cntrs = this.counter;
|
||||
final Card source = ability.getHostCard();
|
||||
final String type = this.getType();
|
||||
|
||||
final Integer amount;
|
||||
final int amount;
|
||||
if (getAmount().equals("All")) {
|
||||
amount = source.getCounters(cntrs);
|
||||
}
|
||||
else {
|
||||
amount = this.convertAmount();
|
||||
amount = getAbilityAmount(ability);
|
||||
}
|
||||
if (this.payCostFromSource()) {
|
||||
return (amount == null) || ((source.getCounters(cntrs) - amount) >= 0);
|
||||
return (source.getCounters(cntrs) - amount) >= 0;
|
||||
}
|
||||
else {
|
||||
List<Card> typeList;
|
||||
@@ -159,22 +159,20 @@ public class CostRemoveCounter extends CostPart {
|
||||
} else {
|
||||
typeList = CardLists.getValidCards(payer.getCardsIn(this.zone), type.split(";"), payer, source, ability);
|
||||
}
|
||||
if (amount != null) {
|
||||
// (default logic) remove X counters from a single permanent
|
||||
for (Card c : typeList) {
|
||||
if (c.getCounters(cntrs) - amount >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// (default logic) remove X counters from a single permanent
|
||||
for (Card c : typeList) {
|
||||
if (c.getCounters(cntrs) - amount >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
int removed = 0;
|
||||
final int toRemove = decision.c;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class CostReturn extends CostPartWithList {
|
||||
public int paymentOrder() { return 10; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
CardCollectionView typeList = payer.getCardsIn(ZoneType.Battlefield);
|
||||
@@ -102,21 +102,20 @@ public class CostReturn extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
if (payCostFromSource()) {
|
||||
return source.isInPlay();
|
||||
}
|
||||
|
||||
final Integer amount = this.convertAmount();
|
||||
return amount == null || getMaxAmountX(ability, payer) >= amount;
|
||||
return getMaxAmountX(ability, payer, effect) >= getAbilityAmount(ability);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
return targetCard.getGame().getAction().moveToHand(targetCard, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class CostReveal extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
CardCollectionView handList = payer.getCardsIn(revealFrom);
|
||||
if (ability.isSpell()) {
|
||||
@@ -78,20 +78,17 @@ public class CostReveal extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
CardCollectionView handList = payer.getCardsIn(revealFrom);
|
||||
final Integer amount = this.convertAmount();
|
||||
final int amount = this.getAbilityAmount(ability);
|
||||
|
||||
if (this.payCostFromSource()) {
|
||||
return revealFrom.contains(source.getLastKnownZone().getZoneType());
|
||||
} else if (this.getType().equals("Hand")) {
|
||||
return true;
|
||||
} else if (this.getType().equals("SameColor")) {
|
||||
if (amount == null) {
|
||||
return false;
|
||||
}
|
||||
for (final Card card : handList) {
|
||||
if (CardLists.filter(handList, new Predicate<Card>() {
|
||||
@Override
|
||||
@@ -104,7 +101,7 @@ public class CostReveal extends CostPartWithList {
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return (amount == null) || (amount <= getMaxAmountX(ability, payer));
|
||||
return amount <= getMaxAmountX(ability, payer, effect);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -151,7 +148,7 @@ public class CostReveal extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
targetCard.getGame().getAction().reveal(new CardCollection(targetCard), ability.getActivatingPlayer());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ability.getActivatingPlayer());
|
||||
@@ -172,7 +169,7 @@ public class CostReveal extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CardCollectionView doListPayment(SpellAbility ability, CardCollectionView targetCards) {
|
||||
protected CardCollectionView doListPayment(SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
|
||||
ability.getActivatingPlayer().getGame().getAction().reveal(targetCards, ability.getActivatingPlayer());
|
||||
return targetCards;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class CostRevealChosenPlayer extends CostPart {
|
||||
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CostRevealChosenPlayer() { }
|
||||
@@ -40,22 +37,15 @@ public class CostRevealChosenPlayer extends CostPart {
|
||||
return "Reveal the player you chose";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player activator) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player activator, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
return source.getChosenPlayer() != null && source.getTurnInController().equals(activator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
ability.getHostCard().revealChosenPlayer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@ import forge.game.spellability.SpellAbility;
|
||||
*/
|
||||
public class CostRollDice extends CostPart {
|
||||
|
||||
/**
|
||||
* Serializables need a version ID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String resultSVar;
|
||||
@@ -27,15 +24,8 @@ public class CostRollDice extends CostPart {
|
||||
this.resultSVar = resultSVar;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,7 +45,7 @@ public class CostRollDice extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) {
|
||||
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa, final boolean effect) {
|
||||
int sides = Integer.parseInt(getType());
|
||||
int result = RollDiceEffect.rollDiceForPlayer(sa, payer, pd.c, sides);
|
||||
sa.setSVar(resultSVar, Integer.toString(result));
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -58,11 +57,11 @@ public class CostSacrifice extends CostPartWithList {
|
||||
public int paymentOrder() { return 15; }
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
CardCollectionView typeList = payer.getCardsIn(ZoneType.Battlefield);
|
||||
typeList = CardLists.getValidCards(typeList, getType().split(";"), payer, source, ability);
|
||||
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
|
||||
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability, effect));
|
||||
return typeList.size();
|
||||
}
|
||||
|
||||
@@ -101,38 +100,35 @@ public class CostSacrifice extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player activator) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player activator, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
if (getType().equals("OriginalHost")) {
|
||||
Card originalEquipment = ability.getOriginalHost();
|
||||
return originalEquipment.isEquipping();
|
||||
return originalEquipment.isEquipping() && originalEquipment.canBeSacrificedBy(ability, effect);
|
||||
}
|
||||
else if (!payCostFromSource()) { // You can always sac all
|
||||
if ("All".equalsIgnoreCase(getAmount())) {
|
||||
CardCollectionView typeList = activator.getCardsIn(ZoneType.Battlefield);
|
||||
typeList = CardLists.getValidCards(typeList, getType().split(";"), activator, source, ability);
|
||||
// it needs to check if everything can be sacrificed
|
||||
return Iterables.all(typeList, CardPredicates.canBeSacrificedBy(ability));
|
||||
return Iterables.all(typeList, CardPredicates.canBeSacrificedBy(ability, effect));
|
||||
}
|
||||
|
||||
Integer amount = this.convertAmount();
|
||||
if (amount == null) {
|
||||
amount = AbilityUtils.calculateAmount(source, getAmount(), ability);
|
||||
}
|
||||
int amount = getAbilityAmount(ability);
|
||||
|
||||
return getMaxAmountX(ability, activator) >= amount;
|
||||
return getMaxAmountX(ability, activator, effect) >= amount;
|
||||
// If amount is null, it's either "ALL" or "X"
|
||||
// if X is defined, it needs to be calculated and checked, if X is
|
||||
// choice, it can be Paid even if it's 0
|
||||
}
|
||||
else return source.canBeSacrificedBy(ability);
|
||||
else return source.canBeSacrificedBy(ability, effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
// no table there, it is already handled by CostPartWithList
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability, null, null);
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability, effect, null, null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -59,13 +59,13 @@ public class CostTap extends CostPart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
return source.isUntapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
|
||||
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) {
|
||||
ability.getHostCard().tap(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class CostTapType extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer) {
|
||||
public Integer getMaxAmountX(SpellAbility ability, Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
// extend if cards use X with different conditions
|
||||
@@ -126,7 +126,7 @@ public class CostTapType extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
String type = this.getType();
|
||||
@@ -169,15 +169,15 @@ public class CostTapType extends CostPartWithList {
|
||||
return CardLists.getTotalPower(typeList, true, ability.hasParam("Crew")) >= i;
|
||||
}
|
||||
|
||||
final Integer amount = this.convertAmount();
|
||||
return (typeList.size() != 0) && ((amount == null) || (typeList.size() >= amount));
|
||||
final int amount = this.getAbilityAmount(ability);
|
||||
return (typeList.size() != 0) && (typeList.size() >= amount);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
targetCard.tap(true);
|
||||
return targetCard;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class CostUnattach extends CostPartWithList {
|
||||
* forge.Card, forge.Player, forge.card.cost.Cost)
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer) {
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
final String type = this.getType();
|
||||
@@ -107,7 +107,7 @@ public class CostUnattach extends CostPartWithList {
|
||||
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
targetCard.unattachFromEntity(targetCard.getEntityAttachedTo());
|
||||
return targetCard;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user