The first 2 weeks is ended.
Some checks failed
CI / validate (push) Failing after 14m50s

This commit is contained in:
2026-08-14 10:48:54 +05:00
commit 1774496cf9
48 changed files with 10825 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
import { describe, expect, it } from 'vitest'
import { abilityModifier, applyMechanicalEvents, bindMechanicalEventsToRolls, resolveCheck, shouldCloseRound, type RandomSource } from './index'
import type { Character } from '@dng/shared'
const fixed: RandomSource = { integer: () => 12 }
const hero: Character = {
id: 'hero', name: 'Rook', concept: 'Relic runner', controller: 'human', userId: 'user',
abilities: { str: 14, dex: 16, con: 12, int: 10, wis: 8, cha: 13 },
hp: 10, maxHp: 12, defense: 14, proficiency: 2, inventory: [], statuses: [],
}
describe('game engine', () => {
it('calculates ability modifiers', () => {
expect(abilityModifier(8)).toBe(-1)
expect(abilityModifier(16)).toBe(3)
})
it('keeps dice server-owned and auditable', () => {
const roll = resolveCheck({ actorId: 'hero', kind: 'ability', ability: 'dex', difficulty: 15, mode: 'normal', reason: 'Leap' }, hero, fixed)
expect(roll.rolls).toEqual([12])
expect(roll.total).toBe(17)
expect(roll.success).toBe(true)
})
it('labels disadvantage rolls correctly', () => {
const values = [18, 4]
const roll = resolveCheck(
{ actorId: 'hero', kind: 'ability', ability: 'dex', difficulty: 10, mode: 'disadvantage', reason: 'Sneak' },
hero,
{ integer: () => values.shift()! },
)
expect(roll.formula).toBe('2d20kl1+5')
expect(roll.kept).toEqual([4])
})
it('replaces model-proposed damage with the authoritative roll total', () => {
const damage = resolveCheck(
{ actorId: 'hero', targetId: 'target', kind: 'damage', dice: '1d6+2', mode: 'normal', reason: 'Strike' },
hero,
{ integer: () => 4 },
)
const events = bindMechanicalEventsToRolls([
{ type: 'damage', actorId: 'hero', targetId: 'target', value: 999, item: null, description: 'Strike' },
], [damage])
expect(events[0]?.value).toBe(6)
})
it('clamps mechanical state', () => {
const result = applyMechanicalEvents([hero], [{ type: 'damage', actorId: null, targetId: 'hero', value: 99, item: null, description: 'Catastrophic hit' }])
expect(result.characters[0]?.hp).toBe(0)
expect(result.audit).toHaveLength(1)
})
it('closes only complete or forced rounds', () => {
expect(shouldCloseRound(['a', 'b'], ['a'])).toBe(false)
expect(shouldCloseRound(['a', 'b'], ['a', 'b'])).toBe(true)
expect(shouldCloseRound(['a', 'b'], [], true)).toBe(true)
})
})