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/event/GameEvent.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/GameEventBlockersDeclared.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/GameEventCardDiscarded.java -text
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Observable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import forge.game.event.IGameEventVisitor;
|
||||
import forge.game.phase.Combat;
|
||||
|
||||
|
||||
/**
|
||||
@@ -105,15 +104,6 @@ public class GameLog extends Observable {
|
||||
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() {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -8,6 +9,8 @@ import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import forge.card.spellability.TargetChoices;
|
||||
import forge.game.GameOutcome;
|
||||
import forge.game.event.GameEventAttackersDeclared;
|
||||
import forge.game.event.GameEventBlockersDeclared;
|
||||
import forge.game.event.GameEventCardDamaged;
|
||||
import forge.game.event.GameEventCardDamaged.DamageType;
|
||||
import forge.game.event.GameEventLandPlayed;
|
||||
@@ -21,11 +24,11 @@ import forge.game.event.GameEventGameOutcome;
|
||||
import forge.game.event.GameEvent;
|
||||
import forge.game.event.GameEventTurnPhase;
|
||||
import forge.game.event.GameEventPlayerControl;
|
||||
import forge.game.phase.Combat;
|
||||
import forge.game.player.LobbyPlayer;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerStatistics;
|
||||
import forge.util.Lang;
|
||||
import forge.util.maps.MapOfLists;
|
||||
|
||||
public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
private final GameLog log;
|
||||
@@ -168,40 +171,42 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
|
||||
static GameLogEntry describeAttack(final Combat combat) {
|
||||
@Override
|
||||
public GameLogEntry visit(final GameEventAttackersDeclared ev) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Loop through Defenders
|
||||
// Append Defending Player/Planeswalker
|
||||
|
||||
// Not a big fan of the triple nested loop here
|
||||
for (GameEntity defender : combat.getDefenders()) {
|
||||
List<Card> attackers = combat.getAttackersOf(defender);
|
||||
for (Entry<GameEntity, Collection<Card>> kv : ev.attackersMap.entrySet()) {
|
||||
Collection<Card> attackers = kv.getValue();
|
||||
if (attackers == null || attackers.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if ( sb.length() > 0 ) sb.append("\n");
|
||||
|
||||
sb.append(combat.getAttackingPlayer()).append(" declared ").append(Lang.joinHomogenous(attackers));
|
||||
sb.append(" to attack ").append(defender.toString()).append(".");
|
||||
sb.append(ev.player).append(" assigned ").append(Lang.joinHomogenous(attackers));
|
||||
sb.append(" to attack ").append(kv.getKey().toString()).append(".");
|
||||
}
|
||||
if ( sb.length() == 0 )
|
||||
sb.append(ev.player).append(" didn't attack this turn.");
|
||||
|
||||
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();
|
||||
|
||||
// Loop through Defenders
|
||||
// Append Defending Player/Planeswalker
|
||||
|
||||
List<Card> blockers = null;
|
||||
Collection<Card> blockers = null;
|
||||
|
||||
|
||||
for (GameEntity defender : combat.getDefenders()) {
|
||||
List<Card> attackers = combat.getAttackersOf(defender);
|
||||
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers.entrySet()) {
|
||||
GameEntity defender = kv.getKey();
|
||||
MapOfLists<Card, Card> attackers = kv.getValue();
|
||||
if (attackers == null || attackers.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -209,17 +214,17 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
String controllerName = defender instanceof Card ? ((Card)defender).getController().getName() : defender.getName();
|
||||
boolean firstAttacker = true;
|
||||
for (final Card attacker : attackers) {
|
||||
for (final Entry<Card, Collection<Card>> att : attackers.entrySet()) {
|
||||
if ( !firstAttacker ) sb.append("\n");
|
||||
|
||||
blockers = combat.getBlockers(attacker);
|
||||
blockers = att.getValue();
|
||||
if ( blockers.isEmpty() ) {
|
||||
sb.append(controllerName).append(" didn't block ");
|
||||
} else {
|
||||
sb.append(controllerName).append(" assigned ").append(Lang.joinHomogenous(blockers)).append(" to block ");
|
||||
}
|
||||
|
||||
sb.append(attacker).append(".");
|
||||
sb.append(att.getKey()).append(".");
|
||||
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> {
|
||||
T visit(GameEventAttackersDeclared event);
|
||||
T visit(GameEventBlockersDeclared event);
|
||||
T visit(GameEventBlockerAssigned event);
|
||||
T visit(GameEventCardDamaged event);
|
||||
T visit(GameEventCardDestroyed event);
|
||||
@@ -42,6 +44,8 @@ public interface IGameEventVisitor<T> {
|
||||
|
||||
// This is base class for all visitors.
|
||||
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(GameEventCardDamaged 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(GameEventPlayerDamaged event) { return null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ public class Combat {
|
||||
* verifyCreaturesInPlay.
|
||||
* </p>
|
||||
*/
|
||||
public final void verifyCreaturesInPlay() {
|
||||
public final void removeAbsentCombatants() {
|
||||
final List<Card> all = new ArrayList<Card>();
|
||||
all.addAll(this.getAttackers());
|
||||
all.addAll(this.getAllBlockers());
|
||||
@@ -582,7 +582,7 @@ public class Combat {
|
||||
* setUnblocked.
|
||||
* </p>
|
||||
*/
|
||||
public final void setUnblocked() {
|
||||
public final void setUnblockedAttackers() {
|
||||
final List<Card> attacking = this.getAttackers();
|
||||
|
||||
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
|
||||
|
||||
@@ -26,12 +26,15 @@ import org.apache.commons.lang.time.StopWatch;
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.FThreads;
|
||||
import forge.GameEntity;
|
||||
import forge.Singletons;
|
||||
import forge.CardPredicates.Presets;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.GameAge;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.event.GameEventAttackersDeclared;
|
||||
import forge.game.event.GameEventBlockersDeclared;
|
||||
import forge.game.event.GameEventPlayerPriority;
|
||||
import forge.game.event.GameEventTurnBegan;
|
||||
import forge.game.event.GameEventTurnEnded;
|
||||
@@ -44,6 +47,9 @@ import forge.gui.framework.SDisplayUtil;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.nonsingleton.VField;
|
||||
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();
|
||||
|
||||
game.getCombat().verifyCreaturesInPlay();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ) {
|
||||
@@ -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
|
||||
// check for exalted:
|
||||
// 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);
|
||||
@@ -305,7 +303,6 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
int exaltedMagnitude = card.getKeywordAmount("Exalted");
|
||||
if (exaltedMagnitude > 0) {
|
||||
CombatUtil.executeExaltedAbility(game, attacker, exaltedMagnitude, card);
|
||||
// Make sure exalted effects get applied only once per combat
|
||||
}
|
||||
|
||||
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>();
|
||||
runParams.put("Attackers", game.getCombat().getAttackers());
|
||||
runParams.put("AttackingPlayer", game.getCombat().getAttackingPlayer());
|
||||
@@ -332,7 +328,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_BLOCKERS:
|
||||
game.getCombat().verifyCreaturesInPlay();
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
game.getStack().freezeStack();
|
||||
|
||||
Player p = playerTurn;
|
||||
@@ -342,12 +338,25 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
p.getController().declareBlockers();
|
||||
} while(p != playerTurn);
|
||||
|
||||
game.getStack().unfreezeStack();
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
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;
|
||||
|
||||
case COMBAT_FIRST_STRIKE_DAMAGE:
|
||||
game.getCombat().verifyCreaturesInPlay();
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
|
||||
// no first strikers, skip this step
|
||||
if (!game.getCombat().assignCombatDamage(true)) {
|
||||
@@ -359,7 +368,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
break;
|
||||
|
||||
case COMBAT_DAMAGE:
|
||||
game.getCombat().verifyCreaturesInPlay();
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
|
||||
if (!game.getCombat().assignCombatDamage(false)) {
|
||||
this.givePriorityToPlayer = false;
|
||||
|
||||
@@ -15,9 +15,7 @@ public enum PhaseType {
|
||||
MAIN1("Main, precombat", "Main1"),
|
||||
COMBAT_BEGIN("Begin Combat", "BeginCombat"),
|
||||
COMBAT_DECLARE_ATTACKERS("Declare Attackers"),
|
||||
//COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY("Declare Attackers - Play Instants and Abilities"),
|
||||
COMBAT_DECLARE_BLOCKERS("Declare Blockers"),
|
||||
//COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY("Declare Blockers - Play Instants and Abilities"),
|
||||
COMBAT_FIRST_STRIKE_DAMAGE("First Strike Damage"),
|
||||
COMBAT_DAMAGE("Combat Damage"),
|
||||
COMBAT_END("End Combat", "EndCombat"),
|
||||
@@ -27,11 +25,11 @@ public enum PhaseType {
|
||||
|
||||
public static final List<PhaseType> ALL_PHASES = Collections.unmodifiableList(
|
||||
Arrays.asList(
|
||||
UNTAP, UPKEEP, DRAW, MAIN1,
|
||||
COMBAT_BEGIN, COMBAT_DECLARE_ATTACKERS, //COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY,
|
||||
COMBAT_DECLARE_BLOCKERS, // COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY,
|
||||
COMBAT_FIRST_STRIKE_DAMAGE, COMBAT_DAMAGE, COMBAT_END,
|
||||
MAIN2, END_OF_TURN, CLEANUP
|
||||
UNTAP, UPKEEP, DRAW,
|
||||
MAIN1,
|
||||
COMBAT_BEGIN, COMBAT_DECLARE_ATTACKERS, COMBAT_DECLARE_BLOCKERS, COMBAT_FIRST_STRIKE_DAMAGE, COMBAT_DAMAGE, COMBAT_END,
|
||||
MAIN2,
|
||||
END_OF_TURN, CLEANUP
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package forge.game.phase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
@@ -28,7 +27,6 @@ import forge.CardLists;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.staticability.StaticAbility;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.ManaPaymentPurpose;
|
||||
@@ -77,7 +75,6 @@ public class PhaseUtil {
|
||||
*/
|
||||
public static void handleDeclareBlockers(Game game) {
|
||||
final Combat combat = game.getCombat();
|
||||
combat.verifyCreaturesInPlay();
|
||||
|
||||
// Handles removing cards like Mogg Flunkies from combat if group block
|
||||
// didn't occur
|
||||
@@ -85,6 +82,41 @@ public class PhaseUtil {
|
||||
for (Card blocker : filterList) {
|
||||
final List<Card> attackers = new ArrayList<Card>(combat.getAttackersBlockedBy(blocker));
|
||||
for (Card attacker : attackers) {
|
||||
boolean hasPaid = payRequiredBlockCosts(game, blocker, attacker);
|
||||
|
||||
if ( !hasPaid ) {
|
||||
combat.removeBlockAssignment(attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Card c : filterList) {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block alone.") && c.isBlocking()) {
|
||||
if (combat.getAllBlockers().size() < 2) {
|
||||
combat.undoBlockingAssignment(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
combat.setUnblockedAttackers();
|
||||
|
||||
List<Card> list = combat.getAllBlockers();
|
||||
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !c.getDamageHistory().getCreatureBlockedThisCombat();
|
||||
}
|
||||
});
|
||||
|
||||
CombatUtil.checkDeclareBlockers(game, list);
|
||||
|
||||
for (final Card a : combat.getAttackers()) {
|
||||
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)) {
|
||||
@@ -100,44 +132,6 @@ public class PhaseUtil {
|
||||
if (!hasPaid) {
|
||||
hasPaid = blocker.getController().getController().payManaOptional(blocker, blockCost, "Pay cost to declare " + blocker + " a blocker", ManaPaymentPurpose.DeclareBlocker);
|
||||
}
|
||||
|
||||
if ( !hasPaid ) {
|
||||
combat.removeBlockAssignment(attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Card c : filterList) {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block alone.") && c.isBlocking()) {
|
||||
if (combat.getAllBlockers().size() < 2) {
|
||||
combat.undoBlockingAssignment(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
game.getStack().freezeStack();
|
||||
|
||||
combat.setUnblocked();
|
||||
|
||||
List<Card> list = new ArrayList<Card>();
|
||||
list.addAll(combat.getAllBlockers());
|
||||
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !c.getDamageHistory().getCreatureBlockedThisCombat();
|
||||
}
|
||||
});
|
||||
|
||||
final List<Card> attList = combat.getAttackers();
|
||||
|
||||
CombatUtil.checkDeclareBlockers(game, list);
|
||||
|
||||
for (final Card a : attList) {
|
||||
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
|
||||
}
|
||||
|
||||
game.getStack().unfreezeStack();
|
||||
|
||||
game.getGameLog().addCombatBlockers(game.getCombat());
|
||||
return hasPaid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
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(List<T> objects, Function<T, String> accessor) {
|
||||
public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null); }
|
||||
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
|
||||
int remaining = objects.size();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(T obj : objects) {
|
||||
|
||||
Reference in New Issue
Block a user