feat: support extra health state mechanics
Validate and deploy production / validate (push) Successful in 21s
Validate and deploy staging / validate (push) Successful in 23s
Validate and deploy production / deploy (push) Successful in 44s
Validate and deploy staging / deploy (push) Successful in 30s

This commit is contained in:
jb
2026-07-31 23:55:49 -04:00
parent 9ac2fe47cf
commit 1998e87309
16 changed files with 575 additions and 47 deletions
+47
View File
@@ -164,6 +164,53 @@ describe('loadout mechanic timeline', () => {
expect(sprinting.every((event) => event.values?.state === 'sprinting')).toBe(true);
});
it('seeds Extra Health state before periodic stack evaluation', () => {
const profile = createDraft().combatProfile;
profile.encounterSeconds = 2;
profile.extraHealthActive = true;
const timeline = buildWeaponMechanicTimeline(
{ rpm: 60, magazine: 100, reload_speed: 0 },
profile
);
expect(timeline.filter((event) => event.kind === 'extra_health_changed')).toEqual([{
kind: 'extra_health_changed',
at: 0,
actorId: 'agent',
values: { hasExtraHealth: true }
}]);
const program: MechanicProgram = {
id: 'extra-health-periodic-stacks',
name: 'Extra Health periodic stacks',
pools: [
{ key: 'active', label: 'Active', scope: 'agent', initial: 0, min: 0, max: 1 },
{ key: 'stacks', label: 'Stacks', scope: 'agent', initial: 0, min: 0, max: 10 }
],
clauses: [{
id: 'set-active',
when: 'extra_health_changed',
if: { kind: 'compare', left: { source: 'event', path: 'values.hasExtraHealth' }, operator: 'eq', right: true },
reducer: { kind: 'none' },
mutations: [{ kind: 'set', pool: 'active', value: 1 }]
}, {
id: 'gain-stack',
when: 'tick',
if: { kind: 'compare', left: { source: 'pool', pool: 'active' }, operator: 'eq', right: 1 },
reducer: { kind: 'periodic', intervalSeconds: 1 },
mutations: [{ kind: 'add', pool: 'stacks', value: 1 }]
}]
};
const result = evaluateMechanicProgram(program, timeline, {
mode: 'custom',
encounterSeconds: 2
});
expect(result.finalState).toEqual(expect.arrayContaining([
expect.objectContaining({ pool: 'stacks', value: 2 })
]));
expect(result.emissions.filter((emission) => emission.clauseId === 'gain-stack')
.map((emission) => emission.at)).toEqual([1, 2]);
});
it('falls back to a documented Tier I mechanic when equipment Tier II is absent', () => {
const persistedRule = {
rule: {