mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Merge remote-tracking branch 'remotes/origin/AINecrologia' into PriceOfProgress
This commit is contained in:
@@ -347,6 +347,9 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
if (source.getName().equals("Maralen of the Mornsong Avatar")) {
|
||||
return PaymentDecision.number(2);
|
||||
}
|
||||
if (source.getName().equals("Necrologia")) {
|
||||
return PaymentDecision.number(Integer.parseInt(ability.getSVar("ChosenX")));
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
|
||||
@@ -2831,4 +2831,39 @@ public class ComputerUtil {
|
||||
|
||||
return srcList;
|
||||
}
|
||||
|
||||
// Check if AI life is in danger/serious danger based on next expected combat
|
||||
// assuming a loss of "payment" life
|
||||
// call this to determine if it's safe to use a life payment spell
|
||||
// or trigger "emergency" strategies such as holding mana for Spike Weaver of Counterspell.
|
||||
public static boolean aiLifeInDanger(Player ai, boolean serious, int payment) {
|
||||
Player opponent = ComputerUtil.getOpponentFor(ai);
|
||||
// test whether the human can kill the ai next turn
|
||||
Combat combat = new Combat(opponent);
|
||||
boolean containsAttacker = false;
|
||||
for (Card att : opponent.getCreaturesInPlay()) {
|
||||
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
|
||||
combat.addAttacker(att, ai);
|
||||
containsAttacker = true;
|
||||
}
|
||||
}
|
||||
if (!containsAttacker) {
|
||||
return false;
|
||||
}
|
||||
AiBlockController block = new AiBlockController(ai);
|
||||
block.assignBlockersForCombat(combat);
|
||||
|
||||
// TODO predict other, noncombat sources of damage and add them to the "payment" variable.
|
||||
// examples : Black Vise, The Rack, known direct damage spells in enemy hand, etc
|
||||
// If added, might need a parameter to define whether we want to check all threats or combat threats.
|
||||
|
||||
if ((serious) && (ComputerUtilCombat.lifeInSeriousDanger(ai, combat, payment))) {
|
||||
return true;
|
||||
}
|
||||
if ((!serious) && (ComputerUtilCombat.lifeInDanger(ai, combat, payment))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,6 +382,10 @@ public class ComputerUtilCombat {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean lifeInDanger(final Player ai, final Combat combat) {
|
||||
return lifeInDanger(ai, combat, 0);
|
||||
}
|
||||
|
||||
public static boolean lifeInDanger(final Player ai, final Combat combat, final int payment) {
|
||||
// life in danger only cares about the player's life. Not Planeswalkers' life
|
||||
if (ai.cantLose() || combat == null || combat.getAttackingPlayer() == ai) {
|
||||
return false;
|
||||
@@ -401,12 +405,12 @@ public class ComputerUtilCombat {
|
||||
|
||||
// check for creatures that must be blocked
|
||||
final List<Card> attackers = combat.getAttackersOf(ai);
|
||||
|
||||
|
||||
final List<Card> threateningCommanders = getLifeThreateningCommanders(ai,combat);
|
||||
|
||||
for (final Card attacker : attackers) {
|
||||
|
||||
final List<Card> blockers = combat.getBlockers(attacker);
|
||||
final List<Card> blockers = combat.getBlockers(attacker);
|
||||
|
||||
if (blockers.isEmpty()) {
|
||||
if (!attacker.getSVar("MustBeBlocked").equals("")) {
|
||||
@@ -429,7 +433,7 @@ public class ComputerUtilCombat {
|
||||
}
|
||||
}
|
||||
|
||||
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < Math.min(4, ai.getLife())
|
||||
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) - payment < Math.min(4, ai.getLife())
|
||||
&& !ai.cantLoseForZeroOrLessLife()) {
|
||||
return true;
|
||||
}
|
||||
@@ -463,12 +467,16 @@ public class ComputerUtilCombat {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean lifeInSeriousDanger(final Player ai, final Combat combat) {
|
||||
return lifeInSeriousDanger(ai, combat, 0);
|
||||
}
|
||||
|
||||
public static boolean lifeInSeriousDanger(final Player ai, final Combat combat, final int payment) {
|
||||
// life in danger only cares about the player's life. Not about a
|
||||
// Planeswalkers life
|
||||
if (ai.cantLose() || combat == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final List<Card> threateningCommanders = ComputerUtilCombat.getLifeThreateningCommanders(ai, combat);
|
||||
|
||||
// check for creatures that must be blocked
|
||||
@@ -488,7 +496,7 @@ public class ComputerUtilCombat {
|
||||
}
|
||||
}
|
||||
|
||||
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < 1 && !ai.cantLoseForZeroOrLessLife()) {
|
||||
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) - payment < 1 && !ai.cantLoseForZeroOrLessLife()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +251,20 @@ public class DrawAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
if (num != null && num.equals("ChosenX")) {
|
||||
// Necrologia, Pay X Life : Draw X Cards
|
||||
if (sa.getSVar("X").equals("XChoice")) {
|
||||
// Draw up to max hand size but leave at least 3 in library
|
||||
numCards = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3);
|
||||
// But no more than what's "safe" and doesn't risk a near death experience
|
||||
// Maybe would be better to check for "serious danger" and take more risk?
|
||||
while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) {
|
||||
numCards--;
|
||||
}
|
||||
sa.setSVar("ChosenX", Integer.toString(numCards));
|
||||
source.setSVar("ChosenX", Integer.toString(numCards));
|
||||
}
|
||||
}
|
||||
// Logic for cards that require special handling
|
||||
if ("YawgmothsBargain".equals(logic)) {
|
||||
return SpecialCardAi.YawgmothsBargain.consider(ai, sa);
|
||||
|
||||
@@ -5,6 +5,5 @@ Text:Cast CARDNAME only during your end step.\r\n
|
||||
A:SP$ Draw | Cost$ 3 B B PayLife<X> | NumCards$ ChosenX | Defined$ You | ActivationPhases$ End of Turn | PlayerTurn$ True | References$ X | SpellDescription$ Draw X cards.
|
||||
SVar:X:XChoice
|
||||
#ChosenX SVar created by Cost payment
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/necrologia.jpg
|
||||
Oracle:Cast Necrologia only during your end step.\nAs an additional cost to cast Necrologia, pay X life.\nDraw X cards.
|
||||
|
||||
Reference in New Issue
Block a user