diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 1f1fa1380d1..c362140ec8f 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -843,4 +843,15 @@ public abstract class SpellAbilityEffect { game.getEndOfTurn().addUntil(until); } } + + public Player getNewChooser(final SpellAbility sa, final Player activator, final Player loser) { + // CR 800.4g + final PlayerCollection options; + if (loser.isOpponentOf(activator)) { + options = activator.getOpponents(); + } else { + options = activator.getAllOtherPlayers(); + } + return activator.getController().chooseSingleEntityForEffect(options, sa, Localizer.getInstance().getMessage("lblChoosePlayer") , null); + } } 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/BecomeMonarchEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BecomeMonarchEffect.java index d8ba6c707f3..519b73f886b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/BecomeMonarchEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/BecomeMonarchEffect.java @@ -27,10 +27,11 @@ public class BecomeMonarchEffect extends SpellAbilityEffect { final String set = sa.getHostCard().getSetCode(); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - if (p.canBecomeMonarch()) { - p.getGame().getAction().becomeMonarch(p, set); - } + if (!p.isInGame()) { + continue; + } + if (p.canBecomeMonarch()) { + p.getGame().getAction().becomeMonarch(p, set); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java index 3e31e6f37fa..8547d6bd190 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java @@ -34,17 +34,15 @@ public class BecomesBlockedEffect extends SpellAbilityEffect { final Game game = sa.getActivatingPlayer().getGame(); List blocked = Lists.newArrayList(); for (final Card c : getTargetCards(sa)) { - if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) { - game.getCombat().setBlocked(c, true); - if (!c.getDamageHistory().getCreatureGotBlockedThisCombat()) { - blocked.add(c); - final Map runParams = AbilityKey.newMap(); - runParams.put(AbilityKey.Attacker, c); - runParams.put(AbilityKey.Blockers, Lists.newArrayList()); - runParams.put(AbilityKey.Defender, game.getCombat().getDefenderByAttacker(c)); - runParams.put(AbilityKey.DefendingPlayer, game.getCombat().getDefenderPlayerByAttacker(c)); - game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false); - } + game.getCombat().setBlocked(c, true); + if (!c.getDamageHistory().getCreatureGotBlockedThisCombat()) { + blocked.add(c); + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.Attacker, c); + runParams.put(AbilityKey.Blockers, Lists.newArrayList()); + runParams.put(AbilityKey.Defender, game.getCombat().getDefenderByAttacker(c)); + runParams.put(AbilityKey.DefendingPlayer, game.getCombat().getDefenderPlayerByAttacker(c)); + game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false); } } 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/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 040e11893ae..5c7876e2dbe 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -537,15 +537,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard) || gameCard.isPhasedOut()) { continue; } - if (sa.usesTargeting() && !gameCard.canBeTargetedBy(sa)) { - continue; - } + if (sa.hasParam("RememberLKI")) { hostCard.addRemembered(CardUtil.getLKICopy(gameCard)); } final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantMoveTargetFromOriToDest", CardTranslation.getTranslatedName(gameCard.getName()), Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName())); - if (optional && !chooser.getController().confirmAction(sa, null, prompt, null) ) + if (optional && !chooser.getController().confirmAction(sa, null, prompt, null)) continue; final Zone originZone = game.getZoneOf(gameCard); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java index 8636ebe868e..fc7d378193b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java @@ -22,7 +22,6 @@ import forge.game.card.CardPredicates.Presets; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.Lang; @@ -61,7 +60,6 @@ public class ChooseCardEffect extends SpellAbilityEffect { final Game game = activator.getGame(); CardCollection chosen = new CardCollection(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); final List tgtPlayers = getTargetPlayers(sa); List choiceZone = Lists.newArrayList(ZoneType.Battlefield); @@ -101,7 +99,10 @@ public class ChooseCardEffect extends SpellAbilityEffect { return; } - for (final Player p : tgtPlayers) { + for (Player p : tgtPlayers) { + if (!p.isInGame()) { + p = getNewChooser(sa, activator, p); + } boolean dontRevealToOwner = true; if (sa.hasParam("EachBasicType")) { // Get all lands, @@ -212,51 +213,49 @@ public class ChooseCardEffect extends SpellAbilityEffect { // Targeted player (p) chooses N creatures that belongs to them CardCollection tgtPlayerCtrl = CardLists.filterControlledBy(choices, p); chosen.addAll(p.getController().chooseCardsForEffect(tgtPlayerCtrl, sa, title + " " + "you control", minAmount, validAmount, - !sa.hasParam("Mandatory"), null)); + !sa.hasParam("Mandatory"), null)); // Targeted player (p) chooses N creatures that don't belong to them CardCollection notTgtPlayerCtrl = new CardCollection(choices); notTgtPlayerCtrl.removeAll(tgtPlayerCtrl); chosen.addAll(p.getController().chooseCardsForEffect(notTgtPlayerCtrl, sa, title + " " + "you don't control", minAmount, validAmount, - !sa.hasParam("Mandatory"), null)); + !sa.hasParam("Mandatory"), null)); - } else if ((tgt == null) || p.canBeTargetedBy(sa)) { - if (sa.hasParam("AtRandom") && !choices.isEmpty()) { - // don't pass FCollection for direct modification, the Set part would get messed up - chosen = new CardCollection(Aggregates.random(choices, validAmount)); - dontRevealToOwner = false; + } else if (sa.hasParam("AtRandom") && !choices.isEmpty()) { + // don't pass FCollection for direct modification, the Set part would get messed up + chosen = new CardCollection(Aggregates.random(choices, validAmount)); + dontRevealToOwner = false; + } else { + String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " "; + if (sa.hasParam ("ChoiceTitleAppend")) { + String tag = ""; + String value = sa.getParam("ChoiceTitleAppend"); + if (value.startsWith("Defined ")) { + tag = AbilityUtils.getDefinedPlayers(host, value.substring(8), sa).toString(); + } else if (value.equals("ChosenType")) { + tag = host.getChosenType(); + } + if (!tag.equals("")) { + title = title + " (" + tag +")"; + } + } + if (sa.hasParam("QuasiLibrarySearch")) { + final Player searched = AbilityUtils.getDefinedPlayers(host, + sa.getParam("QuasiLibrarySearch"), sa).get(0); + final int fetchNum = Math.min(searched.getCardsIn(ZoneType.Library).size(), 4); + CardCollectionView shown = !p.hasKeyword("LimitSearchLibrary") + ? searched.getCardsIn(ZoneType.Library) : searched.getCardsIn(ZoneType.Library, fetchNum); + DelayedReveal delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(searched), + CardTranslation.getTranslatedName(host.getName()) + " - " + + Localizer.getInstance().getMessage("lblLookingCardIn") + " "); + Card choice = p.getController().chooseSingleEntityForEffect(choices, delayedReveal, sa, title, + !sa.hasParam("Mandatory"), p, null); + if (choice == null) { + return; + } + chosen.add(choice); } else { - String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " "; - if (sa.hasParam ("ChoiceTitleAppend")) { - String tag = ""; - String value = sa.getParam("ChoiceTitleAppend"); - if (value.startsWith("Defined ")) { - tag = AbilityUtils.getDefinedPlayers(host, value.substring(8), sa).toString(); - } else if (value.equals("ChosenType")) { - tag = host.getChosenType(); - } - if (!tag.equals("")) { - title = title + " (" + tag +")"; - } - } - if (sa.hasParam("QuasiLibrarySearch")) { - final Player searched = AbilityUtils.getDefinedPlayers(host, - sa.getParam("QuasiLibrarySearch"), sa).get(0); - final int fetchNum = Math.min(searched.getCardsIn(ZoneType.Library).size(), 4); - CardCollectionView shown = !p.hasKeyword("LimitSearchLibrary") - ? searched.getCardsIn(ZoneType.Library) : searched.getCardsIn(ZoneType.Library, fetchNum); - DelayedReveal delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(searched), - CardTranslation.getTranslatedName(host.getName()) + " - " + - Localizer.getInstance().getMessage("lblLookingCardIn") + " "); - Card choice = p.getController().chooseSingleEntityForEffect(choices, delayedReveal, sa, title, - !sa.hasParam("Mandatory"), p, null); - if (choice == null) { - return; - } - chosen.add(choice); - } else { - chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, - !sa.hasParam("Mandatory"), null)); - } + chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, + !sa.hasParam("Mandatory"), null)); } } if (sa.hasParam("Reveal") && !sa.hasParam("SecretlyChoose")) { 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/ChooseColorEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java index 75c5655960d..74154447d4f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java @@ -6,7 +6,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.Aggregates; import forge.util.Lang; import forge.util.Localizer; @@ -49,48 +48,45 @@ public class ChooseColorEffect extends SpellAbilityEffect { } } - final List tgtPlayers = getTargetPlayers(sa); - - final TargetRestrictions tgt = sa.getTargetRestrictions(); - - for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - List chosenColors = new ArrayList<>(); - int cntMin = sa.hasParam("TwoColors") ? 2 : 1; - int cntMax = sa.hasParam("TwoColors") ? 2 : sa.hasParam("OrColors") ? colorChoices.size() : 1; - String prompt = null; - if (cntMax == 1) { - prompt = Localizer.getInstance().getMessage("lblChooseAColor"); - } else { - if (cntMax > cntMin) { - if (cntMax >= MagicColor.NUMBER_OR_COLORS) { - prompt = Localizer.getInstance().getMessage("lblAtLastChooseNumColors", Lang.getNumeral(cntMin)); - } else { - prompt = Localizer.getInstance().getMessage("lblChooseSpecifiedRangeColors", Lang.getNumeral(cntMin), Lang.getNumeral(cntMax)); - } - } else { - prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax)); - } - } - Player noNotify = p; - if (sa.hasParam("Random")) { - String choice; - for (int i=0; i chosenColors = new ArrayList<>(); + int cntMin = sa.hasParam("TwoColors") ? 2 : 1; + int cntMax = sa.hasParam("TwoColors") ? 2 : sa.hasParam("OrColors") ? colorChoices.size() : 1; + String prompt = null; + if (cntMax == 1) { + prompt = Localizer.getInstance().getMessage("lblChooseAColor"); + } else { + if (cntMax > cntMin) { + if (cntMax >= MagicColor.NUMBER_OR_COLORS) { + prompt = Localizer.getInstance().getMessage("lblAtLastChooseNumColors", Lang.getNumeral(cntMin)); + } else { + prompt = Localizer.getInstance().getMessage("lblChooseSpecifiedRangeColors", Lang.getNumeral(cntMin), Lang.getNumeral(cntMax)); + } + } else { + prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax)); + } + } + Player noNotify = p; + if (sa.hasParam("Random")) { + String choice; + for (int i=0; i tgtPlayers = getDefinedPlayersOrTargeted(sa); - - for (final Player p : tgtPlayers) { + for (Player p : getDefinedPlayersOrTargeted(sa)) { + if (!p.isInGame()) { + p = getNewChooser(sa, sa.getActivatingPlayer(), p); + } // determine if any of the choices are not valid List saToRemove = Lists.newArrayList(); @@ -62,10 +63,6 @@ public class ChooseGenericEffect extends SpellAbilityEffect { } abilities.removeAll(saToRemove); - if (sa.usesTargeting() && sa.getTargets().contains(p) && !p.canBeTargetedBy(sa)) { - continue; - } - List chosenSAs = Lists.newArrayList(); String prompt = sa.getParamOrDefault("ChoicePrompt", "Choose"); boolean random = false; 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/ControlPlayerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java index 44131d15e8a..2490857f88f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java @@ -37,7 +37,7 @@ public class ControlPlayerEffect extends SpellAbilityEffect { @Override public void run() { // CR 800.4b - if (controller.hasLost()) { + if (!controller.isInGame()) { return; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index a13870820f3..f034c4312c7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -135,6 +135,9 @@ public class CopyPermanentEffect extends TokenEffectBase { } for (final Player controller : controllers) { + if (!controller.isInGame()) { + continue; + } List tgtCards = Lists.newArrayList(); if (sa.hasParam("ValidSupportedCopy")) { @@ -231,10 +234,8 @@ public class CopyPermanentEffect extends TokenEffectBase { continue; } - // if it only targets player, it already got all needed cards from defined - if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !c.canBeTargetedBy(sa)) { - continue; - } + // because copy should be able to copy LKI values, don't handle target and timestamp there + if (sa.hasParam("ForEach")) { for (Player p : AbilityUtils.getDefinedPlayers(host, sa.getParam("ForEach"), sa)) { Card proto = getProtoType(sa, c, controller); 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/CountersPutOrRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java index a55b34a1bc6..c36f92001f4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java @@ -79,18 +79,16 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect { CardTranslation.getTranslatedName(gameCard.getName())), null)) { continue; } - if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) { - if (gameCard.hasCounters()) { - if (eachExisting) { - for (CounterType listType : Lists.newArrayList(gameCard.getCounters().keySet())) { - addOrRemoveCounter(sa, gameCard, listType, counterAmount, table, pl); - } - } else { - addOrRemoveCounter(sa, gameCard, ctype, counterAmount, table, pl); + if (gameCard.hasCounters()) { + if (eachExisting) { + for (CounterType listType : Lists.newArrayList(gameCard.getCounters().keySet())) { + addOrRemoveCounter(sa, gameCard, listType, counterAmount, table, pl); } - } else if (!eachExisting && ctype != null) { - gameCard.addCounter(ctype, counterAmount, pl, table); + } else { + addOrRemoveCounter(sa, gameCard, ctype, counterAmount, table, pl); } + } else if (!eachExisting && ctype != null) { + gameCard.addCounter(ctype, counterAmount, pl, table); } } table.replaceCounterEffect(game, sa, true); 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/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 6e3df066009..f74cc649e8d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -272,14 +272,10 @@ public class DamageDealEffect extends DamageBaseEffect { if (c.isPhasedOut()) { continue; } - if (!sa.usesTargeting() || gc.canBeTargetedBy(sa)) { - internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap); - } + internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap); } else if (o instanceof Player) { final Player p = (Player) o; - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - damageMap.put(sourceLKI, p, dmg); - } + damageMap.put(sourceLKI, p, dmg); } } for (final Card unTgtC : untargetedCards) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java index c3faf6b5e08..26020eb59eb 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java @@ -1,10 +1,7 @@ package forge.game.ability.effects; -import java.util.List; - import forge.game.Game; import forge.game.GameEntityCounterTable; -import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardDamageMap; @@ -68,10 +65,6 @@ public class DamageEachEffect extends DamageBaseEffect { sources = CardLists.getValidCards(sources, sa.getParam("ValidCards"), sa.getActivatingPlayer(), card, sa); } - final List tgts = getTargets(sa, "DefinedPlayers"); - - final boolean targeted = sa.usesTargeting(); - boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); @@ -85,24 +78,21 @@ public class DamageEachEffect extends DamageBaseEffect { usedDamageMap = false; } - for (final Object o : tgts) { + for (final Object o : getTargetEntities(sa, "DefinedPlayers")) { for (final Card source : sources) { final Card sourceLKI = game.getChangeZoneLKIInfo(source); // TODO shouldn't that be using Num or something first? final int dmg = AbilityUtils.calculateAmount(source, "X", sa); - + if (o instanceof Card) { final Card c = (Card) o; - if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { + if (c.isInPlay()) { damageMap.put(sourceLKI, c, dmg); } } else if (o instanceof Player) { - final Player p = (Player) o; - if (!targeted || p.canBeTargetedBy(sa)) { - damageMap.put(sourceLKI, p, dmg); - } + damageMap.put(sourceLKI, (Player) o, dmg); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java index d06dab25e17..ac6eaa94b1f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java @@ -93,20 +93,15 @@ public class DamagePreventEffect extends DamagePreventEffectBase { final CardCollection untargetedCards = CardUtil.getRadiance(sa); - final boolean targeted = sa.usesTargeting(); - for (final GameObject o : tgts) { - numDam = targeted && sa.isDividedAsYouChoose() ? sa.getDividedValue(o) : numDam; + numDam = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(o) : numDam; if (o instanceof Card) { final Card c = (Card) o; - if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { + if (c.isInPlay()) { addPreventNextDamage(sa, o, numDam); } } else if (o instanceof Player) { - final Player p = (Player) o; - if (!targeted || p.canBeTargetedBy(sa)) { - addPreventNextDamage(sa, o, numDam); - } + addPreventNextDamage(sa, o, numDam); } } @@ -115,5 +110,5 @@ public class DamagePreventEffect extends DamagePreventEffectBase { addPreventNextDamage(sa, c, numDam); } } - } // preventDamageResolve + } } 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/DestroyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java index 470464d119f..257fd457768 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java @@ -64,16 +64,17 @@ public class DestroyEffect extends SpellAbilityEffect { CardZoneTable table = new CardZoneTable(); Map cachedMap = Maps.newHashMap(); for (final Card tgtC : tgtCards) { - if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) { - 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; - } - internalDestroy(gameCard, sa, table, cachedMap, params); + if (!tgtC.isInPlay()) { + continue; } + 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; + } + internalDestroy(gameCard, sa, table, cachedMap, params); } if (untargetedCards.size() > 1) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 659b86dd55e..896d3ba928d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -15,7 +15,6 @@ import forge.game.player.DelayedReveal; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.util.CardTranslation; @@ -169,19 +168,17 @@ public class DigEffect extends SpellAbilityEffect { } } - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final List tgtPlayers = getDefinedPlayersOrTargeted(sa); - CardZoneTable table = new CardZoneTable(); GameEntityCounterTable counterTable = new GameEntityCounterTable(); boolean combatChanged = false; CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield(); CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); - for (final Player p : tgtPlayers) { - if (tgt != null && !p.canBeTargetedBy(sa)) { + for (final Player p : getDefinedPlayersOrTargeted(sa)) { + if (!p.isInGame()) { continue; } + final CardCollection top = new CardCollection(); final CardCollection rest = new CardCollection(); CardCollection all = new CardCollection(p.getCardsIn(srcZone)); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java index d9b4ddf3e6b..e815b89f8a9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java @@ -40,7 +40,7 @@ public class DigMultipleEffect extends SpellAbilityEffect { CardZoneTable table = new CardZoneTable(); for (final Player chooser : getDefinedPlayersOrTargeted(sa)) { - if (sa.usesTargeting() && !chooser.canBeTargetedBy(sa)) { + if (!chooser.isInGame()) { continue; } final CardCollection top = new CardCollection(); 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/DiscardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java index 5b3f5c479ac..aab3d4d44c7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java @@ -134,9 +134,6 @@ public class DiscardEffect extends SpellAbilityEffect { // In this case the target need not be the discarding player discarders = getDefinedPlayersOrTargeted(sa); firstTarget = Iterables.getFirst(targets, null); - if (sa.usesTargeting() && !firstTarget.canBeTargetedBy(sa)) { - firstTarget = null; - } } else { discarders = targets; } @@ -144,6 +141,10 @@ public class DiscardEffect extends SpellAbilityEffect { final CardZoneTable table = new CardZoneTable(); Map discardedMap = Maps.newHashMap(); for (final Player p : discarders) { + if (!p.isInGame()) { + continue; + } + CardCollectionView toBeDiscarded = new CardCollection(); if ((mode.equals("RevealTgtChoose") && firstTarget != null) || !sa.usesTargeting() || p.canBeTargetedBy(sa)) { final int numCardsInHand = p.getCardsIn(ZoneType.Hand).size(); @@ -268,9 +269,6 @@ public class DiscardEffect extends SpellAbilityEffect { dPHand = p.getController().chooseCardsToRevealFromHand(amount, amount, dPHand); } - final String valid = sa.getParamOrDefault("DiscardValid", "Card"); - CardCollection validCards = CardLists.getValidCards(dPHand, valid, source.getController(), source, sa); - Player chooser = p; if (mode.endsWith("YouChoose")) { chooser = source.getController(); @@ -289,6 +287,9 @@ public class DiscardEffect extends SpellAbilityEffect { continue; } + final String valid = sa.getParamOrDefault("DiscardValid", "Card"); + CardCollection validCards = CardLists.getValidCards(dPHand, valid, source.getController(), source, sa); + int min = sa.hasParam("AnyNumber") || sa.hasParam("Optional") ? 0 : Math.min(validCards.size(), numCards); int max = sa.hasParam("AnyNumber") ? validCards.size() : Math.min(validCards.size(), numCards); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java index 1d1871b7b4c..1532eb72755 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java @@ -3,22 +3,19 @@ package forge.game.ability.effects; import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang3.StringUtils; - import forge.game.ability.SpellAbilityEffect; import forge.game.mana.Mana; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; +import forge.util.Lang; public class DrainManaEffect extends SpellAbilityEffect { @Override protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); - final List tgtPlayers = getTargetPlayers(sa); + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); - sb.append(StringUtils.join(tgtPlayers, ", ")); sb.append(" loses all unspent mana."); return sb.toString(); @@ -26,13 +23,13 @@ public class DrainManaEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - final TargetRestrictions tgt = sa.getTargetRestrictions(); List drained = new ArrayList<>(); for (final Player p : getTargetPlayers(sa)) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - drained.addAll(p.getManaPool().clearPool(false)); + if (!p.isInGame()) { + continue; } + drained.addAll(p.getManaPool().clearPool(false)); } if (sa.hasParam("DrainMana")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java index 5c90c773ed4..d67830d3895 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java @@ -62,8 +62,7 @@ public class DrawEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getDefinedPlayersOrTargeted(sa)) { - // TODO can this be removed? - if (sa.usesTargeting() && !p.canBeTargetedBy(sa)) { + if (!p.isInGame()) { continue; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java index 9e73af3ba86..b2e2dfad95b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java @@ -86,7 +86,6 @@ public class FlipOntoBattlefieldEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); final Card host = sa.getHostCard(); - final Player p = sa.getActivatingPlayer(); sb.append("Flip "); sb.append(host.toString()); diff --git a/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java b/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java index ad5116f444e..6b441f4f756 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java @@ -6,7 +6,6 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.zone.ZoneType; import forge.util.Lang; import java.util.List; @@ -36,12 +35,7 @@ public class GoadEffect extends SpellAbilityEffect { for (final Card tgtC : getDefinedCardsOrTargeted(sa)) { // only goad things on the battlefield - if (!game.getCardsIn(ZoneType.Battlefield).contains(tgtC)) { - continue; - } - - // make sure we can still target now if using targeting - if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) { + if (!tgtC.isInPlay()) { continue; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java index 08a50416ce5..01952a587fd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java @@ -58,13 +58,14 @@ public class LifeGainEffect extends SpellAbilityEffect { } for (final Player p : tgtPlayers) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - if (variableAmount) { - sa.setSVar("AFNotDrawnNum", sa.getSVar("AFNotDrawnNum_" + p.getId())); - lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa); - } - p.gainLife(lifeAmount, sa.getHostCard(), sa); + if (!p.isInGame()) { + continue; } + if (variableAmount) { + sa.setSVar("AFNotDrawnNum", sa.getSVar("AFNotDrawnNum_" + p.getId())); + lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa); + } + p.gainLife(lifeAmount, sa.getHostCard(), sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java index e8afee2a13e..a2afc3381ea 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java @@ -1,6 +1,5 @@ package forge.game.ability.effects; - import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.player.Player; @@ -43,9 +42,10 @@ public class LifeLoseEffect extends SpellAbilityEffect { final int lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - lifeLost += p.loseLife(lifeAmount, false, false); + if (!p.isInGame()) { + continue; } + lifeLost += p.loseLife(lifeAmount, false, false); } sa.setSVar("AFLifeLost", "Number$" + lifeLost); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java index 6ec9b377ee9..82fbb2d2d44 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java @@ -10,7 +10,6 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.player.Player; import forge.game.player.PlayerCollection; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Localizer; public class LifeSetEffect extends SpellAbilityEffect { @@ -22,29 +21,30 @@ public class LifeSetEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final boolean redistribute = sa.hasParam("Redistribute"); final int lifeAmount = redistribute ? 0 : AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa); - final TargetRestrictions tgt = sa.getTargetRestrictions(); final List lifetotals = new ArrayList<>(); final PlayerCollection players = getTargetPlayers(sa); if (redistribute) { for (final Player p : players) { - if (tgt == null || p.canBeTargetedBy(sa)) { - lifetotals.add(p.getLife()); + if (!p.isInGame()) { + continue; } + lifetotals.add(p.getLife()); } } for (final Player p : players.threadSafeIterable()) { - if (tgt == null || p.canBeTargetedBy(sa)) { - if (!redistribute) { - p.setLife(lifeAmount, sa); - } else { - List validChoices = getDistribution(players, true, lifetotals); - int life = sa.getActivatingPlayer().getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblLifeTotal") + ": " + p, validChoices, p); - p.setLife(life, sa); - lifetotals.remove((Integer) life); - players.remove(p); - } + if (!p.isInGame()) { + continue; + } + if (!redistribute) { + p.setLife(lifeAmount, sa); + } else { + List validChoices = getDistribution(players, true, lifetotals); + int life = sa.getActivatingPlayer().getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblLifeTotal") + ": " + p, validChoices, p); + p.setLife(life, sa); + lifetotals.remove((Integer) life); + players.remove(p); } } } @@ -87,7 +87,7 @@ public class LifeSetEffect extends SpellAbilityEffect { return validChoices; } } - return new ArrayList(); + return new ArrayList<>(); } /* (non-Javadoc) diff --git a/forge-game/src/main/java/forge/game/ability/effects/LookAtEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LookAtEffect.java index e2b27fa0e31..eb37cadfd91 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LookAtEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LookAtEffect.java @@ -2,30 +2,18 @@ package forge.game.ability.effects; import forge.game.Game; import forge.game.ability.SpellAbilityEffect; -import forge.game.card.Card; -import forge.game.card.CardCollection; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Lang; public class LookAtEffect extends SpellAbilityEffect { @Override public void resolve(final SpellAbility sa) { - final Card host = sa.getHostCard(); - final Game game = host.getGame(); + final Game game = sa.getHostCard().getGame(); final Player activator = sa.getActivatingPlayer(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final CardCollection targets = new CardCollection(); - for (final Card tgtCard : getTargetCards(sa)) { - if (tgt == null || tgtCard.canBeTargetedBy(sa)) { - targets.add(tgtCard); - } - } - - game.getAction().revealTo(targets, activator); + game.getAction().revealTo(getTargetCards(sa), activator); } @Override diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java index 05d62f0fd23..1a56a2150d8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java @@ -48,8 +48,7 @@ public class ManaEffect extends SpellAbilityEffect { final StringBuilder producedMana = new StringBuilder(); for (Player p : tgtPlayers) { - if (sa.usesTargeting() && !p.canBeTargetedBy(sa)) { - // Illegal target. Skip. + if (!p.isInGame()) { continue; } 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/MustBlockEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java index 57e817ab00b..ecc54d41580 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java @@ -62,13 +62,11 @@ public class MustBlockEffect extends SpellAbilityEffect { long ts = game.getNextTimestamp(); for (final Card c : tgtCards) { - if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) { - if (mustBlockAll) { - c.addMustBlockCards(ts, cards); - } else { - final Card attacker = cards.get(0); - c.addMustBlockCard(ts, attacker); - } + if (mustBlockAll) { + c.addMustBlockCards(ts, cards); + } else { + final Card attacker = cards.get(0); + c.addMustBlockCard(ts, attacker); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java index ae42a9eb585..b406d699175 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java @@ -29,13 +29,16 @@ public class PoisonEffect extends SpellAbilityEffect { final int amount = AbilityUtils.calculateAmount(host, sa.getParam("Num"), sa); GameEntityCounterTable table = new GameEntityCounterTable(); + for (final Player p : getTargetPlayers(sa)) { - if ((!sa.usesTargeting()) || p.canBeTargetedBy(sa)) { - if (amount >= 0) { - p.addPoisonCounters(amount, sa.getActivatingPlayer(), table); - } else { - p.removePoisonCounters(-amount, sa.getActivatingPlayer()); - } + if (!p.isInGame()) { + continue; + } + + if (amount >= 0) { + p.addPoisonCounters(amount, sa.getActivatingPlayer(), table); + } else { + p.removePoisonCounters(-amount, sa.getActivatingPlayer()); } } table.replaceCounterEffect(game, sa, true); 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/ProtectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ProtectEffect.java index 853f20f2aa9..9a39d627394 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ProtectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ProtectEffect.java @@ -90,7 +90,7 @@ public class ProtectEffect extends SpellAbilityEffect { } return sb.toString(); - } // protectStackDescription() + } @Override public void resolve(SpellAbility sa) { @@ -140,11 +140,6 @@ public class ProtectEffect extends SpellAbilityEffect { continue; } - // if this is a target, make sure we can still target now - if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) { - continue; - } - tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, 0, true); if (!"Permanent".equals(sa.getParam("Duration"))) { @@ -186,7 +181,7 @@ public class ProtectEffect extends SpellAbilityEffect { addUntilCommand(sa, untilEOT); } } - } // protectResolve() + } public static List getProtectionList(final SpellAbility sa) { final List gains = new ArrayList<>(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java index be5e0074fe2..d310adce2d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java @@ -199,7 +199,7 @@ public class PumpEffect extends SpellAbilityEffect { sb.append(" "); } - if (sa instanceof AbilitySub & sa.getRootAbility().getTargets().containsAll(tgts)) { + if (sa instanceof AbilitySub && sa.getRootAbility().getTargets().containsAll(tgts)) { //try to avoid having the same long list of targets twice in a StackDescription sb.append(tgts.size() == 1 && tgts.get(0) instanceof Card ? "It " : "They "); } else { @@ -266,7 +266,7 @@ public class PumpEffect extends SpellAbilityEffect { } return sb.toString(); - } // pumpStackDescription() + } @Override public void resolve(final SpellAbility sa) { @@ -274,7 +274,6 @@ public class PumpEffect extends SpellAbilityEffect { final Game game = activator.getGame(); final Card host = sa.getHostCard(); final long timestamp = game.getNextTimestamp(); - List tgts = Lists.newArrayList(); List tgtCards = getCardsfromTargets(sa); List tgtPlayers = getTargetPlayers(sa); @@ -296,8 +295,6 @@ public class PumpEffect extends SpellAbilityEffect { keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, host, sa); } - tgts.addAll(tgtCards); - tgts.addAll(tgtPlayers); final CardCollection untargetedCards = CardUtil.getRadiance(sa); if (sa.hasParam("DefinedKW")) { @@ -433,11 +430,6 @@ public class PumpEffect extends SpellAbilityEffect { continue; } - // if pump is a target, make sure we can still target now - if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) { - continue; - } - // substitute specific tgtC mana cost for keyword placeholder CardManaCost List affectedKeywords = Lists.newArrayList(keywords); @@ -486,7 +478,7 @@ public class PumpEffect extends SpellAbilityEffect { } for (Player p : tgtPlayers) { - if (!p.canBeTargetedBy(sa)) { + if (!p.isInGame()) { continue; } @@ -494,5 +486,5 @@ public class PumpEffect extends SpellAbilityEffect { } replaceDying(sa); - } // pumpResolve() + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java index 3d6c7ab50bd..b2124e95ca9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java @@ -11,7 +11,6 @@ import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; 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; @@ -70,19 +69,15 @@ public class RearrangeTopOfLibraryEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - int numCards = 0; Card host = sa.getHostCard(); - boolean shuffle = false; - - final TargetRestrictions tgt = sa.getTargetRestrictions(); - - numCards = AbilityUtils.calculateAmount(host, sa.getParam("NumCards"), sa); - shuffle = sa.hasParam("MayShuffle"); + int numCards = AbilityUtils.calculateAmount(host, sa.getParam("NumCards"), sa); + boolean shuffle = sa.hasParam("MayShuffle"); for (final Player p : getTargetPlayers(sa)) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - rearrangeTopOfLibrary(host, p, numCards, shuffle, sa); + if (!p.isInGame()) { + continue; } + rearrangeTopOfLibrary(host, p, numCards, shuffle, sa); } } 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/ReorderZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java index 1b2cb597848..ffd17743cdd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java @@ -7,7 +7,6 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.card.CardCollection; 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.MyRandom; @@ -26,17 +25,18 @@ public class ReorderZoneEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final ZoneType zone = ZoneType.smartValueOf(sa.getParam("Zone")); boolean shuffle = sa.hasParam("Random"); - final TargetRestrictions tgt = sa.getTargetRestrictions(); for (final Player p : getTargetPlayers(sa)) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - CardCollection list = new CardCollection(p.getCardsIn(zone)); - if (shuffle) { - Collections.shuffle(list, MyRandom.getRandom()); - p.getZone(zone).setCards(list); - } else { - p.getController().orderMoveToZoneList(list, zone, sa); - } + if (!p.isInGame()) { + continue; + } + + CardCollection list = new CardCollection(p.getCardsIn(zone)); + if (shuffle) { + Collections.shuffle(list, MyRandom.getRandom()); + p.getZone(zone).setCards(list); + } else { + p.getController().orderMoveToZoneList(list, zone, sa); } } } 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/ScryEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java index 5d77f6350b1..a2719abc4f3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java @@ -43,8 +43,10 @@ public class ScryEffect extends SpellAbilityEffect { // Optional here for spells that have optional multi-player scrying for (final Player p : getTargetPlayers(sa)) { - if ( (!sa.usesTargeting() || p.canBeTargetedBy(sa)) && - (!isOptional || p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWanttoScry"), null)) ) { + if (!p.isInGame()) { + continue; + } + if (!isOptional || p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWanttoScry"), null)) { players.add(p); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index da458252bbb..f0c5f9cb494 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -93,10 +93,6 @@ public class SetStateEffect extends SpellAbilityEffect { continue; } - if (sa.usesTargeting() && !gameCard.canBeTargetedBy(sa)) { - continue; - } - // Cards which are not on the battlefield should not be able to transform. // TurnFace should be allowed in other zones like Exile too // Specialize and Unspecialize are allowed in other zones diff --git a/forge-game/src/main/java/forge/game/ability/effects/ShuffleEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ShuffleEffect.java index 0832d421302..ecfc6e98e8f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ShuffleEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ShuffleEffect.java @@ -6,7 +6,6 @@ import java.util.List; import forge.game.ability.SpellAbilityEffect; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.Localizer; public class ShuffleEffect extends SpellAbilityEffect { @@ -15,16 +14,13 @@ public class ShuffleEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final boolean optional = sa.hasParam("Optional"); - final List tgtPlayers = getTargetPlayers(sa); - - final TargetRestrictions tgt = sa.getTargetRestrictions(); - - for (final Player p : tgtPlayers) { - if ((tgt == null) || p.canBeTargetedBy(sa)) { - boolean mustShuffle = !optional || sa.getActivatingPlayer().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblHaveTargetShuffle", p.getName()), null); - if (mustShuffle) - p.shuffle(sa); + for (final Player p : getTargetPlayers(sa)) { + if (!p.isInGame()) { + continue; } + boolean mustShuffle = !optional || sa.getActivatingPlayer().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblHaveTargetShuffle", p.getName()), null); + if (mustShuffle) + p.shuffle(sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java index f79c5c9e43e..56c8492ad0c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java @@ -42,13 +42,14 @@ public class SurveilEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - if (isOptional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantSurveil"), null)) { - continue; - } - - p.surveil(num, sa, table, moveParams); + if (!p.isInGame()) { + continue; } + if (isOptional && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantSurveil"), null)) { + continue; + } + + p.surveil(num, sa, table, moveParams); } table.triggerChangesZoneAll(sa.getHostCard().getGame(), sa); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/TakeInitiativeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TakeInitiativeEffect.java index 9fa95a288a5..c8fcaa5dba2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TakeInitiativeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TakeInitiativeEffect.java @@ -27,9 +27,11 @@ public class TakeInitiativeEffect extends SpellAbilityEffect { final String set = sa.getHostCard().getSetCode(); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - p.getGame().getAction().takeInitiative(p, set); + if (!p.isInGame()) { + continue; } + + p.getGame().getAction().takeInitiative(p, set); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/TapAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TapAllEffect.java index 873e3096b59..982bb6e2b8f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TapAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TapAllEffect.java @@ -1,12 +1,9 @@ package forge.game.ability.effects; -import java.util.List; - import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.player.Player; import forge.game.spellability.AbilitySub; @@ -34,17 +31,10 @@ public class TapAllEffect extends SpellAbilityEffect { } CardCollectionView cards; - - final List tgtPlayers = getTargetPlayers(sa); - if (!sa.usesTargeting() && !sa.hasParam("Defined")) { cards = game.getCardsIn(ZoneType.Battlefield); } else { - CardCollection cards2 = new CardCollection(); - for (final Player p : tgtPlayers) { - cards2.addAll(p.getCardsIn(ZoneType.Battlefield)); - } - cards = cards2; + cards = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield); } cards = AbilityUtils.filterListByType(cards, sa.getParam("ValidCards"), sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TapEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TapEffect.java index 33b1402c859..656f9cde456 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TapEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TapEffect.java @@ -23,9 +23,6 @@ public class TapEffect extends SpellAbilityEffect { if (tgtC.isPhasedOut()) { continue; } - if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) { - continue; - } if (tgtC.isInPlay()) { if (tgtC.isUntapped() && remTapped || alwaysRem) { card.addRemembered(tgtC); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java index a041728e6d1..ef60486fb0f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java @@ -1,8 +1,5 @@ package forge.game.ability.effects; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; import forge.game.Game; import forge.game.ability.AbilityUtils; @@ -14,9 +11,8 @@ import forge.game.player.Player; import forge.game.player.PlayerController; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.Lang; import forge.util.Localizer; -import forge.util.collect.FCollection; - public class TapOrUntapAllEffect extends SpellAbilityEffect { @@ -29,8 +25,7 @@ public class TapOrUntapAllEffect extends SpellAbilityEffect { if (sa.hasParam("ValidMessage")) { sb.append(sa.getParam("ValidMessage")); } else { - final List tgtCards = getTargetCards(sa); - sb.append(StringUtils.join(tgtCards, ", ")); + sb.append(Lang.joinHomogenous(getTargetCards(sa))); } sb.append("."); return sb.toString(); @@ -38,19 +33,18 @@ public class TapOrUntapAllEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - CardCollectionView validCards = getTargetCards(sa); final Player activator = sa.getActivatingPlayer(); final Game game = activator.getGame(); - FCollection targetedPlayers = getTargetPlayers(sa); - + CardCollectionView validCards; if (sa.hasParam("ValidCards")) { - validCards = game.getCardsIn(ZoneType.Battlefield); - validCards = AbilityUtils.filterListByType(validCards, sa.getParam("ValidCards"), sa); + validCards = AbilityUtils.filterListByType(game.getCardsIn(ZoneType.Battlefield), sa.getParam("ValidCards"), sa); + } else { + validCards = getTargetCards(sa); } if (sa.usesTargeting() || sa.hasParam("Defined")) { - validCards = CardLists.filterControlledBy(validCards, targetedPlayers); + validCards = CardLists.filterControlledBy(validCards, getTargetPlayers(sa)); } // Default to tapping for AI @@ -66,13 +60,14 @@ public class TapOrUntapAllEffect extends SpellAbilityEffect { toTap = sa.getActivatingPlayer().getController().chooseBinary(sa, sb.toString(), PlayerController.BinaryChoiceType.TapOrUntap); - for (final Card cad : validCards) { - if (cad.isInPlay()) { - if (toTap) { - cad.tap(true); - } else { - cad.untap(true); - } + for (final Card tgtC : validCards) { + if (!tgtC.isInPlay()) { + continue; + } + if (toTap) { + tgtC.tap(true); + } else { + tgtC.untap(true); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java index eb911ac0b89..3c732f920e1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java @@ -2,14 +2,12 @@ package forge.game.ability.effects; import java.util.List; -import org.apache.commons.lang3.StringUtils; - import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.PlayerController; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.CardTranslation; +import forge.util.Lang; import forge.util.Localizer; public class TapOrUntapEffect extends SpellAbilityEffect { @@ -24,8 +22,7 @@ public class TapOrUntapEffect extends SpellAbilityEffect { sb.append("Tap or untap "); - final List tgtCards = getTargetCards(sa); - sb.append(StringUtils.join(tgtCards, ", ")); + sb.append(Lang.joinHomogenous(getTargetCards(sa))); sb.append("."); return sb.toString(); } @@ -33,24 +30,24 @@ public class TapOrUntapEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final List tgtCards = getTargetCards(sa); - - final TargetRestrictions tgt = sa.getTargetRestrictions(); PlayerController pc = sa.getActivatingPlayer().getController(); for (final Card tgtC : tgtCards) { + if (!tgtC.isInPlay()) { + continue; + } if (tgtC.isPhasedOut()) { continue; } - if (tgtC.isInPlay() && ((tgt == null) || tgtC.canBeTargetedBy(sa))) { - // If the effected card is controlled by the same controller of the SA, default to untap. - boolean tap = pc.chooseBinary(sa, Localizer.getInstance().getMessage("lblTapOrUntapTarget", CardTranslation.getTranslatedName(tgtC.getName())), PlayerController.BinaryChoiceType.TapOrUntap, - !tgtC.getController().equals(sa.getActivatingPlayer()) ); - if (tap) { - tgtC.tap(true); - } else { - tgtC.untap(true); - } + // If the effected card is controlled by the same controller of the SA, default to untap. + boolean tap = pc.chooseBinary(sa, Localizer.getInstance().getMessage("lblTapOrUntapTarget", CardTranslation.getTranslatedName(tgtC.getName())), PlayerController.BinaryChoiceType.TapOrUntap, + !tgtC.getController().equals(sa.getActivatingPlayer()) ); + + if (tap) { + tgtC.tap(true); + } else { + tgtC.untap(true); } } } 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")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java index c25aae259d5..947920ec8ef 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java @@ -1,18 +1,14 @@ package forge.game.ability.effects; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import forge.game.Game; import forge.game.GameEntity; -import forge.game.GameObject; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.Lang; public class UnattachAllEffect extends SpellAbilityEffect { private static void handleUnattachment(final GameEntity o, final Card cardToUnattach) { @@ -121,8 +117,7 @@ public class UnattachAllEffect extends SpellAbilityEffect { protected String getStackDescription(final SpellAbility sa) { final StringBuilder sb = new StringBuilder(); sb.append("Unattach all valid Equipment and Auras from "); - final List targets = getTargets(sa); - sb.append(StringUtils.join(targets, " ")); + sb.append(Lang.joinHomogenous(getTargets(sa))); return sb.toString(); } @@ -130,19 +125,14 @@ public class UnattachAllEffect extends SpellAbilityEffect { public void resolve(final SpellAbility sa) { Card source = sa.getHostCard(); final Game game = sa.getActivatingPlayer().getGame(); - final List targets = getTargets(sa); // If Cast Targets will be checked on the Stack - for (final Object o : targets) { - if (!(o instanceof GameEntity)) { - continue; - } - + for (final GameEntity ge : getTargetEntities(sa)) { String valid = sa.getParam("UnattachValid"); CardCollectionView unattachList = game.getCardsIn(ZoneType.Battlefield); unattachList = CardLists.getValidCards(unattachList, valid, source.getController(), source, sa); for (final Card c : unattachList) { - handleUnattachment((GameEntity) o, c); + handleUnattachment(ge, c); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java index 65453f29391..5bdfc94b070 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UnattachEffect.java @@ -1,12 +1,9 @@ package forge.game.ability.effects; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.spellability.SpellAbility; +import forge.util.Lang; public class UnattachEffect extends SpellAbilityEffect { /* (non-Javadoc) @@ -16,8 +13,7 @@ public class UnattachEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); sb.append("Unattach "); - final List targets = getTargetCards(sa); - sb.append(StringUtils.join(targets, " ")); + sb.append(Lang.joinHomogenous(getTargetCards(sa))); return sb.toString(); } @@ -26,15 +22,9 @@ public class UnattachEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - final List unattachList = getTargetCards(sa); - for (final Card cardToUnattach : unattachList) { - if (cardToUnattach.isAura()) { - //final boolean gainControl = "GainControl".equals(af.parseParams().get("AILogic")); - //AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl); - } else if (cardToUnattach.isAttachment()) { - if (cardToUnattach.isAttachedToEntity()) { - cardToUnattach.unattachFromEntity(cardToUnattach.getEntityAttachedTo()); - } + for (final Card cardToUnattach : getTargetCards(sa)) { + if (cardToUnattach.isAttachment() && cardToUnattach.isAttachedToEntity()) { + cardToUnattach.unattachFromEntity(cardToUnattach.getEntityAttachedTo()); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/UntapAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UntapAllEffect.java index c749602dcde..538117eb812 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UntapAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UntapAllEffect.java @@ -1,13 +1,9 @@ package forge.game.ability.effects; -import java.util.List; - import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.player.Player; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; @@ -25,17 +21,12 @@ public class UntapAllEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); CardCollectionView list; - List tgtPlayers = getTargetPlayers(sa); final String valid = sa.getParamOrDefault("ValidCards", ""); if (!sa.usesTargeting() && !sa.hasParam("Defined")) { list = sa.getActivatingPlayer().getGame().getCardsIn(ZoneType.Battlefield); } else { - CardCollection list2 = new CardCollection(); - for (final Player p : tgtPlayers) { - list2.addAll(p.getCardsIn(ZoneType.Battlefield)); - } - list = list2; + list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield); } list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), card, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java index 12f893c2870..9ac90333844 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java @@ -48,9 +48,6 @@ public class UntapEffect extends SpellAbilityEffect { if (tgtC.isPhasedOut()) { continue; } - if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) { - continue; - } if (tgtC.isInPlay()) { tgtC.untap(true); } @@ -86,6 +83,10 @@ public class UntapEffect extends SpellAbilityEffect { final String valid = sa.getParam("UntapType"); for (final Player p : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa)) { + if (!p.isInGame()) { + continue; + } + CardCollectionView list = CardLists.getValidCards(p.getGame().getCardsIn(ZoneType.Battlefield), valid, sa.getActivatingPlayer(), sa.getHostCard(), sa); list = CardLists.filter(list, Presets.TAPPED); diff --git a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java index 239576a109c..e2fe177b61d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java @@ -132,9 +132,10 @@ public class VentureEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getTargetPlayers(sa)) { - if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - ventureIntoDungeon(sa, p, moveParams); + if (!p.isInGame()) { + continue; } + ventureIntoDungeon(sa, p, moveParams); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index fe0c36e6c23..a1d0489d59f 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -717,8 +717,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (!turnFaceDown(true) && !isFaceDown()) { return null; } - // Move to p's battlefield - Game game = p.getGame(); // Just in case you aren't the controller, now you are! setController(p, game.getNextTimestamp()); @@ -726,6 +724,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // Mark this card as "manifested" setManifested(true); + // Move to p's battlefield Card c = game.getAction().moveToPlay(this, p, sa, params); if (c.isInPlay()) { c.setManifested(true); @@ -3656,7 +3655,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (hasKeyword(Keyword.RECONFIGURE)) { // need extra time stamp so it doesn't collide with existing ones long ts = getGame().getNextTimestamp(); - // TODO make it use a Static Layer Effect instead + // 702.151b Attaching an Equipment with reconfigure to another creature causes the Equipment to stop being a creature until it becomes unattached from that creature. + // it is not a Static Ability addChangedCardTypes(null, CardType.parse("Creature", true), false, false, false, false, false, false, false, false, ts, 0, true, false); GameCommand unattach = new GameCommand() { @@ -6107,7 +6107,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { @Override public final boolean canBeTargetedBy(final SpellAbility sa) { - if (getOwner().hasLost()) { + if (!getOwner().isInGame()) { return false; } diff --git a/forge-gui/res/cardsfolder/d/dhalsim_pliable_pacifist.txt b/forge-gui/res/cardsfolder/d/dhalsim_pliable_pacifist.txt index 26484bd1b2c..566dbc3a41b 100644 --- a/forge-gui/res/cardsfolder/d/dhalsim_pliable_pacifist.txt +++ b/forge-gui/res/cardsfolder/d/dhalsim_pliable_pacifist.txt @@ -5,10 +5,10 @@ PT:1/3 K:Reach S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Hexproof | IsPresent$ Card.Self+notattacking | Description$ Teleport — CARDNAME has hexproof unless he's attacking. T:Mode$ Attacks | ValidCard$ Creature.YouCtrl+withReach | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Whenever a creature you control with reach attacks, untap it and it can't be blocked by creatures with greater power this combat. -SVar:TrigUntap:DB$ Untap | Defined$ TriggeredAttackerLKICopy | SubAbility$ DBAnimate -SVar:DBAnimate:DB$ Animate | Defined$ TriggeredAttackerLKICopy | staticAbilities$ CantBeBlockedPow | Duration$ UntilEndOfCombat -SVar:CantBeBlockedPow:Mode$ CantBlockBy | ValidAttacker$ Card.Self | ValidBlocker$ Creature.powerGTX | Description$ CARDNAME can't be blocked by creatures with greater power this combat. -SVar:X:Count$CardPower +SVar:TrigUntap:DB$ Untap | Defined$ TriggeredAttackerLKICopy | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ TriggeredAttacker | StaticAbilities$ CantBeBlockedPow | ForgetOnMoved$ Battlefield | Duration$ UntilEndOfCombat +SVar:CantBeBlockedPow:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | ValidBlocker$ Creature.powerGTX | Description$ CARDNAME can't be blocked by creatures with greater power this combat. +SVar:X:Remembered$CardPower T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ TrigDraw | TriggerDescription$ Fierce Punch — Whenever one or more creatures you control deal combat damage to a player, draw a card. SVar:TrigDraw:DB$ Draw DeckHints:Keyword$Reach diff --git a/forge-gui/res/cardsfolder/f/fog_patch.txt b/forge-gui/res/cardsfolder/f/fog_patch.txt index 79f682c5f9c..d3977f3a37f 100644 --- a/forge-gui/res/cardsfolder/f/fog_patch.txt +++ b/forge-gui/res/cardsfolder/f/fog_patch.txt @@ -2,7 +2,6 @@ Name:Fog Patch ManaCost:1 G Types:Instant Text:Cast this spell only during the declare blockers step. -A:SP$ RepeatEach | Cost$ 1 G | ActivationPhases$ Declare Blockers | RepeatSubAbility$ DBBecomeBlocked | RepeatCards$ Creature.attacking | SpellDescription$ Attacking creatures become blocked. (This spell works on creatures that can't be blocked.) -SVar:DBBecomeBlocked:DB$ BecomesBlocked | Defined$ Remembered +A:SP$ BecomesBlocked | Cost$ 1 G | Defined$ Valid Creature.attacking | ActivationPhases$ Declare Blockers | SpellDescription$ Attacking creatures become blocked. (This spell works on creatures that can't be blocked.) AI:RemoveDeck:All Oracle:Cast this spell only during the declare blockers step.\nAttacking creatures become blocked. (This spell works on creatures that can't be blocked.) diff --git a/forge-gui/res/cardsfolder/m/mist_of_stagnation.txt b/forge-gui/res/cardsfolder/m/mist_of_stagnation.txt index 20c68b65047..cfefc77f823 100644 --- a/forge-gui/res/cardsfolder/m/mist_of_stagnation.txt +++ b/forge-gui/res/cardsfolder/m/mist_of_stagnation.txt @@ -2,10 +2,8 @@ Name:Mist of Stagnation ManaCost:3 U U Types:Enchantment S:Mode$ Continuous | Affected$ Permanent | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Permanents don't untap during their controllers' untap steps. -T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ TrigChoose | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each player's upkeep, that player chooses a permanent for each card in their graveyard, then untaps those permanents. -SVar:TrigChoose:DB$ ChooseCard | Defined$ TriggeredPlayer | Amount$ X | Mandatory$ True | Choices$ Permanent.ActivePlayerCtrl | ChoiceTitle$ Choose a permanent to untap | AILogic$ Untap | SubAbility$ DBUntap -SVar:DBUntap:DB$ UntapAll | ValidCards$ Permanent.ChosenCard | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ DBUntap | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each player's upkeep, that player chooses a permanent for each card in their graveyard, then untaps those permanents. +SVar:DBUntap:DB$ Untap | UntapExactly$ True | Defined$ TriggeredPlayer | Amount$ X | UntapType$ Card SVar:X:Count$ValidGraveyard Card.ActivePlayerCtrl AI:RemoveDeck:Random SVar:NonStackingEffect:True diff --git a/forge-gui/res/cardsfolder/t/triumphant_adventurer.txt b/forge-gui/res/cardsfolder/t/triumphant_adventurer.txt index 253cd65fa7b..f82bd4a284d 100644 --- a/forge-gui/res/cardsfolder/t/triumphant_adventurer.txt +++ b/forge-gui/res/cardsfolder/t/triumphant_adventurer.txt @@ -4,7 +4,7 @@ Types:Creature Human Knight PT:1/1 K:Deathtouch S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has first strike. -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBVenture | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks, venture into the dungeon. (Enter the first room or advance to the next room.) +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBVenture | TriggerDescription$ Whenever CARDNAME attacks, venture into the dungeon. (Enter the first room or advance to the next room.) SVar:DBVenture:DB$ Venture | Defined$ You SVar:HasAttackEffect:TRUE Oracle:Deathtouch\nAs long as it's your turn, Triumphant Adventurer has first strike.\nWhenever Triumphant Adventurer attacks, venture into the dungeon. (Enter the first room or advance to the next room.)