diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/cards/official/set03/set3-elven.hjson b/gemp-lotr/gemp-lotr-async/src/main/web/cards/official/set03/set3-elven.hjson
index ba4abc9f4..2243f7afa 100644
--- a/gemp-lotr/gemp-lotr-async/src/main/web/cards/official/set03/set3-elven.hjson
+++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/official/set03/set3-elven.hjson
@@ -31,4 +31,77 @@
}
}
}
+
+ 3_17: {
+ cardInfo: {
+ //Either a full URL, or a subpath for the PC image server
+ imagePath: decipher/LOTR03017.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: null
+ //This is the tree path to use within the alts structure on the parent.
+ // Can of course be ommitted if parent is null.
+ parentPath: null
+ //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: 0
+ collInfo: 3R17
+ rarity: R
+ setNum: "3"
+ cardNum: 17
+ // Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard.
+ style: Standard
+ }
+ title: Galadriel
+ subtitle: Lady of the Golden Wood
+ unique: true
+ culture: elven
+ twilight: 3
+ type: ally
+ allyHome: fellowship,6
+ race: elf
+ strength: 3
+ vitality: 3
+ effects: [
+ {
+ type: trigger
+ optional: true
+ trigger: {
+ type: startOfTurn
+ }
+ effect: {
+ type: heal
+ filter: choose(elf)
+ }
+ }
+ {
+ type: activated
+ phase: fellowship
+ cost: {
+ type: exert
+ filter: self
+ times: 1
+ }
+ effect: {
+ type: playNextSite
+ filter: forest
+ }
+ }
+ ]
+ gametext: At the start of each of your turns, you may heal an Elf.
Fellowship: Exert Galadriel to play the fellowship's next site if it is a forest (replacing opponent's site if necessary).
+ lore: "'There is in her and in this land no evil, unless a man bring it hither himself. Then let him beware!'"
+ 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-async/src/main/web/cards/unofficial/pc/errata/set03/set03-Elven-errata-playtest.hjson b/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/errata/set03/set03-Elven-errata-playtest.hjson
index 29db074a3..e6a716250 100644
--- a/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/errata/set03/set03-Elven-errata-playtest.hjson
+++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/errata/set03/set03-Elven-errata-playtest.hjson
@@ -47,22 +47,36 @@
trigger: {
type: startOfTurn
}
+ requires: {
+ type: canSpot
+ filter: Elf,wounded,not(name(Galadriel))
+ }
effect: {
type: heal
filter: choose(another,elf)
}
}
{
- type: activated
- phase: fellowship
+ type: trigger
+ optional: true
+ trigger: {
+ type: aboutToMoveTo
+ filter: not(your),siteHasSiteNumber
+ }
+ requires: {
+ type: phase
+ phase: fellowship
+ }
cost: {
type: exert
filter: self
- times: 1
}
effect: {
- type: playNextSite
+ type: playSite
filter: forest
+ number: {
+ type: currentSiteNumber
+ }
}
}
]
diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/setV01/V1_moria_playtest.hjson b/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/setV01/V1_moria_playtest.hjson
index 18e9d2b99..0b7732125 100644
--- a/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/setV01/V1_moria_playtest.hjson
+++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/unofficial/pc/setV01/V1_moria_playtest.hjson
@@ -216,7 +216,7 @@
effect: {
type: choice
texts: [
- Stack a [Moria] item here.
+ Stack a [Moria] item from hand here.
Take a [Moria] card stacked here into hand.
]
effects: [
diff --git a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set3/elven/Card3_017.java b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set3/elven/Card3_017.java
deleted file mode 100644
index 1ff91c292..000000000
--- a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set3/elven/Card3_017.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.gempukku.lotro.cards.set3.elven;
-
-import com.gempukku.lotro.common.*;
-import com.gempukku.lotro.game.PhysicalCard;
-import com.gempukku.lotro.game.state.LotroGame;
-import com.gempukku.lotro.logic.actions.ActivateCardAction;
-import com.gempukku.lotro.logic.actions.OptionalTriggerAction;
-import com.gempukku.lotro.logic.cardtype.AbstractAlly;
-import com.gempukku.lotro.logic.effects.ChooseAndHealCharactersEffect;
-import com.gempukku.lotro.logic.effects.PlaySiteEffect;
-import com.gempukku.lotro.logic.effects.SelfExertEffect;
-import com.gempukku.lotro.logic.timing.EffectResult;
-import com.gempukku.lotro.logic.timing.PlayConditions;
-import com.gempukku.lotro.logic.timing.TriggerConditions;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Set: Realms of Elf-lords
- * Side: Free
- * Culture: Elven
- * Twilight Cost: 3
- * Type: Ally • Home 6 • Elf
- * Strength: 3
- * Vitality: 3
- * Site: 6
- * Game Text: At the start of each of your turns, you may heal an Elf. Fellowship: Exert Galadriel to play
- * the fellowship's next site if it is a forest (replacing opponent's site if necessary).
- */
-public class Card3_017 extends AbstractAlly {
- public Card3_017() {
- super(3, SitesBlock.FELLOWSHIP, 6, 3, 3, Race.ELF, Culture.ELVEN, "Galadriel", "Lady of the Golden Wood", true);
- }
-
- @Override
- public List getOptionalAfterTriggers(String playerId, LotroGame game, EffectResult effectResult, PhysicalCard self) {
- if (TriggerConditions.startOfTurn(game, effectResult)) {
- OptionalTriggerAction action = new OptionalTriggerAction(self);
- action.appendEffect(
- new ChooseAndHealCharactersEffect(action, playerId, 1, 1, Race.ELF));
- return Collections.singletonList(action);
- }
- return null;
- }
-
- @Override
- public List extends ActivateCardAction> getPhaseActionsInPlay(String playerId, LotroGame game, PhysicalCard self) {
- if (PlayConditions.canUseFPCardDuringPhase(game, Phase.FELLOWSHIP, self)
- && PlayConditions.canExert(self, game, self)) {
- ActivateCardAction action = new ActivateCardAction(self);
- action.appendCost(
- new SelfExertEffect(action, self));
- action.appendEffect(
- new PlaySiteEffect(action, playerId, null, game.getGameState().getCurrentSiteNumber() + 1, Keyword.FOREST));
- return Collections.singletonList(action);
- }
- return null;
- }
-}
diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToMoveTo.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToMoveTo.java
new file mode 100644
index 000000000..bf5ec9cf7
--- /dev/null
+++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/AboutToMoveTo.java
@@ -0,0 +1,32 @@
+package com.gempukku.lotro.cards.build.field.effect.trigger;
+
+import com.gempukku.lotro.cards.build.ActionContext;
+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.logic.timing.TriggerConditions;
+import org.json.simple.JSONObject;
+
+public class AboutToMoveTo implements TriggerCheckerProducer {
+ @Override
+ public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
+ FieldUtils.validateAllowedFields(value, "filter");
+
+ String filter = FieldUtils.getString(value.get("filter"), "filter", "any");
+
+ final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment);
+
+ return new TriggerChecker() {
+ @Override
+ public boolean accepts(ActionContext actionContext) {
+ return TriggerConditions.isMovingTo(actionContext.getEffect(), actionContext.getGame(), filterableSource.getFilterable(actionContext));
+ }
+
+ @Override
+ public boolean isBefore() {
+ return true;
+ }
+ };
+ }
+}
diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java
index ac41e3c99..6cd92b598 100644
--- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java
+++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java
@@ -16,6 +16,7 @@ public class TriggerCheckerFactory {
triggerCheckers.put("abouttodiscard", new AboutToDiscardFromPlay());
triggerCheckers.put("abouttoexert", new AboutToExert());
triggerCheckers.put("abouttoheal", new AboutToHeal());
+ triggerCheckers.put("abouttomoveto", new AboutToMoveTo());
triggerCheckers.put("abouttotakewound", new AboutToTakeWound());
triggerCheckers.put("activated", new ActivatedTriggerCheckerProducer());
triggerCheckers.put("addsburden", new AddsBurden());
diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/Effect.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/Effect.java
index 12c7fe9b3..3d818867d 100644
--- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/Effect.java
+++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/Effect.java
@@ -10,7 +10,8 @@ public interface Effect {
BEFORE_TAKE_CONTROL_OF_A_SITE,
BEFORE_SKIRMISH_RESOLVED,
BEFORE_THREAT_WOUNDS, BEFORE_TOIL,
- BEFORE_DRAW_CARD
+ BEFORE_DRAW_CARD,
+ BEFORE_MOVE_FROM, BEFORE_MOVE, BEFORE_MOVE_TO
}
/**
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 2cddb8b6b..aa955d423 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
@@ -435,6 +435,14 @@ public class TriggerConditions {
return false;
}
+ public static boolean isMovingTo(Effect effect, LotroGame game, Filterable... filters) {
+ if (effect.getType() == Effect.Type.BEFORE_MOVE_TO
+ && Filters.and(filters).accepts(game, game.getGameState().getCurrentSite()))
+ return true;
+
+ return false;
+ }
+
public static boolean movesFrom(LotroGame game, EffectResult effectResult, Filterable... filters) {
if (effectResult.getType() == EffectResult.Type.WHEN_MOVE_FROM
&& Filters.and(filters).accepts(game, ((WhenMoveFromResult) effectResult).getSite())) {
diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/turn/move/MovementGameProcess.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/turn/move/MovementGameProcess.java
index 6fb5b938b..314faa7fb 100644
--- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/turn/move/MovementGameProcess.java
+++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/turn/move/MovementGameProcess.java
@@ -9,6 +9,7 @@ import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.actions.SystemQueueAction;
import com.gempukku.lotro.logic.effects.AddTwilightEffect;
import com.gempukku.lotro.logic.effects.TriggeringResultEffect;
+import com.gempukku.lotro.logic.timing.Effect;
import com.gempukku.lotro.logic.timing.UnrespondableEffect;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
import com.gempukku.lotro.logic.timing.results.WhenMoveFromResult;
@@ -35,7 +36,7 @@ public class MovementGameProcess implements GameProcess {
});
game.getFormat().getAdventure().appendNextSiteAction(action);
action.appendEffect(
- new TriggeringResultEffect(new WhenMoveFromResult(currentSite), "Fellowship moved from"));
+ new TriggeringResultEffect(Effect.Type.BEFORE_MOVE_FROM, new WhenMoveFromResult(currentSite), "Fellowship moved from"));
action.appendEffect(
new UnrespondableEffect() {
@Override
@@ -45,9 +46,9 @@ public class MovementGameProcess implements GameProcess {
}
});
action.appendEffect(
- new TriggeringResultEffect(new WhenMovesResult(), "Fellowship moves"));
+ new TriggeringResultEffect(Effect.Type.BEFORE_MOVE, new WhenMovesResult(), "Fellowship moves"));
action.appendEffect(
- new TriggeringResultEffect(new WhenMoveToResult(), "Fellowship moved to"));
+ new TriggeringResultEffect(Effect.Type.BEFORE_MOVE_TO, new WhenMoveToResult(), "Fellowship moved to"));
action.appendEffect(
new UnrespondableEffect() {
@Override
diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java
index 224a7a549..6b409b86d 100644
--- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java
+++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java
@@ -173,6 +173,11 @@ public class GenericCardTestHelper extends AbstractAtTest {
List advDeck = (List)_game.getGameState().getAdventureDeck(playerID);
return advDeck.stream().filter(x -> x.getBlueprint().getSiteNumber() == siteNum).findFirst().get();
}
+
+ public PhysicalCardImpl GetSite(int siteNum)
+ {
+ return (PhysicalCardImpl) _game.getGameState().getSite(siteNum);
+ }
public PhysicalCardImpl GetFreepsSite(String name) { return GetSiteByName(P1, name); }
public PhysicalCardImpl GetShadowSite(String name) { return GetSiteByName(P2, name); }
public PhysicalCardImpl GetSiteByName(String player, String name)
diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set03/Card_03_017_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set03/Card_03_017_Tests.java
new file mode 100644
index 000000000..b575e7c5a
--- /dev/null
+++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set03/Card_03_017_Tests.java
@@ -0,0 +1,257 @@
+package com.gempukku.lotro.cards.official.set03;
+
+import com.gempukku.lotro.cards.GenericCardTestHelper;
+import com.gempukku.lotro.common.*;
+import com.gempukku.lotro.filters.Filters;
+import com.gempukku.lotro.game.CardNotFoundException;
+import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
+import com.gempukku.lotro.logic.modifiers.KeywordModifier;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.*;
+
+public class Card_03_017_Tests
+{
+
+ protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException {
+ return new GenericCardTestHelper(
+ new HashMap()
+ {{
+ put("galadriel", "3_17");
+ put("celeborn", "1_34");
+ put("greenleaf", "1_50");
+ }},
+ new HashMap() {{
+ put("site1", "1_319");
+ put("site2", "1_332");
+ put("site3", "1_337");
+ put("site4", "1_343");
+ put("site5", "1_349");
+ put("site6", "1_350");
+ put("site7", "1_353");
+ put("site8", "1_356");
+ put("site2F", "1_329"); //We are cheating here and putting a second site 2 in
+ put("site2T", "4_330"); //and a Towers site 2
+ put("site2K", "7_337"); //and a King site 2
+ put("siteX", "11_231"); //and a Shadows site
+ }},
+ GenericCardTestHelper.FOTRFrodo,
+ GenericCardTestHelper.FOTRRing
+ );
+ }
+
+ protected GenericCardTestHelper GetExpandedScenario() throws CardNotFoundException, DecisionResultInvalidException {
+ return new GenericCardTestHelper(
+ new HashMap()
+ {{
+ put("galadriel", "3_17");
+ // put other cards in here as needed for the test case
+ }},
+ new HashMap() {{
+ put("site1", "1_319");
+ put("site2", "1_332");
+ put("site3", "1_337");
+ put("site4", "1_343");
+ put("site5", "1_349");
+ put("site6", "1_350");
+ put("site7", "1_353");
+ put("site8", "1_356");
+ put("site2F", "1_329"); //We are cheating here and putting a second site 2 in
+ put("site2T", "4_330"); //and a Towers site 2
+ put("site2K", "7_337"); //and a King site 2
+ put("siteX", "11_231"); //and a Shadows site
+ }},
+ GenericCardTestHelper.FOTRFrodo,
+ GenericCardTestHelper.FOTRRing,
+ "expanded"
+ );
+ }
+
+
+ @Test
+ public void GaladrielStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException {
+
+ /**
+ * Set: 3
+ * Title: *Galadriel, Lady of the Golden Wood
+ * Side: Free Peoples
+ * Culture: Elven
+ * Twilight Cost: 3
+ * Type: ally
+ * Subtype: Elf
+ * Strength: 3
+ * Vitality: 3
+ * Site Number: 6
+ * Game Text: At the start of each of your turns, you may heal another Elf.
+ * Fellowship: Exert Galadriel to play the fellowship's next site if it is a forest (replacing opponent's site if necessary).
+ */
+
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+
+ assertTrue(galadriel.getBlueprint().isUnique());
+ assertEquals(Side.FREE_PEOPLE, galadriel.getBlueprint().getSide());
+ assertEquals(Culture.ELVEN, galadriel.getBlueprint().getCulture());
+ assertEquals(CardType.ALLY, galadriel.getBlueprint().getCardType());
+ assertEquals(Race.ELF, galadriel.getBlueprint().getRace());
+ assertEquals(3, galadriel.getBlueprint().getTwilightCost());
+ assertEquals(3, galadriel.getBlueprint().getStrength());
+ assertEquals(3, galadriel.getBlueprint().getVitality());
+ //assertEquals(, galadriel.getBlueprint().getResistance());
+ //assertEquals(Signet., galadriel.getBlueprint().getSignet());
+ assertEquals(6, galadriel.getBlueprint().getAllyHomeSiteNumbers()[0]); // Change this to getAllyHomeSiteNumbers for allies
+ assertEquals(SitesBlock.FELLOWSHIP, galadriel.getBlueprint().getAllyHomeSiteBlock());
+
+ }
+
+ @Test
+ public void FellowshipActionPlaysNextFellowshipForestSite() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2F");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ scn.StartGame();
+
+ scn.FreepsDeclineOptionalTrigger();
+
+ assertTrue(scn.FreepsCardActionAvailable(galadriel));
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(Zone.ADVENTURE_DECK, forestSite.getZone());
+ assertEquals(null, scn.GetSite(2));
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+
+ scn.FreepsUseCardAction(galadriel);
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(forestSite, scn.GetSite(2));
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void FellowshipActionPlaysNextTowersForestSite() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2T");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ scn.StartGame();
+
+ scn.FreepsDeclineOptionalTrigger();
+
+ assertTrue(scn.FreepsCardActionAvailable(galadriel));
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(Zone.ADVENTURE_DECK, forestSite.getZone());
+ assertEquals(null, scn.GetSite(2));
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+
+ scn.FreepsUseCardAction(galadriel);
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(forestSite, scn.GetSite(2));
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void FellowshipActionPlaysNextKingForestSite() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2K");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ //cheating to ensure site 2 qualifies
+ scn.ApplyAdHocModifier(new KeywordModifier(null, Filters.name("West Road"), Keyword.FOREST));
+
+ scn.StartGame();
+
+ scn.FreepsDeclineOptionalTrigger();
+
+ assertTrue(scn.FreepsCardActionAvailable(galadriel));
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(Zone.ADVENTURE_DECK, forestSite.getZone());
+ assertEquals(null, scn.GetSite(2));
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+
+ scn.FreepsUseCardAction(galadriel);
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(forestSite, scn.GetSite(2));
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void FellowshipActionPlaysNextShadowsForestSite() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetExpandedScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("siteX");
+ var site1 = scn.GetFreepsSite("site1");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("siteX");
+
+ scn.FreepsChooseCardBPFromSelection(site1);
+ scn.StartGame();
+
+ scn.FreepsDeclineOptionalTrigger();
+
+ assertTrue(scn.FreepsCardActionAvailable(galadriel));
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(Zone.ADVENTURE_DECK, forestSite.getZone());
+ assertEquals(null, scn.GetSite(2));
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+
+ scn.FreepsUseCardAction(galadriel);
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+
+ assertEquals(Zone.ADVENTURE_DECK, shadowSite2.getZone());
+ assertEquals(forestSite, scn.GetSite(2));
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void HealAbilityTriggersOnElves() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var celeborn = scn.GetFreepsCard("celeborn");
+ var greenleaf = scn.GetFreepsCard("greenleaf");
+ scn.FreepsMoveCharToTable(galadriel, celeborn, greenleaf);
+
+ scn.AddWoundsToChar(galadriel, 1);
+ scn.AddWoundsToChar(celeborn, 1);
+ scn.AddWoundsToChar(greenleaf, 1);
+
+ scn.StartGame();
+
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+ scn.FreepsAcceptOptionalTrigger();
+
+ assertEquals(3, scn.GetFreepsCardChoiceCount()); //celeborn and legolas and galadriel
+ scn.FreepsChooseCard(celeborn);
+ assertEquals(0, scn.GetWoundsOn(celeborn));
+ assertEquals(1, scn.GetWoundsOn(greenleaf));
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+}
diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set03/Card_03_017_ErrataTests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set03/Card_03_017_ErrataTests.java
index 3e2951eda..d0609f45b 100644
--- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set03/Card_03_017_ErrataTests.java
+++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set03/Card_03_017_ErrataTests.java
@@ -2,10 +2,10 @@ package com.gempukku.lotro.cards.unofficial.pc.errata.set03;
import com.gempukku.lotro.cards.GenericCardTestHelper;
import com.gempukku.lotro.common.*;
+import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.CardNotFoundException;
-import com.gempukku.lotro.game.PhysicalCardImpl;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
-import com.gempukku.lotro.logic.modifiers.MoveLimitModifier;
+import com.gempukku.lotro.logic.modifiers.KeywordModifier;
import org.junit.Test;
import java.util.HashMap;
@@ -21,18 +21,58 @@ public class Card_03_017_ErrataTests
return new GenericCardTestHelper(
new HashMap()
{{
- put("card", "73_17");
- // put other cards in here as needed for the test case
+ put("galadriel", "73_17");
+ put("celeborn", "1_34");
+ put("greenleaf", "1_50");
+ }},
+ new HashMap() {{
+ put("site1", "1_319");
+ put("site2", "1_332");
+ put("site3", "1_337");
+ put("site4", "1_343");
+ put("site5", "1_349");
+ put("site6", "1_350");
+ put("site7", "1_353");
+ put("site8", "1_356");
+ put("site2F", "1_329"); //We are cheating here and putting a second site 2 in
+ put("site2T", "4_330"); //and a Towers site 2
+ put("site2K", "7_337"); //and a King site 2
+ put("siteX", "11_231"); //and a Shadows site
}},
- GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
GenericCardTestHelper.FOTRRing
);
}
- // Uncomment both @Test markers below once this is ready to be used
+ protected GenericCardTestHelper GetExpandedScenario() throws CardNotFoundException, DecisionResultInvalidException {
+ return new GenericCardTestHelper(
+ new HashMap()
+ {{
+ put("galadriel", "73_17");
+ // put other cards in here as needed for the test case
+ }},
+ new HashMap() {{
+ put("site1", "1_319");
+ put("site2", "1_332");
+ put("site3", "1_337");
+ put("site4", "1_343");
+ put("site5", "1_349");
+ put("site6", "1_350");
+ put("site7", "1_353");
+ put("site8", "1_356");
+ put("site2F", "1_329"); //We are cheating here and putting a second site 2 in
+ put("site2T", "4_330"); //and a Towers site 2
+ put("site2K", "7_337"); //and a King site 2
+ put("siteX", "11_231"); //and a Shadows site
+ }},
+ GenericCardTestHelper.FOTRFrodo,
+ GenericCardTestHelper.FOTRRing,
+ "expanded"
+ );
+ }
- //@Test
+
+ @Test
public void GaladrielStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException {
/**
@@ -47,40 +87,204 @@ public class Card_03_017_ErrataTests
* Vitality: 3
* Site Number: 6
* Game Text: At the start of each of your turns, you may heal another Elf.
- * Fellowship: Exert Galadriel twice to play the fellowship's next site if it is a forest (replacing opponent's site if necessary).
+ * Response: If the fellowship is about to move to an opponent's site during the Fellowship phase,
+ * exert Galadriel to replace that site with a forest site of the same site number from your adventure deck.
*/
//Pre-game setup
- GenericCardTestHelper scn = GetScenario();
+ var scn = GetScenario();
- PhysicalCardImpl card = scn.GetFreepsCard("card");
+ var galadriel = scn.GetFreepsCard("galadriel");
- assertTrue(card.getBlueprint().isUnique());
- assertEquals(Side.FREE_PEOPLE, card.getBlueprint().getSide());
- assertEquals(Culture.ELVEN, card.getBlueprint().getCulture());
- assertEquals(CardType.ALLY, card.getBlueprint().getCardType());
- assertEquals(Race.CREATURE, card.getBlueprint().getRace());
- assertTrue(scn.HasKeyword(card, Keyword.SUPPORT_AREA));
- assertEquals(3, card.getBlueprint().getTwilightCost());
- assertEquals(3, card.getBlueprint().getStrength());
- assertEquals(3, card.getBlueprint().getVitality());
- //assertEquals(, card.getBlueprint().getResistance());
- //assertEquals(Signet., card.getBlueprint().getSignet());
- assertEquals(6, card.getBlueprint().getSiteNumber()); // Change this to getAllyHomeSiteNumbers for allies
+ assertTrue(galadriel.getBlueprint().isUnique());
+ assertEquals(Side.FREE_PEOPLE, galadriel.getBlueprint().getSide());
+ assertEquals(Culture.ELVEN, galadriel.getBlueprint().getCulture());
+ assertEquals(CardType.ALLY, galadriel.getBlueprint().getCardType());
+ assertEquals(Race.ELF, galadriel.getBlueprint().getRace());
+ assertEquals(3, galadriel.getBlueprint().getTwilightCost());
+ assertEquals(3, galadriel.getBlueprint().getStrength());
+ assertEquals(3, galadriel.getBlueprint().getVitality());
+ //assertEquals(, galadriel.getBlueprint().getResistance());
+ //assertEquals(Signet., galadriel.getBlueprint().getSignet());
+ assertEquals(6, galadriel.getBlueprint().getAllyHomeSiteNumbers()[0]); // Change this to getAllyHomeSiteNumbers for allies
+ assertEquals(SitesBlock.FELLOWSHIP, galadriel.getBlueprint().getAllyHomeSiteBlock());
}
- //@Test
- public void GaladrielTest1() throws DecisionResultInvalidException, CardNotFoundException {
+ @Test
+ public void MovingToOpponentsSiteWithMatchingFellowshipForestSiteTriggers() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
- GenericCardTestHelper scn = GetScenario();
+ var scn = GetScenario();
- PhysicalCardImpl card = scn.GetFreepsCard("card");
- scn.FreepsMoveCardToHand(card);
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2F");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
scn.StartGame();
- scn.FreepsPlayCard(card);
- assertEquals(3, scn.GetTwilight());
+
+ scn.FreepsPassCurrentPhaseAction();
+
+ //We have to specify which site here since we cheated and included a second site 2
+ scn.ShadowChooseCardBPFromSelection(shadowSite2);
+ assertEquals(shadowSite2, scn.GetCurrentSite());
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+ scn.FreepsAcceptOptionalTrigger();
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+ assertEquals(forestSite.getBlueprintId(), scn.GetCurrentSite().getBlueprintId());
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void MovingToOpponentsSiteWithMatchingTowersForestSiteTriggers() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2T");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ scn.StartGame();
+ scn.FreepsPassCurrentPhaseAction();
+
+ //We have to specify which site here since we cheated and included a second site 2
+ scn.ShadowChooseCardBPFromSelection(shadowSite2);
+ assertEquals(shadowSite2, scn.GetCurrentSite());
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+ scn.FreepsAcceptOptionalTrigger();
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+ assertEquals(forestSite.getBlueprintId(), scn.GetCurrentSite().getBlueprintId());
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void MovingToOpponentsSiteWithMatchingKingForestSiteTriggers() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2K");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ //cheating to ensure site 2 qualifies
+ scn.ApplyAdHocModifier(new KeywordModifier(null, Filters.name("West Road"), Keyword.FOREST));
+
+ scn.StartGame();
+ scn.FreepsPassCurrentPhaseAction();
+
+ //We have to specify which site here since we cheated and included a second site 2
+ scn.ShadowChooseCardBPFromSelection(shadowSite2);
+ assertEquals(shadowSite2, scn.GetCurrentSite());
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+ scn.FreepsAcceptOptionalTrigger();
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+ assertEquals(forestSite.getBlueprintId(), scn.GetCurrentSite().getBlueprintId());
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void MovingToOpponentsShadowsPathSiteTriggersButCannotBePlayed() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetExpandedScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2K");
+ var freepsSiteX = scn.GetFreepsSite("siteX");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("siteX");
+
+ scn.FreepsChooseCardBPFromSelection(freepsSiteX);
+ scn.StartGame();
+ scn.FreepsPassCurrentPhaseAction();
+
+
+ //We have to specify which site here since we cheated and included a second site 2
+ scn.ShadowChooseCardBPFromSelection(shadowSite2);
+ assertEquals(shadowSite2, scn.GetCurrentSite());
+
+ assertEquals(0, scn.GetWoundsOn(galadriel));
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+ scn.FreepsAcceptOptionalTrigger();
+ //The cheated Towers and Fellowship site 2, but not the Shadows variant (as that doesn't have a site number until played)
+ assertEquals(2, scn.GetFreepsCardChoiceCount());
+ assertEquals(1, scn.GetWoundsOn(galadriel));
+ }
+
+ @Test
+ public void ReplacedSiteDoesNotTriggerBeforeReplacement() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var frodo = scn.GetRingBearer();
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var forestSite = scn.GetFreepsSite("site2F");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ var shadowSite2 = scn.GetShadowSite("site2");
+
+ scn.StartGame();
+ assertEquals(0, scn.GetWoundsOn(frodo));
+ scn.FreepsPassCurrentPhaseAction();
+
+ //We have to specify which site here since we cheated and included a second site 2
+ scn.ShadowChooseCardBPFromSelection(shadowSite2);
+ scn.FreepsAcceptOptionalTrigger();
+ scn.FreepsChooseCardBPFromSelection(forestSite);
+ //Midgwater Moors as the replaced site 2 would have exerted all hobbits upon arrival. If everything
+ //worked out timing-wise, this should never have been evaluated.
+ assertEquals(0, scn.GetWoundsOn(frodo));
+ }
+
+ @Test
+ public void HealAbilityDoesNotTriggerOnSelf() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ scn.FreepsMoveCharToTable(galadriel);
+
+ scn.AddWoundsToChar(galadriel, 1);
+
+ scn.StartGame();
+
+ assertFalse(scn.FreepsHasOptionalTriggerAvailable());
+ }
+
+ @Test
+ public void HealAbilityTriggersOnOtherElves() throws DecisionResultInvalidException, CardNotFoundException {
+ //Pre-game setup
+ var scn = GetScenario();
+
+ var galadriel = scn.GetFreepsCard("galadriel");
+ var celeborn = scn.GetFreepsCard("celeborn");
+ var greenleaf = scn.GetFreepsCard("greenleaf");
+ scn.FreepsMoveCharToTable(galadriel, celeborn, greenleaf);
+
+ scn.AddWoundsToChar(galadriel, 1);
+ scn.AddWoundsToChar(celeborn, 1);
+ scn.AddWoundsToChar(greenleaf, 1);
+
+ scn.StartGame();
+
+ assertTrue(scn.FreepsHasOptionalTriggerAvailable());
+ scn.FreepsAcceptOptionalTrigger();
+
+ assertEquals(2, scn.GetFreepsCardChoiceCount()); //celeborn and legolas, but not galadriel
+ scn.FreepsChooseCard(celeborn);
+ assertEquals(0, scn.GetWoundsOn(celeborn));
+ assertEquals(1, scn.GetWoundsOn(greenleaf));
}
}