新增 5 个矩阵套件: - data-matrix 242:物品/功法/建筑/秘境/敌人/势力/事件选项/职事/秉性/灵根/境界/时节 逐行 - balance-edge 81:年龄/修为/气血/库存/金钱边界、传承谱系年轴、境界战力、突破矩阵、评分分档、难度 - world-matrix 21:多 seed 长跑、继承链多路径、gameOver 守卫、岁簿/大比/回声周期、确定性复跑 - rng-matrix 38:多种子性质、范围分桶、RngHub 回放/隔离、state 拷贝、pick/shuffle 保集 - api-matrix 32:act 全目录逐项、query 全 ref、subscribe 全事件矩阵 - event-state-matrix 193:逐事件逐选项应用不破、effect 引用、职事上限/寄读、志向、normalize 残缺矩阵、动态事件 引擎附带优化:月底 clampState(修为/气血/库存/金钱永不越界——负血量负库存负钱被矩阵钓出后修复) 总测试 256 → 861;金钟罩复验通过(clamp 零漂移)
160 lines
5.6 KiB
TypeScript
160 lines
5.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { ITEMS, ARTIFACT_POWER } from '../src/renderer/game/data/items'
|
|
import { TECHNIQUES } from '../src/renderer/game/data/techniques'
|
|
import { BUILDINGS } from '../src/renderer/game/data/buildings'
|
|
import { MISSIONS, ENEMIES } from '../src/renderer/game/data/secrets'
|
|
import { NPCS } from '../src/renderer/game/data/npcs'
|
|
import { EVENTS } from '../src/renderer/game/data/events'
|
|
import { POSTS } from '../src/renderer/game/data/posts'
|
|
import { TRAITS } from '../src/renderer/game/data/traits'
|
|
import { ROOT_GRADES } from '../src/renderer/game/data/elements'
|
|
import { MAJORS, MAJOR_ORDER } from '../src/renderer/game/data/realms'
|
|
import { SEASON, seasonOf } from '../src/renderer/game/data/season'
|
|
|
|
describe('物品表逐行矩阵', () => {
|
|
it.each(Object.entries(ITEMS).map(([id, v]) => [id, v.name, v.basePrice, v.kind] as const))(
|
|
'物品 %s 名称/价格/类别合法',
|
|
(_id, name, price, kind) => {
|
|
expect(name.length).toBeGreaterThan(0)
|
|
expect(price).toBeGreaterThan(0)
|
|
expect(['resource', 'pill', 'artifact']).toContain(kind)
|
|
}
|
|
)
|
|
|
|
it.each(Object.entries(ITEMS).filter(([, v]) => v.kind === 'artifact').map(([id]) => [id] as const))(
|
|
'法器 %s 有战力修正',
|
|
(id) => {
|
|
expect(ARTIFACT_POWER[id]).toBeGreaterThan(0)
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('功法表逐行矩阵', () => {
|
|
it.each(TECHNIQUES.map((t) => [t.id, t.grade, t.powerBonus, t.expBonus, t.path] as const))(
|
|
'功法 %s 参数板面合法',
|
|
(id, grade, pow, exp, path) => {
|
|
expect(id.startsWith('t-')).toBe(true)
|
|
expect(grade).toBeGreaterThanOrEqual(1)
|
|
expect(grade).toBeLessThanOrEqual(4)
|
|
expect(pow).toBeGreaterThan(0)
|
|
expect(exp).toBeGreaterThan(0)
|
|
expect(path.length).toBeGreaterThan(0)
|
|
}
|
|
)
|
|
|
|
it('功法 id 全局唯一', () => {
|
|
expect(new Set(TECHNIQUES.map((t) => t.id)).size).toBe(TECHNIQUES.length)
|
|
})
|
|
})
|
|
|
|
describe('建筑表逐行矩阵', () => {
|
|
it.each(Object.entries(BUILDINGS).map(([id, v]) => [id, v.maxLevel, v.kind] as const))(
|
|
'建筑 %s 等级/类型合法',
|
|
(id, max, kind) => {
|
|
expect(id.length).toBeGreaterThan(1)
|
|
expect(max).toBeGreaterThanOrEqual(3)
|
|
expect(['produce', 'function']).toContain(kind)
|
|
}
|
|
)
|
|
|
|
it.each(Object.entries(BUILDINGS).map(([id, v]) => [id, v.upgradeCost(1), v.upgradeCost(v.maxLevel)] as const))(
|
|
'建筑 %s 升级成本随级递增',
|
|
(_id, c1, cMax) => {
|
|
expect(cMax.stones).toBeGreaterThan(c1.stones)
|
|
expect(cMax.lingkuang).toBeGreaterThan(c1.lingkuang)
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('秘境表逐行矩阵', () => {
|
|
it.each(MISSIONS.map((m) => [m.id, m.name] as const))('秘境 %s 存在启程摘要', (id, name) => {
|
|
expect(id.startsWith('m-')).toBe(true)
|
|
expect(name.length).toBeGreaterThan(1)
|
|
})
|
|
|
|
it.each(
|
|
MISSIONS.flatMap((m) =>
|
|
m.stages.map(
|
|
(st, i) => [m.id, i, st.kind, st.months] as const
|
|
)
|
|
)
|
|
)('秘境 %s 第%i 阶段类型/月数合法', (_id, _i, kind, months) => {
|
|
expect(['event', 'combat', 'resource', 'boss']).toContain(kind)
|
|
expect(months).toBeGreaterThan(0)
|
|
})
|
|
|
|
it.each(ENEMIES.map((e) => [e.id, e.realm] as const))('敌人 %s 境界关联存在', (id, realm) => {
|
|
expect(id.startsWith('e-')).toBe(true)
|
|
expect(MAJOR_ORDER).toContain(realm)
|
|
})
|
|
})
|
|
|
|
describe('势力表逐行矩阵', () => {
|
|
it.each(NPCS.map((n) => [n.id, n.name] as const))('势力 %s 命名与风格', (id, name) => {
|
|
expect(id.startsWith('n-')).toBe(true)
|
|
expect(name).toContain('氏')
|
|
})
|
|
|
|
it.each(NPCS.map((n) => [n.id, n.initialPower] as const))('势力 %s 初始战力为正', (_id, p) => {
|
|
expect(p).toBeGreaterThan(0)
|
|
})
|
|
|
|
it.each(NPCS.map((n) => [n.id, n.sells ?? [], n.buys ?? []] as const))('势力 %s 交易品类引用合法', (_id, sells, buys) => {
|
|
for (const s of sells) {
|
|
expect(ITEMS[s] ?? TECHNIQUES.find((t) => t.id === s)).toBeTruthy()
|
|
}
|
|
void buys
|
|
})
|
|
})
|
|
|
|
describe('事件表逐选项矩阵', () => {
|
|
it.each(EVENTS.flatMap((e) => e.options.map((o, idx) => [e.id, idx, o.label.length] as const)))(
|
|
'事件 %s 选项%i 有文案',
|
|
(_id, _idx, len) => {
|
|
expect(len).toBeGreaterThan(0)
|
|
}
|
|
)
|
|
|
|
it.each(EVENTS.map((e) => [e.id, e.weight] as const))('事件 %s 权重为正', (_id, w) => {
|
|
expect(w).toBeGreaterThan(0)
|
|
})
|
|
|
|
it.each(EVENTS.filter((e) => e.cond?.minBuilding).map((e) => [e.id, e.cond!.minBuilding!.id] as const))(
|
|
'事件 %s 建筑条件引用存在',
|
|
(_id, b) => {
|
|
expect(BUILDINGS[b]).toBeTruthy()
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('职事/秉性/灵根/境界矩阵', () => {
|
|
it.each(Object.values(POSTS).map((p) => [p.id, p.name, p.max] as const))('职事 %s %s 上限合法', (id, name, max) => {
|
|
expect(id.length).toBeGreaterThan(0)
|
|
expect(name.length).toBeGreaterThan(0)
|
|
expect(max).toBeGreaterThanOrEqual(1)
|
|
})
|
|
|
|
it.each(Object.entries(TRAITS).map(([id, t]) => [id, t.danger] as const))('秉性 %s 危险度在界', (_id, d) => {
|
|
expect(d).toBeGreaterThanOrEqual(0)
|
|
expect(d).toBeLessThanOrEqual(1)
|
|
})
|
|
|
|
it.each(Object.entries(ROOT_GRADES).map(([k, v]) => [Number(k), v.expBonus] as const))('灵根品阶%i 加成递增', (grade, exp) => {
|
|
expect(exp).toBeGreaterThan(0)
|
|
if (grade > 0) {
|
|
expect(exp).toBeGreaterThan(ROOT_GRADES[grade - 1].expBonus)
|
|
}
|
|
})
|
|
|
|
it.each(MAJOR_ORDER.map((m) => [m, MAJORS[m].lifespan] as const))('大境界 %s 寿元为正', (_m, l) => {
|
|
expect(l).toBeGreaterThan(0)
|
|
})
|
|
})
|
|
|
|
describe('时节数学矩阵', () => {
|
|
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((m) => [m, seasonOf(m)] as const))('%i月 归季 %s', (m, s) => {
|
|
const mod = SEASON[s]
|
|
expect(mod.name.length).toBeGreaterThan(0)
|
|
})
|
|
})
|