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 5014b4288..744747fd2 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 @@ -1025,12 +1025,22 @@ { type: chooseYesOrNo player: you - text: Would you like to play a minion from your discard pile? + text: Would you like to play a minion from your discard? memorize: choice } { - type: playCardFromDiscard - filter: choose(minion) + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + ] + effect: { + type: playCardFromDiscard + filter: choose(minion) + } } ] } 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 9ec5cd6e6..9437e663f 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 @@ -1217,12 +1217,114 @@ effect: { type: playCardFromDiscard filter: choose(tracker) + //This should be updated to "optional" + nocheck: true } } { type: endPhase } ] + + /* + // A less obnoxious version that short-circuits the loop if the + // player ever chooses 0 cards at the prompt. Unfortunately, it + // turns out that "memoryIs" does not check card memory, so this + // is useless until those are unified. + effect: [ + { + type: MemorizeValue + memory: choice + value: default + } + { + type: repeat + amount: 3 + effect: { + type: conditional + requires: [ + { + type: Not + requires: { + type: MemoryIs + memory: choice + value: "" + } + } + { + type: HasCardInDiscard + filter: playable,tracker + } + ] + effect: { + type: playCardFromDiscard + filter: choose(tracker) + memorize: choice + //Forces the choice to allow 0 options + nocheck: true + } + } + } + { + type: endPhase + } + ] + */ + + /* + // An obnoxious version that prompts the user with a question + // for each individual tracker played + effect: [ + { + type: MemorizeValue + memory: choice + value: yes + } + { + type: repeat + amount: 3 + effect: { + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + { + type: HasCardInDiscard + filter: playable,tracker + } + ] + effect: [ + { + type: chooseYesOrNo + player: you + text: Would you like to play a tracker from your discard pile? + memorize: choice + } + { + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + ] + effect: { + type: playCardFromDiscard + filter: choose(tracker) + } + } + ] + } + } + { + type: endPhase + } + ] + */ } } } 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 7ae13feaf..90f35f81d 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 @@ -77,8 +77,18 @@ memorize: choice } { - type: playCardFromDiscard - filter: choose(unique,culture(wraith),minion) + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + ] + effect: { + type: playCardFromDiscard + filter: choose(unique,culture(wraith),minion) + } } ] } 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 8a2ceefa4..f8fcf2fa9 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 @@ -949,6 +949,8 @@ public class GenericCardTestHelper extends AbstractAtTest { public void FreepsDeclineOptionalTrigger() throws DecisionResultInvalidException { playerDecided(P1, ""); } public void ShadowAcceptOptionalTrigger() throws DecisionResultInvalidException { playerDecided(P2, "0"); } public void ShadowDeclineOptionalTrigger() throws DecisionResultInvalidException { playerDecided(P2, ""); } + public void FreepsDeclineChoosing() throws DecisionResultInvalidException { playerDecided(P1, ""); } + public void ShadowDeclineChoosing() throws DecisionResultInvalidException { playerDecided(P2, ""); } public void FreepsDeclineReconciliation() throws DecisionResultInvalidException { FreepsPassCurrentPhaseAction(); } public void ShadowDeclineReconciliation() throws DecisionResultInvalidException { ShadowPassCurrentPhaseAction(); } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java index 46fc04934..b22006981 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java @@ -182,6 +182,40 @@ public class Card_01_234_Tests assertFalse(scn.ShadowDecisionAvailable("play a minion")); } + @Test + public void NerteaStopsPlayingMinionsIfUserDeclinesPartwayThrough() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + var twk = scn.GetShadowCard("twk"); + var attea = scn.GetShadowCard("attea"); + var nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToDiscard("runner", "rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + scn.ShadowChooseYes(); + assertEquals(4, scn.GetShadowCardChoiceCount()); + assertEquals(Zone.DISCARD, twk.getZone()); + scn.ShadowChooseCardBPFromSelection(twk); + assertEquals(Zone.SHADOW_CHARACTERS, twk.getZone()); + + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + assertEquals(Zone.DISCARD, attea.getZone()); + scn.ShadowChooseNo(); + assertEquals(Zone.DISCARD, attea.getZone()); + + assertFalse(scn.ShadowDecisionAvailable("play a minion")); + } + @Test public void NerteaDoesNotPromptIfNoMinionsInDiscardPile() throws DecisionResultInvalidException, CardNotFoundException { //Pre-game setup diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_363_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_363_Tests.java index 0d425de31..6cb979bd3 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_363_Tests.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_363_Tests.java @@ -5,6 +5,7 @@ import com.gempukku.lotro.cards.GenericCardTestHelper; import com.gempukku.lotro.common.CardType; import com.gempukku.lotro.common.Keyword; import com.gempukku.lotro.common.Phase; +import com.gempukku.lotro.common.Zone; import com.gempukku.lotro.game.CardNotFoundException; import com.gempukku.lotro.game.PhysicalCardImpl; import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; @@ -21,11 +22,11 @@ public class Card_01_363_Tests protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException { return new GenericCardTestHelper( new HashMap<>() {{ - put("greenleaf", "1_50"); - put("moriatroop1", "1_177"); - put("moriatroop2", "1_177"); - put("moriatroop3", "1_177"); + put("tracker1", "1_261"); + put("tracker2", "1_262"); + put("tracker3", "1_270"); put("shelob", "8_26"); + put("hollowing", "3_54"); }}, new HashMap<>() {{ put("site1", "1_319"); @@ -58,7 +59,7 @@ public class Card_01_363_Tests //Pre-game setup GenericCardTestHelper scn = GetScenario(); - PhysicalCardImpl site9 = scn.GetFreepsSite(9); + var site9 = scn.GetFreepsSite(9); assertFalse(site9.getBlueprint().isUnique()); assertEquals(CardType.SITE, site9.getBlueprint().getCardType()); @@ -72,60 +73,168 @@ public class Card_01_363_Tests //Pre-game setup GenericCardTestHelper scn = GetScenario(); - PhysicalCardImpl moriatroop1 = scn.GetShadowCard("moriatroop1"); - PhysicalCardImpl moriatroop2 = scn.GetShadowCard("moriatroop2"); - PhysicalCardImpl moriatroop3 = scn.GetShadowCard("moriatroop3"); - PhysicalCardImpl shelob = scn.GetShadowCard("shelob"); - scn.ShadowMoveCardToHand(moriatroop1, moriatroop2, moriatroop3, shelob); - - //Max out the move limit so we don't have to juggle play back and forth - scn.ApplyAdHocModifier(new MoveLimitModifier(null, 10)); + var tolbrandir = scn.GetShadowSite(9); + var tracker1 = scn.GetShadowCard("tracker1"); + var tracker2 = scn.GetShadowCard("tracker2"); + var tracker3 = scn.GetShadowCard("tracker3"); + var hollowing = scn.GetShadowCard("hollowing"); + scn.ShadowMoveCardToSupportArea(hollowing); + scn.ShadowMoveCardToDiscard(tracker1, tracker2, tracker3); + scn.ShadowMoveCardToDiscard("shelob"); scn.StartGame(); - // 1 -> 3 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + scn.SkipToSite(8); + scn.FreepsPassCurrentPhaseAction(); - // 3 -> 4 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + assertSame(tolbrandir, scn.GetCurrentSite()); + assertTrue(scn.ShadowActionAvailable(tolbrandir)); + assertTrue(scn.ShadowActionAvailable(hollowing)); + assertEquals(Zone.DISCARD, tracker1.getZone()); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertEquals(10, scn.GetTwilight()); - // 4 -> 5 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + scn.ShadowUseCardAction(tolbrandir); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + //Should not have the non-tracker in the choice pool + assertEquals(3, scn.ShadowGetBPChoices().size()); - // 5 -> 6 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + scn.ShadowChooseCardBPFromSelection(tracker1); + assertEquals(Zone.SHADOW_CHARACTERS, tracker1.getZone()); + assertEquals(9, scn.GetTwilight()); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); - // 6 -> 7 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + scn.ShadowChooseCardBPFromSelection(tracker2); + assertEquals(Zone.SHADOW_CHARACTERS, tracker2.getZone()); + assertEquals(7, scn.GetTwilight()); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); - // 7 -> 8 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + scn.ShadowChooseCardBPFromSelection(tracker3); + assertEquals(Zone.SHADOW_CHARACTERS, tracker3.getZone()); + assertEquals(4, scn.GetTwilight()); - // 8 -> 9 - scn.SkipToPhase(Phase.REGROUP); - scn.PassCurrentPhaseActions(); - scn.ShadowDeclineReconciliation(); - scn.FreepsChooseToMove(); + //No more shadow actions should be permitted (but Shadow is still required to pass manually) + assertFalse(scn.ShadowActionAvailable(hollowing)); + } - //TODO: actually finish this + @Test + public void TolBrandirActionCanPlay0TrackersAndDecline3() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + var tolbrandir = scn.GetShadowSite(9); + var tracker1 = scn.GetShadowCard("tracker1"); + var tracker2 = scn.GetShadowCard("tracker2"); + var tracker3 = scn.GetShadowCard("tracker3"); + scn.ShadowMoveCardToDiscard(tracker1, tracker2, tracker3); + + scn.StartGame(); + + scn.SkipToSite(8); + scn.FreepsPassCurrentPhaseAction(); + + assertSame(tolbrandir, scn.GetCurrentSite()); + assertTrue(scn.ShadowActionAvailable(tolbrandir)); + assertEquals(Zone.DISCARD, tracker1.getZone()); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertEquals(10, scn.GetTwilight()); + + scn.ShadowUseCardAction(tolbrandir); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + //Should not have the non-tracker in the choice pool + assertEquals(3, scn.ShadowGetBPChoices().size()); + + scn.ShadowDeclineChoosing(); + scn.ShadowDeclineChoosing(); + scn.ShadowDeclineChoosing(); + assertEquals(Zone.DISCARD, tracker1.getZone()); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertTrue(scn.ShadowDecisionAvailable("Play Shadow action or pass")); + } + + @Test + public void TolBrandirActionCanPlay1TrackerAndDecline2() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + var tolbrandir = scn.GetShadowSite(9); + var tracker1 = scn.GetShadowCard("tracker1"); + var tracker2 = scn.GetShadowCard("tracker2"); + var tracker3 = scn.GetShadowCard("tracker3"); + scn.ShadowMoveCardToDiscard(tracker1, tracker2, tracker3); + + scn.StartGame(); + + scn.SkipToSite(8); + scn.FreepsPassCurrentPhaseAction(); + + assertSame(tolbrandir, scn.GetCurrentSite()); + assertTrue(scn.ShadowActionAvailable(tolbrandir)); + assertEquals(Zone.DISCARD, tracker1.getZone()); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertEquals(10, scn.GetTwilight()); + + scn.ShadowUseCardAction(tolbrandir); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + //Should not have the non-tracker in the choice pool + assertEquals(3, scn.ShadowGetBPChoices().size()); + + scn.ShadowChooseCardBPFromSelection(tracker1); + assertEquals(Zone.SHADOW_CHARACTERS, tracker1.getZone()); + assertEquals(9, scn.GetTwilight()); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + + scn.ShadowDeclineChoosing(); + scn.ShadowDeclineChoosing(); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertTrue(scn.ShadowDecisionAvailable("Play Shadow action or pass")); + } + + @Test + public void TolBrandirActionCanPlay2TrackersAndDecline1() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + var tolbrandir = scn.GetShadowSite(9); + var tracker1 = scn.GetShadowCard("tracker1"); + var tracker2 = scn.GetShadowCard("tracker2"); + var tracker3 = scn.GetShadowCard("tracker3"); + scn.ShadowMoveCardToDiscard(tracker1, tracker2, tracker3); + + scn.StartGame(); + + scn.SkipToSite(8); + scn.FreepsPassCurrentPhaseAction(); + + assertSame(tolbrandir, scn.GetCurrentSite()); + assertTrue(scn.ShadowActionAvailable(tolbrandir)); + assertEquals(Zone.DISCARD, tracker1.getZone()); + assertEquals(Zone.DISCARD, tracker2.getZone()); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertEquals(10, scn.GetTwilight()); + + scn.ShadowUseCardAction(tolbrandir); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + //Should not have the non-tracker in the choice pool + assertEquals(3, scn.ShadowGetBPChoices().size()); + + scn.ShadowChooseCardBPFromSelection(tracker1); + assertEquals(Zone.SHADOW_CHARACTERS, tracker1.getZone()); + assertEquals(9, scn.GetTwilight()); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + + scn.ShadowChooseCardBPFromSelection(tracker2); + assertEquals(Zone.SHADOW_CHARACTERS, tracker2.getZone()); + assertEquals(7, scn.GetTwilight()); + assertTrue(scn.ShadowDecisionAvailable("Choose card from discard")); + + scn.ShadowDeclineChoosing(); + assertEquals(Zone.DISCARD, tracker3.getZone()); + assertTrue(scn.ShadowDecisionAvailable("Play Shadow action or pass")); } } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java index 4a929ac89..128efe00c 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java @@ -180,6 +180,41 @@ public class Card_01_234_ErrataTests assertFalse(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); } + @Test + public void NerteaStopsPlayingMinionsIfUserDeclinesPartwayThrough() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + var twk = scn.GetShadowCard("twk"); + var attea = scn.GetShadowCard("attea"); + var nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToDiscard("runner", "rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + scn.ShadowChooseYes(); + //twk and attea, but not rit or runner + assertEquals(2, scn.GetShadowCardChoiceCount()); + assertEquals(Zone.DISCARD, twk.getZone()); + scn.ShadowChooseCardBPFromSelection(twk); + assertEquals(Zone.SHADOW_CHARACTERS, twk.getZone()); + + assertTrue(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + assertEquals(Zone.DISCARD, attea.getZone()); + scn.ShadowChooseNo(); + assertEquals(Zone.DISCARD, attea.getZone()); + + assertFalse(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + } + @Test public void NerteaDoesNotPromptIfNoUniqueRingwraithMinionsInDiscardPile() throws DecisionResultInvalidException, CardNotFoundException { //Pre-game setup @@ -202,7 +237,7 @@ public class Card_01_234_ErrataTests //Imported from the at tests @Test - public void ulaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { + public void UlaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { var scn = GetScenario(); var _game = scn._game;