DeclaresCombatants effect

This commit is contained in:
Maxmtg
2013-06-02 17:22:49 +00:00
parent 06b7993e3b
commit ff489765e2
10 changed files with 111 additions and 33 deletions

1
.gitattributes vendored
View File

@@ -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/DamagePreventEffect.java -text
src/main/java/forge/card/ability/effects/DebuffAllEffect.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/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/DelayedTriggerEffect.java -text
src/main/java/forge/card/ability/effects/DestroyAllEffect.java -text src/main/java/forge/card/ability/effects/DestroyAllEffect.java -text
src/main/java/forge/card/ability/effects/DestroyEffect.java -text src/main/java/forge/card/ability/effects/DestroyEffect.java -text

View File

@@ -138,6 +138,7 @@ public enum ApiType {
DealDamage (DamageDealEffect.class, DamageDealAi.class), DealDamage (DamageDealEffect.class, DamageDealAi.class),
Debuff (DebuffEffect.class, DebuffAi.class), Debuff (DebuffEffect.class, DebuffAi.class),
DebuffAll (DebuffAllEffect.class, DebuffAllAi.class), DebuffAll (DebuffAllEffect.class, DebuffAllAi.class),
DeclaresCombatants(DeclareCombatantsEffect.class, CannotPlayAi.class),
DelayedTrigger (DelayedTriggerEffect.class, DelayedTriggerAi.class), DelayedTrigger (DelayedTriggerEffect.class, DelayedTriggerAi.class),
Destroy (DestroyEffect.class, DestroyAi.class), Destroy (DestroyEffect.class, DestroyAi.class),
DestroyAll (DestroyAllEffect.class, DestroyAllAi.class), DestroyAll (DestroyAllEffect.class, DestroyAllAi.class),

View File

@@ -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<Player> 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<Player> 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);
}
}
}

View File

