Allow faction swaps with AI seats

This commit is contained in:
Quintine committed 2026-10-28 00:25:38 +11:00
1 parent c6b4a165bd
commit 910e1f9b40
11 files changed
+188 -28

No files matched your search

+15 -4
View File
@@ -42996,11 +42996,22 @@ function applyActionInner(
}
if (t === 'factionSwapOffer') {
requireRule(Object.keys(action).every(key => ['type', 'target'].includes(key)) &&
typeof action.target === 'string' && !p.bot, 'Choose another human seat for the faction swap.');
typeof action.target === 'string' && !p.bot, 'Choose another seat for the faction swap.');
const target = g.players.find(player => player.id === action.target);
requireRule(target && !target.bot, 'Choose another human seat for the faction swap.');
g.factionSwap = factionSwapRule(() => createFactionSwap(g.players, id, target.id, crypto.randomUUID()));
log(g, `${p.name} offered to exchange factions with ${target.name}.`);
requireRule(target, 'Choose another seat for the faction swap.');
const offer = factionSwapRule(() => createFactionSwap(g.players, id, target.id, crypto.randomUUID()));
if (target.bot) {
// Lobby bots do not run asynchronous decision workers. Their provisional
// policy accepts a human's offer in the same authoritative action.
const result = factionSwapRule(() => answerFactionSwap(g.players, offer, target.id, true));
const oldName = target.name;
for (const assignment of result.assignments) assignLobbyFaction(getPlayer(g, assignment.player), assignment.faction);
g.players.forEach(player => (player.ready = !!player.bot));
log(g, `${p.name} exchanged factions with ${oldName}, which accepted automatically. Human readiness was cleared.`);
} else {
g.factionSwap = offer;
log(g, `${p.name} offered to exchange factions with ${target.name}.`);
}
return g;
}
if (t === 'factionAllocation') {
+2 -2
View File
@@ -2446,7 +2446,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'Create a room, invite rivals with its code, and complete each faction’s starting choices.',
steps: [
'Each player chooses a distinct faction and an unoccupied player circle, then marks Ready. The six circles appear around the map and determine storm order. Changing your circle resets readiness. The host starts when everyone is ready.',
'The host may instead deal random enabled factions or draw distinct prediction numbers for a faction draft. The highest number picks first; choices apply together after all seats pick. AI seats choose automatically and human readiness clears. Human seats may offer to swap their factions; the recipient must accept, and human readiness then clears. Other lobby changes wait for the offer to be accepted, declined or canceled. AI seats cannot swap yet.',
'The host may instead deal random enabled factions or draw distinct prediction numbers for a faction draft. The highest number picks first; choices apply together after all seats pick. AI seats choose automatically and human readiness clears. Human seats may offer a faction swap; a human recipient must accept, while an AI currently accepts immediately. Human readiness then clears. Other lobby changes wait for a pending human offer to be accepted, declined or canceled.',
'After positions are fixed, Bene Gesserit seals a secret prediction of a faction and turn. No traitor or Treachery Cards have been dealt yet. Other players wait for this choice; if Bene Gesserit is absent, the table continues automatically.',
'Each player receives four private traitor choices and keeps one. Harkonnen keeps all four automatically. Every remaining selection must finish before starting spice and forces are placed. Your starting Treachery hand is still empty.',
'Starting spice and fixed forces are then placed. Fremen distributes ten forces among Sietch Tabr, False Wall South and False Wall West, choosing their printed sectors. Basic Bene Gesserit starts with one fighter in Polar Sink. In Advanced setup, Bene Gesserit instead chooses a printed territory and sector after Fremen placement; its advisor becomes a fighter when alone.',
@@ -2460,7 +2460,7 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Implementation',
status: 'Partial',
detail:
'Lobby random faction dealing, prediction-number drafting and human-to-human consensual swaps retain seat identities and clear readiness on assignment; AI swaps remain unfinished. Base setup enforces prediction, traitors, forces and automatic dealing in order, with private choices and preserved saved tables. Advanced starting advisors and elite forces share the same setup logic. The normal lobby starts selected expansions and optional modules through existing genuine setups as labelled previews; unsupported combinations still reject at start.',
'Lobby random faction dealing, prediction-number drafting and human-initiated faction swaps retain seat identities and clear readiness on assignment. AI targets automatically accept under a provisional policy; AI-initiated offers remain unfinished. Base setup enforces prediction, traitors, forces and automatic dealing in order, with private choices and preserved saved tables. Advanced starting advisors and elite forces share the same setup logic. The normal lobby starts selected expansions and optional modules through existing genuine setups as labelled previews; unsupported combinations still reject at start.',
},
{
area: 'Player controls',