mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
DeclaresCombatants effect
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -94,6 +94,9 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user