Files

202 lines
7.5 KiB
TypeScript

import { describe, expect, it, beforeEach } from 'vitest'
import { World } from '../src/renderer/game/engine/runtime/World'
import { modToPlugin, modPreflight } from '../src/renderer/game/engine/runtime/modManager'
import { validateMod } from '../src/renderer/game/engine/runtime/modSchema'
import { allWonderKinds, addWonderType, removeWonderType, wonderDefOf, worldNum, overrideWorldNum, resetWorldNum } from '../src/renderer/game/engine/sim/worldsim-data'
import { findEvent } from '../src/renderer/game/engine/runtime/Systems/events'
import { resetSaveBus, attachLogSink } from './world.helpers'
import type { ModPack } from '../src/renderer/game/engine/runtime/modSchema'
function baseWorld(seed: string): World {
const w = World.create({ seed, surname: '苏', familyName: '苏家', motto: 'm', difficulty: 'normal' })
resetSaveBus()
attachLogSink(w)
return w
}
const WONDER_MOD: ModPack = {
id: 'wonder-mod-test',
name: '奇观扩展测试包',
version: '1',
author: 'test',
schema: 1,
data: {
wonders: [
{ kind: 'star_fall', name: '星陨天落', desc: '天外陨星坠落——灵气激荡,宝材出世。', duration: 12, effect: 'tide_up', oneshot: 'loot' },
{ kind: 'void_rift', name: '虚空裂隙', desc: '空间碎裂——灵气逆流入虚。', duration: 6, effect: 'tide_down', oneshot: 'none' }
]
}
}
const KARMA_EVENT_MOD: ModPack = {
id: 'karma-event-mod',
name: '因果事件测试包',
version: '1',
author: 'test',
schema: 1,
data: {
events: [
{
id: 'ev-mod-karma-test',
name: '济世救人',
category: 'daily' as const,
weight: 10,
text: '路边有一受伤老者,族中子弟该如何处之?',
options: [
{ label: '施救', hint: '灵石-50,功德+8', eff: { res: { stones: -50 }, karma: 8 } },
{ label: '不管', hint: '无', eff: {} }
]
}
]
}
}
describe('0.1.37 P5 MOD 扩展口 · 奇观类型注册', () => {
it('内置奇观类型齐全(4 种)', () => {
const kinds = allWonderKinds()
expect(kinds.length).toBe(4)
expect(kinds.map((k) => k.kind)).toContain('tide_surge')
expect(kinds.map((k) => k.kind)).toContain('ancient_ruin')
expect(kinds.map((k) => k.kind)).toContain('heaven_bless')
expect(kinds.map((k) => k.kind)).toContain('qi_wane')
})
it('addWonderType 注册自定义奇观类型', () => {
expect(addWonderType({ kind: 'test_star', name: '星陨', desc: '测试', duration: 6, effect: 'tide_up', oneshot: 'none' })).toBe(true)
expect(allWonderKinds().some((k) => k.kind === 'test_star')).toBe(true)
expect(wonderDefOf('test_star')?.name).toBe('星陨')
// 重复注册失败
expect(addWonderType({ kind: 'test_star', name: '重复' })).toBe(false)
// 内置冲突失败
expect(addWonderType({ kind: 'tide_surge', name: '冲突' })).toBe(false)
removeWonderType('test_star')
})
it('removeWonderType 卸载扩展奇观', () => {
addWonderType({ kind: 'test_remove', name: '临时', desc: '...', duration: 0, effect: 'none', oneshot: 'none' })
expect(allWonderKinds().some((k) => k.kind === 'test_remove')).toBe(true)
removeWonderType('test_remove')
expect(allWonderKinds().some((k) => k.kind === 'test_remove')).toBe(false)
})
it('wonderDefOf 查询内置+扩展奇观', () => {
expect(wonderDefOf('tide_surge')?.name).toBe('灵气涌潮')
expect(wonderDefOf('heaven_bless')?.oneshot).toBe('bless')
expect(wonderDefOf('ancient_ruin')?.oneshot).toBe('loot')
expect(wonderDefOf('nonexistent')).toBeUndefined()
})
})
describe('0.1.37 P5 MOD 扩展口 · MOD 包校验', () => {
it('validateMod 奇观字段校验(正常包零警告)', () => {
const v = validateMod(WONDER_MOD)
expect(v.warnings.filter((w) => w.includes('奇观'))).toHaveLength(0)
})
it('validateMod 内置冲突检测', () => {
const conflictMod: ModPack = {
...WONDER_MOD,
data: { ...WONDER_MOD.data, wonders: [{ kind: 'tide_surge', name: '冲突', desc: '...' }] }
}
const v = validateMod(conflictMod)
expect(v.warnings.some((w) => w.includes('tide_surge') && w.includes('冲突'))).toBe(true)
})
it('modPreflight 飞升事件前缀保护', () => {
const w = baseWorld('preflight-asc')
const badMod: ModPack = {
id: 'bad-asc-mod',
name: '飞升劫持',
version: '1',
data: {
events: [{
id: 'ev-ascension-heart-x5',
name: '劫持',
category: 'fate' as const,
weight: 1,
text: '劫持',
options: [{ label: 'ok', eff: {} }]
}]
}
}
const r = modPreflight(w, badMod)
expect(r.ok).toBe(false)
expect(r.reason).toContain('ev-ascension-heart-x5')
})
})
describe('0.1.37 P5 MOD 扩展口 · MOD 安装/卸载奇观', () => {
it('modToPlugin 安装后扩展奇观入池', () => {
const w = baseWorld('mod-wonder-install')
const plugin = modToPlugin(WONDER_MOD)
w.installPlugin(plugin)
const kinds = allWonderKinds()
expect(kinds.some((k) => k.kind === 'star_fall')).toBe(true)
expect(kinds.some((k) => k.kind === 'void_rift')).toBe(true)
expect(wonderDefOf('star_fall')?.name).toBe('星陨天落')
expect(wonderDefOf('star_fall')?.effect).toBe('tide_up')
expect(wonderDefOf('star_fall')?.oneshot).toBe('loot')
})
it('卸载 MOD 后扩展奇观摘除', () => {
const w = baseWorld('mod-wonder-uninstall')
const plugin = modToPlugin(WONDER_MOD)
w.installPlugin(plugin)
expect(allWonderKinds().some((k) => k.kind === 'star_fall')).toBe(true)
w.removePlugin(plugin.id)
expect(allWonderKinds().some((k) => k.kind === 'star_fall')).toBe(false)
expect(allWonderKinds().some((k) => k.kind === 'void_rift')).toBe(false)
})
})
describe('0.1.37 P5 MOD 扩展口 · 因果事件 MOD', () => {
it('因果事件 MOD 安装后 eff.karma 生效', () => {
const w = baseWorld('mod-karma-event')
const plugin = modToPlugin(KARMA_EVENT_MOD)
w.installPlugin(plugin)
// 事件应可被 findEvent 查到
const def = findEvent('ev-mod-karma-test', w)
expect(def).toBeTruthy()
expect(def.options[0].eff.karma).toBe(8)
})
it('因果事件卸载后从池中移除', () => {
const w = baseWorld('mod-karma-uninstall')
const plugin = modToPlugin(KARMA_EVENT_MOD)
w.installPlugin(plugin)
expect(findEvent('ev-mod-karma-test', w)).toBeTruthy()
w.removePlugin(plugin.id)
expect(findEvent('ev-mod-karma-test', w)).toBeFalsy()
})
})
describe('0.1.37 P5 MOD 扩展口 · worldNum 白名单', () => {
beforeEach(() => resetWorldNum())
it('wonderChance 可经 worldNum 覆写', () => {
const original = worldNum('wonderChance')
expect(original).toBe(0.08)
overrideWorldNum('wonderChance', 0.15)
expect(worldNum('wonderChance')).toBe(0.15)
resetWorldNum('wonderChance')
expect(worldNum('wonderChance')).toBe(0.08)
})
it('karma 阈值可经 worldNum 覆写', () => {
expect(worldNum('karmaHighThreshold')).toBe(30)
expect(worldNum('karmaLowThreshold')).toBe(-20)
overrideWorldNum('karmaHighThreshold', 50)
expect(worldNum('karmaHighThreshold')).toBe(50)
resetWorldNum('karmaHighThreshold')
expect(worldNum('karmaHighThreshold')).toBe(30)
})
it('karmaDecayRate 可经 worldNum 覆写', () => {
expect(worldNum('karmaDecayRate')).toBe(0.1)
overrideWorldNum('karmaDecayRate', 0.05)
expect(worldNum('karmaDecayRate')).toBe(0.05)
resetWorldNum('karmaDecayRate')
expect(worldNum('karmaDecayRate')).toBe(0.1)
})
})