diff --git a/README.md b/README.md index 34e26496..3eba50ab 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,19 @@ this is not a second shipment, free upfront financing or extra Guild income. See [payment custody and source boundaries](docs/NEXUS_GUILD_RULES.md#betrayal-payment-replacement). Existing games and public start gates remain unchanged. +For local **Ixian Nexus purchased-card replacement**, use +`node --import tsx tools/start-prototype.ts --profile ixian-replacement` +on a fresh ready classic Basic/Advanced lobby with base Treachery and Nexus +alone. Exact-version/private-backup safeguards apply; existing games and +public starts are unchanged. A real alliance-qualified Nexus must first +give an unallied buyer its card, so effect scenarios need at least three +seats. **Keep purchased card** retains both cards; eligible **Use Ixian Nexus** +discards exactly the normal paid/printed-Karama purchase before drawing the +real deck top, with the original payment and auction continuation once. +Full hands and same-card redraw from an exhausted deck are supported. +Harkonnen buyers and special sale origins remain guarded; see +[verified source, privacy and runtime boundaries](docs/NEXUS_IXIAN_REPLACEMENT_RULES.md). + Above the board, **Treachery draw pile** and **Spice draw pile** show live card counts separately from hands, auction cards and discards. See [draw-pile counts](docs/DRAW_PILES.md). diff --git a/components/game-table.tsx b/components/game-table.tsx index 28ecdb3c..4dbee962 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -81,6 +81,7 @@ import { SemutaReaction } from './semuta-reaction'; import { ChoamKull } from './choam-kull'; import { NexusRicheseBetrayal } from './nexus-richese-betrayal'; import { NexusGuildBetrayal } from './nexus-guild-betrayal'; +import { NexusIxianReplacement } from './nexus-ixian-replacement'; import { OrnithopterMovement } from './ornithopter-movement'; import { DiscoveryOrnithopterMovement } from './discovery-flight-movement'; import { PlanetologistMovement } from './planetologist-movement'; @@ -305,8 +306,8 @@ export function GameTable({ ); const reactionBusy = transportBusy || !!g.roomControl?.paused || !!g.roomControl?.closed || !!me.autopilot; - const busy = reactionBusy || g.automaticContinuationPending || !!g.semutaReaction || !!g.kullReaction || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction; - const reactionOwnsControls = !!g.kullReaction || !!g.kullCounterEvent || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction; + const busy = reactionBusy || g.automaticContinuationPending || !!g.semutaReaction || !!g.kullReaction || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction || !!g.nexusIxianReplacement; + const reactionOwnsControls = !!g.kullReaction || !!g.kullCounterEvent || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction || !!g.nexusIxianReplacement; const canActivateKarama = (card: Card) => !g.karamaBlocked && canUseAsKaramaRole(g, me, card); const traitorBattle = g.battle; @@ -790,6 +791,13 @@ export function GameTable({ a.event !== guildReaction.event || (a.type === 'guildBetrayalUse' && (!guildReaction.canUse || guildReaction.blocked))) return; } + const replacement = g.nexusIxianReplacement; + if (replacement && a.type !== 'setAutopilot') { + if (reactionBusy || g.status !== 'playing' || !replacement.canPass || replacement.buyer !== me.id || + (a.type !== 'nexusIxianReplacementPass' && a.type !== 'nexusIxianReplacementUse') || + a.event !== replacement.event || + (a.type === 'nexusIxianReplacementUse' && (!replacement.canUse || replacement.blocked))) return; + } void send(a); }; const choose = (id: string) => { @@ -1077,6 +1085,14 @@ export function GameTable({ Preview rules and limits

)} + {g.nexusIxianReplacementPreview && ( +

+ Ixian Nexus purchased-card replacement development preview · classic Basic/Advanced, + base Treachery and Nexus only. Normal paid and printed Karama purchases are supported; + Harkonnen buyers and special auctions are guarded. Not complete Ixian or certified Nexus rules.{' '} + Preview rules and limits +

+ )} {g.status !== 'lobby' && (