Fixing Nertea and Tol Brandir playing one more minion than the player asked for

This commit is contained in:
Christian 'ketura' McCarty
2024-10-07 09:09:57 -05:00
parent d3bf3de7f0
commit be20092bc8
7 changed files with 357 additions and 55 deletions

View File

@@ -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)
}
}
]
}

View File

@@ -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
}
]
*/
}
}
}

View File

@@ -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)
}
}
]
}

View File

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

View File

@@ -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

View File

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

View File

@@ -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;