结构(旧 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
102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import { World } from '../src/renderer/game/engine/runtime/World'
|
||
import { calcGiftGain, GIFT_TIERS, giftNpc } from '../src/renderer/game/engine/runtime/Systems/diplomacy'
|
||
import { resetSaveBus, attachLogSink } from './world.helpers'
|
||
|
||
function baseWorld(seed: string): World {
|
||
const w = World.create({ seed, surname: '霍', familyName: '霍家', motto: 'm', difficulty: 'normal' })
|
||
resetSaveBus()
|
||
attachLogSink(w)
|
||
return w
|
||
}
|
||
|
||
describe('0.1.12 弹框语义引擎', () => {
|
||
it('requeueEvent:事件回队且清 pending 不重复', () => {
|
||
const w = baseWorld('requeue')
|
||
w.state.pendingEvent = 'ev-youdao'
|
||
w.requeueEvent('ev-youdao')
|
||
expect(w.state.pendingEvent).toBeUndefined()
|
||
expect(w.state.eventQueue).toContain('ev-youdao')
|
||
// 再次 requeue 不重复
|
||
w.requeueEvent('ev-youdao')
|
||
expect(w.state.eventQueue.filter((e) => e === 'ev-youdao').length).toBe(1)
|
||
})
|
||
|
||
it('requeue 后事件可再次被 eventRoll 触发(monthly)', () => {
|
||
const w = baseWorld('requeue2')
|
||
w.state.pendingEvent = 'ev-youdao'
|
||
w.requeueEvent('ev-youdao')
|
||
// 下一月 eventRoll 会从队列抽出
|
||
w.advanceMonth()
|
||
expect(w.state.eventQueue).not.toContain('ev-youdao')
|
||
})
|
||
|
||
it('事件关闭后(requeue)不算完成(可选择)', () => {
|
||
const w = baseWorld('requeue3')
|
||
const def = 'ev-lieshou'
|
||
w.state.pendingEvent = def
|
||
w.requeueEvent(def)
|
||
expect(w.state.completedEvents).not.toContain(def)
|
||
})
|
||
})
|
||
|
||
describe('赠礼档位', () => {
|
||
it.each(GIFT_TIERS.map((t) => [t] as const))('档位 %i 换算关系为 +N', (t) => {
|
||
const gain = calcGiftGain(t)
|
||
expect(gain).toBe(Math.max(1, Math.round(t / 12)))
|
||
expect(gain).toBeGreaterThan(0)
|
||
})
|
||
|
||
it.each(GIFT_TIERS.map((t) => [t] as const))('档位 %i 赠予后关系精确', (t) => {
|
||
const w = baseWorld(`gift-${t}`)
|
||
const npc = w.state.npcFamilies['n-xuanying']
|
||
const before = npc.relation
|
||
w.state.family.stones = 1000
|
||
giftNpc(w, 'n-xuanying', t)
|
||
expect(npc.relation).toBe(Math.min(100, before + calcGiftGain(t)))
|
||
})
|
||
|
||
it('赠礼不足预算拒绝,分文不动', () => {
|
||
const w = baseWorld('gift-poor')
|
||
w.state.family.stones = 10
|
||
const before = w.state.npcFamilies['n-xuanying'].relation
|
||
const ok = giftNpc(w, 'n-xuanying', 40)
|
||
expect(ok).toBe(false)
|
||
expect(w.state.npcFamilies['n-xuanying'].relation).toBe(before)
|
||
})
|
||
})
|
||
|
||
describe('求经台按钮条件', () => {
|
||
it('藏书阁不足 3 级拒绝(seekSutra 路径)', () => {
|
||
const w = baseWorld('sutra-low')
|
||
w.state.family.buildings = { cangshu: 2 }
|
||
expect(w.seekSutra()).toBe(false)
|
||
})
|
||
|
||
it('藏书阁 3 级 + 灵石足够 + 冷却期外成功', () => {
|
||
const w = baseWorld('sutra-ok')
|
||
w.state.family.buildings = { cangshu: 3 }
|
||
w.state.family.stones = 1000
|
||
expect(w.seekSutra()).toBe(true)
|
||
expect(w.state.family.stones).toBe(850)
|
||
})
|
||
|
||
it('两年冷却内再访拒绝', () => {
|
||
const w = baseWorld('sutra-cd')
|
||
w.state.family.buildings = { cangshu: 3 }
|
||
w.state.family.stones = 1000
|
||
w.seekSutra()
|
||
expect(w.seekSutra()).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('ModalShell 规格约束(min(92vw,Npx) 类防横滚契约)', () => {
|
||
it('弹框宽度定义均在 560-740 区间(可视化验收带)', () => {
|
||
const widths = { event: 680, battle: 720, paper: 620, member: 740, resolve: 700, confirm: 560 }
|
||
for (const [, w] of Object.entries(widths)) {
|
||
expect(w).toBeGreaterThanOrEqual(540)
|
||
expect(w).toBeLessThanOrEqual(760)
|
||
}
|
||
})
|
||
})
|