Fix ClassCastException when adding attacker

This commit is contained in:
tool4EvEr
2023-04-26 20:21:01 +02:00
parent 49bf19948e
commit 19510914b4
8 changed files with 27 additions and 24 deletions

View File

@@ -2478,10 +2478,15 @@ public class ComputerUtilCombat {
public static GameEntity addAttackerToCombat(SpellAbility sa, Card attacker, Iterable<? extends GameEntity> defenders) { public static GameEntity addAttackerToCombat(SpellAbility sa, Card attacker, Iterable<? extends GameEntity> defenders) {
Combat combat = sa.getHostCard().getGame().getCombat(); Combat combat = sa.getHostCard().getGame().getCombat();
if (combat != null) { if (combat != null) {
// 1. If the card that spawned the attacker was sent at a planeswalker, attack the same. Consider improving.
GameEntity def = combat.getDefenderByAttacker(sa.getHostCard()); GameEntity def = combat.getDefenderByAttacker(sa.getHostCard());
if (def instanceof Card && ((Card)def).isPlaneswalker() && Iterables.contains(defenders, def)) { // 1. If the card that spawned the attacker was sent at a card, attack the same. Consider improving.
return def; if (def instanceof Card && Iterables.contains(defenders, def)) {
if (((Card)def).isPlaneswalker()) {
return def;
}
if (((Card)def).isBattle()) {
return def;
}
} }
// 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.
for (GameEntity p : defenders) { for (GameEntity p : defenders) {

View File

@@ -314,21 +314,21 @@ public abstract class SpellAbilityAi {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) { public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
boolean hasPlayer = false; boolean hasPlayer = false;
boolean hasCard = false; boolean hasCard = false;
boolean hasPlaneswalker = false; boolean hasAttackableCard = false;
for (T ent : options) { for (T ent : options) {
if (ent instanceof Player) { if (ent instanceof Player) {
hasPlayer = true; hasPlayer = true;
} else if (ent instanceof Card) { } else if (ent instanceof Card) {
hasCard = true; hasCard = true;
if (((Card)ent).isPlaneswalker()) { if (((Card)ent).isPlaneswalker() || ((Card)ent).isBattle()) {
hasPlaneswalker = true; hasAttackableCard = true;
} }
} }
} }
if (hasPlayer && hasPlaneswalker) { if (hasPlayer && hasAttackableCard) {
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options, params); return (T) chooseSingleAttackableEntity(ai, sa, (Collection<GameEntity>) options, params);
} else if (hasCard) { } else if (hasCard) {
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer, params); return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer, params);
} else if (hasPlayer) { } else if (hasPlayer) {
@@ -353,7 +353,7 @@ public abstract class SpellAbilityAi {
return Iterables.getFirst(options, null); return Iterables.getFirst(options, null);
} }
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) { protected GameEntity chooseSingleAttackableEntity(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); 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); return Iterables.getFirst(options, null);
} }

View File

@@ -1760,12 +1760,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
@Override @Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) { protected GameEntity chooseSingleAttackableEntity(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
if (params != null && params.containsKey("Attacker")) { if (params != null && params.containsKey("Attacker")) {
return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options);
} }
// should not be reached // should not be reached
return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); return super.chooseSingleAttackableEntity(ai, sa, options, params);
} }
private boolean doSacAndReturnFromGraveLogic(final Player ai, final SpellAbility sa) { private boolean doSacAndReturnFromGraveLogic(final Player ai, final SpellAbility sa) {

View File

@@ -1,13 +1,11 @@
package forge.ai.ability; package forge.ai.ability;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.player.Player; import forge.game.player.Player;

View File

@@ -261,12 +261,12 @@ public class CopyPermanentAi extends SpellAbilityAi {
} }
@Override @Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) { protected GameEntity chooseSingleAttackableEntity(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
if (params != null && params.containsKey("Attacker")) { if (params != null && params.containsKey("Attacker")) {
return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options);
} }
// should not be reached // should not be reached
return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); return super.chooseSingleAttackableEntity(ai, sa, options, params);
} }
} }

View File

@@ -196,12 +196,12 @@ public class DigAi extends SpellAbilityAi {
} }
@Override @Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) { protected GameEntity chooseSingleAttackableEntity(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
if (params != null && params.containsKey("Attacker")) { if (params != null && params.containsKey("Attacker")) {
return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options);
} }
// should not be reached // should not be reached
return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); return super.chooseSingleAttackableEntity(ai, sa, options, params);
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -326,12 +326,12 @@ public class TokenAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options) * @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options)
*/ */
@Override @Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) { protected GameEntity chooseSingleAttackableEntity(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
if (params != null && params.containsKey("Attacker")) { if (params != null && params.containsKey("Attacker")) {
return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options);
} }
// should not be reached // should not be reached
return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); return super.chooseSingleAttackableEntity(ai, sa, options, params);
} }
/** /**

View File

@@ -625,17 +625,17 @@ public class Combat {
} }
} }
for (Card pw : getDefendingPlaneswalkers()) { for (Card battleOrPW : Iterables.filter(attackableEntries, Card.class)) {
if (pw.equals(c)) { if (battleOrPW.equals(c)) {
Multimap<GameEntity, AttackingBand> attackerBuffer = ArrayListMultimap.create(); Multimap<GameEntity, AttackingBand> attackerBuffer = ArrayListMultimap.create();
Collection<AttackingBand> bands = attackedByBands.get(c); Collection<AttackingBand> bands = attackedByBands.get(c);
for (AttackingBand abPW : bands) { for (AttackingBand abDef : bands) {
unregisterDefender(c, abPW); unregisterDefender(c, abDef);
// Rule 506.4c workaround to keep creatures in combat // Rule 506.4c workaround to keep creatures in combat
Card fake = new Card(-1, c.getGame()); Card fake = new Card(-1, c.getGame());
fake.setName("<Nothing>"); fake.setName("<Nothing>");
fake.setController(c.getController(), 0); fake.setController(c.getController(), 0);
attackerBuffer.put(fake, abPW); attackerBuffer.put(fake, abDef);
} }
bands.clear(); bands.clear();
attackedByBands.putAll(attackerBuffer); attackedByBands.putAll(attackerBuffer);