mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Battle AI support + improve Battle mechanics support (#3107)
* - First draft of (very sketchy) Battle AI code. * - Imports fix. * - Slightly cleaner refreshCombatants. * - Update Combat to allow the protecting player to participate in declaring blocks to defend a battle. * - Update AiBlockController in order to allow the AI to participate in defending battles it's protecting. * Clean up * - Minor cleanup. * Fix checking backside * Add TODO * Fix missing combat removal * - Suggested minor cleanup. * - Fix imports. * - Improve support for battles in getAllPossibleDefenders. * - AI: prefer own Battles before choosing allied Battles. * Fix ClassCastException --------- Co-authored-by: TRT <>
This commit is contained in:
@@ -119,7 +119,11 @@ public class AiAttackController {
|
||||
} // overloaded constructor to evaluate single specified attacker
|
||||
|
||||
private void refreshCombatants(GameEntity defender) {
|
||||
this.oppList = getOpponentCreatures(defendingOpponent);
|
||||
if (defender instanceof Card && ((Card) defender).isBattle()) {
|
||||
this.oppList = getOpponentCreatures(((Card) defender).getProtectingPlayer());
|
||||
} else {
|
||||
this.oppList = getOpponentCreatures(defendingOpponent);
|
||||
}
|
||||
this.attackers = new ArrayList<>();
|
||||
for (Card c : myList) {
|
||||
if (canAttackWrapper(c, defender)) {
|
||||
@@ -722,9 +726,14 @@ public class AiAttackController {
|
||||
return pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
|
||||
}
|
||||
|
||||
List<Card> battleDefending = c.getDefendingBattles();
|
||||
if (!battleDefending.isEmpty()) {
|
||||
// TODO filter for team ones
|
||||
// Get the preferred battle (prefer own battles, then ally battles)
|
||||
final CardCollection defBattles = c.getDefendingBattles();
|
||||
List<Card> ownBattleDefending = CardLists.filter(defBattles, CardPredicates.isController(ai));
|
||||
List<Card> allyBattleDefending = CardLists.filter(defBattles, CardPredicates.isControlledByAnyOf(ai.getAllies()));
|
||||
List<Card> prefBattleList = ownBattleDefending.isEmpty() ? allyBattleDefending : ownBattleDefending;
|
||||
if (!prefBattleList.isEmpty()) {
|
||||
// TODO try to be less predictable here, should really check if something would make the back uncastable
|
||||
return Collections.min(prefBattleList, CardPredicates.compareByCounterType(CounterEnumType.DEFENSE));
|
||||
}
|
||||
|
||||
return prefDefender;
|
||||
@@ -756,7 +765,17 @@ public class AiAttackController {
|
||||
// decided to attack another defender so related lists need to be updated
|
||||
// (though usually rather try to avoid this situation for performance reasons)
|
||||
if (defender != defendingOpponent) {
|
||||
defendingOpponent = defender instanceof Player ? (Player) defender : ((Card)defender).getController();
|
||||
if (defender instanceof Player) {
|
||||
defendingOpponent = (Player) defender;
|
||||
} else if (defender instanceof Card) {
|
||||
Card defCard = (Card) defender;
|
||||
if (defCard.isBattle()) {
|
||||
defendingOpponent = defCard.getProtectingPlayer();
|
||||
} else {
|
||||
// TODO: assume Planeswalker for now, may need to be updated later if more unique mechanics appear like Battle
|
||||
defendingOpponent = defCard.getController();
|
||||
}
|
||||
}
|
||||
refreshCombatants(defender);
|
||||
}
|
||||
if (this.attackers.isEmpty()) {
|
||||
|
||||
@@ -165,10 +165,12 @@ public class AiBlockController {
|
||||
}
|
||||
|
||||
// TODO Add creatures attacking Planeswalkers in order of which we want to protect
|
||||
// defend planeswalkers with more loyalty before planeswalkers with less loyalty
|
||||
// if planeswalker will be too difficult to defend don't even bother
|
||||
// defend planeswalkers with more loyalty before planeswalkers with less loyalty,
|
||||
// defend battles with fewer defense counters before battles with more defense counters,
|
||||
// if planeswalker/battle will be too difficult to defend don't even bother
|
||||
for (GameEntity defender : defenders) {
|
||||
if (defender instanceof Card && ((Card) defender).getController().equals(ai)) {
|
||||
if ((defender instanceof Card && ((Card) defender).getController().equals(ai))
|
||||
|| (defender instanceof Card && ((Card) defender).isBattle() && ((Card) defender).getProtectingPlayer().equals(ai))) {
|
||||
final CardCollection attackers = combat.getAttackersOf(defender);
|
||||
// Begin with the attackers that pose the biggest threat
|
||||
CardLists.sortByPowerDesc(attackers);
|
||||
|
||||
@@ -139,17 +139,25 @@ public class PlayAi extends SpellAbilityAi {
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
|
||||
final boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
final CardStateName state;
|
||||
if (sa.hasParam("CastTransformed")) {
|
||||
state = CardStateName.Transformed;
|
||||
options.forEach(c -> c.changeToState(CardStateName.Transformed));
|
||||
} else {
|
||||
state = CardStateName.Original;
|
||||
}
|
||||
|
||||
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
// TODO needs to be aligned for MDFC along with getAbilityToPlay so the knowledge
|
||||
// of which spell was the reason for the choice can be used there
|
||||
for (SpellAbility s : c.getBasicSpells(c.getState(CardStateName.Original))) {
|
||||
for (SpellAbility s : AbilityUtils.getBasicSpellsFromPlayEffect(c, ai, state)) {
|
||||
if (!(s instanceof Spell)) {
|
||||
continue;
|
||||
}
|
||||
Spell spell = (Spell) s;
|
||||
s.setActivatingPlayer(ai, true);
|
||||
// timing restrictions still apply
|
||||
if (!s.getRestrictions().checkTimingRestrictions(c, s))
|
||||
continue;
|
||||
if (params != null && params.containsKey("CMCLimit")) {
|
||||
Integer cmcLimit = (Integer) params.get("CMCLimit");
|
||||
if (spell.getPayCosts().getTotalMana().getCMC() > cmcLimit)
|
||||
@@ -188,6 +196,11 @@ public class PlayAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (sa.hasParam("CastTransformed")) {
|
||||
options.forEach(c -> c.changeToState(CardStateName.Original));
|
||||
}
|
||||
|
||||
final Card best = ComputerUtilCard.getBestAI(tgtCards);
|
||||
if (sa.usesTargeting() && !sa.isTargetNumberValid()) {
|
||||
sa.getTargets().add(best);
|
||||
|
||||
Reference in New Issue
Block a user