Connect printed high Ecaz Homeworld alliance victory

This commit is contained in:
Quintine committed 2026-10-05 14:41:11 +11:00
1 parent cfa0fe3952
commit 17bc4537c3
14 files changed
+878 -8

No files matched your search

+15 -1
View File
@@ -1109,6 +1109,15 @@ export function GameTable({
<a href="/rules?topic=homeworlds#homeworlds">Preview rules and limits</a>
</output>
)}
{g.homeworldEcazVictoryPreview && (
<output className="notice block">
Partial · Ecaz High Homeworld victory development preview, enabled only
for this fresh preview game. Public progress is separate from ordinary
stronghold and Ecaz three-joint-stronghold victory; it is evaluated only
at the Mentat victory check. Unresolved source cases remain guarded.
Not complete or certified Homeworld rules.
</output>
)}
{g.homeworldTupilePreview && (
<output className="notice block">
Occupied Tupile development preview · the occupier and reciprocal ally
@@ -6002,7 +6011,12 @@ export function GameTable({
</div>
)}
</section>
<VictoryProgress progress={g.victoryProgress} players={g.players} fremen={g.fremenVictory} />
<VictoryProgress
progress={g.victoryProgress}
players={g.players}
fremen={g.fremenVictory}
ecazHomeworld={g.ecazHomeworldVictory}
/>
{g.strongholdCards && (
<details className="m-4 rounded-xl border border-[#a88b60] p-4">
<summary className="cursor-pointer text-lg">
+78 -2
View File
@@ -2,23 +2,32 @@ import { faction } from '@/game/catalog';
import { territory } from '@/game/board';
import type { StrongholdProgress } from '@/game/victory-progress';
import type { FremenVictoryProgress } from '@/game/fremen-victory';
import type { GameView } from '@/game/engine';
import { homeworldCard } from '@/game/homeworld-cards';
export function VictoryProgress({
progress,
players,
fremen,
ecazHomeworld,
}: {
progress: readonly StrongholdProgress[];
players: readonly { id: string; faction: string }[];
fremen?: FremenVictoryProgress | null;
ecazHomeworld?: GameView['ecazHomeworldVictory'];
}) {
if (!progress.length) return null;
if (!progress.length && !fremen && !ecazHomeworld) return null;
const rows = progress.filter((row) => row.player === row.members[0]);
const playerFactionName = (id: string) => {
const player = players.find((candidate) => candidate.id === id);
return player ? faction(player.faction).name : id;
};
return (
<details className="m-4 rounded-xl border border-[#a88b60] p-4">
<summary className="cursor-pointer text-lg">
Stronghold victory progress
Victory progress
</summary>
<h3 className="mt-3">Stronghold victory progress</h3>
<p className="muted mt-3">
Current board, evaluated at the Mentat victory check. Battles and other
effects may change these holdings. A correct Bene Gesserit prediction
@@ -69,6 +78,73 @@ export function VictoryProgress({
</li>
))}
</ul>
{ecazHomeworld && (
<section
className="mt-4 space-y-2 break-words rounded-lg border border-[#a88b60]/40 p-3"
aria-label="Ecaz Homeworld victory"
>
<h3>Ecaz High Homeworld victory</h3>
<p>{ecazHomeworld.members.map(playerFactionName).join(' & ')}</p>
<p>
{playerFactionName(ecazHomeworld.owner)} native population:{' '}
{ecazHomeworld.population} / {ecazHomeworld.minimum} for High
{' · '}{ecazHomeworld.high ? 'High' : 'Low'}
</p>
<p className="fine">
Source: printed Ecaz High Homeworld card. High requires at least{' '}
{ecazHomeworld.minimum} native forces on Ecaz. The alliance must jointly
occupy at least one actual stronghold and occupy the Homeworlds of two
other native factions.
</p>
<p>
Joint strongholds: {ecazHomeworld.jointStrongholds.length} / 1 required
</p>
<p className="fine">
{ecazHomeworld.jointStrongholds.length
? ecazHomeworld.jointStrongholds.map((id) => territory(id).name).join(', ')
: 'No jointly occupied stronghold.'}
</p>
<p>
Other native factions:{' '}
{ecazHomeworld.distinctNativeFactions.length} / 2 required
</p>
<p className="fine">
{ecazHomeworld.distinctNativeFactions.length
? ecazHomeworld.distinctNativeFactions.map((id) => faction(id).name).join(', ')
: 'No other native faction’s Homeworld occupied.'}
</p>
{ecazHomeworld.foreignHomeworlds.length > 0 && (
<ul className="space-y-1 text-sm" aria-label="Occupied foreign Homeworlds">
{ecazHomeworld.foreignHomeworlds.map((world) => (
<li key={world.world}>
{homeworldCard(world.card)?.name ?? world.world}
{' · '}native {faction(world.faction).name}
{' · '}held by {playerFactionName(world.holder)}
</li>
))}
</ul>
)}
<p className="fine">
Two physical Emperor Homeworlds count as one other native faction.
Homeworlds add no ordinary stronghold points; a Tech Token set is not a
joint stronghold.
</p>
{ecazHomeworld.blocked && (
<output className="notice block">
Source blocked: {ecazHomeworld.blocked}
</output>
)}
<p className={ecazHomeworld.qualifies && !ecazHomeworld.blocked ? 'notice' : 'fine'}>
{ecazHomeworld.blocked
? 'This source cannot resolve the Ecaz Homeworld victory check.'
: ecazHomeworld.qualifies
? 'Ecaz Homeworld conditions are met on this board.'
: 'Ecaz Homeworld conditions are not met on this board.'}
{' '}Prospective progress only: victory is evaluated at the Mentat
victory check, not awarded here.
</p>
</section>
)}
{fremen && (
<section className="mt-4 space-y-2 rounded-lg border border-[#a88b60]/40 p-3" aria-label="Fremen final-turn victory">
<h3>Fremen final-turn victory</h3>