Assignment fixes and others

- (Hopefully) Fixed forced assignment actions causing an incorrect message about too many assigned minions that soft-locks the game
- Syntax fix for previous Siege Engine fix
- Fixed Sam and Merry errata not being properly limited to once per turn
- Fixed a front-end error related to the card display rework
This commit is contained in:
Christian 'ketura' McCarty
2024-11-08 16:47:58 -06:00
parent 9f0afffc9b
commit 45161fb0ed
9 changed files with 66 additions and 27 deletions

View File

@@ -2079,6 +2079,15 @@ var GempLotrGameUI = Class.extend({
var actionId = actionIds[i];
var actionText = actionTexts[i];
var blueprintId = blueprintIds[i];
var testingText = testingTexts[i];
if (testingText == "null") {
testingText = null;
}
var backSideTestingText = backSideTestingTexts[i];
if (backSideTestingText == "null") {
backSideTestingText = null;
}
if (blueprintId == "inPlay") {
var cardIdElem = $(".card:cardId(" + cardId + ")");
@@ -2227,6 +2236,14 @@ var GempLotrGameUI = Class.extend({
for (var i = 0; i < blueprintIds.length; i++) {
var blueprintId = blueprintIds[i];
var testingText = testingTexts[i];
if (testingText == "null") {
testingText = null;
}
var backSideTestingText = backSideTestingTexts[i];
if (backSideTestingText == "null") {
backSideTestingText = null;
}
cardIds.push("temp" + i);
var card = new Card(blueprintId, testingText, backSideTestingText, "SPECIAL", "temp" + i, null);

View File

@@ -920,7 +920,7 @@
effect: {
type: AddCultureTokens
culture: isengard
select: machine
select: choose(machine)
}
}
{

View File

@@ -1196,7 +1196,7 @@
type: assignFpCharacterToSkirmish
fpCharacter: choose(ringBearer)
minion: self
ignoreUnassigned: true
ignoreExistingAssignments: true
}
}
]

View File

@@ -85,6 +85,7 @@
resistance: 5
effects: {
type: activated
limitPerTurn: 1
phase: fellowship
cost: {
type: exert

View File

@@ -73,6 +73,7 @@
resistance: 6
effects: {
type: activated
limitPerTurn: 1
phase: fellowship
cost: {
type: exert

View File

@@ -21,7 +21,7 @@ import java.util.Collection;
public class AssignFpCharacterToSkirmish implements EffectAppenderProducer {
@Override
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(effectObject, "player", "fpCharacter", "minion", "memorizeMinion", "memorizeFPCharacter", "ignoreUnassigned", "preventCost", "preventText", "insteadEffect");
FieldUtils.validateAllowedFields(effectObject, "player", "fpCharacter", "minion", "memorizeMinion", "memorizeFPCharacter", "ignoreExistingAssignments", "preventCost", "preventText", "insteadEffect");
final String player = FieldUtils.getString(effectObject.get("player"), "player", "you");
final String fpCharacter = FieldUtils.getString(effectObject.get("fpCharacter"), "fpCharacter", "choose(any)");
@@ -39,7 +39,7 @@ public class AssignFpCharacterToSkirmish implements EffectAppenderProducer {
final String minionMemory = FieldUtils.getString(effectObject.get("memorizeMinion"), "memorizeMinion", "_tempMinion");
final String fpCharacterMemory = FieldUtils.getString(effectObject.get("memorizeFPCharacter"), "memorizeFPCharacter", "_tempFpCharacter");
final boolean ignoreUnassigned = FieldUtils.getBoolean(effectObject.get("ignoreUnassigned"), "ignoreUnassigned", false);
final boolean ignoreExistingAssignments = FieldUtils.getBoolean(effectObject.get("ignoreExistingAssignments"), "ignoreExistingAssignments", false);
final PlayerSource playerSource = PlayerResolver.resolvePlayer(player);
@@ -52,7 +52,7 @@ public class AssignFpCharacterToSkirmish implements EffectAppenderProducer {
final String assigningPlayer = playerSource.getPlayer(actionContext);
Side assigningSide = GameUtils.getSide(actionContext.getGame(), assigningPlayer);
final Filterable filter = minionFilter.getFilterable(actionContext);
return Filters.assignableToSkirmishAgainst(assigningSide, filter, ignoreUnassigned, false);
return Filters.assignableToSkirmishAgainst(assigningSide, filter, ignoreExistingAssignments, false);
}, fpCharacterMemory, player, "Choose character to assign to skirmish", environment));
result.addEffectAppender(
CardResolver.resolveCard(minion,
@@ -60,7 +60,7 @@ public class AssignFpCharacterToSkirmish implements EffectAppenderProducer {
final String assigningPlayer = playerSource.getPlayer(actionContext);
Side assigningSide = GameUtils.getSide(actionContext.getGame(), assigningPlayer);
final Collection<? extends PhysicalCard> fpChar = actionContext.getCardsFromMemory(fpCharacterMemory);
return Filters.assignableToSkirmishAgainst(assigningSide, Filters.in(fpChar), ignoreUnassigned, false);
return Filters.assignableToSkirmishAgainst(assigningSide, Filters.in(fpChar), ignoreExistingAssignments, false);
}, minionMemory, player, "Choose minion to assign to character", environment));
DelayedAppender assignAppender = new DelayedAppender() {
@@ -71,7 +71,7 @@ public class AssignFpCharacterToSkirmish implements EffectAppenderProducer {
final Collection<? extends PhysicalCard> minion = actionContext.getCardsFromMemory(minionMemory);
if (fpChar.size() == 1 && minion.size() == 1) {
AssignmentEffect effect = new AssignmentEffect(assigningPlayer, fpChar.iterator().next(), minion.iterator().next());
effect.setIgnoreSingleMinionRestriction(ignoreUnassigned);
effect.setIgnoreSingleMinionRestriction(ignoreExistingAssignments);
return effect;
} else {
return null;

View File

@@ -194,13 +194,13 @@ public class Filters {
return assignableToSkirmishAgainst(assignedBySide, againstFilter, false, false);
}
public static Filter assignableToSkirmishAgainst(final Side assignedBySide, final Filterable againstFilter, final boolean ignoreUnassigned, final boolean allowAllyToSkirmish) {
public static Filter assignableToSkirmishAgainst(final Side assignedBySide, final Filterable againstFilter, final boolean ignoreExistingAssignments, final boolean allowAllyToSkirmish) {
return Filters.and(
assignableToSkirmish(assignedBySide, ignoreUnassigned, allowAllyToSkirmish),
assignableToSkirmish(assignedBySide, ignoreExistingAssignments, allowAllyToSkirmish),
(Filter) (game, physicalCard) -> {
for (PhysicalCard card : Filters.filterActive(game, againstFilter)) {
if (card.getBlueprint().getSide() != physicalCard.getBlueprint().getSide()
&& Filters.assignableToSkirmish(assignedBySide, ignoreUnassigned, allowAllyToSkirmish).accepts(game, card)) {
&& Filters.assignableToSkirmish(assignedBySide, ignoreExistingAssignments, allowAllyToSkirmish).accepts(game, card)) {
Map<PhysicalCard, Set<PhysicalCard>> thisAssignment = new HashMap<>();
if (card.getBlueprint().getSide() == Side.FREE_PEOPLE) {
if (thisAssignment.containsKey(card))
@@ -222,7 +222,7 @@ public class Filters {
});
}
public static Filter assignableToSkirmish(final Side assignedBySide, final boolean ignoreUnassigned, final boolean allowAllyToSkirmish) {
public static Filter assignableToSkirmish(final Side assignedBySide, final boolean ignoreExistingAssignments, final boolean allowAllyToSkirmish) {
Filter assignableFilter = Filters.or(
Filters.and(
CardType.ALLY,
@@ -253,9 +253,9 @@ public class Filters {
return Filters.and(
assignableFilter,
(Filter) (game, physicalCard) -> {
if (!ignoreUnassigned) {
boolean notAssignedToSkirmish = Filters.notAssignedToSkirmish.accepts(game, physicalCard);
if (!notAssignedToSkirmish)
if (!ignoreExistingAssignments) {
boolean assignedToSkirmish = Filters.assignedToSkirmish.accepts(game, physicalCard);
if (assignedToSkirmish)
return false;
}
return game.getModifiersQuerying().canBeAssignedToSkirmish(game, assignedBySide, physicalCard);

View File

@@ -44,14 +44,14 @@ public class FreePeoplePlayerAssignsMinionsGameProcess implements GameProcess {
protected void doPlayEffect(final LotroGame game) {
final GameState gameState = game.getGameState();
final Collection<PhysicalCard> minions = Filters.filterActive(game, CardType.MINION, Filters.assignableToSkirmish(Side.FREE_PEOPLE, true, false));
final Collection<PhysicalCard> minions = Filters.filterActive(game, CardType.MINION, Filters.assignableToSkirmish(Side.FREE_PEOPLE, false, false));
if (minions.size() > 0) {
final Collection<PhysicalCard> freePeopleTargets =
Filters.filterActive(game,
Filters.and(
Filters.or(
CardType.COMPANION, CardType.ALLY),
Filters.assignableToSkirmish(Side.FREE_PEOPLE, true, false)));
Filters.assignableToSkirmish(Side.FREE_PEOPLE, false, false)));
game.getUserFeedback().sendAwaitingDecision(gameState.getCurrentPlayerId(),
@@ -60,7 +60,7 @@ public class FreePeoplePlayerAssignsMinionsGameProcess implements GameProcess {
public void decisionMade(String result) throws DecisionResultInvalidException {
Map<PhysicalCard, Set<PhysicalCard>> assignments = getAssignmentsBasedOnResponse(result);
Set<PhysicalCard> unassignedMinions = new HashSet<>(Filters.filterActive(game, CardType.MINION));
Set<PhysicalCard> unassignedMinions = new HashSet<>(Filters.filterActive(game, CardType.MINION, Filters.assignableToSkirmish(Side.FREE_PEOPLE, false, false)));
// Validate minion count (Defender)
for (PhysicalCard freeCard : assignments.keySet()) {
Set<PhysicalCard> minionsAssigned = assignments.get(freeCard);

View File

@@ -17,8 +17,9 @@ public class Card_01_262_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "1_262");
// put other cards in here as needed for the test case
put("assassin", "1_262");
put("runner", "1_178");
put("sam", "1_310");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -46,7 +47,7 @@ public class Card_01_262_Tests
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("assassin");
assertEquals("Orc Assassin", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -62,18 +63,37 @@ public class Card_01_262_Tests
assertEquals(6, card.getBlueprint().getSiteNumber());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void OrcAssassinTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void OrcAssassinDoesNotInterruptSkirmishesAfterUsingAssignmentAbility() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var assassin = scn.GetShadowCard("assassin");
var runner = scn.GetShadowCard("runner");
scn.ShadowMoveCharToTable(assassin, runner);
var frodo = scn.GetRingBearer();
var sam = scn.GetFreepsCard("sam");
scn.FreepsMoveCharToTable(sam);
scn.StartGame();
scn.FreepsPlayCard(card);
assertEquals(2, scn.GetTwilight());
scn.SkipToPhase(Phase.ASSIGNMENT);
scn.FreepsPassCurrentPhaseAction();
assertTrue(scn.ShadowActionAvailable(assassin));
scn.ShadowUseCardAction(assassin);
assertEquals(2, scn.GetFreepsCardChoiceCount());
assertTrue(scn.CanBeAssigned(sam));
scn.FreepsChooseCard(sam);
assertTrue(scn.IsCharAssigned(sam));
assertTrue(scn.IsCharAssigned(assassin));
scn.PassCurrentPhaseActions();
scn.FreepsAssignToMinions(frodo, runner);
// If beginning skirmishes now fails, then there is some dangling minion that Gemp thinks
// is unassigned, which isn't right.
scn.FreepsResolveSkirmish(sam);
}
}