printedTwilightCostFromMemory change

- Reverted printedTwilightCostFromMemory filter to use card memory reference.
- Added MatchesTwilight for an exact numeric match of the twilight cost of a card
- Altered Gathering Wind to use MatchesTwilight, since it is having the player choose an arbitrary number
- Fixed Catapult crashing the game due to the card reference
This commit is contained in:
Christian 'ketura' McCarty
2025-04-01 08:06:37 -05:00
parent 7e3764b61a
commit b1abddbce5
6 changed files with 113 additions and 23 deletions

View File

@@ -132,7 +132,7 @@
} }
{ {
type: modifyStrength type: modifyStrength
select: all(minion,printedTwilightCostFromMemory(chosenNumber)) select: all(minion,matchesTwilight(memory(chosenNumber)))
amount: -2 amount: -2
until: regroup until: regroup
} }

View File

@@ -45,7 +45,6 @@ public class RevealTopCardsOfDrawDeck implements EffectAppenderProducer {
return new RevealTopCardsOfDrawDeckEffect(actionContext.getSource(), deckId, count) { return new RevealTopCardsOfDrawDeckEffect(actionContext.getSource(), deckId, count) {
@Override @Override
protected void cardsRevealed(List<PhysicalCard> revealedCards) { protected void cardsRevealed(List<PhysicalCard> revealedCards) {
if (memorize != null)
actionContext.setCardMemory(memorize, revealedCards); actionContext.setCardMemory(memorize, revealedCards);
} }
}; };

View File

@@ -427,6 +427,26 @@ public class FilterFactory {
return Filters.filter(game, game.getGameState().getSkirmish().getShadowCharacters(), filterable).size() >= count; return Filters.filter(game, game.getGameState().getSkirmish().getShadowCharacters(), filterable).size() >= count;
}); });
}); });
parameterFilters.put("matchestwilight",
(parameter, environment) -> {
if (parameter.startsWith("memory(") && parameter.endsWith(")")) {
String memory = parameter.substring(parameter.indexOf("(") + 1, parameter.lastIndexOf(")"));
return actionContext -> {
try {
final int value = Integer.parseInt(actionContext.getValueFromMemory(memory));
return Filters.printedTwilightCost(value);
} catch (IllegalArgumentException ex) {
return Filters.minPrintedTwilightCost(0);
}
};
} else {
final ValueSource valueSource = ValueResolver.resolveEvaluator(parameter, environment);
return actionContext -> {
final int value = valueSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null);
return Filters.printedTwilightCost(value);
};
}
});
parameterFilters.put("maxtwilight", parameterFilters.put("maxtwilight",
(parameter, environment) -> { (parameter, environment) -> {
if (parameter.startsWith("memory(") && parameter.endsWith(")")) { if (parameter.startsWith("memory(") && parameter.endsWith(")")) {
@@ -467,6 +487,7 @@ public class FilterFactory {
}; };
} }
}); });
parameterFilters.put("maxresistance", parameterFilters.put("maxresistance",
(parameter, environment) -> { (parameter, environment) -> {
final ValueSource valueSource = ValueResolver.resolveEvaluator(parameter, environment); final ValueSource valueSource = ValueResolver.resolveEvaluator(parameter, environment);
@@ -568,17 +589,11 @@ public class FilterFactory {
}); });
parameterFilters.put("printedtwilightcostfrommemory", parameterFilters.put("printedtwilightcostfrommemory",
(parameter, environment) -> actionContext -> { (parameter, environment) -> actionContext -> {
String valueStr = actionContext.getValueFromMemory(parameter); var card = actionContext.getCardFromMemory(parameter).getBlueprint();
if (valueStr == null) if (card.getCardType() == CardType.THE_ONE_RING || card.getCardType() == CardType.MAP)
return Filters.none; return Filters.none;
try { return Filters.printedTwilightCost(card.getTwilightCost());
int twilight = Integer.parseInt(valueStr);
return Filters.printedTwilightCost(twilight);
}
catch (NumberFormatException ex) {
return Filters.none;
}
}); });
parameterFilters.put("race", parameterFilters.put("race",
(parameter, environment) -> { (parameter, environment) -> {

View File

@@ -44,7 +44,7 @@ public abstract class RevealTopCardsOfDrawDeckEffect extends AbstractEffect {
List<? extends PhysicalCard> deck = game.getGameState().getDeck(_playerId); List<? extends PhysicalCard> deck = game.getGameState().getDeck(_playerId);
int count = Math.min(deck.size(), _count); int count = Math.min(deck.size(), _count);
LinkedList<PhysicalCard> topCards = new LinkedList<>(deck.subList(0, count)); LinkedList<PhysicalCard> topCards = new LinkedList<>(deck.subList(0, count));
if (topCards.size() > 0) { if (!topCards.isEmpty()) {
final PlayOrder playerOrder = game.getGameState().getPlayerOrder().getCounterClockwisePlayOrder(_source.getOwner(), false); final PlayOrder playerOrder = game.getGameState().getPlayerOrder().getCounterClockwisePlayOrder(_source.getOwner(), false);
String nextPlayer; String nextPlayer;

View File

@@ -965,6 +965,32 @@ public class GenericCardTestHelper extends AbstractAtTest {
playerDecided(player, String.join(",", bps)); playerDecided(player, String.join(",", bps));
} }
public boolean FreepsHasCardChoiceAvailable(PhysicalCardImpl card) throws DecisionResultInvalidException { return HasCardChoiceAvailable(P1, card);}
public boolean ShadowHasCardChoiceAvailable(PhysicalCardImpl card) throws DecisionResultInvalidException { return HasCardChoiceAvailable(P2, card);}
public boolean HasCardChoiceAvailable(String player, PhysicalCardImpl card) throws DecisionResultInvalidException {
String[] choices = GetAwaitingDecisionParam(player,"blueprintId");
if(choices != null) {
for (String choice : choices) {
if (card.getBlueprintId().equals(choice))
return true;
}
return false;
}
choices = GetAwaitingDecisionParam(player,"cardId");
if(choices != null) {
for (String choice : choices) {
if (card.getCardId() == Integer.parseInt(choice))
return true;
}
return false;
}
return false;
}
public void FreepsChooseCardIDFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P1, cards);} public void FreepsChooseCardIDFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P1, cards);}
public void ShadowChooseCardIDFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P2, cards);} public void ShadowChooseCardIDFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P2, cards);}

