diff --git a/.gitattributes b/.gitattributes index 36d9d424bd2..6bec2fa8aee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2552,6 +2552,7 @@ res/cardsfolder/d/defender_of_law.txt svneol=native#text/plain res/cardsfolder/d/defender_of_the_order.txt svneol=native#text/plain res/cardsfolder/d/defense_grid.txt svneol=native#text/plain res/cardsfolder/d/defense_of_the_heart.txt svneol=native#text/plain +res/cardsfolder/d/defensive_formation.txt -text res/cardsfolder/d/defensive_maneuvers.txt -text svneol=unset#text/plain res/cardsfolder/d/defensive_stance.txt svneol=native#text/plain res/cardsfolder/d/defiant_elf.txt svneol=native#text/plain diff --git a/res/cardsfolder/d/defensive_formation.txt b/res/cardsfolder/d/defensive_formation.txt new file mode 100644 index 00000000000..a61606527a8 --- /dev/null +++ b/res/cardsfolder/d/defensive_formation.txt @@ -0,0 +1,8 @@ +Name:Defensive Formation +ManaCost:W +Types:Enchantment +S:Mode$ Continuous | Affected$ You | AddKeyword$ You assign combat damage of each creature attacking you. | Description$ Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it. +SVar:NonStackingEffect:True +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/defensive_formation.jpg +Oracle:Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it. diff --git a/src/main/java/forge/game/ai/ComputerUtilCombat.java b/src/main/java/forge/game/ai/ComputerUtilCombat.java index 91de0fcc8e6..7569ba39d33 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCombat.java +++ b/src/main/java/forge/game/ai/ComputerUtilCombat.java @@ -1670,8 +1670,10 @@ public class ComputerUtilCombat { * @param dmgCanDeal * a int. * @param defender + * @param overrideOrder overriding combatant order */ - public static Map distributeAIDamage(final Card attacker, final List block, int dmgCanDeal, GameEntity defender) { + public static Map distributeAIDamage(final Card attacker, final List block, int dmgCanDeal, GameEntity defender, boolean overrideOrder) { + // TODO: Distribute defensive Damage (AI controls how damage is dealt to own cards) for Banding and Defensive Formation Map damageMap = new HashMap(); boolean isAttacking = defender != null; diff --git a/src/main/java/forge/game/phase/Combat.java b/src/main/java/forge/game/phase/Combat.java index 0c69060ba9f..88ae95a77ae 100644 --- a/src/main/java/forge/game/phase/Combat.java +++ b/src/main/java/forge/game/phase/Combat.java @@ -618,7 +618,7 @@ public class Combat { if (!attackers.isEmpty()) { assignedDamage = true; - Map map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null); + Map map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null, false); for (Entry dt : map.entrySet()) { dt.getKey().addAssignedDamage(dt.getValue(), blocker); dt.getKey().updateObservers(); @@ -659,7 +659,22 @@ public class Combat { continue; } } else { - Map map = this.getAttackingPlayer().getController().assignCombatDamage(attacker, blockers, damageDealt, this.getDefenderByAttacker(attacker)); + GameEntity defender = this.getDefenderByAttacker(attacker); + 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; + } + + if (!assigningPlayer.equals(defender)) { + List blockingBand = CardLists.getKeyword(blockers, "Banding"); + if (!blockingBand.isEmpty()) { + assigningPlayer = blockingBand.get(0).getController(); + } + } + + Map map = assigningPlayer.getController().assignCombatDamage(attacker, blockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer); for (Entry dt : map.entrySet()) { if( dt.getKey() == null) { if (dt.getValue() > 0) diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index acb58126e56..a1d93a8078d 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -96,7 +96,7 @@ public abstract class PlayerController { public abstract Deck sideboard(final Deck deck, GameType gameType); - public abstract Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender); + public abstract Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder); public abstract Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero); public abstract List choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List validTargets, String message); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 4a35b54da85..b359423504e 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -113,8 +113,8 @@ public class PlayerControllerAi extends PlayerController { } @Override - public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender) { - return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender); + public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { + return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder); } @Override diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index e6e11983b25..752ff32c79a 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -180,7 +180,7 @@ public class PlayerControllerHuman extends PlayerController { * @see forge.game.player.PlayerController#assignCombatDamage() */ @Override - public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender) { + public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { // Attacker is a poor name here, since the creature assigning damage // could just as easily be the blocker. Map map; @@ -189,7 +189,7 @@ public class PlayerControllerHuman extends PlayerController { map.put(null, damageDealt); } else { if ((attacker.hasKeyword("Trample") && defender != null) || (blockers.size() > 1)) { - map = CMatchUI.SINGLETON_INSTANCE.getDamageToAssign(attacker, blockers, damageDealt, defender); + map = CMatchUI.SINGLETON_INSTANCE.getDamageToAssign(attacker, blockers, damageDealt, defender, overrideOrder); } else { map = new HashMap(); map.put(blockers.get(0), damageDealt); diff --git a/src/main/java/forge/gui/match/CMatchUI.java b/src/main/java/forge/gui/match/CMatchUI.java index 9fbe900ee31..d254f364baa 100644 --- a/src/main/java/forge/gui/match/CMatchUI.java +++ b/src/main/java/forge/gui/match/CMatchUI.java @@ -200,9 +200,10 @@ public enum CMatchUI { * @param attacker   {@link forge.Card} * @param blockers   {@link forge.CardList} * @param damage   int + * @param overrideOrder overriding combatant order */ @SuppressWarnings("unchecked") - public Map getDamageToAssign(final Card attacker, final List blockers, final int damage, final GameEntity defender) { + public Map getDamageToAssign(final Card attacker, final List blockers, final int damage, final GameEntity defender, final boolean overrideOrder) { if (damage <= 0) { return new HashMap(); } @@ -219,8 +220,7 @@ public enum CMatchUI { FThreads.invokeInEdtAndWait(new Runnable() { @Override public void run() { - // TODO Auto-generated method stub - VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender); + VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender, overrideOrder); result[0] = v.getDamageMap(); }}); return (Map)result[0]; diff --git a/src/main/java/forge/gui/match/VAssignDamage.java b/src/main/java/forge/gui/match/VAssignDamage.java index 74a726f7d04..c4532f2073e 100644 --- a/src/main/java/forge/gui/match/VAssignDamage.java +++ b/src/main/java/forge/gui/match/VAssignDamage.java @@ -69,6 +69,7 @@ public class VAssignDamage { private boolean attackerHasDeathtouch = false; private boolean attackerHasTrample = false; private boolean attackerHasInfect = false; + private boolean overrideCombatantOrder = false; private final GameEntity defender; @@ -136,17 +137,20 @@ public class VAssignDamage { /** Constructor. * * @param attacker0 {@link forge.Card} - * @param defenders0 List<{@link forge.Card}> + * @param defenderCards List<{@link forge.Card}> * @param damage0 int * @param defender GameEntity that's bein attacked + * @param overrideOrder override combatant order + */ - public VAssignDamage(final Card attacker0, final List defenderCards, final int damage0, final GameEntity defender) { + public VAssignDamage(final Card attacker0, final List defenderCards, final int damage0, final GameEntity defender, boolean overrideOrder) { // Set damage storage vars this.totalDamageToAssign = damage0; this.defender = defender; this.attackerHasDeathtouch = attacker0.hasKeyword("Deathtouch"); this.attackerHasInfect = attacker0.hasKeyword("Infect"); this.attackerHasTrample = defender != null && attacker0.hasKeyword("Trample"); + this.overrideCombatantOrder = overrideOrder; // Top-level UI stuff final JPanel overlay = SOverlayUtils.genericOverlay(); @@ -275,8 +279,10 @@ public class VAssignDamage { if ( !damage.containsKey(source) ) source = null; - // Allow click if this is active, or next to active and active has lethal - if (isAdding && !VAssignDamage.this.canAssignTo(source)) { + // If trying to assign to the defender, follow the normal assignment rules + // No need to check for "active" creature assignee when overiding combatant order + if ((source == null || source == this.defender || !this.overrideCombatantOrder) && isAdding && + !VAssignDamage.this.canAssignTo(source)) { return; } @@ -301,9 +307,9 @@ public class VAssignDamage { if ( damageToAdd > leftToAssign ) damageToAdd = leftToAssign; - // cannot assign first blocker less than lethal damage + // cannot assign first blocker less than lethal damage except when overriding order boolean isFirstBlocker = defenders.get(0).card == source; - if ( isFirstBlocker && damageToAdd + damageItHad < lethalDamage ) + if (!this.overrideCombatantOrder && isFirstBlocker && damageToAdd + damageItHad < lethalDamage ) return; if ( 0 == damageToAdd || damageToAdd + damageItHad < 0) @@ -314,23 +320,29 @@ public class VAssignDamage { updateLabels(); } - /** - * TODO: Write javadoc for this method. - */ private void checkDamageQueue() { + // Clear out any Damage that shouldn't be assigned to other combatants boolean hasAliveEnemy = false; for(DamageTarget dt : defenders) { int lethal = getDamageToKill(dt.card); int damage = dt.damage; - if ( hasAliveEnemy ) + // If overriding combatant order, make sure everything has lethal if defender has damage assigned to it + // Otherwise, follow normal combatant order + if ( hasAliveEnemy && (!this.overrideCombatantOrder || dt.card == null || dt.card == this.defender)) dt.damage = 0; else - hasAliveEnemy = damage < lethal; + hasAliveEnemy |= damage < lethal; } } // will assign all damage to defenders and rest to player, if present private void initialAssignDamage(boolean toAllBlockers) { + if (!toAllBlockers && this.overrideCombatantOrder) { + // Don't auto assign the first damage when overriding combatant order + updateLabels(); + return; + } + int dmgLeft = totalDamageToAssign; DamageTarget dtLast = null; for(DamageTarget dt : defenders) { // MUST NOT RUN WITH EMPTY collection