- A somewhat cleaner fix for Token AI that fully separates the logic for detecting and processing mixed player/planeswalker target lists.

This commit is contained in:
Agetian
2016-07-20 04:18:12 +00:00
parent 1acd48be9e
commit 7d89f5ee70
2 changed files with 48 additions and 12 deletions

View File

@@ -222,14 +222,29 @@ public abstract class SpellAbilityAi {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) { public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
T firstOption = Iterables.getFirst(options, null); boolean hasPlayer = false;
boolean hasCard = false;
if (firstOption instanceof Card) { boolean hasPlaneswalker = false;
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
for (T ent : options) {
if (ent instanceof Player) {
hasPlayer = true;
} else if (ent instanceof Card) {
hasCard = true;
if (((Card)ent).isPlaneswalker()) {
hasPlaneswalker = true;
}
}
} }
if (firstOption instanceof Player) {
if (hasPlayer && hasPlaneswalker) {
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
} else if (hasCard) {
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
} else if (hasPlayer) {
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options); return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
} }
return null; return null;
} }
@@ -239,16 +254,20 @@ public abstract class SpellAbilityAi {
} }
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) { protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleEntity is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null); return Iterables.getFirst(options, null);
} }
protected GameEntity chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) { protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleEntity is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null); return Iterables.getFirst(options, null);
} }
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected static boolean isUselessCreature(Player ai, Card c) { protected static boolean isUselessCreature(Player ai, Card c) {
if (c == null) { if (c == null) {

View File

@@ -305,11 +305,30 @@ public class TokenAi extends SpellAbilityAi {
return true; return true;
} }
/* (non-Javadoc) /*
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.player.Player> options) * @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.player.Player> options)
*/ */
@Override @Override
protected GameEntity chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) { protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
// TODO: AILogic
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
Card attacker = spawnToken(ai, sa);
for (Player p : options) {
if (!ComputerUtilCard.canBeBlockedProfitably(p, attacker)) {
return p;
}
}
}
return Iterables.getFirst(options, null);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options)
*/
@Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
// TODO: AILogic // TODO: AILogic
Combat combat = ai.getGame().getCombat(); Combat combat = ai.getGame().getCombat();
// TokenAttacking // TokenAttacking
@@ -323,8 +342,6 @@ public class TokenAi extends SpellAbilityAi {
} }
// 2. Otherwise, go through the list of options one by one, choose the first one that can't be blocked profitably. // 2. Otherwise, go through the list of options one by one, choose the first one that can't be blocked profitably.
Card attacker = spawnToken(ai, sa); Card attacker = spawnToken(ai, sa);
// TODO: this list potentially includes planeswalkers represented as Card objects. This may need further improvement
// (SpellAbilityAi::chooseSingleGameEntity normally expects the list of options to be of the same type).
for (GameEntity p : options) { for (GameEntity p : options) {
if (p instanceof Player && !ComputerUtilCard.canBeBlockedProfitably((Player)p, attacker)) { if (p instanceof Player && !ComputerUtilCard.canBeBlockedProfitably((Player)p, attacker)) {
return p; return p;