Preserve Intrusion views across JSON save boundaries
This commit is contained in:
1 parent
0d01d18ddd
commit
51ef855d19
4 files changed
+47
-2
No files matched your search
@@ -60,6 +60,15 @@ this access change; see the [decision index](RULE_DECISIONS.md).
|
||||
- `advanced-preview-http.test.ts`: normal authenticated endpoints, explicit
|
||||
opt-in, origin/authority checks, readiness, duplicate start and refresh.
|
||||
|
||||
An Advanced six-seat sample exposed an Intrusion save boundary: a shipment
|
||||
without an accompanying-advisor offer stored `followup: undefined`, which JSON
|
||||
removed and changed the restored private views. Ordinary and Junction shipment
|
||||
calls now omit that field when no follow-up exists. Focused regressions cover
|
||||
both routes and every seat across JSON restoration. The same genuine six-seat
|
||||
seed then completed at turn 10 after 1,531 actions and 41 restores. A separate
|
||||
ten-game Basic/Advanced base-roster matrix (two through six seats) completed
|
||||
with no rejected actions; these samples do not certify all Advanced interactions.
|
||||
|
||||
The private checkpoint directory records final check/build/HTTP results, ordinary
|
||||
preview-start complete-game samples, browser checks and saved-game preservation.
|
||||
These are development samples, not AI calibration or complete interaction coverage.
|
||||
|
||||
+2
-2
@@ -19212,7 +19212,7 @@ function performJunctionTransport(g: Game, p: Player, action: Action) {
|
||||
const bg = byFaction(g, 'beneGesserit');
|
||||
const followup = offPlanet && bg && bg.id !== p.id && spiritualAdvisorMaximum(g, bg.id) > 0
|
||||
? {shipment: p.id, destination} : undefined;
|
||||
if (!intrusion(g, p, arrival.territory, {followup}) && followup)
|
||||
if (!intrusion(g, p, arrival.territory, followup ? { followup } : undefined) && followup)
|
||||
g.decision = {kind: 'advisor', player: bg!.id, ...followup};
|
||||
openTerritoryEntry(g, p, arrival.territory, arrival.sector, quote.amount, quote.elite, 'shipment');
|
||||
}
|
||||
@@ -19696,7 +19696,7 @@ function commitShipment(g: Game, shipment: PendingShipment) {
|
||||
p.faction !== 'fremen' && bg && bg.id !== p.id && spiritualAdvisorMaximum(g, bg.id) > 0
|
||||
? { shipment: p.id, destination: location(to, s) }
|
||||
: undefined;
|
||||
if (!intrusion(g, p, to, { followup }) && followup)
|
||||
if (!intrusion(g, p, to, followup ? { followup } : undefined) && followup)
|
||||
g.decision = { kind: 'advisor', player: bg!.id, ...followup };
|
||||
if (g.advanced && p.faction === 'fremen' && s === g.storm)
|
||||
g.response = {
|
||||
|
||||
@@ -220,6 +220,19 @@ void test('advisors moving to an empty territory become fighters; optional occup
|
||||
assert.equal(isAdvisor(empty.players[1], 'imperial_basin'), false);
|
||||
assert.equal(empty.response, null);
|
||||
});
|
||||
void test('intrusion without advisor accompaniment restores the same decision and private views', () => {
|
||||
const g = fixture();
|
||||
army(g, 'b', { 'arrakeen:10': 20 });
|
||||
const pending = applyAction(g, 'a', {
|
||||
type: 'ship', territory: 'arrakeen', sector: 10, amount: 2,
|
||||
});
|
||||
assert.equal(pending.decision?.kind, 'intrusion');
|
||||
assert.equal(Object.hasOwn(pending.decision!, 'followup'), false);
|
||||
const restored = JSON.parse(JSON.stringify(pending)) as Game;
|
||||
for (const player of pending.players)
|
||||
assert.deepEqual(viewGame(restored, player.id), viewGame(pending, player.id));
|
||||
});
|
||||
|
||||
void test('intrusion resolves before free accompaniment, with a separate cancelable token flip and same-turn advisor lock', () => {
|
||||
let g = fixture();
|
||||
g.players[0].hand = [baseDeck().find((c) => c.effect === 'karama')!];
|
||||
|
||||
@@ -279,3 +279,26 @@ void test('Junction distinguishes native Fremen Arrakis arrival from a foreign H
|
||||
viewGame(foreign, 'beneGesserit').decision,
|
||||
);
|
||||
});
|
||||
|
||||
void test('Junction Intrusion without accompanying advisors survives JSON restoration', () => {
|
||||
let g = fixture(true);
|
||||
g.active = 'fremen';
|
||||
g.movementRemaining = ['fremen', 'atreides', 'beneGesserit', 'guild'];
|
||||
const bg = player(g, 'beneGesserit');
|
||||
bg.forces = { 'wind_pass:14': 1 };
|
||||
bg.reserves = 19;
|
||||
g = offered(g);
|
||||
const option = viewGame(g, 'fremen').junctionTransport!;
|
||||
g = applyAction(g, 'fremen', {
|
||||
type: 'junctionShip',
|
||||
event: option.event,
|
||||
offer: option.offer!.event,
|
||||
destination: 'wind_pass:14',
|
||||
sources: { 'homeworld:fremen': { normal: 1, elite: 0 } },
|
||||
});
|
||||
assert.equal(g.decision?.kind, 'intrusion');
|
||||
assert.equal(Object.hasOwn(g.decision!, 'followup'), false);
|
||||
const restored: Game = JSON.parse(JSON.stringify(g));
|
||||
for (const seat of g.players)
|
||||
assert.deepEqual(viewGame(restored, seat.id), viewGame(g, seat.id));
|
||||
});
|
||||
Reference in new issue
Block a user