- Initial checkin for Combat refactor.

- Introduction of AttackingBands which group Attackers, Blockers and Blocked state.
This commit is contained in:
Sol
2013-06-14 12:56:05 +00:00
parent dea4a38a6c
commit 786650f660
12 changed files with 343 additions and 351 deletions

1
.gitattributes vendored
View File

@@ -14405,6 +14405,7 @@ src/main/java/forge/game/ai/ComputerUtilCard.java -text
src/main/java/forge/game/ai/ComputerUtilCombat.java -text src/main/java/forge/game/ai/ComputerUtilCombat.java -text
src/main/java/forge/game/ai/ComputerUtilCost.java -text src/main/java/forge/game/ai/ComputerUtilCost.java -text
src/main/java/forge/game/ai/ComputerUtilMana.java -text src/main/java/forge/game/ai/ComputerUtilMana.java -text
src/main/java/forge/game/combat/AttackingBand.java -text
src/main/java/forge/game/event/GameEvent.java -text src/main/java/forge/game/event/GameEvent.java -text
src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text
src/main/java/forge/game/event/GameEventAttackersDeclared.java -text src/main/java/forge/game/event/GameEventAttackersDeclared.java -text

View File

@@ -199,8 +199,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE)) { if (ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE)) {
return false; return false;
} }
List<Card> attackers = new ArrayList<Card>(); List<Card> attackers = ai.getGame().getCombat().getUnblockedAttackers();
attackers.addAll(ai.getGame().getCombat().getUnblockedAttackers());
boolean lowerCMC = false; boolean lowerCMC = false;
for (Card attacker : attackers) { for (Card attacker : attackers) {
if (attacker.getCMC() < source.getCMC()) { if (attacker.getCMC() < source.getCMC()) {
@@ -499,6 +498,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* @return Card * @return Card
*/ */
private static Card chooseCreature(final Player ai, List<Card> list) { private static Card chooseCreature(final Player ai, List<Card> list) {
// Creating a new combat for testing purposes.
Combat combat = new Combat(); Combat combat = new Combat();
combat.initiatePossibleDefenders(ai); combat.initiatePossibleDefenders(ai);
List<Card> attackers = ai.getOpponent().getCreaturesInPlay(); List<Card> attackers = ai.getOpponent().getCreaturesInPlay();

View File

@@ -18,6 +18,7 @@ import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat; import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilCost;
import forge.game.phase.Combat;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -68,6 +69,7 @@ public class ProtectAi extends SpellAbilityAi {
private static List<Card> getProtectCreatures(final Player ai, final SpellAbility sa) { private static List<Card> getProtectCreatures(final Player ai, final SpellAbility sa) {
final ArrayList<String> gains = AbilityUtils.getProtectionList(sa); final ArrayList<String> gains = AbilityUtils.getProtectionList(sa);
final Game game = ai.getGame(); final Game game = ai.getGame();
final Combat combat = game.getCombat();
List<Card> list = ai.getCreaturesInPlay(); List<Card> list = ai.getCreaturesInPlay();
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@@ -97,9 +99,10 @@ public class ProtectAi extends SpellAbilityAi {
} }
// is the creature in blocked and the blocker would survive // is the creature in blocked and the blocker would survive
if (game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) // TODO Potential NPE here if no blockers are actually left
&& game.getCombat().isAttacking(c) && game.getCombat().isBlocked(c) if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.blockerWouldBeDestroyed(ai, game.getCombat().getBlockers(c).get(0))) { && combat.isAttacking(c) && game.getCombat().isBlocked(c)
&& ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0))) {
return true; return true;
} }

View File

@@ -386,6 +386,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
protected boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int defense, final int attack, protected boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int defense, final int attack,
final List<String> keywords) { final List<String> keywords) {
final Game game = ai.getGame(); final Game game = ai.getGame();
final Combat combat = game.getCombat();
PhaseHandler phase = game.getPhaseHandler(); PhaseHandler phase = game.getPhaseHandler();
if (!c.canBeTargetedBy(sa)) { if (!c.canBeTargetedBy(sa)) {
@@ -437,22 +438,22 @@ public abstract class PumpAiBase extends SpellAbilityAi {
// is the creature unblocked and the spell will pump its power? // is the creature unblocked and the spell will pump its power?
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& game.getCombat().isAttacking(c) && game.getCombat().isUnblocked(c) && attack > 0) { && combat.isAttacking(c) && combat.isUnblocked(c) && attack > 0) {
return true; return true;
} }
// is the creature blocked and the blocker would survive // is the creature blocked and the blocker would survive
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && attack > 0 if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && attack > 0
&& game.getCombat().isAttacking(c) && combat.isAttacking(c)
&& game.getCombat().isBlocked(c) && combat.isBlocked(c)
&& game.getCombat().getBlockers(c) != null && combat.getBlockers(c) != null
&& !game.getCombat().getBlockers(c).isEmpty() && !combat.getBlockers(c).isEmpty()
&& !ComputerUtilCombat.blockerWouldBeDestroyed(ai, game.getCombat().getBlockers(c).get(0))) { && !ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0))) {
return true; return true;
} }
// if the life of the computer is in danger, try to pump blockers blocking Tramplers // if the life of the computer is in danger, try to pump blockers blocking Tramplers
List<Card> blockedBy = game.getCombat().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");

View File

@@ -91,7 +91,7 @@ public class PumpAllAi extends PumpAiBase {
} }
totalPower += Math.min(c.getNetAttack(), power * -1); totalPower += Math.min(c.getNetAttack(), power * -1);
if (phase == PhaseType.COMBAT_DECLARE_BLOCKERS if (phase == PhaseType.COMBAT_DECLARE_BLOCKERS
&& game.getCombat().getUnblockedAttackers().contains(c)) { && game.getCombat().isUnblocked(c)) {
if (ComputerUtilCombat.lifeInDanger(sa.getActivatingPlayer(), game.getCombat())) { if (ComputerUtilCombat.lifeInDanger(sa.getActivatingPlayer(), game.getCombat())) {
return true; return true;
} }

View File

@@ -507,10 +507,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("Ninjutsu") || sa.hasParam("Attacking")) { if (sa.hasParam("Ninjutsu") || sa.hasParam("Attacking")) {
// What should they attack? // What should they attack?
// TODO Ninjutsu needs to actually select the Defender, instead of auto selecting player
List<GameEntity> defenders = game.getCombat().getDefenders(); List<GameEntity> defenders = game.getCombat().getDefenders();
if (!defenders.isEmpty()) { if (!defenders.isEmpty()) {
game.getCombat().addAttacker(tgtC, defenders.get(0)); // Blockeres are already declared, set this to unblocked
game.getCombat().addUnblockedAttacker(tgtC); game.getCombat().addAttacker(tgtC, defenders.get(0), false);
} }
} }
if (sa.hasParam("Tapped") || sa.hasParam("Ninjutsu")) { if (sa.hasParam("Tapped") || sa.hasParam("Ninjutsu")) {

View File

@@ -787,7 +787,7 @@ public class AiAttackController {
final int blockNum = this.blockers.size(); final int blockNum = this.blockers.size();
int attackNum = 0; int attackNum = 0;
int damage = 0; int damage = 0;
List<Card> attacking = combat.getAttackersByDefenderSlot(iDefender); List<Card> attacking = combat.getAttackersOf(defender);
CardLists.sortByPowerAsc(attacking); CardLists.sortByPowerAsc(attacking);
for (Card atta : attacking) { for (Card atta : attacking) {
if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers)) { if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers)) {

View File

@@ -219,7 +219,7 @@ public class ComputerUtilCombat {
int damage = 0; int damage = 0;
final List<Card> attackers = combat.getAttackersByDefenderSlot(0); final List<Card> attackers = combat.getAttackersOf(ai);
final List<Card> unblocked = new ArrayList<Card>(); final List<Card> unblocked = new ArrayList<Card>();
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -261,7 +261,7 @@ public class ComputerUtilCombat {
int poison = 0; int poison = 0;
final List<Card> attackers = combat.getAttackersByDefenderSlot(0); final List<Card> attackers = combat.getAttackersOf(ai);
final List<Card> unblocked = new ArrayList<Card>(); final List<Card> unblocked = new ArrayList<Card>();
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -305,7 +305,7 @@ public class ComputerUtilCombat {
} }
// check for creatures that must be blocked // check for creatures that must be blocked
final List<Card> attackers = combat.getAttackersByDefenderSlot(0); final List<Card> attackers = combat.getAttackersOf(ai);
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -359,7 +359,7 @@ public class ComputerUtilCombat {
} }
// check for creatures that must be blocked // check for creatures that must be blocked
final List<Card> attackers = combat.getAttackersByDefenderSlot(0); final List<Card> attackers = combat.getAttackersOf(ai);
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -551,7 +551,8 @@ public class ComputerUtilCombat {
*/ */
public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker) { public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker) {
final Game game = ai.getGame(); final Game game = ai.getGame();
final List<Card> blockers = game.getCombat().getBlockers(attacker); final Combat combat = game.getCombat();
final List<Card> blockers = combat.getBlockers(attacker);
for (final Card defender : blockers) { for (final Card defender : blockers) {
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, game.getCombat(), true) if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, game.getCombat(), true)

View File

@@ -0,0 +1,91 @@
package forge.game.combat;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.GameEntity;
import forge.item.IPaperCard.Predicates;
/**
* TODO: Write javadoc for this type.
*
*/
public class AttackingBand implements Comparable<AttackingBand> {
private List<Card> attackers = new ArrayList<Card>();
private List<Card> blockers = new ArrayList<Card>();
private GameEntity defender = null;
private Boolean blocked = null;
public AttackingBand(List<Card> band, GameEntity def) {
attackers.addAll(band);
this.defender = def;
}
public AttackingBand(Card card, GameEntity def) {
attackers.add(card);
this.defender = def;
}
public List<Card> getAttackers() { return this.attackers; }
public List<Card> getBlockers() { return this.blockers; }
public GameEntity getDefender() { return this.defender; }
public void addAttacker(Card card) { attackers.add(card); }
public void removeAttacker(Card card) { attackers.remove(card); }
public void addBlocker(Card card) { blockers.add(card); }
public void removeBlocker(Card card) { blockers.remove(card); }
public void setBlockers(List<Card> blockers) { this.blockers = blockers; }
public void setDefender(GameEntity def) { this.defender = def; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public boolean getBlocked() { return this.blocked != null && this.blocked.booleanValue(); }
public void calculateBlockedState() { this.blocked = !this.blockers.isEmpty(); }
public boolean canJoinBand(Card card) {
// If this card has banding it can definitely join
if (card.hasKeyword("Banding")) {
return true;
}
// If all of the cards in the Band have banding, it can definitely join
if (attackers.size() == CardLists.getKeyword(attackers, "Banding").size()) {
return true;
}
// TODO add checks for bands with other
//List<Card> bandsWithOther = CardLists.getKeyword(attackers, "Bands with Other");
return false;
}
/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(AttackingBand o) {
if (o == null) {
return -1;
}
List<Card> compareAttackers = o.getAttackers();
int sizeDiff = this.attackers.size() - compareAttackers.size();
if (sizeDiff > 0) {
return 1;
} else if (sizeDiff < 0) {
return -1;
} else if (sizeDiff == 0 && this.attackers.isEmpty()) {
return 0;
}
return this.attackers.get(0).compareTo(compareAttackers.get(0));
}
}

View File

@@ -19,11 +19,9 @@ package forge.game.phase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -33,6 +31,7 @@ import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.GameEntity; import forge.GameEntity;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.combat.AttackingBand;
import forge.game.event.GameEventBlockerAssigned; import forge.game.event.GameEventBlockerAssigned;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -46,34 +45,23 @@ import forge.game.zone.ZoneType;
* @version $Id$ * @version $Id$
*/ */
public class Combat { public class Combat {
// key is attacker Card // List of AttackingBands
// value is List<Card> of blockers private final List<AttackingBand> attackingBands = new ArrayList<AttackingBand>();
private final Map<Card, List<Card>> attackerMap = new TreeMap<Card, List<Card>>(); // Attacker -> AttackingBand (Attackers can only be in a single band)
private final Map<Card, List<Card>> blockerMap = new TreeMap<Card, List<Card>>(); private final Map<Card, AttackingBand> attackerToBandMap = new TreeMap<Card, AttackingBand>();
// Blocker -> AttackingBands (Blockers can block multiple bands/creatures
private final Map<Card, List<AttackingBand>> blockerToBandsMap = new TreeMap<Card, List<AttackingBand>>();
private final Set<Card> blocked = new HashSet<Card>();
private final Set<Card> unblocked = new HashSet<Card>();
private final HashMap<Card, Integer> defendingDamageMap = new HashMap<Card, Integer>(); private final HashMap<Card, Integer> defendingDamageMap = new HashMap<Card, Integer>();
// Defenders are the Defending Player + Each controlled Planeswalker // Defenders are all Opposing Players + Planeswalker's Controller By Opposing Players
private List<GameEntity> defenders = new ArrayList<GameEntity>();
private Map<GameEntity, List<Card>> defenderMap = new HashMap<GameEntity, List<Card>>(); private Map<GameEntity, List<Card>> defenderMap = new HashMap<GameEntity, List<Card>>();
private Map<Card, List<Card>> blockerDamageAssignmentOrder = new TreeMap<Card, List<Card>>();
// This Hash keeps track of private Map<Card, List<Card>> attackerDamageAssignmentOrder = new TreeMap<Card, List<Card>>();
private final HashMap<Card, GameEntity> attackerToDefender = new HashMap<Card, GameEntity>();
private Player attackingPlayer = null; private Player attackingPlayer = null;
/**
* <p>
* Constructor for Combat.
* </p>
*/
public Combat() {
// Let the Begin Turn/Untap Phase Reset Combat properly
}
/** /**
* <p> * <p>
* reset. * reset.
@@ -81,11 +69,7 @@ public class Combat {
*/ */
public final void reset(Player playerTurn) { public final void reset(Player playerTurn) {
this.resetAttackers(); this.resetAttackers();
this.blocked.clear();
this.unblocked.clear();
this.defendingDamageMap.clear(); this.defendingDamageMap.clear();
this.attackingPlayer = playerTurn; this.attackingPlayer = playerTurn;
this.initiatePossibleDefenders(playerTurn.getOpponents()); this.initiatePossibleDefenders(playerTurn.getOpponents());
@@ -100,7 +84,6 @@ public class Combat {
* a {@link forge.game.player.Player} object. * a {@link forge.game.player.Player} object.
*/ */
public final void initiatePossibleDefenders(final Iterable<Player> defenders) { public final void initiatePossibleDefenders(final Iterable<Player> defenders) {
this.defenders.clear();
this.defenderMap.clear(); this.defenderMap.clear();
for (Player defender : defenders) { for (Player defender : defenders) {
fillDefenderMaps(defender); fillDefenderMaps(defender);
@@ -108,22 +91,19 @@ public class Combat {
} }
public final void initiatePossibleDefenders(final Player defender) { public final void initiatePossibleDefenders(final Player defender) {
this.defenders.clear();
this.defenderMap.clear(); this.defenderMap.clear();
fillDefenderMaps(defender); fillDefenderMaps(defender);
} }
public final boolean isCombat() { public final boolean isCombat() {
return !attackerMap.isEmpty(); return !attackingBands.isEmpty();
} }
private void fillDefenderMaps(final Player defender) { private void fillDefenderMaps(final Player defender) {
this.defenders.add(defender);
this.defenderMap.put(defender, new ArrayList<Card>()); this.defenderMap.put(defender, new ArrayList<Card>());
List<Card> planeswalkers = List<Card> planeswalkers =
CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS); CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS);
for (final Card pw : planeswalkers) { for (final Card pw : planeswalkers) {
this.defenders.add(pw);
this.defenderMap.put(pw, new ArrayList<Card>()); this.defenderMap.put(pw, new ArrayList<Card>());
} }
} }
@@ -136,7 +116,7 @@ public class Combat {
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final List<GameEntity> getDefenders() { public final List<GameEntity> getDefenders() {
return this.defenders; return new ArrayList<GameEntity>(this.defenderMap.keySet());
} }
/** /**
@@ -148,8 +128,8 @@ public class Combat {
* a {@link java.util.ArrayList} object. * a {@link java.util.ArrayList} object.
*/ */
public final void setDefenders(final List<GameEntity> newDef) { public final void setDefenders(final List<GameEntity> newDef) {
this.defenders = newDef; this.defenderMap.clear();
for (GameEntity entity : this.defenders) { for (GameEntity entity : newDef) {
this.defenderMap.put(entity, new ArrayList<Card>()); this.defenderMap.put(entity, new ArrayList<Card>());
} }
} }
@@ -164,7 +144,7 @@ public class Combat {
public final List<Card> getDefendingPlaneswalkers() { public final List<Card> getDefendingPlaneswalkers() {
final List<Card> pwDefending = new ArrayList<Card>(); final List<Card> pwDefending = new ArrayList<Card>();
for (final GameEntity o : this.defenders) { for (final GameEntity o : this.defenderMap.keySet()) {
if (o instanceof Card) { if (o instanceof Card) {
pwDefending.add((Card) o); pwDefending.add((Card) o);
} }
@@ -196,18 +176,6 @@ public class Combat {
return this.attackingPlayer; return this.attackingPlayer;
} }
/**
* <p>
* Getter for the field <code>defendingDamageMap</code>.
* </p>
*
* @return a {@link java.util.HashMap} object.
*/
public final HashMap<Card, Integer> getDefendingDamageMap() {
return this.defendingDamageMap;
}
/** /**
* <p> * <p>
* addDefendingDamage. * addDefendingDamage.
@@ -235,11 +203,6 @@ public class Combat {
} }
} }
public final List<Card> getAttackersByDefenderSlot(int slot) {
GameEntity entity = this.defenders.get(slot);
return this.defenderMap.get(entity);
}
public final List<Card> getAttackersOf(GameEntity defender) { public final List<Card> getAttackersOf(GameEntity defender) {
return defenderMap.get(defender); return defenderMap.get(defender);
} }
@@ -254,28 +217,39 @@ public class Combat {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isAttacking(final Card c) { public final boolean isAttacking(final Card c) {
return this.attackerMap.containsKey(c); return this.attackerToBandMap.containsKey(c);
} }
/**
* <p>
* addAttacker.
* </p>
*
* @param c
* a {@link forge.Card} object.
* @param defender
* a GameEntity object.
*/
public final void addAttacker(final Card c, GameEntity defender) { public final void addAttacker(final Card c, GameEntity defender) {
if (!defenders.contains(defender)) { addAttacker(c, defender, null);
}
public final void addAttacker(final Card c, GameEntity defender, AttackingBand band) {
addAttacker(c, defender, band, null);
}
public final void addAttacker(final Card c, GameEntity defender, boolean blocked) {
addAttacker(c, defender, null, blocked);
}
public final void addAttacker(final Card c, GameEntity defender, AttackingBand band, Boolean blocked) {
if (!defenderMap.containsKey(defender)) {
System.out.println("Trying to add Attacker " + c + " to missing defender " + defender); System.out.println("Trying to add Attacker " + c + " to missing defender " + defender);
return; return;
} }
this.attackerMap.put(c, new ArrayList<Card>()); if (band == null || !this.attackingBands.contains(band)) {
this.attackerToDefender.put(c, defender); band = new AttackingBand(c, defender);
if (blocked != null) {
band.setBlocked(blocked.booleanValue());
}
this.attackingBands.add(band);
} else {
band.addAttacker(c);
}
// Attacker -> Defender and Defender -> Attacker map to Bands?
this.defenderMap.get(defender).add(c); this.defenderMap.get(defender).add(c);
this.attackerToBandMap.put(c, band);
} }
/** /**
@@ -288,7 +262,7 @@ public class Combat {
* @return a {@link java.lang.Object} object. * @return a {@link java.lang.Object} object.
*/ */
public final GameEntity getDefenderByAttacker(final Card c) { public final GameEntity getDefenderByAttacker(final Card c) {
return this.attackerToDefender.get(c); return this.attackerToBandMap.get(c).getDefender();
} }
public final Player getDefenderPlayerByAttacker(final Card c) { public final Player getDefenderPlayerByAttacker(final Card c) {
@@ -307,26 +281,30 @@ public class Combat {
} }
public final GameEntity getDefendingEntity(final Card c) { public final GameEntity getDefendingEntity(final Card c) {
GameEntity defender = this.attackerToDefender.get(c); GameEntity defender = this.getDefenderByAttacker(c);
if (this.defenderMap.containsKey(defender)) {
if (this.defenders.contains(defender)) {
return defender; return defender;
} }
System.out.println("Attacker " + c + " missing defender " + defender); System.out.println("Attacker " + c + " missing defender " + defender);
return null; return null;
} }
public final AttackingBand getBandByAttacker(final Card c) {
return this.attackerToBandMap.get(c);
}
/** /**
* <p> * <p>
* resetAttackers. * resetAttackers.
* </p> * </p>
*/ */
public final void resetAttackers() { public final void resetAttackers() {
this.attackerMap.clear(); this.attackingBands.clear();
this.attackerToDefender.clear(); this.blockerToBandsMap.clear();
this.blockerMap.clear(); this.attackerToBandMap.clear();
this.blockerDamageAssignmentOrder.clear();
this.attackerDamageAssignmentOrder.clear();
} }
/** /**
@@ -336,28 +314,24 @@ public class Combat {
* *
* @return an array of {@link forge.Card} objects. * @return an array of {@link forge.Card} objects.
*/ */
public final List<Card> getAttackers() { public final List<AttackingBand> getAttackingBands() {
return new ArrayList<Card>(this.attackerMap.keySet()); return attackingBands;
} // getAttackers() } // getAttackers()
/** public final List<Card> getAttackers() {
* <p> List<Card> attackers = new ArrayList<Card>();
* isBlocked. for(AttackingBand band : attackingBands) {
* </p> attackers.addAll(band.getAttackers());
* }
* @param attacker return attackers;
* a {@link forge.Card} object. }
* @return a boolean.
*/
public final boolean isBlocked(final Card attacker) { public final boolean isBlocked(final Card attacker) {
return this.blocked.contains(attacker); return this.attackerToBandMap.get(attacker).getBlocked();
} }
public final void setBlocked(final Card attacker) { public final void setBlocked(final Card attacker) {
if (!this.blocked.contains(attacker)) { this.attackerToBandMap.get(attacker).setBlocked(true);
this.blocked.add(attacker);
this.unblocked.remove(attacker);
}
} }
/** /**
@@ -371,24 +345,23 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void addBlocker(final Card attacker, final Card blocker) { public final void addBlocker(final Card attacker, final Card blocker) {
this.blocked.add(attacker); AttackingBand band = this.attackerToBandMap.get(attacker);
this.attackerMap.get(attacker).add(blocker); band.addBlocker(blocker);
if (!this.blockerMap.containsKey(blocker)) {
this.blockerMap.put(blocker, Lists.newArrayList(attacker)); if (!this.blockerToBandsMap.containsKey(blocker)) {
this.blockerToBandsMap.put(blocker, Lists.newArrayList(band));
} else { } else {
this.blockerMap.get(blocker).add(attacker); this.blockerToBandsMap.get(blocker).add(band);
} }
attacker.getGame().fireEvent(new GameEventBlockerAssigned()); attacker.getGame().fireEvent(new GameEventBlockerAssigned());
} }
public final void removeBlockAssignment(final Card attacker, final Card blocker) { public final void removeBlockAssignment(final Card attacker, final Card blocker) {
this.attackerMap.get(attacker).remove(blocker); AttackingBand band = this.attackerToBandMap.get(attacker);
this.blockerMap.get(blocker).remove(attacker); band.removeBlocker(blocker);
if (this.attackerMap.get(attacker).isEmpty()) { this.blockerToBandsMap.get(blocker).remove(attacker);
this.blocked.remove(attacker); if (this.blockerToBandsMap.get(blocker).isEmpty()) {
} this.blockerToBandsMap.remove(blocker);
if (this.blockerMap.get(blocker).isEmpty()) {
this.blockerMap.remove(blocker);
} }
} }
@@ -401,17 +374,12 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void undoBlockingAssignment(final Card blocker) { public final void undoBlockingAssignment(final Card blocker) {
final List<Card> att = this.getAttackers(); final List<AttackingBand> att = this.blockerToBandsMap.get(blocker);
for (final Card attacker : att) { for (final AttackingBand band : att) {
if (this.getBlockers(attacker).contains(blocker)) { band.removeBlocker(blocker);
this.getBlockingAttackerList(attacker).remove(blocker);
if (this.getBlockers(attacker).isEmpty()) {
this.blocked.remove(attacker);
} }
this.blockerToBandsMap.remove(blocker);
} }
}
this.blockerMap.remove(blocker);
} // undoBlockingAssignment(Card)
/** /**
* <p> * <p>
@@ -422,25 +390,37 @@ public class Combat {
*/ */
public final List<Card> getAllBlockers() { public final List<Card> getAllBlockers() {
final List<Card> block = new ArrayList<Card>(); final List<Card> block = new ArrayList<Card>();
block.addAll(blockerMap.keySet()); block.addAll(blockerToBandsMap.keySet());
return block; return block;
} // getAllBlockers() }
/** public final List<Card> getBlockers(final AttackingBand band) {
* <p> List<Card> list = band.getBlockers();
* getBlockers. if (list == null) {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* @return a {@link forge.CardList} object.
*/
public final List<Card> getBlockers(final Card attacker) {
if (this.getBlockingAttackerList(attacker) == null) {
return new ArrayList<Card>(); return new ArrayList<Card>();
} else { } else {
return new ArrayList<Card>(this.getBlockingAttackerList(attacker)); return new ArrayList<Card>(list);
}
}
public final List<Card> getBlockers(final Card card) {
return getBlockers(card, false);
}
public final List<Card> getBlockers(final Card card, boolean ordered) {
// If requesting the ordered blocking lsit pass true, directly.
List<Card> list = null;
if (ordered) {
list = this.attackerDamageAssignmentOrder.containsKey(card) ? this.attackerDamageAssignmentOrder.get(card) : null;
} else {
list = this.getBandByAttacker(card).getBlockers();
}
if (list == null) {
return new ArrayList<Card>();
} else {
return new ArrayList<Card>(list);
} }
} }
@@ -454,25 +434,15 @@ public class Combat {
* @return a {@link forge.Card} object. * @return a {@link forge.Card} object.
*/ */
public final List<Card> getAttackersBlockedBy(final Card blocker) { public final List<Card> getAttackersBlockedBy(final Card blocker) {
if (blockerMap.containsKey(blocker)) { List<Card> blocked = new ArrayList<Card>();
return blockerMap.get(blocker);
}
return new ArrayList<Card>();
}
/** if (blockerToBandsMap.containsKey(blocker)) {
* <p> for(AttackingBand band : blockerToBandsMap.get(blocker)) {
* getList. blocked.addAll(band.getAttackers());
* </p> }
* }
* @param attacker return blocked;
* a {@link forge.Card} object.
* @return a {@link forge.CardList} object.
*/
private List<Card> getBlockingAttackerList(final Card attacker) {
return this.attackerMap.get(attacker);
} }
/** /**
* <p> * <p>
@@ -499,7 +469,8 @@ public class Combat {
return players; return players;
} }
// return all defenders // Can't figure out who it's related to... just return all???
// return all defending players
List<GameEntity> defenders = this.getDefenders(); List<GameEntity> defenders = this.getDefenders();
for (GameEntity ge : defenders) { for (GameEntity ge : defenders) {
if (ge instanceof Player) { if (ge instanceof Player) {
@@ -509,56 +480,42 @@ public class Combat {
return players; return players;
} }
/** public void setAttackerDamageAssignmentOrder(final Card attacker, final List<Card> blockers) {
* <p> this.attackerDamageAssignmentOrder.put(attacker, blockers);
* setBlockerList.
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* @param blockers
* a {@link forge.CardList} object.
*/
public void setBlockerList(final Card attacker, final List<Card> blockers) {
this.attackerMap.put(attacker, blockers);
} }
public void setAttackersBlockedByList(final Card blocker, final List<Card> attackers) { public void setBlockerDamageAssignmentOrder(final Card blocker, final List<Card> attackers) {
this.blockerMap.put(blocker, attackers); this.blockerDamageAssignmentOrder.put(blocker, attackers);
} }
/**
* <p>
* removeFromCombat.
* </p>
*
* @param c
* a {@link forge.Card} object.
*/
public final void removeFromCombat(final Card c) { public final void removeFromCombat(final Card c) {
// todo(sol) add some more solid error checking in here
// is card an attacker? // is card an attacker?
if (this.attackerMap.containsKey(c)) { if (this.attackerToBandMap.containsKey(c)) {
// Keep track of all of the different maps // Soooo many maps to keep track of
List<Card> blockers = this.attackerMap.get(c); AttackingBand band = this.attackerToBandMap.get(c);
this.attackerMap.remove(c); band.removeAttacker(c);
this.attackerToBandMap.remove(c);
this.attackerDamageAssignmentOrder.remove(c);
List<Card> blockers = band.getBlockers();
for (Card b : blockers) { for (Card b : blockers) {
this.blockerMap.get(b).remove(c); if (band.getAttackers().isEmpty()) {
this.blockerToBandsMap.get(b).remove(c);
}
// Clear removed attacker from assignment order
this.blockerDamageAssignmentOrder.get(b).remove(c);
} }
// Keep track of all of the different maps this.defenderMap.get(band.getDefender()).remove(c);
GameEntity entity = this.attackerToDefender.get(c); } else if (this.blockerDamageAssignmentOrder.containsKey(c)) { // card is a blocker
this.attackerToDefender.remove(c); List<AttackingBand> attackers = this.blockerToBandsMap.get(c);
this.defenderMap.get(entity).remove(c);
} else if (this.blockerMap.containsKey(c)) { // card is a blocker
List<Card> attackers = this.blockerMap.get(c);
boolean stillDeclaring = c.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS); this.blockerToBandsMap.remove(c);
this.blockerMap.remove(c); this.blockerDamageAssignmentOrder.remove(c);
for (Card a : attackers) { for (AttackingBand a : attackers) {
this.attackerMap.get(a).remove(c); a.removeBlocker(c);
if (stillDeclaring && this.attackerMap.get(a).isEmpty()) { for(Card atk : a.getAttackers()) {
this.blocked.remove(a); this.attackerDamageAssignmentOrder.get(atk).remove(c);
} }
} }
} }
@@ -571,7 +528,9 @@ public class Combat {
*/ */
public final void removeAbsentCombatants() { public final void removeAbsentCombatants() {
final List<Card> all = new ArrayList<Card>(); final List<Card> all = new ArrayList<Card>();
all.addAll(this.getAttackers()); for(AttackingBand band : this.getAttackingBands()) {
all.addAll(band.getAttackers());
}
all.addAll(this.getAllBlockers()); all.addAll(this.getAllBlockers());
for (int i = 0; i < all.size(); i++) { for (int i = 0; i < all.size(); i++) {
@@ -587,21 +546,18 @@ public class Combat {
* </p> * </p>
*/ */
public final void setUnblockedAttackers() { public final void setUnblockedAttackers() {
final List<Card> attacking = this.getAttackers(); final List<AttackingBand> attacking = this.getAttackingBands();
for (final AttackingBand band : attacking) {
for (final Card attacker : attacking) { band.calculateBlockedState();
final List<Card> block = this.getBlockers(attacker);
if (block.isEmpty()) {
// this damage is assigned to a player by setPlayerDamage()
this.addUnblockedAttacker(attacker);
if (band.getBlocked()) {
for (Card attacker : band.getAttackers()) {
// Run Unblocked Trigger // Run Unblocked Trigger
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Attacker", attacker); runParams.put("Attacker", attacker);
runParams.put("Defender",this.getDefenderByAttacker(attacker)); runParams.put("Defender",this.getDefenderByAttacker(attacker));
attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false); attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false);
}
} }
} }
} }
@@ -612,13 +568,25 @@ public class Combat {
for (final Card blocker : blockers) { for (final Card blocker : blockers) {
if (blocker.hasDoubleStrike() || blocker.hasFirstStrike() == firstStrikeDamage) { if (blocker.hasDoubleStrike() || blocker.hasFirstStrike() == firstStrikeDamage) {
List<Card> attackers = this.getAttackersBlockedBy(blocker); List<Card> attackers = this.blockerDamageAssignmentOrder.get(blocker);
final int damage = blocker.getNetCombatDamage(); final int damage = blocker.getNetCombatDamage();
if (!attackers.isEmpty()) { if (!attackers.isEmpty()) {
Player attackingPlayer = this.getAttackingPlayer();
Player assigningPlayer = blocker.getController();
List<Card> bandingAttackers = CardLists.getKeyword(attackers, "Banding");
if (!bandingAttackers.isEmpty()) {
assigningPlayer = attackingPlayer;
} else {
// TODO Get each bands with other creature
// Check if any other valid creatures matches the bands with other
// assigningPlayer = blockingBand.get(0).getController();
}
assignedDamage = true; assignedDamage = true;
Map<Card, Integer> map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null, false); Map<Card, Integer> map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController());
for (Entry<Card, Integer> dt : map.entrySet()) { for (Entry<Card, Integer> dt : map.entrySet()) {
dt.getKey().addAssignedDamage(dt.getValue(), blocker); dt.getKey().addAssignedDamage(dt.getValue(), blocker);
dt.getKey().updateObservers(); dt.getKey().updateObservers();
@@ -647,30 +615,31 @@ public class Combat {
continue; continue;
} }
AttackingBand band = this.getBandByAttacker(attacker);
boolean trampler = attacker.hasKeyword("Trample"); boolean trampler = attacker.hasKeyword("Trample");
blockers = this.getBlockers(attacker); blockers = this.attackerDamageAssignmentOrder.get(attacker);
assignedDamage = true; assignedDamage = true;
// If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender // If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender
if (blockers.isEmpty()) { if (blockers.isEmpty()) {
if (trampler || this.isUnblocked(attacker)) { if (trampler || !band.getBlocked()) {
this.addDefendingDamage(damageDealt, attacker); this.addDefendingDamage(damageDealt, attacker);
} // No damage happens if blocked but no blockers left
} else { } else {
// Else no damage can be dealt anywhere GameEntity defender = band.getDefender();
continue;
}
} else {
GameEntity defender = this.getDefenderByAttacker(attacker);
Player assigningPlayer = this.getAttackingPlayer(); Player assigningPlayer = this.getAttackingPlayer();
// Defensive Formation is very similar to Banding with Blockers // Defensive Formation is very similar to Banding with Blockers
// It allows the defending player to assign damage instead of the attacking player // It allows the defending player to assign damage instead of the attacking player
if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) { if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) {
assigningPlayer = (Player)defender; assigningPlayer = (Player)defender;
} } else {
if (!assigningPlayer.equals(defender)) {
List<Card> blockingBand = CardLists.getKeyword(blockers, "Banding"); List<Card> blockingBand = CardLists.getKeyword(blockers, "Banding");
if (!blockingBand.isEmpty()) { if (!blockingBand.isEmpty()) {
assigningPlayer = blockingBand.get(0).getController(); assigningPlayer = blockingBand.get(0).getController();
} else {
// TODO Get each bands with other creature
// Check if any other valid creatures matches the bands with other
// assigningPlayer = blockingBand.get(0).getController();
} }
} }
@@ -704,7 +673,7 @@ public class Combat {
public void dealAssignedDamage() { public void dealAssignedDamage() {
// This function handles both Regular and First Strike combat assignment // This function handles both Regular and First Strike combat assignment
final HashMap<Card, Integer> defMap = this.getDefendingDamageMap(); final HashMap<Card, Integer> defMap = this.defendingDamageMap;
final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>(); final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>();
for (final Entry<Card, Integer> entry : defMap.entrySet()) { for (final Entry<Card, Integer> entry : defMap.entrySet()) {
@@ -784,7 +753,7 @@ public class Combat {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isUnblocked(final Card att) { public final boolean isUnblocked(final Card att) {
return this.unblocked.contains(att); return !this.attackerToBandMap.get(att).getBlocked();
} }
/** /**
@@ -795,56 +764,26 @@ public class Combat {
* @return an array of {@link forge.Card} objects. * @return an array of {@link forge.Card} objects.
*/ */
public final List<Card> getUnblockedAttackers() { public final List<Card> getUnblockedAttackers() {
final List<Card> out = new ArrayList<Card>(); ArrayList<Card> unblocked = new ArrayList<Card>();
for (Card c : this.unblocked) { for (AttackingBand band : this.attackingBands) {
if (!c.hasFirstStrike()) { if (!band.getBlocked()) {
out.add(c); unblocked.addAll(band.getAttackers());
} }
} }
return out;
} // getUnblockedAttackers()
/** return unblocked;
* <p>
* getUnblockedFirstStrikeAttackers.
* </p>
*
* @return an array of {@link forge.Card} objects.
*/
public final List<Card> getUnblockedFirstStrikeAttackers() {
final List<Card> out = new ArrayList<Card>();
for (Card c : this.unblocked) { // only add creatures without firstStrike to this
if (c.hasFirstStrike() || c.hasDoubleStrike()) {
out.add(c);
}
}
return out;
} // getUnblockedAttackers()
/**
* <p>
* addUnblockedAttacker.
* </p>
*
* @param c
* a {@link forge.Card} object.
*/
public final void addUnblockedAttacker(final Card c) {
if (!this.unblocked.contains(c)) {
this.unblocked.add(c);
}
} }
public boolean isPlayerAttacked(Player priority) { public boolean isPlayerAttacked(Player priority) {
for(GameEntity defender : defenderMap.keySet()) {
// System.out.println("\nWho attacks attacks " + priority.toString() + "?"); if ((defender instanceof Player && priority.equals(defender)) ||
for (Card c : getAttackers()) { (defender instanceof Card && priority.equals(((Card)defender).getController()))) {
List<Card> attackers = defenderMap.get(priority);
if (priority.equals(getDefenderPlayerByAttacker(c))) { if (attackers != null && !attackers.isEmpty())
return true; return true;
} }
} }
return false; return false;
} }
} // Class Combat } // Class Combat

View File

@@ -46,6 +46,7 @@ import forge.card.staticability.StaticAbility;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.Game; import forge.game.Game;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;
import forge.game.combat.AttackingBand;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerController.ManaPaymentPurpose; import forge.game.player.PlayerController.ManaPaymentPurpose;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -179,11 +180,10 @@ public class CombatUtil {
return true; return true;
} }
if (attacker.hasKeyword("CARDNAME can't be blocked by more than one creature.") if (attacker.hasKeyword("CARDNAME can't be blocked by more than one creature.") &&
&& (combat.getBlockers(attacker).size() > 0)) { !combat.getBlockers(attacker).isEmpty()) {
return false; return false;
} }
return CombatUtil.canBeBlocked(attacker); return CombatUtil.canBeBlocked(attacker);
} }
@@ -425,33 +425,43 @@ public class CombatUtil {
private static void orderMultipleBlockers(final Combat combat) { private static void orderMultipleBlockers(final Combat combat) {
// If there are multiple blockers, the Attacker declares the Assignment Order // If there are multiple blockers, the Attacker declares the Assignment Order
final Player player = combat.getAttackingPlayer(); final Player player = combat.getAttackingPlayer();
for (final Card attacker : combat.getAttackers()) { for (final AttackingBand band : combat.getAttackingBands()) {
List<Card> blockers = combat.getBlockers(attacker); List<Card> attackers = band.getAttackers();
if (blockers.size() <= 1) { if (attackers.isEmpty()) {
continue; continue;
} }
List<Card> orderedBlockers = player.getController().orderBlockers(attacker, blockers);
combat.setBlockerList(attacker, orderedBlockers);
}
// Refresh Combat Panel List<Card> blockers = combat.getBlockers(band);
for(Card attacker : attackers) {
List<Card> orderedBlockers = null;
if (blockers.size() <= 1) {
orderedBlockers = blockers;
} else {
// Damage Ordering needs to take cards like Melee into account, is that happening?
orderedBlockers = player.getController().orderBlockers(attacker, blockers);
}
combat.setAttackerDamageAssignmentOrder(attacker, orderedBlockers);
}
}
} }
private static void orderBlockingMultipleAttackers(final Combat combat) { private static void orderBlockingMultipleAttackers(final Combat combat) {
// If there are multiple blockers, the Attacker declares the Assignment Order // If there are multiple blockers, the Attacker declares the Assignment Order
for (final Card blocker : combat.getAllBlockers()) { for (final Card blocker : combat.getAllBlockers()) {
List<Card> attackers = combat.getAttackersBlockedBy(blocker); List<Card> attackers = combat.getAttackersBlockedBy(blocker);
List<Card> orderedAttacker = null;
if (attackers.size() <= 1) { if (attackers.size() <= 1) {
continue; orderedAttacker = attackers;
} else {
// Damage Ordering needs to take cards like Melee into account, is that happening?
orderedAttacker = blocker.getController().getController().orderAttackers(blocker, attackers);
} }
List<Card> orderedAttacker = blocker.getController().getController().orderAttackers(blocker, attackers); combat.setBlockerDamageAssignmentOrder(blocker, orderedAttacker);
combat.setAttackersBlockedByList(blocker, orderedAttacker);
} }
// Refresh Combat Panel
} }
// can the blocker block an attacker with a lure effect? // can the blocker block an attacker with a lure effect?
/** /**
* <p> * <p>
@@ -465,7 +475,6 @@ public class CombatUtil {
* @return a boolean. * @return a boolean.
*/ */
public static boolean mustBlockAnAttacker(final Card blocker, final Combat combat) { public static boolean mustBlockAnAttacker(final Card blocker, final Combat combat) {
if (blocker == null || combat == null) { if (blocker == null || combat == null) {
return false; return false;
} }
@@ -1108,7 +1117,7 @@ public class CombatUtil {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public static void checkBlockedAttackers(final Game game, final Card a, final List<Card> blockers) { public static void checkBlockedAttackers(final Game game, final Card a, final List<Card> blockers) {
final Combat combat = game.getCombat();
if (blockers.isEmpty()) { if (blockers.isEmpty()) {
return; return;
} }
@@ -1135,7 +1144,7 @@ public class CombatUtil {
if (m.find()) { if (m.find()) {
final String[] k = keyword.split(" "); final String[] k = keyword.split(" ");
final int magnitude = Integer.valueOf(k[1]); final int magnitude = Integer.valueOf(k[1]);
final int numBlockers = game.getCombat().getBlockers(a).size(); final int numBlockers = combat.getBlockers(a).size();
if (numBlockers > 1) { if (numBlockers > 1) {
CombatUtil.executeRampageAbility(game, a, magnitude, numBlockers); CombatUtil.executeRampageAbility(game, a, magnitude, numBlockers);
} }
@@ -1253,60 +1262,6 @@ public class CombatUtil {
} }
} }
/*
public static void souverignsOfAlara2ndAbility(final Game game, final Card attacker) {
final Ability ability4 = new Ability(attacker, ManaCost.ZERO) {
@Override
public void resolve() {
List<Card> enchantments =
CardLists.filter(attacker.getController().getCardsIn(ZoneType.Library), new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (attacker.hasKeyword("Protection from enchantments")
|| (attacker.hasKeyword("Protection from everything"))) {
return false;
}
return (c.isEnchantment() && c.hasKeyword("Enchant creature") && !CardFactoryUtil
.hasProtectionFrom(c, attacker));
}
});
final Player player = attacker.getController();
Card enchantment = null;
if (player.isHuman()) {
final Card[] target = new Card[enchantments.size()];
for (int j = 0; j < enchantments.size(); j++) {
final Card crd = enchantments.get(j);
target[j] = crd;
}
final Object check = GuiChoose.oneOrNone(
"Select enchantment to enchant exalted creature", target);
if (check != null) {
enchantment = ((Card) check);
}
} else {
enchantment = ComputerUtilCard.getBestEnchantmentAI(enchantments, this, false);
}
if ((enchantment != null) && attacker.isInPlay()) {
game.getAction().changeZone(game.getZoneOf(enchantment),
enchantment.getOwner().getZone(ZoneType.Battlefield), enchantment, null);
enchantment.enchantEntity(attacker);
}
attacker.getController().shuffle();
} // resolve
}; // ability4
final StringBuilder sb4 = new StringBuilder();
sb4.append(attacker).append(
" - (Exalted) searches library for an Aura card that could enchant that creature, ");
sb4.append("put it onto the battlefield attached to that creature, then shuffles library.");
ability4.setDescription(sb4.toString());
ability4.setStackDescription(sb4.toString());
game.getStack().addSimultaneousStackEntry(ability4);
}
*/
/** /**
* executes Rampage abilities for a given card. * executes Rampage abilities for a given card.
* @param game * @param game

View File

@@ -91,7 +91,7 @@ public enum CCombat implements ICDoc {
display.append(" > "); display.append(" > ");
display.append(combatantToString(c)).append("\n"); display.append(combatantToString(c)).append("\n");
List<Card> blockers = combat.getBlockers(c); List<Card> blockers = combat.getBlockers(c, true);
// loop through blockers // loop through blockers
for (final Card element : blockers) { for (final Card element : blockers) {