@@ -800,16 +800,17 @@ public class AiController {
} }
} }
public void declateBlockers() { public void declateBlockers(Player defender) {
final List<Card> blockers = player.getCreaturesInPlay(); final List<Card> blockers = defender.getCreaturesInPlay();
game.setCombat(ComputerUtilBlock.getBlockers(player, game.getCombat(), blockers)); // When player != defender, AI should declare blockers for its benefit.
game.setCombat(ComputerUtilBlock.getBlockers(defender, game.getCombat(), blockers));
CombatUtil.orderMultipleCombatants(game.getCombat()); CombatUtil.orderMultipleCombatants(game.getCombat());
} }
public void declareAttackers() { public void declareAttackers(Player attacker) {
// 12/2/10(sol) the decision making here has moved to getAttackers() // 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()) { for (final Card element : game.getCombat().getAttackers()) {
// tapping of attackers happens after Propaganda is paid for // tapping of attackers happens after Propaganda is paid for
@@ -818,7 +819,7 @@ public class AiController {
Log.debug(sb.toString()); Log.debug(sb.toString());
} }
player.getZone(ZoneType.Battlefield).updateObservers(); attacker.getZone(ZoneType.Battlefield).updateObservers();
// ai is about to attack, cancel all phase skipping // ai is about to attack, cancel all phase skipping
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {

View File

@@ -93,6 +93,9 @@ public class PhaseHandler implements java.io.Serializable {
private Player pFirstPriority = null; private Player pFirstPriority = null;
private boolean bCombat = false; private boolean bCombat = false;
private boolean bRepeatCleanup = false; private boolean bRepeatCleanup = false;
private Player playerDeclaresBlockers = null;
private Player playerDeclaresAttackers = null;
/** The need to next phase. */ /** The need to next phase. */
private boolean givePriorityToPlayer = false; private boolean givePriorityToPlayer = false;
@@ -211,11 +214,7 @@ public class PhaseHandler implements java.io.Serializable {
if (this.phase == PhaseType.UNTAP) { if (this.phase == PhaseType.UNTAP) {
this.turn++; this.turn++;
game.fireEvent(new GameEventTurnBegan(playerTurn, 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 // Here's what happens on new turn, regardless of skipped phases
game.getCombat().reset(playerTurn); game.getCombat().reset(playerTurn);
@@ -232,8 +231,13 @@ public class PhaseHandler implements java.io.Serializable {
final List<Card> lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED); final List<Card> lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED);
playerTurn.setNumPowerSurgeLands(lands.size()); 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) { private boolean isSkippingPhase(PhaseType phase) {
@@ -465,11 +469,11 @@ public class PhaseHandler implements java.io.Serializable {
break; break;
default: // no action default: // no action
} }
} }
private void declateAttackTurnBasedActions() { private void declateAttackTurnBasedActions() {
playerTurn.getController().declareAttackers(); Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers;
whoDeclares.getController().declareAttackers(playerTurn);
game.getCombat().removeAbsentCombatants(); game.getCombat().removeAbsentCombatants();
CombatUtil.checkAttackOrBlockAlone(game.getCombat()); CombatUtil.checkAttackOrBlockAlone(game.getCombat());
@@ -520,12 +524,14 @@ public class PhaseHandler implements java.io.Serializable {
private void declareBlockersTurnBaseActions() { private void declareBlockersTurnBaseActions() {
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
Player p = playerTurn; Player p = playerTurn;
do { do {
p = game.getNextPlayerAfter(p); p = game.getNextPlayerAfter(p);
// Apply Odric's effect here
Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers;
if ( combat.isPlayerAttacked(p) ) if ( combat.isPlayerAttacked(p) )
p.getController().declareBlockers(); whoDeclaresBlockers.getController().declareBlockers(p);
} while(p != playerTurn); } while(p != playerTurn);
combat.removeAbsentCombatants(); 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;
}
} }

View File

@@ -135,8 +135,8 @@ public abstract class PlayerController {
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question); public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
public abstract List<Card> getCardsToMulligan(boolean isCommander, Player firstPlayer); public abstract List<Card> getCardsToMulligan(boolean isCommander, Player firstPlayer);
public abstract void declareAttackers(); public abstract void declareAttackers(Player attacker);
public abstract void declareBlockers(); public abstract void declareBlockers(Player defender);
public abstract void takePriority(); public abstract void takePriority();
public abstract List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard);

View File

@@ -287,14 +287,14 @@ public class PlayerControllerAi extends PlayerController {
} }
@Override @Override
public void declareAttackers() { public void declareAttackers(Player attacker) {
brains.declareAttackers(); brains.declareAttackers(attacker);
} }
@Override @Override
public void declareBlockers() { public void declareBlockers(Player defender) {
brains.declateBlockers(); brains.declateBlockers(defender);
} }
@Override @Override

View File

@@ -470,17 +470,17 @@ public class PlayerControllerHuman extends PlayerController {
} }
@Override @Override
public void declareAttackers() { public void declareAttackers(Player attacker) {
game.getCombat().initiatePossibleDefenders(player.getOpponents()); game.getCombat().initiatePossibleDefenders(attacker.getOpponents());
// This input should not modify combat object itself, but should return user choice // This input should not modify combat object itself, but should return user choice
InputSynchronized inpAttack = new InputAttack(player, game.getCombat()); InputSynchronized inpAttack = new InputAttack(player, game.getCombat());
Singletons.getControl().getInputQueue().setInputAndWait(inpAttack); Singletons.getControl().getInputQueue().setInputAndWait(inpAttack);
} }
@Override @Override
public void declareBlockers() { public void declareBlockers(Player defender) {
// This input should not modify combat object itself, but should return user choice // 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); Singletons.getControl().getInputQueue().setInputAndWait(inpBlock);
} }

View File

@@ -43,14 +43,16 @@ public class InputBlock extends InputSyncronizedBase {
private Card currentAttacker = null; private Card currentAttacker = null;
private final HashMap<Card, List<Card>> allBlocking = new HashMap<Card, List<Card>>(); private final HashMap<Card, List<Card>> allBlocking = new HashMap<Card, List<Card>>();
private final Combat combat; private final Combat combat;
private final Player player; private final Player defender;
private final Player declarer;
/** /**
* TODO: Write javadoc for Constructor. * TODO: Write javadoc for Constructor.
* @param priority * @param priority
*/ */
public InputBlock(Player human, Combat combat) { public InputBlock(Player whoDeclares, Player whoDefends, Combat combat) {
player = human; defender = whoDefends;
declarer = whoDeclares;
this.combat = combat; this.combat = combat;
} }
@@ -63,8 +65,11 @@ public class InputBlock extends InputSyncronizedBase {
protected final void showMessage() { protected final void showMessage() {
// could add "Reset Blockers" button // could add "Reset Blockers" button
ButtonUtil.enableOnlyOk(); 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) { if (this.currentAttacker == null) {
sb.append("To Block, click on your opponent's attacker first, then your blocker(s).\n"); 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} */ /** {@inheritDoc} */
@Override @Override
public final void onOk() { public final void onOk() {
if (CombatUtil.finishedMandatoryBlocks(combat, player)) { if (CombatUtil.finishedMandatoryBlocks(combat, defender)) {
// Done blocking // Done blocking
ButtonUtil.reset(); ButtonUtil.reset();
CombatUtil.orderMultipleCombatants(combat); CombatUtil.orderMultipleCombatants(combat);
@@ -100,7 +105,7 @@ public class InputBlock extends InputSyncronizedBase {
public final void onCardSelected(final Card card, boolean isMetaDown) { public final void onCardSelected(final Card card, boolean isMetaDown) {
if (isMetaDown) { if (isMetaDown) {
if (card.getController() == player ) { if (card.getController() == defender ) {
combat.removeFromCombat(card); combat.removeFromCombat(card);
} }
removeFromAllBlocking(card); removeFromAllBlocking(card);
@@ -116,7 +121,7 @@ public class InputBlock extends InputSyncronizedBase {
reminder = false; reminder = false;
} else { } else {
// Make sure this card is valid to even be a blocker // 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 // Create a new blockedBy list if it doesn't exist
if (!this.allBlocking.containsKey(card)) { if (!this.allBlocking.containsKey(card)) {
this.allBlocking.put(card, new ArrayList<Card>()); this.allBlocking.put(card, new ArrayList<Card>());

View File

@@ -3,6 +3,8 @@ package forge.util;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
/** /**
@@ -28,6 +30,12 @@ public class Lang {
} }
} }
public static <T> 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 <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null); } public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null); }
public static <T> String joinHomogenous(Collection<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();