mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Streamline logic so canPlay always before canPay (#8234)
This commit is contained in:
@@ -911,21 +911,8 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
int oldCMC = -1;
|
||||
boolean xCost = sa.costHasX() || host.hasKeyword(Keyword.STRIVE) || sa.getApi() == ApiType.Charm;
|
||||
if (!xCost) {
|
||||
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;
|
||||
}
|
||||
// TODO check for Reduce too, e.g. Battlefield Thaumaturge could make it castable
|
||||
if (!sa.getAllTargetChoices().isEmpty()) {
|
||||
oldCMC = CostAdjustment.adjust(sa.getPayCosts(), sa, false).getTotalMana().getCMC();
|
||||
}
|
||||
}
|
||||
|
||||
AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||
// this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||
AiPlayDecision canPlay = canPlaySa(sa);
|
||||
|
||||
if (canPlay != AiPlayDecision.WillPlay) {
|
||||
return canPlay;
|
||||
@@ -940,9 +927,6 @@ public class AiController {
|
||||
// TODO some older cards don't use the keyword, so check for trigger instead
|
||||
if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(host.getController())) {
|
||||
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
||||
if (wardCost.hasManaCost()) {
|
||||
xCost |= wardCost.getTotalMana().getCMC() > 0;
|
||||
}
|
||||
SpellAbilityAi topAI = new SpellAbilityAi() {};
|
||||
if (!topAI.willPayCosts(player, sa, wardCost, host)) {
|
||||
return AiPlayDecision.CostNotAcceptable;
|
||||
@@ -952,15 +936,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
// check if some target raised cost
|
||||
if (!xCost && oldCMC > -1) {
|
||||
int finalCMC = CostAdjustment.adjust(sa.getPayCosts(), sa, false).getTotalMana().getCMC();
|
||||
if (finalCMC > oldCMC) {
|
||||
xCost = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (xCost && !ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||
if (!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;
|
||||
@@ -973,8 +949,6 @@ public class AiController {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
|
||||
// if we got here, looks like we can play the final cost and we could properly set up and target the API and
|
||||
// are willing to play the SA
|
||||
return AiPlayDecision.WillPlay;
|
||||
}
|
||||
|
||||
|
||||
@@ -666,6 +666,7 @@ public class ComputerUtilMana {
|
||||
return true;
|
||||
}
|
||||
|
||||
int phyLifeToPay = 2;
|
||||
boolean purePhyrexian = cost.containsOnlyPhyrexianMana();
|
||||
boolean hasConverge = sa.getHostCard().hasConverge();
|
||||
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = getSourcesForShards(cost, sa, ai, test, checkPlayable, hasConverge);
|
||||
@@ -693,13 +694,12 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
if (sourcesForShards == null && !purePhyrexian) {
|
||||
break; // no mana abilities to use for paying
|
||||
// no mana abilities to use for paying
|
||||
break;
|
||||
}
|
||||
|
||||
toPay = getNextShardToPay(cost, sourcesForShards);
|
||||
|
||||
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
|
||||
|
||||
Collection<SpellAbility> saList = null;
|
||||
if (hasConverge &&
|
||||
(toPay == ManaCostShard.GENERIC || toPay == ManaCostShard.X)) {
|
||||
@@ -752,9 +752,14 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
if (saPayment == null) {
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(2, false, sa)
|
||||
|| (ai.getLife() <= 2 && !ai.cantLoseForZeroOrLessLife())) {
|
||||
break; // cannot pay
|
||||
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
|
||||
if ((!toPay.isPhyrexian() && !lifeInsteadOfBlack) || !ai.canPayLife(phyLifeToPay, false, sa)
|
||||
|| (ai.getLife() <= phyLifeToPay && !ai.cantLoseForZeroOrLessLife())) {
|
||||
// cannot pay
|
||||
break;
|
||||
}
|
||||
if (test) {
|
||||
phyLifeToPay += 2;
|
||||
}
|
||||
|
||||
if (sa.hasParam("AIPhyrexianPayment")) {
|
||||
|
||||
@@ -551,10 +551,10 @@ public class ManaCostBeingPaid {
|
||||
// The generic portion of a 2/Colored mana, should be lower priority than generic mana
|
||||
return !ColorSet.fromMask(bill.getColorMask() & paymentColor).isColorless() ? 9 : 1;
|
||||
}
|
||||
if (!bill.isPhyrexian()) {
|
||||
return 10;
|
||||
if (bill.isPhyrexian()) {
|
||||
return 8;
|
||||
}
|
||||
return 8;
|
||||
return 10;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
@@ -47,11 +47,9 @@ public class InputPayManaOfCostPayment extends InputPayMana {
|
||||
if (manaCost.payPhyrexian()) {
|
||||
saPaidFor.setSpendPhyrexianMana(true);
|
||||
this.phyLifeToLose += 2;
|
||||
} else {
|
||||
if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) {
|
||||
manaCost.decreaseShard(ManaCostShard.BLACK, 1);
|
||||
this.phyLifeToLose += 2;
|
||||
}
|
||||
} else if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) {
|
||||
manaCost.decreaseShard(ManaCostShard.BLACK, 1);
|
||||
this.phyLifeToLose += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user