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

View File

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

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() {
final List<Card> blockers = player.getCreaturesInPlay();
game.setCombat(ComputerUtilBlock.getBlockers(player, game.getCombat(), blockers));
public void declateBlockers(Player defender) {
final List<Card> 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()) {

View File

@@ -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<Card> 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;
}
}

View File

@@ -135,8 +135,8 @@ public abstract class PlayerController {
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
public abstract List<Card> 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<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard);

View File

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

View File

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

View File

@@ -43,14 +43,16 @@ public class InputBlock extends InputSyncronizedBase {
private Card currentAttacker = null;
private final HashMap<Card, List<Card>> allBlocking = new HashMap<Card, List<Card>>();
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<Card>());

View File

@@ -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 <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, Function<T, String> accessor) {
int remaining = objects.size();