mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
- Complete the basic implementation of AI optional cost choice, remove getOptionalCosts.
This commit is contained in:
@@ -849,20 +849,22 @@ public class AiController {
|
||||
|
||||
int neededMana = 0;
|
||||
boolean dangerousRecurringCost = false;
|
||||
for (SpellAbility sa2 : GameActionUtil.getOptionalCosts(sa)) {
|
||||
if (sa2.isOptionalCostPaid(OptionalCost.Buyback)) {
|
||||
Cost sac = sa2.getPayCosts();
|
||||
CostAdjustment.adjust(sac, sa2);
|
||||
if (sac.getCostMana() != null) {
|
||||
neededMana = sac.getCostMana().getMana().getCMC();
|
||||
}
|
||||
if (sac.hasSpecificCostType(CostPayLife.class)
|
||||
|| sac.hasSpecificCostType(CostDiscard.class)
|
||||
|| sac.hasSpecificCostType(CostSacrifice.class)) {
|
||||
dangerousRecurringCost = true;
|
||||
}
|
||||
|
||||
Cost costWithBuyback = sa.getPayCosts().copy();
|
||||
for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) {
|
||||
if (opt.getType() == OptionalCost.Buyback) {
|
||||
costWithBuyback.add(opt.getCost());
|
||||
}
|
||||
}
|
||||
CostAdjustment.adjust(costWithBuyback, sa);
|
||||
if (costWithBuyback.getCostMana() != null) {
|
||||
neededMana = costWithBuyback.getCostMana().getMana().getCMC();
|
||||
}
|
||||
if (costWithBuyback.hasSpecificCostType(CostPayLife.class)
|
||||
|| costWithBuyback.hasSpecificCostType(CostDiscard.class)
|
||||
|| costWithBuyback.hasSpecificCostType(CostSacrifice.class)) {
|
||||
dangerousRecurringCost = true;
|
||||
}
|
||||
|
||||
// won't be able to afford buyback any time soon
|
||||
// if Buyback cost includes sacrifice, life, discard
|
||||
|
||||
@@ -105,17 +105,22 @@ public class ComputerUtilAbility {
|
||||
final List<SpellAbility> result = Lists.newArrayList();
|
||||
for (SpellAbility sa : newAbilities) {
|
||||
sa.setActivatingPlayer(player);
|
||||
// TODO: remove this once all optional costs are ported over to chooseOptionalCosts
|
||||
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
||||
|
||||
// Optional cost selection through the AI controller
|
||||
boolean choseOptCost = false;
|
||||
List<OptionalCostValue> list = GameActionUtil.getOptionalCostValues(sa);
|
||||
if (!list.isEmpty()) {
|
||||
list = player.getController().chooseOptionalCosts(sa, list);
|
||||
if (!list.isEmpty()) {
|
||||
choseOptCost = true;
|
||||
result.add(GameActionUtil.addOptionalCosts(sa, list));
|
||||
}
|
||||
}
|
||||
|
||||
// Add only one ability: either the one with preferred optional costs, or the original one if there are none
|
||||
if (!choseOptCost) {
|
||||
result.add(sa);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1147,19 +1147,20 @@ public class PlayerControllerAi extends PlayerController {
|
||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen,
|
||||
List<OptionalCostValue> optionalCostValues) {
|
||||
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
|
||||
Cost cost = chosen.getPayCosts();
|
||||
Cost costSoFar = chosen.getPayCosts() != null ? chosen.getPayCosts().copy() : Cost.Zero;
|
||||
|
||||
for (OptionalCostValue opt : optionalCostValues) {
|
||||
if (opt.getType() == OptionalCost.Entwine) {
|
||||
// Test implementation: just always choose Entwine
|
||||
Cost fullCost = opt.getCost().copy().add(cost);
|
||||
SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost);
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
|
||||
chosenOptCosts.add(opt);
|
||||
}
|
||||
// Specific code for optional costs should be made conditional here
|
||||
}
|
||||
else {
|
||||
System.out.println("Skipping unported optional cost: " + opt.getType());
|
||||
|
||||
// Generic code: for now, chooses the optional cost if it can be paid (and later - played)
|
||||
Cost fullCost = opt.getCost().copy().add(costSoFar);
|
||||
SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost);
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
|
||||
chosenOptCosts.add(opt);
|
||||
costSoFar.add(opt.getCost());
|
||||
System.out.println("Chosen: " + opt + " for total cost of " + costSoFar.toSimpleString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -345,138 +345,6 @@ public final class GameActionUtil {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get optional additional costs.
|
||||
*
|
||||
* @param original
|
||||
* the original sa
|
||||
* @return an ArrayList<SpellAbility>.
|
||||
*
|
||||
* @deprecated only used by AI, replace it with new functions in AI
|
||||
*/
|
||||
@Deprecated public static List<SpellAbility> getOptionalCosts(final SpellAbility original) {
|
||||
final List<SpellAbility> abilities = getAdditionalCostSpell(original);
|
||||
|
||||
final Card source = original.getHostCard();
|
||||
|
||||
if (!original.isSpell()) {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
// Buyback, Kicker
|
||||
for (KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
if (keyword.startsWith("Buyback")) {
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
final SpellAbility newSA = abilities.get(i).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setPayCosts(new Cost(keyword.substring(8), false).add(newSA.getPayCosts()));
|
||||
newSA.setDescription(newSA.getDescription() + " (with Buyback)");
|
||||
newSA.addOptionalCost(OptionalCost.Buyback);
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(i, newSA);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else if (keyword.startsWith("MayFlashCost")) {
|
||||
// this is there for the AI
|
||||
if (source.getGame().getPhaseHandler().isPlayerTurn(source.getController())) {
|
||||
continue; // don't cast it with additional flash cost during AI's own turn, commonly a waste of mana
|
||||
}
|
||||
final String[] k = keyword.split(":");
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
final SpellAbility newSA = abilities.get(i).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setPayCosts(new Cost(k[1], false).add(newSA.getPayCosts()));
|
||||
newSA.setDescription(newSA.getDescription() + " (as though it had flash)");
|
||||
newSA.getRestrictions().setInstantSpeed(true);
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(i, newSA);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else if (keyword.startsWith("Kicker")) {
|
||||
String[] sCosts = TextUtil.split(keyword.substring(6), ':');
|
||||
boolean generic = "Generic".equals(sCosts[sCosts.length - 1]);
|
||||
// If this is a "generic kicker" (Undergrowth), ignore value for kicker creations
|
||||
int numKickers = sCosts.length - (generic ? 1 : 0);
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
int iUnKicked = i;
|
||||
for (int j = 0; j < numKickers; j++) {
|
||||
final SpellAbility newSA = abilities.get(iUnKicked).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
final Cost cost = new Cost(sCosts[j], false);
|
||||
newSA.setPayCosts(cost.add(newSA.getPayCosts()));
|
||||
if (!generic) {
|
||||
newSA.setDescription(newSA.getDescription() + " (Kicker " + cost.toSimpleString() + ")");
|
||||
newSA.addOptionalCost(j == 0 ? OptionalCost.Kicker1 : OptionalCost.Kicker2);
|
||||
} else {
|
||||
newSA.setDescription(newSA.getDescription() + " (Optional " + cost.toSimpleString() + ")");
|
||||
newSA.addOptionalCost(OptionalCost.Generic);
|
||||
}
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(i, newSA);
|
||||
i++;
|
||||
iUnKicked++;
|
||||
}
|
||||
}
|
||||
if (numKickers == 2) { // case for both kickers - it's hardcoded since they never have more than 2 kickers
|
||||
final SpellAbility newSA = abilities.get(iUnKicked).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
final Cost cost1 = new Cost(sCosts[0], false);
|
||||
final Cost cost2 = new Cost(sCosts[1], false);
|
||||
newSA.setDescription(TextUtil.addSuffix(newSA.getDescription(), TextUtil.concatWithSpace(" (Both kickers:", cost1.toSimpleString(), "and", TextUtil.addSuffix(cost2.toSimpleString(), ")"))));
|
||||
newSA.setPayCosts(cost2.add(cost1.add(newSA.getPayCosts())));
|
||||
newSA.addOptionalCost(OptionalCost.Kicker1);
|
||||
newSA.addOptionalCost(OptionalCost.Kicker2);
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(i, newSA);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.hasKeyword(Keyword.CONSPIRE)) {
|
||||
int amount = source.getAmountOfKeyword(Keyword.CONSPIRE);
|
||||
for (int kwInstance = 1; kwInstance <= amount; kwInstance++) {
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
final SpellAbility newSA = abilities.get(i).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
final String conspireCost = "tapXType<2/Creature.SharesColorWith/untapped creature you control that shares a color with " + source.getName() + ">";
|
||||
newSA.setPayCosts(new Cost(conspireCost, false).add(newSA.getPayCosts()));
|
||||
final String tag = kwInstance > 1 ? " (Conspire " + kwInstance + ")" : " (Conspire)";
|
||||
newSA.setDescription(newSA.getDescription() + tag);
|
||||
newSA.addOptionalCost(OptionalCost.Conspire);
|
||||
newSA.addConspireInstance();
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(++i, newSA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.hasKeyword(Keyword.JUMP_START)) {
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
final SpellAbility newSA = abilities.get(i).copy();
|
||||
newSA.setBasicSpell(false);
|
||||
final String jumpstartCost = "Discard<1/Card>";
|
||||
newSA.setPayCosts(new Cost(jumpstartCost, false).add(newSA.getPayCosts()));
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
newSA.setDescription(newSA.getDescription() + " (Jump-start)");
|
||||
newSA.addOptionalCost(OptionalCost.Jumpstart);
|
||||
if (newSA.canPlay()) {
|
||||
abilities.add(i, newSA);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return abilities;
|
||||
}
|
||||
|
||||
private static boolean hasUrzaLands(final Player p) {
|
||||
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield);
|
||||
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
|
||||
|
||||
Reference in New Issue
Block a user