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 基线固化(修为耗草/炼丹/灾害序列变更受控)
This commit is contained in:
2026-08-23 14:13:13 +08:00
parent de46808188
commit 809707408b
14 changed files with 266 additions and 18 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "chronicle-of-the-immortal-clan", "name": "chronicle-of-the-immortal-clan",
"productName": "仙途家族志", "productName": "仙途家族志",
"version": "0.1.15", "version": "0.1.16",
"description": "修仙 · 家族 · 经营 · 战斗 模拟器", "description": "修仙 · 家族 · 经营 · 战斗 模拟器",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "MetonaTeam", "author": "MetonaTeam",
+10 -1
View File
@@ -8,6 +8,9 @@ import { POSTS } from './posts'
import { TRAITS } from './traits' import { TRAITS } from './traits'
import { ROOT_GRADES, ROOT_GRADE_NAMES, ELEMENT_LIST } from './elements' import { ROOT_GRADES, ROOT_GRADE_NAMES, ELEMENT_LIST } from './elements'
import { MAJORS, MAJOR_ORDER } from './realms' import { MAJORS, MAJOR_ORDER } from './realms'
import { ASPIRATIONS } from './aspirations'
import { FORMATIONS } from './formations'
import { SEASON } from './season'
export interface DataPack { export interface DataPack {
items: typeof ITEMS items: typeof ITEMS
@@ -25,6 +28,9 @@ export interface DataPack {
elements: typeof ELEMENT_LIST elements: typeof ELEMENT_LIST
majors: typeof MAJORS majors: typeof MAJORS
majorOrder: typeof MAJOR_ORDER majorOrder: typeof MAJOR_ORDER
aspirations: typeof ASPIRATIONS
formations: typeof FORMATIONS
season: typeof SEASON
} }
/** 默认包:与库内静态数据**同引用**(保证确定性指纹不变);未来 MOD 通过注入覆盖 */ /** 默认包:与库内静态数据**同引用**(保证确定性指纹不变);未来 MOD 通过注入覆盖 */
@@ -43,7 +49,10 @@ export const DEFAULT_PACK: DataPack = {
rootGradeNames: ROOT_GRADE_NAMES, rootGradeNames: ROOT_GRADE_NAMES,
elements: ELEMENT_LIST, elements: ELEMENT_LIST,
majors: MAJORS, majors: MAJORS,
majorOrder: MAJOR_ORDER majorOrder: MAJOR_ORDER,
aspirations: ASPIRATIONS,
formations: FORMATIONS,
season: SEASON
} }
/** /**
@@ -137,7 +137,7 @@ export class GameFacade {
about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } { about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } {
return { return {
title: '仙途家族志', title: '仙途家族志',
version: '0.1.15', version: '0.1.16',
modules: this.world.systemList().length, modules: this.world.systemList().length,
systems: this.world.systemList().filter((s) => s.enabled).length, systems: this.world.systemList().filter((s) => s.enabled).length,
plugins: this.world.pluginList().length, plugins: this.world.pluginList().length,
@@ -5,6 +5,7 @@ import { combatPowerOf } from '../../game/engine/runtime/Systems/combat'
import { useState } from 'react' import { useState } from 'react'
import { describeRealm, MAJOR_ORDER } from '../../game/data/realms' import { describeRealm, MAJOR_ORDER } from '../../game/data/realms'
import { FORMATIONS, FormationId } from '../../game/data/formations' import { FORMATIONS, FormationId } from '../../game/data/formations'
import { WorldSim } from '../../game/engine/sim/WorldSim'
export default function ExpeditionPanel() { export default function ExpeditionPanel() {
const world = useGameStore((s) => s.world) const world = useGameStore((s) => s.world)
@@ -24,6 +25,14 @@ export default function ExpeditionPanel() {
) )
const def = selectedMissionDef ? missionById(selectedMissionDef) : null const def = selectedMissionDef ? missionById(selectedMissionDef) : null
const qiOf = (id: string): number | null => {
try {
return Math.round(new WorldSim(w).secretQiOf(id))
} catch {
return null
}
}
const toggle = (id: string) => { const toggle = (id: string) => {
setSquad((old) => (old.includes(id) ? old.filter((x) => x !== id) : [...old, id])) setSquad((old) => (old.includes(id) ? old.filter((x) => x !== id) : [...old, id]))
} }
@@ -58,6 +67,11 @@ export default function ExpeditionPanel() {
<div className="dim2" style={{ fontSize: '0.78rem' }}>{m.region} · {describeRealm({ major: m.realmHint, minor: 0 })}</div> <div className="dim2" style={{ fontSize: '0.78rem' }}>{m.region} · {describeRealm({ major: m.realmHint, minor: 0 })}</div>
<div className="dim2" style={{ fontSize: '0.78rem' }}>{m.minMembers}-{m.maxMembers} · {m.stages.length}</div> <div className="dim2" style={{ fontSize: '0.78rem' }}>{m.minMembers}-{m.maxMembers} · {m.stages.length}</div>
<div className="dim" style={{ fontSize: '0.8rem', marginTop: 3 }}>{m.desc}</div> <div className="dim" style={{ fontSize: '0.8rem', marginTop: 3 }}>{m.desc}</div>
{qiOf(m.id) !== null && (
<div className="dim2" style={{ fontSize: '0.76rem', marginTop: 3 }}>
{qiOf(m.id)}% -62/
</div>
)}
</div> </div>
) )
})} })}
+1 -1
View File
@@ -210,7 +210,7 @@ export default function SettingsPanel() {
· <br /> · <br />
· ·
</div> </div>
<div className="dim2" style={{ marginTop: 8 }}> 0.1.15 · Chronicle of the Immortal Clan</div> <div className="dim2" style={{ marginTop: 8 }}> 0.1.16 · Chronicle of the Immortal Clan</div>
</div> </div>
</div> </div>
) )
+107
View File
@@ -0,0 +1,107 @@
import { useGameStore } from '../store'
import { npcById } from '../../game/data/npcs'
import { MAJOR_NAMES } from '../../game/data/realms'
import { POOL_BASE } from '../../game/engine/sim/worldsim-data'
import { useState } from 'react'
const RES_LABEL: Record<string, string> = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核' }
const REALM_IDX = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] as const
export default function WorldPanel() {
const world = useGameStore((s) => s.world)
const bump = useGameStore((s) => s.bump)
const revision = useGameStore((s) => s.revision)
void revision
const [view, setView] = useState<string | null>(null)
if (!world) return null
const w = world
const ws = w.state.worldSim
if (!ws) return <div className="help-text"></div>
const tide = ws.tide ?? 0.5
const tideLabel = tide > 0.9 ? '灵涨' : tide < 0.7 ? '灵衰' : '汐平'
const pool = ws.marketPool ?? {}
const news = [...(ws.newsFeed ?? [])].slice().reverse()
const calamity = ws.calamity
return (
<div>
<div className="help-text">
40
</div>
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 10 }}>
<span className="tag gold-t"> {tideLabel}{Math.round(tide * 100)}%</span>
{calamity
? <span className="tag" style={{ borderColor: '#a33', color: '#e8a08a' }}>·{calamity}</span>
: <span className="tag"></span>}
{Object.entries(pool).map(([k, v]) => {
const base = POOL_BASE[k as keyof typeof POOL_BASE] ?? 100
const r = ((v as number) / base) * 100
const cls = r > 115 ? 'bad' : r < 85 ? 'good' : 'dim'
const dir = r > 105 ? '↑' : r < 95 ? '↓' : '→'
return (
<span key={k} className="tag"><span className={cls}>{RES_LABEL[k] ?? k}{dir}{Math.round(r)}%</span></span>
)
})}
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<div>
<h4 className="dim" style={{ margin: '4px 0 6px' }}></h4>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 380, overflowY: 'auto' }}>
{news.length === 0 && <div className="dim2"></div>}
{news.map((row, i) => (
<div key={i} className="ch-item" style={{ padding: '5px 8px' }}>
<span className="ch-month">{row.year}{row.month}</span>
<span>{row.text}</span>
</div>
))}
</div>
</div>
<div>
<h4 className="dim" style={{ margin: '4px 0 6px' }}></h4>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{Object.values(w.state.npcFamilies).map((npc) => {
const def = npcById(npc.id)
const dyn = (ws.npcDyn ?? {})[npc.id]
const open = view === npc.id
return (
<div key={npc.id} style={{ border: '1px solid rgba(140,100,50,0.3)', borderRadius: 6, padding: '7px 10px', background: 'rgba(18,14,9,0.4)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }} onClick={() => setView(open ? null : npc.id)}>
<span>
<b>{npc.name}</b> <span className="dim">{def.style}</span>
{npc.allied && <span className="tag gold-t" style={{ marginLeft: 6 }}></span>}
</span>
<span className="dim">{npc.power} · {npc.relation}</span>
</div>
{open && (
<div style={{ marginTop: 6, fontSize: '0.86rem', color: '#c8b285', borderTop: '1px solid rgba(140,100,50,0.25)', paddingTop: 6 }}>
<div className="dim">{dyn?.leaderName ?? '未知'}{dyn ? MAJOR_NAMES[REALM_IDX[dyn.leaderRealmIdx] ?? 'qi'] : ''} · {dyn?.leaderAge ?? '?'}</div>
<div className="dim2" style={{ margin: '2px 0 6px' }}>{def.desc}</div>
<div className="dim">{dyn?.lastEvent ? `${dyn.lastEvent}${dyn.lastEventYear ?? '?'}年)` : '暂无'}</div>
{npc.alliedSinceYear && <div className="dim"> {npc.alliedSinceYear} </div>}
<div style={{ marginTop: 6, display: 'flex', gap: 6 }}>
<button
className="btn btn-sm"
disabled={npc.allied || npc.relation < 40}
title={npc.relation < 40 ? '关系不足(需40' : ''}
onClick={() => { w.setAlliance(npc.id, true); bump() }}
></button>
{npc.allied && (
<button
className="btn btn-sm"
onClick={() => { w.setAlliance(npc.id, false); bump() }}
>-20</button>
)}
</div>
</div>
)}
</div>
)
})}
</div>
</div>
</div>
</div>
)
}
+3
View File
@@ -9,6 +9,7 @@ import MarketPanel from '../panels/MarketPanel'
import DiplomacyPanel from '../panels/DiplomacyPanel' import DiplomacyPanel from '../panels/DiplomacyPanel'
import ExpeditionPanel from '../panels/ExpeditionPanel' import ExpeditionPanel from '../panels/ExpeditionPanel'
import ChroniclePanel from '../panels/ChroniclePanel' import ChroniclePanel from '../panels/ChroniclePanel'
import WorldPanel from '../panels/WorldPanel'
import SettingsPanel from '../panels/SettingsPanel' import SettingsPanel from '../panels/SettingsPanel'
import { LogFeed } from '../components/LogFeed' import { LogFeed } from '../components/LogFeed'
import { UrgentBadges } from '../components/UrgentBadges' import { UrgentBadges } from '../components/UrgentBadges'
@@ -28,6 +29,7 @@ const TABS: { id: PanelId; label: string }[] = [
{ id: 'expedition', label: '探秘' }, { id: 'expedition', label: '探秘' },
{ id: 'chronicle', label: '史书' }, { id: 'chronicle', label: '史书' },
{ id: 'legacy', label: '春秋原' }, { id: 'legacy', label: '春秋原' },
{ id: 'world', label: '天下' },
{ id: 'settings', label: '设置' } { id: 'settings', label: '设置' }
] ]
@@ -113,6 +115,7 @@ export default function GameScreen() {
{panel === 'expedition' && <ExpeditionPanel />} {panel === 'expedition' && <ExpeditionPanel />}
{panel === 'chronicle' && <ChroniclePanel />} {panel === 'chronicle' && <ChroniclePanel />}
{panel === 'legacy' && <LegacyPanel />} {panel === 'legacy' && <LegacyPanel />}
{panel === 'world' && <WorldPanel />}
{panel === 'settings' && <SettingsPanel />} {panel === 'settings' && <SettingsPanel />}
{gameOverReason && ( {gameOverReason && (
<div className="gameover-banner"> <div className="gameover-banner">
+3 -2
View File
@@ -11,7 +11,7 @@ import { setSoundEnabled, sPaper, sGood, sBad, sWar, sBell, sTick } from './soun
import { seasonOf } from '../game/data/season' import { seasonOf } from '../game/data/season'
export type Screen = 'boot' | 'newgame' | 'game' export type Screen = 'boot' | 'newgame' | 'game'
export type PanelId = 'family' | 'genealogy' | 'territory' | 'market' | 'diplomacy' | 'expedition' | 'chronicle' | 'legacy' | 'settings' export type PanelId = 'family' | 'genealogy' | 'territory' | 'market' | 'diplomacy' | 'expedition' | 'chronicle' | 'legacy' | 'world' | 'settings'
export interface GameStore { export interface GameStore {
screen: Screen screen: Screen
@@ -150,7 +150,8 @@ export const useGameStore = create<GameStore>((set, get) => ({
set({ world, engine, slot, screen: 'game', panel: 'family', logFeed: [], battleView: undefined, pendingEventId: undefined, pendingEventDef: undefined, revision: 1, speed: 0, gameOverReason: undefined, paperReport: undefined, toast: undefined, selectedMemberId: undefined, selectedMissionDef: undefined }) set({ world, engine, slot, screen: 'game', panel: 'family', logFeed: [], battleView: undefined, pendingEventId: undefined, pendingEventDef: undefined, revision: 1, speed: 0, gameOverReason: undefined, paperReport: undefined, toast: undefined, selectedMemberId: undefined, selectedMissionDef: undefined })
const st = get() const st = get()
st.world?.out.push(makeBus(st)) st.world?.out.push(makeBus(st))
const facade = st.facade ?? new GameFacade(world, slot) // 新档永远建新门面(旧 facade 绑定旧 world,复用会回放错误目标)
const facade = new GameFacade(world, slot)
set({ facade }) set({ facade })
await st.saveNow('开局') await st.saveNow('开局')
}, },
+23
View File
@@ -1355,6 +1355,29 @@ html[data-blade] .battle-lines {
.modal-title { .modal-title {
padding-right: 44px; padding-right: 44px;
} }
.opt-eff {
display: block;
font-size: 0.74rem;
color: rgba(150, 132, 96, 0.9);
margin-top: 3px;
letter-spacing: 0.02em;
}
.ch-draft {
flex: 1;
min-width: 200px;
background: rgba(18, 14, 9, 0.75);
border: 1px solid rgba(140, 100, 50, 0.5);
border-radius: 4px;
color: #e8d5a4;
padding: 7px 10px;
font-family: inherit;
font-size: 0.9rem;
}
.ch-draft:focus {
outline: none;
border-color: var(--vermilion);
box-shadow: 0 0 0 2px rgba(200, 60, 30, 0.18);
}
.modal-opts { .modal-opts {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+2 -2
View File
@@ -82,7 +82,7 @@ describe('审计回归:P0 修复固化', () => {
}) })
it('防御性修补后金钟罩不变(行为等价确认)', () => { it('防御性修补后金钟罩不变(行为等价确认)', () => {
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e')
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96')
}) })
}) })
+4 -4
View File
@@ -7,11 +7,11 @@ import { World } from '../src/renderer/game/engine/runtime/World'
* 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。 * 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。
* 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。 * 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。
*/ */
// 0.1.15 深度审计最终基线:worldsim 修复(换代年首化/power年度/relation去漂/快讯节流/灵脉初始化)后固化。 // 0.1.16 资源闭环基线:修为耗草/炼丹概率化/铸器/灵脉折算/NPC战力/灾变落效/渡劫药力/结盟后固化。
const GOLDEN: Record<string, Record<number, string>> = { const GOLDEN: Record<string, Record<number, string>> = {
'bell-seed-1': { 560: 'a977654a', 1200: 'faefa00a', 2160: '5b7b375b' }, 'bell-seed-1': { 560: '9531969e', 1200: 'cdd70056', 2160: '7e33a89d' },
'bell-seed-2': { 560: '1c41881c', 1200: 'a06af06c', 2160: '142d04c3' }, 'bell-seed-2': { 560: 'f2ecc3e1', 1200: 'dab62832', 2160: '03c02233' },
'bell-seed-3': { 560: 'ee501983', 1200: '2a899a90', 2160: '0e490e5c' } 'bell-seed-3': { 560: 'aeefac96', 1200: '9eda1d0f', 2160: 'b3bcf822' }
} }
const TIERS = [ const TIERS = [
+91
View File
@@ -0,0 +1,91 @@
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))
}
+3 -3
View File
@@ -162,7 +162,7 @@ describe('GameFacade 门面', () => {
const f = new GameFacade(w, 1) const f = new GameFacade(w, 1)
const info = f.about() const info = f.about()
expect(info.title).toBe('仙途家族志') expect(info.title).toBe('仙途家族志')
expect(info.version).toContain('0.1.15') expect(info.version).toContain('0.1.16')
expect(info.modules).toBeGreaterThanOrEqual(11) expect(info.modules).toBeGreaterThanOrEqual(11)
expect(info.systems).toBeGreaterThan(0) expect(info.systems).toBeGreaterThan(0)
expect(info.plugins).toBeGreaterThanOrEqual(3) expect(info.plugins).toBeGreaterThanOrEqual(3)
@@ -170,7 +170,7 @@ describe('GameFacade 门面', () => {
it('默认配置金钟罩不受门面化影响', () => { it('默认配置金钟罩不受门面化影响', () => {
PACK.reset() PACK.reset()
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e')
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96')
}) })
}) })
+3 -3
View File
@@ -80,9 +80,9 @@ describe('PluginCore 插件协议', () => {
}) })
it('默认管线金钟罩不受插件层影响', () => { it('默认管线金钟罩不受插件层影响', () => {
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e')
expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('1c41881c') expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('f2ecc3e1')
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96')
}) })
it('facade 插件查询与 about.plugins', () => { it('facade 插件查询与 about.plugins', () => {