Initial commit
This commit is contained in:
commit
8c709fb52f
771 files changed
+222983
No files matched your search
@@ -0,0 +1,84 @@
|
||||
'use client';
|
||||
import { useState } from 'react';
|
||||
import type { Action, GameView } from '@/game/engine';
|
||||
import { splitLocation } from '@/game/board';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
export function IxSubstitution({
|
||||
game: g,
|
||||
act,
|
||||
busy,
|
||||
}: {
|
||||
game: GameView;
|
||||
act: (action: Action) => void;
|
||||
busy: boolean;
|
||||
}) {
|
||||
const [sources, setSources] = useState<Record<string, number>>({});
|
||||
const [recover, setRecover] = useState<Record<string, number>>({});
|
||||
if (g.decision?.kind !== 'ixSubstitution') return null;
|
||||
const d = g.decision;
|
||||
const me = g.players.find((p) => p.id === g.me)!;
|
||||
const available = Object.entries(me.forces)
|
||||
.filter(([key]) => splitLocation(key).territory === d.territory)
|
||||
.map(
|
||||
([key, count]) => [key, count - (me.elites?.forces[key] ?? 0)] as const,
|
||||
)
|
||||
.filter(([, count]) => count > 0);
|
||||
const total = (value: Record<string, number>) =>
|
||||
Object.values(value).reduce((a, b) => a + b, 0);
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Exchange surviving suboids, one for one, to retain cyborgs lost in this
|
||||
battle. Retained cyborgs return to their casualty sectors. Opponents may
|
||||
cancel this substitution with Karama.
|
||||
</p>
|
||||
<h3>Suboids to sacrifice</h3>
|
||||
{available.map(([key, max]) => (
|
||||
<label key={key}>
|
||||
Sector {splitLocation(key).sector} · {max} suboids available
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={max}
|
||||
value={sources[key] ?? 0}
|
||||
onChange={(e) =>
|
||||
setSources({ ...sources, [key]: Number(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
<h3>Cyborgs to retain</h3>
|
||||
{Object.entries(d.losses).map(([key, max]) => (
|
||||
<label key={key}>
|
||||
Sector {splitLocation(key).sector} · {max} cyborgs lost
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={max}
|
||||
value={recover[key] ?? 0}
|
||||
onChange={(e) =>
|
||||
setRecover({ ...recover, [key]: Number(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
<p>
|
||||
{total(sources)} suboids for {total(recover)} cyborgs
|
||||
</p>
|
||||
<Button
|
||||
disabled={busy || !total(sources) || total(sources) !== total(recover)}
|
||||
onClick={() => act({ type: 'decision', sources, recover })}
|
||||
>
|
||||
Confirm substitution
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={busy}
|
||||
onClick={() => act({ type: 'decision', decline: true })}
|
||||
>
|
||||
Keep these casualties
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user