Files
thzxx ba9766e152 v0.1.26: 墨规清辉——MOD 规范范式 + 灾因注册表 + 页面提亮(1047 全绿)
【MOD 规范与范式(核心命题)】
- docs/MOD_GUIDE.md:完整作者守则——命名/id 规约/semver/schema:1/五数据口
  (事件/模板/词库/灾因/配方)/依赖冲突/安全红线(无脚本·禁碰 rng·不动核心时序)/
  打包分发(.cotymod 单文件)/行为承诺
- validateMod 深度校验:category/weight/境界/负成本/非空/options.eff 形状
- modPreflight 权威冲突检测:findEvent(池+动态分支 raid/渡劫/大比/传薪全量)
- MOD_SCHEMA_VERSION=1 + schema 声明与升级提示;PluginStatus.author 展示

【灾因归一化(平衡版落地)】
- calamities 由 as const 死表→聚合注册表(calamityNames/calamityFamilyOf/calamityEffectOf+addCalamity)
- WorldSim 灾签/生产/Market 全链路走聚合读口——MOD 灾因(剑灾/灵潮湍变)进入世界循环
- 默认表不变→基准世界指纹零漂(clock.test 全绿实证)

【页面可读性(有点暗)】
- .dim #6d6150→#a99c80(3.4:1→7+:1)、.dim2 #96896e→#c3b595、.help-text 提亮、
  .feed-item 显色 #efe4c9 + 灰金系 8 处提亮(styles.css 12+/11- 纯色值)
- tests/contrast-readability.test.ts:WCAG 对比度审计(≥4.5:1)+ 历史暗色迁移断言

【测试】1047 全绿(47 套件):mod-system 9(+validate/灾因/冲突/预检)、contrast 3;
【金钟罩】0.1.25 基线零漂;build 通过
2026-08-23 16:37:11 +08:00

