feat: add getEffectiveStat helper for computed stat values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-07 14:17:49 +00:00
parent 9c6834c05f
commit aebfad07ce
2 changed files with 97 additions and 0 deletions
@@ -0,0 +1,82 @@
import { describe, it, expect } from 'vitest';
import { World } from '../../ecs/World.js';
import { getEffectiveStat } from '../statHelpers.js';
import type { Stats, StatModifiers } from '@dflike/shared';
function addStats(world: World, entity: number, overrides: Partial<Stats> = {}): void {
const base: Stats = {
strength: 10, dexterity: 10, constitution: 10, intelligence: 10, perception: 10,
sociability: 10, courage: 10, curiosity: 10, empathy: 10, temperament: 10,
...overrides,
};
world.addComponent<Stats>(entity, 'stats', base);
}
describe('getEffectiveStat', () => {
it('returns base stat when no modifiers', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { strength: 14 });
expect(getEffectiveStat(world, e, 'strength')).toBe(14);
});
it('adds positive modifier', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { empathy: 12 });
world.addComponent<StatModifiers>(e, 'statModifiers', {
modifiers: [{ stat: 'empathy', value: 2, remaining: 50 }],
});
expect(getEffectiveStat(world, e, 'empathy')).toBe(14);
});
it('adds negative modifier', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { intelligence: 10 });
world.addComponent<StatModifiers>(e, 'statModifiers', {
modifiers: [{ stat: 'intelligence', value: -2, remaining: 50 }],
});
expect(getEffectiveStat(world, e, 'intelligence')).toBe(8);
});
it('clamps to minimum 3', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { dexterity: 4 });
world.addComponent<StatModifiers>(e, 'statModifiers', {
modifiers: [{ stat: 'dexterity', value: -5, remaining: 50 }],
});
expect(getEffectiveStat(world, e, 'dexterity')).toBe(3);
});
it('clamps to maximum 18', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { strength: 17 });
world.addComponent<StatModifiers>(e, 'statModifiers', {
modifiers: [{ stat: 'strength', value: 5, remaining: 50 }],
});
expect(getEffectiveStat(world, e, 'strength')).toBe(18);
});
it('sums multiple modifiers for same stat', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { sociability: 10 });
world.addComponent<StatModifiers>(e, 'statModifiers', {
modifiers: [
{ stat: 'sociability', value: 2, remaining: 50 },
{ stat: 'sociability', value: -1, remaining: 30 },
],
});
expect(getEffectiveStat(world, e, 'sociability')).toBe(11);
});
it('floors float base stats to integer', () => {
const world = new World();
const e = world.createEntity();
addStats(world, e, { courage: 10.7 });
expect(getEffectiveStat(world, e, 'courage')).toBe(10);
});
});
+15
View File
@@ -0,0 +1,15 @@
import type { Stats, StatName, StatModifiers } from '@dflike/shared';
import type { World } from '../ecs/World.js';
export function getEffectiveStat(world: World, entity: number, stat: StatName): number {
const base = world.getComponent<Stats>(entity, 'stats');
if (!base) return 10; // fallback for entities without stats
let value = base[stat];
const mods = world.getComponent<StatModifiers>(entity, 'statModifiers');
if (mods) {
for (const m of mods.modifiers) {
if (m.stat === stat) value += m.value;
}
}
return Math.max(3, Math.min(18, Math.floor(value)));
}