- Lots of Combat simplification. Including maps of Attackers Block By a Blocker, and Attackers attacking a Defender for simpler access.

- Adding support for Block Any Creatures and Block Additional Creatures
This commit is contained in:
Sol
2012-09-10 00:39:36 +00:00
parent 24f257517b
commit bab2bfa8af
12 changed files with 251 additions and 101 deletions

2
.gitattributes vendored
View File

@@ -3431,6 +3431,7 @@ res/cardsfolder/f/forget.txt -text
res/cardsfolder/f/forgotten_ancient.txt svneol=native#text/plain res/cardsfolder/f/forgotten_ancient.txt svneol=native#text/plain
res/cardsfolder/f/forgotten_cave.txt svneol=native#text/plain res/cardsfolder/f/forgotten_cave.txt svneol=native#text/plain
res/cardsfolder/f/forgotten_harvest.txt svneol=native#text/plain res/cardsfolder/f/forgotten_harvest.txt svneol=native#text/plain
res/cardsfolder/f/foriysian_brigade.txt -text
res/cardsfolder/f/fork.txt svneol=native#text/plain res/cardsfolder/f/fork.txt svneol=native#text/plain
res/cardsfolder/f/forked_bolt.txt svneol=native#text/plain res/cardsfolder/f/forked_bolt.txt svneol=native#text/plain
res/cardsfolder/f/forked_branch_garami.txt svneol=native#text/plain res/cardsfolder/f/forked_branch_garami.txt svneol=native#text/plain
@@ -10612,6 +10613,7 @@ res/cardsfolder/w/wall_of_essence.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_faith.txt svneol=native#text/plain res/cardsfolder/w/wall_of_faith.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_fire.txt svneol=native#text/plain res/cardsfolder/w/wall_of_fire.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_frost.txt svneol=native#text/plain res/cardsfolder/w/wall_of_frost.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_glare.txt -text
res/cardsfolder/w/wall_of_granite.txt svneol=native#text/plain res/cardsfolder/w/wall_of_granite.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_heat.txt svneol=native#text/plain res/cardsfolder/w/wall_of_heat.txt svneol=native#text/plain
res/cardsfolder/w/wall_of_hope.txt svneol=native#text/plain res/cardsfolder/w/wall_of_hope.txt svneol=native#text/plain

View File

@@ -0,0 +1,11 @@
Name:Foriysian Brigade
ManaCost:3 W
Types:Creature Human Soldier
Text:no text
PT:2/4
K:CARDNAME can block an additional creature.
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/foriysian_brigade.jpg
SetInfo:WTH|Uncommon|http://magiccards.info/scans/en/wl/130.jpg
Oracle:Foriysian Brigade can block an additional creature.
End

View File

