Add Basic group alliance preview to lobby
Verify and publish checkpoint container / container (push) Successful in 10m49s
Verify and publish checkpoint container / container (push) Successful in 10m49s
This commit is contained in:
1 parent
44e7cf8001
commit
89386af95b
7 files changed
+273
-10
No files matched your search
+30
-6
@@ -752,7 +752,7 @@ import {
|
||||
} from './advisors';
|
||||
import { isAlliedShipmentVisit, makeBasicAlliedShipment, quoteBasicAlliedShipment, basicAlliedShipmentHasWitness,
|
||||
type BasicAlliedShipment } from './allied-separation';
|
||||
import { answerCoalitionAdmission, beginCoalitionAdmission, coalitionAllies, coalitionGroups, coalitionOf,
|
||||
import { answerCoalitionAdmission, beginCoalitionAdmission, coalitionAllies, coalitionGroups, coalitionOf, emptyCoalitionState,
|
||||
leaveCoalition, CoalitionAllianceError, type CoalitionState, type CoalitionTransition } from './coalition-alliance';
|
||||
import { coalitionBattleBeneficiary } from './coalition-battle-preparation';
|
||||
import { createCoalitionWormProtection, currentCoalitionWormAlly, decideCoalitionWormProtection,
|
||||
@@ -1448,7 +1448,9 @@ type RicheseAllyOffer = {
|
||||
payer: string;
|
||||
};
|
||||
export type Game = {
|
||||
/** Unexposed group-alliance integration state; absent in existing games. */
|
||||
/** Explicit incomplete Basic group preview; absent in legacy games. */
|
||||
coalitionPreview?: 'three' | 'three-short' | 'unlimited';
|
||||
/** Group-alliance integration state; absent in ordinary existing games. */
|
||||
coalitionState?: CoalitionState;
|
||||
coalitionEscrow?: CoalitionEscrowState;
|
||||
coalitionWormProtection?: CoalitionWormProtectionFrame;
|
||||
@@ -5804,6 +5806,8 @@ function twoPlayerRule<T>(operation: () => T): T {
|
||||
function twoPlayerControllersIntegrity(g: Game): void {
|
||||
requireRule(g.shortAllianceVictory === undefined || typeof g.shortAllianceVictory === 'boolean',
|
||||
'The optional alliance victory setting must be true or false.');
|
||||
requireRule(g.coalitionPreview === undefined || ['three', 'three-short', 'unlimited'].includes(g.coalitionPreview),
|
||||
'Invalid group-alliance preview setting.');
|
||||
if (g.twoPlayerControllers === undefined) return;
|
||||
const seats = g.players.map(player => player.id);
|
||||
twoPlayerRule(() => {
|
||||
@@ -10881,6 +10885,12 @@ function startModules(state: Game): Game {
|
||||
}
|
||||
/** Public consistency only; each module's genuine initializer validates its own envelope. */
|
||||
function baseStartProfileBlock(g: Game): string | null {
|
||||
if (g.coalitionPreview && g.status === 'lobby' &&
|
||||
(g.advanced || g.expansions.length > 0 || g.players.length < 3 ||
|
||||
!g.players.every(p => CLASSIC_FACTIONS[p.faction] === true) || g.twoPlayerControllers ||
|
||||
g.shortAllianceVictory || g.techTokens || g.strongholdCards || g.homeworlds ||
|
||||
g.nexusCards || g.discoveryEnabled || g.leaderSkills || g.ecazTreachery || lobbyModulesSelected(g)))
|
||||
return 'The group-alliance preview needs at least three classic factions, Basic rules, and no expansion decks, other optional modules, two-person setup or separate short-alliance setting.';
|
||||
if (g.status === 'lobby' && (g.moritaniAssassinatePreview || g.moritaniAssassinate))
|
||||
return 'Separate development profiles cannot be enabled by a normal start.';
|
||||
if (g.status === 'lobby' && (g.leaderSkills || g.ecazTreachery))
|
||||
@@ -10945,7 +10955,7 @@ function requireFreshBaseRuntime(g: Game) {
|
||||
const configuration = new Set<keyof Game>(['code', 'version', 'host', 'players', 'playerPositions',
|
||||
'advanced', 'expansions', 'log', 'botsPending', 'botNextActionAt', 'techTokens', 'strongholdCards',
|
||||
'homeworlds', 'nexusCards', 'discoveryEnabled', 'leaderSkills', 'ecazTreachery', 'lobbyOptions', 'kullDefaultPolicy',
|
||||
'twoPlayerControllers', 'shortAllianceVictory']);
|
||||
'twoPlayerControllers', 'shortAllianceVictory', 'coalitionPreview']);
|
||||
const keys = [...new Set([...Object.keys(g), ...Object.keys(fresh)])] as (keyof Game)[];
|
||||
requireRule(keys.every(key => configuration.has(key) ||
|
||||
JSON.stringify(g[key]) === JSON.stringify(fresh[key])),
|
||||
@@ -10982,6 +10992,8 @@ function start(g: Game, advancedPreview = false): Game {
|
||||
'Start with unused Stronghold Cards before assigning ownership.');
|
||||
requireFreshBaseRuntime(g);
|
||||
initializeSetup(g);
|
||||
if (g.coalitionPreview) g.coalitionState = emptyCoalitionState(
|
||||
g.coalitionPreview === 'three' ? undefined : g.coalitionPreview);
|
||||
if (advancedPreview) g.advancedPreview = true;
|
||||
return g;
|
||||
}
|
||||
@@ -16278,9 +16290,8 @@ function allianceQuestionWindow(g: Game): boolean {
|
||||
function unsettledPromises(records: readonly {fulfilled?: boolean; released?: boolean}[] | undefined): boolean {
|
||||
return !!records?.some(record => !record.fulfilled && !record.released);
|
||||
}
|
||||
/** Group negotiation is deliberately unexposed until its other alliance
|
||||
* mechanics are integrated. This narrow profile keeps existing pair games
|
||||
* and unrelated commitments on their original paths. */
|
||||
/** The incomplete group preview and staged fixtures retain this narrow profile;
|
||||
* existing pair games and unrelated commitments keep their original paths. */
|
||||
function coalitionNexusAvailable(g: Game): boolean {
|
||||
return g.coalitionState !== undefined && g.status === 'playing' &&
|
||||
g.phase === 1 && !!g.nexus && !g.spiceWindow && !g.spiceResolution &&
|
||||
@@ -44695,6 +44706,18 @@ function applyActionInner(
|
||||
log(g, `${p.name} ${action.enabled ? 'enabled' : 'disabled'} the provisional two-person, four-faction setup. Human readiness was cleared.`);
|
||||
return g;
|
||||
}
|
||||
if (t === 'coalitionPreview') {
|
||||
requireRule(id === g.host && Object.keys(action).every(key => key === 'type' || key === 'variant') &&
|
||||
(action.variant === 'off' || action.variant === 'three' || action.variant === 'three-short' || action.variant === 'unlimited'),
|
||||
'Only the host may choose a group-alliance preview option.');
|
||||
const variant = action.variant === 'off' ? undefined : action.variant;
|
||||
if (variant === g.coalitionPreview) return g;
|
||||
if (variant) g.coalitionPreview = variant;
|
||||
else delete g.coalitionPreview;
|
||||
g.players.forEach(player => (player.ready = !!player.bot));
|
||||
log(g, `${p.name} changed the unfinished group-alliance preview. Human readiness was cleared.`);
|
||||
return g;
|
||||
}
|
||||
if (t === 'shortAllianceVictory') {
|
||||
requireRule(id === g.host && typeof action.enabled === 'boolean' &&
|
||||
Object.keys(action).every(key => key === 'type' || key === 'enabled'),
|
||||
@@ -47056,6 +47079,7 @@ export function viewGame(state: Game, id: string, now?: number) {
|
||||
factionSwap: g.factionSwap ?? null,
|
||||
twoPlayerControllers: g.twoPlayerControllers ?? null,
|
||||
shortAllianceVictory: g.shortAllianceVictory === true,
|
||||
coalitionPreview: g.coalitionPreview ?? 'off',
|
||||
controlledFactionSeats: g.twoPlayerControllers
|
||||
? g.players.filter(player => g.twoPlayerControllers!.controllers.some(controller =>
|
||||
controller.seats.includes(id) && controller.seats.includes(player.id)))
|
||||
|
||||
Reference in new issue
Block a user