【事件闸(机制重锤)】 - fire(w,id,priority):单闸+优先级——高优(raid/渡劫/大比)顶替被占者、被顶替入 eventQueue 下月兑现不丢失;普通被占返回 false 调用方冷却(raid 被吞也写 CD) - pending 唯一写入入口:渡劫/破境丹改走 fire(防覆盖丢弃);名宿传薪年锚 flag(防同年重复 12 次) - applyEventChoice 前置校验 pendingEvent===id(陈旧 Modal 二次结算防线) 【世界模型四修(真·活世界)】 - era 延续档:flow 权重和<1(留白=续任)——盛世能撑 30 年而非固定 25 - 潮汐供给符号修正:灵涨万物丰(原实现反了)+tide 双界 clamp - driftMarket 弱锚定:rebalance 0.1→0.025——供需/era/灾年真实撬动价格(原五池被锚死±5%) - 断供/池底单常量 poolFloorPct=0.35(消灭禁买暗雷带) 【知识防线(审计虫洞修复)】 - 指纹扩展:era/stance/prosperity/relationsWithOthers/calamityLeft/pendingEvent 全入 hash - 第二金钟罩 GOLDEN_RESOLVED:自动 resolve 长跑锁“现实”世界(原指纹锁的只是“事件流冻结”世界) 【性能与存储】 - 存档节流(疾12/常6/缓4,手动即时)+ saveChronicle O(N)→O(1) 单查增量写(2160 月档告别每 tick 数千 OPFS) - battles≤220/chronicle≤520 截断(stringify 线性膨胀止血);startNewGame/openState 停 timer 摘旧 bus 【杂项】actDirect 对齐 ACT_CATALOG 全 24 项;about 三版本统一 0.1.20;kernel.bus 死总线删;seed 改 RngHub.rollSeed;normalize 补 eraStartYear/overflux/distress;WorldPanel 买卖条件反转修复;音效与视效同闸 【测试】1011 全绿(42 套件):audit-0.1.20 9 例(事件闸/传薪/二次结算/era延续/潮汐符号/断供一致/弱锚定/指纹敏感)
106 lines
4.6 KiB
TypeScript
106 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { World } from '../src/renderer/game/engine/runtime/World'
|
|
import { fire, applyEventChoice } from '../src/renderer/game/engine/runtime/Systems/events'
|
|
import { ERA_CONF, ERA_DURA, WORLDSIM, WorldSimState } from '../src/renderer/game/engine/sim/worldsim-data'
|
|
import { stateFingerprint } from './fingerprint.helper'
|
|
|
|
describe('0.1.20 万世无谬', () => {
|
|
it('fire 高优可顶替普通 pending,被顶替者入队不丢失', () => {
|
|
const w = World.create({ seed: 'a1', surname: '岳', familyName: '岳家', motto: 'm', difficulty: 'normal' })
|
|
w.advanceMonth()
|
|
w.state.pendingEvent = undefined // 清月度事件闸(测试隔离)
|
|
fire(w, 'ev-legacypass') // 普通占位
|
|
expect(w.state.pendingEvent).toBe('ev-legacypass')
|
|
const ok = fire(w, 'ev-raid-n-nulei', 1) // 高优顶替
|
|
expect(ok).toBe(true)
|
|
expect(w.state.pendingEvent).toBe('ev-raid-n-nulei')
|
|
expect(w.state.eventQueue).toContain('ev-legacypass') // 未丢失
|
|
})
|
|
|
|
it('fire 普通被占则 false(调用方可冷却)', () => {
|
|
const w = World.create({ seed: 'a2', surname: '满', familyName: '满家', motto: 'm', difficulty: 'normal' })
|
|
w.advanceMonth()
|
|
w.state.pendingEvent = undefined
|
|
fire(w, 'ev-legacypass')
|
|
expect(fire(w, 'ev-legacypass')).toBe(false)
|
|
})
|
|
|
|
it('名宿传薪同一年仅一次(年锚 flag)', () => {
|
|
const w = World.create({ seed: 'a3', surname: '贺', familyName: '贺家', motto: 'm', difficulty: 'normal' })
|
|
w.state.year = 4 // year%8===4
|
|
w.state.completedEvents = []
|
|
let n = 0
|
|
for (let i = 0; i < 12; i++) {
|
|
if (w.state.year % 8 === 4 && !w.state.family.flag[`legacyDone-${w.state.year}`]) {
|
|
if (fire(w, 'ev-legacypass')) {
|
|
w.state.family.flag[`legacyDone-${w.state.year}`] = true
|
|
n++
|
|
}
|
|
w.applyEventChoice(w.state.pendingEvent ?? 'ev-legacypass', 0)
|
|
}
|
|
w.state.month++
|
|
if (w.state.month > 12) { w.state.month = 1; w.state.year++ }
|
|
}
|
|
expect(n).toBeLessThanOrEqual(1)
|
|
})
|
|
|
|
it('二次结算防线:陈旧 Modal 选择被忽略(pending 已清/易主)', () => {
|
|
const w = World.create({ seed: 'a4', surname: '冼', familyName: '冼家', motto: 'm', difficulty: 'normal' })
|
|
w.advanceMonth()
|
|
fire(w, 'ev-legacypass')
|
|
const before = w.state.pendingEvent
|
|
// 恶意重复选择:pending 已易主(自动压制)时 apply 不得落盘
|
|
w.state.pendingEvent = undefined
|
|
let threw = false
|
|
try {
|
|
applyEventChoice(w, before!, 0)
|
|
} catch {
|
|
threw = true
|
|
}
|
|
expect(threw).toBe(false) // 不崩
|
|
expect(w.state.pendingEvent).toBeUndefined() // 未二次应用
|
|
})
|
|
|
|
it('era 有延续档:flow 权重和 < 1(不再每届必换)', () => {
|
|
for (const k of Object.keys(ERA_CONF) as Array<keyof typeof ERA_CONF>) {
|
|
const sum = ERA_CONF[k].flow.reduce((a, [, wgt]) => a + wgt, 0)
|
|
expect(sum).toBeLessThan(1)
|
|
}
|
|
expect(ERA_DURA.pingshi[0]).toBeLessThan(ERA_DURA.pingshi[1])
|
|
})
|
|
|
|
it('潮汐供给正号:tide>1 → 供给乘 >1(灵涨必丰)', () => {
|
|
const multAt = (tide: number): number => Math.max(0.75, Math.min(1.35, 1 + (tide - 1) * WORLDSIM.tideSupplySpan))
|
|
expect(multAt(1.3)).toBeGreaterThan(1)
|
|
expect(multAt(0.7)).toBeLessThan(1)
|
|
})
|
|
|
|
it('断供/池底单常量一致:拒单阈值 == poolFloorPct', () => {
|
|
expect(WORLDSIM.tradeFloorPct).toBe(WORLDSIM.poolFloorPct)
|
|
})
|
|
|
|
it('rebalance 弱锚定:供需可撬动价格(1200 月池有超过 ±25% 振幅的时机)', () => {
|
|
const w = World.create({ seed: 'a9', surname: '郄', familyName: '郄家', motto: 'm', difficulty: 'normal' })
|
|
let min = Infinity
|
|
let max = -Infinity
|
|
for (let i = 0; i < 1200; i++) {
|
|
w.advanceMonth()
|
|
const pool = (w.state.worldSim as WorldSimState | undefined)?.marketPool?.['lingcao'] ?? 600
|
|
min = Math.min(min, pool / 600)
|
|
max = Math.max(max, pool / 600)
|
|
}
|
|
expect(max - min).toBeGreaterThan(0.5) // 弱锚定下真实起伏
|
|
})
|
|
|
|
it('指纹含世界轴(era/stance 变更会红)', () => {
|
|
const w = World.create({ seed: 'a10', surname: '苻', familyName: '苻家', motto: 'm', difficulty: 'normal' })
|
|
for (let i = 0; i < 240; i++) w.advanceMonth()
|
|
const f1 = stateFingerprint(w.state)
|
|
const ws = w.state.worldSim as WorldSimState
|
|
ws.era = ws.era === 'mofa' ? 'shengshi' : 'mofa' // 确保改成异值
|
|
ws.npcDyn['n-xuanying']!.stance = ws.npcDyn['n-xuanying']!.stance === 'expand' ? 'endure' : 'expand'
|
|
const f2 = stateFingerprint(w.state)
|
|
expect(f1).not.toBe(f2)
|
|
})
|
|
})
|