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 c969088479a..8c6b04601b5 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 @@ -33,7 +33,6 @@ import forge.util.Aggregates; import forge.util.Localizer; import forge.util.PredicateString.StringOp; import forge.util.TextUtil; -import forge.util.collect.FCollectionView; public class CopyPermanentEffect extends TokenEffectBase { @@ -105,112 +104,6 @@ public class CopyPermanentEffect extends TokenEffectBase { final Card host = sa.getHostCard(); final Player activator = sa.getActivatingPlayer(); final Game game = host.getGame(); - - if (sa.hasParam("Optional") && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblCopyPermanentConfirm"))) { - return; - } - - final int numCopies = sa.hasParam("NumCopies") ? AbilityUtils.calculateAmount(host, sa.getParam("NumCopies"), sa) : 1; - - Player controller = null; - if (sa.hasParam("Controller")) { - final FCollectionView defined = AbilityUtils.getDefinedPlayers(host, sa.getParam("Controller"), sa); - if (!defined.isEmpty()) { - controller = defined.getFirst(); - } - } - if (controller == null) { - controller = activator; - } - - List tgtCards = Lists.newArrayList(); - - if (sa.hasParam("ValidSupportedCopy")) { - List cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards()); - String valid = sa.getParam("ValidSupportedCopy"); - if (valid.contains("X")) { - valid = TextUtil.fastReplace(valid, - "X", Integer.toString(AbilityUtils.calculateAmount(host, "X", sa))); - } - if (StringUtils.containsIgnoreCase(valid, "creature")) { - Predicate cpp = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES); - cards = Lists.newArrayList(Iterables.filter(cards, cpp)); - } - if (StringUtils.containsIgnoreCase(valid, "equipment")) { - Predicate cpp = Predicates.compose(CardRulesPredicates.Presets.IS_EQUIPMENT, PaperCard.FN_GET_RULES); - cards = Lists.newArrayList(Iterables.filter(cards, cpp)); - } - if (sa.hasParam("RandomCopied")) { - List copysource = Lists.newArrayList(cards); - List choice = Lists.newArrayList(); - final String num = sa.getParamOrDefault("RandomNum", "1"); - int ncopied = AbilityUtils.calculateAmount(host, num, sa); - while (ncopied > 0 && !copysource.isEmpty()) { - final PaperCard cp = Aggregates.random(copysource); - Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set - - if (possibleCard.isValid(valid, host.getController(), host, sa)) { - if (host.getController().isAI() && possibleCard.getRules() != null && possibleCard.getRules().getAiHints().getRemAIDecks()) - continue; - choice.add(possibleCard); - ncopied -= 1; - } - copysource.remove(cp); - } - tgtCards = choice; - - System.err.println("Copying random permanent(s): " + tgtCards.toString()); - } else if (sa.hasParam("DefinedName")) { - String name = sa.getParam("DefinedName"); - if (name.equals("NamedCard")) { - if (!host.getNamedCard().isEmpty()) { - name = host.getNamedCard(); - } - } - - Predicate cpp = Predicates.compose(CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard.FN_GET_RULES); - cards = Lists.newArrayList(Iterables.filter(cards, cpp)); - - if (!cards.isEmpty()) { - tgtCards.add(Card.fromPaperCard(cards.get(0), controller)); - } - } - } else if (sa.hasParam("Choices")) { - Player chooser = activator; - if (sa.hasParam("Chooser")) { - final String choose = sa.getParam("Chooser"); - chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0); - } - - // For Mimic Vat with mutated creature, need to choose one imprinted card - CardCollectionView choices = sa.hasParam("Defined") ? getDefinedCardsOrTargeted(sa) : game.getCardsIn(ZoneType.Battlefield); - choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host, sa); - if (!choices.isEmpty()) { - String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard"); - - if (sa.hasParam("WithDifferentNames")) { - // any Number of choices with different names - while (!choices.isEmpty()) { - Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, true, null); - - if (choosen != null) { - tgtCards.add(choosen); - choices = CardLists.filter(choices, Predicates.not(CardPredicates.sharesNameWith(choosen))); - } else if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) { - break; - } - } - } else { - Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null); - if (choosen != null) { - tgtCards.add(choosen); - } - } - } - } else { - tgtCards = getDefinedCardsOrTargeted(sa); - } - boolean useZoneTable = true; CardZoneTable triggerList = sa.getChangeZoneTable(); if (triggerList == null) { @@ -225,21 +118,127 @@ public class CopyPermanentEffect extends TokenEffectBase { MutableBoolean combatChanged = new MutableBoolean(false); TokenCreateTable tokenTable = new TokenCreateTable(); - for (final Card c : tgtCards) { - // if it only targets player, it already got all needed cards from defined - if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !c.canBeTargetedBy(sa)) { - continue; - } - if (sa.hasParam("ForEach")) { - for (Player p : AbilityUtils.getDefinedPlayers(host, sa.getParam("ForEach"), sa)) { - Card proto = getProtoType(sa, c, controller); - proto.addRemembered(p); - tokenTable.put(controller, proto, numCopies); + if (sa.hasParam("Optional") && !activator.getController().confirmAction(sa, null, + Localizer.getInstance().getMessage("lblCopyPermanentConfirm"))) { + return; + } + + final int numCopies = sa.hasParam("NumCopies") ? AbilityUtils.calculateAmount(host, + sa.getParam("NumCopies"), sa) : 1; + + List controllers = Lists.newArrayList(); + if (sa.hasParam("Controller")) { + controllers = AbilityUtils.getDefinedPlayers(host, sa.getParam("Controller"), sa); + } + if (controllers.isEmpty()) { + controllers.add(activator); + } + + for (final Player controller : controllers) { + List tgtCards = Lists.newArrayList(); + + if (sa.hasParam("ValidSupportedCopy")) { + List cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards()); + String valid = sa.getParam("ValidSupportedCopy"); + if (valid.contains("X")) { + valid = TextUtil.fastReplace(valid, + "X", Integer.toString(AbilityUtils.calculateAmount(host, "X", sa))); + } + if (StringUtils.containsIgnoreCase(valid, "creature")) { + Predicate cpp = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES); + cards = Lists.newArrayList(Iterables.filter(cards, cpp)); + } + if (StringUtils.containsIgnoreCase(valid, "equipment")) { + Predicate cpp = Predicates.compose(CardRulesPredicates.Presets.IS_EQUIPMENT, PaperCard.FN_GET_RULES); + cards = Lists.newArrayList(Iterables.filter(cards, cpp)); + } + if (sa.hasParam("RandomCopied")) { + List copysource = Lists.newArrayList(cards); + List choice = Lists.newArrayList(); + final String num = sa.getParamOrDefault("RandomNum", "1"); + int ncopied = AbilityUtils.calculateAmount(host, num, sa); + while (ncopied > 0 && !copysource.isEmpty()) { + final PaperCard cp = Aggregates.random(copysource); + Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set + + if (possibleCard.isValid(valid, host.getController(), host, sa)) { + if (host.getController().isAI() && possibleCard.getRules() != null && possibleCard.getRules().getAiHints().getRemAIDecks()) + continue; + choice.add(possibleCard); + ncopied -= 1; + } + copysource.remove(cp); + } + tgtCards = choice; + + System.err.println("Copying random permanent(s): " + tgtCards.toString()); + } else if (sa.hasParam("DefinedName")) { + String name = sa.getParam("DefinedName"); + if (name.equals("NamedCard")) { + if (!host.getNamedCard().isEmpty()) { + name = host.getNamedCard(); + } + } + + Predicate cpp = Predicates.compose(CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard.FN_GET_RULES); + cards = Lists.newArrayList(Iterables.filter(cards, cpp)); + + if (!cards.isEmpty()) { + tgtCards.add(Card.fromPaperCard(cards.get(0), controller)); + } + } + } else if (sa.hasParam("Choices")) { + Player chooser = activator; + if (sa.hasParam("Chooser")) { + final String choose = sa.getParam("Chooser"); + chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0); + } + + // For Mimic Vat with mutated creature, need to choose one imprinted card + CardCollectionView choices = sa.hasParam("Defined") ? getDefinedCardsOrTargeted(sa) : game.getCardsIn(ZoneType.Battlefield); + choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host, sa); + if (!choices.isEmpty()) { + String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard"); + + if (sa.hasParam("WithDifferentNames")) { + // any Number of choices with different names + while (!choices.isEmpty()) { + Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, true, null); + + if (choosen != null) { + tgtCards.add(choosen); + choices = CardLists.filter(choices, Predicates.not(CardPredicates.sharesNameWith(choosen))); + } else if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) { + break; + } + } + } else { + Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null); + if (choosen != null) { + tgtCards.add(choosen); + } + } } } else { - tokenTable.put(controller, getProtoType(sa, c, controller), numCopies); + tgtCards = getDefinedCardsOrTargeted(sa); } - } // end foreach Card + + for (final Card c : tgtCards) { + // if it only targets player, it already got all needed cards from defined + if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !c.canBeTargetedBy(sa)) { + continue; + } + if (sa.hasParam("ForEach")) { + for (Player p : AbilityUtils.getDefinedPlayers(host, sa.getParam("ForEach"), sa)) { + Card proto = getProtoType(sa, c, controller); + proto.addRemembered(p); + tokenTable.put(controller, proto, numCopies); + } + } else { + tokenTable.put(controller, getProtoType(sa, c, controller), numCopies); + } + } // end foreach Card + } makeTokenTable(tokenTable, true, triggerList, combatChanged, sa); 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 31a2baa0152..be2e7579c3d 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 @@ -18,12 +18,12 @@ public class GoadEffect extends SpellAbilityEffect { final boolean remember = sa.hasParam("RememberGoaded"); for (final Card tgtC : getDefinedCardsOrTargeted(sa)) { - // only pump things in PumpZone + // only goad things on the battlefield if (!game.getCardsIn(ZoneType.Battlefield).contains(tgtC)) { continue; } - // if pump is a target, make sure we can still target now + // make sure we can still target now if using targeting if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) { continue; } @@ -31,16 +31,19 @@ public class GoadEffect extends SpellAbilityEffect { // 701.38d is handled by getGoaded tgtC.addGoad(timestamp, player); - final GameCommand untilEOT = new GameCommand() { - private static final long serialVersionUID = -1731759226844770852L; + // currently, only Life of the Party uses Duration$ – Duration$ Permanent + if (!sa.hasParam("Duration")) { + final GameCommand untilEOT = new GameCommand() { + private static final long serialVersionUID = -1731759226844770852L; - @Override - public void run() { - tgtC.removeGoad(timestamp); - } - }; + @Override + public void run() { + tgtC.removeGoad(timestamp); + } + }; - game.getCleanup().addUntil(player, untilEOT); + game.getCleanup().addUntil(player, untilEOT); + } if (remember && tgtC.isGoaded()) { sa.getHostCard().addRemembered(tgtC); 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 2703f546590..f5295311b61 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -535,7 +535,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return currentStateName; } - // use by CopyPermament + // use by CopyPermanent public void setStates(Map map) { states.clear(); states.putAll(map); diff --git a/forge-gui/res/cardsfolder/upcoming/life_of_the_party.txt b/forge-gui/res/cardsfolder/upcoming/life_of_the_party.txt new file mode 100644 index 00000000000..6b276b4a3ad --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/life_of_the_party.txt @@ -0,0 +1,17 @@ +Name:Life of the Party +ManaCost:3 R +Types:Creature Elemental +PT:0/1 +K:First Strike +K:Trample +K:Haste +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, it gets +X/+0 until end of turn, where X is the number of creatures you control. +SVar:TrigPump:DB$ Pump | NumAtt$ X +SVar:X:Count$Valid Creature.YouCtrl +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+nonToken | Execute$ TrigCopyPermanent | TriggerDescription$ When CARDNAME enters the battlefield, if it's not a token, each opponent creates a token that's a copy of it. The tokens are goaded for the rest of the game. (They attack each combat if able and attack a player other than you if able.) +SVar:TrigCopyPermanent:DB$ CopyPermanent | Defined$ TriggeredCard | Controller$ Opponent | RememberTokens$ True | SubAbility$ DBGoad +SVar:DBGoad:DB$ Goad | Defined$ Remembered | Duration$ Permanent | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:NeedsToPlayVar:X GE3 +SVar:HasAttackEffect:TRUE +Oracle:First strike, trample, haste\nWhenever Life of the Party attacks, it gets +X/+0 until end of turn, where X is the number of creatures you control.\nWhen Life of the Party enters the battlefield, if it's not a token, each opponent creates a token that's a copy of it. The tokens are goaded for the rest of the game. (They attack each combat if able and attack a player other than you if able.)