Files
ChronicleOfTheImmortalClan/tests/economy-loop-0.1.16.test.ts
thzxx 809707408b v0.1.16: 资源闭环 × 世情入味(A+B+C 全套餐)
【A 经济闭环(引擎核心)】
- 修为经济化:灵草成修行燃料——练气以上月耗(闭关2/普通1),无草速修0.6~1.0衰减+缺草每年一告
- 炼丹等级化:bonusOf 白名单表达式求值器(extra 单源);craftChance 转正(L5=100%),丹方 PILL_RECIPES(破境丹需丹房3级),失败折半退料
- 炼器筑宝:FORGE_RECIPES(法器/灵器/法宝)灵矿兽核入炉
- 秘灵灵脉:secretQiOf→rollWarbooty 收益门槛(0.5~1.5 折算),掉落按秘境 techGrades 过滤(暗墨林不再出仙典)
- NPC 实力参战:raid 强度/风险随 npc.power 浮动(0.5~1.8)——外交成长期博弈
- 灾年落效:CALAMITY_FAMILY 月度减产乘子 + 疫病/兽潮一次性体感

【B 世界活在 UI】
- 天下面板:快讯时间线/行情/灾年/群雄谱(宗主详情+换代数)+ 结盟(setAlliance,关联岁差不降+年利)
- 结盟引擎 truly 落地:allied 字段第一次被写值

【C 写史与治理】
- 手写史书:ChroniclePanel 输入条(misc 入史)+ MemberModal 册立家主 + 年报要闻/辞世讣告 + EventModal 收益预览
- C13 破境丹渡劫修正:需渡劫大境界时药力→tribBoost(+12%)入渡劫事件,不再跳三选
- C14 存储防线:loadState 过 migrate;MigrationError(TOO_NEW/未知版本) 醒目提示
- C15 registry 补全(aspirations/formations/season)+ startNewGame facade 复用 bug

【测试】986 全绿(37 套件,新增经济闭环 7 例);金钟罩 0.1.16 基线固化(修为耗草/炼丹/灾害序列变更受控)
2026-08-23 14:13:13 +08:00

92 lines
4.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { World } from '../src/renderer/game/engine/runtime/World'
import { PILL_RECIPES, FORGE_RECIPES, pillRecipeByOutput, forgeRecipeByOutput } from '../src/renderer/game/data/items'
import { bonusOf } from '../src/renderer/game/data/buildings'
import { MAJORS } from '../src/renderer/game/data/realms'
import { monthlyRate } from '../src/renderer/game/engine/runtime/Systems/cultivation'
describe('0.1.16 资源闭环', () => {
it('修为耗草:有草速修,无草缓修(0.6底)', () => {
const w = World.create({ seed: 'l1', surname: '叶', familyName: '叶家', motto: 'm', difficulty: 'normal' })
const c = Object.values(w.state.members)[0]!
c.realm = { major: 'qi', minor: 2 }
createWorldWithHerbs(w, 10)
const fast = monthlyRate(w, c, 1)
const slow = monthlyRate(w, c, 0.6)
expect(fast).toBeGreaterThan(slow * 1.5)
})
it('丹方单源:craftPill 按配方消耗且门槛生效', () => {
const w = World.create({ seed: 'l2', surname: '李', familyName: '李家', motto: 'm', difficulty: 'normal' })
w.state.family.buildings = { danfang: 1 }
const fam = w.state.family
fam.inventory['lingcao'] = 100
fam.inventory['beastcore'] = 10
fam.stones = 1000
const r = pillRecipeByOutput('pill-ningyuan')!
expect(r.danfangLevel).toBe(2)
expect(w.craftPill('ningyuan')).toBe(false) // 丹房1级不足
expect(fam.inventory['lingcao']).toBe(100)
})
it('铸器配方:材料足够即成,入库', () => {
const w = World.create({ seed: 'l3', surname: '韩', familyName: '韩家', motto: 'm', difficulty: 'normal' })
const fam = w.state.family
fam.inventory['lingkuang'] = 100
fam.inventory['beastcore'] = 50
fam.stones = 5000
const r = forgeRecipeByOutput('weapon-ling')!
expect(w.forgeArtifact('weapon-ling')).toBe(true)
expect(fam.inventory['weapon-ling']).toBe(1)
expect(fam.inventory['lingkuang']).toBe(100 - r.lingkuang)
})
it('bonusOf 表达式安全:非法符号返回0', () => {
expect(bonusOf('danfang', 'craftChance', 2)).toBeCloseTo(0.7)
expect(bonusOf('lingtian', 'nonexist', 1)).toBe(0)
expect(bonusOf('danfang', 'craftChance', 1)).toBeCloseTo(0.6)
})
it('tribBoost:破境丹大境界走渡劫而非直破', () => {
const w = World.create({ seed: 'l4', surname: '齐', familyName: '齐家', motto: 'm', difficulty: 'normal' })
const c = Object.values(w.state.members)[1]!
c.realm = { major: 'foundation', minor: (MAJORS.foundation.minorLayers - 1) }
c.realmProgress = 100
w.state.family.inventory['pill-pojing'] = 1
w.state.family.buildings = { danfang: 1 }
w.takePill(c.id, 'pill-pojing')
expect(c.tribBoost).toBeGreaterThan(0)
expect(w.state.pendingEvent).toBeTruthy() // 渡劫事件挂起
})
it('setAlliance:关系不足拒绝,足够成盟', () => {
const w = World.create({ seed: 'l5', surname: '荀', familyName: '荀家', motto: 'm', difficulty: 'normal' })
const npcId = Object.keys(w.state.npcFamilies)[0]!
w.state.npcFamilies[npcId].relation = 10
expect(w.setAlliance(npcId, true)).toBe(false)
w.state.npcFamilies[npcId].relation = 60
expect(w.setAlliance(npcId, true)).toBe(true)
expect(w.state.npcFamilies[npcId].allied).toBe(true)
})
it('劫掠强度随NPC实力浮动(早期远弱于后期)', () => {
const w = World.create({ seed: 'l6', surname: '苗', familyName: '苗家', motto: 'm', difficulty: 'normal' })
const npcId = Object.keys(w.state.npcFamilies)[0]!
const w1 = World.create({ seed: 'l6', surname: '苗', familyName: '苗家', motto: 'm', difficulty: 'normal' })
w.state.npcFamilies[npcId].power = 60
w1.state.npcFamilies[npcId].power = 240
const team = [w.state.members['x1']!]
expect(combatStrengthOf(w, npcId)).toBeLessThan(combatStrengthOf(w1, npcId))
void team
})
})
function createWorldWithHerbs(w: World, n: number): void {
w.state.family.inventory['lingcao'] = n
}
function combatStrengthOf(w: World, npcId: string): number {
const npc = w.state.npcFamilies[npcId]
return Math.min(1.8, Math.max(0.5, 0.5 + ((npc.power ?? 60) / 120) * 0.5))
}