Connect optional Smuggler reserve shipment and saved interception
Prototype one free accompanying force into an empty territory with explicit opt-out, shared human/AI pricing, physical elite custody and Guild recipient accounting. Bind pending quotes to their unique saved Guild decision; preserve legacy unstamped full-price declarations. Smuggler battle collection and combined modules remain unfinished and gated. Repair a randomly dealt-out Karama fixture by relocating its actual physical card without changing inventory. Validation: 70 focused tests; typecheck, lint, 4477 offline tests, production build and 43 HTTP tests passed. Independent rules/persistence review completed, including interruption and cancellation. Browser checked the price toggle, three arrivals for two spice, and refresh with unchanged private cards. Two genuine four-profile Basic/Advanced samples completed without rejected candidates but did not use Smuggler. Preserved all 427 opening rooms and original seat/recovery rows (461 rooms after QA/HTTP). SQLite integrity and foreign keys passed. Server remained healthy after the reported power outage; no restart or reset. Verified source tree: bf6e02bf6873ca6d6b01c83481489f55b2c7db0184a7056364343440dae88803
This commit is contained in:
1 parent
06f46c6f79
commit
9381ef140d
18 files changed
+1015
-25
No files matched your search
@@ -20,6 +20,29 @@ import { enterDiscoveryCollection } from './fixture-discovery';
|
||||
|
||||
const reload = (game: Game): Game => JSON.parse(JSON.stringify(game)) as Game;
|
||||
|
||||
function giveExistingCard(
|
||||
game: Game,
|
||||
owner: string,
|
||||
predicate: (card: Game['deck'][number]) => boolean,
|
||||
) {
|
||||
const recipient = game.players.find((player) => player.id === owner)!;
|
||||
const zones = [game.deck, game.discard, ...game.players.map((p) => p.hand)];
|
||||
for (const zone of zones) {
|
||||
const index = zone.findIndex(predicate);
|
||||
if (index < 0) continue;
|
||||
const [card] = zone.splice(index, 1);
|
||||
recipient.hand.push(card);
|
||||
return card;
|
||||
}
|
||||
assert.fail('The initialized physical deck has no Karama card.');
|
||||
}
|
||||
|
||||
function physicalCardIds(game: Game) {
|
||||
return [...game.deck, ...game.discard, ...game.players.flatMap((p) => p.hand)]
|
||||
.map((card) => card.id)
|
||||
.sort();
|
||||
}
|
||||
|
||||
function initialized(
|
||||
factions: [FactionId, FactionId, FactionId],
|
||||
expansions: string[] = [],
|
||||
@@ -116,10 +139,9 @@ function pendingAdvisorEntry(
|
||||
const beneGesserit = game.players[1];
|
||||
beneGesserit.reserves--;
|
||||
beneGesserit.forces['cistern:0'] = 1;
|
||||
const karama = game.deck.find((card) => card.effect === 'karama');
|
||||
assert.ok(karama);
|
||||
game.deck = game.deck.filter((card) => card.id !== karama.id);
|
||||
actor.hand.push(karama);
|
||||
const custody = physicalCardIds(game);
|
||||
giveExistingCard(game, actor.id, (card) => card.effect === 'karama');
|
||||
assert.deepEqual(physicalCardIds(game), custody);
|
||||
game = nextTurn(game);
|
||||
game = applyAction(game, actor.id, entryAction(game));
|
||||
assert.equal(game.discoveryEntry?.stage, 'arrival');
|
||||
|
||||
Reference in new issue
Block a user