@@ -0,0 +1,12 @@
Name:Wall of Glare
ManaCost:1 W
Types:Creature Wall
Text:no text
PT:0/5
K:Defender
K:CARDNAME can block any number of creatures.
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/wall_of_glare.jpg
SetInfo:UDS|Common|http://magiccards.info/scans/en/ud/25.jpg
Oracle:Defender (This creature can't attack.)\nWall of Glare can block any number of creatures.
End

View File

@@ -7526,7 +7526,7 @@ public class Card extends GameEntity implements Comparable<Card> {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isBlocking(final Card attacker) { public final boolean isBlocking(final Card attacker) {
return attacker.equals(AllZone.getCombat().getAttackerBlockedBy(this)); return AllZone.getCombat().getAttackersBlockedBy(this).contains(attacker);
} }
/** /**
@@ -7539,7 +7539,7 @@ public class Card extends GameEntity implements Comparable<Card> {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isBlockedBy(final Card blocker) { public final boolean isBlockedBy(final Card blocker) {
return this.equals(AllZone.getCombat().getAttackerBlockedBy(blocker)); return AllZone.getCombat().getAttackersBlockedBy(blocker).contains(this);
} }
// ///////////////////////// // /////////////////////////

View File

@@ -595,10 +595,16 @@ public class AbilityFactoryPump {
// is the creature blocking and unable to destroy the attacker // is the creature blocking and unable to destroy the attacker
// or would be destroyed itself? // or would be destroyed itself?
if (c.isBlocking() if (c.isBlocking()) {
&& (CombatUtil.blockerWouldBeDestroyed(c) || !CombatUtil.attackerWouldBeDestroyed(AllZone if (CombatUtil.blockerWouldBeDestroyed(c)) {
.getCombat().getAttackerBlockedBy(c)))) { return true;
return true; }
CardList blockedBy = AllZone.getCombat().getAttackersBlockedBy(c);
// For now, Only care the first creature blocked by a card.
// TODO Add in better BlockAdditional support
if (blockedBy.size() != 0 && !CombatUtil.attackerWouldBeDestroyed(blockedBy.get(0))) {
return true;
}
} }
// is the creature unblocked and the spell will pump its power? // is the creature unblocked and the spell will pump its power?
@@ -617,11 +623,17 @@ public class AbilityFactoryPump {
} }
// if the life of the computer is in danger, try to pump blockers blocking Tramplers // if the life of the computer is in danger, try to pump blockers blocking Tramplers
CardList blockedBy = AllZone.getCombat().getAttackersBlockedBy(c);
boolean attackerHasTrample = false;
for(Card b : blockedBy) {
attackerHasTrample |= b.hasKeyword("Trample");
}
if (phase.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) if (phase.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)
&& phase.isPlayerTurn(AllZone.getHumanPlayer()) && phase.isPlayerTurn(AllZone.getHumanPlayer())
&& c.isBlocking() && c.isBlocking()
&& defense > 0 && defense > 0
&& AllZone.getCombat().getAttackerBlockedBy(c).hasKeyword("Trample") && attackerHasTrample
&& (sa.isAbility() || CombatUtil.lifeInDanger(AllZone.getCombat()))) { && (sa.isAbility() || CombatUtil.lifeInDanger(AllZone.getCombat()))) {
return true; return true;
} }

View File

@@ -17,10 +17,11 @@
*/ */
package forge.control.input; package forge.control.input;
import java.util.ArrayList; import java.util.HashMap;
import forge.AllZone; import forge.AllZone;
import forge.Card; import forge.Card;
import forge.CardList;
import forge.Singletons; import forge.Singletons;
import forge.game.phase.CombatUtil; import forge.game.phase.CombatUtil;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
@@ -43,7 +44,7 @@ public class InputBlock extends Input {
private static final long serialVersionUID = 6120743598368928128L; private static final long serialVersionUID = 6120743598368928128L;
private Card currentAttacker = null; private Card currentAttacker = null;
private final ArrayList<Card> allBlocking = new ArrayList<Card>(); private final HashMap<Card,CardList> allBlocking = new HashMap<Card, CardList>();
/** /**
* <p> * <p>
@@ -88,7 +89,7 @@ public class InputBlock extends Input {
// Done blocking // Done blocking
ButtonUtil.reset(); ButtonUtil.reset();
CombatUtil.orderMultipleBlockers(AllZone.getCombat()); CombatUtil.orderMultipleCombatants(AllZone.getCombat());
Singletons.getModel().getGameState().getPhaseHandler().setNeedToNextPhase(true); Singletons.getModel().getGameState().getPhaseHandler().setNeedToNextPhase(true);
} }
@@ -98,17 +99,35 @@ public class InputBlock extends Input {
@Override @Override
public final void selectCard(final Card card, final PlayerZone zone) { public final void selectCard(final Card card, final PlayerZone zone) {
// is attacking? // is attacking?
boolean reminder = true;
if (AllZone.getCombat().getAttackers().contains(card)) { if (AllZone.getCombat().getAttackers().contains(card)) {
this.currentAttacker = card; this.currentAttacker = card;
} else if (zone.is(ZoneType.Battlefield, AllZone.getHumanPlayer()) && card.isCreature() reminder = false;
&& CombatUtil.canBlock(this.currentAttacker, card, AllZone.getCombat())
&& this.currentAttacker != null && !this.allBlocking.contains(card)
&& card.getController().isHuman()) {
this.allBlocking.add(card);
AllZone.getCombat().addBlocker(this.currentAttacker, card);
} else { } else {
// Make sure this card is valid to even be a blocker
if (this.currentAttacker != null && card.isCreature() && card.getController().isHuman()
&& zone.is(ZoneType.Battlefield, AllZone.getHumanPlayer())) {
// Create a new blockedBy list if it doesn't exist
if (!this.allBlocking.containsKey(card)) {
this.allBlocking.put(card, new CardList());
}
CardList attackersBlocked = this.allBlocking.get(card);
if (!attackersBlocked.contains(this.currentAttacker) &&
CombatUtil.canBlock(this.currentAttacker, card, AllZone.getCombat()) &&
CombatUtil.canBlockMoreCreatures(card, attackersBlocked)) {
attackersBlocked.add(this.currentAttacker);
AllZone.getCombat().addBlocker(this.currentAttacker, card);
reminder = false;
}
}
}
if (reminder) {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
} }
this.showMessage(); this.showMessage();
} // selectCard() } // selectCard()
} }

View File

@@ -49,21 +49,21 @@ import forge.gui.match.CMatchUI;
public class Combat { public class Combat {
// key is attacker Card // key is attacker Card
// value is CardList of blockers // value is CardList of blockers
private final Map<Card, CardList> map = new TreeMap<Card, CardList>(); private final Map<Card, CardList> attackerMap = new TreeMap<Card, CardList>();
private final Map<Card, CardList> blockerMap = new TreeMap<Card, CardList>();
private final Set<Card> blocked = new HashSet<Card>(); private final Set<Card> blocked = new HashSet<Card>();
private final HashMap<Card, CardList> unblockedMap = new HashMap<Card, CardList>(); private final HashMap<Card, CardList> unblockedMap = new HashMap<Card, CardList>();
private final HashMap<Card, Integer> defendingDamageMap = new HashMap<Card, Integer>(); private final HashMap<Card, Integer> defendingDamageMap = new HashMap<Card, Integer>();
// Defenders are the Defending Player + Each Planeswalker that player // Defenders are the Defending Player + Each controlled Planeswalker
// controls
private List<GameEntity> defenders = new ArrayList<GameEntity>(); private List<GameEntity> defenders = new ArrayList<GameEntity>();
private Map<GameEntity, CardList> defenderMap = new TreeMap<GameEntity, CardList>();
private int currentDefender = 0; private int currentDefender = 0;
private int nextDefender = 0; private int nextDefender = 0;
// This Hash keeps track of // This Hash keeps track of
private final HashMap<Card, Integer> attackerToDefender = new HashMap<Card, Integer>(); private final HashMap<Card, GameEntity> attackerToDefender = new HashMap<Card, GameEntity>();
private Player attackingPlayer = null; private Player attackingPlayer = null;
private Player defendingPlayer = null; private Player defendingPlayer = null;
@@ -107,11 +107,14 @@ public class Combat {
*/ */
public final void initiatePossibleDefenders(final Player defender) { public final void initiatePossibleDefenders(final Player defender) {
this.defenders.clear(); this.defenders.clear();
this.defenders.add(defender); this.defenderMap.clear();
this.defenders.add((GameEntity)defender);
this.defenderMap.put((GameEntity)defender, new CardList());
CardList planeswalkers = defender.getCardsIn(ZoneType.Battlefield); CardList planeswalkers = defender.getCardsIn(ZoneType.Battlefield);
planeswalkers = planeswalkers.getType("Planeswalker"); planeswalkers = planeswalkers.getType("Planeswalker");
for (final Card pw : planeswalkers) { for (final Card pw : planeswalkers) {
this.defenders.add(pw); this.defenders.add((GameEntity)pw);
this.defenderMap.put((GameEntity)pw, new CardList());
} }
} }
@@ -199,6 +202,9 @@ public class Combat {
*/ */
public final void setDefenders(final List<GameEntity> newDef) { public final void setDefenders(final List<GameEntity> newDef) {
this.defenders = newDef; this.defenders = newDef;
for(GameEntity entity : this.defenders) {
this.defenderMap.put(entity, new CardList());
}
} }
/** /**
@@ -296,8 +302,7 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void addDefendingDamage(final int n, final Card source) { public final void addDefendingDamage(final int n, final Card source) {
final int slot = this.getDefenderByAttacker(source); final GameEntity ge = this.getDefenderByAttacker(source);
final GameEntity ge = this.defenders.get(slot);
if (ge instanceof Card) { if (ge instanceof Card) {
final Card pw = (Card) ge; final Card pw = (Card) ge;
@@ -321,18 +326,19 @@ public class Combat {
* @return an array of {@link forge.CardList} objects. * @return an array of {@link forge.CardList} objects.
*/ */
public final CardList[] sortAttackerByDefender() { public final CardList[] sortAttackerByDefender() {
final CardList[] attackers = new CardList[this.defenders.size()]; int size = this.defenders.size();
for (int i = 0; i < attackers.length; i++) { final CardList[] attackers = new CardList[size];
attackers[i] = new CardList(); for (int i = 0; i < size; i++) {
} attackers[i] = getAttackersByDefenderSlot(i);
for (final Card atk : this.attackerToDefender.keySet()) {
final int i = this.attackerToDefender.get(atk);
attackers[i].add(atk);
} }
return attackers; return attackers;
} }
public final CardList getAttackersByDefenderSlot(int slot) {
GameEntity entity = this.defenders.get(slot);
return this.defenderMap.get(entity);
}
/** /**
* <p> * <p>
@@ -344,7 +350,7 @@ public class Combat {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isAttacking(final Card c) { public final boolean isAttacking(final Card c) {
return this.map.get(c) != null; return this.attackerMap.get(c) != null;
} }
/** /**
@@ -356,8 +362,7 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void addAttacker(final Card c) { public final void addAttacker(final Card c) {
this.map.put(c, new CardList()); this.addAttacker(c, defenders.get(this.currentDefender));
this.attackerToDefender.put(c, this.currentDefender);
} }
/** /**
@@ -371,13 +376,14 @@ public class Combat {
* a GameEntity object. * a GameEntity object.
*/ */
public final void addAttacker(final Card c, GameEntity defender) { public final void addAttacker(final Card c, GameEntity defender) {
int n = defenders.indexOf(defender); if (!defenders.contains(defender)) {
if (-1 == n) {
System.out.println("Trying to add Attacker " + c + " to missing defender " + defender); System.out.println("Trying to add Attacker " + c + " to missing defender " + defender);
} else { return;
this.map.put(c, new CardList());
this.attackerToDefender.put(c, n);
} }
this.attackerMap.put(c, new CardList());
this.attackerToDefender.put(c, defender);
this.defenderMap.get(defender).add(c);
} }
/** /**
@@ -389,12 +395,20 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a {@link java.lang.Object} object. * @return a {@link java.lang.Object} object.
*/ */
public final Integer getDefenderByAttacker(final Card c) { public final GameEntity getDefenderByAttacker(final Card c) {
return this.attackerToDefender.get(c); return this.attackerToDefender.get(c);
} }
public final GameEntity getDefendingEntity(final Card c) { public final GameEntity getDefendingEntity(final Card c) {
return this.getDefenders().get(this.attackerToDefender.get(c)); GameEntity defender = this.attackerToDefender.get(c);
if (this.defenders.contains(defender)) {
return defender;
}
System.out.println("Attacker " + c + " missing defender " + defender);
return null;
} }
/** /**
@@ -403,8 +417,9 @@ public class Combat {
* </p> * </p>
*/ */
public final void resetAttackers() { public final void resetAttackers() {
this.map.clear(); this.attackerMap.clear();
this.attackerToDefender.clear(); this.attackerToDefender.clear();
this.blockerMap.clear();
} }
/** /**
@@ -415,7 +430,7 @@ public class Combat {
* @return an array of {@link forge.Card} objects. * @return an array of {@link forge.Card} objects.
*/ */
public final List<Card> getAttackers() { public final List<Card> getAttackers() {
return new ArrayList<Card>(this.map.keySet()); return new ArrayList<Card>(this.attackerMap.keySet());
} // getAttackers() } // getAttackers()
/** /**
@@ -426,7 +441,7 @@ public class Combat {
* @return an array of {@link forge.Card} objects. * @return an array of {@link forge.Card} objects.
*/ */
public final CardList getAttackerList() { public final CardList getAttackerList() {
return new CardList(this.map.keySet()); return new CardList(this.attackerMap.keySet());
} // getAttackers() } // getAttackers()
/** /**
@@ -454,7 +469,13 @@ public class Combat {
*/ */
public final void addBlocker(final Card attacker, final Card blocker) { public final void addBlocker(final Card attacker, final Card blocker) {
this.blocked.add(attacker); this.blocked.add(attacker);
this.getBlockerList(attacker).add(blocker); this.attackerMap.get(attacker).add(blocker);
if (!this.blockerMap.containsKey(blocker)) {
this.blockerMap.put(blocker, new CardList(attacker));
}
else {
this.blockerMap.get(blocker).add(attacker);
}
} }
/** /**
@@ -465,12 +486,8 @@ public class Combat {
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
public final CardList getAllBlockers() { public final CardList getAllBlockers() {
final CardList att = this.getAttackerList();
final CardList block = new CardList(); final CardList block = new CardList();
block.addAll(blockerMap.keySet());
for (int i = 0; i < att.size(); i++) {
block.addAll(this.getBlockers(att.get(i)));
}
return block; return block;
} // getAllBlockers() } // getAllBlockers()
@@ -485,10 +502,10 @@ public class Combat {
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
public final CardList getBlockers(final Card attacker) { public final CardList getBlockers(final Card attacker) {
if (this.getBlockerList(attacker) == null) { if (this.getBlockingAttackerList(attacker) == null) {
return new CardList(); return new CardList();
} else { } else {
return new CardList(this.getBlockerList(attacker)); return new CardList(this.getBlockingAttackerList(attacker));
} }
} }
@@ -501,17 +518,11 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a {@link forge.Card} object. * @return a {@link forge.Card} object.
*/ */
public final Card getAttackerBlockedBy(final Card blocker) { public final CardList getAttackersBlockedBy(final Card blocker) {
// TODO(sol) Return CardList instead of a card if (blockerMap.containsKey(blocker)) {
final CardList att = this.getAttackerList(); return blockerMap.get(blocker);
}
for (int i = 0; i < att.size(); i++) { return new CardList();
if (this.getBlockers(att.get(i)).contains(blocker)) {
return att.get(i);
}
} // for
return null;
} }
/** /**
@@ -523,8 +534,8 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
private CardList getBlockerList(final Card attacker) { private CardList getBlockingAttackerList(final Card attacker) {
return this.map.get(attacker); return this.attackerMap.get(attacker);
} }
/** /**
@@ -537,7 +548,11 @@ public class Combat {
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
public void setBlockerList(final Card attacker, final CardList blockers) { public void setBlockerList(final Card attacker, final CardList blockers) {
this.map.put(attacker, blockers); this.attackerMap.put(attacker, blockers);
}
public void setAttackersBlockedByList(final Card blocker, final CardList attackers) {
this.blockerMap.put(blocker, attackers);
} }
/** /**
@@ -549,21 +564,24 @@ public class Combat {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void removeFromCombat(final Card c) { public final void removeFromCombat(final Card c) {
// todo(sol) add some more solid error checking in here
// is card an attacker? // is card an attacker?
final CardList att = this.getAttackerList(); if (this.attackerMap.containsKey(c)) {
if (att.contains(c)) { CardList blockers = this.attackerMap.get(c);
this.map.remove(c); this.attackerMap.remove(c);
for(Card b : blockers) {
this.blockerMap.get(b).remove(c);
}
this.attackerToDefender.remove(c); this.attackerToDefender.remove(c);
} else { // card is a blocker } else if (this.blockerMap.containsKey(c)){ // card is a blocker
for (final Card a : att) { CardList attackers = this.blockerMap.get(c);
if (this.getBlockers(a).contains(c)) {
this.getBlockerList(a).remove(c); boolean stillDeclaring = Singletons.getModel().getGameState().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS);
// TODO if Declare Blockers and Declare Blockers (Abilities) this.blockerMap.remove(c);
// merge this logic needs to be tweaked for(Card a : attackers) {
if ((this.getBlockers(a).size() == 0) this.attackerMap.get(a).remove(c);
&& Singletons.getModel().getGameState().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) { if (stillDeclaring && this.attackerMap.get(a).size() == 0) {
this.blocked.remove(a); this.blocked.remove(a);
}
} }
} }
} }
@@ -581,7 +599,7 @@ public class Combat {
final CardList att = this.getAttackerList(); final CardList att = this.getAttackerList();
for (final Card attacker : att) { for (final Card attacker : att) {
if (this.getBlockers(attacker).contains(blocker)) { if (this.getBlockers(attacker).contains(blocker)) {
this.getBlockerList(attacker).remove(blocker); this.getBlockingAttackerList(attacker).remove(blocker);
if (this.getBlockers(attacker).size() == 0) { if (this.getBlockers(attacker).size() == 0) {
this.blocked.remove(attacker); this.blocked.remove(attacker);
} }
@@ -636,10 +654,7 @@ public class Combat {
for (final Card blocker : blockers) { for (final Card blocker : blockers) {
if (blocker.hasDoubleStrike() || blocker.hasFirstStrike() == firstStrikeDamage) { if (blocker.hasDoubleStrike() || blocker.hasFirstStrike() == firstStrikeDamage) {
// TODO Switch comment lines when Blockers can block more than one creature CardList attackers = this.getAttackersBlockedBy(blocker);
final Card attacker = this.getAttackerBlockedBy(blocker);
CardList attackers = new CardList(attacker);
//CardList attackers = this.getAttackersBlockedBy(b);
final int damage = blocker.getNetCombatDamage(); final int damage = blocker.getNetCombatDamage();
@@ -647,7 +662,7 @@ public class Combat {
// Just in case it was removed or something // Just in case it was removed or something
} else { } else {
assignedDamage = true; assignedDamage = true;
if (this.getAttackingPlayer().isHuman()) { // human attacks if (this.getAttackingPlayer().isComputer()) { // ai attacks
if (attackers.size() > 1) { if (attackers.size() > 1) {
CMatchUI.SINGLETON_INSTANCE.assignDamage(blocker, attackers, damage); CMatchUI.SINGLETON_INSTANCE.assignDamage(blocker, attackers, damage);
} else { } else {

View File

@@ -157,6 +157,17 @@ public class CombatUtil {
return true; return true;
} }
public static boolean canBlockMoreCreatures(final Card blocker, final CardList blockedBy) {
// TODO(sol) expand this for the additional blocking keyword
int size = blockedBy.size();
if (size == 0 || blocker.hasKeyword("CARDNAME can block any number of creatures.")) {
return true;
}
return blocker.getKeywordAmount("CARDNAME can block an additional creature.") >= size;
}
// can the attacker be blocked at all? // can the attacker be blocked at all?
/** /**
@@ -387,7 +398,12 @@ public class CombatUtil {
return true; return true;
} }
public static void orderMultipleBlockers(final Combat combat) { public static void orderMultipleCombatants(final Combat combat) {
CombatUtil.orderMultipleBlockers(combat);
CombatUtil.orderBlockingMultipleAttackers(combat);
}
private static void orderMultipleBlockers(final Combat combat) {
// If there are multiple blockers, the Attacker declares the Assignment Order // If there are multiple blockers, the Attacker declares the Assignment Order
final Player player = combat.getAttackingPlayer(); final Player player = combat.getAttackingPlayer();
final CardList attackers = combat.getAttackerList(); final CardList attackers = combat.getAttackerList();
@@ -415,6 +431,34 @@ public class CombatUtil {
// Refresh Combat Panel // Refresh Combat Panel
} }
private static void orderBlockingMultipleAttackers(final Combat combat) {
// If there are multiple blockers, the Attacker declares the Assignment Order
final Player player = combat.getDefendingPlayer();
final CardList blockers = combat.getAllBlockers();
for (final Card blocker : blockers) {
CardList attackers = combat.getAttackersBlockedBy(blocker);
if (attackers.size() <= 1) {
continue;
}
CardList orderedAttacker = null;
if (player.isHuman()) {
List<Object> ordered = GuiUtils.getOrderChoices("Choose Blocking Order", "Damaged First", true, (Object[])attackers.toArray());
orderedAttacker = new CardList();
for(Object o : ordered) {
orderedAttacker.add((Card)o);
}
}
else {
orderedAttacker = ComputerUtilBlock.orderAttackers(blocker, attackers);
}
combat.setAttackersBlockedByList(blocker, orderedAttacker);
}
CombatUtil.showCombat();
// Refresh Combat Panel
}
// can the blocker block an attacker with a lure effect? // can the blocker block an attacker with a lure effect?
/** /**
@@ -1040,7 +1084,7 @@ public class CombatUtil {
int damage = 0; int damage = 0;
final CardList attackers = combat.sortAttackerByDefender()[0]; final CardList attackers = combat.getAttackersByDefenderSlot(0);
final CardList unblocked = new CardList(); final CardList unblocked = new CardList();
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -1082,7 +1126,7 @@ public class CombatUtil {
int poison = 0; int poison = 0;
final CardList attackers = combat.sortAttackerByDefender()[0]; final CardList attackers = combat.getAttackersByDefenderSlot(0);
final CardList unblocked = new CardList(); final CardList unblocked = new CardList();
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -1127,7 +1171,7 @@ public class CombatUtil {
} }
// check for creatures that must be blocked // check for creatures that must be blocked
final CardList attackers = combat.sortAttackerByDefender()[0]; final CardList attackers = combat.getAttackersByDefenderSlot(0);
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -1181,7 +1225,7 @@ public class CombatUtil {
} }
// check for creatures that must be blocked // check for creatures that must be blocked
final CardList attackers = combat.sortAttackerByDefender()[0]; final CardList attackers = combat.getAttackersByDefenderSlot(0);
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -2215,11 +2259,15 @@ public class CombatUtil {
* @return a boolean. * @return a boolean.
*/ */
public static boolean blockerWouldBeDestroyed(final Card blocker) { public static boolean blockerWouldBeDestroyed(final Card blocker) {
final Card attacker = AllZone.getCombat().getAttackerBlockedBy(blocker); // 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 CardList attackers = AllZone.getCombat().getAttackersBlockedBy(blocker);
if (CombatUtil.canDestroyBlocker(blocker, attacker, AllZone.getCombat(), true) for(Card attacker : attackers) {
&& !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) { if (CombatUtil.canDestroyBlocker(blocker, attacker, AllZone.getCombat(), true)
return true; && !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) {
return true;
}
} }
return false; return false;
} }
@@ -2436,7 +2484,13 @@ public class CombatUtil {
* </p> * </p>
*/ */
public static void showCombat() { public static void showCombat() {
// TODO(sol) ShowCombat seems to be resetting itself when switching away and switching back?
final StringBuilder display = new StringBuilder(); final StringBuilder display = new StringBuilder();
if (!Singletons.getModel().getGameState().getPhaseHandler().inCombat()) {
VCombat.SINGLETON_INSTANCE.updateCombat(display.toString().trim());
return;
}
// Loop through Defenders // Loop through Defenders
// Append Defending Player/Planeswalker // Append Defending Player/Planeswalker

View File

@@ -281,7 +281,7 @@ public class ComputerAIGeneral implements Computer {
AllZone.setCombat(ComputerUtilBlock.getBlockers(AllZone.getCombat(), blockers)); AllZone.setCombat(ComputerUtilBlock.getBlockers(AllZone.getCombat(), blockers));
CombatUtil.orderMultipleBlockers(AllZone.getCombat()); CombatUtil.orderMultipleCombatants(AllZone.getCombat());
Singletons.getModel().getGameState().getPhaseHandler().setNeedToNextPhase(true); Singletons.getModel().getGameState().getPhaseHandler().setNeedToNextPhase(true);
} }

View File

@@ -781,7 +781,7 @@ public class ComputerUtilAttack {
final int blockNum = this.blockers.size(); final int blockNum = this.blockers.size();
int attackNum = 0; int attackNum = 0;
int damage = 0; int damage = 0;
CardList attacking = combat.sortAttackerByDefender()[combat.getCurrentDefenderNumber()]; CardList attacking = combat.getAttackersByDefenderSlot(combat.getCurrentDefenderNumber());
CardListUtil.sortAttackLowFirst(attacking); CardListUtil.sortAttackLowFirst(attacking);
for (Card atta : attacking) { for (Card atta : attacking) {
if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers)) { if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers)) {

View File

@@ -928,4 +928,17 @@ public class ComputerUtilBlock {
return blockers; return blockers;
} }
public static CardList orderAttackers(Card attacker, CardList blockers) {
// This shouldn't really take trample into account, but otherwise should be pretty similar to orderBlockers
// very very simple ordering of attackers, sort by evaluate, then sort by attack
//final int damage = attacker.getNetCombatDamage();
CardListUtil.sortByEvaluateCreature(blockers);
CardListUtil.sortAttack(blockers);
// TODO: Take total damage, and attempt to maximize killing the greatest evaluation of creatures
// It's probably generally better to kill the largest creature, but sometimes its better to kill a few smaller ones
return blockers;
}
} }

View File

@@ -61,8 +61,7 @@ import forge.util.MyRandom;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public abstract class Player extends GameEntity { public abstract class Player extends GameEntity implements Comparable<Player> {
/** The poison counters. */ /** The poison counters. */
private int poisonCounters; private int poisonCounters;
@@ -2681,6 +2680,19 @@ public abstract class Player extends GameEntity {
} }
} }
@Override
public int compareTo(Player o) {
if (o == null) {
return +1;
}
int subtractedHash = o.hashCode() - this.hashCode();
if (subtractedHash == 0) {
return 0;
}
return Math.abs(subtractedHash)/subtractedHash;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public int hashCode() { public int hashCode() {