mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
LoseLifeAi: refactored to work better with DrawAi
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilCost;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
@@ -9,29 +13,37 @@ import forge.game.card.Card;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LifeLoseAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.
|
||||
* SpellAbility, forge.game.player.Player)
|
||||
*/
|
||||
@Override
|
||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||
|
||||
final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined")
|
||||
? new FCollection<Player>(sa.getTargets().getTargetPlayers())
|
||||
: AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
final String amountStr = sa.getParam("LifeAmount");
|
||||
int amount = 0;
|
||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
source.setSVar("PayX", Integer.toString(xPay));
|
||||
amount = xPay;
|
||||
// something already set PayX
|
||||
if (source.hasSVar("PayX")) {
|
||||
amount = Integer.parseInt(source.getSVar("PayX"));
|
||||
} else {
|
||||
// Set PayX here to maximum value.
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
source.setSVar("PayX", Integer.toString(xPay));
|
||||
amount = xPay;
|
||||
}
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
}
|
||||
@@ -42,80 +54,84 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.AbilityFactoryAlterLife.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
||||
* forge.game.card.Card)
|
||||
*/
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
|
||||
final Cost abCost = sa.getPayCosts();
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
||||
final String amountStr = sa.getParam("LifeAmount");
|
||||
int amount = 0;
|
||||
|
||||
// TODO handle proper calculation of X values based on Cost and what
|
||||
// would be paid
|
||||
int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
// source.setSVar("PayX", Integer.toString(amount));
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
}
|
||||
|
||||
// special logic for checkLifeCost
|
||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, amount, sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// other cost as the same
|
||||
return super.willPayCosts(ai, sa, cost, source);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
final String amountStr = sa.getParam("LifeAmount");
|
||||
int amount = 0;
|
||||
|
||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
source.setSVar("PayX", Integer.toString(amount));
|
||||
} else {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (abCost != null) {
|
||||
// AI currently disabled for these costs
|
||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, amount, sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Player opp = ai.getOpponent();
|
||||
|
||||
if (!opp.canLoseLife()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
if (sa.canTarget(opp)) {
|
||||
sa.getTargets().add(opp);
|
||||
} else {
|
||||
if (!doTgt(ai, sa, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (amount >= opp.getLife()) {
|
||||
return true; // killing the human should be done asap
|
||||
}
|
||||
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
||||
|
||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PlayerCollection filteredPlayer = tgtPlayers
|
||||
.filter(Predicates.and(PlayerPredicates.isOpponentOf(ai), PlayerPredicates.lifeLessOrEqualTo(amount)));
|
||||
// killing opponents asap
|
||||
if (!filteredPlayer.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't use loselife before main 2 if possible
|
||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
||||
&& !sa.hasParam("ActivationPhases")
|
||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||
return false;
|
||||
}
|
||||
@@ -125,9 +141,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SpellAbilityAi.isSorcerySpeed(sa)
|
||||
|| sa.hasParam("ActivationPhases")
|
||||
|| SpellAbilityAi.playReusable(ai, sa)
|
||||
if (SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa)
|
||||
|| ComputerUtil.activateForCost(sa, ai)) {
|
||||
return true;
|
||||
}
|
||||
@@ -135,16 +149,17 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, boolean)
|
||||
*/
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
||||
final boolean mandatory) {
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
if (tgt != null) {
|
||||
if (sa.canTarget(ai.getOpponent())) {
|
||||
sa.getTargets().add(ai.getOpponent());
|
||||
} else if (mandatory && sa.canTarget(ai)) {
|
||||
sa.getTargets().add(ai);
|
||||
} else {
|
||||
if (sa.usesTargeting()) {
|
||||
if (!doTgt(ai, sa, mandatory)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -172,4 +187,50 @@ public class LifeLoseAi extends SpellAbilityAi {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
sa.resetTargets();
|
||||
PlayerCollection opps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||
// try first to find Opponent that can lose life and lose the game
|
||||
if (!opps.isEmpty()) {
|
||||
for (Player opp : opps) {
|
||||
if (opp.canLoseLife() && !opp.cantLose()) {
|
||||
sa.getTargets().add(opp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do that only if needed
|
||||
if (mandatory) {
|
||||
if (!opps.isEmpty()) {
|
||||
// try another opponent even if it can't lose life
|
||||
sa.getTargets().add(opps.getFirst());
|
||||
return true;
|
||||
}
|
||||
// try hit ally instead of itself
|
||||
for (Player ally : ai.getAllies()) {
|
||||
if (sa.canTarget(ally)) {
|
||||
sa.getTargets().add(ally);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// need to hit itself
|
||||
if (sa.canTarget(ai)) {
|
||||
sa.getTargets().add(ai);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected PlayerCollection getPlayers(Player ai, SpellAbility sa) {
|
||||
Iterable<Player> it;
|
||||
if (sa.usesTargeting() && !sa.hasParam("Defined")) {
|
||||
it = sa.getTargets().getTargetPlayers();
|
||||
} else {
|
||||
it = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||
}
|
||||
return new PlayerCollection(it);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user