结构(旧 game/core+engine → 新 engine/ 域): - engine/kernel/ 时钟/随机/插件协议/fxqueue/timesense/format/urgency/guide/names(原 core) - engine/narrative/ legacy/报告/列传/谱系/年轴(原 core 叙事族) - engine/runtime/ World/creation/pcgen/ApiFacade/capabilities/pluginManager/boot/clocks + Systems/*(12 系统) - engine/sim/ Market(未来 WorldSim 同行) - 旧 game/core、engine/systems、engine/world.ts 等路径全部废弃(无 re-export 兼容层) 验证:35 套件/967 测试全绿(金钟罩三档零漂移=纯搬迁无行为变化) typecheck 0 error
84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { World } from '../src/renderer/game/engine/runtime/World'
|
|
import { resetSaveBus, attachLogSink } from './world.helpers'
|
|
|
|
describe('World engine', () => {
|
|
it('creates a valid starting family', () => {
|
|
const w = World.create({ seed: 't1', surname: '林', familyName: '林家', motto: 'm', difficulty: 'normal' })
|
|
expect(Object.values(w.state.members).length).toBe(5)
|
|
expect(w.aliveMembers().length).toBe(5)
|
|
expect(w.state.family.headId).toBe('x1')
|
|
expect(w.ageOf(w.head())).toBe(35)
|
|
})
|
|
|
|
it('survives 20 years (240 months) without NaN or throws', () => {
|
|
const w = World.create({ seed: 'smoke', surname: '沈', familyName: '沈氏', motto: 'x', difficulty: 'normal' })
|
|
resetSaveBus()
|
|
attachLogSink(w)
|
|
for (let i = 0; i < 240; i++) {
|
|
if (w.state.gameOver) break
|
|
w.advanceMonth()
|
|
}
|
|
const s = w.state
|
|
expect(Number.isNaN(s.family.stones)).toBe(false)
|
|
expect(s.year).toBeGreaterThan(10)
|
|
for (const c of Object.values(s.members)) {
|
|
expect(Number.isNaN(c.realmProgress)).toBe(false)
|
|
expect(Number.isNaN(c.health)).toBe(false)
|
|
}
|
|
})
|
|
|
|
it('same seed plays identically (deterministic)', () => {
|
|
const a = World.create({ seed: 'det', surname: '苏', familyName: '苏家', motto: 'm', difficulty: 'easy' })
|
|
const b = World.create({ seed: 'det', surname: '苏', familyName: '苏家', motto: 'm', difficulty: 'easy' })
|
|
resetSaveBus()
|
|
attachLogSink(a)
|
|
resetSaveBus()
|
|
attachLogSink(b)
|
|
const ignoreRng = (state: { rng: unknown }) => JSON.parse(JSON.stringify(state))
|
|
for (let i = 0; i < 60; i++) {
|
|
a.advanceMonth()
|
|
b.advanceMonth()
|
|
}
|
|
a.state.pendingEvent && (a.state.pendingEvent = undefined)
|
|
b.state.pendingEvent && (b.state.pendingEvent = undefined)
|
|
const x = ignoreRng({ ...a.state, pendingEvent: undefined })
|
|
const y = ignoreRng({ ...b.state, pendingEvent: undefined })
|
|
expect(JSON.stringify(x)).toBe(JSON.stringify(y))
|
|
})
|
|
|
|
it('families grow over time (births occur)', () => {
|
|
const w = World.create({ seed: 'grow', surname: '叶', familyName: '叶家', motto: 'm', difficulty: 'easy' })
|
|
resetSaveBus()
|
|
attachLogSink(w)
|
|
for (let i = 0; i < 240; i++) {
|
|
if (w.state.gameOver) break
|
|
w.advanceMonth()
|
|
}
|
|
expect(Object.keys(w.state.members).length).toBeGreaterThan(5)
|
|
})
|
|
|
|
it('game over when everyone dies', () => {
|
|
const w = World.create({ seed: 'diehard', surname: '麦', familyName: '麦家', motto: 'm', difficulty: 'hard' })
|
|
resetSaveBus()
|
|
attachLogSink(w)
|
|
for (const c of Object.values(w.state.members)) {
|
|
c.alive = false
|
|
}
|
|
for (let i = 0; i < 5; i++) w.advanceMonth()
|
|
expect(w.state.gameOver).toBeTruthy()
|
|
})
|
|
|
|
it('building upgrade consumes resources', () => {
|
|
const w = World.create({ seed: 'bld', surname: '金', familyName: '金家', motto: 'm', difficulty: 'easy' })
|
|
const before = w.state.family.inventory['lingkuang'] ?? 0
|
|
w.state.family.inventory['lingkuang'] = (w.state.family.inventory['lingkuang'] ?? 0) + 100
|
|
const ok = w.build('fangshi')
|
|
expect(ok).toBe(true)
|
|
const upgraded = w.upgrade('fangshi')
|
|
expect(upgraded).toBe(true)
|
|
expect(w.state.family.buildings['fangshi']).toBe(2)
|
|
expect((w.state.family.inventory['lingkuang'] ?? 0) - before).toBeLessThan(100)
|
|
})
|
|
})
|