Files
Dune/game/kull-default-profile.ts

62 lines
3.5 KiB
TypeScript

/** Public lobby facts only. This controls fresh Basic setup and never examines
* a player's cards, spice or any hidden Karama/counter eligibility. */
export type KullDefaultProfile = {
status: string;
advanced: boolean;
expansions: readonly string[];
players: readonly { faction: string }[];
lobbyOptions?: { leaderSkills?: unknown; ecazTreachery?: unknown } | null;
/** Same public selections on a projected lobby view. */
lobbyModules?: { leaderSkills?: unknown; ecazTreachery?: unknown; nexusCards?: unknown; discoveries?: unknown } | null;
leaderSkills?: unknown;
ecazTreachery?: unknown;
nexusCards?: unknown;
discoveryEnabled?: unknown;
discoveries?: unknown;
discoveryStash?: unknown;
greatMaker?: unknown;
homeworlds?: unknown;
techTokens?: unknown;
strongholdCards?: unknown;
};
export type KullDefaultEligibility = { eligible: boolean; reason: string | null };
const classic = new Set(['atreides', 'beneGesserit', 'emperor', 'fremen', 'guild', 'harkonnen']);
const ix = new Set(['ixians', 'tleilaxu']);
const choam = new Set(['choam', 'richese']);
const ecaz = new Set(['ecaz', 'moritani']);
const blocked = (reason: string): KullDefaultEligibility => ({ eligible: false, reason });
/** Public profile eligibility is independent of the host's explicit opt-out.
* A lone CHOAM seat can see the choice while waiting for others; the native
* start still checks readiness, minimum seats and all fresh private inventory.
* This quote cannot authorize a start or inspect pending/private game state. */
export function kullDefaultProfile(game: KullDefaultProfile): KullDefaultEligibility {
if (game.status !== 'lobby') return blocked('Automatic Kull applies only to a fresh lobby.');
if (game.advanced !== false) return blocked('Automatic Kull currently supports Basic rules only.');
if (!Array.isArray(game.expansions) ||
!(game.expansions.length === 2 || game.expansions.length === 3 && game.expansions.includes('ecaz')) ||
!game.expansions.includes('choam') || !game.expansions.includes('ix') ||
new Set(game.expansions).size !== game.expansions.length)
return blocked('Automatic Kull currently needs the CHOAM and Ix decks, with Ecaz optional.');
if (!Array.isArray(game.players) || game.players.length < 1 || game.players.length > 6 ||
!game.players.some(player => player?.faction === 'choam') ||
!game.players.every(player => !!player && typeof player.faction === 'string' &&
(classic.has(player.faction) || ix.has(player.faction) && game.expansions.includes('ix') ||
choam.has(player.faction) && game.expansions.includes('choam') ||
ecaz.has(player.faction) && game.expansions.includes('ecaz'))) ||
new Set(game.players.map(player => player.faction)).size !== game.players.length)
return blocked('Automatic Kull needs one to six distinct factions, including CHOAM, with their selected decks.');
const options = game.lobbyOptions, projected = game.lobbyModules;
if (options?.leaderSkills || options?.ecazTreachery || projected?.leaderSkills || projected?.ecazTreachery ||
projected?.nexusCards || projected?.discoveries || game.leaderSkills || game.ecazTreachery || game.nexusCards ||
game.discoveryEnabled || game.discoveries || game.discoveryStash || game.greatMaker ||
game.homeworlds || game.techTokens || game.strongholdCards)
return blocked('Automatic Kull currently excludes optional modules.');
return { eligible: true, reason: null };
}
export function kullDefaultEligible(game: KullDefaultProfile): boolean {
return kullDefaultProfile(game).eligible;
}