From ff489765e2edbb65624df71b901b2934cebaa567 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sun, 2 Jun 2013 17:22:49 +0000 Subject: [PATCH] DeclaresCombatants effect --- .gitattributes | 1 + src/main/java/forge/card/ability/ApiType.java | 1 + .../effects/DeclareCombatantsEffect.java | 44 +++++++++++++++++++ src/main/java/forge/game/ai/AiController.java | 13 +++--- .../java/forge/game/phase/PhaseHandler.java | 36 +++++++++++---- .../forge/game/player/PlayerController.java | 4 +- .../forge/game/player/PlayerControllerAi.java | 8 ++-- .../game/player/PlayerControllerHuman.java | 8 ++-- src/main/java/forge/gui/input/InputBlock.java | 21 +++++---- src/main/java/forge/util/Lang.java | 8 ++++ 10 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 src/main/java/forge/card/ability/effects/DeclareCombatantsEffect.java diff --git a/.gitattributes b/.gitattributes index 7f81249771d..10f0a7d3b88 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13977,6 +13977,7 @@ src/main/java/forge/card/ability/effects/DamagePreventAllEffect.java -text src/main/java/forge/card/ability/effects/DamagePreventEffect.java -text src/main/java/forge/card/ability/effects/DebuffAllEffect.java -text src/main/java/forge/card/ability/effects/DebuffEffect.java -text +src/main/java/forge/card/ability/effects/DeclareCombatantsEffect.java -text src/main/java/forge/card/ability/effects/DelayedTriggerEffect.java -text src/main/java/forge/card/ability/effects/DestroyAllEffect.java -text src/main/java/forge/card/ability/effects/DestroyEffect.java -text diff --git a/src/main/java/forge/card/ability/ApiType.java b/src/main/java/forge/card/ability/ApiType.java index 9bd68d70b9e..247faa2997f 100644 --- a/src/main/java/forge/card/ability/ApiType.java +++ b/src/main/java/forge/card/ability/ApiType.java @@ -138,6 +138,7 @@ public enum ApiType { DealDamage (DamageDealEffect.class, DamageDealAi.class), Debuff (DebuffEffect.class, DebuffAi.class), DebuffAll (DebuffAllEffect.class, DebuffAllAi.class), + DeclaresCombatants(DeclareCombatantsEffect.class, CannotPlayAi.class), DelayedTrigger (DelayedTriggerEffect.class, DelayedTriggerAi.class), Destroy (DestroyEffect.class, DestroyAi.class), DestroyAll (DestroyAllEffect.class, DestroyAllAi.class), diff --git a/src/main/java/forge/card/ability/effects/DeclareCombatantsEffect.java b/src/main/java/forge/card/ability/effects/DeclareCombatantsEffect.java new file mode 100644 index 00000000000..f2732f45530 --- /dev/null +++ b/src/main/java/forge/card/ability/effects/DeclareCombatantsEffect.java @@ -0,0 +1,44 @@ +package forge.card.ability.effects; + +import java.util.List; + +import forge.card.ability.SpellAbilityEffect; +import forge.card.spellability.SpellAbility; +import forge.game.phase.PhaseHandler; +import forge.game.player.Player; +import forge.util.Lang; + +public class DeclareCombatantsEffect extends SpellAbilityEffect { + + @Override + protected String getStackDescription(SpellAbility sa) { + List tgtPlayers = getDefinedPlayersBeforeTargetOnes(sa); + + boolean attackers = sa.hasParam("DeclareAttackers"); + final String attDesc = "which creatures attack"; + + boolean blockers = sa.hasParam("DeclareBlockers"); + final String defDesc = "which creatures block this turn and how those creatures block"; + + String what = Lang.joinHomogenous(attackers ? attDesc : null, blockers ? defDesc : null); + + // TODO Auto-generated method stub + return Lang.joinHomogenous(tgtPlayers) + " " + Lang.joinVerb(tgtPlayers, "choose") + " " + what + " this turn."; + } + + @Override + public void resolve(SpellAbility sa) { + List tgtPlayers = getDefinedPlayersBeforeTargetOnes(sa); + + boolean attackers = sa.hasParam("DeclareAttackers"); + boolean blockers = sa.hasParam("DeclareBlockers"); + + for(Player p : tgtPlayers) { // Obviuosly the last player will be applied + final PhaseHandler ph = p.getGame().getPhaseHandler(); + if( attackers ) ph.setPlayerDeclaresAttackers(p); + if( blockers ) ph.setPlayerDeclaresBlockers(p); + } + + } + +} diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index a54f3bbea21..3ba44b020a3 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -800,16 +800,17 @@ public class AiController { } } - public void declateBlockers() { - final List blockers = player.getCreaturesInPlay(); - game.setCombat(ComputerUtilBlock.getBlockers(player, game.getCombat(), blockers)); + public void declateBlockers(Player defender) { + final List blockers = defender.getCreaturesInPlay(); + // When player != defender, AI should declare blockers for its benefit. + game.setCombat(ComputerUtilBlock.getBlockers(defender, game.getCombat(), blockers)); CombatUtil.orderMultipleCombatants(game.getCombat()); } - public void declareAttackers() { + public void declareAttackers(Player attacker) { // 12/2/10(sol) the decision making here has moved to getAttackers() - game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers()); + game.setCombat(new AiAttackController(attacker, attacker.getOpponent()).getAttackers()); for (final Card element : game.getCombat().getAttackers()) { // tapping of attackers happens after Propaganda is paid for @@ -818,7 +819,7 @@ public class AiController { Log.debug(sb.toString()); } - player.getZone(ZoneType.Battlefield).updateObservers(); + attacker.getZone(ZoneType.Battlefield).updateObservers(); // ai is about to attack, cancel all phase skipping for (Player p : game.getPlayers()) { diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 38d66a86882..cb453783b6d 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -93,6 +93,9 @@ public class PhaseHandler implements java.io.Serializable { private Player pFirstPriority = null; private boolean bCombat = false; private boolean bRepeatCleanup = false; + + private Player playerDeclaresBlockers = null; + private Player playerDeclaresAttackers = null; /** The need to next phase. */ private boolean givePriorityToPlayer = false; @@ -211,11 +214,7 @@ public class PhaseHandler implements java.io.Serializable { if (this.phase == PhaseType.UNTAP) { this.turn++; game.fireEvent(new GameEventTurnBegan(playerTurn, turn)); - } - game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), phaseType)); - - if (this.phase == PhaseType.UNTAP) { // Here's what happens on new turn, regardless of skipped phases game.getCombat().reset(playerTurn); @@ -232,8 +231,13 @@ public class PhaseHandler implements java.io.Serializable { final List lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED); playerTurn.setNumPowerSurgeLands(lands.size()); + + // reset players controlling attack or defense + playerDeclaresAttackers = null; + playerDeclaresBlockers = null; } - + + game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), phaseType)); } private boolean isSkippingPhase(PhaseType phase) { @@ -465,11 +469,11 @@ public class PhaseHandler implements java.io.Serializable { break; default: // no action } - } private void declateAttackTurnBasedActions() { - playerTurn.getController().declareAttackers(); + Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers; + whoDeclares.getController().declareAttackers(playerTurn); game.getCombat().removeAbsentCombatants(); CombatUtil.checkAttackOrBlockAlone(game.getCombat()); @@ -520,12 +524,14 @@ public class PhaseHandler implements java.io.Serializable { private void declareBlockersTurnBaseActions() { final Combat combat = game.getCombat(); - Player p = playerTurn; + do { p = game.getNextPlayerAfter(p); + // Apply Odric's effect here + Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers; if ( combat.isPlayerAttacked(p) ) - p.getController().declareBlockers(); + whoDeclaresBlockers.getController().declareBlockers(p); } while(p != playerTurn); combat.removeAbsentCombatants(); @@ -936,4 +942,16 @@ public class PhaseHandler implements java.io.Serializable { } + public final void setPlayerDeclaresBlockers(Player player) { + this.playerDeclaresBlockers = player; + } + + + + public final void setPlayerDeclaresAttackers(Player player) { + this.playerDeclaresAttackers = player; + } + + + } diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 9b2746229a7..5636c0e3787 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -135,8 +135,8 @@ public abstract class PlayerController { public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question); public abstract List getCardsToMulligan(boolean isCommander, Player firstPlayer); - public abstract void declareAttackers(); - public abstract void declareBlockers(); + public abstract void declareAttackers(Player attacker); + public abstract void declareBlockers(Player defender); public abstract void takePriority(); public abstract List chooseCardsToDiscardToMaximumHandSize(int numDiscard); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 971d9c63dc4..c790b848847 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -287,14 +287,14 @@ public class PlayerControllerAi extends PlayerController { } @Override - public void declareAttackers() { - brains.declareAttackers(); + public void declareAttackers(Player attacker) { + brains.declareAttackers(attacker); } @Override - public void declareBlockers() { - brains.declateBlockers(); + public void declareBlockers(Player defender) { + brains.declateBlockers(defender); } @Override diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 59715a533e4..700613f1f68 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -470,17 +470,17 @@ public class PlayerControllerHuman extends PlayerController { } @Override - public void declareAttackers() { - game.getCombat().initiatePossibleDefenders(player.getOpponents()); + public void declareAttackers(Player attacker) { + game.getCombat().initiatePossibleDefenders(attacker.getOpponents()); // This input should not modify combat object itself, but should return user choice InputSynchronized inpAttack = new InputAttack(player, game.getCombat()); Singletons.getControl().getInputQueue().setInputAndWait(inpAttack); } @Override - public void declareBlockers() { + public void declareBlockers(Player defender) { // This input should not modify combat object itself, but should return user choice - InputSynchronized inpBlock = new InputBlock(player, game.getCombat()); + InputSynchronized inpBlock = new InputBlock(player, defender, game.getCombat()); Singletons.getControl().getInputQueue().setInputAndWait(inpBlock); } diff --git a/src/main/java/forge/gui/input/InputBlock.java b/src/main/java/forge/gui/input/InputBlock.java index 9433590eb71..abb5879382b 100644 --- a/src/main/java/forge/gui/input/InputBlock.java +++ b/src/main/java/forge/gui/input/InputBlock.java @@ -43,14 +43,16 @@ public class InputBlock extends InputSyncronizedBase { private Card currentAttacker = null; private final HashMap> allBlocking = new HashMap>(); private final Combat combat; - private final Player player; + private final Player defender; + private final Player declarer; /** * TODO: Write javadoc for Constructor. * @param priority */ - public InputBlock(Player human, Combat combat) { - player = human; + public InputBlock(Player whoDeclares, Player whoDefends, Combat combat) { + defender = whoDefends; + declarer = whoDeclares; this.combat = combat; } @@ -63,8 +65,11 @@ public class InputBlock extends InputSyncronizedBase { protected final void showMessage() { // could add "Reset Blockers" button ButtonUtil.enableOnlyOk(); - final StringBuilder sb = new StringBuilder(); - sb.append(player.getName() + ", declare blockers.\n\n"); + + String prompt = declarer == defender ? "declare blockers." : "declare blockers for " + defender.getName(); + + final StringBuilder sb = new StringBuilder(declarer.getName()); + sb.append(", ").append(prompt).append("\n\n"); if (this.currentAttacker == null) { sb.append("To Block, click on your opponent's attacker first, then your blocker(s).\n"); @@ -84,7 +89,7 @@ public class InputBlock extends InputSyncronizedBase { /** {@inheritDoc} */ @Override public final void onOk() { - if (CombatUtil.finishedMandatoryBlocks(combat, player)) { + if (CombatUtil.finishedMandatoryBlocks(combat, defender)) { // Done blocking ButtonUtil.reset(); CombatUtil.orderMultipleCombatants(combat); @@ -100,7 +105,7 @@ public class InputBlock extends InputSyncronizedBase { public final void onCardSelected(final Card card, boolean isMetaDown) { if (isMetaDown) { - if (card.getController() == player ) { + if (card.getController() == defender ) { combat.removeFromCombat(card); } removeFromAllBlocking(card); @@ -116,7 +121,7 @@ public class InputBlock extends InputSyncronizedBase { reminder = false; } else { // Make sure this card is valid to even be a blocker - if (this.currentAttacker != null && card.isCreature() && player.getZone(ZoneType.Battlefield).contains(card)) { + if (this.currentAttacker != null && card.isCreature() && defender.getZone(ZoneType.Battlefield).contains(card)) { // Create a new blockedBy list if it doesn't exist if (!this.allBlocking.containsKey(card)) { this.allBlocking.put(card, new ArrayList()); diff --git a/src/main/java/forge/util/Lang.java b/src/main/java/forge/util/Lang.java index 291a23635d5..9bd8e9194f0 100644 --- a/src/main/java/forge/util/Lang.java +++ b/src/main/java/forge/util/Lang.java @@ -3,6 +3,8 @@ package forge.util; import java.util.Collection; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import com.google.common.base.Function; /** @@ -28,6 +30,12 @@ public class Lang { } } + public static String joinHomogenous(String s1, String s2) { + boolean has1 = StringUtils.isNotBlank(s1); + boolean has2 = StringUtils.isNotBlank(s2); + return has1 ? (has2 ? s1 + " and " + s2 : s1) : (has2 ? s2 : ""); + } + public static String joinHomogenous(Collection objects) { return joinHomogenous(objects, null); } public static String joinHomogenous(Collection objects, Function accessor) { int remaining = objects.size();