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

This commit is contained in:
Agetian
2017-08-27 18:17:21 +00:00
parent 1d19f56de8
commit d0579b5f75
3 changed files with 59 additions and 2 deletions

View File

@@ -7,7 +7,9 @@ import forge.game.card.Card;
import forge.game.combat.AttackingBand; import forge.game.combat.AttackingBand;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.event.GameEventCombatChanged; import forge.game.event.GameEventCombatChanged;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions; import forge.game.spellability.TargetRestrictions;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
@@ -38,17 +40,29 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
for (final Card c : getTargetCards(sa)) { for (final Card c : getTargetCards(sa)) {
if ((tgt == null) || c.canBeTargetedBy(sa)) { if ((tgt == null) || c.canBeTargetedBy(sa)) {
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
final GameEntity orginalDefender = combat.getDefenderByAttacker(c); final GameEntity originalDefender = combat.getDefenderByAttacker(c);
final FCollectionView<GameEntity> defs = combat.getDefenders(); final FCollectionView<GameEntity> defs = combat.getDefenders();
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa,
"Choose which defender to attack with " + c, false); "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); AttackingBand ab = combat.getBandOfAttacker(c);
if (ab != null) { if (ab != null) {
combat.unregisterAttacker(c, ab); combat.unregisterAttacker(c, ab);
ab.removeAttacker(c); ab.removeAttacker(c);
} }
combat.addAttacker(c, defender); 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; isCombatChanged = true;
} }
} }

View File

@@ -24,6 +24,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import forge.game.Game;
import forge.game.spellability.SpellAbilityStackInstance;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function; 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 // removes references to this defender from all indices and orders

View File

@@ -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) { public boolean compareToSpellAbility(SpellAbility sa) {
// Compare my target choices to the SA passed in // Compare my target choices to the SA passed in
// TODO? Compare other data points in the SI to the passed SpellAbility for confirmation // TODO? Compare other data points in the SI to the passed SpellAbility for confirmation