Compose native Ecaz and Moritani Stronghold rule paths
This commit is contained in:
1 parent
50808aed5e
commit
39e2954ab4
15 files changed
+1010
-91
No files matched your search
+34
-12
@@ -7510,11 +7510,28 @@ function leaderSkillAssignmentUnavailable(
|
||||
? ADVANCED_ATREIDES_SUK_ASSIGNMENT_BLOCK
|
||||
: null;
|
||||
}
|
||||
/** Public standalone E3 roster; the existing held-card owner remains the plan actor. */
|
||||
function e3StrongholdFactionProfile(g: Game): boolean {
|
||||
if (!g.advanced || g.expansions.length !== 1 || g.expansions[0] !== 'ecaz' ||
|
||||
g.players.length < 2 || g.players.length > 6 ||
|
||||
g.homeworlds || g.nexusCards || g.leaderSkills || g.techTokens ||
|
||||
g.discoveryEnabled || g.discoveries || g.discoveryStash || g.greatMaker ||
|
||||
g.ecazTreachery) return false;
|
||||
let native: 'ecaz' | 'moritani' | null = null;
|
||||
for (const player of g.players) {
|
||||
if (player.faction === 'ecaz' || player.faction === 'moritani') {
|
||||
if (native !== null) return false;
|
||||
native = player.faction;
|
||||
} else if (!CLASSIC_FACTIONS[player.faction]) return false;
|
||||
}
|
||||
return native !== null && (native !== 'moritani' || !byFaction(g, 'harkonnen'));
|
||||
}
|
||||
function moritaniAssassinateModeSupported(g: Game) {
|
||||
return g.advanced && g.expansions.length === 1 && g.expansions[0] === 'ecaz' &&
|
||||
g.players.some(p => p.faction === 'moritani') &&
|
||||
g.players.every(p => ['moritani','atreides','beneGesserit','guild','emperor','fremen'].includes(p.faction)) &&
|
||||
!g.nexusCards && (!g.leaderSkills || advancedMoritaniLeaderSkillsProfile(g)) && !g.strongholdCards && !g.homeworlds &&
|
||||
!g.nexusCards && (!g.leaderSkills || advancedMoritaniLeaderSkillsProfile(g)) &&
|
||||
(!g.strongholdCards || e3StrongholdFactionProfile(g)) && !g.homeworlds &&
|
||||
!g.discoveryEnabled && !g.discoveries && !g.techTokens;
|
||||
}
|
||||
function moritaniAssassinateContext(g: Game, receipt: MoritaniAssassinateReceipt) {
|
||||
@@ -9243,7 +9260,8 @@ function ecazOccupyCompositionSupported(g: Game): boolean {
|
||||
g.players.every(p => faction(p.faction).expansion === 'base' ||
|
||||
(['ecaz', 'moritani', 'ixians', 'tleilaxu', 'choam', 'richese'].includes(p.faction) &&
|
||||
g.expansions.includes(faction(p.faction).expansion))) &&
|
||||
!g.homeworlds && !g.nexusCards && !g.leaderSkills && !g.strongholdCards &&
|
||||
!g.homeworlds && !g.nexusCards && !g.leaderSkills &&
|
||||
(!g.strongholdCards || e3StrongholdFactionProfile(g)) &&
|
||||
!g.techTokens && !g.discoveryEnabled && !g.discoveries &&
|
||||
!g.discoveryStash && !g.greatMaker &&
|
||||
(g.ecazTreachery === undefined || g.ecazTreachery === true);
|
||||
@@ -9258,15 +9276,16 @@ export function initializeEcazOccupyGameForAudit(state: Game): Game {
|
||||
g.ecazOccupyPreview = true;
|
||||
return g;
|
||||
}
|
||||
/** Fresh Advanced E1/E2 faction composition; normal starts and other modules stay gated. */
|
||||
/** Fresh native Stronghold composition; other overlays and public starts stay gated. */
|
||||
export function initializeStrongholdFactionsGameForAudit(state: Game): Game {
|
||||
requireRule(state.advanced === true && Array.isArray(state.expansions) &&
|
||||
state.expansions.length >= 1 && state.expansions.length <= 2 &&
|
||||
state.expansions.every(id => id === 'ix' || id === 'choam') &&
|
||||
new Set(state.expansions).size === state.expansions.length &&
|
||||
state.players.some(p => faction(p.faction).expansion !== 'base') &&
|
||||
state.players.every(p => faction(p.faction).expansion === 'base' || state.expansions.includes(faction(p.faction).expansion)),
|
||||
'Stronghold factions require Advanced Ixian/Tleilaxu or CHOAM/Richese factions, classic opponents and their selected distinct decks.');
|
||||
requireRule(e3StrongholdFactionProfile(state) ||
|
||||
(state.advanced === true && Array.isArray(state.expansions) &&
|
||||
state.expansions.length >= 1 && state.expansions.length <= 2 &&
|
||||
state.expansions.every(id => id === 'ix' || id === 'choam') &&
|
||||
new Set(state.expansions).size === state.expansions.length &&
|
||||
state.players.some(p => faction(p.faction).expansion !== 'base') &&
|
||||
state.players.every(p => faction(p.faction).expansion === 'base' || state.expansions.includes(faction(p.faction).expansion))),
|
||||
'Stronghold factions require supported Advanced native factions, classic opponents and their selected distinct decks.');
|
||||
requireRule(!state.homeworlds && !state.nexusCards && !state.leaderSkills &&
|
||||
!state.discoveryEnabled && !state.discoveries && !state.discoveryStash && !state.greatMaker &&
|
||||
!state.techTokens && !state.ecazTreachery &&
|
||||
@@ -9274,7 +9293,10 @@ export function initializeStrongholdFactionsGameForAudit(state: Game): Game {
|
||||
'Stronghold factions admit only unused Stronghold Cards, without other optional modules.');
|
||||
requireFreshFactionInventory(state);
|
||||
requireFreshBaseRuntime(state);
|
||||
return initializeSetupGameForAudit(state, false, false, false, false, false, false, true, false, true);
|
||||
const initialized = initializeSetupGameForAudit(state, false, false, false, false, false, false, true, false, true);
|
||||
if (e3StrongholdFactionProfile(initialized) && byFaction(initialized, 'moritani'))
|
||||
initializeMoritaniAssassinateState(initialized);
|
||||
return initialized;
|
||||
}
|
||||
function requireFreshFactionInventory(state: Game) {
|
||||
requireRule(state.richeseCache === undefined && state.richeseRemoved === undefined &&
|
||||
@@ -9415,7 +9437,7 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
|
||||
),
|
||||
),
|
||||
strongholdFactions
|
||||
? 'The Stronghold faction audit needs a fresh Advanced Ixian/CHOAM lobby without other optional modules.'
|
||||
? 'The Stronghold faction audit needs a fresh supported Advanced native lobby without other optional modules.'
|
||||
: factions && nexus
|
||||
? 'The paired expansion Nexus audit needs a fresh lobby without other optional modules.'
|
||||
: factions
|
||||
|
||||
+7
-5
@@ -3246,23 +3246,25 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
category: 'Advanced & expansions',
|
||||
coverage: 'Partial',
|
||||
developmentStage: 'Verified',
|
||||
summary: 'Fresh Advanced Stronghold play connects native Ixian/Tleilaxu and CHOAM/Richese paired and mixed families through existing rule handlers.',
|
||||
summary: 'Fresh Advanced Stronghold play connects native E1/E2 families and standalone Ecaz or Moritani through original holder-only rule handlers.',
|
||||
steps: [
|
||||
'This private local profile admits selected native Ixian/Tleilaxu and CHOAM/Richese families with classic opponents in unique two-to-six-seat Advanced rosters. At least one expansion faction and its distinct selected ix/choam decks are required, with Stronghold Cards alone. Basic, Ecaz, Moritani and other overlays remain outside it.',
|
||||
'This private local profile admits selected native Ixian/Tleilaxu and CHOAM/Richese families with classic opponents in unique two-to-six-seat Advanced rosters. Standalone Ecaz OR Moritani with classic opponents additionally uses the exact ecaz deck. Moritani excludes Harkonnen under its original assassination/Duke boundary. An E3 pair, E1/E2/E3 mixtures, Basic and other overlays remain separate; only Stronghold Cards are enabled.',
|
||||
'CHOAM-only ordinary Treachery has 35 physical cards; Ix or both decks have47, not49, because CHOAM replaces the shared Ix cards. Native Richese’s ten-card cache remains separate. Genuine setup retains original faction offers/leaders/forces and any native Auditor/HMS, with all six Stronghold Cards initially unowned.',
|
||||
'Card ownership begins only at the real first end of Mentat and is retained through the following turn. It is not assigned by a battle fixture or reset on refresh. Hidden Mobile Stronghold interior forces and its outside pointing territory stay distinct.',
|
||||
'The mobile card copies an advantage of another currently controlled stronghold, not merely a retained card. A sole choice is automatic; multiple choices use the existing public pre-plan selection. Accepted choices stay bound to the same original battle through private plans and saved continuation.',
|
||||
'Arrakeen provides at most two spice toward actual support, not extra personal cash. Native CHOAM income uses the original bank and player payment shares, including its established traitor and own-donor exclusions. Original cyborg/suboid and starred casualties, bounty and other native income stay separate.',
|
||||
'Carthag, Habbanya, Sietch Tabr and Tuek’s Sietch retain their existing defense, tie, opposing-dial and played-Worthless contracts. New Richese coverage uses actual No-Field shipment/reveal and native cache acquisition for Stone Burner; mixed No-Field dials/full-plan and unresolved Stone/Poison Tooth cases are not silently enabled.',
|
||||
'Shared Ecaz control holds the card at Ecaz; the selected lead gets only its own held advantage. Ecaz-led Arrakeen may bank-fund actual ally-variable support up to two, never fixed free Ecaz strength. Choosing the ally gives no automatically shared card benefit. Original Habbanya tie and Tabr income likewise follow the actual holder-plan.',
|
||||
'Native Moritani losing Tuek holder still earns two bank spice per played Worthless card. Original post-loss assassination follows support/card income, retains only printed bounty and one actual private Mentat replacement. Winner cleanup and force losses finish once; normal Traitor revelation forfeits assassination for the game. Native Terror placement remains free.',
|
||||
'Bounded expanded-family evidence includes sixteen rule cases, eleven actual No-Field/Stone/FaceDance program cases, all four legal policies and eighteen unstaged complete paired/mixed2..6 games (11066 accepted actions, no rejections). Human390px Stone kill/Habbanya and two-reserve Face Dance controls complete their native decisions. Save/recovery/privacy/custody assurance follows all rules, not a new prototype gate. Full-card/faction/module/deployed acceptance and public expansion starts remain open.',
|
||||
],
|
||||
related: ['stronghold-cards', 'mobile-stronghold', 'choam-combat', 'setup', 'implementation-checklist'],
|
||||
checklist: [
|
||||
{ area: 'Implementation', status: 'Implemented', detail: 'Fresh selected E1/E2 setup, 35/47 ordinary decks plus separate native Richese cache, actual end-Mentat ownership and native HMS/No-Field/Stone/FaceDance/CHOAM economy compose existing effects without public-start or old-game changes.', evidence: ['game/engine.ts', 'game/stronghold-cards.ts', 'game/battle-resolution-quote.ts'] },
|
||||
{ area: 'Player controls', status: 'Implemented', detail: 'Original local390px controls cover HMS copy/support, real cache Stone reveal mode and partial Face Dance physical-source replacement. Native rules retain original winner benefits and turn-end controller changes.', evidence: ['components/stronghold-cards.tsx', 'components/game-table.tsx'] },
|
||||
{ area: 'Implementation', status: 'Implemented', detail: 'Fresh selected E1/E2 setup and standalone Ecaz/Moritani exact ecaz deck, actual end-Mentat ownership, holder-only Ecaz coalition funding and original Moritani loss/assassination compose existing effects without public-start or old-game changes.', evidence: ['game/engine.ts', 'game/stronghold-cards.ts', 'game/battle-resolution-quote.ts'] },
|
||||
{ area: 'Player controls', status: 'Implemented', detail: 'Original local390px controls cover HMS copy/support, real cache Stone reveal mode and partial Face Dance replacement. Standalone E3 human controls select the Ecaz holder lead, show bank2/personal1 for actual support3, seal native plans and keep the physical Shield; Moritani reveals printed Master Bewt bounty through its original inspector and choice. Original Collection and Mentat continue after refresh.', evidence: ['components/stronghold-cards.tsx', 'components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'Four minimal legal policies consume actual marker/Stone/FaceDance/copy choices; eighteen genuine paired/mixed Advanced2..6 games complete. Strategy/calibration and other combinations remain deferred.', evidence: ['game/bots.ts', 'tools/faction-games.ts'] },
|
||||
{ area: 'Documentation', status: 'Partial', detail: 'Advanced family/deck/cache admission, existing six effects, original winner versus replacement and explicit guarded combinations are recorded without completing every card or faction.', evidence: ['docs/STRONGHOLD_CARDS.md', 'docs/RULE_DECISIONS.md'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Targeted16 rule cases, native11-case program, eighteen complete games and390px human decisions cover expanded rule paths; prior Ixian/CHOAM proofs stay dated. No new save/privacy/custody or full-suite campaign. Final assurance remains after all rules.', evidence: ['tests/richese-strongholds-runtime.test.ts', 'tests/tleilaxu-strongholds-runtime.test.ts', 'tests/fixture-richese-strongholds.ts', 'tests/fixture-tleilaxu-strongholds.ts'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Prior E1/E2 evidence remains dated. Standalone E3 adds20 focused cases, affected native/CLI179, types/lint/build and actual controlled holder-only funding, tie/income, assassination/death/forfeiture and four legal policies. Four genuine two/three-seat E3 games complete642 accepted actions,15 JSON continuations and3 battles without rejection; no natural assassination claim. Human390px Ecaz reaches v18/Mentat with bank2/personal1, separate Collection2 and physical Ecaz2/Guild1 survivors; Moritani reaches v11/Mentat with wallet18 and one original private replacement. No new assurance/full-suite campaign or public/deployed certification.', evidence: ['tests/ecaz-stronghold-runtime.test.ts', 'tests/moritani-stronghold-runtime.test.ts', 'tests/richese-strongholds-runtime.test.ts', 'tests/tleilaxu-strongholds-runtime.test.ts'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user