From f0c35775a1a7654ebfb1a27a6ba55b68bc8adafb Mon Sep 17 00:00:00 2001 From: Christian 'ketura' McCarty Date: Mon, 5 Dec 2022 23:47:29 -0600 Subject: [PATCH] More errata - Added support for new ForEachRace value resolver - Added Get off the Road, Hand of Sauron, His Cruelty and Malice, Massing in the East, and Tower of Barad-dur errata --- .../pc/errata/set03/set03-Sauron-errata.hjson | 518 ++++++++++++++---- .../appender/resolver/ValueResolver.java | 10 + .../com/gempukku/lotro/logic/GameUtils.java | 15 +- 3 files changed, 445 insertions(+), 98 deletions(-) diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Sauron-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Sauron-errata.hjson index 2d7a8b922..637d29d38 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Sauron-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Sauron-errata.hjson @@ -91,100 +91,430 @@ } } - # 73_88: { - # cardInfo: { - # //Either a full URL, or a subpath for the PC image server - # imagePath: errata/LOTR-EN03E088.1.jpg - # //If this is true, then all gameplay-related info outside this cardInfo definition - # // will be ignored and the java class loaded instead. - # javaClass: false - # //This instructs the blueprint generator to insert this card as an alt of the listed - # // parent blueprint. Can of course be ommitted if not an errata or promo. - # parent: 3_88 - # //This is the tree path to use within the alts structure on the parent. - # // Can of course be ommitted if parent is null. - # parentPath: errata/pc - # //Versioning differentiates releases within a particular alt path, such as PC errata - # // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. - # //Top-level cards should always be version 0. - # version: 1 - # collInfo: 3U88 - # rarity: U - # setNum: "3" - # cardNum: 88 - # // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. - # style: Standard - # } - # title: Get Off the Road! - # unique: false - # culture: sauron - # twilight: 0 - # type: event - # keyword: [ - # maneuver - # search - # ] - # effects: { - # type: event - # cost: { - # type: exert - # filter: choose(culture(sauron),minion) - # } - # effect: [ - # { - # type: RevealRandomCardsFromHand - # hand: freeps - # memorize: revealedCard - # forced: true - # count: 1 - # } - # { - # type: memorizeNumber - # memory: revealedTwilightCost - # amount: { - # type: twilightCostInMemory - # memory: revealedCard - # } - # } - # { - # type: ChooseCardsFromDrawDeck - # filter: choose(minion,MaxTwilight(memory(revealedTwilightCost))) - # memorize: chosenCard - # text: Choose a minion to take into hand from your draw deck - # } - # { - # type: preventable - # player: freeps - # cost: { - # type: discardfromhand - # hand: freeps - # player: freeps - # forced: false - # count: { - # type: fromMemory - # memory: twilightCostInMemory - # } - # } - # text: Discard {twilightCostInMemory} card from hand to prevent your opponent taking {chosenCard} into hand from their draw deck - # effect: { - # type: PutCardsFromDeckIntoHand - # filter: memory(chosenCard) - # } - # } - # ] - # } - # gametext: Search.Spot a [sauron] minion and a Nazgûl to exert a companion for each burden you can spot. The Free Peoples player may make the move limit -1 to prevent this. - # lore: "'The Morgul-lord and his Black Riders have come forth. War is preparing!'" - # promotext: "" - # alts: { - # //These are just CardInfo objects - # promos: { - # } - # //These are full card definitions, with redundant info that is the same as the original card ommitted - # errata: { - # } - # } - # } + 73_88: { + cardInfo: { + //Either a full URL, or a subpath for the PC image server + imagePath: errata/LOTR-EN03E088.1.jpg + //If this is true, then all gameplay-related info outside this cardInfo definition + // will be ignored and the java class loaded instead. + javaClass: false + //This instructs the blueprint generator to insert this card as an alt of the listed + // parent blueprint. Can of course be ommitted if not an errata or promo. + parent: 3_88 + //This is the tree path to use within the alts structure on the parent. + // Can of course be ommitted if parent is null. + parentPath: errata/pc + //Versioning differentiates releases within a particular alt path, such as PC errata + // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. + //Top-level cards should always be version 0. + version: 1 + collInfo: 3U88 + rarity: U + setNum: "3" + cardNum: 88 + // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. + style: Standard + } + title: Get Off the Road! + unique: false + culture: sauron + twilight: 0 + type: event + keyword: [ + maneuver + search + ] + effects: { + type: event + effect: { + type: conditional + requires: [ + { + type: canSpot + filter: culture(sauron) + } + { + type: canSpot + filter: nazgul + } + ] + effect: { + type: preventable + player: freeps + cost: { + type: addModifier + modifier: { + type: modifyMoveLimit + amount: -1 + } + until: endOfTurn + } + text: Make the move limit -1 to prevent exerting a companion for each burden + effect: { + type: exert + player: freeps + times: { + type: ForEachBurden + } + filter: choose(companion) + } + } + } + } + gametext: Search.Spot a [sauron] card and a Nazgul to make the Free Peoples player exert a companion for each burden you can spot. The Free Peoples player may make the move limit -1 to prevent this. + lore: "'The Morgul-lord and his Black Riders have come forth. War is preparing!'" + promotext: "" + alts: { + //These are just CardInfo objects + promos: { + } + //These are full card definitions, with redundant info that is the same as the original card ommitted + errata: { + } + } + } + + 73_90: { + cardInfo: { + //Either a full URL, or a subpath for the PC image server + imagePath: errata/LOTR-EN03E090.1.jpg + //If this is true, then all gameplay-related info outside this cardInfo definition + // will be ignored and the java class loaded instead. + javaClass: false + //This instructs the blueprint generator to insert this card as an alt of the listed + // parent blueprint. Can of course be ommitted if not an errata or promo. + parent: 3_90 + //This is the tree path to use within the alts structure on the parent. + // Can of course be ommitted if parent is null. + parentPath: errata/pc + //Versioning differentiates releases within a particular alt path, such as PC errata + // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. + //Top-level cards should always be version 0. + version: 1 + collInfo: 3C90 + rarity: C + setNum: "3" + cardNum: 90 + // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. + style: Standard + } + title: Hand of Sauron + unique: false + culture: sauron + twilight: 0 + type: event + keyword: [ + maneuver + ] + effects: { + type: event + cost: { + type: exert + filter: choose(culture(sauron),minion,unique) + } + effect: [ + { + type: chooseActiveCards + filter: choose(companion,not(exhausted)) + memorize: chosenComp + text: Choose a companion to exhaust + } + { + type: preventable + player: freeps + cost: { + type: discardCardAtRandomFromHand + forced: false + hand: freeps + count: 2 + } + text: Discard 2 cards at random from hand to prevent exhausting {chosenComp} + effect: { + type: exhaust + filter: memory(chosenComp) + } + } + ] + } + gametext: Exert a unique [sauron] minion to exhaust a companion (except the Ring-bearer). The Free Peoples player may discard 2 cards at random from their hand to prevent this. + lore: "'The power of the Ring could not be undone.'" + promotext: "" + alts: { + //These are just CardInfo objects + promos: { + } + //These are full card definitions, with redundant info that is the same as the original card ommitted + errata: { + } + } + } + + 73_91: { + cardInfo: { + //Either a full URL, or a subpath for the PC image server + imagePath: errata/LOTR-EN03E091.1.jpg + //If this is true, then all gameplay-related info outside this cardInfo definition + // will be ignored and the java class loaded instead. + javaClass: false + //This instructs the blueprint generator to insert this card as an alt of the listed + // parent blueprint. Can of course be ommitted if not an errata or promo. + parent: 3_91 + //This is the tree path to use within the alts structure on the parent. + // Can of course be ommitted if parent is null. + parentPath: errata/pc + //Versioning differentiates releases within a particular alt path, such as PC errata + // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. + //Top-level cards should always be version 0. + version: 1 + collInfo: 3R91 + rarity: R + setNum: "3" + cardNum: 91 + // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. + style: Standard + } + title: His Cruelty and Malice + unique: false + culture: sauron + twilight: 1 + type: condition + keyword: [ + Support Area + ] + effects: { + type: activated + phase: regroup + cost: [ + { + type: chooseActiveCards + filter: choose(culture(sauron),minion) + memorize: chosenMinion + text: Choose a [Sauron] minion to exert or discard + } + { + type: choice + texts: [ + Exert {chosenMinion} + Discard {chosenMinion} + ] + effects: [ + { + type: exert + filter: memory(chosenMinion) + } + { + type: discard + filter: memory(chosenMinion) + } + ] + } + ] + effect: [ + { + type: chooseActiveCards + filter: choose(companion,not(exhausted)) + memorize: chosenComp + text: Choose a companion to exert + } + { + type: preventable + player: freeps + cost: { + type: discardTopCardsFromDeck + deck: freeps + count: 3 + forced: false + } + text: Discard the top 3 cards of your deck to prevent {chosenComp} + effect: { + type: exert + filter: memory(chosenComp) + } + } + ] + } + gametext: Regroup: Exert or discard a [sauron] minion to exert a companion. The Free Peoples player may discard the top 3 cards from their draw deck to prevent this. + lore: "'Into this Ring he poured his cruelty, his malice, and his will to dominate all life.'" + promotext: "" + alts: { + //These are just CardInfo objects + promos: { + } + //These are full card definitions, with redundant info that is the same as the original card ommitted + errata: { + } + } + } + + 73_92: { + cardInfo: { + //Either a full URL, or a subpath for the PC image server + imagePath: errata/LOTR-EN03E092.1.jpg + //If this is true, then all gameplay-related info outside this cardInfo definition + // will be ignored and the java class loaded instead. + javaClass: false + //This instructs the blueprint generator to insert this card as an alt of the listed + // parent blueprint. Can of course be ommitted if not an errata or promo. + parent: 3_92 + //This is the tree path to use within the alts structure on the parent. + // Can of course be ommitted if parent is null. + parentPath: errata/pc + //Versioning differentiates releases within a particular alt path, such as PC errata + // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. + //Top-level cards should always be version 0. + version: 1 + collInfo: 3U92 + rarity: U + setNum: "3" + cardNum: 92 + // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. + style: Standard + } + title: Massing in the East + unique: false + culture: sauron + twilight: 1 + type: event + keyword: [ + regroup + ] + effects: { + type: event + cost: { + type: discard + filter: choose(culture(sauron),minion) + } + effect: [ + { + type: memorizeNumber + memory: raceCount + amount: { + type: ForEachRace + filter: companion + } + } + { + type: chooseActiveCards + filter: choose(culture(sauron),minion) + memorize: chosenMinions + text: Choose minions to return to hand + count: { + type: fromMemory + memory: raceCount + } + } + { + type: preventable + player: freeps + cost: { + type: DiscardFromHand + forced: false + hand: freeps + count: { + type: fromMemory + memory: raceCount + } + } + text: Discard {raceCount} cards to prevent Shadow taking {chosenMinions} back into hand + effect: { + type: ReturnToHand + player: shadow + filter: memory(chosenMinions) + } + } + ] + } + gametext: To play, discard a [sauron] minion. Return up to X [sauron] minions to hand, where X is the number of races you can spot in the fellowship. The Free Peoples player may discard X cards from hand to prevent this. + lore: "'The power of the Black Land grows....'" + promotext: "" + alts: { + //These are just CardInfo objects + promos: { + } + //These are full card definitions, with redundant info that is the same as the original card ommitted + errata: { + } + } + } + + 73_104: { + cardInfo: { + //Either a full URL, or a subpath for the PC image server + imagePath: errata/LOTR-EN03E104.1.jpg + //If this is true, then all gameplay-related info outside this cardInfo definition + // will be ignored and the java class loaded instead. + javaClass: false + //This instructs the blueprint generator to insert this card as an alt of the listed + // parent blueprint. Can of course be ommitted if not an errata or promo. + parent: 3_104 + //This is the tree path to use within the alts structure on the parent. + // Can of course be ommitted if parent is null. + parentPath: errata/pc + //Versioning differentiates releases within a particular alt path, such as PC errata + // version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2. + //Top-level cards should always be version 0. + version: 1 + collInfo: 3R104 + rarity: R + setNum: "3" + cardNum: 104 + // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard. + style: Standard + } + title: Tower of Barad-dur + unique: false + culture: sauron + twilight: 1 + type: condition + keyword: [ + Support Area + ] + + effects: [ + { + type: ExtraCost + cost: { + type: exert + filter: choose(culture(sauron),minion) + } + } + { + type: trigger + optional: true + trigger: { + type: moves + } + effect: [ + { + type: preventable + player: freeps + cost: { + type: DiscardFromHand + forced: false + hand: freeps + count: 1 + } + text: Discard a card from hand to prevent Shadow drawing 2 cards + effect: { + type: drawCards + count: 2 + } + } + ] + } + ] + gametext: To play, exert a [sauron] minion.
Each time the fellowship moves, draw 2 cards. The Free Peoples player may discard a card from hand to prevent this. + lore: "The Dark Tower had been rebuilt, it was said. From there the power was spreading...." + promotext: "" + alts: { + //These are just CardInfo objects + promos: { + } + //These are full card definitions, with redundant info that is the same as the original card ommitted + errata: { + } + } + } } \ No newline at end of file diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/ValueResolver.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/ValueResolver.java index 5757cdcf7..896460c31 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/ValueResolver.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/ValueResolver.java @@ -237,6 +237,16 @@ public class ValueResolver { int result = Math.max(0, spottable - over); return new ConstantEvaluator(result); }; + } else if (type.equalsIgnoreCase("forEachRace")) { + FieldUtils.validateAllowedFields(object, "over", "filter"); + final int over = FieldUtils.getInteger(object.get("over"), "over", 0); + final String filter = FieldUtils.getString(object.get("filter"), "filter", "any"); + final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); + return actionContext -> { + int spottable = GameUtils.getSpottableRacesCount(actionContext.getGame(), filterableSource.getFilterable(actionContext)); + int result = Math.max(0, spottable - over); + return new ConstantEvaluator(result); + }; } else if (type.equalsIgnoreCase("forEachFPCultureLessThan")) { FieldUtils.validateAllowedFields(object, "amount"); final int lessThan = FieldUtils.getInteger(object.get("amount"), "amount"); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/GameUtils.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/GameUtils.java index 6ca598c3a..76ba97ef4 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/GameUtils.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/GameUtils.java @@ -1,10 +1,7 @@ package com.gempukku.lotro.logic; import com.gempukku.lotro.cards.build.ActionContext; -import com.gempukku.lotro.common.Culture; -import com.gempukku.lotro.common.Filterable; -import com.gempukku.lotro.common.Side; -import com.gempukku.lotro.common.Token; +import com.gempukku.lotro.common.*; import com.gempukku.lotro.filters.Filters; import com.gempukku.lotro.game.LotroCardBlueprint; import com.gempukku.lotro.game.PhysicalCard; @@ -200,6 +197,16 @@ public class GameUtils { return cultures.size(); } + public static int getSpottableRacesCount(LotroGame game, Filterable... filters) { + Set races = new HashSet<>(); + for (PhysicalCard physicalCard : Filters.filterActive(game, filters)) { + final Race race = physicalCard.getBlueprint().getRace(); + if (race != null) + races.add(race); + } + return races.size(); + } + public static String formatNumber(int effective, int requested) { if (effective != requested) return effective + "(out of " + requested + ")";