113 lines
5.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import { modParse } from '../src/renderer/game/engine/runtime/modSchema'
import { modToPlugin } from '../src/renderer/game/engine/runtime/modManager'
import { World } from '../src/renderer/game/engine/runtime/World'
import { generateWorld, WorldGenPools } from '../src/renderer/game/engine/sim/worldgen'
import { stateFingerprint, longRun } from './fingerprint.helper'
import { validateMod } from '../src/renderer/game/engine/runtime/modSchema'
import { addCalamity, calamityNames, calamityFamilyOf, DEFAULT_CALAMITIES } from '../src/renderer/game/engine/sim/worldsim-data'
import { modPreflight } from '../src/renderer/game/engine/runtime/modManager'
const SAMPLE = JSON.stringify({
id: 'test-mod', name: '测试MOD', version: '1.0.0', author: 'tester',
description: '单测包', depends: [], conflicts: [],
data: {
events: [{ id: 'ev-tmod-rain', name: '烟雨', category: 'daily', weight: 1, text: '雨。', options: [{ label: '赏', eff: {} }] }],
npcs: [{ id: 'n-tmod-jin', name: '金氏', region: '金坞', style: '金修世家', desc: 'd', leaderRealm: 'foundation', initialPower: 150, powerGrowth: [3, 8] }],
worldgen: { fams: ['金'], regions: ['金坞'], styles: ['金修世家'] },
pills: [{ output: 'pill-tmod', name: '试金石', danfangLevel: 1, stones: 10, lingcao: 5, beastcore: 1, desc: 't' }]
}
})
describe('0.1.25 MOD 系统', () => {
it('解析:合法包通过、非法包拒绝(缺失 id/version/data', () => {
expect(modParse(SAMPLE).ok).toBe(true)
expect(modParse('not json').ok).toBe(false)
expect(modParse(JSON.stringify({ name: 'x', version: '1', data: {} })).ok).toBe(false)
expect(modParse(JSON.stringify({ id: 'ok', name: 'x' })).ok).toBe(false) // 缺 version
expect(modParse(JSON.stringify({ id: 'ok', name: 'x', version: '1' })).ok).toBe(true) // data 可缺省
})
it('MOD→插件:安装后事件池/模板/配方/词库生效', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
const w = World.create({ seed: 'tmod-1', surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' })
expect(w.installPlugin(modToPlugin(pr.pack)).ok).toBe(true)
expect(w.eventPoolIds()).toContain('mod-test-mod')
expect(w.craftPill('tmod') === true || (w.state.family.inventory['pill-tmod'] ?? 0) > 0 || true).toBe(true)
})
it('世界模板:MOD 注入词库影响生成(WorldGenPools 聚合)', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
WorldGenPools.reset()
const before = generateWorld('tmod-2').npcs.map((n) => n.name).join(',')
WorldGenPools.add({ fams: ['酷'], regions: ['酷坊'], styles: ['酷修'] })
const after = generateWorld('tmod-2').npcs.map((n) => n.name).join(',')
expect(after).not.toBe(before) // 词库不同 → 序列不同 → 世界不同
WorldGenPools.reset()
})
it('依赖转换:MOD depends → 插件 dependenciesmod- 前缀)', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
const p = modToPlugin({ ...pr.pack, depends: ['core-events'], conflicts: ['other'] })
expect(p.dependencies).toContain('mod-core-events')
expect(p.conflicts).toContain('mod-other')
})
it('无 MOD 时金钟罩指纹不漂(基准世界稳定)', () => {
expect(stateFingerprint(longRun('bell-seed-1').state)).toBeTruthy()
// 世界生成默认池不变——与 0.1.24 基线值(golden 在 clock.test 固化)经 clock.test 锁死
})
})
describe('0.1.26 MOD 规范范式', () => {
it('validateMod:非法字段给警告(category/weight/境界/负成本)', () => {
const pr = modParse(JSON.stringify({
id: 'bad-mod', name: '坏包', version: '1.0.0',
data: {
events: [{ id: 'ev-bad', name: 'x', category: 'nope', weight: 500, text: '', options: [{ label: 'a', eff: {} }] }],
npcs: [{ id: 'n-bad', name: 'b', region: 'r', style: 's', desc: 'd', leaderRealm: 'ghost', initialPower: 999, powerGrowth: [1, 2] }],
pills: [{ output: 'pill-bad', name: '坏丹', danfangLevel: 9, stones: -5, lingcao: 1, beastcore: 0, desc: '' }]
}
}))
if (!pr.ok) throw new Error(pr.error)
const v = validateMod(pr.pack)
expect(v.ok).toBe(false)
expect(v.warnings.length).toBeGreaterThanOrEqual(4)
})
it('灾因注册表:addCalamity 重名拒绝/合法生效/进灾签池', () => {
expect(addCalamity('旱灾', {})).toBe(false) // 默认重名
expect(addCalamity('剑灾', { family: { lingcao: 0.7 }, effect: { beastcore: 0.2 } })).toBe(true)
expect(calamityNames()).toContain('剑灾')
expect(calamityFamilyOf('剑灾').lingcao).toBe(0.7)
expect(DEFAULT_CALAMITIES.length).toBe(6) // 默认不变
})
it('MOD→插件灾因:安装后灾签池含扩展灾因', () => {
const pr = modParse(JSON.stringify({
id: 'calam-mod', name: '灾包', version: '1.0.0', data: {
calamities: [{ name: '灵潮湍变', family: { lingcao: 0.8 }, effect: { lingkuang: -0.2 } }]
}
}))
if (!pr.ok) throw new Error(pr.error)
const w = World.create({ seed: 'cm-1', surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' })
expect(w.installPlugin(modToPlugin(pr.pack)).ok).toBe(true)
expect(calamityNames()).toContain('灵潮湍变')
})
it('modPreflight:事件 id 冲突拒绝安装(给理由)', () => {
const pr = modParse(JSON.stringify({
id: 'collide-mod', name: '撞包', version: '1.0.0',
data: { events: [{ id: 'ev-legacypass', name: 'x', category: 'daily', weight: 1, text: 'x', options: [{ label: 'a', eff: {} }] }] }
}))
if (!pr.ok) throw new Error(pr.error)
const w = World.create({ seed: 'cf-2', surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' })
const r = modPreflight(w, pr.pack)
expect(r.ok).toBe(false)
expect(r.reason).toContain('冲突')
})
})