From d0579b5f75884e81773fa375342f4d597bdd9a90 Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 27 Aug 2017 18:17:21 +0000 Subject: [PATCH] - ChangeCombatantsEffect: attempt to account for the new rule for Ulamog, the Ceaseless Hunger + Portal Mage interaction in multiplayer games. Currently achieved by rigging the triggering info on the stack instance, which may not be optimal. Feel free to propose a better solution. --- .../effects/ChangeCombatantsEffect.java | 18 +++++++++++++-- .../main/java/forge/game/combat/Combat.java | 20 ++++++++++++++++ .../SpellAbilityStackInstance.java | 23 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java index 42ae107ffa6..19e589fbc54 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java @@ -7,7 +7,9 @@ import forge.game.card.Card; import forge.game.combat.AttackingBand; import forge.game.combat.Combat; import forge.game.event.GameEventCombatChanged; +import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.TargetRestrictions; import forge.util.collect.FCollectionView; @@ -38,17 +40,29 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect { for (final Card c : getTargetCards(sa)) { if ((tgt == null) || c.canBeTargetedBy(sa)) { final Combat combat = game.getCombat(); - final GameEntity orginalDefender = combat.getDefenderByAttacker(c); + final GameEntity originalDefender = combat.getDefenderByAttacker(c); final FCollectionView defs = combat.getDefenders(); final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false); - if (orginalDefender != null && !orginalDefender.equals(defender)) { + if (originalDefender != null && !originalDefender.equals(defender)) { AttackingBand ab = combat.getBandOfAttacker(c); if (ab != null) { combat.unregisterAttacker(c, ab); ab.removeAttacker(c); } combat.addAttacker(c, defender); + // retarget triggers to the new defender (e.g. Ulamog, Ceaseless Hunger + Portal Mage) + for (SpellAbilityStackInstance si : game.getStack()) { + if (si.isTrigger() && c.equals(si.getSourceCard()) + && si.getTriggeringObject("Attacker") != null) { + si.addTriggeringObject("OriginalDefender", originalDefender); + if (defender instanceof Player) { + si.updateTriggeringObject("DefendingPlayer", defender); + } else if (defender instanceof Card) { + si.updateTriggeringObject("DefendingPlayer", ((Card)defender).getController()); + } + } + } isCombatChanged = true; } } 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 33b7aa9d4df..d4ccebcd13b 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import forge.game.Game; +import forge.game.spellability.SpellAbilityStackInstance; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Function; @@ -541,6 +543,24 @@ public class Combat { } } } + + // restore the original defender in case it was changed before the creature was + // removed from combat but before the trigger resolved (e.g. Ulamog, the Ceaseless + // Hunger + Portal Mage + Unsummon) + Game game = c.getGame(); + for (SpellAbilityStackInstance si : game.getStack()) { + if (si.isTrigger() && c.equals(si.getSourceCard())) { + GameEntity origDefender = (GameEntity)si.getTriggeringObject("OriginalDefender"); + if (origDefender != null) { + si.updateTriggeringObject("Defender", origDefender); + if (origDefender instanceof Player) { + si.updateTriggeringObject("DefendingPlayer", origDefender); + } else if (origDefender instanceof Card) { + si.updateTriggeringObject("DefendingPlayer", ((Card)origDefender).getController()); + } + } + } + } } // removes references to this defender from all indices and orders diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java index b26c764888e..f68cbaa2650 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java @@ -356,6 +356,29 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView { } } + public boolean addTriggeringObject(String trigObj, Object value) { + if (!triggeringObjects.containsKey(trigObj)) { + triggeringObjects.put(trigObj, value); + return true; + } + return false; + } + + public boolean updateTriggeringObject(String trigObj, Object value) { + if (triggeringObjects.containsKey(trigObj)) { + triggeringObjects.replace(trigObj, value); + return true; + } + return false; + } + + public Object getTriggeringObject(String trigObj) { + if (triggeringObjects.containsKey(trigObj)) { + return triggeringObjects.get(trigObj); + } + return null; + } + public boolean compareToSpellAbility(SpellAbility sa) { // Compare my target choices to the SA passed in // TODO? Compare other data points in the SI to the passed SpellAbility for confirmation