Prototype Fremen Nexus free Revival for base factions

This commit is contained in:
Quintine committed 2026-09-27 14:48:31 +10:00
1 parent 5c1c944a4f
commit 1e690b7062
10 files changed
+350 -7

No files matched your search

+3 -1
View File
@@ -12,6 +12,7 @@ import { nexusCardAction } from '@/game/nexus-card-options';
import { NEXUS_CARD_REFERENCE, NEXUS_PANEL_NAMES, nexusCardReference } from '@/game/nexus-card-reference';
import { Button } from './ui/button';
import { NexusEmperorSecretAlly } from './nexus-emperor-secret-ally';
import { NexusFremenRevival } from './nexus-fremen-revival';
import { Dialog, DialogClose, DialogDescription, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './ui/dialog';
const panels: NexusCardMode[] = ['betrayal', 'cunning', 'secretAlly'];
@@ -119,7 +120,7 @@ export function NexusCards({ game, act, busy }: { game: GameView; act: (action:
<div>
<h3>Nexus cards</h3>
<p className="fine">{offer.deckCount} in deck · {offer.discardCount} discarded · {Object.values(offer.held).filter((held) => held === true).length} held</p>
<p>Playable effects include Atreides inspections, Harkonnen exchanges, and Tleilaxu, Ixian, Bene Gesserit, Emperor, CHOAM and Moritani Cunning, plus Emperor Secret Ally’s three extra free revivals, Richese Secret Ally reserve shipping and Guild Cunning’s second shipment or absent-Guild Secret Ally shipping. Use the controls shown at each effect’s timing. CHOAM’s Kull Wahad effect, Moritani Cunning relocation and Grumman combinations, and other unfinished effects remain unavailable.</p>
<p>Playable effects include Atreides inspections, Harkonnen exchanges, and Tleilaxu, Ixian, Bene Gesserit, Emperor, CHOAM and Moritani Cunning, plus Emperor Secret Ally’s three extra free revivals, Fremen Secret Ally’s ordinary three free revivals, Richese Secret Ally reserve shipping and Guild Cunning’s second shipment or absent-Guild Secret Ally shipping. Use the controls shown at each effect’s timing. CHOAM’s Kull Wahad effect, Moritani Cunning relocation and Grumman combinations, and other unfinished effects remain unavailable.</p>
</div>
{game.nexusAtreides && offer.card === 'atreides' && (
<div aria-label="Atreides Nexus effect" className="space-y-2">
@@ -145,6 +146,7 @@ export function NexusCards({ game, act, busy }: { game: GameView; act: (action:
</div>
)}
<NexusEmperorSecretAlly game={game} act={act} busy={busy} />
<NexusFremenRevival game={game} act={act} busy={busy} />
<div aria-label="Your private Nexus card" className="max-w-xl">
{offer.card ? <NexusCardFace card={offer.card} mode={mode} /> : <p>You do not hold a Nexus card.</p>}
</div>
+43
View File
@@ -0,0 +1,43 @@
'use client';
import { useState } from 'react';
import type { Action, GameView } from '@/game/engine';
import { nexusFremenRevivalAction } from '@/game/nexus-fremen-revival-options';
import { Button } from '@/components/ui/button';
export function NexusFremenRevival({ game, act, busy }: {
game: GameView;
act: (action: Action) => void;
busy: boolean;
}) {
const [selectedElite, setSelectedElite] = useState<number | null>(null);
const offer = game.nexusFremenRevival;
if (!offer || game.phase !== 4 || game.nexusCards?.card !== 'fremen') return null;
const options = offer.eliteOptions;
const elite = options.length === 1 ? options[0] :
selectedElite !== null && options.includes(selectedElite) ? selectedElite : null;
const action = elite === null ? null : nexusFremenRevivalAction(game, elite);
return (
<section className="min-w-0 space-y-3" aria-label="Fremen Secret Ally revival">
<h3 className="font-serif text-xl">Fremen Secret Ally</h3>
<p className="text-base leading-7">Spend your Fremen Nexus card to return 3 forces from Tanks to reserves for free. This uses your ordinary three-force Revival allowance; the Advanced elite cap still applies.</p>
{offer.blocked && <p className="notice">{offer.blocked}</p>}
{options.length > 1 && (
<label className="block space-y-2">
<span className="font-semibold">Elite forces to revive</span>
<select aria-label="Fremen Nexus elite forces to revive"
className="min-h-11 w-full rounded border border-[#65644b] bg-[#171b17] px-3 py-2"
value={elite ?? ''} disabled={busy || !!offer.blocked}
onChange={event => setSelectedElite(event.currentTarget.value === '' ? null : Number(event.currentTarget.value))}>
<option value="">Choose an elite count</option>
{options.map(count => <option key={count} value={count}>{count}</option>)}
</select>
</label>
)}
<Button className="min-h-11 whitespace-normal" disabled={busy || !action}
onClick={() => { if (!busy && action) act(action); }}>
Revive 3 free forces{elite === null ? '' : ` · ${elite} elite`}
</Button>
</section>
);
}