feat: CoC 7e d100 rule-pack

This commit is contained in:
2026-07-20 16:18:27 +01:00
parent 4ebf8d3994
commit 605b107c77
3 changed files with 56 additions and 1 deletions
+22
View File
@@ -0,0 +1,22 @@
// Call of Cthulhu 7e success ladder vs a skill %.
export const cocD100 = {
id: 'coc-d100',
label: 'd100 (Call of Cthulhu)',
params: [{ key: 'target', label: 'Skill %', default: 50 }],
interpret(results, params = {}) {
const value = results[0]?.value ?? 0;
const target = params.target ?? 50;
let level;
if (value === 1) level = 'critical';
else if (value <= Math.floor(target / 5)) level = 'extreme';
else if (value <= Math.floor(target / 2)) level = 'hard';
else if (value <= target) level = 'success';
else if ((target < 50 && value >= 96) || value === 100) level = 'fumble';
else level = 'failure';
const success = ['critical', 'extreme', 'hard', 'success'].includes(level);
return { value, target, level, success };
},
summary(v) {
return `${v.value} vs ${v.target}% → ${v.level.toUpperCase()}`;
},
};
+2 -1
View File
@@ -1,6 +1,7 @@
import { yearZero } from './year-zero.js';
import { cocD100 } from './coc-d100.js';
const packs = new Map([[yearZero.id, yearZero]]);
const packs = new Map([[yearZero.id, yearZero], [cocD100.id, cocD100]]);
export function getRulePack(id) { return packs.get(id) || null; }
export function registerRulePack(pack) { packs.set(pack.id, pack); }
+32
View File
@@ -0,0 +1,32 @@
import { describe, it, expect } from 'vitest';
import { cocD100 } from '../../src/dice/rulepacks/coc-d100.js';
import { getRulePack } from '../../src/dice/rulepacks/index.js';
const roll = (v) => [{ sides: 100, value: v }];
describe('coc-d100 rule-pack', () => {
it('grades a roll against a 50% skill', () => {
expect(cocD100.interpret(roll(1), { target: 50 }).level).toBe('critical');
expect(cocD100.interpret(roll(10), { target: 50 }).level).toBe('extreme'); // <= 10
expect(cocD100.interpret(roll(25), { target: 50 }).level).toBe('hard'); // <= 25
expect(cocD100.interpret(roll(50), { target: 50 }).level).toBe('success');
expect(cocD100.interpret(roll(51), { target: 50 }).level).toBe('failure');
expect(cocD100.interpret(roll(100), { target: 50 }).level).toBe('fumble');
});
it('fumbles on 96+ when the skill is under 50', () => {
expect(cocD100.interpret(roll(96), { target: 40 }).level).toBe('fumble');
expect(cocD100.interpret(roll(41), { target: 40 }).level).toBe('failure');
});
it('marks the four passing bands as success', () => {
expect(cocD100.interpret(roll(10), { target: 50 }).success).toBe(true);
expect(cocD100.interpret(roll(51), { target: 50 }).success).toBe(false);
});
it('defaults target to 50 and summarises', () => {
const v = cocD100.interpret(roll(5));
expect(v.target).toBe(50);
expect(cocD100.summary(v)).toBe('5 vs 50% → EXTREME');
});
it('is registered', () => {
expect(getRulePack('coc-d100')).toBe(cocD100);
});
});