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}
}