mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Combat instance lifespan limited to Combat phase (for the rest combat = null, checks will return 'not attacking', 'not blocking'), the very object is stored in PhaseHandler
Card: removed methods to test if card is attacking/blocking, because these properties are related to combat, not the card itself. AiAttackController - no longer creates Combat. Instead it uses a provided instance and fills attackers there ComputerUtilBlock.java became non-static class AiBlockController, also modifies the provided Combat instance
This commit is contained in:
@@ -5357,6 +5357,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
*/
|
||||
@Override
|
||||
public boolean hasProperty(final String property, final Player sourceController, final Card source) {
|
||||
final Game game = getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
// by name can also have color names, so needs to happen before colors.
|
||||
if (property.startsWith("named")) {
|
||||
if (!this.getName().equals(property.substring(5))) {
|
||||
@@ -5438,10 +5440,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("DefenderCtrl")) {
|
||||
if (!getGame().getPhaseHandler().inCombat()) {
|
||||
if (!game.getPhaseHandler().inCombat()) {
|
||||
return false;
|
||||
}
|
||||
if (!getGame().getCombat().getDefendingPlayerRelatedTo(source).contains(this.getController())) {
|
||||
if (getGame().getCombat().getDefendingPlayerRelatedTo(source) != this.getController()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("EnchantedPlayerCtrl")) {
|
||||
@@ -5455,7 +5457,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
} else if (property.startsWith("RememberedPlayerCtrl")) {
|
||||
if (source.getRemembered().isEmpty()) {
|
||||
final Card newCard = getGame().getCardState(source);
|
||||
final Card newCard = game.getCardState(source);
|
||||
for (final Object o : newCard.getRemembered()) {
|
||||
if (o instanceof Player) {
|
||||
if (!this.getController().equals(o)) {
|
||||
@@ -5501,11 +5503,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("ActivePlayerCtrl")) {
|
||||
if (!getGame().getPhaseHandler().isPlayerTurn(this.getController())) {
|
||||
if (!game.getPhaseHandler().isPlayerTurn(this.getController())) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("NonActivePlayerCtrl")) {
|
||||
if (getGame().getPhaseHandler().isPlayerTurn(this.getController())) {
|
||||
if (game.getPhaseHandler().isPlayerTurn(this.getController())) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("YouOwn")) {
|
||||
@@ -5845,11 +5847,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (restriction.equals("MostProminentColor")) {
|
||||
byte mask = CardFactoryUtil.getMostProminentColors(getGame().getCardsIn(ZoneType.Battlefield));
|
||||
byte mask = CardFactoryUtil.getMostProminentColors(game.getCardsIn(ZoneType.Battlefield));
|
||||
if( !CardUtil.getColors(this).hasAnyColor(mask))
|
||||
return false;
|
||||
} else if (restriction.equals("LastCastThisTurn")) {
|
||||
final List<Card> c = source.getGame().getStack().getCardsCastThisTurn();
|
||||
final List<Card> c = game.getStack().getCardsCastThisTurn();
|
||||
if (c.isEmpty() || !this.sharesColorWith(c.get(c.size() - 1))) {
|
||||
return false;
|
||||
}
|
||||
@@ -5872,7 +5874,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
String color = props[1];
|
||||
|
||||
byte mostProm = CardFactoryUtil.getMostProminentColors(getGame().getCardsIn(ZoneType.Battlefield));
|
||||
byte mostProm = CardFactoryUtil.getMostProminentColors(game.getCardsIn(ZoneType.Battlefield));
|
||||
return ColorSet.fromMask(mostProm).hasAnyColor(MagicColor.fromName(color));
|
||||
} else if (property.startsWith("notSharesColorWith")) {
|
||||
if (property.equals("notSharesColorWith")) {
|
||||
@@ -5971,7 +5973,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
} else if (restriction.equals("EachTopLibrary")) {
|
||||
final List<Card> list = new ArrayList<Card>();
|
||||
for (Player p : getGame().getPlayers()) {
|
||||
for (Player p : game.getPlayers()) {
|
||||
final Card top = p.getCardsIn(ZoneType.Library).get(0);
|
||||
list.add(top);
|
||||
}
|
||||
@@ -5998,14 +6000,14 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
return false;
|
||||
} else if (restriction.equals(ZoneType.Graveyard.toString())) {
|
||||
for (final Card card : getGame().getCardsIn(ZoneType.Graveyard)) {
|
||||
for (final Card card : game.getCardsIn(ZoneType.Graveyard)) {
|
||||
if (this.getName().equals(card.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (restriction.equals(ZoneType.Battlefield.toString())) {
|
||||
for (final Card card : getGame().getCardsIn(ZoneType.Battlefield)) {
|
||||
for (final Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (this.getName().equals(card.getName())) {
|
||||
return true;
|
||||
}
|
||||
@@ -6050,7 +6052,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
return false;
|
||||
} else if (restriction.equals("NonToken")) {
|
||||
final List<Card> list = CardLists.filter(getGame().getCardsIn(ZoneType.Battlefield),
|
||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield),
|
||||
Presets.NON_TOKEN);
|
||||
for (final Card card : list) {
|
||||
if (this.getName().equals(card.getName())) {
|
||||
@@ -6208,11 +6210,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("enteredBattlefieldThisTurn")) {
|
||||
if (!(this.getTurnInZone() == getGame().getPhaseHandler().getTurn())) {
|
||||
if (!(this.getTurnInZone() == game.getPhaseHandler().getTurn())) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("notEnteredBattlefieldThisTurn")) {
|
||||
if (this.getTurnInZone() == getGame().getPhaseHandler().getTurn()) {
|
||||
if (this.getTurnInZone() == game.getPhaseHandler().getTurn()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("firstTurnControlled")) {
|
||||
@@ -6288,7 +6290,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("greatestPower")) {
|
||||
final List<Card> list = CardLists.filter(getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
for (final Card crd : list) {
|
||||
if (crd.getNetAttack() > this.getNetAttack()) {
|
||||
return false;
|
||||
@@ -6302,21 +6304,21 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("leastPower")) {
|
||||
final List<Card> list = CardLists.filter(getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
for (final Card crd : list) {
|
||||
if (crd.getNetAttack() < this.getNetAttack()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("leastToughness")) {
|
||||
final List<Card> list = CardLists.filter(getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
for (final Card crd : list) {
|
||||
if (crd.getNetDefense() < this.getNetDefense()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("greatestCMC")) {
|
||||
final List<Card> list = CardLists.filter(getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
for (final Card crd : list) {
|
||||
if (crd.isSplitCard()) {
|
||||
if (crd.getCMC(Card.SplitCMCMode.LeftSplitCMC) > this.getCMC() || crd.getCMC(Card.SplitCMCMode.RightSplitCMC) > this.getCMC()) {
|
||||
@@ -6332,7 +6334,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
List<Card> list = new ArrayList<Card>();
|
||||
for (final Object o : source.getRemembered()) {
|
||||
if (o instanceof Card) {
|
||||
list.add(getGame().getCardState((Card) o));
|
||||
list.add(game.getCardState((Card) o));
|
||||
}
|
||||
}
|
||||
if (!list.contains(this)) {
|
||||
@@ -6343,7 +6345,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("lowestCMC")) {
|
||||
final List<Card> list = getGame().getCardsIn(ZoneType.Battlefield);
|
||||
final List<Card> list = game.getCardsIn(ZoneType.Battlefield);
|
||||
for (final Card crd : list) {
|
||||
if (!crd.isLand() && !crd.isImmutable()) {
|
||||
if (crd.isSplitCard()) {
|
||||
@@ -6395,7 +6397,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("suspended")) {
|
||||
if (!this.hasSuspend() || !getGame().isCardExiled(this)
|
||||
if (!this.hasSuspend() || !game.isCardExiled(this)
|
||||
|| !(this.getCounters(CounterType.getType("TIME")) >= 1)) {
|
||||
return false;
|
||||
}
|
||||
@@ -6478,60 +6480,54 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
if (!Expressions.compare(actualnumber, comparator, number)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("attacking")) {
|
||||
if (property.equals("attacking")) {
|
||||
if (!this.isAttacking()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("attackingYou")) {
|
||||
if (!this.isAttacking(sourceController)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// These predicated refer to ongoing combat. If no combat happens, they'll return false (meaning not attacking/blocking ATM)
|
||||
else if (property.startsWith("attacking")) {
|
||||
if ( null == combat ) return false;
|
||||
if (property.equals("attacking")) return combat.isAttacking(this);
|
||||
if (property.equals("attackingYou")) return combat.isAttacking(this, sourceController);
|
||||
} else if (property.startsWith("notattacking")) {
|
||||
if (this.isAttacking()) {
|
||||
return false;
|
||||
}
|
||||
return null == combat || !combat.isAttacking(this);
|
||||
} else if (property.equals("attackedBySourceThisCombat")) {
|
||||
final GameEntity defender = getGame().getCombat().getDefenderByAttacker(source);
|
||||
final GameEntity defender = game.getCombat().getDefenderByAttacker(source);
|
||||
if (defender instanceof Card) {
|
||||
if (!this.equals((Card) defender)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (property.equals("blocking")) {
|
||||
if (!this.isBlocking()) {
|
||||
} else if (property.startsWith("blocking")) {
|
||||
if ( null == combat ) return false;
|
||||
String what = property.substring("blocking".length());
|
||||
|
||||
if( StringUtils.isEmpty(what)) return combat.isBlocking(this);
|
||||
if (what.startsWith("Source")) return combat.isBlocking(this, source) ;
|
||||
if (what.startsWith("CreatureYouCtrl")) {
|
||||
for (final Card c : CardLists.filter(sourceController.getCardsIn(ZoneType.Battlefield), Presets.CREATURES))
|
||||
if (combat.isBlocking(this, c))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("blockingSource")) {
|
||||
if (!this.isBlocking(source)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("blockingCreatureYouCtrl")) {
|
||||
final List<Card> list = CardLists.filter(sourceController.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
for (final Card c : list) {
|
||||
if (this.isBlocking(c)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("blockingRemembered")) {
|
||||
for (final Object o : source.getRemembered()) {
|
||||
if (o instanceof Card) {
|
||||
final Card card = (Card) o;
|
||||
if (this.isBlocking(card)) {
|
||||
if (what.startsWith("Remembered")) {
|
||||
for (final Object o : source.getRemembered()) {
|
||||
if (o instanceof Card && combat.isBlocking(this, (Card) o)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} else if (property.startsWith("notblocking")) {
|
||||
return null == combat || !combat.isBlocking(this);
|
||||
}
|
||||
// Nex predicates refer to past combat and don't need a reference to actual combat
|
||||
else if (property.equals("blocked")) {
|
||||
return null != combat && combat.isBlocked(this);
|
||||
} else if (property.startsWith("blockedBySource")) {
|
||||
return null != combat && combat.isBlocking(source, this);
|
||||
} else if (property.startsWith("isBlockedByRemembered")) {
|
||||
if ( null == combat ) return false;
|
||||
for (final Object o : source.getRemembered()) {
|
||||
if (o instanceof Card) {
|
||||
final Card crd = (Card) o;
|
||||
if (this.isBlockedBy(crd)) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof Card && combat.isBlocking((Card) o, this)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -6555,20 +6551,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("notblocking")) {
|
||||
if (this.isBlocking()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("blocked")) {
|
||||
if (!this.isBlocked()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("blockedBySource")) {
|
||||
if (!this.isBlockedBy(source)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("unblocked")) {
|
||||
if (!getGame().getCombat().isUnblocked(this)) {
|
||||
if (!game.getCombat().isUnblocked(this)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("attackersBandedWith")) {
|
||||
@@ -6576,9 +6560,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
// You don't band with yourself
|
||||
return false;
|
||||
}
|
||||
|
||||
Combat combat = getGame().getCombat();
|
||||
AttackingBand band = combat.getBandByAttacker(source);
|
||||
AttackingBand band = combat == null ? null : combat.getBandOfAttacker(source);
|
||||
if (band == null || !band.getAttackers().contains(this)) {
|
||||
return false;
|
||||
}
|
||||
@@ -6889,82 +6871,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
this.usedToPayCost = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isAttacking.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isAttacking() {
|
||||
return getGame().getCombat().isAttacking(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isAttacking.
|
||||
* </p>
|
||||
* @param ge the GameEntity to check
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isAttacking(GameEntity ge) {
|
||||
Combat combat = getGame().getCombat();
|
||||
GameEntity defender = combat.getDefenderByAttacker(this);
|
||||
if (!combat.isAttacking(this) || defender == null) {
|
||||
return false;
|
||||
}
|
||||
return defender.equals(ge);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isBlocking.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBlocking() {
|
||||
final List<Card> blockers = getGame().getCombat().getAllBlockers();
|
||||
return blockers.contains(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isBlocked.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBlocked() {
|
||||
return getGame().getCombat().isBlocked(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isBlocking.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBlocking(final Card attacker) {
|
||||
return getGame().getCombat().getAttackersBlockedBy(this).contains(attacker);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isBlockedBy.
|
||||
* </p>
|
||||
*
|
||||
* @param blocker
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBlockedBy(final Card blocker) {
|
||||
return getGame().getCombat().getAttackersBlockedBy(blocker).contains(this);
|
||||
}
|
||||
|
||||
// /////////////////////////
|
||||
//
|
||||
// Damage code
|
||||
|
||||
@@ -896,7 +896,7 @@ public class AbilityUtils {
|
||||
players.add(p);
|
||||
}
|
||||
} else if (defined.equals("DefendingPlayer")) {
|
||||
players.addAll(game.getCombat().getDefendingPlayerRelatedTo(card));
|
||||
players.add(game.getCombat().getDefendingPlayerRelatedTo(card));
|
||||
} else if (defined.equals("ChosenPlayer")) {
|
||||
final Player p = card.getChosenPlayer();
|
||||
if (!players.contains(p)) {
|
||||
|
||||
@@ -60,9 +60,7 @@ public class AnimateAi extends SpellAbilityAi {
|
||||
|
||||
// don't use instant speed animate abilities outside humans
|
||||
// Combat_Declare_Attackers_InstantAbility step
|
||||
if ((!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| (game.getCombat().getAttackers().isEmpty()))
|
||||
&& game.getPhaseHandler().isPlayerTurn(opponent)) {
|
||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, opponent) || game.getCombat().getAttackers().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -817,7 +817,12 @@ public class AttachAi extends SpellAbilityAi {
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return containsUsefulKeyword(keywords, c, sa, pow);
|
||||
for (final String keyword : keywords) {
|
||||
if (isUsefulAttachKeyword(keyword, c, sa, pow)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -991,25 +996,6 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains useful keyword.
|
||||
*
|
||||
* @param keywords
|
||||
* the keywords
|
||||
* @param card
|
||||
* the card
|
||||
* @param sa SpellAbility
|
||||
* @return true, if successful
|
||||
*/
|
||||
private static boolean containsUsefulKeyword(final ArrayList<String> keywords, final Card card, final SpellAbility sa, final int powerBonus) {
|
||||
for (final String keyword : keywords) {
|
||||
if (isUsefulAttachKeyword(keyword, card, sa, powerBonus)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains useful curse keyword.
|
||||
*
|
||||
@@ -1040,7 +1026,10 @@ public class AttachAi extends SpellAbilityAi {
|
||||
* @return true, if is useful keyword
|
||||
*/
|
||||
private static boolean isUsefulAttachKeyword(final String keyword, final Card card, final SpellAbility sa, final int powerBonus) {
|
||||
final PhaseHandler ph = sa.getActivatingPlayer().getGame().getPhaseHandler();
|
||||
final Player ai = sa.getActivatingPlayer();
|
||||
final Player opponent = ai.getOpponent();
|
||||
final PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||
|
||||
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1053,7 +1042,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (evasive) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)
|
||||
|| !CombatUtil.canBeBlocked(card)) {
|
||||
|| !CombatUtil.canBeBlocked(card, opponent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Haste")) {
|
||||
@@ -1068,7 +1057,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return true;
|
||||
} else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card))
|
||||
|| ((!CombatUtil.canBeBlocked(card, opponent) || !CombatUtil.canAttackNextTurn(card))
|
||||
&& !CombatUtil.canBlock(card, true))) {
|
||||
return false;
|
||||
}
|
||||
@@ -1084,17 +1073,17 @@ public class AttachAi extends SpellAbilityAi {
|
||||
} else if (keyword.startsWith("Flanking")) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)
|
||||
|| !CombatUtil.canBeBlocked(card)) {
|
||||
|| !CombatUtil.canBeBlocked(card, opponent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Bushido")) {
|
||||
if ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card))
|
||||
if ((!CombatUtil.canBeBlocked(card, opponent) || !CombatUtil.canAttackNextTurn(card))
|
||||
&& !CombatUtil.canBlock(card, true)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Trample")) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 1
|
||||
|| !CombatUtil.canBeBlocked(card)
|
||||
|| !CombatUtil.canBeBlocked(card, opponent)
|
||||
|| !CombatUtil.canAttackNextTurn(card)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import forge.card.trigger.TriggerType;
|
||||
import forge.game.Game;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilBlock;
|
||||
import forge.game.ai.AiBlockController;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
@@ -500,13 +500,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
*/
|
||||
private static Card chooseCreature(final Player ai, List<Card> list) {
|
||||
// Creating a new combat for testing purposes.
|
||||
Combat combat = new Combat();
|
||||
combat.initiatePossibleDefenders(ai);
|
||||
List<Card> attackers = ai.getOpponent().getCreaturesInPlay();
|
||||
for (Card att : attackers) {
|
||||
Combat combat = new Combat(ai.getOpponent());
|
||||
for (Card att : ai.getOpponent().getCreaturesInPlay()) {
|
||||
combat.addAttacker(att, ai);
|
||||
}
|
||||
combat = ComputerUtilBlock.getBlockers(ai, combat, ai.getCreaturesInPlay());
|
||||
AiBlockController block = new AiBlockController(ai);
|
||||
block.assignBlockers(combat);
|
||||
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
// need something AI can cast now
|
||||
@@ -523,10 +522,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// *************************************************************************************
|
||||
// **************** Known Origin (Battlefield/Graveyard/Exile)
|
||||
// *************************
|
||||
// ******* Known origin cards are chosen during casting of the spell
|
||||
// (target) **********
|
||||
// **************** Known Origin (Battlefield/Graveyard/Exile) *************************
|
||||
// ******* Known origin cards are chosen during casting of the spell (target) **********
|
||||
// *************************************************************************************
|
||||
|
||||
/**
|
||||
@@ -690,6 +687,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final Game game = ai.getGame();
|
||||
|
||||
final AbilitySub abSub = sa.getSubAbility();
|
||||
ApiType subApi = null;
|
||||
@@ -702,7 +700,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
sa.resetTargets();
|
||||
List<Card> list = CardLists.getValidCards(ai.getGame().getCardsIn(origin), tgt.getValidTgts(), ai, source);
|
||||
List<Card> list = CardLists.getValidCards(game.getCardsIn(origin), tgt.getValidTgts(), ai, source);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
if (sa.hasParam("AITgts")) {
|
||||
list = CardLists.getValidCards(list, sa.getParam("AITgts"), sa.getActivatingPlayer(), source);
|
||||
@@ -730,7 +728,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
// check stack for something on the stack that will kill
|
||||
// anything i control
|
||||
if (!ai.getGame().getStack().isEmpty()) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
final List<ITargetable> objects = ComputerUtil.predictThreatenedObjects(ai, sa);
|
||||
|
||||
final List<Card> threatenedTargets = new ArrayList<Card>();
|
||||
@@ -748,12 +746,13 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
// Save combatants
|
||||
else if (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
else if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
Combat combat = game.getCombat();
|
||||
final List<Card> combatants = CardLists.filter(aiPermanents, CardPredicates.Presets.CREATURES);
|
||||
CardLists.sortByEvaluateCreature(combatants);
|
||||
|
||||
for (final Card c : combatants) {
|
||||
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c) && c.getOwner() == ai && !c.isToken()) {
|
||||
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && c.getOwner() == ai && !c.isToken()) {
|
||||
sa.getTargets().add(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.card.spellability.TargetRestrictions;
|
||||
import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -64,19 +65,18 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
} else if (sa.getParam("AILogic").equals("Never")) {
|
||||
return false;
|
||||
} else if (sa.getParam("AILogic").equals("NeedsPrevention")) {
|
||||
if (!game.getPhaseHandler().getPhase() .equals(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
return false;
|
||||
}
|
||||
final Combat combat = game.getCombat();
|
||||
choices = CardLists.filter(choices, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) {
|
||||
if (!combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||
return false;
|
||||
}
|
||||
if (host.getName().equals("Forcefield")) {
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 1;
|
||||
}
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0;
|
||||
int ref = host.getName().equals("Forcefield") ? 1 : 0;
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > ref;
|
||||
}
|
||||
});
|
||||
if (choices.isEmpty()) {
|
||||
@@ -122,17 +122,16 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
}
|
||||
choice = ComputerUtilCard.getBestAI(options);
|
||||
} else if (logic.equals("NeedsPrevention")) {
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
List<Card> better = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
final Game game = ai.getGame();
|
||||
if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) {
|
||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||
return false;
|
||||
}
|
||||
if (host.getName().equals("Forcefield")) {
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 1;
|
||||
}
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0;
|
||||
int ref = host.getName().equals("Forcefield") ? 1 : 0;
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > ref;
|
||||
}
|
||||
});
|
||||
if (!better.isEmpty()) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.card.spellability.TargetRestrictions;
|
||||
import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -100,13 +101,14 @@ public class ChooseSourceAi extends SpellAbilityAi {
|
||||
if (sa.hasParam("Choices")) {
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
||||
}
|
||||
final Combat combat = game.getCombat();
|
||||
choices = CardLists.filter(choices, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) {
|
||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||
return false;
|
||||
}
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0;
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > 0;
|
||||
}
|
||||
});
|
||||
if (choices.isEmpty()) {
|
||||
|
||||
@@ -46,9 +46,7 @@ public class CloneAi extends SpellAbilityAi {
|
||||
|
||||
// don't use instant speed clone abilities outside humans
|
||||
// Combat_Declare_Attackers_InstantAbility step
|
||||
if ((!phase.is(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| game.getCombat().getAttackers().isEmpty())
|
||||
&& !phase.isPlayerTurn(ai)) {
|
||||
if (!phase.is(PhaseType.COMBAT_DECLARE_ATTACKERS) && !phase.isPlayerTurn(ai) || game.getCombat().getAttackers().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -28,6 +29,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
final Card hostCard = sa.getSourceCard();
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
boolean chance = false;
|
||||
|
||||
final Cost cost = sa.getPayCosts();
|
||||
@@ -69,14 +71,13 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
boolean flag = false;
|
||||
for (final Object o : objects) {
|
||||
if (o instanceof Card) {
|
||||
final Card c = (Card) o;
|
||||
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c);
|
||||
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, (Card) o, combat);
|
||||
} else if (o instanceof Player) {
|
||||
// Don't need to worry about Combat Damage during AI's turn
|
||||
final Player p = (Player) o;
|
||||
if (!handler.isPlayerTurn(p)) {
|
||||
flag |= (p == ai && ((ComputerUtilCombat.wouldLoseLife(ai, game.getCombat()) && sa
|
||||
.isAbility()) || ComputerUtilCombat.lifeInDanger(ai, game.getCombat())));
|
||||
flag |= (p == ai && ((ComputerUtilCombat.wouldLoseLife(ai, combat) && sa
|
||||
.isAbility()) || ComputerUtilCombat.lifeInDanger(ai, combat)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,8 +121,8 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
|
||||
} // Protect combatants
|
||||
else if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
if (sa.canTarget(ai) && ComputerUtilCombat.wouldLoseLife(ai, game.getCombat())
|
||||
&& (ComputerUtilCombat.lifeInDanger(ai, game.getCombat()) || sa.isAbility())
|
||||
if (sa.canTarget(ai) && ComputerUtilCombat.wouldLoseLife(ai, combat)
|
||||
&& (ComputerUtilCombat.lifeInDanger(ai, combat) || sa.isAbility())
|
||||
&& game.getPhaseHandler().isPlayerTurn(ai.getOpponent())) {
|
||||
sa.getTargets().add(ai);
|
||||
chance = true;
|
||||
@@ -138,7 +139,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
CardLists.sortByEvaluateCreature(combatants);
|
||||
|
||||
for (final Card c : combatants) {
|
||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
|
||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||
sa.getTargets().add(c);
|
||||
chance = true;
|
||||
break;
|
||||
@@ -177,8 +178,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
* a boolean.
|
||||
* @return a boolean.
|
||||
*/
|
||||
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa,
|
||||
final boolean mandatory) {
|
||||
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
sa.resetTargets();
|
||||
// filter AIs battlefield by what I can target
|
||||
@@ -199,8 +199,9 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
final List<Card> combatants = CardLists.filter(compTargetables, CardPredicates.Presets.CREATURES);
|
||||
CardLists.sortByEvaluateCreature(combatants);
|
||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
Combat combat = game.getCombat();
|
||||
for (final Card c : combatants) {
|
||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
|
||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||
sa.getTargets().add(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
@@ -14,8 +16,10 @@ import forge.card.cost.Cost;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.SpellAbilityRestriction;
|
||||
import forge.card.spellability.TargetRestrictions;
|
||||
import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -30,6 +34,7 @@ public class DebuffAi extends SpellAbilityAi {
|
||||
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
||||
// if there is no target and host card isn't in play, don't activate
|
||||
final Card source = sa.getSourceCard();
|
||||
final Game game = ai.getGame();
|
||||
if ((sa.getTargetRestrictions() == null) && !source.isInPlay()) {
|
||||
return false;
|
||||
}
|
||||
@@ -50,12 +55,12 @@ public class DebuffAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
final SpellAbilityRestriction restrict = sa.getRestrictions();
|
||||
final PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
|
||||
// Phase Restrictions
|
||||
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
|| !ai.getGame().getStack().isEmpty()) {
|
||||
|| !game.getStack().isEmpty()) {
|
||||
// Instant-speed pumps should not be cast outside of combat when the
|
||||
// stack is empty
|
||||
if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||
@@ -70,29 +75,28 @@ public class DebuffAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||
if (!sa.usesTargeting() || !sa.getTargetRestrictions().doesTarget()) {
|
||||
List<Card> cards = AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa);
|
||||
|
||||
if (!cards.isEmpty()) {
|
||||
cards = CardLists.filter(cards, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if ((c.getController().equals(sa.getActivatingPlayer())) || (!c.isBlocking() && !c.isAttacking())) {
|
||||
return false;
|
||||
}
|
||||
// don't add duplicate negative keywords
|
||||
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<String>(), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
final Combat combat = game.getCombat();
|
||||
return Iterables.any(cards, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
|
||||
if (c.getController().equals(sa.getActivatingPlayer()) || combat == null)
|
||||
return false;
|
||||
|
||||
if (!combat.isBlocking(c) && !combat.isAttacking(c)) {
|
||||
return false;
|
||||
}
|
||||
// don't add duplicate negative keywords
|
||||
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +105,7 @@ public class DebuffAi extends SpellAbilityAi {
|
||||
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be
|
||||
// here?
|
||||
} else {
|
||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<String>(), false);
|
||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -130,7 +134,7 @@ public class DebuffAi extends SpellAbilityAi {
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
sa.resetTargets();
|
||||
List<Card> list = getCurseCreatures(ai, sa, kws);
|
||||
List<Card> list = getCurseCreatures(ai, sa, kws == null ? Lists.<String>newArrayList() : kws);
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
|
||||
|
||||
// several uses here:
|
||||
|
||||
@@ -71,30 +71,30 @@ public final class EncodeAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
||||
Card choice = null;
|
||||
final String logic = sa.getParam("AILogic");
|
||||
if (logic == null) {
|
||||
final List<Card> attackers = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CombatUtil.canAttackNextTurn(c);
|
||||
}
|
||||
});
|
||||
final List<Card> unblockables = CardLists.filter(attackers, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !CombatUtil.canBeBlocked(c);
|
||||
}
|
||||
});
|
||||
if (!unblockables.isEmpty()) {
|
||||
choice = ComputerUtilCard.getBestAI(unblockables);
|
||||
} else if (!attackers.isEmpty()) {
|
||||
choice = ComputerUtilCard.getBestAI(attackers);
|
||||
} else {
|
||||
choice = ComputerUtilCard.getBestAI(options);
|
||||
// final String logic = sa.getParam("AILogic");
|
||||
// if (logic == null) {
|
||||
final List<Card> attackers = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CombatUtil.canAttackNextTurn(c);
|
||||
}
|
||||
});
|
||||
final List<Card> unblockables = CardLists.filter(attackers, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !CombatUtil.canBeBlocked(c, ai.getOpponent());
|
||||
}
|
||||
});
|
||||
if (!unblockables.isEmpty()) {
|
||||
choice = ComputerUtilCard.getBestAI(unblockables);
|
||||
} else if (!attackers.isEmpty()) {
|
||||
choice = ComputerUtilCard.getBestAI(attackers);
|
||||
} else {
|
||||
choice = ComputerUtilCard.getBestAI(options);
|
||||
}
|
||||
// }
|
||||
return choice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,7 @@ public class LifeGainAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
boolean lifeCritical = life <= 5;
|
||||
lifeCritical |= (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DAMAGE) && ComputerUtilCombat
|
||||
.lifeInDanger(ai, game.getCombat()));
|
||||
lifeCritical |= game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DAMAGE) && ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
||||
|
||||
if (abCost != null && !lifeCritical) {
|
||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, false)) {
|
||||
|
||||
@@ -91,19 +91,20 @@ public class ProtectAi extends SpellAbilityAi {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is the creature blocking and unable to destroy the attacker
|
||||
// or would be destroyed itself?
|
||||
if (c.isBlocking()
|
||||
&& (ComputerUtilCombat.blockerWouldBeDestroyed(ai, c))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is the creature in blocked and the blocker would survive
|
||||
// TODO Potential NPE here if no blockers are actually left
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& combat.isAttacking(c) && game.getCombat().isBlocked(c)
|
||||
&& ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0))) {
|
||||
return true;
|
||||
if( combat != null ) {
|
||||
// is the creature blocking and unable to destroy the attacker
|
||||
// or would be destroyed itself?
|
||||
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is the creature in blocked and the blocker would survive
|
||||
// TODO Potential NPE here if no blockers are actually left
|
||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& combat.isAttacking(c) && combat.isBlocked(c)
|
||||
&& ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0), combat)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -52,6 +52,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
*/
|
||||
public boolean isUsefulCurseKeyword(final Player ai, final String keyword, final Card card, final SpellAbility sa) {
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final Player human = ai.getOpponent();
|
||||
//int attack = getNumAttack(sa);
|
||||
@@ -115,19 +116,18 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
}
|
||||
} else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")
|
||||
|| keyword.endsWith("Prevent all damage that would be dealt by CARDNAME.")) {
|
||||
if (ph.isPlayerTurn(ai) && (!(CombatUtil.canBlock(card) || card.isBlocking())
|
||||
if (ph.isPlayerTurn(ai) && (!(CombatUtil.canBlock(card) || combat != null && combat.isBlocking(card))
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
|| ph.getPhase().isBefore(PhaseType.MAIN1)
|
||||
|| CardLists.getNotKeyword(ai.getCreaturesInPlay(), "Defender").isEmpty())) {
|
||||
return false;
|
||||
}
|
||||
if (ph.isPlayerTurn(human) && (!card.isAttacking()
|
||||
|| card.getNetCombatDamage() <= 0)) {
|
||||
if (ph.isPlayerTurn(human) && (combat == null || !combat.isAttacking(card) || card.getNetCombatDamage() <= 0)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("CARDNAME attacks each turn if able.")) {
|
||||
if (ph.isPlayerTurn(ai) || !CombatUtil.canAttack(card, human) || !CombatUtil.canBeBlocked(card)
|
||||
if (ph.isPlayerTurn(ai) || !CombatUtil.canAttack(card, human) || !CombatUtil.canBeBlocked(card, ai.getOpponent())
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
return false;
|
||||
}
|
||||
@@ -135,8 +135,8 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (card.getShield() > 0) {
|
||||
return true;
|
||||
}
|
||||
if (card.hasKeyword("If CARDNAME would be destroyed, regenerate it.")
|
||||
&& (card.isBlocked() || card.isBlocking())) {
|
||||
if (card.hasKeyword("If CARDNAME would be destroyed, regenerate it.") && combat != null
|
||||
&& (combat.isBlocked(card) || combat.isBlocking(card))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -158,6 +158,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
*/
|
||||
public boolean isUsefulPumpKeyword(final Player ai, final String keyword, final Card card, final SpellAbility sa, final int attack) {
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final Player opp = ai.getOpponent();
|
||||
//int defense = getNumDefense(sa);
|
||||
@@ -171,7 +172,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
final boolean combatRelevant = (keyword.endsWith("First Strike") || keyword.contains("Bushido"));
|
||||
// give evasive keywords to creatures that can or do attack
|
||||
if (evasive) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) {
|
||||
@@ -187,7 +188,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return true;
|
||||
}
|
||||
Predicate<Card> flyingOrReach = Predicates.or(CardPredicates.hasKeyword("Flying"), CardPredicates.hasKeyword("Reach"));
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| !Iterables.any(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
@@ -202,7 +203,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||
return true;
|
||||
}
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
@@ -220,7 +221,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
} else if (keyword.endsWith("Indestructible")) {
|
||||
return true;
|
||||
} else if (keyword.endsWith("Deathtouch")) {
|
||||
Combat combat = game.getCombat();
|
||||
if (ph.isPlayerTurn(opp) && ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
List<Card> attackers = combat.getAttackers();
|
||||
for (Card attacker : attackers) {
|
||||
@@ -241,34 +241,34 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
}
|
||||
return false;
|
||||
} else if (combatRelevant) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
|| (opp.getCreaturesInPlay().size() < 1)
|
||||
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Double Strike")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Rampage")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).size() < 2) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Flanking")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
"Flanking").isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Trample")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
|| !CombatUtil.canBeBlocked(card)
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| !CombatUtil.canBeBlocked(card, opp)
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() + attack <= 1
|
||||
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) {
|
||||
@@ -278,11 +278,11 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (card.getNetCombatDamage() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (card.isBlocking()) {
|
||||
if (combat != null && combat.isBlocking(card)) {
|
||||
return true;
|
||||
}
|
||||
if ((ph.isPlayerTurn(opp))
|
||||
|| !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
|| !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
return false;
|
||||
}
|
||||
@@ -290,20 +290,12 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (card.getNetCombatDamage() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (card.isBlocking()) {
|
||||
return true;
|
||||
}
|
||||
if (card.isAttacking() && card.isBlocked()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return combat != null && ( combat.isBlocking(card) || combat.isAttacking(card) && combat.isBlocked(card) );
|
||||
} else if (keyword.equals("Lifelink")) {
|
||||
if (card.getNetCombatDamage() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!card.isBlocking() && !card.isAttacking()) {
|
||||
return false;
|
||||
}
|
||||
return combat != null && ( combat.isAttacking(card) || combat.isBlocking(card) );
|
||||
} else if (keyword.equals("Vigilance")) {
|
||||
if (ph.isPlayerTurn(opp) || !CombatUtil.canAttack(card, opp)
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
@@ -341,7 +333,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Islandwalk")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.getType(opp.getLandsInPlay(), "Island").isEmpty()
|
||||
@@ -349,7 +341,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Swampwalk")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.getType(opp.getLandsInPlay(), "Swamp").isEmpty()
|
||||
@@ -357,7 +349,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Mountainwalk")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.getType(opp.getLandsInPlay(), "Mountain").isEmpty()
|
||||
@@ -365,7 +357,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Forestwalk")) {
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || card.isAttacking())
|
||||
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || combat != null && combat.isAttacking(card))
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| CardLists.getType(opp.getLandsInPlay(), "Forest").isEmpty()
|
||||
@@ -385,9 +377,9 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
protected boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int defense, final int attack,
|
||||
final List<String> keywords) {
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
PhaseHandler phase = game.getPhaseHandler();
|
||||
|
||||
final PhaseHandler phase = game.getPhaseHandler();
|
||||
final Combat combat = phase.getCombat();
|
||||
|
||||
if (!c.canBeTargetedBy(sa)) {
|
||||
return false;
|
||||
}
|
||||
@@ -423,14 +415,15 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
|
||||
// is the creature blocking and unable to destroy the attacker
|
||||
// or would be destroyed itself?
|
||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && c.isBlocking()) {
|
||||
if (defense > 0 && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c)) {
|
||||
|
||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && combat.isBlocking(c)) {
|
||||
if (defense > 0 && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
||||
return true;
|
||||
}
|
||||
List<Card> blockedBy = game.getCombat().getAttackersBlockedBy(c);
|
||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||
// For now, Only care the first creature blocked by a card.
|
||||
// TODO Add in better BlockAdditional support
|
||||
if (!blockedBy.isEmpty() && attack > 0 && !ComputerUtilCombat.attackerWouldBeDestroyed(ai, blockedBy.get(0))) {
|
||||
if (!blockedBy.isEmpty() && attack > 0 && !ComputerUtilCombat.attackerWouldBeDestroyed(ai, blockedBy.get(0), combat)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -447,24 +440,20 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
&& combat.isBlocked(c)
|
||||
&& combat.getBlockers(c) != null
|
||||
&& !combat.getBlockers(c).isEmpty()
|
||||
&& !ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0))) {
|
||||
&& !ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0), combat)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||
boolean attackerHasTrample = false;
|
||||
for (Card b : blockedBy) {
|
||||
attackerHasTrample |= b.hasKeyword("Trample");
|
||||
}
|
||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS, ai.getOpponent()) && combat.isBlocking(c) && defense > 0 ) {
|
||||
// if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||
boolean attackerHasTrample = false;
|
||||
for (Card b : blockedBy) {
|
||||
attackerHasTrample |= b.hasKeyword("Trample");
|
||||
}
|
||||
|
||||
if (phase.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& phase.isPlayerTurn(ai.getOpponent())
|
||||
&& c.isBlocking()
|
||||
&& defense > 0
|
||||
&& attackerHasTrample
|
||||
&& (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, game.getCombat()))) {
|
||||
return true;
|
||||
if (attackerHasTrample && (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, combat)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -505,6 +494,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
protected List<Card> getCurseCreatures(final Player ai, final SpellAbility sa, final int defense, final int attack, final List<String> keywords) {
|
||||
List<Card> list = ai.getOpponent().getCreaturesInPlay();
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
@@ -538,7 +528,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!c.isAttacking()) {
|
||||
if (combat == null || !combat.isAttacking(c)) {
|
||||
return false;
|
||||
}
|
||||
if (c.getNetAttack() > 0 && ai.getLife() < 5) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -30,6 +31,7 @@ public class PumpAllAi extends PumpAiBase {
|
||||
String valid = "";
|
||||
final Card source = sa.getSourceCard();
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
|
||||
final int power = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumAtt"), sa);
|
||||
final int defense = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumDef"), sa);
|
||||
@@ -86,13 +88,12 @@ public class PumpAllAi extends PumpAiBase {
|
||||
}
|
||||
int totalPower = 0;
|
||||
for (Card c : human) {
|
||||
if (!c.isAttacking()) {
|
||||
if (combat == null || !combat.isAttacking(c)) {
|
||||
continue;
|
||||
}
|
||||
totalPower += Math.min(c.getNetAttack(), power * -1);
|
||||
if (phase == PhaseType.COMBAT_DECLARE_BLOCKERS
|
||||
&& game.getCombat().isUnblocked(c)) {
|
||||
if (ComputerUtilCombat.lifeInDanger(sa.getActivatingPlayer(), game.getCombat())) {
|
||||
if (phase == PhaseType.COMBAT_DECLARE_BLOCKERS && combat.isUnblocked(c)) {
|
||||
if (ComputerUtilCombat.lifeInDanger(sa.getActivatingPlayer(), combat)) {
|
||||
return true;
|
||||
}
|
||||
totalPower += Math.min(c.getNetAttack(), power * -1);
|
||||
@@ -124,7 +125,7 @@ public class PumpAllAi extends PumpAiBase {
|
||||
if (power <= 0 && !containsUsefulKeyword(ai, keywords, c, sa, power)) {
|
||||
return false;
|
||||
}
|
||||
if (phase.equals(PhaseType.COMBAT_DECLARE_ATTACKERS) && c.isAttacking()) {
|
||||
if (phase == PhaseType.COMBAT_DECLARE_ATTACKERS && combat.isAttacking(c)) {
|
||||
return true;
|
||||
}
|
||||
if (phase.isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && CombatUtil.canAttack(c, opp)) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -61,6 +62,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
final Card hostCard = sa.getSourceCard();
|
||||
final Cost abCost = sa.getPayCosts();
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
|
||||
boolean chance = false;
|
||||
if (abCost != null) {
|
||||
@@ -98,7 +100,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
|
||||
for (final Card c : list) {
|
||||
if (c.getShield() == 0) {
|
||||
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c);
|
||||
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
CardLists.sortByEvaluateCreature(combatants);
|
||||
|
||||
for (final Card c : combatants) {
|
||||
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
|
||||
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||
sa.getTargets().add(c);
|
||||
chance = true;
|
||||
break;
|
||||
@@ -196,8 +198,9 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
final List<Card> combatants = CardLists.filter(compTargetables, CardPredicates.Presets.CREATURES);
|
||||
CardLists.sortByEvaluateCreature(combatants);
|
||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
Combat combat = game.getCombat();
|
||||
for (final Card c : combatants) {
|
||||
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
|
||||
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||
sa.getTargets().add(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -67,9 +68,9 @@ public class RegenerateAllAi extends SpellAbilityAi {
|
||||
} else {
|
||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
final List<Card> combatants = CardLists.filter(list, CardPredicates.Presets.CREATURES);
|
||||
|
||||
final Combat combat = game.getCombat();
|
||||
for (final Card c : combatants) {
|
||||
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
|
||||
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||
numSaved++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class BecomesBlockedEffect extends SpellAbilityEffect {
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
for (final Card c : getTargetCards(sa)) {
|
||||
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
||||
game.getCombat().setBlocked(c);
|
||||
game.getCombat().setBlocked(c, true);
|
||||
if (!c.getDamageHistory().getCreatureGotBlockedThisCombat()) {
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attacker", c);
|
||||
|
||||
@@ -23,6 +23,7 @@ import forge.card.spellability.SpellAbilityStackInstance;
|
||||
import forge.card.spellability.TargetRestrictions;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.Game;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -499,7 +500,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
List<GameEntity> defenders = game.getCombat().getDefenders();
|
||||
if (!defenders.isEmpty()) {
|
||||
// Blockeres are already declared, set this to unblocked
|
||||
game.getCombat().addAttacker(tgtC, defenders.get(0), false);
|
||||
game.getCombat().addAttacker(tgtC, defenders.get(0));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("Tapped") || sa.hasParam("Ninjutsu")) {
|
||||
@@ -840,9 +841,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("Attacking")) {
|
||||
final List<GameEntity> e = c.getController().getGame().getCombat().getDefenders();
|
||||
final GameEntity defender = e.size() == 1 ? e.get(0) : GuiChoose.one("Declare " + c, e);
|
||||
game.getCombat().addAttacker(c, defender);
|
||||
final Combat combat = game.getCombat();
|
||||
if ( null != combat ) {
|
||||
final List<GameEntity> e = combat.getDefenders();
|
||||
final GameEntity defender = e.size() == 1 ? e.get(0) : GuiChoose.one("Declare " + c, e);
|
||||
combat.addAttacker(c, defender);
|
||||
}
|
||||
}
|
||||
|
||||
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c);
|
||||
|
||||
@@ -100,7 +100,7 @@ public class ChooseColorEffect extends SpellAbilityEffect {
|
||||
final List<Card> list = game.getCardsIn(ZoneType.Battlefield);
|
||||
chosen.add(ComputerUtilCard.getMostProminentColor(list));
|
||||
}
|
||||
else if (logic.equals("MostProminentAttackers")) {
|
||||
else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) {
|
||||
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCombat().getAttackers()));
|
||||
}
|
||||
else if (logic.equals("MostProminentKeywordInComputerDeck")) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import forge.card.spellability.TargetRestrictions;
|
||||
import forge.game.Game;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
@@ -138,7 +139,7 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
|
||||
sourcesToChooseFrom.remove(o);
|
||||
|
||||
} else {
|
||||
if (sa.hasParam("AILogic") && sa.getParam("AILogic").equals("NeedsPrevention")) {
|
||||
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
|
||||
final Player ai = sa.getActivatingPlayer();
|
||||
if (!game.getStack().isEmpty()) {
|
||||
Card choseCard = ChooseCardOnStack(sa, ai, game);
|
||||
@@ -147,14 +148,15 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
final Combat combat = game.getCombat();
|
||||
if (chosen.isEmpty()) {
|
||||
permanentSources = CardLists.filter(permanentSources, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) {
|
||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||
return false;
|
||||
}
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0;
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > 0;
|
||||
}
|
||||
});
|
||||
chosen.add(ComputerUtilCard.getBestCreatureAI(permanentSources));
|
||||
|
||||
@@ -248,9 +248,8 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("Tapped")) {
|
||||
copy.setTapped(true);
|
||||
}
|
||||
if (sa.hasParam("CopyAttacking")) {
|
||||
final GameEntity defender = (GameEntity) AbilityUtils.getDefinedPlayers(hostCard,
|
||||
sa.getParam("CopyAttacking"), sa).get(0);
|
||||
if (sa.hasParam("CopyAttacking") && game.getPhaseHandler().inCombat()) {
|
||||
final GameEntity defender = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("CopyAttacking"), sa).get(0);
|
||||
game.getCombat().addAttacker(copy, defender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class EndTurnEffect extends SpellAbilityEffect {
|
||||
game.getStack().clear();
|
||||
|
||||
// 2) All attacking and blocking creatures are removed from combat.
|
||||
game.getCombat().reset(game.getPhaseHandler().getPlayerTurn());
|
||||
game.getPhaseHandler().endCombat();
|
||||
|
||||
// 3) State-based actions are checked. No player gets priority, and no
|
||||
// triggered abilities are put onto the stack.
|
||||
|
||||
@@ -31,11 +31,10 @@ public class RemoveFromCombatEffect extends SpellAbilityEffect {
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
for (final Card c : getTargetCards(sa)) {
|
||||
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
||||
game.getCombat().removeFromCombat(c);
|
||||
if ((tgt == null) || c.canBeTargetedBy(sa) && game.getPhaseHandler().inCombat()) {
|
||||
game.getPhaseHandler().getCombat().removeFromCombat(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import forge.card.trigger.Trigger;
|
||||
import forge.card.trigger.TriggerHandler;
|
||||
import forge.game.Game;
|
||||
import forge.game.event.GameEventTokenCreated;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.player.Player;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.item.PaperToken;
|
||||
@@ -281,14 +282,16 @@ public class TokenEffect extends SpellAbilityEffect {
|
||||
if (this.tokenTapped) {
|
||||
c.setTapped(true);
|
||||
}
|
||||
if (this.tokenAttacking) {
|
||||
final List<GameEntity> defs = c.getController().getGame().getCombat().getDefenders();
|
||||
if (this.tokenAttacking && game.getPhaseHandler().inCombat()) {
|
||||
Combat combat = game.getPhaseHandler().getCombat();
|
||||
final List<GameEntity> defs = combat.getDefenders();
|
||||
final GameEntity defender;
|
||||
if (c.getController().isHuman()) {
|
||||
final GameEntity defender = defs.size() == 1 ? defs.get(0) : GuiChoose.one("Declare " + c, defs);
|
||||
game.getCombat().addAttacker(c, defender);
|
||||
defender = defs.size() == 1 ? defs.get(0) : GuiChoose.one("Declare " + c, defs);
|
||||
} else {
|
||||
game.getCombat().addAttacker(c, defs.get(0));
|
||||
defender = defs.get(0);
|
||||
}
|
||||
combat.addAttacker(c, defender);
|
||||
}
|
||||
if (remember != null) {
|
||||
game.getCardState(sa.getSourceCard()).addRemembered(c);
|
||||
|
||||
@@ -71,7 +71,6 @@ public class Game {
|
||||
private final StaticEffects staticEffects = new StaticEffects();
|
||||
private final TriggerHandler triggerHandler = new TriggerHandler(this);
|
||||
private final ReplacementHandler replacementHandler = new ReplacementHandler(this);
|
||||
private Combat combat = new Combat();
|
||||
private final EventBus events = new EventBus();
|
||||
private final GameLog gameLog = new GameLog();
|
||||
private final ColorChanger colorChanger = new ColorChanger();
|
||||
@@ -228,18 +227,10 @@ public class Game {
|
||||
* @return the combat
|
||||
*/
|
||||
public final Combat getCombat() {
|
||||
return this.combat;
|
||||
return this.getPhaseHandler().getCombat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the combat.
|
||||
*
|
||||
* @param combat0
|
||||
* the combat to set
|
||||
*/
|
||||
public final void setCombat(final Combat combat0) {
|
||||
this.combat = combat0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the game log.
|
||||
|
||||
@@ -222,7 +222,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
if (zoneFrom != null) {
|
||||
if (zoneFrom.is(ZoneType.Battlefield) && c.isCreature()) {
|
||||
if (zoneFrom.is(ZoneType.Battlefield) && c.isCreature() && game.getCombat() != null) {
|
||||
game.getCombat().removeFromCombat(c);
|
||||
}
|
||||
zoneFrom.remove(c);
|
||||
@@ -1348,7 +1348,8 @@ public class GameAction {
|
||||
final boolean persist = (c.hasKeyword("Persist") && (c.getCounters(CounterType.M1M1) == 0)) && !c.isToken();
|
||||
final boolean undying = (c.hasKeyword("Undying") && (c.getCounters(CounterType.P1P1) == 0)) && !c.isToken();
|
||||
|
||||
game.getCombat().removeFromCombat(c);
|
||||
if (game.getPhaseHandler().inCombat())
|
||||
game.getPhaseHandler().getCombat().removeFromCombat(c);
|
||||
|
||||
final Card newCard = this.moveToGraveyard(c);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
@@ -29,7 +30,6 @@ import forge.CounterType;
|
||||
import forge.GameEntity;
|
||||
import forge.card.trigger.Trigger;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.Game;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.player.Player;
|
||||
@@ -58,11 +58,10 @@ public class AiAttackController {
|
||||
private List<Card> myList; // holds computer creatures
|
||||
|
||||
private final Player ai;
|
||||
private final Player opponent;
|
||||
|
||||
private int aiAggression = 0; // added by Masher, how aggressive the ai is
|
||||
// attack will be depending on circumstances
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -74,11 +73,12 @@ public class AiAttackController {
|
||||
* @param possibleBlockers
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
public AiAttackController(final Player ai, final Player opponent) {
|
||||
public AiAttackController(final Player ai) {
|
||||
this.ai = ai;
|
||||
this.opponent = opponent;
|
||||
Player opponent = ai.getOpponent();
|
||||
|
||||
this.oppList = opponent.getCreaturesInPlay();
|
||||
this.oppList = Lists.newArrayList();
|
||||
this.oppList.addAll(opponent.getCreaturesInPlay());
|
||||
this.myList = ai.getCreaturesInPlay();
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ public class AiAttackController {
|
||||
if (!CombatUtil.canAttackNextTurn(attacker)) {
|
||||
continue;
|
||||
}
|
||||
if (blockersLeft > 0 && CombatUtil.canBeBlocked(attacker)) {
|
||||
if (blockersLeft > 0 && CombatUtil.canBeBlocked(attacker, ai)) {
|
||||
blockersLeft--;
|
||||
continue;
|
||||
}
|
||||
@@ -442,7 +442,7 @@ public class AiAttackController {
|
||||
* @param bAssault
|
||||
* a boolean.
|
||||
*/
|
||||
public final GameEntity chooseDefender(final Combat c, final Combat gameCombat, final boolean bAssault) {
|
||||
private final GameEntity chooseDefender(final Combat c, final boolean bAssault) {
|
||||
final List<GameEntity> defs = c.getDefenders();
|
||||
if (defs.size() == 1) {
|
||||
return defs.get(0);
|
||||
@@ -450,8 +450,7 @@ public class AiAttackController {
|
||||
|
||||
final GameEntity entity = ai.getMustAttackEntity();
|
||||
if (null != entity) {
|
||||
final List<GameEntity> defenders = gameCombat.getDefenders();
|
||||
int n = defenders.indexOf(entity);
|
||||
int n = defs.indexOf(entity);
|
||||
if (-1 == n) {
|
||||
System.out.println("getMustAttackEntity() returned something not in defenders.");
|
||||
return defs.get(0);
|
||||
@@ -483,29 +482,22 @@ public class AiAttackController {
|
||||
*
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
public final Combat getAttackers() {
|
||||
public final void declareAttackers(final Combat combat) {
|
||||
// if this method is called multiple times during a turn,
|
||||
// it will always return the same value
|
||||
// randomInt is used so that the computer doesn't always
|
||||
// do the same thing on turn 3 if he had the same creatures in play
|
||||
// I know this is a little confusing
|
||||
Game game = ai.getGame();
|
||||
|
||||
random.setSeed(game.getPhaseHandler().getTurn() + AiAttackController.randomInt);
|
||||
|
||||
final Combat combat = new Combat();
|
||||
combat.setAttackingPlayer(game.getCombat().getAttackingPlayer());
|
||||
|
||||
game.getCombat().initiatePossibleDefenders(opponent);
|
||||
combat.setDefenders(game.getCombat().getDefenders());
|
||||
|
||||
random.setSeed(ai.getGame().getPhaseHandler().getTurn() + AiAttackController.randomInt);
|
||||
|
||||
if (this.attackers.isEmpty()) {
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean bAssault = this.doAssault(ai);
|
||||
// Determine who will be attacked
|
||||
GameEntity defender = this.chooseDefender(combat, game.getCombat(), bAssault);
|
||||
GameEntity defender = this.chooseDefender(combat, bAssault);
|
||||
List<Card> attackersLeft = new ArrayList<Card>(this.attackers);
|
||||
// Attackers that don't really have a choice
|
||||
for (final Card attacker : this.attackers) {
|
||||
@@ -529,7 +521,7 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
if (attackersLeft.isEmpty()) {
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
if (bAssault) {
|
||||
if ( LOG_AI_ATTACKS )
|
||||
@@ -540,7 +532,7 @@ public class AiAttackController {
|
||||
combat.addAttacker(attacker, defender);
|
||||
}
|
||||
}
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exalted
|
||||
@@ -552,8 +544,7 @@ public class AiAttackController {
|
||||
exalted = true;
|
||||
break;
|
||||
}
|
||||
if (c.getName().equals("Finest Hour")
|
||||
&& game.getPhaseHandler().isFirstCombat()) {
|
||||
if (c.getName().equals("Finest Hour") && ai.getGame().getPhaseHandler().isFirstCombat()) {
|
||||
exalted = true;
|
||||
break;
|
||||
}
|
||||
@@ -573,7 +564,7 @@ public class AiAttackController {
|
||||
for (Card attacker : this.attackers) {
|
||||
if (CombatUtil.canAttack(attacker, defender, combat) && this.shouldAttack(ai, attacker, this.blockers, combat)) {
|
||||
combat.addAttacker(attacker, defender);
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -821,8 +812,6 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combat;
|
||||
} // getAttackers()
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,162 +41,36 @@ import forge.game.player.Player;
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ComputerUtilBlock {
|
||||
public class AiBlockController {
|
||||
|
||||
private final Player ai;
|
||||
/** Constant <code>attackers</code>. */
|
||||
private static List<Card> attackers = new ArrayList<Card>(); // all attackers
|
||||
private List<Card> attackers = new ArrayList<Card>(); // all attackers
|
||||
/** Constant <code>attackersLeft</code>. */
|
||||
private static List<Card> attackersLeft = new ArrayList<Card>(); // keeps track of
|
||||
private List<Card> attackersLeft = new ArrayList<Card>(); // keeps track of
|
||||
// all currently
|
||||
// unblocked
|
||||
// attackers
|
||||
/** Constant <code>blockedButUnkilled</code>. */
|
||||
private static List<Card> blockedButUnkilled = new ArrayList<Card>(); // blocked
|
||||
private List<Card> blockedButUnkilled = new ArrayList<Card>(); // blocked
|
||||
// attackers
|
||||
// that
|
||||
// currently
|
||||
// wouldn't be
|
||||
// destroyed
|
||||
/** Constant <code>blockersLeft</code>. */
|
||||
private static List<Card> blockersLeft = new ArrayList<Card>(); // keeps track of all
|
||||
private List<Card> blockersLeft = new ArrayList<Card>(); // keeps track of all
|
||||
// unassigned
|
||||
// blockers
|
||||
/** Constant <code>diff=0</code>. */
|
||||
private static int diff = 0;
|
||||
private int diff = 0;
|
||||
|
||||
private static boolean lifeInDanger = false;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>attackers</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getAttackers() {
|
||||
return ComputerUtilBlock.attackers;
|
||||
private boolean lifeInDanger = false;
|
||||
public AiBlockController(Player aiPlayer) {
|
||||
this.ai = aiPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>attackers</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param cardList
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
private static void setAttackers(final List<Card> cardList) {
|
||||
ComputerUtilBlock.attackers = (cardList);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>attackersLeft</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getAttackersLeft() {
|
||||
return ComputerUtilBlock.attackersLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>attackersLeft</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param cardList
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
private static void setAttackersLeft(final List<Card> cardList) {
|
||||
ComputerUtilBlock.attackersLeft = (cardList);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>blockedButUnkilled</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getBlockedButUnkilled() {
|
||||
return ComputerUtilBlock.blockedButUnkilled;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>blockedButUnkilled</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param cardList
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
private static void setBlockedButUnkilled(final List<Card> cardList) {
|
||||
ComputerUtilBlock.blockedButUnkilled = (cardList);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>blockersLeft</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getBlockersLeft() {
|
||||
return ComputerUtilBlock.blockersLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>blockersLeft</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param cardList
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
private static void setBlockersLeft(final List<Card> cardList) {
|
||||
ComputerUtilBlock.blockersLeft = (cardList);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>diff</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a int.
|
||||
*/
|
||||
private static int getDiff() {
|
||||
return ComputerUtilBlock.diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>diff</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param diff
|
||||
* a int.
|
||||
*/
|
||||
private static void setDiff(final int diff) {
|
||||
ComputerUtilBlock.diff = (diff);
|
||||
}
|
||||
|
||||
|
||||
// finds the creatures able to block the attacker
|
||||
/**
|
||||
* <p>
|
||||
* getPossibleBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @param blockersLeft
|
||||
* a {@link forge.CardList} object.
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getPossibleBlockers(final Card attacker, final List<Card> blockersLeft, final Combat combat
|
||||
, final boolean solo) {
|
||||
private List<Card> getPossibleBlockers(final Combat combat, final Card attacker, final List<Card> blockersLeft, final boolean solo) {
|
||||
final List<Card> blockers = new ArrayList<Card>();
|
||||
|
||||
for (final Card blocker : blockersLeft) {
|
||||
@@ -214,20 +88,7 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
|
||||
// finds blockers that won't be destroyed
|
||||
/**
|
||||
* <p>
|
||||
* getSafeBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @param blockersLeft
|
||||
* a {@link forge.CardList} object.
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getSafeBlockers(final Player ai, final Card attacker, final List<Card> blockersLeft, final Combat combat) {
|
||||
private List<Card> getSafeBlockers(final Combat combat, final Card attacker, final List<Card> blockersLeft) {
|
||||
final List<Card> blockers = new ArrayList<Card>();
|
||||
|
||||
for (final Card b : blockersLeft) {
|
||||
@@ -240,20 +101,7 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
|
||||
// finds blockers that destroy the attacker
|
||||
/**
|
||||
* <p>
|
||||
* getKillingBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @param blockersLeft
|
||||
* a {@link forge.CardList} object.
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
private static List<Card> getKillingBlockers(final Player ai, final Card attacker, final List<Card> blockersLeft, final Combat combat) {
|
||||
private List<Card> getKillingBlockers(final Combat combat, final Card attacker, final List<Card> blockersLeft) {
|
||||
final List<Card> blockers = new ArrayList<Card>();
|
||||
|
||||
for (final Card b : blockersLeft) {
|
||||
@@ -267,7 +115,7 @@ public class ComputerUtilBlock {
|
||||
|
||||
|
||||
|
||||
public final static List<List<Card>> sortAttackerByDefender(Combat combat) {
|
||||
private List<List<Card>> sortAttackerByDefender(final Combat combat) {
|
||||
List<GameEntity> defenders = combat.getDefenders();
|
||||
final ArrayList<List<Card>> attackers = new ArrayList<List<Card>>(defenders.size());
|
||||
for (GameEntity defender : defenders) {
|
||||
@@ -276,7 +124,7 @@ public class ComputerUtilBlock {
|
||||
return attackers;
|
||||
}
|
||||
|
||||
public static List<Card> sortPotentialAttackers(final Player ai, final Combat combat) {
|
||||
private List<Card> sortPotentialAttackers(final Combat combat) {
|
||||
final List<List<Card>> attackerLists = sortAttackerByDefender(combat);
|
||||
final List<Card> sortedAttackers = new ArrayList<Card>();
|
||||
final List<Card> firstAttacker = attackerLists.get(0);
|
||||
@@ -328,20 +176,11 @@ public class ComputerUtilBlock {
|
||||
// ================================
|
||||
|
||||
// Good Blocks means a good trade or no trade
|
||||
/**
|
||||
* <p>
|
||||
* makeGoodBlocks.
|
||||
* </p>
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat makeGoodBlocks(final Player ai, final Combat combat) {
|
||||
private void makeGoodBlocks(final Combat combat) {
|
||||
|
||||
List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
|
||||
List<Card> currentAttackers = new ArrayList<Card>(attackersLeft);
|
||||
|
||||
for (final Card attacker : ComputerUtilBlock.getAttackersLeft()) {
|
||||
for (final Card attacker : attackersLeft) {
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
continue;
|
||||
@@ -349,26 +188,25 @@ public class ComputerUtilBlock {
|
||||
|
||||
Card blocker = null;
|
||||
|
||||
final List<Card> blockers = ComputerUtilBlock.getPossibleBlockers(attacker,
|
||||
ComputerUtilBlock.getBlockersLeft(), combat, true);
|
||||
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
|
||||
final List<Card> safeBlockers = ComputerUtilBlock.getSafeBlockers(ai, attacker, blockers, combat);
|
||||
final List<Card> safeBlockers = getSafeBlockers(combat, attacker, blockers);
|
||||
List<Card> killingBlockers;
|
||||
|
||||
if (safeBlockers.size() > 0) {
|
||||
// 1.Blockers that can destroy the attacker but won't get
|
||||
// destroyed
|
||||
killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker, safeBlockers, combat);
|
||||
killingBlockers = getKillingBlockers(combat, attacker, safeBlockers);
|
||||
if (killingBlockers.size() > 0) {
|
||||
blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
|
||||
} else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||
blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
|
||||
ComputerUtilBlock.getBlockedButUnkilled().add(attacker);
|
||||
blockedButUnkilled.add(attacker);
|
||||
}
|
||||
} // no safe blockers
|
||||
else {
|
||||
// 3.Blockers that can destroy the attacker and have an upside when dying
|
||||
killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker, blockers, combat);
|
||||
killingBlockers = getKillingBlockers(combat, attacker, blockers);
|
||||
for (Card b : killingBlockers) {
|
||||
if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0)
|
||||
|| !b.getSVar("SacMe").equals("")) {
|
||||
@@ -380,7 +218,7 @@ public class ComputerUtilBlock {
|
||||
if (blocker == null && killingBlockers.size() > 0) {
|
||||
final Card worst = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
|
||||
|
||||
if ((ComputerUtilCard.evaluateCreature(worst) + ComputerUtilBlock.getDiff()) < ComputerUtilCard
|
||||
if ((ComputerUtilCard.evaluateCreature(worst) + diff) < ComputerUtilCard
|
||||
.evaluateCreature(attacker)) {
|
||||
blocker = worst;
|
||||
}
|
||||
@@ -391,8 +229,7 @@ public class ComputerUtilBlock {
|
||||
combat.addBlocker(attacker, blocker);
|
||||
}
|
||||
}
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(currentAttackers));
|
||||
return combat;
|
||||
attackersLeft = (new ArrayList<Card>(currentAttackers));
|
||||
}
|
||||
|
||||
// Good Gang Blocks means a good trade or no trade
|
||||
@@ -407,14 +244,14 @@ public class ComputerUtilBlock {
|
||||
*/
|
||||
static final Predicate<Card> rampagesOrNeedsManyToBlock = Predicates.or(CardPredicates.containsKeyword("Rampage"), CardPredicates.containsKeyword("CantBeBlockedByAmount GT"));
|
||||
|
||||
private static Combat makeGangBlocks(final Player ai, final Combat combat) {
|
||||
List<Card> currentAttackers = CardLists.filter(ComputerUtilBlock.getAttackersLeft(), Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
private void makeGangBlocks(final Combat combat) {
|
||||
List<Card> currentAttackers = CardLists.filter(attackersLeft, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
List<Card> blockers;
|
||||
|
||||
// Try to block an attacker without first strike with a gang of first strikers
|
||||
for (final Card attacker : ComputerUtilBlock.getAttackersLeft()) {
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (!attacker.hasKeyword("First Strike") && !attacker.hasKeyword("Double Strike")) {
|
||||
blockers = ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, false);
|
||||
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||
final List<Card> firstStrikeBlockers = new ArrayList<Card>();
|
||||
final List<Card> blockGang = new ArrayList<Card>();
|
||||
for (int i = 0; i < blockers.size(); i++) {
|
||||
@@ -448,12 +285,12 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
}
|
||||
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(currentAttackers));
|
||||
currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
|
||||
attackersLeft = (new ArrayList<Card>(currentAttackers));
|
||||
currentAttackers = new ArrayList<Card>(attackersLeft);
|
||||
|
||||
// Try to block an attacker with two blockers of which only one will die
|
||||
for (final Card attacker : ComputerUtilBlock.getAttackersLeft()) {
|
||||
blockers = ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, false);
|
||||
for (final Card attacker : attackersLeft) {
|
||||
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||
List<Card> usableBlockers;
|
||||
final List<Card> blockGang = new ArrayList<Card>();
|
||||
int absorbedDamage = 0; // The amount of damage needed to kill the
|
||||
@@ -471,12 +308,11 @@ public class ComputerUtilBlock {
|
||||
&& !(c.hasKeyword("First Strike") || c.hasKeyword("Double Strike"))) {
|
||||
return false;
|
||||
}
|
||||
return lifeInDanger || (ComputerUtilCard.evaluateCreature(c) + ComputerUtilBlock.getDiff()) < ComputerUtilCard
|
||||
.evaluateCreature(attacker);
|
||||
return lifeInDanger || (ComputerUtilCard.evaluateCreature(c) + diff) < ComputerUtilCard.evaluateCreature(attacker);
|
||||
}
|
||||
});
|
||||
if (usableBlockers.size() < 2) {
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
|
||||
final Card leader = ComputerUtilCard.getBestCreatureAI(usableBlockers);
|
||||
@@ -516,8 +352,7 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
}
|
||||
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(currentAttackers));
|
||||
return combat;
|
||||
attackersLeft = (new ArrayList<Card>(currentAttackers));
|
||||
}
|
||||
|
||||
// Bad Trade Blocks (should only be made if life is in danger)
|
||||
@@ -530,68 +365,56 @@ public class ComputerUtilBlock {
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat makeTradeBlocks(final Player ai, final Combat combat) {
|
||||
private void makeTradeBlocks(final Combat combat) {
|
||||
|
||||
List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
|
||||
List<Card> currentAttackers = new ArrayList<Card>(attackersLeft);
|
||||
List<Card> killingBlockers;
|
||||
|
||||
for (final Card attacker : ComputerUtilBlock.getAttackersLeft()) {
|
||||
for (final Card attacker : attackersLeft) {
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker,
|
||||
ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, true),
|
||||
combat);
|
||||
List<Card> possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
killingBlockers = getKillingBlockers(combat, attacker, possibleBlockers);
|
||||
if ((killingBlockers.size() > 0) && ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
final Card blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
|
||||
combat.addBlocker(attacker, blocker);
|
||||
currentAttackers.remove(attacker);
|
||||
}
|
||||
}
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(currentAttackers));
|
||||
return combat;
|
||||
attackersLeft = (new ArrayList<Card>(currentAttackers));
|
||||
|
||||
}
|
||||
|
||||
// Chump Blocks (should only be made if life is in danger)
|
||||
/**
|
||||
* <p>
|
||||
* makeChumpBlocks.
|
||||
* </p>
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat makeChumpBlocks(final Player ai, final Combat combat) {
|
||||
private void makeChumpBlocks(final Combat combat) {
|
||||
|
||||
List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
|
||||
List<Card> currentAttackers = new ArrayList<Card>(attackersLeft);
|
||||
|
||||
Combat newCombat = makeChumpBlocks(ai, combat, currentAttackers);
|
||||
makeChumpBlocks(combat, currentAttackers);
|
||||
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, newCombat)) {
|
||||
return makeMultiChumpBlocks(ai, newCombat);
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
makeMultiChumpBlocks(combat);
|
||||
}
|
||||
|
||||
return newCombat;
|
||||
}
|
||||
|
||||
private static Combat makeChumpBlocks(final Player ai, final Combat combat, List<Card> attackers) {
|
||||
private void makeChumpBlocks(final Combat combat, List<Card> attackers) {
|
||||
|
||||
if (attackers.isEmpty() || !ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
return combat;
|
||||
return;
|
||||
}
|
||||
|
||||
Card attacker = attackers.get(0);
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||
attackers.remove(0);
|
||||
return makeChumpBlocks(ai, combat, attackers);
|
||||
makeChumpBlocks(combat, attackers);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Card> chumpBlockers = ComputerUtilBlock
|
||||
.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, true);
|
||||
List<Card> chumpBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
if (!chumpBlockers.isEmpty()) {
|
||||
final Card blocker = ComputerUtilCard.getWorstCreatureAI(chumpBlockers);
|
||||
|
||||
@@ -607,44 +430,35 @@ public class ComputerUtilBlock {
|
||||
&& !other.hasKeyword("Trample")
|
||||
&& CombatUtil.canBlock(other, blocker, combat)) {
|
||||
combat.addBlocker(other, blocker);
|
||||
ComputerUtilBlock.getAttackersLeft().remove(other);
|
||||
ComputerUtilBlock.getBlockedButUnkilled().add(other);
|
||||
attackersLeft.remove(other);
|
||||
blockedButUnkilled.add(other);
|
||||
attackers.remove(other);
|
||||
return makeChumpBlocks(ai, combat, attackers);
|
||||
makeChumpBlocks(combat, attackers);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
combat.addBlocker(attacker, blocker);
|
||||
ComputerUtilBlock.getAttackersLeft().remove(attacker);
|
||||
ComputerUtilBlock.getBlockedButUnkilled().add(attacker);
|
||||
attackersLeft.remove(attacker);
|
||||
blockedButUnkilled.add(attacker);
|
||||
}
|
||||
attackers.remove(0);
|
||||
return makeChumpBlocks(ai, combat, attackers);
|
||||
makeChumpBlocks(combat, attackers);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* makeMultiChumpBlocks.
|
||||
* </p>
|
||||
*
|
||||
* Block creatures with "can't be blocked except by two or more creatures"
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat makeMultiChumpBlocks(final Player ai, final Combat combat) {
|
||||
// Block creatures with "can't be blocked except by two or more creatures"
|
||||
private void makeMultiChumpBlocks(final Combat combat) {
|
||||
|
||||
List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
|
||||
List<Card> currentAttackers = new ArrayList<Card>(attackersLeft);
|
||||
|
||||
for (final Card attacker : currentAttackers) {
|
||||
|
||||
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
continue;
|
||||
}
|
||||
List<Card> possibleBlockers = ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, true);
|
||||
List<Card> possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
if (!CombatUtil.canAttackerBeBlockedWithAmount(attacker, possibleBlockers.size())) {
|
||||
continue;
|
||||
}
|
||||
@@ -659,33 +473,21 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
}
|
||||
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size())) {
|
||||
ComputerUtilBlock.getAttackersLeft().remove(attacker);
|
||||
attackersLeft.remove(attacker);
|
||||
} else {
|
||||
for (Card blocker : usedBlockers) {
|
||||
combat.removeBlockAssignment(attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combat;
|
||||
}
|
||||
|
||||
// Reinforce blockers blocking attackers with trample (should only be made
|
||||
// if life is in danger)
|
||||
/**
|
||||
* <p>
|
||||
* reinforceBlockersAgainstTrample.
|
||||
* </p>
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat reinforceBlockersAgainstTrample(final Player ai, final Combat combat) {
|
||||
/** Reinforce blockers blocking attackers with trample (should only be made if life is in danger) */
|
||||
private void reinforceBlockersAgainstTrample(final Combat combat) {
|
||||
|
||||
List<Card> chumpBlockers;
|
||||
|
||||
List<Card> tramplingAttackers = CardLists.getKeyword(ComputerUtilBlock.getAttackers(), "Trample");
|
||||
List<Card> tramplingAttackers = CardLists.getKeyword(attackers, "Trample");
|
||||
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
|
||||
// TODO - should check here for a "rampage-like" trigger that replaced
|
||||
@@ -699,8 +501,7 @@ public class ComputerUtilBlock {
|
||||
continue;
|
||||
}
|
||||
|
||||
chumpBlockers = ComputerUtilBlock
|
||||
.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, false);
|
||||
chumpBlockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||
chumpBlockers.removeAll(combat.getBlockers(attacker));
|
||||
for (final Card blocker : chumpBlockers) {
|
||||
// Add an additional blocker if the current blockers are not
|
||||
@@ -712,38 +513,26 @@ public class ComputerUtilBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combat;
|
||||
}
|
||||
|
||||
// Support blockers not destroying the attacker with more blockers to try to
|
||||
// kill the attacker
|
||||
/**
|
||||
* <p>
|
||||
* reinforceBlockersToKill.
|
||||
* </p>
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat reinforceBlockersToKill(final Player ai, final Combat combat) {
|
||||
/** Support blockers not destroying the attacker with more blockers to try to kill the attacker */
|
||||
private void reinforceBlockersToKill(final Combat combat) {
|
||||
|
||||
List<Card> safeBlockers;
|
||||
List<Card> blockers;
|
||||
|
||||
List<Card> targetAttackers = CardLists.filter(ComputerUtilBlock.getBlockedButUnkilled(), Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
List<Card> targetAttackers = CardLists.filter(blockedButUnkilled, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
|
||||
// TODO - should check here for a "rampage-like" trigger that replaced
|
||||
// the keyword:
|
||||
// "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
|
||||
|
||||
for (final Card attacker : targetAttackers) {
|
||||
blockers = ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, false);
|
||||
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||
blockers.removeAll(combat.getBlockers(attacker));
|
||||
|
||||
// Try to use safe blockers first
|
||||
safeBlockers = ComputerUtilBlock.getSafeBlockers(ai, attacker, blockers, combat);
|
||||
safeBlockers = getSafeBlockers(combat, attacker, blockers);
|
||||
for (final Card blocker : safeBlockers) {
|
||||
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
|
||||
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
|
||||
@@ -777,198 +566,153 @@ public class ComputerUtilBlock {
|
||||
final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
|
||||
if ((damageNeeded > currentDamage)
|
||||
&& !(damageNeeded > (currentDamage + additionalDamage))
|
||||
&& ((ComputerUtilCard.evaluateCreature(blocker) + ComputerUtilBlock.getDiff()) < ComputerUtilCard
|
||||
&& ((ComputerUtilCard.evaluateCreature(blocker) + diff) < ComputerUtilCard
|
||||
.evaluateCreature(attacker)) && CombatUtil.canBlock(attacker, blocker, combat)) {
|
||||
combat.addBlocker(attacker, blocker);
|
||||
ComputerUtilBlock.getBlockersLeft().remove(blocker);
|
||||
blockersLeft.remove(blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combat;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* resetBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @param combat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @param possibleBlockers
|
||||
* a {@link forge.CardList} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
private static Combat resetBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
||||
private void clearBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
||||
|
||||
final List<Card> oldBlockers = combat.getAllBlockers();
|
||||
for (final Card blocker : oldBlockers) {
|
||||
combat.removeFromCombat(blocker);
|
||||
if ( blocker.getController() == ai ) // don't touch other player's blockers
|
||||
combat.removeFromCombat(blocker);
|
||||
}
|
||||
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(ComputerUtilBlock.getAttackers())); // keeps
|
||||
// track
|
||||
// of all
|
||||
// currently
|
||||
// unblocked
|
||||
// attackers
|
||||
ComputerUtilBlock.setBlockersLeft(new ArrayList<Card>(possibleBlockers)); // keeps
|
||||
// track of
|
||||
// all
|
||||
// unassigned
|
||||
// blockers
|
||||
ComputerUtilBlock.setBlockedButUnkilled(new ArrayList<Card>()); // keeps track
|
||||
// of all
|
||||
// blocked
|
||||
// attackers that currently
|
||||
// wouldn't be destroyed
|
||||
|
||||
return combat;
|
||||
attackersLeft = new ArrayList<Card>(attackers); // keeps track of all currently unblocked attackers
|
||||
blockersLeft = new ArrayList<Card>(possibleBlockers); // keeps track of all unassigned blockers
|
||||
blockedButUnkilled = new ArrayList<Card>(); // keeps track of all blocked attackers that currently wouldn't be destroyed
|
||||
}
|
||||
|
||||
// Main function
|
||||
/**
|
||||
* <p>
|
||||
* getBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @param originalCombat
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @param possibleBlockers
|
||||
* a {@link forge.CardList} object.
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
public static Combat getBlockers(final Player ai, final Combat originalCombat, final List<Card> possibleBlockers) {
|
||||
/** Assigns blockers for the provided combat instance (in favor of player passes to ctor) */
|
||||
public void assignBlockers(final Combat combat) {
|
||||
|
||||
final List<Card> possibleBlockers = ai.getCreaturesInPlay();
|
||||
|
||||
Combat combat = originalCombat;
|
||||
attackers = sortPotentialAttackers(combat);
|
||||
|
||||
ComputerUtilBlock.setAttackers(ComputerUtilBlock.sortPotentialAttackers(ai, combat));
|
||||
|
||||
if (ComputerUtilBlock.getAttackers().size() == 0) {
|
||||
return combat;
|
||||
if (attackers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// keeps track of all currently unblocked attackers
|
||||
ComputerUtilBlock.setAttackersLeft(new ArrayList<Card>(ComputerUtilBlock.getAttackers()));
|
||||
// keeps track of all unassigned blockers
|
||||
ComputerUtilBlock.setBlockersLeft(new ArrayList<Card>(possibleBlockers));
|
||||
// keeps track of all blocked attackers that currently wouldn't be destroyed
|
||||
ComputerUtilBlock.setBlockedButUnkilled(new ArrayList<Card>());
|
||||
clearBlockers(combat, possibleBlockers);
|
||||
|
||||
List<Card> blockers;
|
||||
List<Card> chumpBlockers;
|
||||
|
||||
ComputerUtilBlock.setDiff((ai.getLife() * 2) - 5); // This is the minimal gain for an unnecessary trade
|
||||
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
|
||||
|
||||
// remove all attackers that can't be blocked anyway
|
||||
for (final Card a : ComputerUtilBlock.getAttackers()) {
|
||||
if (!CombatUtil.canBeBlocked(a)) {
|
||||
ComputerUtilBlock.getAttackersLeft().remove(a);
|
||||
for (final Card a : attackers) {
|
||||
if (!CombatUtil.canBeBlocked(a, ai)) {
|
||||
attackersLeft.remove(a);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all blockers that can't block anyway
|
||||
for (final Card b : possibleBlockers) {
|
||||
if (!CombatUtil.canBlock(b, combat)) {
|
||||
ComputerUtilBlock.getBlockersLeft().remove(b);
|
||||
blockersLeft.remove(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (ComputerUtilBlock.getAttackersLeft().size() == 0) {
|
||||
return combat;
|
||||
if (attackersLeft.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin with the weakest blockers
|
||||
CardLists.sortByPowerAsc(ComputerUtilBlock.getBlockersLeft());
|
||||
CardLists.sortByPowerAsc(blockersLeft);
|
||||
|
||||
// == 1. choose best blocks first ==
|
||||
combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
|
||||
combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
|
||||
makeGoodBlocks(combat);
|
||||
makeGangBlocks(combat);
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.makeTradeBlocks(ai, combat); // choose necessary trade blocks
|
||||
makeTradeBlocks(combat); // choose necessary trade blocks
|
||||
}
|
||||
// if life is in danger
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.makeChumpBlocks(ai, combat); // choose necessary chump blocks
|
||||
makeChumpBlocks(combat); // choose necessary chump blocks
|
||||
}
|
||||
// if life is still in danger
|
||||
// Reinforce blockers blocking attackers with trample if life is still
|
||||
// in danger
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.reinforceBlockersAgainstTrample(ai, combat);
|
||||
reinforceBlockersAgainstTrample(combat);
|
||||
}
|
||||
// Support blockers not destroying the attacker with more blockers to
|
||||
// try to kill the attacker
|
||||
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
|
||||
// == 2. If the AI life would still be in danger make a safer approach
|
||||
// ==
|
||||
// == 2. If the AI life would still be in danger make a safer approach ==
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
lifeInDanger = true;
|
||||
combat = ComputerUtilBlock.resetBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
combat = ComputerUtilBlock.makeTradeBlocks(ai, combat); // choose necessary trade blocks
|
||||
clearBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
makeTradeBlocks(combat); // choose necessary trade blocks
|
||||
// if life is in danger
|
||||
combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
|
||||
makeGoodBlocks(combat);
|
||||
// choose necessary chump blocks if life is still in danger
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.makeChumpBlocks(ai, combat);
|
||||
makeChumpBlocks(combat);
|
||||
}
|
||||
// Reinforce blockers blocking attackers with trample if life is
|
||||
// still in danger
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.reinforceBlockersAgainstTrample(ai, combat);
|
||||
reinforceBlockersAgainstTrample(combat);
|
||||
}
|
||||
combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
|
||||
combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
|
||||
makeGangBlocks(combat);
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
|
||||
// == 3. If the AI life would be in serious danger make an even safer approach ==
|
||||
if (lifeInDanger && ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.resetBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
combat = ComputerUtilBlock.makeChumpBlocks(ai, combat); // choose chump blocks
|
||||
clearBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
makeChumpBlocks(combat); // choose chump blocks
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.makeTradeBlocks(ai, combat); // choose necessary trade
|
||||
makeTradeBlocks(combat); // choose necessary trade
|
||||
}
|
||||
|
||||
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
|
||||
makeGoodBlocks(combat);
|
||||
}
|
||||
// Reinforce blockers blocking attackers with trample if life is
|
||||
// still in danger
|
||||
else {
|
||||
combat = ComputerUtilBlock.reinforceBlockersAgainstTrample(ai, combat);
|
||||
reinforceBlockersAgainstTrample(combat);
|
||||
}
|
||||
combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
|
||||
makeGangBlocks(combat);
|
||||
// Support blockers not destroying the attacker with more blockers
|
||||
// to try to kill the attacker
|
||||
combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
|
||||
// assign blockers that have to block
|
||||
chumpBlockers = CardLists.getKeyword(ComputerUtilBlock.getBlockersLeft(), "CARDNAME blocks each turn if able.");
|
||||
chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each turn if able.");
|
||||
// if an attacker with lure attacks - all that can block
|
||||
for (final Card blocker : ComputerUtilBlock.getBlockersLeft()) {
|
||||
for (final Card blocker : blockersLeft) {
|
||||
if (CombatUtil.mustBlockAnAttacker(blocker, combat)) {
|
||||
chumpBlockers.add(blocker);
|
||||
}
|
||||
}
|
||||
if (!chumpBlockers.isEmpty()) {
|
||||
CardLists.shuffle(ComputerUtilBlock.getAttackers());
|
||||
for (final Card attacker : ComputerUtilBlock.getAttackers()) {
|
||||
blockers = ComputerUtilBlock.getPossibleBlockers(attacker, chumpBlockers, combat, false);
|
||||
CardLists.shuffle(attackers);
|
||||
for (final Card attacker : attackers) {
|
||||
blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
|
||||
for (final Card blocker : blockers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker, combat) && ComputerUtilBlock.getBlockersLeft().contains(blocker)
|
||||
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
||||
&& (CombatUtil.mustBlockAnAttacker(blocker, combat)
|
||||
|| blocker.hasKeyword("CARDNAME blocks each turn if able."))) {
|
||||
combat.addBlocker(attacker, blocker);
|
||||
ComputerUtilBlock.getBlockersLeft().remove(blocker);
|
||||
blockersLeft.remove(blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combat;
|
||||
}
|
||||
|
||||
public static List<Card> orderBlockers(Card attacker, List<Card> blockers) {
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.ai;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -46,7 +45,7 @@ import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.SpellPermanent;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.Game;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
@@ -647,7 +646,7 @@ public class AiController {
|
||||
}
|
||||
|
||||
String exMsg = String.format("AI confirmAction does not know what to decide about %s mode (api is null).", mode);
|
||||
throw new InvalidParameterException(exMsg);
|
||||
throw new IllegalArgumentException(exMsg);
|
||||
|
||||
} else
|
||||
return api.getAi().confirmAction(player, sa, mode, message);
|
||||
@@ -741,19 +740,20 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
public void declateBlockers(Player defender) {
|
||||
final List<Card> blockers = defender.getCreaturesInPlay();
|
||||
// declares blockers for given defender in a given combat
|
||||
public void declareBlockersFor(Player defender, Combat combat) {
|
||||
AiBlockController block = new AiBlockController(defender);
|
||||
// When player != defender, AI should declare blockers for its benefit.
|
||||
game.setCombat(ComputerUtilBlock.getBlockers(defender, game.getCombat(), blockers));
|
||||
CombatUtil.orderMultipleCombatants(game.getCombat());
|
||||
block.assignBlockers(combat);
|
||||
}
|
||||
|
||||
|
||||
public void declareAttackers(Player attacker) {
|
||||
public Combat declareAttackers(Player attacker, Combat combat) {
|
||||
// 12/2/10(sol) the decision making here has moved to getAttackers()
|
||||
game.setCombat(new AiAttackController(attacker, attacker.getOpponent()).getAttackers());
|
||||
AiAttackController aiAtk = new AiAttackController(attacker);
|
||||
aiAtk.declareAttackers(combat);
|
||||
|
||||
for (final Card element : game.getCombat().getAttackers()) {
|
||||
for (final Card element : combat.getAttackers()) {
|
||||
// tapping of attackers happens after Propaganda is paid for
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");
|
||||
@@ -766,6 +766,7 @@ public class AiController {
|
||||
for (Player p : game.getPlayers()) {
|
||||
p.getController().autoPassCancel();
|
||||
}
|
||||
return combat;
|
||||
}
|
||||
|
||||
private void playLands() {
|
||||
|
||||
@@ -676,21 +676,6 @@ public class ComputerUtil {
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.game.phase.Combat} object.
|
||||
*/
|
||||
public static Combat getBlockers(final Player ai) {
|
||||
final List<Card> blockers = ai.getCardsIn(ZoneType.Battlefield);
|
||||
|
||||
return ComputerUtilBlock.getBlockers(ai, ai.getGame().getCombat(), blockers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* sacrificePermanents.
|
||||
@@ -993,17 +978,18 @@ public class ComputerUtil {
|
||||
boolean ret = true;
|
||||
if (source.getManaCost().countX() > 0) {
|
||||
// If TargetMax is MaxTgts (i.e., an "X" cost), this is fine because AI is limited by mana available.
|
||||
return ret;
|
||||
} else {
|
||||
// Otherwise, if life is possibly in danger, then this is fine.
|
||||
Combat combat = new Combat();
|
||||
combat.initiatePossibleDefenders(ai);
|
||||
Combat combat = new Combat(ai.getOpponent());
|
||||
List<Card> attackers = ai.getOpponent().getCreaturesInPlay();
|
||||
for (Card att : attackers) {
|
||||
if (CombatUtil.canAttackNextTurn(att)) {
|
||||
combat.addAttacker(att, att.getController().getOpponent());
|
||||
}
|
||||
}
|
||||
combat = ComputerUtilBlock.getBlockers(ai, combat, ai.getCreaturesInPlay());
|
||||
AiBlockController aiBlock = new AiBlockController(ai);
|
||||
aiBlock.assignBlockers(combat);
|
||||
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
// Otherwise, return false. Do not play now.
|
||||
ret = false;
|
||||
|
||||
@@ -28,6 +28,7 @@ import forge.card.spellability.SpellAbility;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.player.Player;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
@@ -614,9 +615,10 @@ public class ComputerUtilCard {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean doesCreatureAttackAI(final Player ai, final Card card) {
|
||||
final List<Card> att = new AiAttackController(ai, ai.getOpponent()).getAttackers().getAttackers();
|
||||
|
||||
return att.contains(card);
|
||||
AiAttackController aiAtk = new AiAttackController(ai);
|
||||
Combat combat = new Combat(ai);
|
||||
aiAtk.declareAttackers(combat);
|
||||
return combat.isAttacking(card);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -527,13 +527,13 @@ public class ComputerUtilCombat {
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant) {
|
||||
public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant, Combat combat) {
|
||||
|
||||
if (combatant.isAttacking()) {
|
||||
return ComputerUtilCombat.attackerWouldBeDestroyed(ai, combatant);
|
||||
if (combat.isAttacking(combatant)) {
|
||||
return ComputerUtilCombat.attackerWouldBeDestroyed(ai, combatant, combat);
|
||||
}
|
||||
if (combatant.isBlocking()) {
|
||||
return ComputerUtilCombat.blockerWouldBeDestroyed(ai, combatant);
|
||||
if (combat.isBlocking(combatant)) {
|
||||
return ComputerUtilCombat.blockerWouldBeDestroyed(ai, combatant, combat);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -549,13 +549,11 @@ public class ComputerUtilCombat {
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker) {
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker, Combat combat) {
|
||||
final List<Card> blockers = combat.getBlockers(attacker);
|
||||
|
||||
for (final Card defender : blockers) {
|
||||
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, game.getCombat(), true)
|
||||
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, true)
|
||||
&& !(defender.hasKeyword("Wither") || defender.hasKeyword("Infect"))) {
|
||||
return true;
|
||||
}
|
||||
@@ -600,7 +598,7 @@ public class ComputerUtilCombat {
|
||||
TriggerType mode = trigger.getMode();
|
||||
if (mode == TriggerType.Attacks) {
|
||||
willTrigger = true;
|
||||
if (attacker.isAttacking()) {
|
||||
if (combat.isAttacking(attacker)) {
|
||||
return false; // The trigger should have triggered already
|
||||
}
|
||||
if (trigParams.containsKey("ValidCard")) {
|
||||
@@ -1510,14 +1508,13 @@ public class ComputerUtilCombat {
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean blockerWouldBeDestroyed(Player ai, final Card blocker) {
|
||||
final Game game = ai.getGame();
|
||||
public static boolean blockerWouldBeDestroyed(Player ai, final Card blocker, Combat combat) {
|
||||
// TODO THis function only checks if a single attacker at a time would destroy a blocker
|
||||
// This needs to expand to tally up damage
|
||||
final List<Card> attackers = game.getCombat().getAttackersBlockedBy(blocker);
|
||||
final List<Card> attackers = combat.getAttackersBlockedBy(blocker);
|
||||
|
||||
for (Card attacker : attackers) {
|
||||
if (ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, game.getCombat(), true)
|
||||
if (ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, combat, true)
|
||||
&& !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,40 +11,27 @@ import forge.GameEntity;
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class AttackingBand implements Comparable<AttackingBand> {
|
||||
|
||||
public class AttackingBand {
|
||||
private List<Card> attackers = new ArrayList<Card>();
|
||||
private List<Card> blockers = new ArrayList<Card>();
|
||||
private GameEntity defender = null;
|
||||
private Boolean blocked = null;
|
||||
// private GameEntity defender = null;
|
||||
private boolean blocked = false; // even if all blockers were killed before FS or CD, band remains blocked
|
||||
|
||||
public AttackingBand(List<Card> band, GameEntity def) {
|
||||
attackers.addAll(band);
|
||||
this.defender = def;
|
||||
// this.defender = def;
|
||||
}
|
||||
|
||||
public AttackingBand(Card card, GameEntity def) {
|
||||
attackers.add(card);
|
||||
this.defender = def;
|
||||
// this.defender = def;
|
||||
}
|
||||
|
||||
public List<Card> getAttackers() { return this.attackers; }
|
||||
public List<Card> getBlockers() { return this.blockers; }
|
||||
public GameEntity getDefender() { return this.defender; }
|
||||
// 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 static boolean isValidBand(List<Card> band, boolean shareDamage) {
|
||||
if (band.isEmpty()) {
|
||||
@@ -92,26 +79,29 @@ public class AttackingBand implements Comparable<AttackingBand> {
|
||||
return isValidBand(newBand, false);
|
||||
}
|
||||
|
||||
|
||||
public boolean contains(Card c) {
|
||||
return attackers.contains(c);
|
||||
}
|
||||
|
||||
public boolean isBlocked() { return blocked; }
|
||||
public void setBlocked(boolean value) { blocked = value; }
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
// TODO Auto-generated method stub
|
||||
return attackers.isEmpty();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@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));
|
||||
public String toString() {
|
||||
return String.format("%s %s", attackers.toString(), blocked ? ">||" : ">>>" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@
|
||||
package forge.game.phase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.Card;
|
||||
@@ -32,9 +31,11 @@ import forge.CardPredicates;
|
||||
import forge.GameEntity;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.combat.AttackingBand;
|
||||
import forge.game.event.GameEventBlockerAssigned;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.maps.CollectionSuppliers;
|
||||
import forge.util.maps.HashMapOfLists;
|
||||
import forge.util.maps.MapOfLists;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -45,166 +46,69 @@ import forge.game.zone.ZoneType;
|
||||
* @version $Id$
|
||||
*/
|
||||
public class Combat {
|
||||
// List of AttackingBands
|
||||
private final List<AttackingBand> attackingBands = new ArrayList<AttackingBand>();
|
||||
// Attacker -> AttackingBand (Attackers can only be in a single band)
|
||||
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 Player attackerPlayer;
|
||||
// Defenders, as they are attacked by hostile forces
|
||||
private final MapOfLists<GameEntity, AttackingBand> attackedEntities = new HashMapOfLists<GameEntity, AttackingBand>(CollectionSuppliers.<AttackingBand>arrayLists());
|
||||
// Blockers to stop the hostile invaders
|
||||
private final MapOfLists<AttackingBand, Card> blockedBands = new HashMapOfLists<AttackingBand, Card>(CollectionSuppliers.<Card>arrayLists());
|
||||
|
||||
private final HashMap<Card, Integer> defendingDamageMap = new HashMap<Card, Integer>();
|
||||
|
||||
// Defenders are all Opposing Players + Planeswalker's Controller By Opposing Players
|
||||
private Map<GameEntity, List<Card>> defenderMap = new HashMap<GameEntity, List<Card>>();
|
||||
|
||||
private Map<Card, List<Card>> blockerDamageAssignmentOrder = new TreeMap<Card, List<Card>>();
|
||||
private Map<Card, List<Card>> attackerDamageAssignmentOrder = new TreeMap<Card, List<Card>>();
|
||||
|
||||
private Map<Card, List<Card>> orderBlockerDamageAssignment = new HashMap<Card, List<Card>>();
|
||||
private Map<Card, List<Card>> orderAttackerDamageAssignment = new HashMap<Card, List<Card>>();
|
||||
|
||||
private Player attackingPlayer = null;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* reset.
|
||||
* </p>
|
||||
*/
|
||||
public final void reset(Player playerTurn) {
|
||||
this.resetAttackers();
|
||||
this.defendingDamageMap.clear();
|
||||
this.attackingPlayer = playerTurn;
|
||||
public Combat(Player attacker) {
|
||||
attackerPlayer = attacker;
|
||||
|
||||
this.initiatePossibleDefenders(playerTurn.getOpponents());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* initiatePossibleDefenders.
|
||||
* </p>
|
||||
*
|
||||
* @param defender
|
||||
* a {@link forge.game.player.Player} object.
|
||||
*/
|
||||
public final void initiatePossibleDefenders(final Iterable<Player> defenders) {
|
||||
this.defenderMap.clear();
|
||||
for (Player defender : defenders) {
|
||||
fillDefenderMaps(defender);
|
||||
// Create keys for all possible attack targets
|
||||
for (Player defender : attackerPlayer.getOpponents()) {
|
||||
this.attackedEntities.ensureCollectionFor(defender);
|
||||
List<Card> planeswalkers = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS);
|
||||
for (final Card pw : planeswalkers) {
|
||||
this.attackedEntities.ensureCollectionFor(pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void initiatePossibleDefenders(final Player defender) {
|
||||
this.defenderMap.clear();
|
||||
fillDefenderMaps(defender);
|
||||
}
|
||||
|
||||
public final Player getAttackingPlayer() {
|
||||
return this.attackerPlayer;
|
||||
}
|
||||
|
||||
public final boolean isCombat() {
|
||||
return !attackingBands.isEmpty();
|
||||
}
|
||||
|
||||
private void fillDefenderMaps(final Player defender) {
|
||||
this.defenderMap.put(defender, new ArrayList<Card>());
|
||||
List<Card> planeswalkers =
|
||||
CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS);
|
||||
for (final Card pw : planeswalkers) {
|
||||
this.defenderMap.put(pw, new ArrayList<Card>());
|
||||
for(Collection<AttackingBand> abs : attackedEntities.values()) {
|
||||
if(!abs.isEmpty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>defenders</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link java.util.ArrayList} object.
|
||||
*/
|
||||
public final List<GameEntity> getDefenders() {
|
||||
return new ArrayList<GameEntity>(this.defenderMap.keySet());
|
||||
return Lists.newArrayList(attackedEntities.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>defenders</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param newDef
|
||||
* a {@link java.util.ArrayList} object.
|
||||
*/
|
||||
public final void setDefenders(final List<GameEntity> newDef) {
|
||||
this.defenderMap.clear();
|
||||
for (GameEntity entity : newDef) {
|
||||
this.defenderMap.put(entity, new ArrayList<Card>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDefendingPlaneswalkers.
|
||||
* </p>
|
||||
*
|
||||
* @return an array of {@link forge.Card} objects.
|
||||
*/
|
||||
public final List<Player> getDefendingPlayers() {
|
||||
final List<Player> defending = new ArrayList<Player>();
|
||||
|
||||
for (final GameEntity o : this.defenderMap.keySet()) {
|
||||
for (final GameEntity o : attackedEntities.keySet()) {
|
||||
if (o instanceof Player) {
|
||||
defending.add((Player) o);
|
||||
}
|
||||
}
|
||||
|
||||
return defending;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDefendingPlaneswalkers.
|
||||
* </p>
|
||||
*
|
||||
* @return an array of {@link forge.Card} objects.
|
||||
*/
|
||||
public final List<Card> getDefendingPlaneswalkers() {
|
||||
final List<Card> pwDefending = new ArrayList<Card>();
|
||||
|
||||
for (final GameEntity o : this.defenderMap.keySet()) {
|
||||
for (final GameEntity o : attackedEntities.keySet()) {
|
||||
if (o instanceof Card) {
|
||||
pwDefending.add((Card) o);
|
||||
}
|
||||
}
|
||||
|
||||
return pwDefending;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>attackingPlayer</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param player
|
||||
* a {@link forge.game.player.Player} object.
|
||||
*/
|
||||
public final void setAttackingPlayer(final Player player) {
|
||||
this.attackingPlayer = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>attackingPlayer</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.game.player.Player} object.
|
||||
*/
|
||||
public final Player getAttackingPlayer() {
|
||||
return this.attackingPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addDefendingDamage.
|
||||
* </p>
|
||||
*
|
||||
* @param n
|
||||
* a int.
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
// Damage to whatever was protected there.
|
||||
public final void addDefendingDamage(final int n, final Card source) {
|
||||
final GameEntity ge = this.getDefenderByAttacker(source);
|
||||
|
||||
@@ -222,80 +126,59 @@ public class Combat {
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Card> getAttackersOf(GameEntity defender) {
|
||||
return defenderMap.get(defender);
|
||||
public final List<AttackingBand> getAttackingBandsOf(GameEntity defender) {
|
||||
return Lists.newArrayList(attackedEntities.get(defender));
|
||||
}
|
||||
|
||||
public final List<AttackingBand> getAttackingBandsOf(GameEntity defender) {
|
||||
List<AttackingBand> bands = new ArrayList<AttackingBand>();
|
||||
for(AttackingBand band : this.attackingBands) {
|
||||
if (band.getDefender().equals(defender)) {
|
||||
bands.add(band);
|
||||
}
|
||||
public final List<Card> getAttackersOf(GameEntity defender) {
|
||||
List<Card> result = new ArrayList<Card>();
|
||||
for(AttackingBand v : attackedEntities.get(defender)) {
|
||||
result.addAll(v.getAttackers());
|
||||
}
|
||||
return bands;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isAttacking.
|
||||
* </p>
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isAttacking(final Card c) {
|
||||
return this.attackerToBandMap.containsKey(c);
|
||||
}
|
||||
|
||||
public final void addAttacker(final Card c, GameEntity 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)) {
|
||||
Collection<AttackingBand> attackersOfDefender = attackedEntities.get(defender);
|
||||
if (attackersOfDefender == null) {
|
||||
System.out.println("Trying to add Attacker " + c + " to missing defender " + defender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (band == null || !this.attackingBands.contains(band)) {
|
||||
|
||||
|
||||
if (band == null || !attackersOfDefender.contains(band)) {
|
||||
band = new AttackingBand(c, defender);
|
||||
if (blocked != null) {
|
||||
band.setBlocked(blocked.booleanValue());
|
||||
}
|
||||
this.attackingBands.add(band);
|
||||
attackersOfDefender.add(band);
|
||||
} else {
|
||||
band.addAttacker(c);
|
||||
}
|
||||
// Attacker -> Defender and Defender -> Attacker map to Bands?
|
||||
this.defenderMap.get(defender).add(c);
|
||||
this.attackerToBandMap.put(c, band);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDefenderByAttacker.
|
||||
* </p>
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
* @return a {@link java.lang.Object} object.
|
||||
*/
|
||||
public final GameEntity getDefenderByAttacker(final Card c) {
|
||||
if (!this.attackerToBandMap.containsKey(c)) {
|
||||
return null;
|
||||
for(Entry<GameEntity, Collection<AttackingBand>> e : attackedEntities.entrySet()) {
|
||||
for(AttackingBand ab : e.getValue()) {
|
||||
if ( ab.contains(c) )
|
||||
return e.getKey();
|
||||
}
|
||||
}
|
||||
return this.attackerToBandMap.get(c).getDefender();
|
||||
return null;
|
||||
}
|
||||
|
||||
private final GameEntity getDefenderByAttacker(final AttackingBand c) {
|
||||
for(Entry<GameEntity, Collection<AttackingBand>> e : attackedEntities.entrySet()) {
|
||||
for(AttackingBand ab : e.getValue()) {
|
||||
if ( ab == c )
|
||||
return e.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final Player getDefenderPlayerByAttacker(final Card c) {
|
||||
GameEntity defender = getDefenderByAttacker(c);
|
||||
@@ -312,181 +195,112 @@ public class Combat {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final GameEntity getDefendingEntity(final Card c) {
|
||||
GameEntity defender = this.getDefenderByAttacker(c);
|
||||
if (this.defenderMap.containsKey(defender)) {
|
||||
return defender;
|
||||
public final AttackingBand getBandOfAttacker(final Card c) {
|
||||
for(Collection<AttackingBand> abs : attackedEntities.values()) {
|
||||
for(AttackingBand ab : abs) {
|
||||
if ( ab.contains(c) )
|
||||
return ab;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Attacker " + c + " missing defender " + defender);
|
||||
return null;
|
||||
}
|
||||
|
||||
public final AttackingBand getBandByAttacker(final Card c) {
|
||||
return this.attackerToBandMap.get(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* resetAttackers.
|
||||
* </p>
|
||||
*/
|
||||
public final void resetAttackers() {
|
||||
this.attackingBands.clear();
|
||||
this.blockerToBandsMap.clear();
|
||||
this.attackerToBandMap.clear();
|
||||
this.blockerDamageAssignmentOrder.clear();
|
||||
this.attackerDamageAssignmentOrder.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAttackers.
|
||||
* </p>
|
||||
*
|
||||
* @return an array of {@link forge.Card} objects.
|
||||
*/
|
||||
public final List<AttackingBand> getAttackingBands() {
|
||||
return attackingBands;
|
||||
} // getAttackers()
|
||||
List<AttackingBand> result = Lists.newArrayList();
|
||||
for(Collection<AttackingBand> abs : attackedEntities.values())
|
||||
result.addAll(abs);
|
||||
return result;
|
||||
}
|
||||
|
||||
public final boolean isAttacking(final Card c) {
|
||||
AttackingBand ab = getBandOfAttacker(c);
|
||||
return ab != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param card
|
||||
* @param currentDefender
|
||||
* @return
|
||||
*/
|
||||
public boolean isAttacking(Card card, GameEntity defender) {
|
||||
for(Entry<GameEntity, Collection<AttackingBand>> ee : attackedEntities.entrySet())
|
||||
for(AttackingBand ab : ee.getValue())
|
||||
if (ab.contains(card))
|
||||
return ee.getKey() == defender;
|
||||
return false;
|
||||
}
|
||||
|
||||
public final List<Card> getAttackers() {
|
||||
List<Card> attackers = new ArrayList<Card>();
|
||||
for(AttackingBand band : attackingBands) {
|
||||
attackers.addAll(band.getAttackers());
|
||||
}
|
||||
return attackers;
|
||||
}
|
||||
|
||||
public final boolean isBlocked(final Card attacker) {
|
||||
return this.attackerToBandMap.get(attacker).getBlocked();
|
||||
}
|
||||
|
||||
public final void setBlocked(final Card attacker) {
|
||||
this.attackerToBandMap.get(attacker).setBlocked(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addBlocker.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @param blocker
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
public final void addBlocker(final Card attacker, final Card blocker) {
|
||||
AttackingBand band = this.attackerToBandMap.get(attacker);
|
||||
band.addBlocker(blocker);
|
||||
|
||||
if (!this.blockerToBandsMap.containsKey(blocker)) {
|
||||
this.blockerToBandsMap.put(blocker, Lists.newArrayList(band));
|
||||
} else {
|
||||
this.blockerToBandsMap.get(blocker).add(band);
|
||||
}
|
||||
attacker.getGame().fireEvent(new GameEventBlockerAssigned());
|
||||
}
|
||||
|
||||
public final void removeBlockAssignment(final Card attacker, final Card blocker) {
|
||||
AttackingBand band = this.attackerToBandMap.get(attacker);
|
||||
band.removeBlocker(blocker);
|
||||
this.blockerToBandsMap.get(blocker).remove(attacker);
|
||||
if (this.blockerToBandsMap.get(blocker).isEmpty()) {
|
||||
this.blockerToBandsMap.remove(blocker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* undoBlockingAssignment.
|
||||
* </p>
|
||||
*
|
||||
* @param blocker
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
public final void undoBlockingAssignment(final Card blocker) {
|
||||
final List<AttackingBand> att = this.blockerToBandsMap.get(blocker);
|
||||
for (final AttackingBand band : att) {
|
||||
band.removeBlocker(blocker);
|
||||
}
|
||||
this.blockerToBandsMap.remove(blocker);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAllBlockers.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
public final List<Card> getAllBlockers() {
|
||||
final List<Card> block = new ArrayList<Card>();
|
||||
block.addAll(blockerToBandsMap.keySet());
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
public final List<Card> getBlockers(final AttackingBand band) {
|
||||
List<Card> list = band.getBlockers();
|
||||
if (list == null) {
|
||||
return new ArrayList<Card>();
|
||||
} else {
|
||||
return new ArrayList<Card>(list);
|
||||
}
|
||||
List<Card> result = Lists.newArrayList();
|
||||
for(Collection<AttackingBand> abs : attackedEntities.values())
|
||||
for(AttackingBand ab : abs)
|
||||
result.addAll(ab.getAttackers());
|
||||
return result;
|
||||
}
|
||||
|
||||
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 list pass true, directly.
|
||||
List<Card> list = null;
|
||||
if (ordered) {
|
||||
list = this.attackerDamageAssignmentOrder.containsKey(card) ? this.attackerDamageAssignmentOrder.get(card) : null;
|
||||
} else {
|
||||
list = this.attackerToBandMap.containsKey(card) ? this.getBandByAttacker(card).getBlockers() : null;
|
||||
}
|
||||
AttackingBand band = getBandOfAttacker(card);
|
||||
Collection<Card> blockers = blockedBands.get(band);
|
||||
return blockers == null ? Lists.<Card>newArrayList() : Lists.newArrayList(blockers);
|
||||
}
|
||||
|
||||
if (list == null) {
|
||||
return new ArrayList<Card>();
|
||||
} else {
|
||||
return new ArrayList<Card>(list);
|
||||
public final boolean isBlocked(final Card attacker) {
|
||||
AttackingBand band = getBandOfAttacker(attacker);
|
||||
return band == null ? false : band.isBlocked();
|
||||
|
||||
}
|
||||
|
||||
public final void setBlocked(final Card attacker, boolean value) {
|
||||
getBandOfAttacker(attacker).setBlocked(value); // called by Curtain of Light, Dazzling Beauty, Trap Runner
|
||||
}
|
||||
|
||||
public final void addBlocker(final Card attacker, final Card blocker) {
|
||||
AttackingBand band = getBandOfAttacker(attacker);
|
||||
blockedBands.add(band, blocker);
|
||||
}
|
||||
|
||||
// remove blocked from specific attacker
|
||||
public final void removeBlockAssignment(final Card attacker, final Card blocker) {
|
||||
AttackingBand band = getBandOfAttacker(attacker);
|
||||
Collection<Card> cc = blockedBands.get(band);
|
||||
if( cc != null)
|
||||
cc.remove(blocker);
|
||||
}
|
||||
|
||||
// remove blocker from everywhere
|
||||
public final void undoBlockingAssignment(final Card blocker) {
|
||||
for(Collection<Card> blockers : blockedBands.values()) {
|
||||
blockers.remove(blocker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAttackerBlockedBy.
|
||||
* </p>
|
||||
*
|
||||
* @param blocker
|
||||
* a {@link forge.Card} object.
|
||||
* @return a {@link forge.Card} object.
|
||||
*/
|
||||
public final List<Card> getAllBlockers() {
|
||||
List<Card> result = new ArrayList<Card>();
|
||||
for(Collection<Card> blockers : blockedBands.values()) {
|
||||
if(!result.contains(blockers))
|
||||
result.addAll(blockers);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final List<Card> getBlockers(final AttackingBand band) {
|
||||
Collection<Card> blockers = blockedBands.get(band);
|
||||
return blockers == null ? Lists.<Card>newArrayList() : Lists.newArrayList(blockers);
|
||||
}
|
||||
|
||||
|
||||
public final List<Card> getAttackersBlockedBy(final Card blocker) {
|
||||
List<Card> blocked = new ArrayList<Card>();
|
||||
|
||||
if (blockerToBandsMap.containsKey(blocker)) {
|
||||
for(AttackingBand band : blockerToBandsMap.get(blocker)) {
|
||||
blocked.addAll(band.getAttackers());
|
||||
}
|
||||
for(Entry<AttackingBand, Collection<Card>> s : blockedBands.entrySet()) {
|
||||
if (s.getValue().contains(blocker))
|
||||
blocked.addAll(s.getKey().getAttackers());
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDefendingPlayer.
|
||||
* </p>
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.Card} object.
|
||||
* @return a {@link forge.Player} object.
|
||||
*/
|
||||
public List<Player> getDefendingPlayerRelatedTo(final Card source) {
|
||||
List<Player> players = new ArrayList<Player>();
|
||||
public Player getDefendingPlayerRelatedTo(final Card source) {
|
||||
Card attacker = source;
|
||||
if (source.isAura()) {
|
||||
attacker = source.getEnchantingCard();
|
||||
@@ -497,109 +311,107 @@ public class Combat {
|
||||
}
|
||||
|
||||
// return the corresponding defender
|
||||
Player defender = getDefenderPlayerByAttacker(attacker);
|
||||
if (null != defender) {
|
||||
players.add(defender);
|
||||
return players;
|
||||
}
|
||||
|
||||
// Can't figure out who it's related to... just return all???
|
||||
// return all defending players
|
||||
List<GameEntity> defenders = this.getDefenders();
|
||||
for (GameEntity ge : defenders) {
|
||||
if (ge instanceof Player) {
|
||||
players.add((Player) ge);
|
||||
}
|
||||
}
|
||||
return players;
|
||||
return getDefenderPlayerByAttacker(attacker);
|
||||
}
|
||||
|
||||
public void setAttackerDamageAssignmentOrder(final Card attacker, final List<Card> blockers) {
|
||||
this.attackerDamageAssignmentOrder.put(attacker, blockers);
|
||||
this.orderAttackerDamageAssignment.put(attacker, blockers);
|
||||
}
|
||||
|
||||
public void setBlockerDamageAssignmentOrder(final Card blocker, final List<Card> attackers) {
|
||||
this.blockerDamageAssignmentOrder.put(blocker, attackers);
|
||||
this.orderBlockerDamageAssignment.put(blocker, attackers);
|
||||
}
|
||||
|
||||
public final void removeFromCombat(final Card c) {
|
||||
// is card an attacker?
|
||||
if (this.attackerToBandMap.containsKey(c)) {
|
||||
// Soooo many maps to keep track of
|
||||
AttackingBand band = this.attackerToBandMap.get(c);
|
||||
band.removeAttacker(c);
|
||||
this.attackerToBandMap.remove(c);
|
||||
this.attackerDamageAssignmentOrder.remove(c);
|
||||
|
||||
List<Card> blockers = band.getBlockers();
|
||||
// removes references to this attacker from all indices and orders
|
||||
private void unregisterAttacker(final Card c, AttackingBand ab) {
|
||||
orderAttackerDamageAssignment.remove(c);
|
||||
|
||||
Collection<Card> blockers = blockedBands.get(ab);
|
||||
if ( blockers != null ) {
|
||||
for (Card b : blockers) {
|
||||
if (band.getAttackers().isEmpty()) {
|
||||
this.blockerToBandsMap.get(b).remove(c);
|
||||
}
|
||||
// Clear removed attacker from assignment order
|
||||
if (this.blockerDamageAssignmentOrder.containsKey(b)) {
|
||||
this.blockerDamageAssignmentOrder.get(b).remove(c);
|
||||
// Clear removed attacker from assignment order
|
||||
if (this.orderBlockerDamageAssignment.containsKey(b)) {
|
||||
this.orderBlockerDamageAssignment.get(b).remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.defenderMap.get(band.getDefender()).remove(c);
|
||||
|
||||
if (band.getAttackers().isEmpty() && band.getBlockers().isEmpty()) {
|
||||
this.getAttackingBands().remove(band);
|
||||
// removes references to this defender from all indices and orders
|
||||
private void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) {
|
||||
this.orderBlockerDamageAssignment.remove(c);
|
||||
for(Card atk : bandBeingBlocked.getAttackers()) {
|
||||
if (this.orderAttackerDamageAssignment.containsKey(atk)) {
|
||||
this.orderAttackerDamageAssignment.get(atk).remove(c);
|
||||
}
|
||||
} else if (this.blockerToBandsMap.containsKey(c)) { // card is a blocker
|
||||
List<AttackingBand> attackers = this.blockerToBandsMap.get(c);
|
||||
}
|
||||
}
|
||||
|
||||
this.blockerToBandsMap.remove(c);
|
||||
this.blockerDamageAssignmentOrder.remove(c);
|
||||
for (AttackingBand a : attackers) {
|
||||
a.removeBlocker(c);
|
||||
for(Card atk : a.getAttackers()) {
|
||||
if (this.attackerDamageAssignmentOrder.containsKey(atk)) {
|
||||
this.attackerDamageAssignmentOrder.get(atk).remove(c);
|
||||
}
|
||||
}
|
||||
// remove a combatant whose side is unknown
|
||||
public final void removeFromCombat(final Card c) {
|
||||
AttackingBand ab = getBandOfAttacker(c);
|
||||
if (ab != null) {
|
||||
unregisterAttacker(c, ab);
|
||||
ab.removeAttacker(c);
|
||||
// no need to remove empty bands
|
||||
}
|
||||
|
||||
// if not found in attackers, look for this card in blockers
|
||||
for(Entry<AttackingBand, Collection<Card>> be : blockedBands.entrySet()) {
|
||||
Collection<Card> blockers = be.getValue();
|
||||
if(blockers.contains(c)) {
|
||||
unregisterDefender(c, be.getKey());
|
||||
blockers.remove(c);
|
||||
}
|
||||
}
|
||||
} // removeFromCombat()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* verifyCreaturesInPlay.
|
||||
* </p>
|
||||
*/
|
||||
public final void removeAbsentCombatants() {
|
||||
final List<Card> all = new ArrayList<Card>();
|
||||
for(AttackingBand band : this.getAttackingBands()) {
|
||||
all.addAll(band.getAttackers());
|
||||
}
|
||||
all.addAll(this.getAllBlockers());
|
||||
|
||||
for (int i = 0; i < all.size(); i++) {
|
||||
if (!all.get(i).isInPlay()) {
|
||||
this.removeFromCombat(all.get(i));
|
||||
// iterate all attackers and remove them
|
||||
for(Entry<GameEntity, Collection<AttackingBand>> ee : attackedEntities.entrySet()) {
|
||||
for(AttackingBand ab : ee.getValue()) {
|
||||
List<Card> atk = ab.getAttackers();
|
||||
for(int i = atk.size() - 1; i >= 0; i--) { // might remove items from collection, so no iterators
|
||||
Card c = atk.get(i);
|
||||
if ( !c.isInPlay() ) {
|
||||
unregisterAttacker(c, ab);
|
||||
ab.removeAttacker(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Card> toRemove = Lists.newArrayList();
|
||||
for(Entry<AttackingBand, Collection<Card>> be : blockedBands.entrySet()) {
|
||||
toRemove.clear();
|
||||
for( Card b : be.getValue()) {
|
||||
if ( !b.isInPlay() ) {
|
||||
unregisterDefender(b, be.getKey());
|
||||
toRemove.add(b);
|
||||
}
|
||||
}
|
||||
be.getValue().removeAll(toRemove);
|
||||
}
|
||||
} // verifyCreaturesInPlay()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* setUnblocked.
|
||||
* </p>
|
||||
*/
|
||||
public final void setUnblockedAttackers() {
|
||||
final List<AttackingBand> attacking = this.getAttackingBands();
|
||||
for (final AttackingBand band : attacking) {
|
||||
band.calculateBlockedState();
|
||||
|
||||
if (!band.getBlocked()) {
|
||||
for (Card attacker : band.getAttackers()) {
|
||||
// Run Unblocked Trigger
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attacker", attacker);
|
||||
runParams.put("Defender",this.getDefenderByAttacker(attacker));
|
||||
attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false);
|
||||
}
|
||||
|
||||
// Call this method right after turn-based action of declare blockers has been performed
|
||||
public final void onBlockersDeclared() {
|
||||
for(Collection<AttackingBand> abs : attackedEntities.values()) {
|
||||
for(AttackingBand ab : abs) {
|
||||
Collection<Card> blockers = blockedBands.get(ab);
|
||||
boolean isBlocked = blockers != null && !blockers.isEmpty();
|
||||
if (isBlocked)
|
||||
ab.setBlocked(true);
|
||||
else
|
||||
for (Card attacker : ab.getAttackers()) {
|
||||
// Run Unblocked Trigger
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attacker", attacker);
|
||||
runParams.put("Defender",this.getDefenderByAttacker(attacker));
|
||||
attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -611,7 +423,7 @@ public class Combat {
|
||||
|
||||
for (final Card blocker : blockers) {
|
||||
if (blocker.hasDoubleStrike() || blocker.hasFirstStrike() == firstStrikeDamage) {
|
||||
List<Card> attackers = this.blockerDamageAssignmentOrder.get(blocker);
|
||||
List<Card> attackers = this.orderBlockerDamageAssignment.get(blocker);
|
||||
|
||||
final int damage = blocker.getNetCombatDamage();
|
||||
|
||||
@@ -619,9 +431,8 @@ public class Combat {
|
||||
Player attackingPlayer = this.getAttackingPlayer();
|
||||
Player assigningPlayer = blocker.getController();
|
||||
|
||||
if (AttackingBand.isValidBand(attackers, true)) {
|
||||
if (AttackingBand.isValidBand(attackers, true))
|
||||
assigningPlayer = attackingPlayer;
|
||||
}
|
||||
|
||||
assignedDamage = true;
|
||||
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController());
|
||||
@@ -654,27 +465,25 @@ public class Combat {
|
||||
continue;
|
||||
}
|
||||
|
||||
AttackingBand band = this.getBandByAttacker(attacker);
|
||||
AttackingBand band = this.getBandOfAttacker(attacker);
|
||||
|
||||
boolean trampler = attacker.hasKeyword("Trample");
|
||||
blockers = this.attackerDamageAssignmentOrder.get(attacker);
|
||||
blockers = this.orderAttackerDamageAssignment.get(attacker);
|
||||
assignedDamage = true;
|
||||
// If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender
|
||||
if (blockers == null || blockers.isEmpty()) {
|
||||
if (trampler || !band.getBlocked()) {
|
||||
if (trampler || !band.isBlocked()) {
|
||||
this.addDefendingDamage(damageDealt, attacker);
|
||||
} // No damage happens if blocked but no blockers left
|
||||
} else {
|
||||
GameEntity defender = band.getDefender();
|
||||
GameEntity defender = getDefenderByAttacker(band);
|
||||
Player assigningPlayer = this.getAttackingPlayer();
|
||||
// Defensive Formation is very similar to Banding with Blockers
|
||||
// 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.")) {
|
||||
assigningPlayer = (Player)defender;
|
||||
} else {
|
||||
if (AttackingBand.isValidBand(blockers, true)) {
|
||||
assigningPlayer = blockers.get(0).getController();
|
||||
}
|
||||
} else if ( AttackingBand.isValidBand(blockers, true)){
|
||||
assigningPlayer = blockers.get(0).getController();
|
||||
}
|
||||
|
||||
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, blockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer);
|
||||
@@ -711,7 +520,7 @@ public class Combat {
|
||||
final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>();
|
||||
|
||||
for (final Entry<Card, Integer> entry : defMap.entrySet()) {
|
||||
GameEntity defender = getDefendingEntity(entry.getKey());
|
||||
GameEntity defender = getDefenderByAttacker(entry.getKey());
|
||||
if (defender instanceof Player) { // player
|
||||
if (((Player) defender).addCombatDamage(entry.getValue(), entry.getKey())) {
|
||||
if (wasDamaged.containsKey(defender)) {
|
||||
@@ -787,7 +596,7 @@ public class Combat {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isUnblocked(final Card att) {
|
||||
return !this.attackerToBandMap.get(att).getBlocked();
|
||||
return !isBlocked(att);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,26 +607,42 @@ public class Combat {
|
||||
* @return an array of {@link forge.Card} objects.
|
||||
*/
|
||||
public final List<Card> getUnblockedAttackers() {
|
||||
ArrayList<Card> unblocked = new ArrayList<Card>();
|
||||
for (AttackingBand band : this.attackingBands) {
|
||||
if (!band.getBlocked()) {
|
||||
unblocked.addAll(band.getAttackers());
|
||||
}
|
||||
}
|
||||
List<Card> unblocked = new ArrayList<Card>();
|
||||
for (Collection<AttackingBand> abs : attackedEntities.values())
|
||||
for (AttackingBand ab : abs)
|
||||
if ( ab.isBlocked() )
|
||||
unblocked.addAll(ab.getAttackers());
|
||||
|
||||
return unblocked;
|
||||
}
|
||||
|
||||
public boolean isPlayerAttacked(Player priority) {
|
||||
for(GameEntity defender : defenderMap.keySet()) {
|
||||
if ((defender instanceof Player && priority.equals(defender)) ||
|
||||
(defender instanceof Card && priority.equals(((Card)defender).getController()))) {
|
||||
List<Card> attackers = defenderMap.get(defender);
|
||||
if (attackers != null && !attackers.isEmpty())
|
||||
public boolean isPlayerAttacked(Player who) {
|
||||
for(Entry<GameEntity, Collection<AttackingBand>> ee : attackedEntities.entrySet() ) {
|
||||
GameEntity defender = ee.getKey();
|
||||
Card defenderAsCard = defender instanceof Card ? (Card)defender : null;
|
||||
if ((null != defenderAsCard && defenderAsCard.getController() != who ) ||
|
||||
(null == defenderAsCard && defender != who) )
|
||||
continue; // defender is not related to player 'who'
|
||||
|
||||
for(AttackingBand ab : ee.getValue()) {
|
||||
if ( !ab.isEmpty() )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isBlocking(Card blocker) {
|
||||
for (Collection<Card> blockers : blockedBands.values())
|
||||
if (blockers.contains(blocker))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isBlocking(Card blocker, Card attacker) {
|
||||
AttackingBand ab = getBandOfAttacker(attacker);
|
||||
Collection<Card> blockers = blockedBands.get(ab);
|
||||
return blockers != null && blockers.contains(blocker);
|
||||
}
|
||||
|
||||
} // Class Combat
|
||||
|
||||
@@ -176,7 +176,7 @@ public class CombatUtil {
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canBeBlocked(final Card attacker, final Combat combat) {
|
||||
public static boolean canBeBlocked(final Card attacker, final Combat combat, Player defendingPlayer) {
|
||||
|
||||
if (attacker == null) {
|
||||
return true;
|
||||
@@ -185,7 +185,7 @@ public class CombatUtil {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount GT") && !combat.getBlockers(attacker).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return CombatUtil.canBeBlocked(attacker);
|
||||
return CombatUtil.canBeBlocked(attacker, defendingPlayer);
|
||||
}
|
||||
|
||||
// can the attacker be blocked at all?
|
||||
@@ -198,7 +198,7 @@ public class CombatUtil {
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canBeBlocked(final Card attacker) {
|
||||
public static boolean canBeBlocked(final Card attacker, Player defender) {
|
||||
|
||||
if (attacker == null) {
|
||||
return true;
|
||||
@@ -209,7 +209,7 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
// Landwalk
|
||||
if (isUnblockableFromLandwalk(attacker)) {
|
||||
if (isUnblockableFromLandwalk(attacker, defender)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
|
||||
public static boolean isUnblockableFromLandwalk(final Card attacker) {
|
||||
public static boolean isUnblockableFromLandwalk(final Card attacker, Player defendingPlayer) {
|
||||
//May be blocked as though it doesn't have landwalk. (Staff of the Ages)
|
||||
if (attacker.hasKeyword("May be blocked as though it doesn't have landwalk.")) {
|
||||
return false;
|
||||
@@ -277,10 +277,6 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
String valid = StringUtils.join(walkTypes, ",");
|
||||
Player defendingPlayer = attacker.getController().getOpponent();
|
||||
if (attacker.isAttacking()) {
|
||||
defendingPlayer = defendingPlayer.getGame().getCombat().getDefendingPlayerRelatedTo(attacker).get(0);
|
||||
}
|
||||
List<Card> defendingLands = defendingPlayer.getCardsIn(ZoneType.Battlefield);
|
||||
for (Card c : defendingLands) {
|
||||
if (c.isValid(valid.split(","), defendingPlayer, attacker)) {
|
||||
@@ -319,13 +315,9 @@ public class CombatUtil {
|
||||
* @return true, if successful
|
||||
*/
|
||||
public static boolean canBeBlocked(final Card attacker, final List<Card> blockers) {
|
||||
if (!CombatUtil.canBeBlocked(attacker)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int blocks = 0;
|
||||
for (final Card blocker : blockers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker)) {
|
||||
if (CombatUtil.canBeBlocked(attacker, blocker.getController()) && CombatUtil.canBlock(attacker, blocker)) {
|
||||
blocks++;
|
||||
}
|
||||
}
|
||||
@@ -366,32 +358,33 @@ public class CombatUtil {
|
||||
* a {@link forge.game.phase.Combat} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean finishedMandatoryBlocks(final Combat combat, final Player defending) {
|
||||
public static String validateBlocks(final Combat combat, final Player defending) {
|
||||
|
||||
final List<Card> blockers = defending.getCreaturesInPlay();
|
||||
final List<Card> defendersArmy = defending.getCreaturesInPlay();
|
||||
final List<Card> attackers = combat.getAttackers();
|
||||
final List<Card> blockers = CardLists.filterControlledBy(combat.getAllBlockers(), defending);
|
||||
|
||||
// if a creature does not block but should, return false
|
||||
for (final Card blocker : blockers) {
|
||||
for (final Card blocker : defendersArmy) {
|
||||
// lure effects
|
||||
if (!combat.getAllBlockers().contains(blocker) && CombatUtil.mustBlockAnAttacker(blocker, combat)) {
|
||||
return false;
|
||||
if (!blockers.contains(blocker) && CombatUtil.mustBlockAnAttacker(blocker, combat)) {
|
||||
return String.format("%s must block an attacker, but has not been assigned to block any.", blocker);
|
||||
}
|
||||
|
||||
// "CARDNAME blocks each turn if able."
|
||||
if (!combat.getAllBlockers().contains(blocker) && blocker.hasKeyword("CARDNAME blocks each turn if able.")) {
|
||||
if (!blockers.contains(blocker) && blocker.hasKeyword("CARDNAME blocks each turn if able.")) {
|
||||
for (final Card attacker : attackers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker, combat)) {
|
||||
boolean must = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
final List<Card> possibleBlockers = CardLists.filter(defending.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
final List<Card> possibleBlockers = Lists.newArrayList(defendersArmy);
|
||||
possibleBlockers.remove(blocker);
|
||||
if (!CombatUtil.canBeBlocked(attacker, possibleBlockers)) {
|
||||
must = false;
|
||||
}
|
||||
}
|
||||
if (must) {
|
||||
return false;
|
||||
return String.format("%s must block each turn, but was not assigned to block any attacker now", blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,20 +392,16 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
for (final Card attacker : attackers) {
|
||||
int cntBlockers = combat.getBlockers(attacker).size();
|
||||
// don't accept blocker amount for attackers with keyword defining valid blockers amount
|
||||
if (!canAttackerBeBlockedWithAmount(attacker, combat.getBlockers(attacker).size()))
|
||||
return false;
|
||||
if (cntBlockers > 0 && !canAttackerBeBlockedWithAmount(attacker, cntBlockers))
|
||||
return String.format("%s cannot be blocked with %d creatures you've assigned", attacker, cntBlockers);
|
||||
}
|
||||
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void orderMultipleCombatants(final Combat combat) {
|
||||
CombatUtil.orderMultipleBlockers(combat);
|
||||
CombatUtil.orderBlockingMultipleAttackers(combat);
|
||||
}
|
||||
|
||||
private static void orderMultipleBlockers(final Combat combat) {
|
||||
public static void orderMultipleBlockers(final Combat combat) {
|
||||
// If there are multiple blockers, the Attacker declares the Assignment Order
|
||||
final Player player = combat.getAttackingPlayer();
|
||||
for (final AttackingBand band : combat.getAttackingBands()) {
|
||||
@@ -435,7 +424,7 @@ public class CombatUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void orderBlockingMultipleAttackers(final Combat combat) {
|
||||
public static void orderBlockingMultipleAttackers(final Combat combat) {
|
||||
// If there are multiple blockers, the Attacker declares the Assignment Order
|
||||
for (final Card blocker : combat.getAllBlockers()) {
|
||||
List<Card> attackers = combat.getAttackersBlockedBy(blocker);
|
||||
@@ -485,8 +474,9 @@ public class CombatUtil {
|
||||
}
|
||||
}
|
||||
|
||||
final Player defender = blocker.getController();
|
||||
for (final Card attacker : attackersWithLure) {
|
||||
if (CombatUtil.canBeBlocked(attacker, combat) && CombatUtil.canBlock(attacker, blocker)) {
|
||||
if (CombatUtil.canBeBlocked(attacker, combat, defender) && CombatUtil.canBlock(attacker, blocker)) {
|
||||
boolean canBe = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
final List<Card> blockers = combat.getDefenderPlayerByAttacker(attacker).getCreaturesInPlay();
|
||||
@@ -503,7 +493,7 @@ public class CombatUtil {
|
||||
|
||||
if (blocker.getMustBlockCards() != null) {
|
||||
for (final Card attacker : blocker.getMustBlockCards()) {
|
||||
if (CombatUtil.canBeBlocked(attacker, combat) && CombatUtil.canBlock(attacker, blocker)
|
||||
if (CombatUtil.canBeBlocked(attacker, combat, defender) && CombatUtil.canBlock(attacker, blocker)
|
||||
&& combat.isAttacking(attacker)) {
|
||||
boolean canBe = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
|
||||
@@ -546,7 +536,7 @@ public class CombatUtil {
|
||||
if (!CombatUtil.canBlock(blocker, combat)) {
|
||||
return false;
|
||||
}
|
||||
if (!CombatUtil.canBeBlocked(attacker, combat)) {
|
||||
if (!CombatUtil.canBeBlocked(attacker, combat, blocker.getController())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -600,7 +590,7 @@ public class CombatUtil {
|
||||
if (!CombatUtil.canBlock(blocker, nextTurn)) {
|
||||
return false;
|
||||
}
|
||||
if (!CombatUtil.canBeBlocked(attacker)) {
|
||||
if (!CombatUtil.canBeBlocked(attacker, blocker.getController())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -684,6 +674,18 @@ public class CombatUtil {
|
||||
return true;
|
||||
} // canBlock()
|
||||
|
||||
public static void checkAttackOrBlockAlone(Combat combat) {
|
||||
// Handles removing cards like Mogg Flunkies from combat if group attack
|
||||
// didn't occur
|
||||
for (Card c1 : combat.getAttackers()) {
|
||||
if (c1.hasKeyword("CARDNAME can't attack or block alone.")) {
|
||||
if (combat.getAttackers().size() < 2) {
|
||||
combat.removeFromCombat(c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// can a creature attack given the combat state
|
||||
/**
|
||||
* <p>
|
||||
@@ -762,6 +764,9 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
public static boolean canAttackerBeBlockedWithAmount(Card attacker, int amount) {
|
||||
if( amount == 0 )
|
||||
return false; // no block
|
||||
|
||||
List<String> restrictions = Lists.newArrayList();
|
||||
for ( String kw : attacker.getKeyword() ) {
|
||||
if ( kw.startsWith("CantBeBlockedByAmount") )
|
||||
@@ -918,13 +923,13 @@ public class CombatUtil {
|
||||
* @param bLast
|
||||
* a boolean.
|
||||
*/
|
||||
public static boolean checkPropagandaEffects(final Game game, final Card c) {
|
||||
public static boolean checkPropagandaEffects(final Game game, final Card c, final Combat combat) {
|
||||
Cost attackCost = new Cost(ManaCost.ZERO, true);
|
||||
// Sort abilities to apply them in proper order
|
||||
for (Card card : game.getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
|
||||
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
|
||||
for (final StaticAbility stAb : staticAbilities) {
|
||||
Cost additionalCost = stAb.getAttackCost(c, game.getCombat().getDefenderByAttacker(c));
|
||||
Cost additionalCost = stAb.getAttackCost(c, combat.getDefenderByAttacker(c));
|
||||
if ( null != additionalCost )
|
||||
attackCost.add(additionalCost);
|
||||
}
|
||||
@@ -944,14 +949,14 @@ public class CombatUtil {
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
public static void checkDeclareAttackers(final Game game, final Card c) {
|
||||
public static void checkDeclaredAttacker(final Game game, final Card c, final Combat combat) {
|
||||
// Run triggers
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attacker", c);
|
||||
final List<Card> otherAttackers = game.getCombat().getAttackers();
|
||||
final List<Card> otherAttackers = combat.getAttackers();
|
||||
otherAttackers.remove(c);
|
||||
runParams.put("OtherAttackers", otherAttackers);
|
||||
runParams.put("Attacked", game.getCombat().getDefenderByAttacker(c));
|
||||
runParams.put("Attacked", combat.getDefenderByAttacker(c));
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false);
|
||||
|
||||
// Annihilator:
|
||||
@@ -965,7 +970,7 @@ public class CombatUtil {
|
||||
@Override
|
||||
public void resolve() {
|
||||
this.api = ApiType.Sacrifice;
|
||||
final Player opponent = game.getCombat().getDefendingPlayerRelatedTo(c).get(0);
|
||||
final Player opponent = combat.getDefendingPlayerRelatedTo(c);
|
||||
//List<Card> list = AbilityUtils.filterListByType(opponent.getCardsIn(ZoneType.Battlefield), "Permanent", this);
|
||||
final List<Card> list = opponent.getCardsIn(ZoneType.Battlefield);
|
||||
List<Card> toSac = opponent.getController().choosePermanentsToSacrifice(this, a, a, list, "Card");
|
||||
@@ -1037,7 +1042,7 @@ public class CombatUtil {
|
||||
* @param cl
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
public static void checkDeclareBlockers(Game game, final List<Card> cl) {
|
||||
public static void checkDeclareBlockers(Game game, final List<Card> cl, Combat combat) {
|
||||
for (final Card c : cl) {
|
||||
if (!c.getDamageHistory().getCreatureBlockedThisCombat()) {
|
||||
for (final Ability ab : CardFactoryUtil.getBushidoEffects(c)) {
|
||||
@@ -1046,7 +1051,7 @@ public class CombatUtil {
|
||||
// Run triggers
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Blocker", c);
|
||||
final Card attacker = game.getCombat().getAttackersBlockedBy(c).get(0);
|
||||
final Card attacker = combat.getAttackersBlockedBy(c).get(0);
|
||||
runParams.put("Attacker", attacker);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false);
|
||||
}
|
||||
@@ -1068,7 +1073,6 @@ public class CombatUtil {
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
public static void checkBlockedAttackers(final Game game, final Card a, final List<Card> blockers) {
|
||||
final Combat combat = game.getCombat();
|
||||
if (blockers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1095,7 +1099,7 @@ public class CombatUtil {
|
||||
if (m.find()) {
|
||||
final String[] k = keyword.split(" ");
|
||||
final int magnitude = Integer.valueOf(k[1]);
|
||||
final int numBlockers = combat.getBlockers(a).size();
|
||||
final int numBlockers = blockers.size();
|
||||
if (numBlockers > 1) {
|
||||
CombatUtil.executeRampageAbility(game, a, magnitude, numBlockers);
|
||||
}
|
||||
@@ -1265,16 +1269,4 @@ public class CombatUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkAttackOrBlockAlone(Combat combat) {
|
||||
// Handles removing cards like Mogg Flunkies from combat if group attack
|
||||
// didn't occur
|
||||
for (Card c1 : combat.getAttackers()) {
|
||||
if (c1.hasKeyword("CARDNAME can't attack or block alone.") && c1.isAttacking()) {
|
||||
if (combat.getAttackers().size() < 2) {
|
||||
combat.removeFromCombat(c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end class CombatUtil
|
||||
|
||||
@@ -40,6 +40,7 @@ import forge.game.GameAge;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.event.GameEventAttackersDeclared;
|
||||
import forge.game.event.GameEventBlockerAssigned;
|
||||
import forge.game.event.GameEventBlockersDeclared;
|
||||
import forge.game.event.GameEventPlayerPriority;
|
||||
import forge.game.event.GameEventTurnBegan;
|
||||
@@ -91,7 +92,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
private Player pPlayerPriority = null;
|
||||
private Player pFirstPriority = null;
|
||||
private boolean bCombat = false;
|
||||
private Combat combat = null;
|
||||
private boolean bRepeatCleanup = false;
|
||||
|
||||
private Player playerDeclaresBlockers = null;
|
||||
@@ -184,9 +185,8 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean inCombat() {
|
||||
return this.bCombat;
|
||||
}
|
||||
public final boolean inCombat() { return combat != null; }
|
||||
public final Combat getCombat() { return this.combat; }
|
||||
|
||||
private void advanceToNextPhase() {
|
||||
PhaseType oldPhase = phase;
|
||||
@@ -215,9 +215,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
this.turn++;
|
||||
game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
|
||||
|
||||
// Here's what happens on new turn, regardless of skipped phases
|
||||
game.getCombat().reset(playerTurn);
|
||||
|
||||
// Tokens starting game in play should suffer from Sum. Sickness
|
||||
final List<Card> list = playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield);
|
||||
for (final Card c : list) {
|
||||
@@ -305,43 +302,45 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_ATTACKERS:
|
||||
this.bCombat = true;
|
||||
combat = new Combat(playerTurn);
|
||||
game.getStack().freezeStack();
|
||||
declareAttackersTurnBasedActions();
|
||||
declareAttackersTurnBasedAction();
|
||||
game.getStack().unfreezeStack();
|
||||
|
||||
if (combat != null && combat.getAttackers().isEmpty() )
|
||||
combat = null;
|
||||
|
||||
this.bCombat = !game.getCombat().getAttackers().isEmpty();
|
||||
givePriorityToPlayer = bCombat;
|
||||
givePriorityToPlayer = inCombat();
|
||||
this.nCombatsThisTurn++;
|
||||
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_BLOCKERS:
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
combat.removeAbsentCombatants();
|
||||
game.getStack().freezeStack();
|
||||
declareBlockersTurnBaseActions();
|
||||
declareBlockersTurnBasedAction();
|
||||
game.getStack().unfreezeStack();
|
||||
break;
|
||||
|
||||
case COMBAT_FIRST_STRIKE_DAMAGE:
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
combat.removeAbsentCombatants();
|
||||
|
||||
// no first strikers, skip this step
|
||||
if (!game.getCombat().assignCombatDamage(true)) {
|
||||
if (!combat.assignCombatDamage(true)) {
|
||||
this.givePriorityToPlayer = false;
|
||||
} else {
|
||||
game.getCombat().dealAssignedDamage();
|
||||
combat.dealAssignedDamage();
|
||||
game.getAction().checkStateEffects();
|
||||
}
|
||||
break;
|
||||
|
||||
case COMBAT_DAMAGE:
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
combat.removeAbsentCombatants();
|
||||
|
||||
if (!game.getCombat().assignCombatDamage(false)) {
|
||||
if (!combat.assignCombatDamage(false)) {
|
||||
this.givePriorityToPlayer = false;
|
||||
} else {
|
||||
game.getCombat().dealAssignedDamage();
|
||||
combat.dealAssignedDamage();
|
||||
game.getAction().checkStateEffects();
|
||||
}
|
||||
break;
|
||||
@@ -445,9 +444,8 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
break;
|
||||
|
||||
case COMBAT_END:
|
||||
game.getCombat().reset(playerTurn);
|
||||
combat = null;
|
||||
this.getPlayerTurn().resetAttackedThisCombat();
|
||||
this.bCombat = false;
|
||||
break;
|
||||
|
||||
case CLEANUP:
|
||||
@@ -463,71 +461,75 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
private void declareAttackersTurnBasedActions() {
|
||||
Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers;
|
||||
whoDeclares.getController().declareAttackers(playerTurn);
|
||||
|
||||
if ( game.isGameOver() ) // they just like to close window at any moment
|
||||
return;
|
||||
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
CombatUtil.checkAttackOrBlockAlone(game.getCombat());
|
||||
|
||||
// TODO move propaganda to happen as the Attacker is Declared
|
||||
for (final Card c2 : game.getCombat().getAttackers()) {
|
||||
boolean canAttack = CombatUtil.checkPropagandaEffects(game, c2);
|
||||
if ( canAttack ) {
|
||||
if (!c2.hasKeyword("Vigilance"))
|
||||
c2.tap();
|
||||
} else {
|
||||
game.getCombat().removeFromCombat(c2);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare and fire event 'attackers declared'
|
||||
MapOfLists<GameEntity, Card> attackersMap = new HashMapOfLists<GameEntity, Card>(CollectionSuppliers.<Card>arrayLists());
|
||||
for(GameEntity ge : game.getCombat().getDefenders()) attackersMap.addAll(ge, game.getCombat().getAttackersOf(ge));
|
||||
game.fireEvent(new GameEventAttackersDeclared(playerTurn, attackersMap));
|
||||
|
||||
// This Exalted handler should be converted to script
|
||||
if (game.getCombat().getAttackers().size() == 1) {
|
||||
final Player attackingPlayer = game.getCombat().getAttackingPlayer();
|
||||
final Card attacker = game.getCombat().getAttackers().get(0);
|
||||
for (Card card : attackingPlayer.getCardsIn(ZoneType.Battlefield)) {
|
||||
int exaltedMagnitude = card.getKeywordAmount("Exalted");
|
||||
if (exaltedMagnitude > 0) {
|
||||
CombatUtil.executeExaltedAbility(game, attacker, exaltedMagnitude, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fire trigger
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attackers", game.getCombat().getAttackers());
|
||||
runParams.put("AttackingPlayer", game.getCombat().getAttackingPlayer());
|
||||
game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false);
|
||||
|
||||
for (final Card c : game.getCombat().getAttackers()) {
|
||||
CombatUtil.checkDeclareAttackers(game, c);
|
||||
private Combat declareAttackersTurnBasedAction() {
|
||||
Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers;
|
||||
whoDeclares.getController().declareAttackers(playerTurn, combat);
|
||||
|
||||
if ( game.isGameOver() ) // they just like to close window at any moment
|
||||
return null;
|
||||
|
||||
combat.removeAbsentCombatants();
|
||||
CombatUtil.checkAttackOrBlockAlone(combat);
|
||||
|
||||
// TODO move propaganda to happen as the Attacker is Declared
|
||||
for (final Card c2 : combat.getAttackers()) {
|
||||
boolean canAttack = CombatUtil.checkPropagandaEffects(game, c2, combat);
|
||||
if ( canAttack ) {
|
||||
if (!c2.hasKeyword("Vigilance"))
|
||||
c2.tap();
|
||||
} else {
|
||||
combat.removeFromCombat(c2);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare and fire event 'attackers declared'
|
||||
MapOfLists<GameEntity, Card> attackersMap = new HashMapOfLists<GameEntity, Card>(CollectionSuppliers.<Card>arrayLists());
|
||||
for(GameEntity ge : combat.getDefenders()) attackersMap.addAll(ge, combat.getAttackersOf(ge));
|
||||
game.fireEvent(new GameEventAttackersDeclared(playerTurn, attackersMap));
|
||||
|
||||
private void declareBlockersTurnBaseActions() {
|
||||
final Combat combat = game.getCombat();
|
||||
// This Exalted handler should be converted to script
|
||||
if (combat.getAttackers().size() == 1) {
|
||||
final Player attackingPlayer = combat.getAttackingPlayer();
|
||||
final Card attacker = combat.getAttackers().get(0);
|
||||
for (Card card : attackingPlayer.getCardsIn(ZoneType.Battlefield)) {
|
||||
int exaltedMagnitude = card.getKeywordAmount("Exalted");
|
||||
if (exaltedMagnitude > 0) {
|
||||
CombatUtil.executeExaltedAbility(game, attacker, exaltedMagnitude, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fire trigger
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
runParams.put("Attackers", combat.getAttackers());
|
||||
runParams.put("AttackingPlayer", combat.getAttackingPlayer());
|
||||
game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false);
|
||||
|
||||
for (final Card c : combat.getAttackers()) {
|
||||
CombatUtil.checkDeclaredAttacker(game, c, combat);
|
||||
}
|
||||
return combat;
|
||||
}
|
||||
|
||||
|
||||
private void declareBlockersTurnBasedAction() {
|
||||
Player p = playerTurn;
|
||||
|
||||
do {
|
||||
p = game.getNextPlayerAfter(p);
|
||||
// Apply Odric's effect here
|
||||
Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers;
|
||||
if ( combat.isPlayerAttacked(p) )
|
||||
whoDeclaresBlockers.getController().declareBlockers(p);
|
||||
if ( combat.isPlayerAttacked(p) ) {
|
||||
whoDeclaresBlockers.getController().declareBlockers(p, combat);
|
||||
game.fireEvent(new GameEventBlockerAssigned()); //
|
||||
}
|
||||
|
||||
if ( game.isGameOver() ) // they just like to close window at any moment
|
||||
return;
|
||||
} while(p != playerTurn);
|
||||
|
||||
CombatUtil.orderMultipleBlockers(combat);
|
||||
CombatUtil.orderBlockingMultipleAttackers(combat);
|
||||
|
||||
combat.removeAbsentCombatants();
|
||||
|
||||
|
||||
@@ -545,14 +547,14 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
for (Card c : filterList) {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block alone.") && c.isBlocking()) {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block alone.") && combat.isBlocking(c)) {
|
||||
if (combat.getAllBlockers().size() < 2) {
|
||||
combat.undoBlockingAssignment(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
combat.setUnblockedAttackers();
|
||||
combat.onBlockersDeclared();
|
||||
|
||||
List<Card> list = combat.getAllBlockers();
|
||||
|
||||
@@ -563,7 +565,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
});
|
||||
|
||||
CombatUtil.checkDeclareBlockers(game, list);
|
||||
CombatUtil.checkDeclareBlockers(game, list, combat);
|
||||
|
||||
for (final Card a : combat.getAttackers()) {
|
||||
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
|
||||
@@ -571,10 +573,10 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
|
||||
// map: defender => (many) attacker => (many) blocker
|
||||
Map<GameEntity, MapOfLists<Card, Card>> blockers = new HashMap<GameEntity, MapOfLists<Card,Card>>();
|
||||
for(GameEntity ge : game.getCombat().getDefenders()) {
|
||||
for(GameEntity ge : combat.getDefenders()) {
|
||||
MapOfLists<Card, Card> protectThisDefender = new HashMapOfLists<Card, Card>(CollectionSuppliers.<Card>arrayLists());
|
||||
for(Card att : game.getCombat().getAttackersOf(ge)) {
|
||||
protectThisDefender.addAll(att, game.getCombat().getBlockers(att));
|
||||
for(Card att : combat.getAttackersOf(ge)) {
|
||||
protectThisDefender.addAll(att, combat.getBlockers(att));
|
||||
}
|
||||
blockers.put(ge, protectThisDefender);
|
||||
}
|
||||
@@ -896,6 +898,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
setPlayerTurn(player0);
|
||||
|
||||
game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), ""));
|
||||
combat = null; // not-null can be created only when declare attackers phase begins
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -904,6 +907,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
* @param phaseID the new phase state
|
||||
*/
|
||||
public final void endTurnByEffect() {
|
||||
this.combat = null;
|
||||
this.phase = PhaseType.CLEANUP;
|
||||
this.onPhaseBegin();
|
||||
}
|
||||
@@ -945,6 +949,11 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
public final void setPlayerDeclaresAttackers(Player player) {
|
||||
this.playerDeclaresAttackers = player;
|
||||
}
|
||||
|
||||
|
||||
public void endCombat() {
|
||||
combat = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import forge.card.spellability.TargetChoices;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
@@ -143,8 +144,8 @@ public abstract class PlayerController {
|
||||
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
|
||||
public abstract List<Card> getCardsToMulligan(boolean isCommander, Player firstPlayer);
|
||||
|
||||
public abstract void declareAttackers(Player attacker);
|
||||
public abstract void declareBlockers(Player defender);
|
||||
public abstract void declareAttackers(Player attacker, Combat combat);
|
||||
public abstract void declareBlockers(Player defender, Combat combat);
|
||||
public abstract void takePriority();
|
||||
|
||||
public abstract List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
||||
|
||||
@@ -32,9 +32,10 @@ import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.ai.AiController;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilBlock;
|
||||
import forge.game.ai.AiBlockController;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
@@ -166,12 +167,12 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public List<Card> orderBlockers(Card attacker, List<Card> blockers) {
|
||||
return ComputerUtilBlock.orderBlockers(attacker, blockers);
|
||||
return AiBlockController.orderBlockers(attacker, blockers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Card> orderAttackers(Card blocker, List<Card> attackers) {
|
||||
return ComputerUtilBlock.orderAttackers(blocker, attackers);
|
||||
return AiBlockController.orderAttackers(blocker, attackers);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -322,14 +323,14 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declareAttackers(Player attacker) {
|
||||
brains.declareAttackers(attacker);
|
||||
public void declareAttackers(Player attacker, Combat combat) {
|
||||
brains.declareAttackers(attacker, combat);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void declareBlockers(Player defender) {
|
||||
brains.declateBlockers(defender);
|
||||
public void declareBlockers(Player defender, Combat combat) {
|
||||
brains.declareBlockersFor(defender, combat);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
@@ -45,7 +46,6 @@ import forge.gui.input.InputPassPriority;
|
||||
import forge.gui.input.InputPlayOrDraw;
|
||||
import forge.gui.input.InputSelectCards;
|
||||
import forge.gui.input.InputSelectCardsFromList;
|
||||
import forge.gui.input.InputSynchronized;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
@@ -525,21 +525,19 @@ public class PlayerControllerHuman extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declareAttackers(Player attacker) {
|
||||
game.getCombat().initiatePossibleDefenders(attacker.getOpponents());
|
||||
public void declareAttackers(Player attacker, Combat combat) {
|
||||
// This input should not modify combat object itself, but should return user choice
|
||||
InputSynchronized inpAttack = new InputAttack(attacker, player, game.getCombat());
|
||||
InputAttack inpAttack = new InputAttack(attacker, player, combat);
|
||||
Singletons.getControl().getInputQueue().setInputAndWait(inpAttack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declareBlockers(Player defender) {
|
||||
public void declareBlockers(Player defender, Combat combat) {
|
||||
// This input should not modify combat object itself, but should return user choice
|
||||
InputSynchronized inpBlock = new InputBlock(player, defender, game.getCombat());
|
||||
InputBlock inpBlock = new InputBlock(player, defender, combat);
|
||||
Singletons.getControl().getInputQueue().setInputAndWait(inpBlock);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void takePriority() {
|
||||
PhaseType phase = game.getPhaseHandler().getPhase();
|
||||
|
||||
@@ -151,7 +151,7 @@ public final class GuiDisplayUtil {
|
||||
|
||||
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
|
||||
|
||||
game.getCombat().reset(game.getPhaseHandler().getPlayerTurn());
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
|
||||
devSetupPlayerState(humanLife, humanCardTexts, human);
|
||||
|
||||
@@ -129,10 +129,10 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (card.isAttacking(currentDefender)) {
|
||||
if (combat.isAttacking(card, currentDefender)) {
|
||||
// Activate band by selecting/deselecting a band member
|
||||
if (this.activeBand == null) {
|
||||
this.activateBand(combat.getBandByAttacker(card));
|
||||
this.activateBand(combat.getBandOfAttacker(card));
|
||||
} else if (this.activeBand.getAttackers().contains(card)) {
|
||||
this.activateBand(null);
|
||||
} else { // Join a band by selecting a non-active band member after activating a band
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
*/
|
||||
package forge.gui.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import forge.Card;
|
||||
import forge.Singletons;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.sound.SoundEffectType;
|
||||
import forge.view.ButtonUtil;
|
||||
|
||||
/**
|
||||
@@ -42,7 +41,7 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
private static final long serialVersionUID = 6120743598368928128L;
|
||||
|
||||
private Card currentAttacker = null;
|
||||
private final HashMap<Card, List<Card>> allBlocking = new HashMap<Card, List<Card>>();
|
||||
// some cards may block several creatures at a time. (ex: Two-Headed Dragon, Vanguard's Shield)
|
||||
private final Combat combat;
|
||||
private final Player defender;
|
||||
private final Player declarer;
|
||||
@@ -57,10 +56,6 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
this.combat = combat;
|
||||
}
|
||||
|
||||
private final void removeFromAllBlocking(final Card c) {
|
||||
this.allBlocking.remove(c);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void showMessage() {
|
||||
@@ -90,14 +85,14 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void onOk() {
|
||||
if (CombatUtil.finishedMandatoryBlocks(combat, defender)) {
|
||||
String blockErrors = CombatUtil.validateBlocks(combat, defender);
|
||||
if( null == blockErrors ) {
|
||||
// Done blocking
|
||||
ButtonUtil.reset();
|
||||
CombatUtil.orderMultipleCombatants(combat);
|
||||
currentAttacker = null;
|
||||
allBlocking.clear();
|
||||
|
||||
setCurrentAttacker(null);
|
||||
stop();
|
||||
} else {
|
||||
GuiDialog.message(blockErrors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,43 +100,48 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
@Override
|
||||
public final void onCardSelected(final Card card, boolean isMetaDown) {
|
||||
|
||||
if (isMetaDown) {
|
||||
if (card.getController() == defender ) {
|
||||
combat.removeFromCombat(card);
|
||||
}
|
||||
removeFromAllBlocking(card);
|
||||
if (isMetaDown && card.getController() == defender) {
|
||||
combat.removeFromCombat(card);
|
||||
CMatchUI.SINGLETON_INSTANCE.showCombat();
|
||||
return;
|
||||
}
|
||||
|
||||
// is attacking?
|
||||
boolean reminder = true;
|
||||
boolean isCorrectAction = false;
|
||||
|
||||
if (combat.getAttackers().contains(card)) {
|
||||
this.currentAttacker = card;
|
||||
reminder = false;
|
||||
if (combat.isAttacking(card)) {
|
||||
setCurrentAttacker(card);
|
||||
isCorrectAction = true;
|
||||
} else {
|
||||
// Make sure this card is valid to even be a blocker
|
||||
if (this.currentAttacker != null && card.isCreature() && defender.getZone(ZoneType.Battlefield).contains(card)) {
|
||||
// Create a new blockedBy list if it doesn't exist
|
||||
if (!this.allBlocking.containsKey(card)) {
|
||||
this.allBlocking.put(card, new ArrayList<Card>());
|
||||
}
|
||||
|
||||
List<Card> attackersBlocked = this.allBlocking.get(card);
|
||||
if (!attackersBlocked.contains(this.currentAttacker)
|
||||
&& CombatUtil.canBlock(this.currentAttacker, card, combat)) {
|
||||
attackersBlocked.add(this.currentAttacker);
|
||||
isCorrectAction = CombatUtil.canBlock(this.currentAttacker, card, combat);
|
||||
if ( isCorrectAction ) {
|
||||
combat.addBlocker(this.currentAttacker, card);
|
||||
reminder = false;
|
||||
// This call is performed from GUI and is not intended to propagate to log or net.
|
||||
// No need to use event bus then
|
||||
Singletons.getControl().getSoundSystem().play(SoundEffectType.Block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reminder) {
|
||||
if (!isCorrectAction) {
|
||||
flashIncorrectAction();
|
||||
}
|
||||
|
||||
this.showMessage();
|
||||
} // selectCard()
|
||||
|
||||
|
||||
private void setCurrentAttacker(Card card) {
|
||||
currentAttacker = card;
|
||||
Player attacker = null;
|
||||
for(Card c : combat.getAttackers()) {
|
||||
c.setUsedToPay(card == c);
|
||||
if ( attacker == null )
|
||||
attacker = c.getController();
|
||||
}
|
||||
// request redraw from here
|
||||
attacker.getZone(ZoneType.Battlefield).updateObservers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ public enum CMatchUI {
|
||||
|
||||
|
||||
public void showCombat() {
|
||||
if ( CCombat.SINGLETON_INSTANCE.hasCombatToShow() ) {
|
||||
if (Singletons.getControl().getObservedGame().getPhaseHandler().inCombat()) {
|
||||
SDisplayUtil.showTab(EDocID.REPORT_COMBAT.getDoc());
|
||||
}
|
||||
CCombat.SINGLETON_INSTANCE.update();
|
||||
|
||||
@@ -339,12 +339,12 @@ public enum TargetingOverlay {
|
||||
if (overlaystate == 0) { return; }
|
||||
|
||||
// Arc drawing
|
||||
assembleArcs(combat);
|
||||
if( null != combat )
|
||||
assembleArcs(combat);
|
||||
if (arcs.isEmpty()) { return; }
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color color = FSkin.getColor(FSkin.Colors.CLR_ACTIVE);
|
||||
|
||||
for (Point[] p : arcs) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import forge.game.Game;
|
||||
import forge.game.combat.AttackingBand;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.match.views.VCombat;
|
||||
@@ -42,20 +41,14 @@ public enum CCombat implements ICDoc {
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
public final boolean hasCombatToShow() {
|
||||
PhaseHandler pH = game.getPhaseHandler();
|
||||
PhaseType ph = pH.getPhase();
|
||||
|
||||
return game.getCombat().isCombat() && ph.isAfter(PhaseType.COMBAT_BEGIN) && ph.isBefore(PhaseType.END_OF_TURN);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
if (hasCombatToShow()) // display combat
|
||||
VCombat.SINGLETON_INSTANCE.updateCombat(game.getCombat().getAttackers().size(), getCombatDescription(game.getCombat()));
|
||||
PhaseHandler pH = game.getPhaseHandler();
|
||||
if (pH.inCombat()) // display combat
|
||||
VCombat.SINGLETON_INSTANCE.updateCombat(pH.getCombat().getAttackers().size(), getCombatDescription(pH.getCombat()));
|
||||
else
|
||||
VCombat.SINGLETON_INSTANCE.updateCombat(0, "");
|
||||
}
|
||||
@@ -99,7 +92,7 @@ public enum CCombat implements ICDoc {
|
||||
if (isBand) {
|
||||
// Only print Band data if it's actually a band
|
||||
display.append(" > BAND");
|
||||
if (band.getBlocked()) {
|
||||
if (band.isBlocked()) {
|
||||
display.append(" (blocked)");
|
||||
}
|
||||
display.append("\n");
|
||||
@@ -110,14 +103,14 @@ public enum CCombat implements ICDoc {
|
||||
display.append(combatantToString(c)).append("\n");
|
||||
}
|
||||
|
||||
if (!isBand && band.getBlockers().isEmpty()) {
|
||||
List<Card> blockers = combat.getBlockers(band);
|
||||
if (!isBand && blockers.isEmpty()) {
|
||||
// if single creature is blocked, but no longer has blockers, tell the user!
|
||||
if (band.getBlocked()) {
|
||||
display.append(" (blocked) ");
|
||||
}
|
||||
if (band.isBlocked())
|
||||
display.append(" (blocked)\n");
|
||||
}
|
||||
|
||||
for (final Card element : band.getBlockers()) {
|
||||
for (final Card element : blockers) {
|
||||
display.append(" < ").append(combatantToString(element)).append("\n");
|
||||
}
|
||||
previousBand = isBand;
|
||||
|
||||
@@ -35,6 +35,7 @@ import forge.CardPredicates.Presets;
|
||||
import forge.Command;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.Game;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -184,17 +185,19 @@ public enum CDock implements ICDoc {
|
||||
public void alphaStrike() {
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
|
||||
Player p = findAffectedPlayer();
|
||||
if (ph.is(PhaseType.COMBAT_DECLARE_ATTACKERS, p)) { // ph.is(...) includes null check
|
||||
final Player p = findAffectedPlayer();
|
||||
final Game game = p.getGame();
|
||||
Combat combat = game.getCombat();
|
||||
if (ph.is(PhaseType.COMBAT_DECLARE_ATTACKERS, p) && combat!= null) { // ph.is(...) includes null check
|
||||
List<Player> defenders = p.getOpponents();
|
||||
|
||||
for (Card c : CardLists.filter(p.getCardsIn(ZoneType.Battlefield), Presets.CREATURES)) {
|
||||
if (c.isAttacking())
|
||||
if (combat.isAttacking(c))
|
||||
continue;
|
||||
|
||||
for(Player defender : defenders)
|
||||
if( CombatUtil.canAttack(c, defender, game.getCombat())) {
|
||||
game.getCombat().addAttacker(c, defender);
|
||||
if( CombatUtil.canAttack(c, defender, combat)) {
|
||||
combat.addAttacker(c, defender);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package forge.sound;
|
||||
import forge.Card;
|
||||
import forge.Singletons;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.event.GameEventBlockerAssigned;
|
||||
import forge.game.event.GameEventCardChangeZone;
|
||||
import forge.game.event.GameEventCardDamaged;
|
||||
import forge.game.event.GameEventCardDestroyed;
|
||||
@@ -32,8 +31,6 @@ import forge.game.zone.ZoneType;
|
||||
*/
|
||||
public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> {
|
||||
|
||||
|
||||
public SoundEffectType visit(GameEventBlockerAssigned event) { return SoundEffectType.Block; }
|
||||
public SoundEffectType visit(GameEventCardDamaged event) { return SoundEffectType.Damage; }
|
||||
public SoundEffectType visit(GameEventCardDestroyed event) { return SoundEffectType.Destroy; }
|
||||
public SoundEffectType visit(GameEventCardEquipped event) { return SoundEffectType.Equip; }
|
||||
|
||||
@@ -28,7 +28,7 @@ public class EnumMapOfLists<K extends Enum<K>, V> extends EnumMap<K, Collection<
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
private Collection<V> ensureCollectionFor(K key) {
|
||||
public Collection<V> ensureCollectionFor(K key) {
|
||||
Collection<V> value = get(key);
|
||||
if ( value == null ) {
|
||||
value = factory.get();
|
||||
|
||||
@@ -32,7 +32,7 @@ public class HashMapOfLists<K, V> extends HashMap<K, Collection<V>> implements M
|
||||
|
||||
private static final long serialVersionUID = 3029089910183132930L;
|
||||
|
||||
private Collection<V> ensureCollectionFor(K key) {
|
||||
public Collection<V> ensureCollectionFor(K key) {
|
||||
Collection<V> value = get(key);
|
||||
if ( value == null ) {
|
||||
value = factory.get();
|
||||
|
||||
@@ -6,4 +6,5 @@ import java.util.Map;
|
||||
public interface MapOfLists<K, V> extends Map<K, Collection<V>> {
|
||||
void add(K key, V element);
|
||||
void addAll(K key, Collection<V> element);
|
||||
Collection<V> ensureCollectionFor(K key);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class TreeMapOfLists<K, V> extends TreeMap<K, Collection<V>> implements M
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
private Collection<V> ensureCollectionFor(K key) {
|
||||
public Collection<V> ensureCollectionFor(K key) {
|
||||
Collection<V> value = get(key);
|
||||
if ( value == null ) {
|
||||
value = factory.get();
|
||||
|
||||
@@ -42,6 +42,7 @@ import forge.ImageCache;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.gui.CardContainer;
|
||||
import forge.gui.toolbox.CardFaceSymbols;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
@@ -407,10 +408,12 @@ public class CardPanel extends JPanel implements CardContainer {
|
||||
final int stateXSymbols = (this.cardXOffset + (this.cardWidth / 2)) - 16;
|
||||
final int ySymbols = (this.cardYOffset + this.cardHeight) - (this.cardHeight / 8) - 16;
|
||||
// int yOff = (cardHeight/4) + 2;
|
||||
if (card.isAttacking()) {
|
||||
CardFaceSymbols.drawSymbol("attack", g, combatXSymbols, ySymbols);
|
||||
} else if (card.isBlocking()) {
|
||||
CardFaceSymbols.drawSymbol("defend", g, combatXSymbols, ySymbols);
|
||||
Combat combat = card.getGame().getCombat();
|
||||
if( combat != null ) {
|
||||
if ( combat.isAttacking(card))
|
||||
CardFaceSymbols.drawSymbol("attack", g, combatXSymbols, ySymbols);
|
||||
if ( combat.isBlocking(card))
|
||||
CardFaceSymbols.drawSymbol("defend", g, combatXSymbols, ySymbols);
|
||||
}
|
||||
|
||||
if (card.isSick() && card.isInPlay()) {
|
||||
|
||||
Reference in New Issue
Block a user