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) }) })