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 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)); } }