mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
- Updated tokens AI to check for static effects like Virulent Plague
This commit is contained in:
@@ -173,13 +173,19 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
if ("Pongify".equals(logic)) {
|
||||
Card token = TokenAi.spawnToken(ai.getOpponent(), sa.getSubAbility());
|
||||
if (source.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) || //prevent surprise combatant
|
||||
ComputerUtilCard.evaluateCreature(choice) < 1.5 * ComputerUtilCard.evaluateCreature(token)) {
|
||||
final Card token = TokenAi.spawnToken(ai.getOpponent(), sa.getSubAbility());
|
||||
if (token == null) {
|
||||
return true; // becomes Terminate
|
||||
} else {
|
||||
if (source.getGame().getPhaseHandler().getPhase()
|
||||
.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) || // prevent surprise combatant
|
||||
ComputerUtilCard.evaluateCreature(choice) < 1.5
|
||||
* ComputerUtilCard.evaluateCreature(token)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CardLists.getNotType(list, "Land").isEmpty()) {
|
||||
choice = ComputerUtilCard.getBestLandAI(list);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.ComputerUtilCost;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.ai.SpellApiToAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -22,11 +23,13 @@ import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPutCounter;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.trigger.Trigger;
|
||||
@@ -122,20 +125,26 @@ public class TokenAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
boolean pwAbility = sa.getRestrictions().isPwAbility();
|
||||
if (pwAbility) {
|
||||
// Planeswalker token ability with loyalty costs should be played in Main1 or it might
|
||||
// never be used due to other positive abilities. AI is kept from spamming them by the
|
||||
// loyalty cost of each usage. Zero/loyalty gain token abilities can be evaluated as
|
||||
// per normal.
|
||||
boolean hasCost = false;
|
||||
// Planeswalker-related flags
|
||||
boolean pwMinus = false;
|
||||
boolean pwPlus = false;
|
||||
if (sa.getRestrictions().isPwAbility()) {
|
||||
/*
|
||||
* Planeswalker token ability with loyalty costs should be played in Main1 or it might
|
||||
* never be used due to other positive abilities. AI is kept from spamming them by the
|
||||
* loyalty cost of each usage. Zero/loyalty gain token abilities can be evaluated as
|
||||
* per normal.
|
||||
*/
|
||||
for (CostPart c : sa.getPayCosts().getCostParts()) {
|
||||
if (c instanceof CostRemoveCounter) {
|
||||
hasCost = true;
|
||||
pwMinus = true;
|
||||
break;
|
||||
}
|
||||
if (c instanceof CostPutCounter && c.convertAmount() > 0) {
|
||||
pwPlus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pwAbility = hasCost;
|
||||
}
|
||||
|
||||
PhaseHandler ph = game.getPhaseHandler();
|
||||
@@ -150,13 +159,13 @@ public class TokenAi extends SpellAbilityAi {
|
||||
buff = true;
|
||||
}
|
||||
}
|
||||
if (!buff && !sacOnStack && !pwAbility) {
|
||||
if (!buff && !sacOnStack && !pwMinus) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
||||
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn")
|
||||
&& !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack && !pwAbility) {
|
||||
&& !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack && !pwMinus) {
|
||||
return false;
|
||||
}
|
||||
if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || game.getPhaseHandler().isPlayerTurn(opp))
|
||||
@@ -224,6 +233,31 @@ public class TokenAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tokenAmount.equals("X") || (this.tokenPower != null && this.tokenPower.equals("X")) || (this.tokenToughness != null && this.tokenToughness.equals("X"))) {
|
||||
int x = AbilityUtils.calculateAmount(sa.getHostCard(), this.tokenAmount, sa);
|
||||
if (source.getSVar("X").equals("Count$Converge")) {
|
||||
x = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||
}
|
||||
if (source.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
source.setSVar("PayX", Integer.toString(x));
|
||||
}
|
||||
if (x <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final Card token = spawnToken(ai, sa);
|
||||
if (token == null) {
|
||||
final AbilitySub sub = sa.getSubAbility();
|
||||
if (pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai))) {
|
||||
return true; // planeswalker plus ability or sub-ability is useful
|
||||
} else {
|
||||
return false; // no token created
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt sacrifice effect
|
||||
if (sacOnStack) {
|
||||
final int nTokens = AbilityUtils.calculateAmount(sa.getHostCard(), this.tokenAmount, sa);
|
||||
@@ -237,8 +271,6 @@ public class TokenAi extends SpellAbilityAi {
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
if (!list.isEmpty() && nTokens > 0 && list.size() == nToSac) { // only care about saving single creature for now
|
||||
ComputerUtilCard.sortByEvaluateCreature(list);
|
||||
Card token = spawnToken(ai, sa);
|
||||
if (token != null) {
|
||||
list.add(token);
|
||||
list = CardLists.getValidCards(list, valid.split(","), ai.getOpponent(), topStack.getHostCard(), sa);
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
|
||||
@@ -248,22 +280,6 @@ public class TokenAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tokenAmount.equals("X") || (this.tokenPower != null && this.tokenPower.equals("X")) || (this.tokenToughness != null && this.tokenToughness.equals("X"))) {
|
||||
int x = AbilityUtils.calculateAmount(sa.getHostCard(), this.tokenAmount, sa);
|
||||
if (source.getSVar("X").equals("Count$Converge")) {
|
||||
x = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||
}
|
||||
if (source.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
source.setSVar("PayX", Integer.toString(x));
|
||||
}
|
||||
if (x <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||
return true;
|
||||
@@ -318,13 +334,12 @@ public class TokenAi extends SpellAbilityAi {
|
||||
* Create the token as a Card object.
|
||||
* @param ai owner of the new token
|
||||
* @param sa Token SpellAbility
|
||||
* @return token creature
|
||||
* @return token creature created by ability
|
||||
*/
|
||||
public static Card spawnToken(Player ai, SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
String[] tokenKeywords = sa.hasParam("TokenKeywords") ? sa.getParam("TokenKeywords").split("<>") : new String[0];
|
||||
String tokenAmount = sa.getParamOrDefault("TokenAmount", "1");
|
||||
String tokenPower = sa.getParam("TokenPower");
|
||||
String tokenToughness = sa.getParam("TokenToughness");
|
||||
String tokenName = sa.getParam("TokenName");
|
||||
@@ -376,10 +391,6 @@ public class TokenAi extends SpellAbilityAi {
|
||||
|
||||
final int finalPower = AbilityUtils.calculateAmount(host, tokenPower, sa);
|
||||
final int finalToughness = AbilityUtils.calculateAmount(host, tokenToughness, sa);
|
||||
final int finalAmount = AbilityUtils.calculateAmount(host, tokenAmount, sa);
|
||||
if (finalAmount < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String[] substitutedTypes = Arrays.copyOf(tokenTypes, tokenTypes.length);
|
||||
for (int i = 0; i < substitutedTypes.length; i++) {
|
||||
@@ -391,45 +402,40 @@ public class TokenAi extends SpellAbilityAi {
|
||||
final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size()));
|
||||
final CardFactory.TokenInfo tokenInfo = new CardFactory.TokenInfo(substitutedName, imageName,
|
||||
cost, substitutedTypes, tokenKeywords, finalPower, finalToughness);
|
||||
final List<Card> tokens = CardFactory.makeToken(tokenInfo, ai);
|
||||
List<Card> tokens = CardFactory.makeToken(tokenInfo, ai);
|
||||
if (tokens.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final Card c = tokens.get(0);
|
||||
|
||||
// Grant rule changes
|
||||
if (tokenHiddenKeywords != null) {
|
||||
for (final String s : tokenHiddenKeywords) {
|
||||
for (final Card c : tokens) {
|
||||
c.addHiddenExtrinsicKeyword(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grant abilities
|
||||
if (tokenAbilities != null) {
|
||||
for (final String s : tokenAbilities) {
|
||||
final String actualAbility = host.getSVar(s);
|
||||
for (final Card c : tokens) {
|
||||
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
|
||||
c.addSpellAbility(grantedAbility);
|
||||
// added ability to intrinsic list so copies and clones work
|
||||
c.getCurrentState().addUnparsedAbility(actualAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grant triggers
|
||||
if (tokenTriggers != null) {
|
||||
|
||||
for (final String s : tokenTriggers) {
|
||||
final String actualTrigger = host.getSVar(s);
|
||||
|
||||
for (final Card c : tokens) {
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true);
|
||||
final String ability = host.getSVar(parsedTrigger.getMapParams().get("Execute"));
|
||||
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, c));
|
||||
c.addTrigger(parsedTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grant SVars
|
||||
if (tokenSVars != null) {
|
||||
@@ -441,22 +447,26 @@ public class TokenAi extends SpellAbilityAi {
|
||||
name = actualSVar.split(":")[0];
|
||||
actualSVar = actualSVar.split(":")[1];
|
||||
}
|
||||
for (final Card c : tokens) {
|
||||
c.setSVar(name, actualSVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grant static abilities
|
||||
if (tokenStaticAbilities != null) {
|
||||
for (final String s : tokenStaticAbilities) {
|
||||
final String actualAbility = host.getSVar(s);
|
||||
for (final Card c : tokens) {
|
||||
c.addStaticAbilityString(actualAbility);
|
||||
c.addStaticAbility(actualAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tokens.get(0);
|
||||
|
||||
// Apply static abilities and prune dead tokens
|
||||
final Game game = ai.getGame();
|
||||
ComputerUtilCard.applyStaticContPT(game, c, null);
|
||||
if (c.getNetToughness() < 1) {
|
||||
return null;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user