From 4d769af8c7a54ee7184b285c53c4c46bdef74dd7 Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Wed, 28 Oct 2026 22:26:47 +1100 Subject: [PATCH] Expand Semuta and Richese Homeworld shipments --- components/game-table.tsx | 5 +- components/homeworld-shipment.tsx | 11 +- docs/CURRENT_STATUS.md | 25 +++- docs/RICHESE_NO_FIELD_RULES.md | 16 ++- docs/RULE_DECISIONS.md | 8 ++ docs/SEMUTA_DISCARD_CONTINUATIONS.md | 15 +- game/engine.ts | 17 ++- game/homeworld-shipment-options.ts | 21 ++- game/reference.ts | 4 +- game/richese-homeworld-marker-shipment.ts | 29 +++- tests/fixture-semuta-independent-ordinary.ts | 115 ++++++++++++++++ ...world-marker-fresh-mixed-controls.test.tsx | 80 +++++++++++ ...meworld-marker-fresh-mixed-runtime.test.ts | 129 ++++++++++++++++++ ...chese-homeworld-marker-fresh-mixed.test.ts | 99 ++++++++++++++ tests/semuta-independent-ordinary.test.ts | 91 ++++++++++++ 15 files changed, 626 insertions(+), 39 deletions(-) create mode 100644 tests/fixture-semuta-independent-ordinary.ts create mode 100644 tests/richese-homeworld-marker-fresh-mixed-controls.test.tsx create mode 100644 tests/richese-homeworld-marker-fresh-mixed-runtime.test.ts create mode 100644 tests/richese-homeworld-marker-fresh-mixed.test.ts create mode 100644 tests/semuta-independent-ordinary.test.ts diff --git a/components/game-table.tsx b/components/game-table.tsx index f535aef9..e2059311 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -1281,8 +1281,9 @@ export function GameTable({ {g.semutaRecruitsPreview && (

Semuta Drug preview: in this independent-card game, Semuta can react - to another player discarding Recruits. Other Semuta discard - opportunities in this combination are still being implemented.{' '} + to another player discarding Recruits, Hajr, Ghola, Harvester, + Weather Control or Family Atomics. Other discard opportunities are + still being implemented.{' '} Recruits rules

)} diff --git a/components/homeworld-shipment.tsx b/components/homeworld-shipment.tsx index 32b4bd4d..ec0f95d8 100644 --- a/components/homeworld-shipment.tsx +++ b/components/homeworld-shipment.tsx @@ -30,16 +30,17 @@ export function HomeworldNoFieldShipment({ game, act, busy }: { const destination = targets.find(world => world.id === selectedDestination) ?? targets[0]; const tokens = info.private.tokens.filter(token => source ? token.id === deployed?.tokenId : token.id !== info.private!.lastShipped); const token = tokens.find(candidate => candidate.id === selectedToken) ?? tokens[0]; - const pool = source ? game.homeworlds?.worlds?.find(world => world.id === source)?.forces[me.id] : undefined; + const pool = source ? game.homeworlds?.worlds?.find(world => world.id === source)?.forces[me.id] : + { normal: me.reserves - (me.elites?.reserves ?? 0), elite: me.elites?.reserves ?? 0 }; const quote = richeseHomeworldMarkerChoice(game, destination?.id ?? '', token?.id ?? '', allyPayment, source, - source ? forces : undefined); + forces); return
Ship a concealed No-Field

Provisional Homeworld rule: ship one concealed marker {source ? `from ${homeworldName(game, source)}` : 'from your supply'} to another non-allied Homeworld. Its value stays private and its represented forces remain in your reserves until the marker is revealed. This uses your normal shipment. - {source && ' Physical forces already on the same Homeworld may accompany it.'}

+ {' Physical forces from the same source may accompany it.'}

- {source && pool &&
- Accompanying forces from {homeworldName(game, source)} + {pool &&
+ Accompanying forces from {source ? homeworldName(game, source) : 'your reserves'} {(['normal', 'elite'] as const).filter(kind => kind === 'normal' || pool.elite > 0).map(kind =>