202 lines
7.5 KiB
TypeScript
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)
|
|
})
|
|
})
|