Merge branch 'new-cards-2' into 'master'

Combat Calligrapher

Closes #1795

See merge request core-developers/forge!4462
This commit is contained in:
Michael Kamensky
2021-04-11 19:03:57 +00:00
6 changed files with 40 additions and 52 deletions

View File

@@ -3,23 +3,12 @@ package forge.ai;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.*;
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.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import com.google.common.collect.*;
import org.apache.commons.lang3.StringUtils; 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.StaticData;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.card.MagicColor; import forge.card.MagicColor;
@@ -769,23 +758,10 @@ public abstract class GameState {
} }
game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap)); game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap));
if (!combat.getAttackers().isEmpty()) {
List<GameEntity> attackedTarget = Lists.newArrayList();
for (final Card c : combat.getAttackers()) {
attackedTarget.add(combat.getDefenderByAttacker(c));
}
final Map<AbilityKey, Object> 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()) { for (final Card c : combat.getAttackers()) {
CombatUtil.checkDeclaredAttacker(game, c, combat); CombatUtil.checkDeclaredAttacker(game, c, combat, false);
} }
game.getTriggerHandler().resetActiveTriggers();
game.updateCombatForView(); game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged()); game.fireEvent(new GameEventCombatChanged());

View File

@@ -352,17 +352,19 @@ public class CombatUtil {
* @param c * @param c
* a {@link forge.game.card.Card} object. * 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 // Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.newMap(); if (triggers) {
runParams.put(AbilityKey.Attacker, c); final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
final List<Card> otherAttackers = combat.getAttackers(); runParams.put(AbilityKey.Attacker, c);
otherAttackers.remove(c); final List<Card> otherAttackers = combat.getAttackers();
runParams.put(AbilityKey.OtherAttackers, otherAttackers); otherAttackers.remove(c);
runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c)); runParams.put(AbilityKey.OtherAttackers, otherAttackers);
runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c)); runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c));
runParams.put(AbilityKey.Defenders, combat.getDefenders()); runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c));
game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false); runParams.put(AbilityKey.Defenders, combat.getDefenders());
game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false);
}
c.getDamageHistory().setCreatureAttackedThisCombat(true); c.getDamageHistory().setCreatureAttackedThisCombat(true);
c.getDamageHistory().clearNotAttackedSinceLastUpkeepOf(); c.getDamageHistory().clearNotAttackedSinceLastUpkeepOf();

View File

@@ -17,19 +17,11 @@
*/ */
package forge.game.phase; package forge.game.phase;
import java.util.HashSet; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import com.google.common.collect.*;
import org.apache.commons.lang3.time.StopWatch; 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.card.mana.ManaCost;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
@@ -621,9 +613,16 @@ public class PhaseHandler implements java.io.Serializable {
// fire AttackersDeclared trigger // fire AttackersDeclared trigger
if (!combat.getAttackers().isEmpty()) { if (!combat.getAttackers().isEmpty()) {
List<GameEntity> attackedTarget = Lists.newArrayList(); List<GameEntity> attackedTarget = new ArrayList<>();
for (final Card c : combat.getAttackers()) { for (GameEntity ge : combat.getDefenders()) {
attackedTarget.add(combat.getDefenderByAttacker(c)); if (!combat.getAttackersOf(ge).isEmpty()) {
final Map<AbilityKey, Object> 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<AbilityKey, Object> runParams = AbilityKey.newMap(); final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Attackers, combat.getAttackers()); runParams.put(AbilityKey.Attackers, combat.getAttackers());
@@ -633,7 +632,7 @@ public class PhaseHandler implements java.io.Serializable {
} }
for (final Card c : combat.getAttackers()) { for (final Card c : combat.getAttackers()) {
CombatUtil.checkDeclaredAttacker(game, c, combat); CombatUtil.checkDeclaredAttacker(game, c, combat, true);
} }
game.getTriggerHandler().resetActiveTriggers(); game.getTriggerHandler().resetActiveTriggers();

View File

@@ -22,6 +22,7 @@ public enum TriggerType {
AttackerBlockedOnce(TriggerAttackerBlockedOnce.class), AttackerBlockedOnce(TriggerAttackerBlockedOnce.class),
AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class), AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class),
AttackersDeclared(TriggerAttackersDeclared.class), AttackersDeclared(TriggerAttackersDeclared.class),
AttackersDeclaredOneTarget(TriggerAttackersDeclared.class),
AttackerUnblocked(TriggerAttackerUnblocked.class), AttackerUnblocked(TriggerAttackerUnblocked.class),
AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class), AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class),
Attacks(TriggerAttacks.class), Attacks(TriggerAttacks.class),

View File

@@ -3,7 +3,7 @@ ManaCost:1 W
Types:Artifact Equipment Types:Artifact Equipment
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Equipped creature gets +2/+2. 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. 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 SVar:HasAttackEffect:TRUE
K:Equip:4 K:Equip:4
DeckHas:Ability$Token DeckHas:Ability$Token

View File

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