Support group Harkonnen traitor calls
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
83ccd71821
commit
478b38f5de
10 files changed
+343
-13
No files matched your search
+2
-2
@@ -4125,9 +4125,9 @@ function policyActions(g: GameView): Action[] {
|
||||
if (b.revealed) {
|
||||
const ownsTraitorDecision =
|
||||
b.traitorVoters.includes(me.id) && !b.traitorSubmitted.includes(me.id);
|
||||
const beneficiary = [b.attacker, b.defender].includes(me.id)
|
||||
const beneficiary = b.traitorBeneficiary ?? ([b.attacker, b.defender].includes(me.id)
|
||||
? me.id
|
||||
: me.ally;
|
||||
: me.ally);
|
||||
const enemy = b.attacker === beneficiary ? b.defender : b.attacker;
|
||||
const preferredCall =
|
||||
ownsTraitorDecision &&
|
||||
|
||||
+10
-4
@@ -3939,8 +3939,12 @@ function nexusTraitorParentSignature(g: Game, event: string) {
|
||||
function traitorDeclarationContext(g: Game): TraitorDeclarationContext {
|
||||
const b = g.battle!;
|
||||
return { event: b.event!, attacker: b.attacker, defender: b.defender, plans: b.plans,
|
||||
status: g.status, advanced: g.advanced, coalitionState: g.coalitionState, expansions: g.expansions,
|
||||
homeworlds: g.homeworlds, nexusCards: g.nexusCards, leaderSkills: g.leaderSkills,
|
||||
techTokens: g.techTokens, strongholdCards: g.strongholdCards, ecazTreachery: g.ecazTreachery,
|
||||
discoveryEnabled: g.discoveryEnabled,
|
||||
...(b.ecazOccupy ? { ecazOccupy: ecazBattleProfile(g)! } : {}),
|
||||
players: g.players.map(({id,faction,ally,traitors}) => ({id,faction,ally,traitors})),
|
||||
players: g.players.map(({id,faction,ally,allySinceTurn,traitors}) => ({id,faction,ally,allySinceTurn,traitors})),
|
||||
heroLeaderIds: physicalTreacheryCards(g).filter(c => c.kind === 'hero').map(c => c.id),
|
||||
};
|
||||
}
|
||||
@@ -32545,9 +32549,10 @@ function traitorVoters(g: Game, b: Battle) {
|
||||
for (const owner of [b.ecazOccupy.ecaz, b.ecazOccupy.ally])
|
||||
if (!voters.includes(owner)) voters.push(owner);
|
||||
const harkonnen = byFaction(g, 'harkonnen');
|
||||
const harkonnenBeneficiary = harkonnen && coalitionBattleBeneficiary(g, harkonnen.id, b.attacker, b.defender);
|
||||
if (
|
||||
harkonnen?.ally &&
|
||||
voters.includes(harkonnen.ally) &&
|
||||
harkonnen && harkonnenBeneficiary &&
|
||||
voters.includes(harkonnenBeneficiary) &&
|
||||
!voters.includes(harkonnen.id)
|
||||
)
|
||||
voters.push(harkonnen.id);
|
||||
@@ -32555,7 +32560,7 @@ function traitorVoters(g: Game, b: Battle) {
|
||||
}
|
||||
function traitorBeneficiary(g: Game, b: Battle, id: string) {
|
||||
if (b.ecazOccupy?.lead && [b.ecazOccupy.ecaz, b.ecazOccupy.ally].includes(id)) return b.ecazOccupy.lead;
|
||||
return [b.attacker, b.defender].includes(id) ? id : getPlayer(g, id).ally;
|
||||
return coalitionBattleBeneficiary(g, id, b.attacker, b.defender);
|
||||
}
|
||||
/** One native eligibility quote for declarations and private Truthtrance answers. */
|
||||
function traitorCallBlock(g: Game, b: Battle, id: string, held = getPlayer(g, id).traitors): string | null {
|
||||
@@ -47453,6 +47458,7 @@ export function viewGame(state: Game, id: string, now?: number) {
|
||||
? { [id]: b.plans[id] }
|
||||
: {},
|
||||
traitorSubmitted: Object.keys(b.traitorCalls),
|
||||
traitorBeneficiary: traitorVoters(g, b).includes(id) ? traitorBeneficiary(g, b, id) : null,
|
||||
traitorCallChoices: (() => { const context = traitorTruthContext(g, id); return context
|
||||
? turnActionTraitorChoices(g, id,
|
||||
traitorDecisionRule(() => traitorDecisionChoices(context, b.traitorDecisionPromises ?? []))) : null; })(),
|
||||
|
||||
+2
-2
@@ -4500,11 +4500,11 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
example:
|
||||
'At the final turn with neither Guild nor Fremen, two players each hold two qualifying strongholds and a third holds one. The two leaders of the count share the win.',
|
||||
checklist: [
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'A host-selectable shorter two-member target shares victory calculation. Unexposed staged Basic groups connect Nexus, selected funding, Emperor and Fremen aid, and native Voice and Prescience; other group benefits remain open.', evidence: ['game/alliance-victory-option.ts', 'game/victory-progress.ts', 'game/victory-quote.ts', 'game/engine.ts', 'game/coalition-battle-preparation.ts', 'game/terminal-cancellation.ts'] },
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'A host-selectable shorter two-member target shares victory calculation. Unexposed staged Basic groups connect Nexus, selected funding, Emperor and Fremen aid, Voice, Prescience and Harkonnen traitor support; other group benefits remain open.', evidence: ['game/alliance-victory-option.ts', 'game/victory-progress.ts', 'game/victory-quote.ts', 'game/engine.ts', 'game/coalition-battle-preparation.ts', 'game/traitor-declarations.ts', 'game/terminal-cancellation.ts'] },
|
||||
{ area: 'Player controls', status: 'Partial', detail: 'The lobby offers the pair option. A staged group Emperor can choose either ally for spice and extra revival; group games cannot yet be started from the lobby.', evidence: ['components/game-table.tsx', 'components/coalition-emperor-aid.tsx', 'tests/short-alliance-victory-controls.test.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'Legal alliance and victory paths use the shared target; staged group Emperor bots can offer a targeted revival. Strategic tuning remains deferred.', evidence: ['game/bots.ts', 'game/victory-progress.ts'] },
|
||||
{ area: 'Documentation', status: 'Partial', detail: 'The printed option and provisional two-controller precedence are recorded.', evidence: ['docs/OPTIONAL_ALLIANCE_VICTORY.md'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Focused pair and staged group evidence includes real Nexus actions, Emperor and Fremen aid and battle preparation; larger alliances and full combinations remain open.', evidence: ['tests/alliance-victory-option.test.ts', 'tests/short-alliance-victory-runtime.test.ts', 'tests/coalition-emperor-aid.test.ts', 'tests/coalition-fremen-revival.test.ts', 'tests/coalition-battle-preparation.test.ts', 'tests/coalition-battle-power-question.test.ts'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Focused pair and staged group evidence includes real Nexus actions, Emperor and Fremen aid, battle preparation and allied traitor votes; larger alliances and full combinations remain open.', evidence: ['tests/alliance-victory-option.test.ts', 'tests/short-alliance-victory-runtime.test.ts', 'tests/coalition-emperor-aid.test.ts', 'tests/coalition-fremen-revival.test.ts', 'tests/coalition-battle-preparation.test.ts', 'tests/coalition-battle-power-question.test.ts', 'tests/coalition-harkonnen-traitor.test.ts', 'tests/coalition-traitor-declarations.test.ts'] },
|
||||
],
|
||||
related: [
|
||||
'mentat',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { FACTIONS, type FactionId } from './catalog';
|
||||
import { CHEAP_HERO_TRAITOR } from './traitors';
|
||||
import { quoteEcazOccupyBattle, type EcazOccupyBattleProfile } from './ecaz-occupy-battle';
|
||||
import { coalitionBattleBeneficiary, type CoalitionBattlePreparationContext } from './coalition-battle-preparation';
|
||||
|
||||
export type TraitorDeclarationContext = {
|
||||
export type TraitorDeclarationContext = Omit<Partial<CoalitionBattlePreparationContext>, 'players'> & {
|
||||
ecazOccupy?: EcazOccupyBattleProfile;
|
||||
event: string;
|
||||
attacker: string;
|
||||
@@ -12,6 +13,7 @@ export type TraitorDeclarationContext = {
|
||||
id: string;
|
||||
faction: FactionId;
|
||||
ally?: string | null;
|
||||
allySinceTurn?: number;
|
||||
traitors: readonly string[];
|
||||
}[];
|
||||
heroLeaderIds: readonly string[];
|
||||
@@ -98,7 +100,9 @@ function declaredElements(context: TraitorDeclarationContext, voter: string) {
|
||||
const combatants = [context.attacker, context.defender];
|
||||
const occupy = context.ecazOccupy;
|
||||
const coSide = !!occupy && [occupy.ecaz, occupy.ally].includes(voter);
|
||||
const beneficiary = coSide ? occupy.lead : combatants.includes(voter) ? voter : owner.ally;
|
||||
const beneficiary = coSide ? occupy.lead : combatants.includes(voter) ? voter :
|
||||
owner.faction === 'harkonnen' ? coalitionBattleBeneficiary(
|
||||
{...context, status: context.status ?? ''}, voter, context.attacker, context.defender) : null;
|
||||
requireDeclaration(
|
||||
beneficiary &&
|
||||
combatants.includes(beneficiary) &&
|
||||
|
||||
Reference in new issue
Block a user