Compose Ornithopter with unused Hajr movement

Replace one unused movement entitlement by the selected card mode and retain independent Hajr movement after a completed two-group flight. Preserve distinct cohorts and later legal reuse. Adopt supplied Advanced immediate-after-movement Hajr play timing with explicit provisional online/card-group interpretation; Basic timing remains.

Validation: frozen11-file132/132, types/lint/build and12 original-action/rendered-table programmes; four minimal policies retain both continuations. Actual cards are acquired by original auctions and source groups use funded shipments. Multiple Hajr cards, advisors, wider combinations, final assurance and deployed acceptance remain open. No saved-game, reset, restart or deployment operation.
This commit is contained in:
Quintine committed 2026-10-18 12:23:37 +11:00
1 parent 6ccfaa1b4d
commit 72e77d7f4d
16 files changed
+522 -31

No files matched your search

+10 -4
View File
@@ -163,7 +163,7 @@ for (const advanced of [false, true]) for (const { mode, early } of paths) {
});
}
for (const advanced of [false, true]) void test(`${advanced ? 'Advanced' : 'Basic'} Homeworld original prior-move, Hajr and fixed maximum-three guards remain real`, () => {
for (const advanced of [false, true]) void test(`${advanced ? 'Advanced' : 'Basic'} Homeworld prior move needs an unused entitlement and Hajr preserves the fixed maximum three`, () => {
const f = createSemutaHomeworldOrnithopterFixture('range3', advanced);
const before = structuredClone(f.beforeFirstMove);
assert.throws(() => applyAction(before, f.owner, { ...f.firstMove, ...splitLocation(f.tooFarDestination) }));
@@ -174,14 +174,20 @@ for (const advanced of [false, true]) void test(`${advanced ? 'Advanced' : 'Basi
assert.equal(player(moved, f.owner).hand.some(c => c.id === f.card), true);
assert.throws(() => applyAction(moved, f.owner, { ...f.firstMove, from: f.secondDestination,
...splitLocation(f.origin) }));
const hajrGame = structuredClone(before);
const hajrGame = structuredClone(advanced ? moved : before);
const hajr = holdSemutaHomeworldChoamCard(hajrGame, f.owner, 'Hajr');
const played = applyAction(hajrGame, f.owner, { type: 'card', card: hajr });
const allowed = advanceSemutaHomeworldChoam(played,
g => !g.pendingTreacheryDiscard && !g.response && !g.decision && !g.phaseOpening);
assert.equal(player(allowed, f.owner).moved, 0);
assert.equal(player(allowed, f.owner).moved, advanced ? 1 : 0);
assert.ok(allowed.hajr.includes(f.owner));
assert.throws(() => applyAction(allowed, f.owner, f.firstMove));
assert.equal(player(allowed, f.owner).hand.some(c => c.id === f.card), true);
const declared = applyAction(allowed, f.owner, advanced
? { ...f.firstMove, from: f.secondDestination, ...splitLocation(f.origin) }
: f.firstMove);
const completed = advanceSemutaHomeworldChoam(declared,
g => !g.pendingTreacheryDiscard && !g.response && !g.decision && !g.phaseOpening);
assert.equal(player(completed, f.owner).moved, advanced ? 2 : 1);
assertCounters(allowed, f.setup);
assertCounters(completed, f.setup);
});