diff --git a/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java index a292825e681..cd4d91468bd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java @@ -12,7 +12,6 @@ import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.Localizer; @@ -36,25 +35,26 @@ public class ActivateAbilityEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - final TargetRestrictions tgt = sa.getTargetRestrictions(); final boolean isManaAb = sa.hasParam("ManaAbility"); // TODO: improve ai and fix corner cases for (final Player p : getTargetPlayers(sa)) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - List list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card")); - for (Card c : list) { - List possibleAb = Lists.newArrayList(c.getAllPossibleAbilities(p, true)); - if (isManaAb) { - possibleAb.retainAll((FCollection)c.getManaAbilities()); - } - if (possibleAb.isEmpty()) { - continue; - } - SpellAbility manaAb = p.getController().chooseSingleSpellForEffect( - possibleAb, sa, Localizer.getInstance().getMessage("lblChooseManaAbility"), ImmutableMap.of()); - p.getController().playChosenSpellAbility(manaAb); + if (!p.isInGame()) { + continue; + } + + List list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card")); + for (Card c : list) { + List possibleAb = Lists.newArrayList(c.getAllPossibleAbilities(p, true)); + if (isManaAb) { + possibleAb.retainAll((FCollection)c.getManaAbilities()); } + if (possibleAb.isEmpty()) { + continue; + } + SpellAbility manaAb = p.getController().chooseSingleSpellForEffect( + possibleAb, sa, Localizer.getInstance().getMessage("lblChooseManaAbility"), ImmutableMap.of()); + p.getController().playChosenSpellAbility(manaAb); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java index 45a6b70afa0..265f490143f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java @@ -1,7 +1,5 @@ package forge.game.ability.effects; -import java.util.List; - import forge.game.Game; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; @@ -15,6 +13,7 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; +import forge.util.Lang; import forge.util.Localizer; public class AddTurnEffect extends SpellAbilityEffect { @@ -24,11 +23,7 @@ public class AddTurnEffect extends SpellAbilityEffect { final StringBuilder sb = new StringBuilder(); final int numTurns = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa); - List tgtPlayers = getTargetPlayers(sa); - - for (final Player player : tgtPlayers) { - sb.append(player).append(" "); - } + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); sb.append("takes "); sb.append(numTurns > 1 ? numTurns : "an"); @@ -45,29 +40,28 @@ public class AddTurnEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final int numTurns = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa); - List tgtPlayers = getTargetPlayers(sa); - - for (final Player p : tgtPlayers) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - for (int i = 0; i < numTurns; i++) { - ExtraTurn extra = p.getGame().getPhaseHandler().addExtraTurn(p); - if (sa.hasParam("ExtraTurnDelayedTrigger")) { - final Trigger delTrig = TriggerHandler.parseTrigger(sa.getSVar(sa.getParam("ExtraTurnDelayedTrigger")), sa.getHostCard(), true); - SpellAbility overridingSA = AbilityFactory.getAbility(sa.getSVar(sa.getParam("ExtraTurnDelayedTriggerExcute")), sa.getHostCard()); - overridingSA.setActivatingPlayer(sa.getActivatingPlayer()); - delTrig.setOverridingAbility(overridingSA); - delTrig.setSpawningAbility(sa.copy(sa.getHostCard(), sa.getActivatingPlayer(), true)); - extra.addTrigger(delTrig); - } - if (sa.hasParam("SkipUntap")) { - extra.setSkipUntapSA(sa); - } - if (sa.hasParam("NoSchemes")) { - extra.setCantSetSchemesInMotionSA(sa); - } - if (sa.hasParam("ShowMessage")) { - p.getGame().getAction().notifyOfValue(sa, p, Localizer.getInstance().getMessage("lblPlayerTakesExtraTurn", p.toString()), null); - } + for (final Player p : getTargetPlayers(sa)) { + if (!p.isInGame()) { + continue; + } + for (int i = 0; i < numTurns; i++) { + ExtraTurn extra = p.getGame().getPhaseHandler().addExtraTurn(p); + if (sa.hasParam("ExtraTurnDelayedTrigger")) { + final Trigger delTrig = TriggerHandler.parseTrigger(sa.getSVar(sa.getParam("ExtraTurnDelayedTrigger")), sa.getHostCard(), true); + SpellAbility overridingSA = AbilityFactory.getAbility(sa.getSVar(sa.getParam("ExtraTurnDelayedTriggerExcute")), sa.getHostCard()); + overridingSA.setActivatingPlayer(sa.getActivatingPlayer()); + delTrig.setOverridingAbility(overridingSA); + delTrig.setSpawningAbility(sa.copy(sa.getHostCard(), sa.getActivatingPlayer(), true)); + extra.addTrigger(delTrig); + } + if (sa.hasParam("SkipUntap")) { + extra.setSkipUntapSA(sa); + } + if (sa.hasParam("NoSchemes")) { + extra.setCantSetSchemesInMotionSA(sa); + } + if (sa.hasParam("ShowMessage")) { + p.getGame().getAction().notifyOfValue(sa, p, Localizer.getInstance().getMessage("lblPlayerTakesExtraTurn", p.toString()), null); } } } 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 e09742febc0..4d138cad810 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 @@ -18,7 +18,6 @@ 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.CardTranslation; import forge.util.Localizer; import forge.util.collect.FCollection; @@ -42,7 +41,6 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect { boolean isCombatChanged = false; final Player activator = sa.getActivatingPlayer(); final Game game = activator.getGame(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); // TODO: may expand this effect for defined blocker (False Orders, General Jarkeld, Sorrow's Path, Ydwen Efreet) for (final Card c : getTargetCards(sa)) { String cardString = CardTranslation.getTranslatedName(c.getName()) + " (" + c.getId() + ")"; @@ -51,38 +49,37 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect { Localizer.getInstance().getMessage("lblChangeCombatantOption", cardString), null)) { continue; } - if ((tgt == null) || c.canBeTargetedBy(sa)) { - final Combat combat = game.getCombat(); - final GameEntity originalDefender = combat.getDefenderByAttacker(c); - final FCollection defs = new FCollection<>(); - defs.addAll(sa.hasParam("PlayerOnly") ? combat.getDefendingPlayers() : combat.getDefenders()); - String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", cardString); - Map params = Maps.newHashMap(); - params.put("Attacker", c); + final Combat combat = game.getCombat(); + final GameEntity originalDefender = combat.getDefenderByAttacker(c); + final FCollection defs = new FCollection<>(); + defs.addAll(sa.hasParam("PlayerOnly") ? combat.getDefendingPlayers() : combat.getDefenders()); - final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, title, false, params); - 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(AbilityKey.Attacker) != null) { - si.addTriggeringObject(AbilityKey.OriginalDefender, originalDefender); - if (defender instanceof Player) { - si.updateTriggeringObject(AbilityKey.DefendingPlayer, defender); - } else if (defender instanceof Card) { - si.updateTriggeringObject(AbilityKey.DefendingPlayer, ((Card)defender).getController()); - } + String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", cardString); + Map params = Maps.newHashMap(); + params.put("Attacker", c); + + final GameEntity defender = activator.getController().chooseSingleEntityForEffect(defs, sa, title, false, params); + 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(AbilityKey.Attacker) != null) { + si.addTriggeringObject(AbilityKey.OriginalDefender, originalDefender); + if (defender instanceof Player) { + si.updateTriggeringObject(AbilityKey.DefendingPlayer, defender); + } else if (defender instanceof Card) { + si.updateTriggeringObject(AbilityKey.DefendingPlayer, ((Card)defender).getController()); } } - isCombatChanged = true; } + isCombatChanged = true; } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java index a435520bf40..44ade4d4d8d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java @@ -19,7 +19,6 @@ import forge.game.card.CardCollection; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Aggregates; import forge.util.Localizer; @@ -41,9 +40,6 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final List tgtPlayers = getTargetPlayers(sa); - String valid = "Card"; String validDesc = null; String message = null; @@ -68,102 +64,103 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { } } - for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - String chosen = ""; - //This section was used for Momir Avatar, which no longer uses it - commented out 7/28/2021 - //if (randomChoice) { - //String numericAmount = "X"; - //final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : - // AbilityUtils.calculateAmount(host, numericAmount, sa); - // Momir needs PaperCard - //Collection cards = StaticData.instance().getCommonCards().getUniqueCards(); - //Predicate cpp = Predicates.and( - // Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES), - // Predicates.compose(CardRulesPredicates.cmc(ComparableOp.EQUALS, validAmount), PaperCard.FN_GET_RULES)); - //cards = Lists.newArrayList(Iterables.filter(cards, cpp)); - //if (!cards.isEmpty()) { chosen = Aggregates.random(cards).getName(); - //} else { - // chosen = ""; - //} - if (chooseFromDefined) { - CardCollection choices = AbilityUtils.getDefinedCards(host, sa.getParam("ChooseFromDefinedCards"), sa); - choices = CardLists.getValidCards(choices, valid, host.getController(), host, sa); - List faces = new ArrayList<>(); - // get Card - for (final Card c : choices) { - final CardRules rules = c.getRules(); - if (faces.contains(rules.getMainPart())) - continue; - faces.add(rules.getMainPart()); - // Alhammarret only allows Split for other faces - if (rules.getSplitType() == CardSplitType.Split) { - faces.add(rules.getOtherPart()); - } + for (final Player p : getTargetPlayers(sa)) { + if (!p.isInGame()) { + continue; + } + String chosen = ""; + //This section was used for Momir Avatar, which no longer uses it - commented out 7/28/2021 + //if (randomChoice) { + //String numericAmount = "X"; + //final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : + // AbilityUtils.calculateAmount(host, numericAmount, sa); + // Momir needs PaperCard + //Collection cards = StaticData.instance().getCommonCards().getUniqueCards(); + //Predicate cpp = Predicates.and( + // Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES), + // Predicates.compose(CardRulesPredicates.cmc(ComparableOp.EQUALS, validAmount), PaperCard.FN_GET_RULES)); + //cards = Lists.newArrayList(Iterables.filter(cards, cpp)); + //if (!cards.isEmpty()) { chosen = Aggregates.random(cards).getName(); + //} else { + // chosen = ""; + //} + if (chooseFromDefined) { + CardCollection choices = AbilityUtils.getDefinedCards(host, sa.getParam("ChooseFromDefinedCards"), sa); + choices = CardLists.getValidCards(choices, valid, host.getController(), host, sa); + List faces = new ArrayList<>(); + // get Card + for (final Card c : choices) { + final CardRules rules = c.getRules(); + if (faces.contains(rules.getMainPart())) + continue; + faces.add(rules.getMainPart()); + // Alhammarret only allows Split for other faces + if (rules.getSplitType() == CardSplitType.Split) { + faces.add(rules.getOtherPart()); } - Collections.sort(faces); - chosen = p.getController().chooseCardName(sa, faces, message); - } else if (chooseFromList) { - String [] names = sa.getParam("ChooseFromList").split(","); - List faces = new ArrayList<>(); - for (String name : names) { - // Cardnames that include "," must use ";" instead in ChooseFromList$ (i.e. Tovolar; Dire Overlord) - name = name.replace(";", ","); - faces.add(StaticData.instance().getCommonCards().getFaceByName(name)); - } - if (randomChoice) { - chosen = Aggregates.random(faces).getName(); - } else { - chosen = p.getController().chooseCardName(sa, faces, message); - } - } else if (chooseFromOneTimeList) { - String [] names = sa.getParam("ChooseFromOneTimeList").split(","); - List faces = new ArrayList<>(); - for (String name : names) { - faces.add(StaticData.instance().getCommonCards().getFaceByName(name)); - } - chosen = p.getController().chooseCardName(sa, faces, message); - - // Remove chosen Name from List - StringBuilder sb = new StringBuilder(); - for (String name : names) { - if (chosen.equals(name)) continue; - if (sb.length() > 0) sb.append(','); - sb.append(name); - } - sa.putParam("ChooseFromOneTimeList", sb.toString()); + } + Collections.sort(faces); + chosen = p.getController().chooseCardName(sa, faces, message); + } else if (chooseFromList) { + String [] names = sa.getParam("ChooseFromList").split(","); + List faces = new ArrayList<>(); + for (String name : names) { + // Cardnames that include "," must use ";" instead in ChooseFromList$ (i.e. Tovolar; Dire Overlord) + name = name.replace(";", ","); + faces.add(StaticData.instance().getCommonCards().getFaceByName(name)); + } + if (randomChoice) { + chosen = Aggregates.random(faces).getName(); } else { - // use CardFace because you might name a alternate names - Predicate cpp = Predicates.alwaysTrue(); - if (sa.hasParam("ValidCards")) { - //Calculating/replacing this must happen before running valid in CardFacePredicates - if (valid.contains("ManaCost=")) { - if (valid.contains("ManaCost=Equipped")) { - String s = host.getEquipping().getManaCost().getShortString(); - valid = valid.replace("=Equipped", s); - } else if (valid.contains("ManaCost=Imprinted")) { - String s = host.getImprintedCards().getFirst().getManaCost().getShortString(); - valid = valid.replace("=Imprinted", s); - } - } - cpp = CardFacePredicates.valid(valid); - } - if (randomChoice) { - final Iterable cardsFromDb = StaticData.instance().getCommonCards().getAllFaces(); - final List cards = Lists.newArrayList(Iterables.filter(cardsFromDb, cpp)); - chosen = Aggregates.random(cards).getName(); - } else { - chosen = p.getController().chooseCardName(sa, cpp, valid, message); - } + chosen = p.getController().chooseCardName(sa, faces, message); } + } else if (chooseFromOneTimeList) { + String [] names = sa.getParam("ChooseFromOneTimeList").split(","); + List faces = new ArrayList<>(); + for (String name : names) { + faces.add(StaticData.instance().getCommonCards().getFaceByName(name)); + } + chosen = p.getController().chooseCardName(sa, faces, message); - host.setNamedCard(chosen); - if (!randomChoice) { - p.setNamedCard(chosen); + // Remove chosen Name from List + StringBuilder sb = new StringBuilder(); + for (String name : names) { + if (chosen.equals(name)) continue; + if (sb.length() > 0) sb.append(','); + sb.append(name); } - if (sa.hasParam("NoteFor")) { - p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen); + sa.putParam("ChooseFromOneTimeList", sb.toString()); + } else { + // use CardFace because you might name a alternate names + Predicate cpp = Predicates.alwaysTrue(); + if (sa.hasParam("ValidCards")) { + //Calculating/replacing this must happen before running valid in CardFacePredicates + if (valid.contains("ManaCost=")) { + if (valid.contains("ManaCost=Equipped")) { + String s = host.getEquipping().getManaCost().getShortString(); + valid = valid.replace("=Equipped", s); + } else if (valid.contains("ManaCost=Imprinted")) { + String s = host.getImprintedCards().getFirst().getManaCost().getShortString(); + valid = valid.replace("=Imprinted", s); + } + } + cpp = CardFacePredicates.valid(valid); } + if (randomChoice) { + final Iterable cardsFromDb = StaticData.instance().getCommonCards().getAllFaces(); + final List cards = Lists.newArrayList(Iterables.filter(cardsFromDb, cpp)); + chosen = Aggregates.random(cards).getName(); + } else { + chosen = p.getController().chooseCardName(sa, cpp, valid, message); + } + } + + host.setNamedCard(chosen); + if (!randomChoice) { + p.setNamedCard(chosen); + } + if (sa.hasParam("NoteFor")) { + p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java index 6ac1e42e252..f7734aad013 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java @@ -12,7 +12,6 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Localizer; import forge.util.MyRandom; @@ -46,35 +45,34 @@ public class ChooseNumberEffect extends SpellAbilityEffect { final String sMax = sa.getParamOrDefault("Max", "99"); final int max = AbilityUtils.calculateAmount(card, sMax, sa); - final List tgtPlayers = getTargetPlayers(sa); - final TargetRestrictions tgt = sa.getTargetRestrictions(); final Map chooseMap = Maps.newHashMap(); - for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - int chosen; - if (random) { - chosen = MyRandom.getRandom().nextInt((max - min) + 1) + min; - //TODO more useful notify for RepeatEach -> ChooseNumber with random - p.getGame().getAction().notifyOfValue(sa, p, Integer.toString(chosen), null); + for (final Player p : getTargetPlayers(sa)) { + if (!p.isInGame()) { + continue; + } + int chosen; + if (random) { + chosen = MyRandom.getRandom().nextInt((max - min) + 1) + min; + //TODO more useful notify for RepeatEach -> ChooseNumber with random + p.getGame().getAction().notifyOfValue(sa, p, Integer.toString(chosen), null); + } else { + String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : Localizer.getInstance().getMessage("lblChooseNumber"); + if (anyNumber) { + Integer value = p.getController().announceRequirements(sa, title); + chosen = value == null ? 0 : value; } else { - String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : Localizer.getInstance().getMessage("lblChooseNumber"); - if (anyNumber) { - Integer value = p.getController().announceRequirements(sa, title); - chosen = value == null ? 0 : value; - } else { - chosen = p.getController().chooseNumber(sa, title, min, max); - } - // don't notify here, because most scripts I've seen don't store that number in a long term - } - if (secretlyChoose) { - chooseMap.put(p, chosen); - } else { - card.setChosenNumber(chosen); - } - if (sa.hasParam("Notify")) { - p.getGame().getAction().notifyOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p); + chosen = p.getController().chooseNumber(sa, title, min, max); } + // don't notify here, because most scripts I've seen don't store that number in a long term + } + if (secretlyChoose) { + chooseMap.put(p, chosen); + } else { + card.setChosenNumber(chosen); + } + if (sa.hasParam("Notify")) { + p.getGame().getAction().notifyOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p); } } if (secretlyChoose) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java index 62dba5b5b40..ae42272e447 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java @@ -1,14 +1,12 @@ package forge.game.ability.effects; -import java.util.List; - import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Aggregates; +import forge.util.Lang; import forge.util.Localizer; import forge.util.collect.FCollectionView; @@ -18,9 +16,8 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); - for (final Player p : getTargetPlayers(sa)) { - sb.append(p).append(" "); - } + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); + sb.append("chooses a player."); return sb.toString(); @@ -30,56 +27,53 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); - final List tgtPlayers = getTargetPlayers(sa); - - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final FCollectionView choices = sa.hasParam("Choices") ? AbilityUtils.getDefinedPlayers( card, sa.getParam("Choices"), sa) : sa.getActivatingPlayer().getGame().getPlayersInTurnOrder(); final String choiceDesc = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoosePlayer"); final boolean random = sa.hasParam("Random"); - for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - Player chosen; - if (random) { - chosen = choices.isEmpty() ? null : Aggregates.random(choices); + for (final Player p : getTargetPlayers(sa)) { + if (!p.isInGame()) { + continue; + } + Player chosen; + if (random) { + chosen = choices.isEmpty() ? null : Aggregates.random(choices); + } else { + chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, null); + } + if (null != chosen) { + if (sa.hasParam("Secretly")) { + card.setSecretChosenPlayer(chosen); } else { - chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, null); + card.setChosenPlayer(chosen); + } + if (sa.hasParam("ForgetOtherRemembered")) { + card.clearRemembered(); + } + if (sa.hasParam("RememberChosen")) { + card.addRemembered(chosen); } - if (null != chosen) { - if (sa.hasParam("Secretly")) { - card.setSecretChosenPlayer(chosen); - } else { - card.setChosenPlayer(chosen); - } - if (sa.hasParam("ForgetOtherRemembered")) { - card.clearRemembered(); - } - if (sa.hasParam("RememberChosen")) { - card.addRemembered(chosen); - } - // SubAbility that only fires if a player is chosen - SpellAbility chosenSA = sa.getAdditionalAbility("ChooseSubAbility"); - if (chosenSA != null) { - if (!chosenSA.getHostCard().equals(sa.getHostCard())) { - System.out.println("Warning: ChooseSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct..."); - chosenSA.setHostCard(sa.getHostCard()); - } - AbilityUtils.resolve(chosenSA); + // SubAbility that only fires if a player is chosen + SpellAbility chosenSA = sa.getAdditionalAbility("ChooseSubAbility"); + if (chosenSA != null) { + if (!chosenSA.getHostCard().equals(sa.getHostCard())) { + System.out.println("Warning: ChooseSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct..."); + chosenSA.setHostCard(sa.getHostCard()); } - } else { - // SubAbility that only fires if a player is not chosen - SpellAbility notChosenSA = sa.getAdditionalAbility("CantChooseSubAbility"); - if (notChosenSA != null) { - if (!notChosenSA.getHostCard().equals(sa.getHostCard())) { - System.out.println("Warning: CantChooseSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct..."); - notChosenSA.setHostCard(sa.getHostCard()); - } - AbilityUtils.resolve(notChosenSA); + AbilityUtils.resolve(chosenSA); + } + } else { + // SubAbility that only fires if a player is not chosen + SpellAbility notChosenSA = sa.getAdditionalAbility("CantChooseSubAbility"); + if (notChosenSA != null) { + if (!notChosenSA.getHostCard().equals(sa.getHostCard())) { + System.out.println("Warning: CantChooseSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct..."); + notChosenSA.setHostCard(sa.getHostCard()); } + AbilityUtils.resolve(notChosenSA); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java index 51a845748e4..5f3ea984730 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java @@ -15,6 +15,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.ZoneType; +import forge.util.Lang; import forge.util.Localizer; public class ChooseSourceEffect extends SpellAbilityEffect { @@ -22,9 +23,8 @@ public class ChooseSourceEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); - for (final Player p : getTargetPlayers(sa)) { - sb.append(p).append(" "); - } + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); + sb.append("chooses a source."); return sb.toString(); @@ -129,21 +129,22 @@ public class ChooseSourceEffect extends SpellAbilityEffect { final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : AbilityUtils.calculateAmount(host, numericAmount, sa); for (final Player p : tgtPlayers) { + if (!p.isInGame()) { + continue; + } final CardCollection chosen = new CardCollection(); - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - for (int i = 0; i < validAmount; i++) { - final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseSource") + " "; - Card o = null; - do { - o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle, null); - } while (o == null || o.getName().startsWith("--")); - chosen.add(o); - sourcesToChooseFrom.remove(o); - } - host.setChosenCards(chosen); - if (sa.hasParam("RememberChosen")) { - host.addRemembered(chosen); - } + for (int i = 0; i < validAmount; i++) { + final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseSource") + " "; + Card o = null; + do { + o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle, null); + } while (o == null || o.getName().startsWith("--")); + chosen.add(o); + sourcesToChooseFrom.remove(o); + } + host.setChosenCards(chosen); + if (sa.hasParam("RememberChosen")) { + host.addRemembered(chosen); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java index 1566dcbad69..27bbc775a35 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java @@ -13,9 +13,9 @@ import forge.game.card.CardCollectionView; import forge.game.card.CardFactoryUtil; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Aggregates; +import forge.util.Lang; public class ChooseTypeEffect extends SpellAbilityEffect { @@ -24,9 +24,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect { final StringBuilder sb = new StringBuilder(); if (!sa.usesTargeting()) { - for (final Player p : getTargetPlayers(sa)) { - sb.append(p); - } + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); sb.append(" chooses a ").append(sa.getParam("Type").toLowerCase()).append(" type."); } else { sb.append("Please improve the stack description."); @@ -103,30 +101,26 @@ public class ChooseTypeEffect extends SpellAbilityEffect { } } - final TargetRestrictions tgt = sa.getTargetRestrictions(); - if (validTypes.isEmpty() && sa.hasParam("Note")) { // OK to end up with no choices/have nothing new to note } else if (!validTypes.isEmpty()) { for (final Player p : tgtPlayers) { String choice; - if ((tgt == null) || p.canBeTargetedBy(sa)) { - Player noNotify = p; - if (sa.hasParam("AtRandom")) { - choice = Aggregates.random(validTypes); - noNotify = null; - } else { - choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); - } - if (sa.hasParam("Note")) { - card.addNotedType(choice); - } else if (!sa.hasParam("ChooseType2")) { - card.setChosenType(choice); - } else { - card.setChosenType2(choice); - } - p.getGame().getAction().notifyOfValue(sa, p, choice, noNotify); + Player noNotify = p; + if (sa.hasParam("AtRandom")) { + choice = Aggregates.random(validTypes); + noNotify = null; + } else { + choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); } + if (sa.hasParam("Note")) { + card.addNotedType(choice); + } else if (!sa.hasParam("ChooseType2")) { + card.setChosenType(choice); + } else { + card.setChosenType2(choice); + } + p.getGame().getAction().notifyOfValue(sa, p, choice, noNotify); } } else { throw new InvalidParameterException(sa.getHostCard() + "'s ability resulted in no types to choose from"); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 48854e8b6b3..31e0aa7f217 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -453,124 +453,122 @@ public class CountersPutEffect extends SpellAbilityEffect { } counterAmount = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(gameCard) : counterAmount; - if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) { - if (max != -1) { - counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), - 0); - } - if (sa.hasParam("UpTo")) { - int min = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("UpToMin", "0"), sa); - Map params = Maps.newHashMap(); - params.put("Target", obj); - params.put("CounterType", counterType); - counterAmount = pc.chooseNumber(sa, - Localizer.getInstance().getMessage("lblHowManyCounters"), min, counterAmount, params); - } - if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { - Map params = Maps.newHashMap(); - params.put("Target", obj); - params.put("CounterType", counterType); - divrem++; - if (divrem == tgtObjects.size() || counterRemain == 1) { - counterAmount = counterRemain; - } else { - counterAmount = pc.chooseNumber(sa, - Localizer.getInstance().getMessage("lblHowManyCountersThis", - CardTranslation.getTranslatedName(gameCard.getName())), - 1, counterRemain, params); - } - } - - // Adapt need extra logic - if (sa.hasParam("Adapt")) { - if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0 - || StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) { - continue; - } - } - - if (sa.hasParam("ReadAhead")) { - gameCard.setReadAhead(counterAmount); - } - - if (sa.hasParam("Tribute")) { - // make a copy to check if it would be on the battlefield - Card noTributeLKI = CardUtil.getLKICopy(gameCard); - // this check needs to check if this card would be on the battlefield - noTributeLKI.setLastKnownZone(activator.getZone(ZoneType.Battlefield)); - - // double freeze tracker, so it doesn't update view - game.getTracker().freeze(); - - CardCollection preList = new CardCollection(noTributeLKI); - game.getAction().checkStaticAbilities(false, Sets.newHashSet(noTributeLKI), preList); - - boolean abort = !noTributeLKI.canReceiveCounters(counterType); - - game.getAction().checkStaticAbilities(false); - // clear delayed changes, this check should not have updated the view - game.getTracker().clearDelayed(); - // need to unfreeze tracker - game.getTracker().unfreeze(); - - // check if it can receive the Tribute - if (abort) { - continue; - } - - Map params = Maps.newHashMap(); - params.put("CounterType", counterType); - params.put("Amount", counterAmount); - params.put("Target", gameCard); - - String message = Localizer.getInstance().getMessage( - "lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), - CardTranslation.getTranslatedName(gameCard.getName())); - Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, - Localizer.getInstance().getMessage("lblChooseAnOpponent"), params); - - if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message, null)) { - gameCard.setTributed(true); - } else { - continue; - } - } - - if (etbcounter) { - gameCard.addEtbCounter(counterType, counterAmount, placer); + if (max != -1) { + counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), + 0); + } + if (sa.hasParam("UpTo")) { + int min = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("UpToMin", "0"), sa); + Map params = Maps.newHashMap(); + params.put("Target", obj); + params.put("CounterType", counterType); + counterAmount = pc.chooseNumber(sa, + Localizer.getInstance().getMessage("lblHowManyCounters"), min, counterAmount, params); + } + if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { + Map params = Maps.newHashMap(); + params.put("Target", obj); + params.put("CounterType", counterType); + divrem++; + if (divrem == tgtObjects.size() || counterRemain == 1) { + counterAmount = counterRemain; } else { - gameCard.addCounter(counterType, counterAmount, placer, table); + counterAmount = pc.chooseNumber(sa, + Localizer.getInstance().getMessage("lblHowManyCountersThis", + CardTranslation.getTranslatedName(gameCard.getName())), + 1, counterRemain, params); + } + } + + // Adapt need extra logic + if (sa.hasParam("Adapt")) { + if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0 + || StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) { + continue; + } + } + + if (sa.hasParam("ReadAhead")) { + gameCard.setReadAhead(counterAmount); + } + + if (sa.hasParam("Tribute")) { + // make a copy to check if it would be on the battlefield + Card noTributeLKI = CardUtil.getLKICopy(gameCard); + // this check needs to check if this card would be on the battlefield + noTributeLKI.setLastKnownZone(activator.getZone(ZoneType.Battlefield)); + + // double freeze tracker, so it doesn't update view + game.getTracker().freeze(); + + CardCollection preList = new CardCollection(noTributeLKI); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(noTributeLKI), preList); + + boolean abort = !noTributeLKI.canReceiveCounters(counterType); + + game.getAction().checkStaticAbilities(false); + // clear delayed changes, this check should not have updated the view + game.getTracker().clearDelayed(); + // need to unfreeze tracker + game.getTracker().unfreeze(); + + // check if it can receive the Tribute + if (abort) { + continue; } - if (sa.hasParam("Evolve")) { - game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), - false); - } - if (sa.hasParam("Monstrosity")) { - gameCard.setMonstrous(true); - final Map runParams = AbilityKey.mapFromCard(gameCard); - runParams.put(AbilityKey.MonstrosityAmount, counterAmount); - game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); - } - if (sa.hasParam("Renown")) { - gameCard.setRenowned(true); - game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, - AbilityKey.mapFromCard(gameCard), false); - } - if (sa.hasParam("Adapt")) { - game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), - false); - } - if (sa.hasParam("Training")) { - game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), - false); - } + Map params = Maps.newHashMap(); + params.put("CounterType", counterType); + params.put("Amount", counterAmount); + params.put("Target", gameCard); - game.updateLastStateForCard(gameCard); - if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { - counterRemain = counterRemain - counterAmount; + String message = Localizer.getInstance().getMessage( + "lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), + CardTranslation.getTranslatedName(gameCard.getName())); + Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, + Localizer.getInstance().getMessage("lblChooseAnOpponent"), params); + + if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message, null)) { + gameCard.setTributed(true); + } else { + continue; } } + + if (etbcounter) { + gameCard.addEtbCounter(counterType, counterAmount, placer); + } else { + gameCard.addCounter(counterType, counterAmount, placer, table); + } + + if (sa.hasParam("Evolve")) { + game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), + false); + } + if (sa.hasParam("Monstrosity")) { + gameCard.setMonstrous(true); + final Map runParams = AbilityKey.mapFromCard(gameCard); + runParams.put(AbilityKey.MonstrosityAmount, counterAmount); + game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); + } + if (sa.hasParam("Renown")) { + gameCard.setRenowned(true); + game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, + AbilityKey.mapFromCard(gameCard), false); + } + if (sa.hasParam("Adapt")) { + game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), + false); + } + if (sa.hasParam("Training")) { + game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), + false); + } + + game.updateLastStateForCard(gameCard); + if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { + counterRemain = counterRemain - counterAmount; + } } else if (obj instanceof Player) { // Add Counters to players! Player pl = (Player) obj; diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java index c3de8c42ad2..e17cd12cc04 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java @@ -103,21 +103,22 @@ public class CountersRemoveEffect extends SpellAbilityEffect { boolean rememberAmount = sa.hasParam("RememberAmount"); for (final Player tgtPlayer : getTargetPlayers(sa)) { + if (!tgtPlayer.isInGame()) { + continue; + } // Removing energy - if (!sa.usesTargeting() || tgtPlayer.canBeTargetedBy(sa)) { - if (type.equals("All")) { - for (Map.Entry e : Lists.newArrayList(tgtPlayer.getCounters().entrySet())) { - tgtPlayer.subtractCounter(e.getKey(), e.getValue()); - } + if (type.equals("All")) { + for (Map.Entry e : Lists.newArrayList(tgtPlayer.getCounters().entrySet())) { + tgtPlayer.subtractCounter(e.getKey(), e.getValue()); + } + } else { + if (num.equals("All")) { + cntToRemove = tgtPlayer.getCounters(counterType); + } + if (type.equals("Any")) { + removeAnyType(tgtPlayer, cntToRemove, sa); } else { - if (num.equals("All")) { - cntToRemove = tgtPlayer.getCounters(counterType); - } - if (type.equals("Any")) { - removeAnyType(tgtPlayer, cntToRemove, sa); - } else { - tgtPlayer.subtractCounter(counterType, cntToRemove); - } + tgtPlayer.subtractCounter(counterType, cntToRemove); } } } @@ -160,44 +161,42 @@ public class CountersRemoveEffect extends SpellAbilityEffect { if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) { continue; } - if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) { - final Zone zone = game.getZoneOf(gameCard); - if (type.equals("All")) { - for (Map.Entry e : Lists.newArrayList(gameCard.getCounters().entrySet())) { - gameCard.subtractCounter(e.getKey(), e.getValue()); + final Zone zone = game.getZoneOf(gameCard); + if (type.equals("All")) { + for (Map.Entry e : Lists.newArrayList(gameCard.getCounters().entrySet())) { + gameCard.subtractCounter(e.getKey(), e.getValue()); + } + game.updateLastStateForCard(gameCard); + continue; + } else if (num.equals("All") || num.equals("Any")) { + cntToRemove = gameCard.getCounters(counterType); + } + + if (type.equals("Any")) { + removeAnyType(gameCard, cntToRemove, sa); + } else { + cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType)); + + if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) { + if (sa.hasParam("UpTo") || num.equals("Any")) { + Map params = Maps.newHashMap(); + params.put("Target", gameCard); + params.put("CounterType", counterType); + title = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", type); + cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params); + } + } + if (cntToRemove > 0) { + gameCard.subtractCounter(counterType, cntToRemove); + if (rememberRemoved) { + for (int i = 0; i < cntToRemove; i++) { + // TODO might need to be more specific + card.addRemembered(Pair.of(counterType, i)); + } } game.updateLastStateForCard(gameCard); - continue; - } else if (num.equals("All") || num.equals("Any")) { - cntToRemove = gameCard.getCounters(counterType); - } - if (type.equals("Any")) { - removeAnyType(gameCard, cntToRemove, sa); - } else { - cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType)); - - if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) { - if (sa.hasParam("UpTo") || num.equals("Any")) { - Map params = Maps.newHashMap(); - params.put("Target", gameCard); - params.put("CounterType", counterType); - title = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", type); - cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params); - } - } - if (cntToRemove > 0) { - gameCard.subtractCounter(counterType, cntToRemove); - if (rememberRemoved) { - for (int i = 0; i < cntToRemove; i++) { - // TODO might need to be more specific - card.addRemembered(Pair.of(counterType, i)); - } - } - game.updateLastStateForCard(gameCard); - - totalRemoved += cntToRemove; - } + totalRemoved += cntToRemove; } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java index 4da99ed62ea..5f2597531fc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java @@ -69,80 +69,92 @@ public class DebuffEffect extends SpellAbilityEffect { final long timestamp = game.getNextTimestamp(); for (final Card tgtC : getTargetCards(sa)) { + if (!tgtC.isInPlay()) { + continue; + } if (tgtC.isPhasedOut()) { continue; } + // check if the object is still in game or if it was moved + Card gameCard = game.getCardState(tgtC, null); + // gameCard is LKI in that case, the card is not in game anymore + // or the timestamp did change + // this should check Self too + if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) { + continue; + } + final List addedKW = Lists.newArrayList(); final List removedKW = Lists.newArrayList(); - if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) { - if (sa.hasParam("AllSuffixKeywords")) { - // this only for walk abilities, may to try better - if (sa.getParam("AllSuffixKeywords").equals("walk")) { - for (final KeywordInterface kw : tgtC.getKeywords(Keyword.LANDWALK)) { - removedKW.add(kw.getOriginal()); - } + + if (sa.hasParam("AllSuffixKeywords")) { + // this only for walk abilities, may to try better + if (sa.getParam("AllSuffixKeywords").equals("walk")) { + for (final KeywordInterface kw : tgtC.getKeywords(Keyword.LANDWALK)) { + removedKW.add(kw.getOriginal()); } } + } - // special for Protection:Card.:Protection from :* - for (final KeywordInterface inst : tgtC.getUnhiddenKeywords()) { - String keyword = inst.getOriginal(); - if (keyword.startsWith("Protection:")) { - for (final String kw : kws) { - if (keyword.matches("(?i).*:" + kw)) - removedKW.add(keyword); - } + // special for Protection:Card.:Protection from :* + for (final KeywordInterface inst : tgtC.getUnhiddenKeywords()) { + String keyword = inst.getOriginal(); + if (keyword.startsWith("Protection:")) { + for (final String kw : kws) { + if (keyword.matches("(?i).*:" + kw)) + removedKW.add(keyword); } } + } - boolean ProtectionFromColor = false; - for (final String kw : kws) { - // Check if some of the Keywords are Protection from - if (!ProtectionFromColor && kw.startsWith("Protection from ")) { - for (byte col : MagicColor.WUBRG) { - final String colString = MagicColor.toLongString(col); - if (kw.endsWith(colString.toLowerCase())) { - ProtectionFromColor = true; - } - } - } - } - - // Split "Protection from all colors" into extra Protection from - String allColors = "Protection from all colors"; - if (ProtectionFromColor && tgtC.hasKeyword(allColors)) { - final List allColorsProtect = Lists.newArrayList(); - - for (byte col : MagicColor.WUBRG) { - allColorsProtect.add("Protection from " + MagicColor.toLongString(col).toLowerCase()); - } - allColorsProtect.removeAll(kws); - addedKW.addAll(allColorsProtect); - removedKW.add(allColors); - } - - // Extra for Spectra Ward - allColors = "Protection:Card.nonColorless:Protection from all colors:Aura"; - if (ProtectionFromColor && tgtC.hasKeyword(allColors)) { - final List allColorsProtect = Lists.newArrayList(); - + boolean ProtectionFromColor = false; + for (final String kw : kws) { + // Check if some of the Keywords are Protection from + if (!ProtectionFromColor && kw.startsWith("Protection from ")) { for (byte col : MagicColor.WUBRG) { final String colString = MagicColor.toLongString(col); - if (!kws.contains("Protection from " + colString)) { - allColorsProtect.add( - "Protection:Card." + StringUtils.capitalize(colString) + - ":Protection from " + colString + ":Aura" - ); + if (kw.endsWith(colString.toLowerCase())) { + ProtectionFromColor = true; } } - addedKW.addAll(allColorsProtect); - removedKW.add(allColors); } - - removedKW.addAll(kws); - tgtC.addChangedCardKeywords(addedKW, removedKW, false, timestamp, 0); } + + // Split "Protection from all colors" into extra Protection from + String allColors = "Protection from all colors"; + if (ProtectionFromColor && tgtC.hasKeyword(allColors)) { + final List allColorsProtect = Lists.newArrayList(); + + for (byte col : MagicColor.WUBRG) { + allColorsProtect.add("Protection from " + MagicColor.toLongString(col).toLowerCase()); + } + allColorsProtect.removeAll(kws); + addedKW.addAll(allColorsProtect); + removedKW.add(allColors); + } + + // Extra for Spectra Ward + allColors = "Protection:Card.nonColorless:Protection from all colors:Aura"; + if (ProtectionFromColor && tgtC.hasKeyword(allColors)) { + final List allColorsProtect = Lists.newArrayList(); + + for (byte col : MagicColor.WUBRG) { + final String colString = MagicColor.toLongString(col); + if (!kws.contains("Protection from " + colString)) { + allColorsProtect.add( + "Protection:Card." + StringUtils.capitalize(colString) + + ":Protection from " + colString + ":Aura" + ); + } + } + addedKW.addAll(allColorsProtect); + removedKW.add(allColors); + } + + removedKW.addAll(kws); + tgtC.addChangedCardKeywords(addedKW, removedKW, false, timestamp, 0); + if (!"Permanent".equals(sa.getParam("Duration"))) { final GameCommand until = new GameCommand() { private static final long serialVersionUID = 5387486776282932314L; @@ -155,6 +167,6 @@ public class DebuffEffect extends SpellAbilityEffect { addUntilCommand(sa, until); } } - } // debuffResolve + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index 35a846ef8f2..5f10f698d1c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -122,146 +122,144 @@ public class DigUntilEffect extends SpellAbilityEffect { CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); for (final Player p : getTargetPlayers(sa)) { - if (p == null) { + if (p == null || !p.isInGame()) { continue; } - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - if (optional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantDigYourLibrary"), null)) { - continue; + if (optional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantDigYourLibrary"), null)) { + continue; + } + CardCollection found = new CardCollection(); + CardCollection revealed = new CardCollection(); + + final PlayerZone library = p.getZone(digSite); + + final int maxToDig = maxRevealed != null ? maxRevealed : library.size(); + + for (int i = 0; i < maxToDig; i++) { + final Card c = library.get(i); + revealed.add(c); + if (c.isValid(type, sa.getActivatingPlayer(), host, sa)) { + found.add(c); + if (sa.hasParam("ForgetOtherRemembered")) { + host.clearRemembered(); + } + if (remember) { + host.addRemembered(c); + } + if (imprint) { + host.addImprintedCard(c); + } + if (found.size() == untilAmount) { + break; + } } - CardCollection found = new CardCollection(); - CardCollection revealed = new CardCollection(); + } - final PlayerZone library = p.getZone(digSite); + if (shuffle && sa.hasParam("ShuffleCondition")) { + if (sa.getParam("ShuffleCondition").equals("NoneFound")) { + shuffle = found.isEmpty(); + } + } - final int maxToDig = maxRevealed != null ? maxRevealed : library.size(); + if (revealed.size() > 0) { + game.getAction().reveal(revealed, p, false); + } - for (int i = 0; i < maxToDig; i++) { - final Card c = library.get(i); - revealed.add(c); - if (c.isValid(type, sa.getActivatingPlayer(), host, sa)) { - found.add(c); - if (sa.hasParam("ForgetOtherRemembered")) { - host.clearRemembered(); + if (foundDest != null) { + // Allow ordering of found cards + if (foundDest.isKnown() && found.size() >= 2 && !foundDest.equals(ZoneType.Exile)) { + found = (CardCollection)p.getController().orderMoveToZoneList(found, foundDest, sa); + } + + final Iterator itr = found.iterator(); + while (itr.hasNext()) { + final Card c = itr.next(); + final ZoneType origin = c.getZone().getZoneType(); + if (optionalFound && !p.getController().confirmAction(sa, null, + Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()), null)) { + continue; + } + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); + moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + Card m = null; + if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) { + c.setController(sa.getActivatingPlayer(), game.getNextTimestamp()); + if (sa.hasParam("Tapped")) { + c.setTapped(true); } - if (remember) { - host.addRemembered(c); - } - if (imprint) { - host.addImprintedCard(c); - } - if (found.size() == untilAmount) { - break; + m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa, moveParams); + if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } + } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) { + //Don't do anything + } else { + m = game.getAction().moveTo(foundDest, c, foundLibPos, sa, moveParams); + } + revealed.remove(c); + if (m != null && !origin.equals(m.getZone().getZoneType())) { + table.put(origin, m.getZone().getZoneType(), m); } } + } - if (shuffle && sa.hasParam("ShuffleCondition")) { - if (sa.getParam("ShuffleCondition").equals("NoneFound")) { - shuffle = found.isEmpty(); - } + if (sa.hasParam("RememberRevealed")) { + host.addRemembered(revealed); + } + if (sa.hasParam("ImprintRevealed")) { + host.addImprintedCards(revealed); + } + if (sa.hasParam("RevealRandomOrder")) { + Collections.shuffle(revealed, MyRandom.getRandom()); + } + + if (sa.hasParam("NoMoveRevealed")) { + //don't do anything + } else if (sa.hasParam("NoneFoundDestination") && found.size() < untilAmount) { + // Allow ordering the revealed cards + if (noneFoundDest.isKnown() && revealed.size() >= 2) { + revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, noneFoundDest, sa); + } + if (noneFoundDest == ZoneType.Library && !shuffle + && !sa.hasParam("RevealRandomOrder") && revealed.size() >= 2) { + revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, noneFoundDest, sa); } - if (revealed.size() > 0) { - game.getAction().reveal(revealed, p, false); - } - - if (foundDest != null) { - // Allow ordering of found cards - if (foundDest.isKnown() && found.size() >= 2 && !foundDest.equals(ZoneType.Exile)) { - found = (CardCollection)p.getController().orderMoveToZoneList(found, foundDest, sa); - } - - final Iterator itr = found.iterator(); - while (itr.hasNext()) { - final Card c = itr.next(); - final ZoneType origin = c.getZone().getZoneType(); - if (optionalFound && !p.getController().confirmAction(sa, null, - Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()), null)) { - continue; - } - Map moveParams = AbilityKey.newMap(); - moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); - moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); - Card m = null; - if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) { - c.setController(sa.getActivatingPlayer(), game.getNextTimestamp()); - if (sa.hasParam("Tapped")) { - c.setTapped(true); - } - m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa, moveParams); - if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) { - combatChanged = true; - } - } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) { - //Don't do anything - } else { - m = game.getAction().moveTo(foundDest, c, foundLibPos, sa, moveParams); - } - revealed.remove(c); - if (m != null && !origin.equals(m.getZone().getZoneType())) { - table.put(origin, m.getZone().getZoneType(), m); - } + final Iterator itr = revealed.iterator(); + while (itr.hasNext()) { + final Card c = itr.next(); + final ZoneType origin = c.getZone().getZoneType(); + final Card m = game.getAction().moveTo(noneFoundDest, c, noneFoundLibPos, sa); + if (m != null && !origin.equals(m.getZone().getZoneType())) { + table.put(origin, m.getZone().getZoneType(), m); } } - - if (sa.hasParam("RememberRevealed")) { - host.addRemembered(revealed); + } else { + // Allow ordering the rest of the revealed cards + if (revealedDest.isKnown() && revealed.size() >= 2 && !sa.hasParam("SkipReorder")) { + revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, revealedDest, sa); } - if (sa.hasParam("ImprintRevealed")) { - host.addImprintedCards(revealed); - } - if (sa.hasParam("RevealRandomOrder")) { - Collections.shuffle(revealed, MyRandom.getRandom()); + if (revealedDest == ZoneType.Library && !shuffle + && !sa.hasParam("RevealRandomOrder") && revealed.size() >= 2) { + revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, revealedDest, sa); } - if (sa.hasParam("NoMoveRevealed")) { - //don't do anything - } else if (sa.hasParam("NoneFoundDestination") && found.size() < untilAmount) { - // Allow ordering the revealed cards - if (noneFoundDest.isKnown() && revealed.size() >= 2) { - revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, noneFoundDest, sa); - } - if (noneFoundDest == ZoneType.Library && !shuffle - && !sa.hasParam("RevealRandomOrder") && revealed.size() >= 2) { - revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, noneFoundDest, sa); - } - - final Iterator itr = revealed.iterator(); - while (itr.hasNext()) { - final Card c = itr.next(); - final ZoneType origin = c.getZone().getZoneType(); - final Card m = game.getAction().moveTo(noneFoundDest, c, noneFoundLibPos, sa); - if (m != null && !origin.equals(m.getZone().getZoneType())) { - table.put(origin, m.getZone().getZoneType(), m); - } - } - } else { - // Allow ordering the rest of the revealed cards - if (revealedDest.isKnown() && revealed.size() >= 2 && !sa.hasParam("SkipReorder")) { - revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, revealedDest, sa); - } - if (revealedDest == ZoneType.Library && !shuffle - && !sa.hasParam("RevealRandomOrder") && revealed.size() >= 2) { - revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, revealedDest, sa); - } - - final Iterator itr = revealed.iterator(); - while (itr.hasNext()) { - final Card c = itr.next(); - final ZoneType origin = c.getZone().getZoneType(); - final Card m = game.getAction().moveTo(revealedDest, c, revealedLibPos, sa); - if (m != null && !origin.equals(m.getZone().getZoneType())) { - table.put(origin, m.getZone().getZoneType(), m); - } + final Iterator itr = revealed.iterator(); + while (itr.hasNext()) { + final Card c = itr.next(); + final ZoneType origin = c.getZone().getZoneType(); + final Card m = game.getAction().moveTo(revealedDest, c, revealedLibPos, sa); + if (m != null && !origin.equals(m.getZone().getZoneType())) { + table.put(origin, m.getZone().getZoneType(), m); } } + } - if (shuffle) { - p.shuffle(sa); - } - } // end foreach player - } + if (shuffle) { + p.shuffle(sa); + } + } // end foreach player if (combatChanged) { game.updateCombatForView(); game.fireEvent(new GameEventCombatChanged()); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java index 5c84eb8d5d7..a6b8da19bd4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java @@ -37,39 +37,37 @@ public class ManifestEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) { - if (sa.usesTargeting() || p.canBeTargetedBy(sa)) { - CardCollection tgtCards; - if (sa.hasParam("Choices") || sa.hasParam("ChoiceZone")) { - ZoneType choiceZone = ZoneType.Hand; - if (sa.hasParam("ChoiceZone")) { - choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone")); - } - CardCollection choices = new CardCollection(game.getCardsIn(choiceZone)); - if (sa.hasParam("Choices")) { - choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, source, sa); - } - if (choices.isEmpty()) { - continue; - } - - String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseCardToManifest") + " "; - - tgtCards = new CardCollection(activator.getController().chooseCardsForEffect(choices, sa, title, amount, amount, false, null)); - } else if ("TopOfLibrary".equals(defined)) { - tgtCards = p.getTopXCardsFromLibrary(amount); - } else { - tgtCards = getTargetCards(sa); + CardCollection tgtCards; + if (sa.hasParam("Choices") || sa.hasParam("ChoiceZone")) { + ZoneType choiceZone = ZoneType.Hand; + if (sa.hasParam("ChoiceZone")) { + choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone")); + } + CardCollection choices = new CardCollection(game.getCardsIn(choiceZone)); + if (sa.hasParam("Choices")) { + choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, source, sa); + } + if (choices.isEmpty()) { + continue; } - if (sa.hasParam("Shuffle")) { - CardLists.shuffle(tgtCards); - } + String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseCardToManifest") + " "; - for (Card c : tgtCards) { - Card rem = c.manifest(p, sa, moveParams); - if (sa.hasParam("RememberManifested") && rem != null && rem.isManifested()) { - source.addRemembered(rem); - } + tgtCards = new CardCollection(activator.getController().chooseCardsForEffect(choices, sa, title, amount, amount, false, null)); + } else if ("TopOfLibrary".equals(defined)) { + tgtCards = p.getTopXCardsFromLibrary(amount); + } else { + tgtCards = getTargetCards(sa); + } + + if (sa.hasParam("Shuffle")) { + CardLists.shuffle(tgtCards); + } + + for (Card c : tgtCards) { + Card rem = c.manifest(p, sa, moveParams); + if (sa.hasParam("RememberManifested") && rem != null && rem.isManifested()) { + source.addRemembered(rem); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java index fc8ae802494..1c10c907809 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java @@ -44,44 +44,46 @@ public class MillEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - if (sa.hasParam("Optional")) { - final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantPutLibraryCardsTo", destination.getTranslatedName())); - // CR 701.13b - if (numCards > p.getZone(ZoneType.Library).size() || !p.getController().confirmAction(sa, null, prompt, null)) { - continue; + if (!p.isInGame()) { + continue; + } + + if (sa.hasParam("Optional")) { + final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantPutLibraryCardsTo", destination.getTranslatedName())); + // CR 701.13b + if (numCards > p.getZone(ZoneType.Library).size() || !p.getController().confirmAction(sa, null, prompt, null)) { + continue; + } + } + final CardCollectionView milled = p.mill(numCards, destination, bottom, sa, table, moveParams); + // Reveal the milled cards, so players don't have to manually inspect the + // graveyard to figure out which ones were milled. + if (!facedown && reveal) { // do not reveal when exiling face down + if (showRevealDialog) { + game.getAction().reveal(milled, p, false); + } + StringBuilder sb = new StringBuilder(); + sb.append(p).append(" milled ").append(milled).append(" to ").append(destination); + p.getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); + } + if (destination.equals(ZoneType.Exile)) { + Card host = sa.getOriginalHost(); + if (host == null) { + host = sa.getHostCard(); + } + for (final Card c : milled) { + host.addExiledCard(c); + c.setExiledWith(host); + if (facedown) { + c.turnFaceDown(true); } } - final CardCollectionView milled = p.mill(numCards, destination, bottom, sa, table, moveParams); - // Reveal the milled cards, so players don't have to manually inspect the - // graveyard to figure out which ones were milled. - if (!facedown && reveal) { // do not reveal when exiling face down - if (showRevealDialog) { - game.getAction().reveal(milled, p, false); - } - StringBuilder sb = new StringBuilder(); - sb.append(p).append(" milled ").append(milled).append(" to ").append(destination); - p.getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); - } - if (destination.equals(ZoneType.Exile)) { - Card host = sa.getOriginalHost(); - if (host == null) { - host = sa.getHostCard(); - } - for (final Card c : milled) { - host.addExiledCard(c); - c.setExiledWith(host); - if (facedown) { - c.turnFaceDown(true); - } - } - } - if (sa.hasParam("RememberMilled")) { - source.addRemembered(milled); - } - if (sa.hasParam("Imprint")) { - source.addImprintedCards(milled); - } + } + if (sa.hasParam("RememberMilled")) { + source.addRemembered(milled); + } + if (sa.hasParam("Imprint")) { + source.addImprintedCards(milled); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java index 4337646f372..bc5f14882ea 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java @@ -16,7 +16,6 @@ import forge.game.card.CardCollectionView; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.Localizer; @@ -56,7 +55,6 @@ public class MultiplePilesEffect extends SpellAbilityEffect { final String valid = sa.getParamOrDefault("ValidCards", ""); - final TargetRestrictions tgt = sa.getTargetRestrictions(); final List tgtPlayers = getTargetPlayers(sa); // starting with the activator int pSize = tgtPlayers.size(); @@ -66,28 +64,30 @@ public class MultiplePilesEffect extends SpellAbilityEffect { } for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - CardCollection pool; - if (sa.hasParam("DefinedCards")) { - pool = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa); - } else { - pool = new CardCollection(p.getCardsIn(zone)); - } - pool = CardLists.getValidCards(pool, valid, source.getController(), source, sa); - - List pileList = Lists.newArrayList(); - - for (int i = 1; i < piles; i++) { - int size = pool.size(); - CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false, null); - pileList.add(pile); - pool.removeAll(pile); - } - - pileList.add(pool); - p.getGame().getAction().notifyOfValue(sa, p, pileList.toString(), p); - record.put(p, pileList); + if (!p.isInGame()) { + continue; } + + CardCollection pool; + if (sa.hasParam("DefinedCards")) { + pool = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa); + } else { + pool = new CardCollection(p.getCardsIn(zone)); + } + pool = CardLists.getValidCards(pool, valid, source.getController(), source, sa); + + List pileList = Lists.newArrayList(); + + for (int i = 1; i < piles; i++) { + int size = pool.size(); + CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false, null); + pileList.add(pile); + pool.removeAll(pile); + } + + pileList.add(pool); + p.getGame().getAction().notifyOfValue(sa, p, pileList.toString(), p); + record.put(p, pileList); } if (randomChosen) { for (Entry> ev : record.entrySet()) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java index d157cde5960..635602b0dfd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java @@ -39,7 +39,7 @@ public class ProtectAllEffect extends SpellAbilityEffect { } return sb.toString(); - } // protectStackDescription() + } @Override public void resolve(SpellAbility sa) { @@ -83,27 +83,24 @@ public class ProtectAllEffect extends SpellAbilityEffect { // Deal with permanents final String valid = sa.getParamOrDefault("ValidCards", ""); if (!valid.equals("")) { - CardCollectionView list = game.getCardsIn(ZoneType.Battlefield); - list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa); + CardCollectionView list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), valid, sa.getActivatingPlayer(), host, sa); for (final Card tgtC : list) { - if (tgtC.isInPlay()) { - tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, 0, true); + tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, 0, true); - if (!"Permanent".equals(sa.getParam("Duration"))) { - // If not Permanent, remove protection at EOT - final GameCommand untilEOT = new GameCommand() { - private static final long serialVersionUID = -6573962672873853565L; + if (!"Permanent".equals(sa.getParam("Duration"))) { + // If not Permanent, remove protection at EOT + final GameCommand untilEOT = new GameCommand() { + private static final long serialVersionUID = -6573962672873853565L; - @Override - public void run() { - if (tgtC.isInPlay()) { - tgtC.removeChangedCardKeywords(timestamp, 0, true); - } + @Override + public void run() { + if (tgtC.isInPlay()) { + tgtC.removeChangedCardKeywords(timestamp, 0, true); } - }; - addUntilCommand(sa, untilEOT); - } + } + }; + addUntilCommand(sa, untilEOT); } } } @@ -111,11 +108,8 @@ public class ProtectAllEffect extends SpellAbilityEffect { // Deal with Players final String players = sa.getParamOrDefault("ValidPlayers", ""); if (!players.equals("")) { - final List playerList = AbilityUtils.getDefinedPlayers(host, players, sa); - for (final Player player : playerList) { - for (final String gain : gains) { - player.addChangedKeywords(ImmutableList.of("Protection from " + gain), ImmutableList.of(), timestamp, 0); - } + for (final Player player : AbilityUtils.getDefinedPlayers(host, players, sa)) { + player.addChangedKeywords(gainsKWList, ImmutableList.of(), timestamp, 0); if (!"Permanent".equals(sa.getParam("Duration"))) { // If not Permanent, remove protection at EOT diff --git a/forge-game/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java index 82a377a84f6..dba17adcc12 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java @@ -12,7 +12,6 @@ import forge.game.card.CardCollection; import forge.game.combat.Combat; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; public class RemoveFromCombatEffect extends SpellAbilityEffect { @@ -34,37 +33,38 @@ public class RemoveFromCombatEffect extends SpellAbilityEffect { final Player activator = sa.getActivatingPlayer(); final Game game = activator.getGame(); final boolean rem = sa.hasParam("RememberRemovedFromCombat"); + final Combat combat = game.getPhaseHandler().getCombat(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); for (final Card c : getTargetCards(sa)) { - final Combat combat = game.getPhaseHandler().getCombat(); - if (combat != null && (tgt == null || c.canBeTargetedBy(sa))) { - // Unblock creatures that were blocked only by this card (e.g. Ydwen Efreet) - if (sa.hasParam("UnblockCreaturesBlockedOnlyBy")) { - CardCollection attackers = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("UnblockCreaturesBlockedOnlyBy"), sa); - if (!attackers.isEmpty()) { - CardCollection blockedByCard = combat.getAttackersBlockedBy(attackers.getFirst()); - for (Card atk : blockedByCard) { - boolean blockedOnlyByCard = true; - for (Card blocker : combat.getBlockers(atk)) { - if (!blocker.equals(attackers.getFirst())) { - blockedOnlyByCard = false; - break; - } - } - if (blockedOnlyByCard) { - combat.setBlocked(atk, false); + if (combat == null || !c.isInPlay()) { + continue; + } + + // Unblock creatures that were blocked only by this card (e.g. Ydwen Efreet) + if (sa.hasParam("UnblockCreaturesBlockedOnlyBy")) { + CardCollection attackers = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("UnblockCreaturesBlockedOnlyBy"), sa); + if (!attackers.isEmpty()) { + CardCollection blockedByCard = combat.getAttackersBlockedBy(attackers.getFirst()); + for (Card atk : blockedByCard) { + boolean blockedOnlyByCard = true; + for (Card blocker : combat.getBlockers(atk)) { + if (!blocker.equals(attackers.getFirst())) { + blockedOnlyByCard = false; + break; } } + if (blockedOnlyByCard) { + combat.setBlocked(atk, false); + } } } + } - game.getCombat().saveLKI(c); - combat.removeFromCombat(c); + game.getCombat().saveLKI(c); + combat.removeFromCombat(c); - if (rem) { - sa.getHostCard().addRemembered(c); - } + if (rem) { + sa.getHostCard().addRemembered(c); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java index 87e53bb36d6..f29d2dcc9f9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java @@ -29,76 +29,77 @@ public class RevealEffect extends SpellAbilityEffect { int cnt = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(host, sa.getParam("NumCards"), sa) : 1; for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - final CardCollectionView cardsInHand = p.getZone(ZoneType.Hand).getCards(); - if (cardsInHand.isEmpty()) { + if (!p.isInGame()) { + continue; + } + final CardCollectionView cardsInHand = p.getZone(ZoneType.Hand).getCards(); + if (cardsInHand.isEmpty()) { + continue; + } + final CardCollection revealed = new CardCollection(); + if (sa.hasParam("Random")) { + CardCollection valid = new CardCollection(cardsInHand); + + if (sa.hasParam("RevealValid")) { + valid = CardLists.getValidCards(valid, sa.getParam("RevealValid"), p, host, sa); + } + + if (valid.isEmpty()) continue; - } - final CardCollection revealed = new CardCollection(); - if (sa.hasParam("Random")) { - CardCollection valid = new CardCollection(cardsInHand); - if (sa.hasParam("RevealValid")) { - valid = CardLists.getValidCards(valid, sa.getParam("RevealValid"), p, host, sa); + if (sa.hasParam("NumCards")) { + final int revealnum = Math.min(cardsInHand.size(), cnt); + for (int i = 0; i < revealnum; i++) { + final Card random = Aggregates.random(valid); + revealed.add(random); + valid.remove(random); } - - if (valid.isEmpty()) - continue; - - if (sa.hasParam("NumCards")) { - final int revealnum = Math.min(cardsInHand.size(), cnt); - for (int i = 0; i < revealnum; i++) { - final Card random = Aggregates.random(valid); - revealed.add(random); - valid.remove(random); - } - } else { - revealed.add(Aggregates.random(valid)); - } - - } else if (sa.hasParam("RevealDefined")) { - revealed.addAll(AbilityUtils.getDefinedCards(host, sa.getParam("RevealDefined"), sa)); - } else if (sa.hasParam("RevealAllValid")) { - revealed.addAll(CardLists.getValidCards(cardsInHand, sa.getParam("RevealAllValid"), p, host, sa)); } else { - CardCollection valid = new CardCollection(cardsInHand); - - if (sa.hasParam("RevealValid")) { - valid = CardLists.getValidCards(valid, sa.getParam("RevealValid"), p, host, sa); - } - - if (valid.isEmpty()) - continue; - - if (sa.hasParam("RevealAll")) { //for when cards to reveal are not in hand - revealed.addAll(valid); - } else { - if (cnt > valid.size()) - cnt = valid.size(); - - int min = cnt; - if (anyNumber) { - cnt = valid.size(); - min = 0; - } else if (optional) { - min = 0; - } - - revealed.addAll(p.getController().chooseCardsToRevealFromHand(min, cnt, valid)); - } + revealed.add(Aggregates.random(valid)); } - if (sa.hasParam("RevealToAll") || sa.hasParam("Random")) { - game.getAction().reveal(revealed, p, false, - sa.getParamOrDefault("RevealTitle", "")); + } else if (sa.hasParam("RevealDefined")) { + revealed.addAll(AbilityUtils.getDefinedCards(host, sa.getParam("RevealDefined"), sa)); + } else if (sa.hasParam("RevealAllValid")) { + revealed.addAll(CardLists.getValidCards(cardsInHand, sa.getParam("RevealAllValid"), p, host, sa)); + } else { + CardCollection valid = new CardCollection(cardsInHand); + + if (sa.hasParam("RevealValid")) { + valid = CardLists.getValidCards(valid, sa.getParam("RevealValid"), p, host, sa); + } + + if (valid.isEmpty()) + continue; + + if (sa.hasParam("RevealAll")) { //for when cards to reveal are not in hand + revealed.addAll(valid); } else { - game.getAction().reveal(revealed, p); - } - for (final Card c : revealed) { - game.getTriggerHandler().runTrigger(TriggerType.Revealed, AbilityKey.mapFromCard(c), false); - if (sa.hasParam("RememberRevealed")) { - host.addRemembered(c); + if (cnt > valid.size()) + cnt = valid.size(); + + int min = cnt; + if (anyNumber) { + cnt = valid.size(); + min = 0; + } else if (optional) { + min = 0; } + + revealed.addAll(p.getController().chooseCardsToRevealFromHand(min, cnt, valid)); + } + } + + if (sa.hasParam("RevealToAll") || sa.hasParam("Random")) { + game.getAction().reveal(revealed, p, false, + sa.getParamOrDefault("RevealTitle", "")); + } else { + game.getAction().reveal(revealed, p); + } + for (final Card c : revealed) { + game.getTriggerHandler().runTrigger(TriggerType.Revealed, AbilityKey.mapFromCard(c), false); + if (sa.hasParam("RememberRevealed")) { + host.addRemembered(c); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RevealHandEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RevealHandEffect.java index d5b0687ed8c..c83fc00bc6b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RevealHandEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RevealHandEffect.java @@ -6,7 +6,6 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.card.*; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.Localizer; @@ -36,33 +35,32 @@ public class RevealHandEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); - - final TargetRestrictions tgt = sa.getTargetRestrictions(); final boolean optional = sa.hasParam("Optional"); for (final Player p : getTargetPlayers(sa)) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - if (optional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantRevealYourHand"), null)) { - continue; - } - CardCollectionView hand = p.getCardsIn(ZoneType.Hand); - if (sa.hasParam("RevealType")) { - hand = CardLists.getType(hand, sa.getParam("RevealType")); - } - if (sa.hasParam("Look")) { - sa.getActivatingPlayer().getController().reveal(hand, ZoneType.Hand, p); - } else { - host.getGame().getAction().reveal(hand, p); - } - if (sa.hasParam("RememberRevealed")) { - host.addRemembered(hand); - } - if (sa.hasParam("ImprintRevealed")) { - host.addImprintedCards(hand); - } - if (sa.hasParam("RememberRevealedPlayer")) { - host.addRemembered(p); - } + if (!p.isInGame()) { + continue; + } + if (optional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantRevealYourHand"), null)) { + continue; + } + CardCollectionView hand = p.getCardsIn(ZoneType.Hand); + if (sa.hasParam("RevealType")) { + hand = CardLists.getType(hand, sa.getParam("RevealType")); + } + if (sa.hasParam("Look")) { + sa.getActivatingPlayer().getController().reveal(hand, ZoneType.Hand, p); + } else { + host.getGame().getAction().reveal(hand, p); + } + if (sa.hasParam("RememberRevealed")) { + host.addRemembered(hand); + } + if (sa.hasParam("ImprintRevealed")) { + host.addImprintedCards(hand); + } + if (sa.hasParam("RememberRevealedPlayer")) { + host.addRemembered(p); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java index 96cb9afbcea..6df53cd59e9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java @@ -11,7 +11,6 @@ import forge.game.card.CardCollectionView; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.Localizer; @@ -26,15 +25,11 @@ public class TwoPilesEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); - final List tgtPlayers = getTargetPlayers(sa); - final String valid = sa.getParamOrDefault("ValidCards", ""); sb.append("Separate all ").append(valid).append(" cards "); - for (final Player p : tgtPlayers) { - sb.append(p).append(" "); - } + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); sb.append("controls into two piles."); return sb.toString(); } @@ -55,7 +50,6 @@ public class TwoPilesEffect extends SpellAbilityEffect { final String valid = sa.getParamOrDefault("ValidCards", "Card"); - final TargetRestrictions tgt = sa.getTargetRestrictions(); final List tgtPlayers = getTargetPlayers(sa); Player separator = card.getController(); @@ -75,116 +69,118 @@ public class TwoPilesEffect extends SpellAbilityEffect { } for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - CardCollectionView pool0; - if (sa.hasParam("DefinedCards")) { - pool0 = AbilityUtils.getDefinedCards(card, sa.getParam("DefinedCards"), sa); - } else { - pool0 = p.getCardsIn(zone); - } - CardCollection pool = CardLists.getValidCards(pool0, valid, card.getController(), card, sa); - int size = pool.size(); - if (size == 0) { - return; - } + if (!p.isInGame()) { + continue; + } + + CardCollectionView pool0; + if (sa.hasParam("DefinedCards")) { + pool0 = AbilityUtils.getDefinedCards(card, sa.getParam("DefinedCards"), sa); + } else { + pool0 = p.getCardsIn(zone); + } + CardCollection pool = CardLists.getValidCards(pool0, valid, card.getController(), card, sa); + int size = pool.size(); + if (size == 0) { + return; + } - String title; - if ("One".equals(sa.getParamOrDefault("FaceDown", "False"))) { - title = Localizer.getInstance().getMessage("lblSelectCardForFaceDownPile"); - } else if (isLeftRightPile) { - title = Localizer.getInstance().getMessage("lblSelectCardForLeftPile"); - } else { - title = Localizer.getInstance().getMessage("lblDivideCardIntoTwoPiles"); - } + String title; + if ("One".equals(sa.getParamOrDefault("FaceDown", "False"))) { + title = Localizer.getInstance().getMessage("lblSelectCardForFaceDownPile"); + } else if (isLeftRightPile) { + title = Localizer.getInstance().getMessage("lblSelectCardForLeftPile"); + } else { + title = Localizer.getInstance().getMessage("lblDivideCardIntoTwoPiles"); + } - // first, separate the cards into piles - final CardCollectionView pile1; - final CardCollection pile2; - if (sa.hasParam("DefinedPiles")) { - final String[] def = sa.getParam("DefinedPiles").split(",", 2); - pile1 = AbilityUtils.getDefinedCards(card, def[0], sa); - pile2 = AbilityUtils.getDefinedCards(card, def[1], sa); - } else { - pile1 = separator.getController().chooseCardsForEffect(pool, sa, title, 0, size, false, null); - pile2 = new CardCollection(pool); - pile2.removeAll(pile1); - } + // first, separate the cards into piles + final CardCollectionView pile1; + final CardCollection pile2; + if (sa.hasParam("DefinedPiles")) { + final String[] def = sa.getParam("DefinedPiles").split(",", 2); + pile1 = AbilityUtils.getDefinedCards(card, def[0], sa); + pile2 = AbilityUtils.getDefinedCards(card, def[1], sa); + } else { + pile1 = separator.getController().chooseCardsForEffect(pool, sa, title, 0, size, false, null); + pile2 = new CardCollection(pool); + pile2.removeAll(pile1); + } - if (isLeftRightPile) { - pile1WasChosen = true; - } else { - pile1WasChosen = chooser.getController().chooseCardsPile(sa, pile1, pile2, sa.getParamOrDefault("FaceDown", "False")); - } - CardCollectionView chosenPile = pile1WasChosen ? pile1 : pile2; - CardCollectionView unchosenPile = !pile1WasChosen ? pile1 : pile2; - - StringBuilder notification = new StringBuilder(); - if (isLeftRightPile) { - notification.append("\n"); - notification.append(Lang.getInstance().getPossessedObject(separator.getName(), Localizer.getInstance().getMessage("lblLeftPile"))); - notification.append("\n--------------------\n"); - if (!chosenPile.isEmpty()) { - for (Card c : chosenPile) { - notification.append(c.getName()).append("\n"); - } - } else { - notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")\n"); + if (isLeftRightPile) { + pile1WasChosen = true; + } else { + pile1WasChosen = chooser.getController().chooseCardsPile(sa, pile1, pile2, sa.getParamOrDefault("FaceDown", "False")); + } + CardCollectionView chosenPile = pile1WasChosen ? pile1 : pile2; + CardCollectionView unchosenPile = !pile1WasChosen ? pile1 : pile2; + + StringBuilder notification = new StringBuilder(); + if (isLeftRightPile) { + notification.append("\n"); + notification.append(Lang.getInstance().getPossessedObject(separator.getName(), Localizer.getInstance().getMessage("lblLeftPile"))); + notification.append("\n--------------------\n"); + if (!chosenPile.isEmpty()) { + for (Card c : chosenPile) { + notification.append(c.getName()).append("\n"); } - notification.append("\n"); - notification.append(Lang.getInstance().getPossessedObject(separator.getName(), Localizer.getInstance().getMessage("lblRightPile"))); - notification.append("\n--------------------\n"); - if (!unchosenPile.isEmpty()) { - for (Card c : unchosenPile) { - notification.append(c.getName()).append("\n"); - } - } else { - notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")\n"); - } - p.getGame().getAction().notifyOfValue(sa, separator, notification.toString(), separator); } else { - notification.append(chooser + " " + Localizer.getInstance().getMessage("lblChoosesPile") + " " + (pile1WasChosen ? "1" : "2") + ":\n"); - if (!chosenPile.isEmpty()) { - for (Card c : chosenPile) { - notification.append(c.getName()).append("\n"); - } - } else { - notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")"); + notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")\n"); + } + notification.append("\n"); + notification.append(Lang.getInstance().getPossessedObject(separator.getName(), Localizer.getInstance().getMessage("lblRightPile"))); + notification.append("\n--------------------\n"); + if (!unchosenPile.isEmpty()) { + for (Card c : unchosenPile) { + notification.append(c.getName()).append("\n"); } - p.getGame().getAction().notifyOfValue(sa, chooser, notification.toString(), chooser); + } else { + notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")\n"); } - - - if (sa.hasParam("RememberChosen")) { - card.addRemembered(chosenPile); - } - - // take action on the chosen pile - if (sa.hasParam("ChosenPile")) { - List tempRemembered = Lists.newArrayList(card.getRemembered()); - card.removeRemembered(tempRemembered); - card.addRemembered(chosenPile); - - SpellAbility sub = sa.getAdditionalAbility("ChosenPile"); - if (sub != null) { - AbilityUtils.resolve(sub); + p.getGame().getAction().notifyOfValue(sa, separator, notification.toString(), separator); + } else { + notification.append(chooser + " " + Localizer.getInstance().getMessage("lblChoosesPile") + " " + (pile1WasChosen ? "1" : "2") + ":\n"); + if (!chosenPile.isEmpty()) { + for (Card c : chosenPile) { + notification.append(c.getName()).append("\n"); } - card.removeRemembered(chosenPile); - card.addRemembered(tempRemembered); + } else { + notification.append("(" + Localizer.getInstance().getMessage("lblEmptyPile") + ")"); } + p.getGame().getAction().notifyOfValue(sa, chooser, notification.toString(), chooser); + } - // take action on the unchosen pile - if (sa.hasParam("UnchosenPile")) { - List tempRemembered = Lists.newArrayList(card.getRemembered()); - card.removeRemembered(tempRemembered); - card.addRemembered(unchosenPile); - SpellAbility sub = sa.getAdditionalAbility("UnchosenPile"); - if (sub != null) { - AbilityUtils.resolve(sub); - } - card.removeRemembered(unchosenPile); - card.addRemembered(tempRemembered); + if (sa.hasParam("RememberChosen")) { + card.addRemembered(chosenPile); + } + + // take action on the chosen pile + if (sa.hasParam("ChosenPile")) { + List tempRemembered = Lists.newArrayList(card.getRemembered()); + card.removeRemembered(tempRemembered); + card.addRemembered(chosenPile); + + SpellAbility sub = sa.getAdditionalAbility("ChosenPile"); + if (sub != null) { + AbilityUtils.resolve(sub); } + card.removeRemembered(chosenPile); + card.addRemembered(tempRemembered); + } + + // take action on the unchosen pile + if (sa.hasParam("UnchosenPile")) { + List tempRemembered = Lists.newArrayList(card.getRemembered()); + card.removeRemembered(tempRemembered); + card.addRemembered(unchosenPile); + + SpellAbility sub = sa.getAdditionalAbility("UnchosenPile"); + if (sub != null) { + AbilityUtils.resolve(sub); + } + card.removeRemembered(unchosenPile); + card.addRemembered(tempRemembered); } } if (!sa.hasParam("KeepRemembered")) {