From 9b1d500073f1666b648b099a96b938c487d8fea2 Mon Sep 17 00:00:00 2001 From: apantel <> Date: Sat, 1 Jun 2019 00:05:41 -0400 Subject: [PATCH] BlockEffect and related cards --- .../main/java/forge/game/ability/ApiType.java | 1 + .../game/ability/effects/BlockEffect.java | 107 ++++++++++++++++++ .../main/java/forge/game/combat/Combat.java | 48 ++++---- .../res/cardsfolder/b/balduvian_warlord.txt | 9 ++ forge-gui/res/cardsfolder/f/false_orders.txt | 8 ++ 5 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/BlockEffect.java create mode 100644 forge-gui/res/cardsfolder/b/balduvian_warlord.txt create mode 100644 forge-gui/res/cardsfolder/f/false_orders.txt diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index 4fa8e935dbb..5596f94298d 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -27,6 +27,7 @@ public enum ApiType { BecomeMonarch (BecomeMonarchEffect.class), BecomesBlocked (BecomesBlockedEffect.class), BidLife (BidLifeEffect.class), + Block (BlockEffect.class), Bond (BondEffect.class), Branch (BranchEffect.class), ChangeCombatants (ChangeCombatantsEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/BlockEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BlockEffect.java new file mode 100644 index 00000000000..38711f7fa75 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/BlockEffect.java @@ -0,0 +1,107 @@ +package forge.game.ability.effects; + +import com.google.common.collect.Maps; +import forge.game.ability.AbilityUtils; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.combat.Combat; +import forge.game.event.GameEventCombatChanged; +import forge.game.Game; +import forge.game.spellability.SpellAbility; +import forge.game.trigger.TriggerType; + +import java.util.*; + +public class BlockEffect extends SpellAbilityEffect { + + @Override + public void resolve(SpellAbility sa) { + final Card host = sa.getHostCard(); + final Game game = host.getGame(); + final Combat combat = game.getPhaseHandler().getCombat(); + + List attackers = new ArrayList(); + if (sa.hasParam("DefinedAttacker")) { + for (final Card attacker : AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa)) { + if (combat.isAttacking(attacker)) attackers.add(attacker); + } + } + + List blockers = new ArrayList(); + if (sa.hasParam("DefinedBlocker")) { + for (final Card blocker : AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedBlocker"), sa)) { + if (blocker.isCreature()) blockers.add(blocker); + } + } + + if (attackers.size() == 0 || blockers.size() == 0) return; + + for (final Card attacker : attackers) { + final boolean wasBlocked = combat.isBlocked(attacker); + + for (final Card blocker : blockers) { + // If the attacker was blocked, this covers adding the blocker to the damage assignment + combat.addBlocker(attacker, blocker); + combat.orderAttackersForDamageAssignment(blocker); + + blocker.addBlockedThisTurn(attacker); + attacker.addBlockedByThisTurn(blocker); + + Map runParams = Maps.newHashMap(); + runParams.put("Attacker", attacker); + runParams.put("Blocker", blocker); + game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedByCreature, runParams, false); + + runParams = Maps.newHashMap(); + runParams.put("Blocker", blocker); + runParams.put("Attackers", attacker); + game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false); + } + + attacker.getDamageHistory().setCreatureGotBlockedThisCombat(true); + if (!wasBlocked) { + final Map runParams = Maps.newHashMap(); + runParams.put("Attacker", attacker); + runParams.put("Blockers", blockers); + runParams.put("NumBlockers", blockers.size()); + runParams.put("Defender", combat.getDefenderByAttacker(attacker)); + runParams.put("DefendingPlayer", combat.getDefenderPlayerByAttacker(attacker)); + game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false); + + combat.orderBlockersForDamageAssignment(attacker, new CardCollection(blockers)); + } + } + + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + + } + + @Override + protected String getStackDescription(SpellAbility sa) { + final Card host = sa.getHostCard(); + final StringBuilder sb = new StringBuilder(); + + // end standard pre- + + List attackers = new ArrayList(); + if (sa.hasParam("DefinedAttacker")) { + for (final Card attacker : AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa)) { + attackers.add(attacker.toString()); + } + } + + List blockers = new ArrayList(); + if (sa.hasParam("DefinedBlocker")) { + for (final Card blocker : AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedBlocker"), sa)) { + blockers.add(blocker.toString()); + } + } + + sb.append(String.join(", ", blockers)).append(" block ").append(String.join(", ", attackers)); + + return sb.toString(); + } + +} diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 7d636b92b91..bc5441689ea 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -449,7 +449,7 @@ public class Combat { for (Card attacker : band.getAttackers()) { if (blockers.size() <= 1) { - blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blockers)); + orderBlockersForDamageAssignment(attacker, new CardCollection(blockers)); } else { // process it a bit later blockersNeedManualOrdering.add(Pair.of(attacker, new CardCollection(blockers))); // we know there's a list @@ -459,27 +459,37 @@ public class Combat { // brought this out of iteration on bands to avoid concurrency problems for (Pair pair : blockersNeedManualOrdering) { - // Damage Ordering needs to take cards like Melee into account, is that happening? - CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlockers(pair.getLeft(), pair.getRight()); // we know there's a list - blockersOrderedForDamageAssignment.put(pair.getLeft(), orderedBlockers); - - // Display the chosen order of blockers in the log - // TODO: this is best done via a combat panel update - StringBuilder sb = new StringBuilder(); - sb.append(playerWhoAttacks.getName()); - sb.append(" has ordered blockers for "); - sb.append(pair.getLeft()); - sb.append(": "); - for (int i = 0; i < orderedBlockers.size(); i++) { - sb.append(orderedBlockers.get(i)); - if (i != orderedBlockers.size() - 1) { - sb.append(", "); - } - } - playerWhoAttacks.getGame().getGameLog().add(GameLogEntryType.COMBAT, sb.toString()); + orderBlockersForDamageAssignment(pair.getLeft(), pair.getRight()); } } + /** If there are multiple blockers, the Attacker declares the Assignment Order */ + public void orderBlockersForDamageAssignment(Card attacker, CardCollection blockers) { // this method performs controller's role + if (blockers.size() <= 1) { + blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blockers)); + return; + } + + // Damage Ordering needs to take cards like Melee into account, is that happening? + CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlockers(attacker, blockers); // we know there's a list + blockersOrderedForDamageAssignment.put(attacker, orderedBlockers); + + // Display the chosen order of blockers in the log + // TODO: this is best done via a combat panel update + StringBuilder sb = new StringBuilder(); + sb.append(playerWhoAttacks.getName()); + sb.append(" has ordered blockers for "); + sb.append(attacker); + sb.append(": "); + for (int i = 0; i < orderedBlockers.size(); i++) { + sb.append(orderedBlockers.get(i)); + if (i != orderedBlockers.size() - 1) { + sb.append(", "); + } + } + playerWhoAttacks.getGame().getGameLog().add(GameLogEntryType.COMBAT, sb.toString()); + } + /** * Add a blocker to the damage assignment order of an attacker. The * relative order of creatures already blocking the attacker may not be diff --git a/forge-gui/res/cardsfolder/b/balduvian_warlord.txt b/forge-gui/res/cardsfolder/b/balduvian_warlord.txt new file mode 100644 index 00000000000..513b7ede0b1 --- /dev/null +++ b/forge-gui/res/cardsfolder/b/balduvian_warlord.txt @@ -0,0 +1,9 @@ +Name:Balduvian Warlord +ManaCost:3 R +Types:Creature Human Barbarian +PT:3/2 +A:AB$ RemoveFromCombat | Cost$ T | ActivationPhases$ Declare Blockers | ValidTgts$ Creature.blocking | Defined$ Targeted | UnblockCreaturesBlockedOnlyBy$ Targeted | SubAbility$ ChooseAttacker | SpellDescription$ Remove target blocking creature from combat. Creatures it was blocking that hadn’t become blocked by another creature this combat become unblocked. +SVar:ChooseAttacker:DB$ ChooseCard | Defined$ You | Choices$ Creature.attacking | RememberChosen$ True | | Mandatory$ True | SubAbility$ Block +SVar:Block:DB$ Block | DefinedAttacker$ Remembered | DefinedBlocker$ ParentTarget | SpellDescription$ Then it blocks an attacking creature of your choice. Activate this ability only during the declare blockers step. +SVar:Picture:http://resources.wizards.com/magic/cards/tsb/en-us/card108906.jpg +Oracle:{T}: Remove target blocking creature from combat. Creatures it was blocking that hadn’t become blocked by another creature this combat become unblocked, then it blocks an attacking creature of your choice. Activate this ability only during the declare blockers step. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/false_orders.txt b/forge-gui/res/cardsfolder/f/false_orders.txt new file mode 100644 index 00000000000..ebdbf74df2b --- /dev/null +++ b/forge-gui/res/cardsfolder/f/false_orders.txt @@ -0,0 +1,8 @@ +Name:False Orders +ManaCost:R +Types:Instant +Text:Cast CARDNAME only during the declare blockers step. +A:SP$ RemoveFromCombat | Cost$ R | ActivationPhases$ Declare Blockers | ValidTgts$ Creature.DefendingPlayerCtrl | Defined$ Targeted | UnblockCreaturesBlockedOnlyBy$ Targeted | SubAbility$ ChooseAttacker | SpellDescription$ Remove target creature defending player controls from combat. Creatures it was blocking that had become blocked by only that creature this combat become unblocked. +SVar:ChooseAttacker:DB$ ChooseCard | Defined$ You | Choices$ Creature.attacking | RememberChosen$ True | MinAmount$ 0 | SubAbility$ Block +SVar:Block:DB$ Block | DefinedAttacker$ Remembered | DefinedBlocker$ ParentTarget | SpellDescription$ You may have it block an attacking creature of your choice. +Oracle:Cast this spell only during the declare blockers step.\nRemove target creature defending player controls from combat. Creatures it was blocking that had become blocked by only that creature this combat become unblocked. You may have it block an attacking creature of your choice.