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.
39 lines
2.0 KiB
TypeScript
39 lines
2.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { casualtyOptions, maxCombatDial, maxCombatSupport, type CombatForces } from '../game/combat';
|
|
import { reinforcementsBattleForces } from '../game/reinforcements-homeworld';
|
|
|
|
function original(normal = 7, freeStars = true): CombatForces {
|
|
return {normal,elite:0,temporaryElite:5,eliteStrength:2,freeSupport:false,eliteFreeSupport:freeStars};
|
|
}
|
|
|
|
void test('own-World fee spends unconverted normals first, then removes only paid temporary roles', () => {
|
|
const before = original();
|
|
assert.deepEqual([1,2,3].map(normal => {
|
|
const remaining = reinforcementsBattleForces(before,{normal,elite:0});
|
|
return [remaining.normal,remaining.temporaryElite,maxCombatDial(remaining)];
|
|
}), [[6,5,11],[5,5,10],[4,4,8]]);
|
|
const allConverted = reinforcementsBattleForces(original(5),{normal:3,elite:0});
|
|
assert.deepEqual([allConverted.normal,allConverted.temporaryElite,maxCombatDial(allConverted)],[2,2,4]);
|
|
assert.deepEqual(before,original(),'The declared five-counter group is unchanged.');
|
|
});
|
|
|
|
void test('reduced temporary force roles retain pre-fee support traits and ordinary physical casualties', () => {
|
|
for (const freeStars of [false,true]) {
|
|
const remaining = reinforcementsBattleForces(original(7,freeStars),{normal:3,elite:0});
|
|
assert.equal(remaining.eliteFreeSupport,freeStars);
|
|
assert.equal(maxCombatSupport(remaining),freeStars ? 0 : 4);
|
|
const losses = casualtyOptions(remaining,4,freeStars ? 0 : 2);
|
|
assert.ok(losses.length);
|
|
assert.ok(losses.every(loss => loss.normal === 2 && loss.elite === 0));
|
|
}
|
|
});
|
|
|
|
void test('off-World and zero allocations keep the original five roles; impossible local fees reject', () => {
|
|
const before = original();
|
|
assert.equal(reinforcementsBattleForces(before),before);
|
|
assert.equal(reinforcementsBattleForces(before,{normal:0,elite:0}),before);
|
|
assert.throws(()=>reinforcementsBattleForces(before,{normal:8,elite:0}));
|
|
assert.throws(()=>reinforcementsBattleForces(before,{normal:0,elite:1}));
|
|
});
|