【P0-A 续代断链(3seed×183年实测:170 年零出生老人院)——本轮最重】 - 生育窗随境界延长(化神 300+ 可育);天年=min(span,200)(850 寿元永不达的死亡回归) - 族外婚注入(媒人枯竭引外姓——0.1.1 至今缺失的最后一环) - 全局年胎 cap(≤18 人年 3 胎/18-24 1 胎/>24 停)→ 稳态 ~25 人、gens 3、2200 月 420ms - 确定性回归:inlaw 判定长度化+单一 chance(修复过程中的真 bug——det 二分锁定) 【P1 七件】legacypass 年锚(同一年 12 连发 224 次实测)/敌强度相对化(0.35+rel×0.55, cap 2.6——5000+ 战力零风险碾压的定锚)/eventRoll 插件权重防垄断(>核心 50% 压缩)/ allEvents 按 enabled 过滤(禁用 MOD 事件双闸全面兑现)/bundle 全量导入(modParseAll 导出 2 回 2)/ chronicle 表治理(write-only→no-op)/Stash 并发(open in-flight 去重+save try) + PluginManager catch 全回滚 + about 版本统一 【P2 群】decline 150 激活生灭/化神月俸 20 石泄灵石死水/灾因 brief+descOf 接线/ worldGenMods 落空/EventModal 文案对齐/LegacyPanel 恒等清理 【测试】70 套件 2014 全绿(audit-0.1.29 六例:人口曲线/传薪年锚/强度公式/bundle 全量/异常回滚/灾因 brief); 【金钟罩】0.1.29 基线重算(续代/战斗/事件数值受控变更);build 通过
138 lines
5.1 KiB
TypeScript
138 lines
5.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import { GameEngine } from '../src/renderer/game/engine/GameEngine'
|
||
import { makeKernel } from '../src/renderer/game/engine/kernel/Kernel'
|
||
import { WorldSim } from '../src/renderer/game/engine/sim/WorldSim'
|
||
import { WORLDSIM } from '../src/renderer/game/engine/sim/worldsim-data'
|
||
|
||
describe('GameEngine 引擎门面', () => {
|
||
it('构造:内核注入世界 + 门面 + 数据包', () => {
|
||
const e = new GameEngine({ seed: 'engine-1' })
|
||
expect(e.world).toBeTruthy()
|
||
expect(e.facade).toBeTruthy()
|
||
expect(e.kernel.clock.subscriptionCount()).toBeGreaterThanOrEqual(10)
|
||
expect(e.status()).toBe('running')
|
||
})
|
||
|
||
it('advance 驱动内核时钟(确定单源)', () => {
|
||
const e = new GameEngine({ seed: 'engine-2' })
|
||
const y0 = e.view().year
|
||
const m0 = e.view().month
|
||
e.advance()
|
||
const crossed = e.view().month === 1 && e.view().year === y0 + 1
|
||
const normal = e.view().month === m0 + 1
|
||
expect(crossed || normal).toBe(true)
|
||
})
|
||
|
||
it('act/query/subscribe 全链路', () => {
|
||
const e = new GameEngine({ seed: 'engine-3' })
|
||
const events: string[] = []
|
||
const unsub = e.subscribe((ev) => events.push(ev.type))
|
||
expect(e.act('member.post', { memberId: 'x3', post: 'elder' })).toBe(true)
|
||
e.advance()
|
||
// 一次 advance 必然产生内核事件流(log/pending/paper 至少其一)
|
||
expect(events.length).toBeGreaterThan(0)
|
||
unsub()
|
||
const fam = e.query('family') as { year: number }
|
||
expect(fam.year).toBeGreaterThanOrEqual(1)
|
||
})
|
||
|
||
it('datapack 覆写生效 + restore 可回放', () => {
|
||
const e = new GameEngine({ seed: 'engine-4' })
|
||
e.datapack.override({
|
||
items: { ...e.datapack.current().items, lingcao: { ...e.datapack.current().items['lingcao'], basePrice: 250 } }
|
||
})
|
||
const snap = e.snapshot()
|
||
expect(snap.family.stones).toBeGreaterThanOrEqual(0)
|
||
e.restore(snap)
|
||
expect(e.status()).toBe('running')
|
||
})
|
||
|
||
it('about 元数据含 plugins(≥3)与版本 0.1.20', () => {
|
||
const e = new GameEngine({ seed: 'engine-5' })
|
||
const a = e.about()
|
||
expect(a.plugins).toBeGreaterThanOrEqual(3)
|
||
expect(a.version).toContain('0.1.29')
|
||
})
|
||
})
|
||
|
||
describe('Kernel 三合一', () => {
|
||
it('时钟/随机协同(同 seed 同随机序列);bus 已并入 World.out', () => {
|
||
const k1 = makeKernel('kernel-a')
|
||
const k2 = makeKernel('kernel-a')
|
||
expect(k1.rng.next()).toBe(k2.rng.next())
|
||
expect(k1.clock).toBeTruthy()
|
||
expect((k1 as unknown as { bus?: unknown }).bus).toBeUndefined()
|
||
})
|
||
})
|
||
|
||
function runEngine(e: GameEngine, months: number): void {
|
||
for (let i = 0; i < months; i++) {
|
||
if (e.view().gameOver) break
|
||
e.advance()
|
||
// 引擎语义:遇到待决事件停下来等处理;自动化直接取第一个选项继续
|
||
let guard = 0
|
||
while (e.view().pendingEvent && guard < 4) {
|
||
e.resolvePending(0)
|
||
guard++
|
||
}
|
||
}
|
||
}
|
||
|
||
describe('WorldSim 世界健康(长跑)', () => {
|
||
it.each(['ws-1', 'ws-2', 'ws-3'].map((s) => [s] as const))('%s:1000 月市场池/灵气在界、世界不崩', (seed) => {
|
||
const e = new GameEngine({ seed })
|
||
runEngine(e, 1000)
|
||
const ws = e.view().worldSim as (Record<string, unknown> & {
|
||
marketPool: Record<string, number>
|
||
secretQi: Record<string, number>
|
||
tide: number
|
||
}) | undefined
|
||
expect(ws).toBeTruthy()
|
||
if (ws) {
|
||
for (const v of Object.values(ws.marketPool)) expect(v).toBeGreaterThan(0)
|
||
for (const v of Object.values(ws.secretQi ?? {})) expect(v).toBeGreaterThanOrEqual(0)
|
||
expect(ws.tide).toBeGreaterThan(0.5)
|
||
expect(ws.tide).toBeLessThan(1.4)
|
||
}
|
||
})
|
||
|
||
it('NPC 换代至少发生一次(世界在演化)', () => {
|
||
const e = new GameEngine({ seed: 'ws-evolve' })
|
||
runEngine(e, 1200)
|
||
const ws = e.view().worldSim as { npcSuccessions?: number } | undefined
|
||
expect(ws?.npcSuccessions ?? 0).toBeGreaterThan(0)
|
||
})
|
||
|
||
it('快讯持有且裁剪符合 N(≤120)', () => {
|
||
const e = new GameEngine({ seed: 'ws-news' })
|
||
runEngine(e, 400)
|
||
const ws = e.view().worldSim as { newsFeed?: unknown[] } | undefined
|
||
expect(ws?.newsFeed.length ?? 0).toBeLessThanOrEqual(WORLDSIM.newsKeep)
|
||
})
|
||
})
|
||
|
||
describe('Sim 与外层联动', () => {
|
||
it('tideMult 在界(0.5-1.4)且随年景变化', () => {
|
||
const e = new GameEngine({ seed: 'ws-tide-link' })
|
||
const sim = new WorldSim(e.world)
|
||
const before = sim.tideMult()
|
||
expect(before).toBeGreaterThanOrEqual(0.5)
|
||
expect(before).toBeLessThan(1.4)
|
||
})
|
||
})
|
||
|
||
describe('GameEngine 建档参数', () => {
|
||
it('姓氏透传:新档用玩家输入的姓氏/家号(0.1.22 修复)', () => {
|
||
const e = new GameEngine({ seed: 'name-fix', surname: '慕容', familyName: '慕容家', motto: '铁血丹心', difficulty: 'normal' })
|
||
expect(e.world.state.family.name).toBe('慕容家')
|
||
const head = Object.values(e.world.state.members).find((c) => c.isHead)
|
||
expect(head?.name.startsWith('慕容')).toBe(true)
|
||
expect(e.world.state.family.motto ?? '').toBeTruthy()
|
||
})
|
||
|
||
it('缺省建档仍为林氏(兜底不破)', () => {
|
||
const e = new GameEngine({ seed: 'name-default' })
|
||
expect(e.world.state.family.name).toBe('林氏')
|
||
})
|
||
})
|