View File

@@ -17,8 +17,13 @@ public class Card_08_032_Tests
return new GenericCardTestHelper( return new GenericCardTestHelper(
new HashMap<>() new HashMap<>()
{{ {{
put("card", "8_32"); put("catapult", "8_32");
// put other cards in here as needed for the test case put("fodder1", "8_33");
put("fodder2", "8_34");
put("runner", "1_178");
put("shelob", "8_25");
put("larder", "8_23");
}}, }},
GenericCardTestHelper.FellowshipSites, GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo, GenericCardTestHelper.FOTRFrodo,
@@ -43,7 +48,7 @@ public class Card_08_032_Tests
var scn = GetScenario(); var scn = GetScenario();
var card = scn.GetFreepsCard("card"); var card = scn.GetFreepsCard("catapult");
assertEquals("Catapult", card.getBlueprint().getTitle()); assertEquals("Catapult", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle()); assertNull(card.getBlueprint().getSubtitle());
@@ -55,18 +60,63 @@ public class Card_08_032_Tests
assertEquals(1, card.getBlueprint().getTwilightCost()); assertEquals(1, card.getBlueprint().getTwilightCost());
} }
// Uncomment any @Test markers below once this is ready to be used @Test
//@Test public void CatapultCantBeUsedWithLessThanTwoCardsInHand() throws DecisionResultInvalidException, CardNotFoundException {
public void CatapultTest1() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup //Pre-game setup
var scn = GetScenario(); var scn = GetScenario();
var card = scn.GetFreepsCard("card"); var catapult = scn.GetFreepsCard("catapult");
scn.FreepsMoveCardToHand(card); scn.FreepsMoveCardToHand("fodder1");
scn.FreepsMoveCardToSupportArea(catapult);
scn.ShadowMoveCharToTable("runner");
scn.StartGame(); scn.StartGame();
scn.FreepsPlayCard(card); scn.SkipToPhase(Phase.MANEUVER);
assertEquals(1, scn.GetTwilight()); assertEquals(1, scn.GetFreepsHandCount());
assertFalse(scn.FreepsActionAvailable(catapult));
}
@Test
public void CatapultRevealsTopOfOpponentDeckAndDiscardsShadowCardOfSameCost() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var catapult = scn.GetFreepsCard("catapult");
scn.FreepsMoveCardToHand("fodder1", "fodder2");
scn.FreepsMoveCardToSupportArea(catapult);
var runner = scn.GetShadowCard("runner");
var shelob = scn.GetShadowCard("shelob");
var larder = scn.GetShadowCard("larder");
var evilcatapult = scn.GetShadowCard("catapult");
scn.ShadowMoveCharToTable(runner, shelob);
scn.ShadowMoveCardToSupportArea(larder);
scn.ShadowMoveCardsToTopOfDeck(evilcatapult);
scn.StartGame();
scn.SkipToPhase(Phase.MANEUVER);
assertEquals(2, scn.GetFreepsHandCount());
assertEquals(0, scn.GetFreepsDiscardCount());
assertTrue(scn.FreepsActionAvailable(catapult));
scn.FreepsUseCardAction(catapult);
assertEquals(0, scn.GetFreepsHandCount());
assertEquals(2, scn.GetFreepsDiscardCount());
assertTrue(scn.FreepsHasCardChoiceAvailable(evilcatapult));
scn.DismissRevealedCards();
assertEquals(2, scn.ShadowGetCardChoiceCount());
assertTrue(scn.ShadowHasCardChoiceAvailable(runner));
assertTrue(scn.ShadowHasCardChoiceAvailable(larder));
assertFalse(scn.ShadowHasCardChoiceAvailable(shelob));
assertEquals(Zone.SUPPORT, larder.getZone());
assertEquals(Zone.SHADOW_CHARACTERS, runner.getZone());
scn.ShadowChooseCard(larder);
assertEquals(Zone.DISCARD, larder.getZone());
assertEquals(Zone.SHADOW_CHARACTERS, runner.getZone());
} }
} }