From c0cda4eec7e822b6bd49cc6ca654e3897bad438b Mon Sep 17 00:00:00 2001 From: MarcinSc Date: Mon, 22 Apr 2024 11:25:10 +0700 Subject: [PATCH] Migrated Isengard cards in set 5 to hjson --- .../cards/official/set01/set1-sauron.hjson | 4 +- .../cards/official/set01/set1-wraith.hjson | 2 +- .../resources/cards/official/set01/set1.hjson | 2 +- .../cards/official/set04/set04-Dwarven.hjson | 2 +- .../cards/official/set04/set04-Sites.hjson | 2 +- .../cards/official/set05/set05-Gollum.hjson | 2 +- .../cards/official/set05/set05-Gondor.hjson | 45 +- .../official/set05}/set05-Isengard.hjson | 901 ++++++++++++------ .../cards/official/set09/set9-1.hjson | 4 +- .../cards/official/set09/set9-2.hjson | 2 +- .../cards/official/set14/set14.hjson | 2 +- .../unofficial/hobbit/set31/set31-fp.hjson | 2 +- .../hobbit/set31/set31-shadow.hjson | 2 +- .../hobbit/set32/set32-shadow.hjson | 2 +- .../unofficial/hobbit/set33/set33-fp.hjson | 2 +- .../unofficial/hobbit/set33/set33-sites.hjson | 2 +- .../set01/set01-Ringwraith-errata.hjson | 2 +- .../pc/errata/set01/set01-Sauron-errata.hjson | 4 +- .../errata/set02/set02-Dwarven-errata.hjson | 2 +- .../pc/errata/set03/set03-Moria-errata.hjson | 2 +- .../errata/set04/set04-Isengard-errata.hjson | 2 +- .../errata/set05/set05-Isengard-errata.hjson | 2 +- .../pc/errata/set10/set10-Rohan-errata.hjson | 2 +- .../pc/errata/set15/set15-Gondor-errata.hjson | 2 +- .../second-edition/set40/set40-moria.hjson | 2 +- .../build/field/effect/appender/DoWhile.java | 33 +- .../effect/appender/PlayCardFromDiscard.java | 9 +- .../build/field/effect/appender/Repeat.java | 10 +- .../field/effect/filter/FilterFactory.java | 1 + .../effect/requirement/CanSpotFPCultures.java | 18 + .../requirement/RequirementFactory.java | 1 + .../trigger/AboutToDiscardFromPlay.java | 16 +- .../effect/trigger/UsesSpecialAbility.java | 8 +- .../com/gempukku/lotro/filters/Filters.java | 9 + .../actions/AbstractCostToEffectAction.java | 8 + .../lotro/logic/actions/DoWhileSubAction.java | 37 + .../lotro/logic/timing/TriggerConditions.java | 5 +- 37 files changed, 787 insertions(+), 366 deletions(-) rename gemp-lotr/gemp-lotr-cards/src/main/resources/{card-stubs => cards/official/set05}/set05-Isengard.hjson (58%) create mode 100644 gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/CanSpotFPCultures.java create mode 100644 gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/DoWhileSubAction.java diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-sauron.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-sauron.hjson index c2850a3df..b6f64491d 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-sauron.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-sauron.hjson @@ -23,7 +23,7 @@ ] effect: { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: companion over: 4 @@ -371,7 +371,7 @@ ] effect: { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: companion over: 5 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson index 2a6bd0d6f..f08db05d1 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson @@ -1008,7 +1008,7 @@ ] effect: { type: Repeat - amount: { + times: { type: forEachYouCanSpot filter: companion over: 4 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1.hjson index a5918523f..a0967584b 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1.hjson @@ -1228,7 +1228,7 @@ effect: [ { type: repeat - amount: 3 + times: 3 effect: { type: playCardFromDiscard filter: choose(tracker) diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Dwarven.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Dwarven.hjson index cbe342c05..dc4724a28 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Dwarven.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Dwarven.hjson @@ -802,7 +802,7 @@ } { type: repeat - amount: { + times: { type: forEachInMemory memory: stackedCards } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Sites.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Sites.hjson index 9aacbef69..f15edd73d 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Sites.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set04/set04-Sites.hjson @@ -554,7 +554,7 @@ } effect: { type: repeat - amount: 3 + times: 3 effect: { type: heal count: 0-1 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gollum.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gollum.hjson index cdf5fe21c..55ff57a2e 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gollum.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gollum.hjson @@ -422,7 +422,7 @@ } { type: repeat - amount: { + times: { type: forEachMatchingInMemory filter: side(shadow) memory: revealedCards diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gondor.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gondor.hjson index 9d1e2ed6b..1d4084ec8 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gondor.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Gondor.hjson @@ -566,39 +566,18 @@ effect: [ { type: doWhile - condition: [ - { - type: memoryIs - memory: answer - value: yes - } - { - type: memoryMatches - memory: playedCard - filter: possession - } - ] - effect: [ - { - type: chooseYesOrNo - text: Would you like to play next possession? - memorize: answer - } - { - type: conditional - requires: { - type: memoryIs - memory: answer - value: yes - } - effect: { - type: playCardFromDiscard - filter: choose(possession) - on: companion,signet(Aragorn) - memorize: playedCard - } - } - ] + effect: { + type: playCardFromDiscard + optional: true + filter: choose(possession) + on: companion,signet(Aragorn) + memorize: playedCard + } + condition: { + type: memoryMatches + memory: playedCard + filter: possession + } } ] } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/card-stubs/set05-Isengard.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Isengard.hjson similarity index 58% rename from gemp-lotr/gemp-lotr-cards/src/main/resources/card-stubs/set05-Isengard.hjson rename to gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Isengard.hjson index 531c15b10..3fe0e9252 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/card-stubs/set05-Isengard.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set05/set05-Isengard.hjson @@ -25,17 +25,35 @@ Machine Support Area ] - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: skirmish + requires: [ + { + type: canSpot + filter: companion + count: 5 + } + { + type: canSpot + filter: self,hasToken(isengard) + } + ] + effect: [ + { + type: addKeyword + filter: choose(urukHai) + keyword: damage + amount: 1 + } + { + type: discard + filter: self + } + ] + } + ] gametext: Machine. Plays to your support area.
Shadow: Exert an Uruk-hai to place an [isengard] token on this card.
Skirmish: Spot 5 companions and an [isengard] token here to make an Uruk-hai damage +1. Discard this condition. lore: The trees... smote the timbers with a rending boom. promotext: "" @@ -73,17 +91,41 @@ vitality: 3 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: modifyStrength + filter: self,inSkirmish + amount: { + type: forEachWound + filter: character,inSkirmish + } + } + } + { + type: activated + phase: skirmish + cost: [ + { + type: removeTokens + filter: choose(machine) + culture: isengard + count: 3 + } + { + type: exert + filter: self + times: 2 + } + ] + effect: { + type: wound + filter: all(ally) + times: 2 + } + } + ] gametext: Damage +1.
Berserk Rager is strength +1 for each wound on a character in its skirmish.
Skirmish: Remove 3 [isengard] tokens from a machine and exert Berserk Rager twice to wound every ally twice. lore: Their mission at Helm's Deep was to clear the walls. promotext: "" @@ -121,17 +163,41 @@ vitality: 3 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: modifyStrength + filter: self,inSkirmish + amount: { + type: forEachWound + filter: character,inSkirmish + } + } + } + { + type: activated + phase: skirmish + cost: [ + { + type: removeTokens + filter: choose(machine) + culture: isengard + count: 4 + } + { + type: exert + filter: self + times: 2 + } + ] + effect: { + type: modifyStrength + filter: self + amount: 8 + } + } + ] gametext: Damage +1.
Berserk Savage is strength +1 for each wound on a character in its skirmish.
Skirmish: Remove 4 [isengard] tokens from a machine and exert Berserk Savage twice to make it strength +8. lore: Skull-like helmets are distinctive to the Berserkers. promotext: "" @@ -169,17 +235,41 @@ vitality: 3 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: modifyStrength + filter: self,inSkirmish + amount: { + type: forEachWound + filter: character,inSkirmish + multiplier: 2 + } + } + } + { + type: activated + phase: skirmish + cost: [ + { + type: removeTokens + filter: choose(machine) + culture: isengard + count: 5 + } + { + type: exert + filter: self + times: 2 + } + ] + effect: { + type: wound + filter: all(companion) + } + } + ] gametext: Damage +1.
Berserk Slayer is strength +2 for each wound on a character in its skirmish.
Skirmish: Remove 5 [isengard] tokens from a machine and exert Berserk Slayer twice to wound every companion. lore: Berserkers are the first wave of Uruks sent to battle. promotext: "" @@ -213,20 +303,41 @@ twilight: 1 type: Event keywords: Maneuver - /*requires: { - - } effects: { type: event cost: { - + type: chooseHowManyToSpot + filter: urukHai + text: Choose how many Uruk-hai to spot + memorize: spotCount }, effect: [ { - + type: repeat + times: { + type: fromMemory + memory: spotCount + } + effect: { + type: wound + player: fp + filter: choose(minion) + count: 0-1 + } + } + { + type: repeat + times: { + type: fromMemory + memory: spotCount + } + effect: { + type: playCardFromDiscard + filter: choose(culture(isengard),weapon) + } } ] - }*/ + } gametext: Maneuver: Spot X Uruk-hai. The Free Peoples player may assign X wounds to your minions. Play X [isengard] weapons from your discard pile. lore: Some were squat and broad, some were tall and grim, with high helms and sable shields. promotext: "" @@ -263,17 +374,39 @@ Machine Support Area ] - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: shadow + cost: { + type: exert + filter: choose(urukHai) + } + effect: { + type: addTokens + culture: isengard + filter: self + } + } + { + type: activated + phase: maneuver + requires: { + type: canSpot + filter: self,hasTokenCount(5,isengard) + } + effect: [ + { + type: exert + filter: all(character) + } + { + type: discard + filter: self + } + ] + } + ] gametext: Machine. Plays to your support area.
Shadow: Exert an Uruk-hai to place an [isengard] token on this card.
Maneuver: Spot 8 [isengard] tokens here to exert every character. Discard this condition. lore: They have a blasting fire, and... they took the Wall.' promotext: "" @@ -311,17 +444,34 @@ vitality: 3 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + { + type: modifier + modifier: { + type: addKeyword + filter: self,vitalityMoreThan(2) + keyword: ambush + amount: 1 + } + } + { + type: modifier + modifier: { + type: addKeyword + filter: self,vitalityMoreThan(3) + keyword: damage + amount: 1 + } + } + ] gametext: Warg-rider.
While Foul Horde is not exhausted, it is fierce.
While Foul Horde has at least 3 vitality, it is ambush (1).
While Foul Horde has at least 4 vitality, it is damage +1. lore: "" promotext: "" @@ -359,17 +509,32 @@ strength: 4 vitality: 3 site: 3 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: maneuver + requires: { + type: canSpotFPCultures + amount: 4 + } + cost: { + type: exert + filter: self + } + effect: [ + { + type: chooseActiveCards + filter: choose(unbound,companion,canBeReturnedToHand) + text: Choose unbound companion to return to hand + memorize: chosenCompanion + } + { + type: returnToHand + filter: all(or(memory(chosenCompanion),attachedTo(memory(chosenCompanion)))) + } + ] + } + ] gametext: Maneuver: Spot 4 Free Peoples cultures and exert GrĂ­ma to return an unbound companion, and all cards on him or her, to their owners' hands. lore: I care for you and yours as best I may.... Let others deal with these irksome guests.' promotext: "" @@ -407,17 +572,16 @@ vitality: 2 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + ] gametext: Warg-rider.
While this minion is not exhausted, he is fierce. lore: Sharku used his warg-riders to scout and defend. promotext: "" @@ -455,17 +619,16 @@ vitality: 3 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + ] gametext: Warg-rider.
While this minion is not exhausted, he is fierce. lore: He has taken Orcs into his service, and Wolf-riders, and evil Men....' promotext: "" @@ -500,18 +663,24 @@ type: Possession strength: 2 itemclass: Hand weapon - #target: title(an [isengard] Orc) - /*requires: { - - } + target: culture(isengard),orc effects: [ - { - - } - { - - } - ]*/ + { + type: trigger + optional: true + trigger: { + type: played + filter: self + } + effect: { + type: addTwilight + amount: { + type: forEachYouCanSpot + filter: controlledSite + } + } + } + ] gametext: Bearer must be an [isengard] Orc.
When you play this weapon, you may add (1) for each site you control. lore: Orcs from Isengard favor a short, curved sword when mounted. promotext: "" @@ -549,17 +718,31 @@ vitality: 3 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + { + type: trigger + optional: true + trigger: { + type: winsSkirmish + filter: culture(isengard),orc + } + cost: { + type: exert + filter: self + } + effect: { + type: takeControlOfSite + } + } + ] gametext: Warg-rider.
While this minion is not exhausted, it is fierce.
Each time an [isengard] Orc wins a skirmish, you may exert this minion to control a site. lore: ...where the warg howls, there also the orc prowls.' promotext: "" @@ -597,17 +780,58 @@ strength: 8 vitality: 4 site: 4 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: cantTakeWounds + requires: [ + { + type: sarumanFirstSentenceActive + } + { + type: phase + phase: archery + } + ] + filter: self + } + } + { + type: modifier + modifier: { + type: cantBeAssignedToSkirmish + requires: { + type: sarumanFirstSentenceActive + } + filter: self + } + } + { + type: trigger + optional: true + trigger: { + type: moves + } + effect: { + type: heal + filter: all(culture(isengard),orc) + times: 2 + } + } + { + type: activated + phase: shadow + cost: { + type: exert + filter: self + } + effect: { + type: playCardFromDiscard + filter: choose(culture(isengard),possession) + } + } + ] gametext: Saruman may not take wounds during the archery phase and may not be assigned to a skirmish.
Each time the fellowship moves, you may heal each [isengard] Orc twice.
Shadow: Exert Saruman to play an [isengard] possession from your discard pile. lore: "" promotext: "" @@ -644,17 +868,39 @@ Machine Support Area ] - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: shadow + cost: { + type: exert + filter: choose(urukHai) + } + effect: { + type: addTokens + culture: isengard + filter: self + } + } + { + type: activated + phase: skirmish + requires: { + type: canSpot + filter: self,hasToken(isengard) + } + effect: [ + { + type: heal + filter: choose(urukHai) + } + { + type: discard + filter: self + } + ] + } + ] gametext: Machine. Plays to your support area.
Shadow: Exert an Uruk-hai to place an [isengard] token on this card.
Skirmish: Spot an [isengard] token here to heal an Uruk-hai. Discard this condition. lore: Hundreds of long ladders were lifted up. Many were cast down in ruin, but many more replaced them.... promotext: "" @@ -693,17 +939,29 @@ vitality: 2 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + { + type: trigger + optional: true + trigger: { + type: played + filter: culture(isengard),mount + } + effect: { + type: exert + player: fp + filter: choose(companion,not(hobbit)) + } + } + ] gametext: Warg-rider.
While Sharku is not exhausted, he is fierce.
Each time you play an [isengard] mount, you may make the Free Peoples player exert a companion (except a Hobbit). lore: Only Sharku commanded enough respect to control Saruman's warg-riders. promotext: "" @@ -739,18 +997,36 @@ strength: 5 vitality: 3 itemclass: Mount - #target: a warg-rider - /*requires: { - - } + target: warg-rider effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: bearer,name(Sharku) + keyword: damage + amount: 1 + } + } + { + type: activatedTrigger + trigger: { + type: usesSpecialAbility + phase: skirmish + } + requires: { + type: canSpot + filter: bearer,inSkirmish + } + cost: { + type: exert + filter: bearer + } + effect: { + type: cancelSpecialAbility + } + } + ] gametext: Bearer must be a warg-rider.
If bearer is Sharku, he is damage +1.
Response: If a skirmish special ability is used in a skirmish involving bearer, exert bearer to cancel that action. lore: The warg-captain picked the fastest and strongest mount. promotext: "" @@ -787,17 +1063,37 @@ Machine Support Area ] - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: shadow + cost: { + type: play + filter: choose(urukHai) + } + effect: { + type: addTokens + culture: isengard + filter: self + } + } + { + type: activatedTrigger + trigger: { + type: aboutToDiscard + opponent: true + filter: machine + } + cost: { + type: discard + filter: self + } + effect: { + type: preventDiscard + filter: choose(machine) + } + } + ] gametext: Machine. Plays to your support area.
Shadow: Play an Uruk-hai to place an [isengard] token on a machine.
Response: If one or more machines are about to be discarded by an opponent, discard this condition to prevent that. lore: Ropes... were hurled over the parapet.... promotext: "" @@ -835,17 +1131,21 @@ vitality: 2 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: trigger + optional: true + trigger: { + type: played + filter: self + } + effect: { + type: addTokens + filter: choose(machine) + culture: isengard + } + } + ] gametext: Damage +1.
When you play this minion, you may place an [isengard] token on a machine. lore: Trained Uruk-hai operated Saruman's siege engines at Helm's Deep. promotext: "" @@ -883,17 +1183,21 @@ vitality: 2 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: trigger + optional: true + trigger: { + type: played + filter: self + } + effect: { + type: addTokens + filter: choose(machine) + culture: isengard + } + } + ] gametext: Damage +1.
When you play this minion, you may place an [isengard] token on a machine. lore: The destruction of the Hornburg fortifications was the task of Saruman's engineer troops. promotext: "" @@ -931,17 +1235,19 @@ vitality: 3 site: 5 keywords: Damage+1 - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: modifyStrength + filter: self,inSkirmish + amount: { + type: forEachWound + filter: character,inSkirmish + } + } + } + ] gametext: Damage +1.
This minion is strength +1 for each wound on a character in its skirmish. lore: Goatskins full of blood poured upon berserkers before battle instill their bloodlust. promotext: "" @@ -977,18 +1283,27 @@ strength: 4 vitality: 2 itemclass: Mount - #target: a warg-rider - /*requires: { - - } + target: warg-rider effects: [ - { - - } - { - - } - ]*/ + { + type: activatedTrigger + trigger: { + type: usesSpecialAbility + phase: skirmish + } + requires: { + type: canSpot + filter: bearer,inSkirmish + } + cost: { + type: exert + filter: bearer + } + effect: { + type: cancelSpecialAbility + } + } + ] gametext: Bearer must be a warg-rider.
Response: If a skirmish special ability is used in a skirmish involving bearer, exert bearer to cancel that action. lore: Bigger and stronger wargs were ridden into battle. promotext: "" @@ -1024,18 +1339,27 @@ strength: 3 vitality: 1 itemclass: Mount - #target: a warg-rider - /*requires: { - - } + target: warg-rider effects: [ - { - - } - { - - } - ]*/ + { + type: activatedTrigger + trigger: { + type: usesSpecialAbility + phase: skirmish + } + requires: { + type: canSpot + filter: bearer,inSkirmish + } + cost: { + type: exert + filter: bearer + } + effect: { + type: cancelSpecialAbility + } + } + ] gametext: Bearer must be a warg-rider.
Response: If a skirmish special ability is used in a skirmish involving bearer, exert bearer to cancel that action. lore: Tell him there is no hope ahead. He should return to Edoras before the wolves of Isengard come there.' promotext: "" @@ -1073,17 +1397,28 @@ vitality: 2 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: trigger + optional: true + trigger: { + type: played + filter: self + } + effect: { + type: playCardFromDiscard + filter: choose(culture(isengard),mount) + } + } + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + ] gametext: Warg-rider. When you play this minion, you may play an [isengard] mount from your discard pile.
While this minion is not exhausted, he is fierce. lore: ...yet I feared that wolf-riders and plunderers might ride nonetheless to Meduseld, while it was undefended.' promotext: "" @@ -1121,17 +1456,16 @@ vitality: 2 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + ] gametext: Warg-rider.
While this minion is not exhausted, he is fierce. lore: Swiftly a scout rode back and reported that wolf-riders were abroad in the valley.... promotext: "" @@ -1165,20 +1499,29 @@ twilight: 1 type: Event keywords: Skirmish - /*requires: { - - } effects: { type: event - cost: { - - }, effect: [ { - + type: modifyStrength + filter: choose(culture(isengard),orc) + amount: 2 + memorize: chosenOrc + } + { + type: conditional + requires: { + type: memoryMatches + memory: chosenOrc + filter: mounted + } + effect: { + type: heal + filter: memory(chosenOrc) + } } ] - }*/ + } gametext: Skirmish: Make an [isengard] Orc strength +2 (and heal it if mounted). lore: ...these howls freeze my blood.' promotext: "" @@ -1211,17 +1554,22 @@ culture: Isengard twilight: 2 type: Condition - /*requires: { - - } + target: controlledSite,plains effects: [ - { - - } - { - - } - ]*/ + { + type: activated + phase: regroup + cost: { + type: exert + filter: choose(culture(isengard),orc) + } + effect: { + type: wound + player: fp + filter: choose(companion) + } + } + ] gametext: Plays on a plains site you control.
Regroup: Exert an [isengard] Orc to make the Free Peoples player wound a companion. lore: Saruman's warg-riders traveled the plains faster than even the Riders of Rohan. promotext: "" @@ -1259,17 +1607,34 @@ vitality: 3 site: 4 keywords: Warg-rider - /*requires: { - - } effects: [ - { - - } - { - - } - ]*/ + { + type: modifier + modifier: { + type: addKeyword + filter: self,not(exhausted) + keyword: fierce + } + } + { + type: modifier + modifier: { + type: addKeyword + filter: self,vitalityMoreThan(2) + keyword: ambush + amount: 1 + } + } + { + type: modifier + modifier: { + type: addKeyword + filter: self,vitalityMoreThan(3) + keyword: damage + amount: 1 + } + } + ] gametext: Warg-rider.
While Foul Horde is not exhausted, it is fierce.
While Foul Horde has at least 3 vitality, it is ambush (1).
While Foul Horde has at least 4 vitality, it is damage +1. lore: "" promotext: "" diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-1.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-1.hjson index 63c4e2d26..50fed3e86 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-1.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-1.hjson @@ -490,7 +490,7 @@ } { type: repeat - amount: { + times: { type: fromMemory memory: spottedElfCount } @@ -740,7 +740,7 @@ effect: [ { type: repeat - amount: { + times: { type: forEachVitality filter: bearer } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-2.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-2.hjson index f8a405e52..2f221e43d 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-2.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set09/set9-2.hjson @@ -109,7 +109,7 @@ } { type: repeat - amount: { + times: { type: forEachInHand filter: side(shadow) } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set14/set14.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set14/set14.hjson index 414e99041..5a91a7326 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set14/set14.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set14/set14.hjson @@ -512,7 +512,7 @@ filter: choose(uruk-hai) count: 0-1 } - amount: { + times: { type: forEachYouCanSpot filter: companion } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-fp.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-fp.hjson index bfdfd2713..f33f385e9 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-fp.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-fp.hjson @@ -65,7 +65,7 @@ } effect: { type: repeat - amount: { + times: { type: twilightCostInMemory memory: discardedFollower } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-shadow.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-shadow.hjson index f0e332717..3ee1231ed 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-shadow.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set31/set31-shadow.hjson @@ -745,7 +745,7 @@ } effect: { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: culture(dwarven),companion over: 3 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set32/set32-shadow.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set32/set32-shadow.hjson index 3ba76216e..ce90cb24f 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set32/set32-shadow.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set32/set32-shadow.hjson @@ -185,7 +185,7 @@ } effect: { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: culture(dwarven),companion over: 3 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-fp.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-fp.hjson index c958bc6b6..b39df3b7b 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-fp.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-fp.hjson @@ -753,7 +753,7 @@ } { type: repeat - amount: { + times: { type: forEachMatchingInMemory memory: cardsInHand filter: side(free people),skirmish,event diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-sites.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-sites.hjson index 7bf60eb68..7ec80e2b5 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-sites.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set33/set33-sites.hjson @@ -112,7 +112,7 @@ } { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: follower,culture(dwarven),not(memory(transferredFollowers)) } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson index 3a83323e3..825a1ff54 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson @@ -61,7 +61,7 @@ ] effect: { type: Repeat - amount: { + times: { type: forEachYouCanSpot filter: companion over: 4 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Sauron-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Sauron-errata.hjson index 03a678a46..a013f3074 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Sauron-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Sauron-errata.hjson @@ -392,7 +392,7 @@ } effect: { type: repeat - amount: { + times: { type: ForEachInDeadPile filter: any } @@ -432,7 +432,7 @@ } effect: { type: repeat - amount: { + times: { type: ForEachInDeadPile filter: any } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set02/set02-Dwarven-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set02/set02-Dwarven-errata.hjson index b333f4c71..587ba9bcc 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set02/set02-Dwarven-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set02/set02-Dwarven-errata.hjson @@ -164,7 +164,7 @@ } { type: Repeat - amount: 9 + times: 9 effect: { type: conditional requires: { diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Moria-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Moria-errata.hjson index 4bf77bae8..551fc25f5 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Moria-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set03/set03-Moria-errata.hjson @@ -53,7 +53,7 @@ } { type: repeat - amount: { + times: { type: sum source: [ { diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set04/set04-Isengard-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set04/set04-Isengard-errata.hjson index 9af56d8ac..67348de1e 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set04/set04-Isengard-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set04/set04-Isengard-errata.hjson @@ -50,7 +50,7 @@ } { type: Repeat - amount: { + times: { type: Sum source: [ 1 diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set05/set05-Isengard-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set05/set05-Isengard-errata.hjson index bc2c5f03f..3a0f18dd1 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set05/set05-Isengard-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set05/set05-Isengard-errata.hjson @@ -63,7 +63,7 @@ effect: [ { type: repeat - amount: { + times: { type: ForEachToken filter: self culture: isengard diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set10/set10-Rohan-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set10/set10-Rohan-errata.hjson index dd623418d..84132dcea 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set10/set10-Rohan-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set10/set10-Rohan-errata.hjson @@ -62,7 +62,7 @@ } { type: Repeat - amount: { + times: { type: ForEachWound filter: minion,InSkirmishAgainst(self) } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set15/set15-Gondor-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set15/set15-Gondor-errata.hjson index d5b5e4c8e..5732dc38c 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set15/set15-Gondor-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set15/set15-Gondor-errata.hjson @@ -132,7 +132,7 @@ } effect: { type: repeat - amount: { + times: { type: forEachThreat } effect: { diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson index ca8f1cab4..8685b0025 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson @@ -405,7 +405,7 @@ } effect: { type: repeat - amount: { + times: { type: forEachYouCanSpot filter: companion over: 5 diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/DoWhile.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/DoWhile.java index 761e55037..ff7e9646b 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/DoWhile.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/DoWhile.java @@ -7,12 +7,10 @@ import com.gempukku.lotro.cards.build.Requirement; import com.gempukku.lotro.cards.build.field.FieldUtils; import com.gempukku.lotro.cards.build.field.effect.EffectAppender; import com.gempukku.lotro.cards.build.field.effect.EffectAppenderProducer; -import com.gempukku.lotro.game.state.LotroGame; import com.gempukku.lotro.logic.actions.CostToEffectAction; -import com.gempukku.lotro.logic.actions.SubAction; +import com.gempukku.lotro.logic.actions.DoWhileSubAction; import com.gempukku.lotro.logic.effects.StackActionEffect; import com.gempukku.lotro.logic.timing.Effect; -import com.gempukku.lotro.logic.timing.UnrespondableEffect; import org.json.simple.JSONObject; public class DoWhile implements EffectAppenderProducer { @@ -36,30 +34,15 @@ public class DoWhile implements EffectAppenderProducer { return true; } - private SubAction createSubAction(CostToEffectAction action, ActionContext actionContext) { - SubAction subAction = new SubAction(action); - for (EffectAppender effectAppender : effectAppenders) - effectAppender.appendEffect(false, subAction, actionContext); - subAction.appendEffect( - new UnrespondableEffect() { - @Override - protected void doPlayEffect(LotroGame game) { - if (conditionsMatch(actionContext)) { - game.getActionsEnvironment().addActionToStack(createSubAction(action, actionContext)); - } - } - } - ); - return subAction; - } - @Override protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) { - if (conditionsMatch(actionContext)) { - return new StackActionEffect(createSubAction(action, actionContext)); - } else { - return null; - } + DoWhileSubAction doWhile = new DoWhileSubAction(action, + () -> conditionsMatch(actionContext), + costToEffectAction -> { + for (EffectAppender effectAppender : effectAppenders) + effectAppender.appendEffect(false, costToEffectAction, actionContext); + }); + return new StackActionEffect(doWhile); } }; } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java index c6d2a07c1..c6b30fbc7 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java @@ -15,6 +15,8 @@ import com.gempukku.lotro.logic.actions.CostToEffectAction; import com.gempukku.lotro.logic.effects.StackActionEffect; import com.gempukku.lotro.logic.modifiers.ModifierFlag; import com.gempukku.lotro.logic.modifiers.evaluator.ConstantEvaluator; +import com.gempukku.lotro.logic.modifiers.evaluator.Evaluator; +import com.gempukku.lotro.logic.modifiers.evaluator.RangeEvaluator; import com.gempukku.lotro.logic.timing.Effect; import com.gempukku.lotro.logic.timing.ExtraFilters; import com.gempukku.lotro.logic.timing.FailedEffect; @@ -25,12 +27,13 @@ import java.util.Collection; public class PlayCardFromDiscard implements EffectAppenderProducer { @Override public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "removedTwilight", "memorize"); + FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "removedTwilight", "optional", "memorize"); final String filter = FieldUtils.getString(effectObject.get("filter"), "filter"); final String onFilter = FieldUtils.getString(effectObject.get("on"), "on"); final int removedTwilight = FieldUtils.getInteger(effectObject.get("removedTwilight"), "removedTwilight", 0); final ValueSource costModifierSource = ValueResolver.resolveEvaluator(effectObject.get("cost"), 0, environment); + final boolean optional = FieldUtils.getBoolean(effectObject.get("optional"), "optional", false); final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize", "_temp"); final FilterableSource onFilterableSource = (onFilter != null) ? environment.getFilterFactory().generateFilter(onFilter, environment) : null; @@ -38,6 +41,8 @@ public class PlayCardFromDiscard implements EffectAppenderProducer { MultiEffectAppender result = new MultiEffectAppender(); result.setPlayabilityCheckedForEffect(true); + Evaluator countEvaluator = optional ? new RangeEvaluator(0, 1) : new ConstantEvaluator(1); + result.addEffectAppender( CardResolver.resolveCardsInDiscard(filter, (actionContext) -> { @@ -60,7 +65,7 @@ public class PlayCardFromDiscard implements EffectAppenderProducer { return Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false, true); }, - new ConstantEvaluator(1), memorize, "you", "Choose card to play", environment)); + countEvaluator, memorize, "you", "Choose card to play", environment)); result.addEffectAppender( new DelayedAppender() { @Override diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java index 2c10f19a0..9081eff80 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java @@ -19,9 +19,9 @@ import java.util.Arrays; public class Repeat implements EffectAppenderProducer { @Override public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(effectObject, "amount", "effect"); + FieldUtils.validateAllowedFields(effectObject, "times", "effect"); - final ValueSource amountSource = ValueResolver.resolveEvaluator(effectObject.get("amount"), environment); + final ValueSource timesSource = ValueResolver.resolveEvaluator(effectObject.get("times"), environment); final JSONObject[] effectArray = FieldUtils.getObjectArray(effectObject.get("effect"), "effect"); final EffectAppender[] effectAppenders = environment.getEffectAppenderFactory().getEffectAppenders(effectArray, environment); @@ -29,10 +29,10 @@ public class Repeat implements EffectAppenderProducer { return new DelayedAppender() { @Override protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) { - final int count = amountSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null); - if (count > 0) { + final int times = timesSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null); + if (times > 0) { SubAction subAction = new SubAction(action); - for (int i = 0; i < count; i++) { + for (int i = 0; i < times; i++) { for (EffectAppender effectAppender : effectAppenders) effectAppender.appendEffect(false, subAction, actionContext); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java index 62bf05153..484a7f56b 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java @@ -41,6 +41,7 @@ public class FilterFactory { }); simpleFilters.put("bearer", (actionContext -> Filters.hasAttached(actionContext.getSource()))); simpleFilters.put("character", (actionContext) -> Filters.character); + simpleFilters.put("canbereturnedtohand", (actionContext -> Filters.canBeReturnedToHand(actionContext.getSource()))); simpleFilters.put("canexert", (actionContext -> Filters.canExert(actionContext.getSource()))); simpleFilters.put("controlledbyotherplayer", (actionContext -> Filters.siteControlledByOtherPlayer(actionContext.getPerformingPlayer()))); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/CanSpotFPCultures.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/CanSpotFPCultures.java new file mode 100644 index 000000000..c19b33a77 --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/CanSpotFPCultures.java @@ -0,0 +1,18 @@ +package com.gempukku.lotro.cards.build.field.effect.requirement; + +import com.gempukku.lotro.cards.build.CardGenerationEnvironment; +import com.gempukku.lotro.cards.build.InvalidCardDefinitionException; +import com.gempukku.lotro.cards.build.Requirement; +import com.gempukku.lotro.cards.build.field.FieldUtils; +import com.gempukku.lotro.logic.GameUtils; +import org.json.simple.JSONObject; + +public class CanSpotFPCultures implements RequirementProducer { + @Override + public Requirement getPlayRequirement(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(object, "amount"); + final int amount = FieldUtils.getInteger(object.get("amount"), "amount"); + + return (actionContext) -> GameUtils.getSpottableFPCulturesCount(actionContext.getGame(), actionContext.getPerformingPlayer()) >= amount; + } +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java index 1680e01bf..295a14991 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java @@ -20,6 +20,7 @@ public class RequirementFactory { requirementProducers.put("canspot", new CanSpot()); requirementProducers.put("canspotburdens", new CanSpotBurdens()); requirementProducers.put("canspotculturetokens", new CanSpotCultureTokens()); + requirementProducers.put("canspotfpcultures", new CanSpotFPCultures()); requirementProducers.put("canspotthreats", new CanSpotThreats()); requirementProducers.put("canspottwilight", new CanSpotTwilight()); requirementProducers.put("canspotwounds", new CanSpotWounds()); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToDiscardFromPlay.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToDiscardFromPlay.java index 070d9583c..b4292a6c0 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToDiscardFromPlay.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToDiscardFromPlay.java @@ -11,10 +11,11 @@ import org.json.simple.JSONObject; public class AboutToDiscardFromPlay implements TriggerCheckerProducer { @Override public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "source", "filter"); + FieldUtils.validateAllowedFields(value, "source", "filter", "opponent"); String source = FieldUtils.getString(value.get("source"), "source", "any"); String filter = FieldUtils.getString(value.get("filter"), "filter"); + boolean opponent = FieldUtils.getBoolean(value.get("opponent"), "opponent", false); final FilterableSource sourceFilter = environment.getFilterFactory().generateFilter(source, environment); final FilterableSource affectedFilter = environment.getFilterFactory().generateFilter(filter, environment); @@ -22,9 +23,16 @@ public class AboutToDiscardFromPlay implements TriggerCheckerProducer { return new TriggerChecker() { @Override public boolean accepts(ActionContext actionContext) { - return TriggerConditions.isGettingDiscardedBy(actionContext.getEffect(), actionContext.getGame(), - sourceFilter.getFilterable(actionContext), - affectedFilter.getFilterable(actionContext)); + if (opponent) { + return TriggerConditions.isGettingDiscardedByOpponent(actionContext.getEffect(), actionContext.getGame(), + actionContext.getPerformingPlayer(), + sourceFilter.getFilterable(actionContext), + affectedFilter.getFilterable(actionContext)); + } else { + return TriggerConditions.isGettingDiscardedBy(actionContext.getEffect(), actionContext.getGame(), + sourceFilter.getFilterable(actionContext), + affectedFilter.getFilterable(actionContext)); + } } @Override diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/UsesSpecialAbility.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/UsesSpecialAbility.java index 58a1fc95a..b41dad522 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/UsesSpecialAbility.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/UsesSpecialAbility.java @@ -5,6 +5,7 @@ import com.gempukku.lotro.cards.build.CardGenerationEnvironment; import com.gempukku.lotro.cards.build.FilterableSource; import com.gempukku.lotro.cards.build.InvalidCardDefinitionException; import com.gempukku.lotro.cards.build.field.FieldUtils; +import com.gempukku.lotro.common.Phase; import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.logic.timing.TriggerConditions; import com.gempukku.lotro.logic.timing.results.ActivateCardResult; @@ -13,10 +14,11 @@ import org.json.simple.JSONObject; public class UsesSpecialAbility implements TriggerCheckerProducer { @Override public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "filter", "memorize"); + FieldUtils.validateAllowedFields(value, "filter", "memorize", "phase"); String filter = FieldUtils.getString(value.get("filter"), "filter", "any"); final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); + final Phase phase = FieldUtils.getEnum(Phase.class, value.get("phase"), "phase"); final String memorize = FieldUtils.getString(value.get("memorize"), "memorize"); return new TriggerChecker() { @@ -31,6 +33,10 @@ public class UsesSpecialAbility implements TriggerCheckerProducer { if (activated) { ActivateCardResult activateCardResult = (ActivateCardResult) actionContext.getEffectResult(); + if (phase != null) { + if (activateCardResult.getActionTimeword() != phase) + return false; + } if (memorize != null) { PhysicalCard playedCard = activateCardResult.getSource(); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java index 54856ff74..209414a94 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java @@ -519,6 +519,15 @@ public class Filters { }; } + public static Filter canBeReturnedToHand(final PhysicalCard source) { + return new Filter() { + @Override + public boolean accepts(LotroGame game, PhysicalCard physicalCard) { + return game.getModifiersQuerying().canBeReturnedToHand(game, physicalCard, source); + } + }; + } + public static Filter canExert(final PhysicalCard source) { return canExert(source, 1); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/AbstractCostToEffectAction.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/AbstractCostToEffectAction.java index 936ea814a..103d4f452 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/AbstractCostToEffectAction.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/AbstractCostToEffectAction.java @@ -122,6 +122,10 @@ public abstract class AbstractCostToEffectAction implements CostToEffectAction { return sum; } + protected final boolean hasNextCost() { + return !_costs.isEmpty(); + } + protected final Effect getNextCost() { Effect cost = _costs.poll(); if (cost != null) @@ -129,6 +133,10 @@ public abstract class AbstractCostToEffectAction implements CostToEffectAction { return cost; } + protected final boolean hasNextEffect() { + return !_effects.isEmpty(); + } + protected final Effect getNextEffect() { final Effect effect = _effects.poll(); if (effect != null) diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/DoWhileSubAction.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/DoWhileSubAction.java new file mode 100644 index 000000000..fe056dc2e --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/actions/DoWhileSubAction.java @@ -0,0 +1,37 @@ +package com.gempukku.lotro.logic.actions; + +import com.gempukku.lotro.game.state.LotroGame; +import com.gempukku.lotro.logic.timing.Action; +import com.gempukku.lotro.logic.timing.Effect; + +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +public class DoWhileSubAction extends SubAction { + private final BooleanSupplier condition; + private final Consumer effectsAppender; + + private boolean firstExecution = true; + + public DoWhileSubAction(Action action, BooleanSupplier condition, Consumer effectsAppender) { + super(action); + this.condition = condition; + this.effectsAppender = effectsAppender; + } + + @Override + public Effect nextEffect(LotroGame game) { + if (firstExecution) { + effectsAppender.accept(this); + firstExecution = false; + } + + if (!hasNextEffect()) { + if (condition.getAsBoolean()) { + effectsAppender.accept(this); + } + } + + return getNextEffect(); + } +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/TriggerConditions.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/TriggerConditions.java index fa5e1464e..1f0629d6b 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/TriggerConditions.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/TriggerConditions.java @@ -419,10 +419,11 @@ public class TriggerConditions { return false; } - public static boolean isGettingDiscardedByOpponent(Effect effect, LotroGame game, String playerId, Filterable... filters) { + public static boolean isGettingDiscardedByOpponent(Effect effect, LotroGame game, String playerId, Filterable sourceFilter, Filterable... filters) { if (effect.getType() == Effect.Type.BEFORE_DISCARD_FROM_PLAY) { PreventableCardEffect preventableEffect = (PreventableCardEffect) effect; - if (effect.getSource() != null && !effect.getPerformingPlayer().equals(playerId)) + if (effect.getSource() != null && Filters.and(sourceFilter).accepts(game, effect.getSource()) + && !effect.getPerformingPlayer().equals(playerId)) return Filters.filter(preventableEffect.getAffectedCardsMinusPrevented(game), game, filters).size() > 0; } return false;