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
select: all(minion,printedTwilightCostFromMemory(chosenNumber))
select: all(minion,matchesTwilight(memory(chosenNumber)))
amount: -2
until: regroup
}

View File

@@ -45,8 +45,7 @@ public class RevealTopCardsOfDrawDeck implements EffectAppenderProducer {
return new RevealTopCardsOfDrawDeckEffect(actionContext.getSource(), deckId, count) {
@Override
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;
});
});
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",
(parameter, environment) -> {
if (parameter.startsWith("memory(") && parameter.endsWith(")")) {
@@ -467,6 +487,7 @@ public class FilterFactory {
};
}
});
parameterFilters.put("maxresistance",
(parameter, environment) -> {
final ValueSource valueSource = ValueResolver.resolveEvaluator(parameter, environment);
@@ -568,17 +589,11 @@ public class FilterFactory {
});
parameterFilters.put("printedtwilightcostfrommemory",
(parameter, environment) -> actionContext -> {
String valueStr = actionContext.getValueFromMemory(parameter);
if (valueStr == null)
var card = actionContext.getCardFromMemory(parameter).getBlueprint();
if (card.getCardType() == CardType.THE_ONE_RING || card.getCardType() == CardType.MAP)
return Filters.none;
try {
int twilight = Integer.parseInt(valueStr);
return Filters.printedTwilightCost(twilight);
}
catch (NumberFormatException ex) {
return Filters.none;
}
return Filters.printedTwilightCost(card.getTwilightCost());
});
parameterFilters.put("race",
(parameter, environment) -> {

View File

@@ -44,7 +44,7 @@ public abstract class RevealTopCardsOfDrawDeckEffect extends AbstractEffect {
List<? extends PhysicalCard> deck = game.getGameState().getDeck(_playerId);
int count = Math.min(deck.size(), _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);
String nextPlayer;

View File

@@ -965,6 +965,32 @@ public class GenericCardTestHelper extends AbstractAtTest {
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 ShadowChooseCardIDFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P2, cards);}

View File

@@ -17,8 +17,13 @@ public class Card_08_032_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "8_32");
// put other cards in here as needed for the test case
put("catapult", "8_32");
put("fodder1", "8_33");
put("fodder2", "8_34");
put("runner", "1_178");
put("shelob", "8_25");
put("larder", "8_23");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -43,7 +48,7 @@ public class Card_08_032_Tests
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("catapult");
assertEquals("Catapult", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -55,18 +60,63 @@ public class Card_08_032_Tests
assertEquals(1, card.getBlueprint().getTwilightCost());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void CatapultTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void CatapultCantBeUsedWithLessThanTwoCardsInHand() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var catapult = scn.GetFreepsCard("catapult");
scn.FreepsMoveCardToHand("fodder1");
scn.FreepsMoveCardToSupportArea(catapult);
scn.ShadowMoveCharToTable("runner");
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());
}
}