From b4157aaa9bb89c9643704985eb98b0a630881ded Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:35:59 +1100 Subject: [PATCH] Connect native Fremen Nexus effects across selected modules Retain actual Cunning appearances, paid Karama and typed remote riding with optional Sandmaster collection and source-parent HMS entry. Preserve Betrayal movement sources and apply its declared worm restriction to Great Maker reserve riding without suppressing votes or casualties. Completed Maker history no longer blocks a later ordinary offer. Validation: frozen 14-file86/86, types/lint/build, nine finite original-action/rendered-control programmes, four minimal legal policies. Discovery destination proof remains focused quotes pending actual arrival; full combinations, assurance and deployed acceptance remain required. No saved-game, restart or deployment operation. --- components/game-table.tsx | 22 +- components/nexus-fremen-betrayal.tsx | 4 + docs/CURRENT_STATUS.md | 29 ++ docs/IMPLEMENTATION_STATUS.md | 18 ++ docs/NEXUS_CARD_RULES.md | 55 ++++ docs/NEXUS_FREMEN_RULES.md | 59 ++++ docs/RULE_DECISIONS.md | 16 ++ docs/SANDMASTER_WORM.md | 29 ++ game/bots.ts | 12 +- game/engine.ts | 37 ++- game/fremen-nexus-profile.ts | 18 ++ game/nexus-fremen-betrayal.ts | 10 +- game/nexus-fremen-cunning.ts | 38 ++- game/reference.ts | 14 +- game/sandmaster-worm.ts | 23 ++ tests/fixture-fremen-betrayal-modules.ts | 247 +++++++++++++++++ tests/fixture-fremen-cunning-modules.ts | 261 ++++++++++++++++++ tests/fremen-betrayal-modules.test.ts | 176 ++++++++++++ tests/fremen-cunning-modules.test.ts | 150 ++++++++++ ...exus-fremen-cunning-modules-quotes.test.ts | 172 ++++++++++++ 20 files changed, 1349 insertions(+), 41 deletions(-) create mode 100644 game/fremen-nexus-profile.ts create mode 100644 tests/fixture-fremen-betrayal-modules.ts create mode 100644 tests/fixture-fremen-cunning-modules.ts create mode 100644 tests/fremen-betrayal-modules.test.ts create mode 100644 tests/fremen-cunning-modules.test.ts create mode 100644 tests/nexus-fremen-cunning-modules-quotes.test.ts diff --git a/components/game-table.tsx b/components/game-table.tsx index 8691ac4f..99ec141e 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -101,7 +101,8 @@ import { PlanetologistMovement } from './planetologist-movement'; import { selectedOriginElites, advisorFlipMovementBlocked } from '@/game/planetologist-movement'; import { SandmasterMovement } from './sandmaster-movement'; import { SandmasterWormChoice } from './sandmaster-worm'; -import { sandmasterWormCollection } from '@/game/sandmaster-worm'; +import { sandmasterWormCollection, sandmasterCunningCollection } from '@/game/sandmaster-worm'; +import { fremenCunningPublicDestinationBlock } from '@/game/nexus-fremen-cunning'; import { ResidualPoison, BattleLeaderOpportunity } from './residual-poison'; import { PortableSnooper } from './portable-snooper'; import { @@ -536,12 +537,18 @@ export function GameTable({ const [rideForces, setRideForces] = useState>({}); const [collectSandmasterRide, setCollectSandmasterRide] = useState(true); const [sandmasterRidePile, setSandmasterRidePile] = useState<{ target: string; key: string } | null>(null); - const sandmasterRideTarget = JSON.stringify([g.turn, g.decision?.kind === 'wormRide' ? g.decision : null, selected, sector]); - const baseSandmasterRide = sandmasterWormCollection(g, me.id, selected, sector); + const sandmasterRideTarget = JSON.stringify([g.turn, + g.decision?.kind === 'wormRide' || g.decision?.kind === 'nexusFremenCunningRide' ? g.decision : null, + source, selected, sector]); + const baseSandmasterRide = g.decision?.kind === 'nexusFremenCunningRide' + ? sandmasterCunningCollection(g, me.id, source, selected, sector) + : sandmasterWormCollection(g, me.id, selected, sector); const selectedRidePile = sandmasterRidePile?.target === sandmasterRideTarget ? baseSandmasterRide?.piles.find(pile => pile.key === sandmasterRidePile.key) : null; const sandmasterRide = selectedRidePile && baseSandmasterRide ? { ...baseSandmasterRide, key: selectedRidePile.key, before: selectedRidePile.before } : baseSandmasterRide; + const cunningDestinationBlocked = g.decision?.kind === 'nexusFremenCunningRide' && source + ? fremenCunningPublicDestinationBlock(g, me.id, source, { territory: selected, sector }) : null; const [exchangeCards, setExchangeCards] = useState>( {}, ); @@ -3828,16 +3835,23 @@ export function GameTable({ )} )} {destination} + {cunningDestinationBlocked &&

{cunningDestinationBlocked}

} + setSandmasterRidePile({ target: sandmasterRideTarget, key })} /> {actionButton('Ride remotely', { type: 'decision', event: g.decision.event, accept: true, source, territory: selected, sector, + ...(sandmasterRide?.blocked === null + ? { sandmasterCollect: collectSandmasterRide, + ...(collectSandmasterRide ? { sandmasterPile: sandmasterRide.key } : {}) } : {}), forces: Object.fromEntries(Object.entries(rideForces) .filter(([key, count]) => count > 0 && splitLocation(key).territory === source) .map(([key, count]) => [key, { normal: count - (eliteForces[key] ?? 0), elite: eliteForces[key] ?? 0, }])), - }, !source || !Object.entries(rideForces).some(([key, count]) => + }, !source || !!cunningDestinationBlocked || !Object.entries(rideForces).some(([key, count]) => count > 0 && splitLocation(key).territory === source))} {actionButton('Decline remote ride', { type: 'decision', event: g.decision.event, accept: false, diff --git a/components/nexus-fremen-betrayal.tsx b/components/nexus-fremen-betrayal.tsx index 0ec6df13..1c6f6c0c 100644 --- a/components/nexus-fremen-betrayal.tsx +++ b/components/nexus-fremen-betrayal.tsx @@ -21,6 +21,10 @@ export function NexusFremenBetrayal({ game, act, busy }: Props) { ? `Declare before the first spice blow: ${fremen.name} cannot take ordinary or Cunning worm rides this turn. Worm destruction, survival, protection and Nexus still resolve.` : `This use suppresses ${fremen.name}’s native two-territory movement advantage for the rest of this turn. Independent ornithopters remain available.`}

} + {offer.mode === 'worm' && game.discoveries &&

+ Provisional Great Maker interpretation: its reserve ride is also prevented; + the original vote, worm effects and Nexus continue. +

} {offer.blocked &&

{offer.blocked}

}