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:
Quintine committed 2026-09-13 20:19:33 +10:00
1 parent 06f46c6f79
commit 9381ef140d
18 files changed
+1015 -25

No files matched your search

+6 -3
View File
@@ -65,6 +65,7 @@ import {
shrineTruthtranceBotAction,
} from './shrine';
import { reserveShipmentCost } from './shipment-price';
import { quoteSmugglerShipment } from './smuggler-shipment';
import { nexusGuildSecretAllyAction, nexusGuildSecretAllyCanAct, nexusGuildSecretAllyQuote } from './nexus-guild-secret-ally-options';
import { nexusRicheseAction, nexusRicheseQuote } from './nexus-richese-options';
import { liveShipmentPromises, matchesShipment } from './shipment-promises';
@@ -3010,7 +3011,7 @@ function policyActions(g: GameView): Action[] {
if (marker?.location.territory === to.t) continue;
const desired = Math.min(
me.reserves,
level === 0 ? 1 : Math.max([1, 3, 4, 5][level], to.enemy + 2),
level === 0 ? (quoteSmugglerShipment(g, me.id, to.t, 2) ? 2 : 1) : Math.max([1, 3, 4, 5][level], to.enemy + 2),
);
// Quote the discounted candidate independently of ordinary affordability.
const richeseAmount = Math.min(me.reserves, 5, g.nexusRichese?.maxForces ?? 0);
@@ -3024,12 +3025,13 @@ function policyActions(g: GameView): Action[] {
});
if (candidate) actions.push(candidate);
}
for (const amount of new Set([desired, Math.min(desired, 3), 1])) {
for (const amount of new Set([desired, Math.min(desired, 3),
...(me.reserves >= 2 && quoteSmugglerShipment(g, me.id, to.t, 2) ? [2] : []), 1])) {
if (
reserveShipmentCost(
{ faction: me.faction, halfRate },
territory(to.t).type,
amount,
amount - (quoteSmugglerShipment(g, me.id, to.t, amount) ? 1 : 0),
) > shipmentBudget
)
continue;
@@ -3038,6 +3040,7 @@ function policyActions(g: GameView): Action[] {
territory: to.t,
sector: to.s,
amount,
...(quoteSmugglerShipment(g, me.id, to.t, amount) ? { smuggler: true } : {}),
...(level > 0
? { elite: Math.min(amount, me.elites?.reserves ?? 0) }
: {}),