diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index a1519659b0f..4f42e2b7579 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -3,23 +3,12 @@ package forge.ai; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; +import com.google.common.collect.*; import org.apache.commons.lang3.StringUtils; -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.card.MagicColor; @@ -769,23 +758,10 @@ public abstract class GameState { } game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap)); - if (!combat.getAttackers().isEmpty()) { - List attackedTarget = Lists.newArrayList(); - for (final Card c : combat.getAttackers()) { - attackedTarget.add(combat.getDefenderByAttacker(c)); - } - final Map runParams = Maps.newEnumMap(AbilityKey.class); - runParams.put(AbilityKey.Attackers, combat.getAttackers()); - runParams.put(AbilityKey.AttackingPlayer, combat.getAttackingPlayer()); - runParams.put(AbilityKey.AttackedTarget, attackedTarget); - game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false); - } - for (final Card c : combat.getAttackers()) { - CombatUtil.checkDeclaredAttacker(game, c, combat); + CombatUtil.checkDeclaredAttacker(game, c, combat, false); } - game.getTriggerHandler().resetActiveTriggers(); game.updateCombatForView(); game.fireEvent(new GameEventCombatChanged()); diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index 75583a7bfeb..5ae96a0cc7c 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -352,17 +352,19 @@ public class CombatUtil { * @param c * a {@link forge.game.card.Card} object. */ - public static void checkDeclaredAttacker(final Game game, final Card c, final Combat combat) { + public static void checkDeclaredAttacker(final Game game, final Card c, final Combat combat, boolean triggers) { // Run triggers - final Map runParams = AbilityKey.newMap(); - runParams.put(AbilityKey.Attacker, c); - final List otherAttackers = combat.getAttackers(); - otherAttackers.remove(c); - runParams.put(AbilityKey.OtherAttackers, otherAttackers); - runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c)); - runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c)); - runParams.put(AbilityKey.Defenders, combat.getDefenders()); - game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false); + if (triggers) { + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.Attacker, c); + final List otherAttackers = combat.getAttackers(); + otherAttackers.remove(c); + runParams.put(AbilityKey.OtherAttackers, otherAttackers); + runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c)); + runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c)); + runParams.put(AbilityKey.Defenders, combat.getDefenders()); + game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false); + } c.getDamageHistory().setCreatureAttackedThisCombat(true); c.getDamageHistory().clearNotAttackedSinceLastUpkeepOf(); diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index 61355f6b54e..72c509580c3 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -17,19 +17,11 @@ */ package forge.game.phase; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; +import java.util.*; +import com.google.common.collect.*; import org.apache.commons.lang3.time.StopWatch; -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.card.mana.ManaCost; import forge.game.Game; import forge.game.GameEntity; @@ -621,9 +613,16 @@ public class PhaseHandler implements java.io.Serializable { // fire AttackersDeclared trigger if (!combat.getAttackers().isEmpty()) { - List attackedTarget = Lists.newArrayList(); - for (final Card c : combat.getAttackers()) { - attackedTarget.add(combat.getDefenderByAttacker(c)); + List attackedTarget = new ArrayList<>(); + for (GameEntity ge : combat.getDefenders()) { + if (!combat.getAttackersOf(ge).isEmpty()) { + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.Attackers, combat.getAttackersOf(ge)); + runParams.put(AbilityKey.AttackingPlayer, combat.getAttackingPlayer()); + runParams.put(AbilityKey.AttackedTarget, ge); + attackedTarget.add(ge); + game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclaredOneTarget, runParams, false); + } } final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Attackers, combat.getAttackers()); @@ -633,7 +632,7 @@ public class PhaseHandler implements java.io.Serializable { } for (final Card c : combat.getAttackers()) { - CombatUtil.checkDeclaredAttacker(game, c, combat); + CombatUtil.checkDeclaredAttacker(game, c, combat, true); } game.getTriggerHandler().resetActiveTriggers(); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index 1bc132d9836..606da2ee827 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -22,6 +22,7 @@ public enum TriggerType { AttackerBlockedOnce(TriggerAttackerBlockedOnce.class), AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class), AttackersDeclared(TriggerAttackersDeclared.class), + AttackersDeclaredOneTarget(TriggerAttackersDeclared.class), AttackerUnblocked(TriggerAttackerUnblocked.class), AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class), Attacks(TriggerAttacks.class), diff --git a/forge-gui/res/cardsfolder/s/seraphic_greatsword.txt b/forge-gui/res/cardsfolder/s/seraphic_greatsword.txt index ddd96460f34..30b0ac11394 100755 --- a/forge-gui/res/cardsfolder/s/seraphic_greatsword.txt +++ b/forge-gui/res/cardsfolder/s/seraphic_greatsword.txt @@ -3,7 +3,7 @@ ManaCost:1 W Types:Artifact Equipment S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Equipped creature gets +2/+2. T:Mode$ Attacks | ValidCard$ Card.AttachedBy | Attacked$ Player.withMostLife | Execute$ TrigToken | TriggerDescription$ Whenever equipped creature attacks the player with the most life or tied for most life, create a 4/4 white Angel creature token with flying that's tapped and attacking that player. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_4_4_angel_flying | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ True +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_4_4_angel_flying | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ TriggeredDefender SVar:HasAttackEffect:TRUE K:Equip:4 DeckHas:Ability$Token diff --git a/forge-gui/res/cardsfolder/upcoming/combat_calligrapher.txt b/forge-gui/res/cardsfolder/upcoming/combat_calligrapher.txt new file mode 100644 index 00000000000..7dc4a6c82a6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/combat_calligrapher.txt @@ -0,0 +1,10 @@ +Name:Combat Calligrapher +ManaCost:3 W +Types:Creature Bird Cleric +PT:3/3 +K:Flying +S:Mode$ CantAttack | ValidCard$ Creature.Inkling | Target$ You,Planeswalker.YouCtrl | Description$ Inklings can't attack you or planeswalkers you control. +T:Mode$ AttackersDeclaredOneTarget | AttackedTarget$ Opponent | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent. +SVar:TrigToken:DB$ Token | TokenScript$ wb_2_1_inkling_flying | TokenOwner$ TriggeredAttackingPlayer | TokenTapped$ True | TokenAttacking$ TriggeredAttackedTarget +DeckHas:Ability$Token +Oracle:Flying\nInklings can't attack you or planeswalkers you control.\nWhenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent.