From 174d1f7838ae9ba58bbc3a07820ea47e832885ec Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 8 Aug 2017 04:07:18 +0000 Subject: [PATCH] - Puzzle mode improvements: no triggers will now run when the game state is set up; triggers will run in combat if attackers are declared. --- .../src/main/java/forge/ai/GameState.java | 54 +++++++++++++++---- .../forge/game/trigger/TriggerHandler.java | 10 ++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index feb20a5eda8..132b000ee8e 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -1,6 +1,9 @@ package forge.ai; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import forge.StaticData; import forge.card.CardStateName; import forge.game.Game; @@ -9,6 +12,9 @@ import forge.game.ability.AbilityFactory; import forge.game.ability.effects.DetachedCardEffect; import forge.game.card.*; import forge.game.combat.Combat; +import forge.game.combat.CombatUtil; +import forge.game.event.GameEventAttackersDeclared; +import forge.game.event.GameEventCombatChanged; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -432,9 +438,7 @@ public abstract class GameState { game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn); - game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getTriggerHandler().suppressMode(TriggerType.DamageDone); - game.getTriggerHandler().suppressMode(TriggerType.Unequip); + game.getTriggerHandler().setSuppressAllTriggers(true); setupPlayerState(humanLife, humanCardTexts, human); setupPlayerState(computerLife, aiCardTexts, ai); @@ -446,16 +450,15 @@ public abstract class GameState { handlePrecastSpells(game); handleMarkedDamage(); + game.getTriggerHandler().setSuppressAllTriggers(false); + // Combat only works for 1v1 matches for now (which are the only matches dev mode supports anyway) + // Note: triggers may fire during combat declarations ("whenever X attacks, ...", etc.) if (newPhase == PhaseType.COMBAT_DECLARE_ATTACKERS || newPhase == PhaseType.COMBAT_DECLARE_BLOCKERS) { boolean toDeclareBlockers = newPhase == PhaseType.COMBAT_DECLARE_BLOCKERS; handleCombat(game, newPlayerTurn, newPlayerTurn.getSingleOpponent(), toDeclareBlockers); } - game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); - game.getTriggerHandler().clearSuppression(TriggerType.DamageDone); - game.getTriggerHandler().clearSuppression(TriggerType.Unequip); - game.getStack().setResolving(false); game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated @@ -471,17 +474,46 @@ public abstract class GameState { game.updateCombatForView(); } + Combat combat = game.getPhaseHandler().getCombat(); for (Entry attackMap : cardAttackMap.entrySet()) { Card attacker = attackMap.getKey(); Card attacked = attackMap.getValue(); - game.getPhaseHandler().getCombat().addAttacker(attacker, attacked == null ? defendingPlayer : attacked); + combat.addAttacker(attacker, attacked == null ? defendingPlayer : attacked); } - game.updateCombatForView(); + // Run the necessary combat events and triggers to set things up correctly as if the + // attack was actually declared by the attacking player + Multimap attackersMap = ArrayListMultimap.create(); + for (GameEntity ge : combat.getDefenders()) { + attackersMap.putAll(ge, combat.getAttackersOf(ge)); + } + game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap)); - // Gracefully proceed to Declare Blockers, giving priority to the defending player - if (toDeclareBlockers) { + if (!combat.getAttackers().isEmpty()) { + List attackedTarget = Lists.newArrayList(); + for (final Card c : combat.getAttackers()) { + attackedTarget.add(combat.getDefenderByAttacker(c)); + } + final Map runParams = Maps.newHashMap(); + runParams.put("Attackers", combat.getAttackers()); + runParams.put("AttackingPlayer", combat.getAttackingPlayer()); + runParams.put("AttackedTarget", attackedTarget); + game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false); + } + + for (final Card c : combat.getAttackers()) { + CombatUtil.checkDeclaredAttacker(game, c, combat); + } + + game.getTriggerHandler().resetActiveTriggers(); + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + + // Gracefully proceed to Declare Blockers, giving priority to the defending player, + // but only if the stack is empty (otherwise the game will crash). + game.getStack().addAllTriggeredAbilitiesToStack(); + if (toDeclareBlockers && game.getStack().isEmpty()) { game.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DECLARE_BLOCKERS); } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 68a98f2ba61..32c1ca119a2 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -137,6 +137,16 @@ public class TriggerHandler { suppressedModes.add(mode); } + public final void setSuppressAllTriggers(final boolean suppress) { + for (TriggerType t : TriggerType.values()) { + if (suppress) { + suppressMode(t); + } else { + clearSuppression(t); + } + } + } + public final void clearSuppression(final TriggerType mode) { suppressedModes.remove(mode); }