mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
- AI update for pump spells
This commit is contained in:
@@ -3,6 +3,7 @@ package forge.ai.ability;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
@@ -10,6 +11,7 @@ import forge.ai.SpellAbilityAi;
|
|||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
@@ -21,6 +23,7 @@ import forge.game.phase.Untap;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -433,10 +436,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (containsUsefulKeyword(ai, keywords, c, sa, attack)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// will the creature attack (only relevant for sorcery speed)?
|
// will the creature attack (only relevant for sorcery speed)?
|
||||||
if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& phase.isPlayerTurn(ai)
|
&& phase.isPlayerTurn(ai)
|
||||||
@@ -446,6 +445,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buff attacker/blocker using using triggered pump
|
||||||
if (sa.isTrigger() && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (sa.isTrigger() && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
if (phase.isPlayerTurn(ai)) {
|
if (phase.isPlayerTurn(ai)) {
|
||||||
if (CombatUtil.canAttack(c)) {
|
if (CombatUtil.canAttack(c)) {
|
||||||
@@ -458,51 +458,93 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the creature blocking and unable to destroy the attacker
|
final Player opp = ai.getOpponent();
|
||||||
// or would be destroyed itself?
|
Card pumped = pumpedCreature(ai, sa, c, defense, attack, keywords);
|
||||||
|
List<Card> oppCreatures = opp.getCreaturesInPlay();
|
||||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && combat.isBlocking(c)) {
|
// grant evasive
|
||||||
if (defense > 0 && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
return true;
|
&& !CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(c)).isEmpty()) {
|
||||||
}
|
if (CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(pumped)).isEmpty()
|
||||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
&& ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped)) {
|
||||||
// For now, Only care the first creature blocked by a card.
|
final float chance = MyRandom.getRandom().nextFloat();
|
||||||
// TODO Add in better BlockAdditional support
|
return chance < 0.5f * ComputerUtilCombat.damageIfUnblocked(pumped, ai.getOpponent(), combat) / ai.getOpponent().getLife();
|
||||||
if (!blockedBy.isEmpty() && attack > 0 && !ComputerUtilCombat.attackerWouldBeDestroyed(ai, blockedBy.get(0), combat)
|
|
||||||
&& blockedBy.get(0).staticDamagePrevention(99, c, true, true) > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the creature unblocked and the spell will pump its power?
|
// combat trickery
|
||||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
&& combat.isAttacking(c) && combat.isUnblocked(c) && attack > 0) {
|
//clunky code because ComputerUtilCombat.combatantWouldBeDestroyed() does not work for this sort of artificial combat
|
||||||
|
Combat pumpedCombat = new Combat(phase.isPlayerTurn(ai) ? ai : ai.getOpponent());
|
||||||
|
List<Card> opposing = null;
|
||||||
|
boolean pumpedWillDie = false;
|
||||||
|
final boolean isAttacking = combat.isAttacking(c);
|
||||||
|
if (isAttacking) {
|
||||||
|
pumpedCombat.addAttacker(pumped, opp);
|
||||||
|
opposing = combat.getBlockers(c);
|
||||||
|
for (Card b : opposing) {
|
||||||
|
pumpedCombat.addBlocker(pumped, b);
|
||||||
|
}
|
||||||
|
if (ComputerUtilCombat.attackerWouldBeDestroyed(ai, pumped, pumpedCombat)) {
|
||||||
|
pumpedWillDie = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opposing = combat.getAttackersBlockedBy(c);
|
||||||
|
for (Card a : opposing) {
|
||||||
|
pumpedCombat.addAttacker(a, ai);
|
||||||
|
pumpedCombat.addBlocker(a, pumped);
|
||||||
|
}
|
||||||
|
if (ComputerUtilCombat.blockerWouldBeDestroyed(ai, pumped, pumpedCombat)) {
|
||||||
|
pumpedWillDie = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//1. save combatant
|
||||||
|
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && !pumpedWillDie) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the creature blocked and the blocker would survive
|
//2. kill combatant
|
||||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && attack > 0
|
boolean survivor = false;
|
||||||
&& combat.isAttacking(c)
|
for (Card o : opposing) {
|
||||||
&& combat.isBlocked(c)
|
if (!ComputerUtilCombat.combatantWouldBeDestroyed(opp, o, combat)) {
|
||||||
&& combat.getBlockers(c) != null
|
survivor = true;
|
||||||
&& !combat.getBlockers(c).isEmpty()
|
break;
|
||||||
&& !ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0), combat)
|
}
|
||||||
&& combat.getBlockers(c).get(0).staticDamagePrevention(99, c, true, true) > 0) {
|
}
|
||||||
|
if (survivor) {
|
||||||
|
for (Card o : opposing) {
|
||||||
|
if (!ComputerUtilCombat.combatantWouldBeDestroyed(opp, o, combat)) {
|
||||||
|
if (isAttacking) {
|
||||||
|
if (ComputerUtilCombat.blockerWouldBeDestroyed(opp, o, pumpedCombat)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (ComputerUtilCombat.attackerWouldBeDestroyed(opp, o, pumpedCombat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS, ai.getOpponent()) && combat.isBlocking(c) && defense > 0 ) {
|
//3. buff unblocked attacker
|
||||||
// if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
if (combat.isAttacking(c) && combat.isUnblocked(c) && attack > 0) {
|
||||||
|
final float chance = MyRandom.getRandom().nextFloat();
|
||||||
|
return chance < 1.0f * ComputerUtilCombat.damageIfUnblocked(pumped, ai.getOpponent(), combat) / ai.getOpponent().getLife();
|
||||||
|
}
|
||||||
|
|
||||||
|
//4. if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
||||||
|
if (combat.isBlocking(c) && defense > 0 ) {
|
||||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||||
boolean attackerHasTrample = false;
|
boolean attackerHasTrample = false;
|
||||||
for (Card b : blockedBy) {
|
for (Card b : blockedBy) {
|
||||||
attackerHasTrample |= b.hasKeyword("Trample");
|
attackerHasTrample |= b.hasKeyword("Trample");
|
||||||
}
|
}
|
||||||
|
if (attackerHasTrample && (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, combat))) {
|
||||||
if (attackerHasTrample && (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, combat)))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,4 +665,22 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Card pumpedCreature(final Player ai, final SpellAbility sa, final Card c, final int d, final int a,
|
||||||
|
final List<String> keywords) {
|
||||||
|
Card pumped = c.isToken() ? CardFactory.copyStats(c, ai) : CardFactory.getCard(c.getPaperCard(), ai);
|
||||||
|
final long timestamp = c.getGame().getNextTimestamp();
|
||||||
|
final ArrayList<String> kws = new ArrayList<String>();
|
||||||
|
for (String kw : keywords) {
|
||||||
|
if (kw.startsWith("HIDDEN")) {
|
||||||
|
pumped.addHiddenExtrinsicKeyword(kw);
|
||||||
|
} else {
|
||||||
|
kws.add(kw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pumped.addTempAttackBoost(a);
|
||||||
|
pumped.addTempDefenseBoost(d);
|
||||||
|
pumped.addChangedCardKeywords(kws, new ArrayList<String>(), false, timestamp);
|
||||||
|
pumped.setSickness(c.hasSickness());
|
||||||
|
return pumped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user