mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +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")) {
|
if (source.getName().equals("Maralen of the Mornsong Avatar")) {
|
||||||
return PaymentDecision.number(2);
|
return PaymentDecision.number(2);
|
||||||
}
|
}
|
||||||
|
if (source.getName().equals("Necrologia")) {
|
||||||
|
return PaymentDecision.number(Integer.parseInt(ability.getSVar("ChosenX")));
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||||
|
|||||||
@@ -2831,4 +2831,39 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
return srcList;
|
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.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean lifeInDanger(final Player ai, final Combat combat) {
|
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
|
// life in danger only cares about the player's life. Not Planeswalkers' life
|
||||||
if (ai.cantLose() || combat == null || combat.getAttackingPlayer() == ai) {
|
if (ai.cantLose() || combat == null || combat.getAttackingPlayer() == ai) {
|
||||||
return false;
|
return false;
|
||||||
@@ -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()) {
|
&& !ai.cantLoseForZeroOrLessLife()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -463,6 +467,10 @@ public class ComputerUtilCombat {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean lifeInSeriousDanger(final Player ai, final Combat combat) {
|
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
|
// life in danger only cares about the player's life. Not about a
|
||||||
// Planeswalkers life
|
// Planeswalkers life
|
||||||
if (ai.cantLose() || combat == null) {
|
if (ai.cantLose() || combat == null) {
|
||||||
@@ -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;
|
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
|
// Logic for cards that require special handling
|
||||||
if ("YawgmothsBargain".equals(logic)) {
|
if ("YawgmothsBargain".equals(logic)) {
|
||||||
return SpecialCardAi.YawgmothsBargain.consider(ai, sa);
|
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.
|
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
|
SVar:X:XChoice
|
||||||
#ChosenX SVar created by Cost payment
|
#ChosenX SVar created by Cost payment
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/necrologia.jpg
|
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.
|
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