Prototype charity Truthtrance and coalition rule foundations
Verify and publish checkpoint container / container (push) Successful in 10m14s

This commit is contained in:
Quintine committed 2026-10-28 21:58:30 +11:00
1 parent e896570f07
commit cea310b030
17 files changed
+1024 -35

No files matched your search

+22 -4
View File
@@ -11,6 +11,7 @@ import { TECH_TOKENS } from './tech-tokens';
import { isDiscoveryLocationId, JACURUTU_SIETCH } from './discoveries';
import type { TwoPlayerControllers } from './two-player-controllers';
import { allianceVictoryTarget } from './alliance-victory-option';
import { coalitionGroups } from './coalition-alliance';
export type StrongholdProgress = {
player: string;
@@ -37,6 +38,22 @@ function requireProgress(
if (!condition) throw new VictoryProgressError(message);
}
/** Ordered side membership for the bounded coalition victory quote. Three-member
* Advanced and Ecaz combinations remain unavailable until their board rules
* and faction interactions are integrated. No game mode is enabled here. */
export function victoryMembers(g: BoardContext, player: string): string[] {
const seat = g.players.find(candidate => candidate.id === player);
requireProgress(seat, 'Victory needs a seated player.');
if (g.coalitionState === undefined)
return g.order.filter(member => member === player || member === seat.ally);
const groups = coalitionGroups(g.players, g.coalitionState);
requireProgress(!groups.some(group => group.members.length === 3 &&
(g.advanced || g.players.some(candidate => candidate.faction === 'ecaz'))),
'Three-member victory rules require Basic play without Ecaz.');
const group = groups.find(candidate => candidate.members.includes(player));
return g.order.filter(member => member === player || !!group?.members.includes(member));
}
/** Discovery locations use stronghold shipping and occupancy rules, but only
* Jacurutu Sietch counts toward the normal stronghold victory. */
export function countsForStrongholdVictory(id: string) {
@@ -65,6 +82,8 @@ export function strongholdProgress(
const extra = g.victoryExtraStrongholds ?? 0;
requireProgress(Number.isSafeInteger(extra) && extra >= 0 && extra <= 200,
'The agreed extra stronghold target must be a safe nonnegative count.');
requireProgress(g.shortAllianceVictory === undefined || typeof g.shortAllianceVictory === 'boolean',
'Choose whether the shorter alliance victory option is enabled.');
// This validates every ID, faction and reciprocal alliance, even where no
// stronghold has a force. It does not merge ordinary allies for occupancy.
ecazOccupancyIdentity(g.players, g.players[0].id, {
@@ -102,9 +121,7 @@ export function strongholdProgress(
);
const progress = g.order.map((id) => {
const p = players.find((p) => p.id === id)!;
const members = g.order.filter(
(member) => member === id || member === p.ally,
);
const members = victoryMembers(g, id);
const partner = players.find((other) => other.id === p.ally);
const ecazPair =
!!partner && (p.faction === 'ecaz' || partner.faction === 'ecaz');
@@ -136,7 +153,8 @@ export function strongholdProgress(
TECH_TOKENS.every(({ id }) => tech[id].owner === member),
);
const target = allianceVictoryTarget(
players.map((player) => player.id), g.twoPlayerControllers, !!partner, g.shortAllianceVictory,
players.map((player) => player.id), g.twoPlayerControllers, members.length > 1,
members.length === 3 ? false : g.shortAllianceVictory,
) + extra;
return {
player: id,