mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
GameLog - attack and block are powered by events.
Combat - renamed some methods, extracted aside blocker costs
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -14234,7 +14234,9 @@ src/main/java/forge/game/ai/ComputerUtilCost.java -text
|
|||||||
src/main/java/forge/game/ai/ComputerUtilMana.java -text
|
src/main/java/forge/game/ai/ComputerUtilMana.java -text
|
||||||
src/main/java/forge/game/event/GameEvent.java -text
|
src/main/java/forge/game/event/GameEvent.java -text
|
||||||
src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text
|
src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text
|
||||||
|
src/main/java/forge/game/event/GameEventAttackersDeclared.java -text
|
||||||
src/main/java/forge/game/event/GameEventBlockerAssigned.java -text
|
src/main/java/forge/game/event/GameEventBlockerAssigned.java -text
|
||||||
|
src/main/java/forge/game/event/GameEventBlockersDeclared.java -text
|
||||||
src/main/java/forge/game/event/GameEventCardDamaged.java -text
|
src/main/java/forge/game/event/GameEventCardDamaged.java -text
|
||||||
src/main/java/forge/game/event/GameEventCardDestroyed.java -text
|
src/main/java/forge/game/event/GameEventCardDestroyed.java -text
|
||||||
src/main/java/forge/game/event/GameEventCardDiscarded.java -text
|
src/main/java/forge/game/event/GameEventCardDiscarded.java -text
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import java.util.Observable;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import forge.game.event.IGameEventVisitor;
|
import forge.game.event.IGameEventVisitor;
|
||||||
import forge.game.phase.Combat;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,15 +104,6 @@ public class GameLog extends Observable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCombatAttackers(Combat combat) {
|
|
||||||
this.add(GameLogFormatter.describeAttack(combat));
|
|
||||||
}
|
|
||||||
public void addCombatBlockers(Combat combat) {
|
|
||||||
this.add(GameLogFormatter.describeBlock(combat));
|
|
||||||
}
|
|
||||||
// Special methods
|
|
||||||
|
|
||||||
|
|
||||||
public IGameEventVisitor<?> getEventVisitor() {
|
public IGameEventVisitor<?> getEventVisitor() {
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge;
|
package forge;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ import com.google.common.eventbus.Subscribe;
|
|||||||
|
|
||||||
import forge.card.spellability.TargetChoices;
|
import forge.card.spellability.TargetChoices;
|
||||||
import forge.game.GameOutcome;
|
import forge.game.GameOutcome;
|
||||||
|
import forge.game.event.GameEventAttackersDeclared;
|
||||||
|
import forge.game.event.GameEventBlockersDeclared;
|
||||||
import forge.game.event.GameEventCardDamaged;
|
import forge.game.event.GameEventCardDamaged;
|
||||||
import forge.game.event.GameEventCardDamaged.DamageType;
|
import forge.game.event.GameEventCardDamaged.DamageType;
|
||||||
import forge.game.event.GameEventLandPlayed;
|
import forge.game.event.GameEventLandPlayed;
|
||||||
@@ -21,11 +24,11 @@ import forge.game.event.GameEventGameOutcome;
|
|||||||
import forge.game.event.GameEvent;
|
import forge.game.event.GameEvent;
|
||||||
import forge.game.event.GameEventTurnPhase;
|
import forge.game.event.GameEventTurnPhase;
|
||||||
import forge.game.event.GameEventPlayerControl;
|
import forge.game.event.GameEventPlayerControl;
|
||||||
import forge.game.phase.Combat;
|
|
||||||
import forge.game.player.LobbyPlayer;
|
import forge.game.player.LobbyPlayer;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerStatistics;
|
import forge.game.player.PlayerStatistics;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
|
import forge.util.maps.MapOfLists;
|
||||||
|
|
||||||
public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||||
private final GameLog log;
|
private final GameLog log;
|
||||||
@@ -168,40 +171,42 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
|||||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
static GameLogEntry describeAttack(final Combat combat) {
|
public GameLogEntry visit(final GameEventAttackersDeclared ev) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
// Loop through Defenders
|
// Loop through Defenders
|
||||||
// Append Defending Player/Planeswalker
|
// Append Defending Player/Planeswalker
|
||||||
|
|
||||||
// Not a big fan of the triple nested loop here
|
// Not a big fan of the triple nested loop here
|
||||||
for (GameEntity defender : combat.getDefenders()) {
|
for (Entry<GameEntity, Collection<Card>> kv : ev.attackersMap.entrySet()) {
|
||||||
List<Card> attackers = combat.getAttackersOf(defender);
|
Collection<Card> attackers = kv.getValue();
|
||||||
if (attackers == null || attackers.isEmpty()) {
|
if (attackers == null || attackers.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( sb.length() > 0 ) sb.append("\n");
|
if ( sb.length() > 0 ) sb.append("\n");
|
||||||
|
sb.append(ev.player).append(" assigned ").append(Lang.joinHomogenous(attackers));
|
||||||
sb.append(combat.getAttackingPlayer()).append(" declared ").append(Lang.joinHomogenous(attackers));
|
sb.append(" to attack ").append(kv.getKey().toString()).append(".");
|
||||||
sb.append(" to attack ").append(defender.toString()).append(".");
|
|
||||||
}
|
}
|
||||||
|
if ( sb.length() == 0 )
|
||||||
|
sb.append(ev.player).append(" didn't attack this turn.");
|
||||||
|
|
||||||
return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString());
|
return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static GameLogEntry describeBlock(final Combat combat) {
|
@Override
|
||||||
|
public GameLogEntry visit(final GameEventBlockersDeclared ev) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
// Loop through Defenders
|
// Loop through Defenders
|
||||||
// Append Defending Player/Planeswalker
|
// Append Defending Player/Planeswalker
|
||||||
|
|
||||||
List<Card> blockers = null;
|
Collection<Card> blockers = null;
|
||||||
|
|
||||||
|
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers.entrySet()) {
|
||||||
for (GameEntity defender : combat.getDefenders()) {
|
GameEntity defender = kv.getKey();
|
||||||
List<Card> attackers = combat.getAttackersOf(defender);
|
MapOfLists<Card, Card> attackers = kv.getValue();
|
||||||
if (attackers == null || attackers.isEmpty()) {
|
if (attackers == null || attackers.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -209,17 +214,17 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
|||||||
|
|
||||||
String controllerName = defender instanceof Card ? ((Card)defender).getController().getName() : defender.getName();
|
String controllerName = defender instanceof Card ? ((Card)defender).getController().getName() : defender.getName();
|
||||||
boolean firstAttacker = true;
|
boolean firstAttacker = true;
|
||||||
for (final Card attacker : attackers) {
|
for (final Entry<Card, Collection<Card>> att : attackers.entrySet()) {
|
||||||
if ( !firstAttacker ) sb.append("\n");
|
if ( !firstAttacker ) sb.append("\n");
|
||||||
|
|
||||||
blockers = combat.getBlockers(attacker);
|
blockers = att.getValue();
|
||||||
if ( blockers.isEmpty() ) {
|
if ( blockers.isEmpty() ) {
|
||||||
sb.append(controllerName).append(" didn't block ");
|
sb.append(controllerName).append(" didn't block ");
|
||||||
} else {
|
} else {
|
||||||
sb.append(controllerName).append(" assigned ").append(Lang.joinHomogenous(blockers)).append(" to block ");
|
sb.append(controllerName).append(" assigned ").append(Lang.joinHomogenous(blockers)).append(" to block ");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(attacker).append(".");
|
sb.append(att.getKey()).append(".");
|
||||||
firstAttacker = false;
|
firstAttacker = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package forge.game.event;
|
||||||
|
|
||||||
|
import forge.Card;
|
||||||
|
import forge.GameEntity;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.util.maps.MapOfLists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class GameEventAttackersDeclared extends GameEvent {
|
||||||
|
|
||||||
|
public final Player player;
|
||||||
|
public final MapOfLists<GameEntity, Card> attackersMap;
|
||||||
|
|
||||||
|
public GameEventAttackersDeclared(Player playerTurn, MapOfLists<GameEntity, Card> attackersMap) {
|
||||||
|
this.player = playerTurn;
|
||||||
|
this.attackersMap = attackersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.event.GameEvent#visit(forge.game.event.IGameEventVisitor)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package forge.game.event;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import forge.Card;
|
||||||
|
import forge.GameEntity;
|
||||||
|
import forge.util.maps.MapOfLists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class GameEventBlockersDeclared extends GameEvent {
|
||||||
|
|
||||||
|
public final Map<GameEntity, MapOfLists<Card, Card>> blockers;
|
||||||
|
|
||||||
|
public GameEventBlockersDeclared(Map<GameEntity, MapOfLists<Card, Card>> blockers) {
|
||||||
|
this.blockers = blockers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ package forge.game.event;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface IGameEventVisitor<T> {
|
public interface IGameEventVisitor<T> {
|
||||||
|
T visit(GameEventAttackersDeclared event);
|
||||||
|
T visit(GameEventBlockersDeclared event);
|
||||||
T visit(GameEventBlockerAssigned event);
|
T visit(GameEventBlockerAssigned event);
|
||||||
T visit(GameEventCardDamaged event);
|
T visit(GameEventCardDamaged event);
|
||||||
T visit(GameEventCardDestroyed event);
|
T visit(GameEventCardDestroyed event);
|
||||||
@@ -42,6 +44,8 @@ public interface IGameEventVisitor<T> {
|
|||||||
|
|
||||||
// This is base class for all visitors.
|
// This is base class for all visitors.
|
||||||
public static class Base<T> implements IGameEventVisitor<T>{
|
public static class Base<T> implements IGameEventVisitor<T>{
|
||||||
|
public T visit(GameEventAttackersDeclared event) { return null; }
|
||||||
|
public T visit(GameEventBlockersDeclared event) { return null; }
|
||||||
public T visit(GameEventBlockerAssigned event) { return null; }
|
public T visit(GameEventBlockerAssigned event) { return null; }
|
||||||
public T visit(GameEventCardDamaged event) { return null; }
|
public T visit(GameEventCardDamaged event) { return null; }
|
||||||
public T visit(GameEventCardDestroyed event) { return null; }
|
public T visit(GameEventCardDestroyed event) { return null; }
|
||||||
@@ -76,5 +80,6 @@ public interface IGameEventVisitor<T> {
|
|||||||
public T visit(GameEventTurnPhase event) { return null; }
|
public T visit(GameEventTurnPhase event) { return null; }
|
||||||
public T visit(GameEventPlayerDamaged event) { return null; }
|
public T visit(GameEventPlayerDamaged event) { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ public class Combat {
|
|||||||
* verifyCreaturesInPlay.
|
* verifyCreaturesInPlay.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final void verifyCreaturesInPlay() {
|
public final void removeAbsentCombatants() {
|
||||||
final List<Card> all = new ArrayList<Card>();
|
final List<Card> all = new ArrayList<Card>();
|
||||||
all.addAll(this.getAttackers());
|
all.addAll(this.getAttackers());
|
||||||
all.addAll(this.getAllBlockers());
|
all.addAll(this.getAllBlockers());
|
||||||
@@ -582,7 +582,7 @@ public class Combat {
|
|||||||
* setUnblocked.
|
* setUnblocked.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final void setUnblocked() {
|
public final void setUnblockedAttackers() {
|
||||||
final List<Card> attacking = this.getAttackers();
|
final List<Card> attacking = this.getAttackers();
|
||||||
|
|
||||||
for (final Card attacker : attacking) {
|
for (final Card attacker : attacking) {
|
||||||
|
|||||||
@@ -1405,4 +1405,16 @@ 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
|
} // end class CombatUtil
|
||||||
|
|||||||
@@ -26,12 +26,15 @@ import org.apache.commons.lang.time.StopWatch;
|
|||||||
import forge.Card;
|
import forge.Card;
|
||||||
import forge.CardLists;
|
import forge.CardLists;
|
||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
|
import forge.GameEntity;
|
||||||
import forge.Singletons;
|
import forge.Singletons;
|
||||||
import forge.CardPredicates.Presets;
|
import forge.CardPredicates.Presets;
|
||||||
import forge.card.trigger.TriggerType;
|
import forge.card.trigger.TriggerType;
|
||||||
import forge.game.GameAge;
|
import forge.game.GameAge;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameType;
|
import forge.game.GameType;
|
||||||
|
import forge.game.event.GameEventAttackersDeclared;
|
||||||
|
import forge.game.event.GameEventBlockersDeclared;
|
||||||
import forge.game.event.GameEventPlayerPriority;
|
import forge.game.event.GameEventPlayerPriority;
|
||||||
import forge.game.event.GameEventTurnBegan;
|
import forge.game.event.GameEventTurnBegan;
|
||||||
import forge.game.event.GameEventTurnEnded;
|
import forge.game.event.GameEventTurnEnded;
|
||||||
@@ -44,6 +47,9 @@ import forge.gui.framework.SDisplayUtil;
|
|||||||
import forge.gui.match.CMatchUI;
|
import forge.gui.match.CMatchUI;
|
||||||
import forge.gui.match.nonsingleton.VField;
|
import forge.gui.match.nonsingleton.VField;
|
||||||
import forge.properties.ForgePreferences.FPref;
|
import forge.properties.ForgePreferences.FPref;
|
||||||
|
import forge.util.maps.CollectionSuppliers;
|
||||||
|
import forge.util.maps.HashMapOfLists;
|
||||||
|
import forge.util.maps.MapOfLists;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,21 +276,10 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
|
|
||||||
playerTurn.getController().declareAttackers();
|
playerTurn.getController().declareAttackers();
|
||||||
|
|
||||||
game.getCombat().verifyCreaturesInPlay();
|
game.getCombat().removeAbsentCombatants();
|
||||||
|
CombatUtil.checkAttackOrBlockAlone(game.getCombat());
|
||||||
// Handles removing cards like Mogg Flunkies from combat if group attack
|
|
||||||
// didn't occur
|
|
||||||
for (Card c1 : game.getCombat().getAttackers()) {
|
|
||||||
if (c1.hasKeyword("CARDNAME can't attack or block alone.") && c1.isAttacking()) {
|
|
||||||
if (game.getCombat().getAttackers().size() < 2) {
|
|
||||||
game.getCombat().removeFromCombat(c1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO move propaganda to happen as the Attacker is Declared
|
// TODO move propaganda to happen as the Attacker is Declared
|
||||||
|
|
||||||
for (final Card c2 : game.getCombat().getAttackers()) {
|
for (final Card c2 : game.getCombat().getAttackers()) {
|
||||||
boolean canAttack = CombatUtil.checkPropagandaEffects(game, c2);
|
boolean canAttack = CombatUtil.checkPropagandaEffects(game, c2);
|
||||||
if ( canAttack ) {
|
if ( canAttack ) {
|
||||||
@@ -295,9 +290,12 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
// Then run other Attacker bonuses
|
// This Exalted handler should be converted to script
|
||||||
// check for exalted:
|
|
||||||
if (game.getCombat().getAttackers().size() == 1) {
|
if (game.getCombat().getAttackers().size() == 1) {
|
||||||
final Player attackingPlayer = game.getCombat().getAttackingPlayer();
|
final Player attackingPlayer = game.getCombat().getAttackingPlayer();
|
||||||
final Card attacker = game.getCombat().getAttackers().get(0);
|
final Card attacker = game.getCombat().getAttackers().get(0);
|
||||||
@@ -305,7 +303,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
int exaltedMagnitude = card.getKeywordAmount("Exalted");
|
int exaltedMagnitude = card.getKeywordAmount("Exalted");
|
||||||
if (exaltedMagnitude > 0) {
|
if (exaltedMagnitude > 0) {
|
||||||
CombatUtil.executeExaltedAbility(game, attacker, exaltedMagnitude, card);
|
CombatUtil.executeExaltedAbility(game, attacker, exaltedMagnitude, card);
|
||||||
// Make sure exalted effects get applied only once per combat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Sovereigns of Lost Alara".equals(card.getName())) {
|
if ("Sovereigns of Lost Alara".equals(card.getName())) {
|
||||||
@@ -314,8 +311,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getGameLog().addCombatAttackers(game.getCombat());
|
// fire trigger
|
||||||
|
|
||||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||||
runParams.put("Attackers", game.getCombat().getAttackers());
|
runParams.put("Attackers", game.getCombat().getAttackers());
|
||||||
runParams.put("AttackingPlayer", game.getCombat().getAttackingPlayer());
|
runParams.put("AttackingPlayer", game.getCombat().getAttackingPlayer());
|
||||||
@@ -332,7 +328,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBAT_DECLARE_BLOCKERS:
|
case COMBAT_DECLARE_BLOCKERS:
|
||||||
game.getCombat().verifyCreaturesInPlay();
|
game.getCombat().removeAbsentCombatants();
|
||||||
game.getStack().freezeStack();
|
game.getStack().freezeStack();
|
||||||
|
|
||||||
Player p = playerTurn;
|
Player p = playerTurn;
|
||||||
@@ -342,12 +338,25 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
p.getController().declareBlockers();
|
p.getController().declareBlockers();
|
||||||
} while(p != playerTurn);
|
} while(p != playerTurn);
|
||||||
|
|
||||||
game.getStack().unfreezeStack();
|
game.getCombat().removeAbsentCombatants();
|
||||||
PhaseUtil.handleDeclareBlockers(game);
|
PhaseUtil.handleDeclareBlockers(game);
|
||||||
|
|
||||||
|
// map: defender => (many) attacker => (many) blocker
|
||||||
|
Map<GameEntity, MapOfLists<Card, Card>> blockers = new HashMap<GameEntity, MapOfLists<Card,Card>>();
|
||||||
|
for(GameEntity ge : game.getCombat().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));
|
||||||
|
}
|
||||||
|
blockers.put(ge, protectThisDefender);
|
||||||
|
}
|
||||||
|
game.fireEvent(new GameEventBlockersDeclared(blockers));
|
||||||
|
|
||||||
|
game.getStack().unfreezeStack();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBAT_FIRST_STRIKE_DAMAGE:
|
case COMBAT_FIRST_STRIKE_DAMAGE:
|
||||||
game.getCombat().verifyCreaturesInPlay();
|
game.getCombat().removeAbsentCombatants();
|
||||||
|
|
||||||
// no first strikers, skip this step
|
// no first strikers, skip this step
|
||||||
if (!game.getCombat().assignCombatDamage(true)) {
|
if (!game.getCombat().assignCombatDamage(true)) {
|
||||||
@@ -359,7 +368,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBAT_DAMAGE:
|
case COMBAT_DAMAGE:
|
||||||
game.getCombat().verifyCreaturesInPlay();
|
game.getCombat().removeAbsentCombatants();
|
||||||
|
|
||||||
if (!game.getCombat().assignCombatDamage(false)) {
|
if (!game.getCombat().assignCombatDamage(false)) {
|
||||||
this.givePriorityToPlayer = false;
|
this.givePriorityToPlayer = false;
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ public enum PhaseType {
|
|||||||
MAIN1("Main, precombat", "Main1"),
|
MAIN1("Main, precombat", "Main1"),
|
||||||
COMBAT_BEGIN("Begin Combat", "BeginCombat"),
|
COMBAT_BEGIN("Begin Combat", "BeginCombat"),
|
||||||
COMBAT_DECLARE_ATTACKERS("Declare Attackers"),
|
COMBAT_DECLARE_ATTACKERS("Declare Attackers"),
|
||||||
//COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY("Declare Attackers - Play Instants and Abilities"),
|
|
||||||
COMBAT_DECLARE_BLOCKERS("Declare Blockers"),
|
COMBAT_DECLARE_BLOCKERS("Declare Blockers"),
|
||||||
//COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY("Declare Blockers - Play Instants and Abilities"),
|
|
||||||
COMBAT_FIRST_STRIKE_DAMAGE("First Strike Damage"),
|
COMBAT_FIRST_STRIKE_DAMAGE("First Strike Damage"),
|
||||||
COMBAT_DAMAGE("Combat Damage"),
|
COMBAT_DAMAGE("Combat Damage"),
|
||||||
COMBAT_END("End Combat", "EndCombat"),
|
COMBAT_END("End Combat", "EndCombat"),
|
||||||
@@ -27,11 +25,11 @@ public enum PhaseType {
|
|||||||
|
|
||||||
public static final List<PhaseType> ALL_PHASES = Collections.unmodifiableList(
|
public static final List<PhaseType> ALL_PHASES = Collections.unmodifiableList(
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
UNTAP, UPKEEP, DRAW, MAIN1,
|
UNTAP, UPKEEP, DRAW,
|
||||||
COMBAT_BEGIN, COMBAT_DECLARE_ATTACKERS, //COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY,
|
MAIN1,
|
||||||
COMBAT_DECLARE_BLOCKERS, // COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY,
|
COMBAT_BEGIN, COMBAT_DECLARE_ATTACKERS, COMBAT_DECLARE_BLOCKERS, COMBAT_FIRST_STRIKE_DAMAGE, COMBAT_DAMAGE, COMBAT_END,
|
||||||
COMBAT_FIRST_STRIKE_DAMAGE, COMBAT_DAMAGE, COMBAT_END,
|
MAIN2,
|
||||||
MAIN2, END_OF_TURN, CLEANUP
|
END_OF_TURN, CLEANUP
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package forge.game.phase;
|
package forge.game.phase;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
@@ -28,7 +27,6 @@ import forge.CardLists;
|
|||||||
import forge.card.cost.Cost;
|
import forge.card.cost.Cost;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.staticability.StaticAbility;
|
import forge.card.staticability.StaticAbility;
|
||||||
import forge.card.trigger.TriggerType;
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController.ManaPaymentPurpose;
|
import forge.game.player.PlayerController.ManaPaymentPurpose;
|
||||||
@@ -77,7 +75,6 @@ public class PhaseUtil {
|
|||||||
*/
|
*/
|
||||||
public static void handleDeclareBlockers(Game game) {
|
public static void handleDeclareBlockers(Game game) {
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
combat.verifyCreaturesInPlay();
|
|
||||||
|
|
||||||
// Handles removing cards like Mogg Flunkies from combat if group block
|
// Handles removing cards like Mogg Flunkies from combat if group block
|
||||||
// didn't occur
|
// didn't occur
|
||||||
@@ -85,21 +82,7 @@ public class PhaseUtil {
|
|||||||
for (Card blocker : filterList) {
|
for (Card blocker : filterList) {
|
||||||
final List<Card> attackers = new ArrayList<Card>(combat.getAttackersBlockedBy(blocker));
|
final List<Card> attackers = new ArrayList<Card>(combat.getAttackersBlockedBy(blocker));
|
||||||
for (Card attacker : attackers) {
|
for (Card attacker : attackers) {
|
||||||
Cost blockCost = new Cost(ManaCost.ZERO, true);
|
boolean hasPaid = payRequiredBlockCosts(game, blocker, attacker);
|
||||||
// Sort abilities to apply them in proper order
|
|
||||||
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
|
||||||
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
|
|
||||||
for (final StaticAbility stAb : staticAbilities) {
|
|
||||||
Cost c1 = stAb.getBlockCost(blocker, attacker);
|
|
||||||
if ( c1 != null )
|
|
||||||
blockCost.add(c1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasPaid = blockCost.getTotalMana().isZero() && blockCost.isOnlyManaCost(); // true if needless to pay
|
|
||||||
if (!hasPaid) {
|
|
||||||
hasPaid = blocker.getController().getController().payManaOptional(blocker, blockCost, "Pay cost to declare " + blocker + " a blocker", ManaPaymentPurpose.DeclareBlocker);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !hasPaid ) {
|
if ( !hasPaid ) {
|
||||||
combat.removeBlockAssignment(attacker, blocker);
|
combat.removeBlockAssignment(attacker, blocker);
|
||||||
@@ -114,12 +97,9 @@ public class PhaseUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getStack().freezeStack();
|
combat.setUnblockedAttackers();
|
||||||
|
|
||||||
combat.setUnblocked();
|
List<Card> list = combat.getAllBlockers();
|
||||||
|
|
||||||
List<Card> list = new ArrayList<Card>();
|
|
||||||
list.addAll(combat.getAllBlockers());
|
|
||||||
|
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -128,16 +108,30 @@ public class PhaseUtil {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<Card> attList = combat.getAttackers();
|
|
||||||
|
|
||||||
CombatUtil.checkDeclareBlockers(game, list);
|
CombatUtil.checkDeclareBlockers(game, list);
|
||||||
|
|
||||||
for (final Card a : attList) {
|
for (final Card a : combat.getAttackers()) {
|
||||||
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
|
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
game.getStack().unfreezeStack();
|
|
||||||
|
|
||||||
game.getGameLog().addCombatBlockers(game.getCombat());
|
private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
|
||||||
|
Cost blockCost = new Cost(ManaCost.ZERO, true);
|
||||||
|
// Sort abilities to apply them in proper order
|
||||||
|
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
||||||
|
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
|
||||||
|
for (final StaticAbility stAb : staticAbilities) {
|
||||||
|
Cost c1 = stAb.getBlockCost(blocker, attacker);
|
||||||
|
if ( c1 != null )
|
||||||
|
blockCost.add(c1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasPaid = blockCost.getTotalMana().isZero() && blockCost.isOnlyManaCost(); // true if needless to pay
|
||||||
|
if (!hasPaid) {
|
||||||
|
hasPaid = blocker.getController().getController().payManaOptional(blocker, blockCost, "Pay cost to declare " + blocker + " a blocker", ManaPaymentPurpose.DeclareBlocker);
|
||||||
|
}
|
||||||
|
return hasPaid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.util;
|
package forge.util;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
@@ -27,8 +28,8 @@ public class Lang {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String joinHomogenous(List<T> objects) { return joinHomogenous(objects, null); }
|
public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null); }
|
||||||
public static <T> String joinHomogenous(List<T> objects, Function<T, String> accessor) {
|
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
|
||||||
int remaining = objects.size();
|
int remaining = objects.size();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(T obj : objects) {
|
for(T obj : objects) {
|
||||||
|
|||||||
Reference in New Issue
Block a user