- Added Defensive Formation

- Added some logic for controllers of combatants to assign combat damage (required for Banding and Defensive Formation)
This commit is contained in:
Sol
2013-06-08 04:08:47 +00:00
parent d29b9cb0fd
commit ac021be730
9 changed files with 60 additions and 22 deletions

1
.gitattributes vendored
View File

@@ -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

View File

@@ -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.

View File

@@ -1670,8 +1670,10 @@ public class ComputerUtilCombat {
* @param dmgCanDeal
* a int.
* @param defender
* @param overrideOrder overriding combatant order
*/
public static Map<Card, Integer> distributeAIDamage(final Card attacker, final List<Card> block, int dmgCanDeal, GameEntity defender) {
public static Map<Card, Integer> distributeAIDamage(final Card attacker, final List<Card> 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<Card, Integer> damageMap = new HashMap<Card, Integer>();
boolean isAttacking = defender != null;

View File

@@ -618,7 +618,7 @@ public class Combat {
if (!attackers.isEmpty()) {
assignedDamage = true;
Map<Card, Integer> map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null);
Map<Card, Integer> map = blocker.getController().getController().assignCombatDamage(blocker, attackers, damage, null, false);
for (Entry<Card, Integer> dt : map.entrySet()) {
dt.getKey().addAssignedDamage(dt.getValue(), blocker);
dt.getKey().updateObservers();
@@ -659,7 +659,22 @@ public class Combat {
continue;
}
} else {
Map<Card, Integer> 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<Card> blockingBand = CardLists.getKeyword(blockers, "Banding");
if (!blockingBand.isEmpty()) {
assigningPlayer = blockingBand.get(0).getController();
}
}
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, blockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer);
for (Entry<Card, Integer> dt : map.entrySet()) {
if( dt.getKey() == null) {
if (dt.getValue() > 0)

View File

@@ -96,7 +96,7 @@ public abstract class PlayerController {
public abstract Deck sideboard(final Deck deck, GameType gameType);
public abstract Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender);
public abstract Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender, boolean overrideOrder);
public abstract Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero);
public abstract List<Card> choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List<Card> validTargets, String message);

View File

@@ -113,8 +113,8 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender) {
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender);
public Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender, boolean overrideOrder) {
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder);
}
@Override

View File

@@ -180,7 +180,7 @@ public class PlayerControllerHuman extends PlayerController {
* @see forge.game.player.PlayerController#assignCombatDamage()
*/
@Override
public Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender) {
public Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> 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<Card, Integer> 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<Card, Integer>();
map.put(blockers.get(0), damageDealt);

View File

@@ -200,9 +200,10 @@ public enum CMatchUI {
* @param attacker &emsp; {@link forge.Card}
* @param blockers &emsp; {@link forge.CardList}
* @param damage &emsp; int
* @param overrideOrder overriding combatant order
*/
@SuppressWarnings("unchecked")
public Map<Card, Integer> getDamageToAssign(final Card attacker, final List<Card> blockers, final int damage, final GameEntity defender) {
public Map<Card, Integer> getDamageToAssign(final Card attacker, final List<Card> blockers, final int damage, final GameEntity defender, final boolean overrideOrder) {
if (damage <= 0) {
return new HashMap<Card, Integer>();
}
@@ -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<Card, Integer>)result[0];

View File

@@ -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<Card> defenderCards, final int damage0, final GameEntity defender) {
public VAssignDamage(final Card attacker0, final List<Card> 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