【世界种子生成器(NPC 数量不再定死)】 - sim/worldgen.ts:generateWorld(seed) 确定性塑造天下——NPC 4~8 家随机 (老牌世家模板 + 词库组合新贵,名字去重)、初始关系网(1-2世仇+1盟友)、 开局 era 三态、区域风味、市场偏移 ±15% - 独立派生 rng(seed::worldgen)——World.rng 主序列零消耗:同 seed 世界可重放、 玩法随机序列不受生成器移动(金钟罩玩法序保护) - state.worldGen 落档(normalize 旧档按 seed 重放=同世界,存档兼容) - creation 初始化 npcFamilies/initSim 关系网/era/池偏置全走 worldgen——开局即恩怨 - 局内补位目标 = 开局家数(4~8),生灭闭环持续 【插件深化(0.1.24 第二组钩子)】 - PluginContext.beforePhase/afterPhase(时轮锚点:phase 前后挂勾,卸载全摘) - PluginContext.addNpcTemplate(世界模板注入——词库插件化) - plugin-public 文档同步;示例 Plugin 契约升级 【测试适配(世界名单随机化)】 - 静态 npc id 假设全面改动态取家(anyNpc helper 共享); - 难度梯度用例改「同 seed 不同难度」比较;worldgen 确定性/范围/去重/关系对称 7 例 【测试】1034 全绿(45 套件);金钟罩 0.1.24 基线固化(worldgen 随机世界)+ worldAnnals 等; build 通过
93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { World } from '../src/renderer/game/engine/runtime/World'
|
|
import { createWorldState, NewGameOptions, findInheritor } from '../src/renderer/game/engine/runtime/creation'
|
|
import { resetSaveBus, attachLogSink } from './world.helpers'
|
|
|
|
function opts(seed: string, difficulty: 'easy' | 'normal' | 'hard' = 'normal', surname = '彭'): NewGameOptions {
|
|
return { seed, surname, familyName: `${surname}家`, motto: 'm', difficulty }
|
|
}
|
|
|
|
describe('World 生命周期与初始态', () => {
|
|
it('三种难度初始资源与 NPC 强度梯度正确', () => {
|
|
// 同 seed 不同难度(worldgen 由 seed 决定——三世界名单一致,可比)
|
|
const easy = createWorldState(opts('st-diff', 'easy'))
|
|
const normal = createWorldState(opts('st-diff', 'normal'))
|
|
const hard = createWorldState(opts('st-diff', 'hard'))
|
|
expect(easy.family.stones).toBeGreaterThan(normal.family.stones)
|
|
expect(normal.family.stones).toBeGreaterThan(hard.family.stones)
|
|
const someId = Object.keys(normal.npcFamilies)[0]!
|
|
expect(easy.npcFamilies[someId].power).toBeLessThan(normal.npcFamilies[someId].power)
|
|
expect(normal.npcFamilies[someId].power).toBeLessThan(hard.npcFamilies[someId].power)
|
|
})
|
|
|
|
it('创建后基本不变量的存在性', () => {
|
|
const w = World.create(opts('st-b'))
|
|
const s = w.state
|
|
expect(s.family.headId).toBe('x1')
|
|
expect(s.family.buildings['lingtian']).toBe(1)
|
|
expect(s.family.buildings['zongci']).toBe(1)
|
|
expect(s.family.techniques.length).toBe(2)
|
|
expect(s.seq).toBeGreaterThanOrEqual(10)
|
|
expect(s.chronicle.length).toBe(1) // 立家
|
|
})
|
|
|
|
it('findInheritor 子代优先,其次高境界', () => {
|
|
const w = World.create(opts('st-fi'))
|
|
const heir = findInheritor(w)
|
|
expect(['x3', 'x5']).toContain(heir?.id)
|
|
})
|
|
})
|
|
|
|
describe('World 推进元心与数据保全', () => {
|
|
it('推进后 seq/months/总tick 单调增长', () => {
|
|
const w = World.create(opts('adv-a'))
|
|
const seq0 = w.state.seq
|
|
const t0 = w.state.totalTicks
|
|
w.advanceMonth()
|
|
expect(w.state.totalTicks).toBe(t0 + 1)
|
|
expect(w.state.seq).toBeGreaterThanOrEqual(seq0)
|
|
})
|
|
|
|
it('月份跨年正确且 yearStart 触发', () => {
|
|
const w = World.create(opts('adv-b'))
|
|
for (let i = 0; i < 11; i++) w.advanceMonth() // 到 12 月
|
|
expect(w.state.month).toBe(12)
|
|
w.advanceMonth() // 跨年
|
|
expect(w.state.month).toBe(1)
|
|
expect(w.state.year).toBe(2)
|
|
expect(w.state.yearlyReports.length).toBe(1)
|
|
})
|
|
|
|
it('gameOver 后 advance 不继续崩', () => {
|
|
const w = World.create(opts('adv-c'))
|
|
for (const c of Object.values(w.state.members)) c.alive = false
|
|
w.advanceMonth()
|
|
expect(w.state.gameOver).toBeTruthy()
|
|
w.advanceMonth() // 不再抛
|
|
expect(w.state.year).toBeGreaterThanOrEqual(1)
|
|
})
|
|
|
|
it('年度报告的 power 值始终有效', () => {
|
|
const w = World.create(opts('adv-d'))
|
|
for (let i = 0; i < 30; i++) w.advanceMonth()
|
|
expect(w.state.yearlyReports.every((r) => r.power > 0)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('存档重建后继续运行一致性', () => {
|
|
it('json 往返后 100 月演进无异常且继续确定', () => {
|
|
const w = World.create(opts('rid-a'))
|
|
for (let i = 0; i < 25; i++) w.advanceMonth()
|
|
w.syncRng()
|
|
const clone = JSON.parse(JSON.stringify(w.state))
|
|
const w2 = new World(clone)
|
|
for (let i = 0; i < 75; i++) {
|
|
if (w.state.gameOver) break
|
|
w.advanceMonth()
|
|
w2.advanceMonth()
|
|
}
|
|
expect(w2.state.year).toBe(w.state.year)
|
|
expect(w2.state.family.stones).toBe(w.state.family.stones)
|
|
})
|
|
})
|