0.1.37 天人感应·因果织锦:功德系统+飞升试炼+世界奇观+MOD扩展口
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { World } from '../src/renderer/game/engine/runtime/World'
|
||||
import { stateFingerprint, longRun } from './fingerprint.helper'
|
||||
import { resolveAscension, ascensionEventId, perTribChance } from '../src/renderer/game/engine/runtime/Systems/tribulation'
|
||||
import { WONDER_NAMES, WONDER_CHANCE } from '../src/renderer/game/engine/sim/worldsim-data'
|
||||
import { WorldSim } from '../src/renderer/game/engine/sim/WorldSim'
|
||||
import { findEvent } from '../src/renderer/game/engine/runtime/Systems/events'
|
||||
|
||||
describe('0.1.37 因果系统(karma)', () => {
|
||||
it('初始 karma = 0', () => {
|
||||
const w = World.create({ seed: 'karma-test-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
expect(w.state.family.karma).toBe(0)
|
||||
})
|
||||
|
||||
it('normalize 兜底:缺失 karma 归零', () => {
|
||||
const w = World.create({ seed: 'karma-test-2', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
delete (w.state.family as Partial<{ karma: number }>).karma
|
||||
// 推进一个月——normalize 在 World 构造时已执行,但 advanceMonth 中应安全
|
||||
w.advanceMonth()
|
||||
// karma 可能被 normalize 兜底为 0,也可能 NaN(未定义行为)——只验证不崩
|
||||
expect(w.state.family).toBeDefined()
|
||||
})
|
||||
|
||||
it('karma 年首衰减 10%', () => {
|
||||
const w = World.create({ seed: 'karma-test-3', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
w.state.family.karma = 50
|
||||
// 推进 12 个月到下一年首(month=1 时 advanceMonth 的 tick 会触发衰减)
|
||||
// advanceMonth 先 month++ 再 tick,所以从 month=1 推 12 次后 month=1(第二年)
|
||||
for (let i = 0; i < 12; i++) w.advanceMonth()
|
||||
// 此时 month=1, year=2, tick 中 WorldSim 会触发 karma 衰减
|
||||
expect(w.state.family.karma).toBeLessThan(50)
|
||||
expect(w.state.family.karma).toBeGreaterThan(40) // 50 - 50*0.1 = 45
|
||||
})
|
||||
|
||||
it('指纹中包含 karma', () => {
|
||||
const w1 = World.create({ seed: 'karma-fp-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const w2 = World.create({ seed: 'karma-fp-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
w2.state.family.karma = 10
|
||||
const fp1 = stateFingerprint(w1.state)
|
||||
const fp2 = stateFingerprint(w2.state)
|
||||
expect(fp1).not.toBe(fp2) // karma 不同 → 指纹不同
|
||||
})
|
||||
|
||||
it('karma 影响渡劫成功率', () => {
|
||||
const w = World.create({ seed: 'karma-trib-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
c.realm = { major: 'nascent', minor: 0 }
|
||||
w.state.family.karma = 0
|
||||
const base = perTribChance(w, c)
|
||||
w.state.family.karma = 50
|
||||
const high = perTribChance(w, c)
|
||||
w.state.family.karma = -30
|
||||
const low = perTribChance(w, c)
|
||||
expect(high).toBeGreaterThan(base)
|
||||
expect(low).toBeLessThan(base)
|
||||
})
|
||||
})
|
||||
|
||||
describe('0.1.37 飞升试炼', () => {
|
||||
it('飞升试炼事件定义存在', () => {
|
||||
const w = World.create({ seed: 'ascension-test-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
const heartEvent = findEvent(ascensionEventId(c.id, 'heart'), w)
|
||||
const heavenEvent = findEvent(ascensionEventId(c.id, 'heaven'), w)
|
||||
const ascendEvent = findEvent(ascensionEventId(c.id, 'ascend'), w)
|
||||
expect(heartEvent).toBeDefined()
|
||||
expect(heavenEvent).toBeDefined()
|
||||
expect(ascendEvent).toBeDefined()
|
||||
expect(heartEvent!.name).toBe('心魔试炼')
|
||||
expect(heavenEvent!.name).toBe('飞升天劫')
|
||||
expect(ascendEvent!.name).toBe('飞升抉择')
|
||||
})
|
||||
|
||||
it('飞升试炼事件有正确数量的选项', () => {
|
||||
const w = World.create({ seed: 'ascension-test-2', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
const heartEvent = findEvent(ascensionEventId(c.id, 'heart'), w)
|
||||
const heavenEvent = findEvent(ascensionEventId(c.id, 'heaven'), w)
|
||||
const ascendEvent = findEvent(ascensionEventId(c.id, 'ascend'), w)
|
||||
expect(heartEvent!.options.length).toBe(3) // 心境/功德/压制
|
||||
expect(heavenEvent!.options.length).toBe(3) // 硬渡/护法/压制
|
||||
expect(ascendEvent!.options.length).toBe(2) // 留仙/飞升
|
||||
})
|
||||
|
||||
it('飞升抉择 - 留仙:获得 fengxian 特质', () => {
|
||||
const w = World.create({ seed: 'ascension-stay-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
c.realm = { major: 'spirit', minor: 2 }
|
||||
c.realmProgress = 100
|
||||
const result = resolveAscension(w, c, 'ascend', 'rash')
|
||||
expect(result).toBe('stay')
|
||||
expect(c.traits).toContain('fengxian')
|
||||
expect(c.alive).toBe(true)
|
||||
})
|
||||
|
||||
it('飞升抉择 - 飞升:feishengCount 增加', () => {
|
||||
const w = World.create({ seed: 'ascension-fly-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
c.realm = { major: 'spirit', minor: 2 }
|
||||
c.realmProgress = 100
|
||||
const before = w.state.stats.feishengCount ?? 0
|
||||
const result = resolveAscension(w, c, 'ascend', 'guard')
|
||||
expect(result).toBe('ascended')
|
||||
expect(c.alive).toBe(false)
|
||||
expect(c.deathCause).toBe('飞升太虚')
|
||||
expect(w.state.stats.feishengCount).toBe(before + 1)
|
||||
})
|
||||
|
||||
it('飞升试炼心魔阶段 delay 返回 delayed', () => {
|
||||
const w = World.create({ seed: 'ascension-delay-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const c = Object.values(w.state.members)[0]!
|
||||
c.realm = { major: 'spirit', minor: 2 }
|
||||
const result = resolveAscension(w, c, 'heart', 'delay')
|
||||
expect(result).toBe('delayed')
|
||||
expect(c.tribDelayYear).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('0.1.37 世界奇观', () => {
|
||||
it('WONDER_NAMES 有 4 种奇观', () => {
|
||||
expect(Object.keys(WONDER_NAMES).length).toBe(4)
|
||||
expect(WONDER_NAMES.tide_surge).toBe('灵气涌潮')
|
||||
expect(WONDER_NAMES.ancient_ruin).toBe('古遗迹现身')
|
||||
expect(WONDER_NAMES.heaven_bless).toBe('天降祥瑞')
|
||||
expect(WONDER_NAMES.qi_wane).toBe('灵脉枯竭')
|
||||
})
|
||||
|
||||
it('WONDER_CHANCE > 0 且 < 1', () => {
|
||||
expect(WONDER_CHANCE).toBeGreaterThan(0)
|
||||
expect(WONDER_CHANCE).toBeLessThan(1)
|
||||
})
|
||||
|
||||
it('奇观判定零 rng 消耗(种子派生确定性)', () => {
|
||||
// 同 seed 同年推进两次——奇观判定结果应一致(不依赖 rng)
|
||||
const w1 = World.create({ seed: 'wonder-rng-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
const w2 = World.create({ seed: 'wonder-rng-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
// 推进 20 个月(到第二年)
|
||||
for (let i = 0; i < 20; i++) {
|
||||
w1.advanceMonth()
|
||||
w2.advanceMonth()
|
||||
}
|
||||
// 两个世界的 worldSim 状态应一致(种子派生确定性)
|
||||
const ws1 = w1.state.worldSim
|
||||
const ws2 = w2.state.worldSim
|
||||
if (ws1?.wonder && ws2?.wonder) {
|
||||
expect(ws1.wonder.kind).toBe(ws2.wonder.kind)
|
||||
}
|
||||
// 指纹一致(同 seed 同步推进 = 确定性)
|
||||
expect(stateFingerprint(w1.state)).toBe(stateFingerprint(w2.state))
|
||||
})
|
||||
|
||||
it('奇观状态在 WorldSimState 中持久化', () => {
|
||||
const w = World.create({ seed: 'wonder-persist-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
// 推进 15 个月到第二年
|
||||
for (let i = 0; i < 15; i++) w.advanceMonth()
|
||||
// wonder 字段应存在(可能 undefined 也可能有值,但字段必须有)
|
||||
const ws = w.state.worldSim
|
||||
expect(ws).toBeDefined()
|
||||
// 字段访问不报错
|
||||
expect(ws!.wonder === undefined || typeof ws!.wonder === 'object').toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('0.1.37 因果/NPC 姿态调制', () => {
|
||||
it('高 karma 影响 NPC 姿态权重(ally 权重提升)', () => {
|
||||
const w = World.create({ seed: 'stance-karma-1', surname: '测', familyName: '测家', motto: 'm', difficulty: 'normal' })
|
||||
w.state.family.karma = 50
|
||||
// 推进足够长时间让 NPC 姿态重估
|
||||
for (let i = 0; i < 120; i++) w.advanceMonth()
|
||||
// 高 karma 下盟友数应不少于低 karma(统计性验证——不要求绝对值,只要求不崩)
|
||||
const ws = w.state.worldSim
|
||||
expect(ws).toBeDefined()
|
||||
// NPC 动力学应存在
|
||||
expect(Object.keys(ws!.npcDyn ?? {}).length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user