LoseLifeAi: refactored to work better with DrawAi

This commit is contained in:
Hanmac
2017-02-25 10:09:34 +00:00
parent 421ceeb776
commit 35ad14aba4

View File

@@ -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")) {
// 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);
}
}