mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Fix Stack Overflow (#2228)
* Fix cards * Fix Stack Overflow * Performance fix Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.59> Co-authored-by: TRT <>
This commit is contained in:
@@ -33,6 +33,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import forge.ai.AiCardMemory.MemorySet;
|
||||
import forge.ai.ability.ChooseGenericEffectAi;
|
||||
import forge.ai.ability.ProtectAi;
|
||||
import forge.ai.ability.TokenAi;
|
||||
@@ -449,14 +450,27 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// try everything when about to die
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
// TODO make sure survival is possible in case the creature blocks a trampler
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
// in some rare situations the call to lifeInDanger could lead us back here, this will prevent an overflow
|
||||
boolean preventReturn = sa != null && sa.isManaAbility();
|
||||
if (preventReturn) {
|
||||
AiCardMemory.rememberCard(ai, sa.getHostCard(), MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
||||
}
|
||||
|
||||
boolean danger = ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat());
|
||||
|
||||
if (preventReturn) {
|
||||
AiCardMemory.forgetCard(ai, sa.getHostCard(), MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
||||
}
|
||||
|
||||
if (danger) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
// TODO make sure survival is possible in case the creature blocks a trampler
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -609,7 +623,7 @@ public class ComputerUtil {
|
||||
int count = 0;
|
||||
|
||||
while (count < amount) {
|
||||
Card prefCard = getCardPreference(ai, source, "SacCost", typeList);
|
||||
Card prefCard = getCardPreference(ai, source, "SacCost", typeList, ability);
|
||||
if (prefCard == null) {
|
||||
prefCard = ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
|
||||
@@ -328,10 +328,10 @@ public class ComputerUtilCombat {
|
||||
if (blockers.size() == 0
|
||||
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||
unblocked.add(attacker);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||
if (!attacker.hasKeyword(Keyword.INFECT)) {
|
||||
damage += getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE) && !attacker.hasKeyword(Keyword.INFECT)) {
|
||||
int dmgAfterShielding = getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
if (dmgAfterShielding > 0) {
|
||||
damage += dmgAfterShielding;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,13 +369,14 @@ public class ComputerUtilCombat {
|
||||
if (blockers.size() == 0
|
||||
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||
unblocked.add(attacker);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
int trampleDamage = getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
poison += trampleDamage;
|
||||
if (trampleDamage > 0) {
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
poison += trampleDamage;
|
||||
}
|
||||
poison += predictPoisonFromTriggers(attacker, ai, trampleDamage);
|
||||
}
|
||||
poison += predictPoisonFromTriggers(attacker, ai, trampleDamage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +687,7 @@ public class ComputerUtilCombat {
|
||||
final int defenderDefense = blocker.getLethalDamage() - flankingMagnitude + defBushidoMagnitude;
|
||||
|
||||
return defenderDefense;
|
||||
} // shieldDamage
|
||||
}
|
||||
|
||||
// For AI safety measures like Regeneration
|
||||
/**
|
||||
|
||||
@@ -234,7 +234,7 @@ public class ComputerUtilCost {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility, final boolean effect) {
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, 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;
|
||||
@@ -247,18 +247,17 @@ public class ComputerUtilCost {
|
||||
exclude.addAll(AiCardMemory.getMemorySet(ai, MemorySet.PAYS_SAC_COST));
|
||||
}
|
||||
if (part.payCostFromSource()) {
|
||||
list.add(source);
|
||||
list.add(sourceAbility.getHostCard());
|
||||
} else if (part.getType().equals("OriginalHost")) {
|
||||
list.add(sourceAbility.getOriginalHost());
|
||||
} else if (part.getAmount().equals("All")) {
|
||||
// Does the AI want to use Sacrifice All?
|
||||
return false;
|
||||
} else {
|
||||
final String amount = part.getAmount();
|
||||
Integer c = part.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, amount, sourceAbility);
|
||||
c = part.getAbilityAmount(sourceAbility);
|
||||
}
|
||||
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
CardCollectionView choices = aic.chooseSacrificeType(part.getType(), sourceAbility, effect, c, exclude);
|
||||
@@ -636,7 +635,7 @@ public class ComputerUtilCost {
|
||||
|
||||
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded, effect)
|
||||
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
} // canPayCost()
|
||||
}
|
||||
|
||||
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
@@ -316,10 +316,6 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma.getHostCard(), ma, ma.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
// For abilities like Genju of the Cedars, make sure that we're not activating the aura ability by tapping the enchanted card for mana
|
||||
if (saHost.isAura() && "Enchanted".equals(sa.getParam("Defined"))
|
||||
@@ -381,9 +377,15 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) {
|
||||
return paymentChoice;
|
||||
if (!canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma, ma.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return paymentChoice;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user