Compose classic independent cards with World Nexus rules
Retain independent36, original Homeworlds and Nexus12 through classic fresh setup with optional Skills and phase modules. Resolve own-World Reinforcements fees before computing temporary Sardaukar roles, preserving original Cunning declarations, printed traits and physical counters. Record the provisional ordinary-nonrole-first policy and align human/AI quotes and captions. Validation: types/lint/build, 128 affected tests across 15 files, bounded rule-source review and finite original-action smoke for Recruits, Harass, local-fee Cunning, all four legal policies and rendered source controls. Verified runtime c219ec587a54b44079539168d93835a5af904c8afb4de58bcec0a3d60c809f0c; documentation-only proof update 8ee21598e7be0c89a08fbad50786c346ebc8ae705492d8b31ea00fae0ffefa3c. Full modes, combinations, assurance and deployed acceptance remain open. No saved-room/provider changes.
This commit is contained in:
1 parent
178251ad35
commit
97c2b67650
18 files changed
+737
-30
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import type { CombatForces } from './combat';
|
||||
import { validCombatForces, type CombatForces } from './combat';
|
||||
import {
|
||||
homeworldForceGroups, type HomeworldCustody, type HomeworldCustodyContext,
|
||||
type HomeworldForces,
|
||||
@@ -63,14 +63,18 @@ export function quoteReinforcementsHomeworldCost(context: HomeworldCustodyContex
|
||||
|
||||
/** On an own World the reserve fee and combat army share physical counters.
|
||||
* Reserve the fee first, but retain the original sealed support/strength traits.
|
||||
* Provisionally spend ordinary counters without a temporary role first, then
|
||||
* remove that role only from converted counters actually used to pay the fee.
|
||||
* Foreign-World and Arrakis callers pass no on-location cost. */
|
||||
export function reinforcementsBattleForces(forces: CombatForces,
|
||||
onLocationCost?: HomeworldForces): CombatForces {
|
||||
if (!onLocationCost) return forces;
|
||||
const { normal, elite } = onLocationCost;
|
||||
if (!Number.isSafeInteger(normal) || normal < 0 || !Number.isSafeInteger(elite) || elite < 0 ||
|
||||
if (!validCombatForces(forces) || !Number.isSafeInteger(normal) || normal < 0 || !Number.isSafeInteger(elite) || elite < 0 ||
|
||||
normal > forces.normal || elite > forces.elite)
|
||||
throw new ReinforcementsError('The reserve fee cannot also fund the physical Homeworld dial.');
|
||||
if (normal + elite === 0) return forces;
|
||||
return { ...forces, normal: forces.normal - normal, elite: forces.elite - elite };
|
||||
const remainingNormal = forces.normal - normal;
|
||||
return { ...forces, normal: remainingNormal, elite: forces.elite - elite,
|
||||
...(forces.temporaryElite === undefined ? {} : {temporaryElite: Math.min(forces.temporaryElite, remainingNormal)}) };
|
||||
}
|
||||
Reference in new issue
Block a user