From 809707408b36c94ec0afea2973f0a37966d3b7b4 Mon Sep 17 00:00:00 2001 From: thzxx <1440196015@qq.com> Date: Sun, 23 Aug 2026 14:13:13 +0800 Subject: [PATCH] =?UTF-8?q?v0.1.16:=20=E8=B5=84=E6=BA=90=E9=97=AD=E7=8E=AF?= =?UTF-8?q?=20=C3=97=20=E4=B8=96=E6=83=85=E5=85=A5=E5=91=B3=EF=BC=88A+B+C?= =?UTF-8?q?=20=E5=85=A8=E5=A5=97=E9=A4=90=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【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 基线固化(修为耗草/炼丹/灾害序列变更受控) --- package.json | 2 +- src/renderer/game/data/registry.ts | 11 +- src/renderer/game/engine/runtime/ApiFacade.ts | 2 +- src/renderer/ui/panels/ExpeditionPanel.tsx | 14 +++ src/renderer/ui/panels/SettingsPanel.tsx | 2 +- src/renderer/ui/panels/WorldPanel.tsx | 107 ++++++++++++++++++ src/renderer/ui/screens/GameScreen.tsx | 3 + src/renderer/ui/store.ts | 5 +- src/renderer/ui/styles.css | 23 ++++ tests/audit-regression.test.ts | 4 +- tests/clock.test.ts | 8 +- tests/economy-loop-0.1.16.test.ts | 91 +++++++++++++++ tests/facade-registry.test.ts | 6 +- tests/plugin.test.ts | 6 +- 14 files changed, 266 insertions(+), 18 deletions(-) create mode 100644 src/renderer/ui/panels/WorldPanel.tsx create mode 100644 tests/economy-loop-0.1.16.test.ts diff --git a/package.json b/package.json index d6f113a..47f3a61 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "chronicle-of-the-immortal-clan", "productName": "仙途家族志", - "version": "0.1.15", + "version": "0.1.16", "description": "修仙 · 家族 · 经营 · 战斗 模拟器", "main": "./out/main/index.js", "author": "MetonaTeam", diff --git a/src/renderer/game/data/registry.ts b/src/renderer/game/data/registry.ts index 79d5443..a2dcf82 100644 --- a/src/renderer/game/data/registry.ts +++ b/src/renderer/game/data/registry.ts @@ -8,6 +8,9 @@ import { POSTS } from './posts' import { TRAITS } from './traits' import { ROOT_GRADES, ROOT_GRADE_NAMES, ELEMENT_LIST } from './elements' import { MAJORS, MAJOR_ORDER } from './realms' +import { ASPIRATIONS } from './aspirations' +import { FORMATIONS } from './formations' +import { SEASON } from './season' export interface DataPack { items: typeof ITEMS @@ -25,6 +28,9 @@ export interface DataPack { elements: typeof ELEMENT_LIST majors: typeof MAJORS majorOrder: typeof MAJOR_ORDER + aspirations: typeof ASPIRATIONS + formations: typeof FORMATIONS + season: typeof SEASON } /** 默认包:与库内静态数据**同引用**(保证确定性指纹不变);未来 MOD 通过注入覆盖 */ @@ -43,7 +49,10 @@ export const DEFAULT_PACK: DataPack = { rootGradeNames: ROOT_GRADE_NAMES, elements: ELEMENT_LIST, majors: MAJORS, - majorOrder: MAJOR_ORDER + majorOrder: MAJOR_ORDER, + aspirations: ASPIRATIONS, + formations: FORMATIONS, + season: SEASON } /** diff --git a/src/renderer/game/engine/runtime/ApiFacade.ts b/src/renderer/game/engine/runtime/ApiFacade.ts index cd8fe9a..194dbbf 100644 --- a/src/renderer/game/engine/runtime/ApiFacade.ts +++ b/src/renderer/game/engine/runtime/ApiFacade.ts @@ -137,7 +137,7 @@ export class GameFacade { about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } { return { title: '仙途家族志', - version: '0.1.15', + version: '0.1.16', modules: this.world.systemList().length, systems: this.world.systemList().filter((s) => s.enabled).length, plugins: this.world.pluginList().length, diff --git a/src/renderer/ui/panels/ExpeditionPanel.tsx b/src/renderer/ui/panels/ExpeditionPanel.tsx index 91c6401..c38772c 100644 --- a/src/renderer/ui/panels/ExpeditionPanel.tsx +++ b/src/renderer/ui/panels/ExpeditionPanel.tsx @@ -5,6 +5,7 @@ import { combatPowerOf } from '../../game/engine/runtime/Systems/combat' import { useState } from 'react' import { describeRealm, MAJOR_ORDER } from '../../game/data/realms' import { FORMATIONS, FormationId } from '../../game/data/formations' +import { WorldSim } from '../../game/engine/sim/WorldSim' export default function ExpeditionPanel() { const world = useGameStore((s) => s.world) @@ -24,6 +25,14 @@ export default function ExpeditionPanel() { ) 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) => { setSquad((old) => (old.includes(id) ? old.filter((x) => x !== id) : [...old, id])) } @@ -58,6 +67,11 @@ export default function ExpeditionPanel() {
{m.region} · 适合{describeRealm({ major: m.realmHint, minor: 0 })}
{m.minMembers}-{m.maxMembers}人 · {m.stages.length}段
{m.desc}
+ {qiOf(m.id) !== null && ( +
+ 灵脉 {qiOf(m.id)}% (探索后-6,回复2/月;灵气越足所获越丰) +
+ )} ) })} diff --git a/src/renderer/ui/panels/SettingsPanel.tsx b/src/renderer/ui/panels/SettingsPanel.tsx index 62632e7..9feabd1 100644 --- a/src/renderer/ui/panels/SettingsPanel.tsx +++ b/src/renderer/ui/panels/SettingsPanel.tsx @@ -210,7 +210,7 @@ export default function SettingsPanel() { · 「外交」与四邻结好联姻;仇雠之族隔岁来犯,打得赢名望大涨,打不赢蚀钱伤丁。
· 「史书」自动记述繁华与凋零——百年之后,后人翻开这一卷家族志,见代代薪火、历历雪泥。 -
版本 0.1.15 · Chronicle of the Immortal Clan
+
版本 0.1.16 · Chronicle of the Immortal Clan
) diff --git a/src/renderer/ui/panels/WorldPanel.tsx b/src/renderer/ui/panels/WorldPanel.tsx new file mode 100644 index 0000000..bce5742 --- /dev/null +++ b/src/renderer/ui/panels/WorldPanel.tsx @@ -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 = { 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(null) + if (!world) return null + const w = world + const ws = w.state.worldSim + if (!ws) return
天机未显……(世界演化尚未展开)
+ + 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 ( +
+
+ 天下时局尽览:行市起落、宗主更替、灾年吉凶——皆随世界自演化而来。 + 结盟后岁差不降,每年另有盟利奉上(需关系≥40)。 +
+
+ 灵潮 {tideLabel}({Math.round(tide * 100)}%) + {calamity + ? 灾年·{calamity}(灵植减产,市价将行) + : 风调雨顺} + {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 ( + {RES_LABEL[k] ?? k}{dir}{Math.round(r)}% + ) + })} +
+
+
+

天下快讯

+
+ {news.length === 0 &&
风平浪静,尚无消息。
} + {news.map((row, i) => ( +
+ {row.year}年{row.month}月 + {row.text} +
+ ))} +
+
+
+

群雄谱(点开宗主详情)

+
+ {Object.values(w.state.npcFamilies).map((npc) => { + const def = npcById(npc.id) + const dyn = (ws.npcDyn ?? {})[npc.id] + const open = view === npc.id + return ( +
+
setView(open ? null : npc.id)}> + + {npc.name} {def.style} + {npc.allied && } + + 势力{npc.power} · 关系{npc.relation} +
+ {open && ( +
+
宗主:{dyn?.leaderName ?? '未知'}({dyn ? MAJOR_NAMES[REALM_IDX[dyn.leaderRealmIdx] ?? 'qi'] : ''} · {dyn?.leaderAge ?? '?'}岁)
+
{def.desc}
+
新近之事:{dyn?.lastEvent ? `${dyn.lastEvent}(${dyn.lastEventYear ?? '?'}年)` : '暂无'}
+ {npc.alliedSinceYear &&
同盟自 {npc.alliedSinceYear} 年
} +
+ + {npc.allied && ( + + )} +
+
+ )} +
+ ) + })} +
+
+
+
+ ) +} diff --git a/src/renderer/ui/screens/GameScreen.tsx b/src/renderer/ui/screens/GameScreen.tsx index c2e2e38..8b51f68 100644 --- a/src/renderer/ui/screens/GameScreen.tsx +++ b/src/renderer/ui/screens/GameScreen.tsx @@ -9,6 +9,7 @@ import MarketPanel from '../panels/MarketPanel' import DiplomacyPanel from '../panels/DiplomacyPanel' import ExpeditionPanel from '../panels/ExpeditionPanel' import ChroniclePanel from '../panels/ChroniclePanel' +import WorldPanel from '../panels/WorldPanel' import SettingsPanel from '../panels/SettingsPanel' import { LogFeed } from '../components/LogFeed' import { UrgentBadges } from '../components/UrgentBadges' @@ -28,6 +29,7 @@ const TABS: { id: PanelId; label: string }[] = [ { id: 'expedition', label: '探秘' }, { id: 'chronicle', label: '史书' }, { id: 'legacy', label: '春秋原' }, + { id: 'world', label: '天下' }, { id: 'settings', label: '设置' } ] @@ -113,6 +115,7 @@ export default function GameScreen() { {panel === 'expedition' && } {panel === 'chronicle' && } {panel === 'legacy' && } + {panel === 'world' && } {panel === 'settings' && } {gameOverReason && (
diff --git a/src/renderer/ui/store.ts b/src/renderer/ui/store.ts index 0c19eea..c3fd650 100644 --- a/src/renderer/ui/store.ts +++ b/src/renderer/ui/store.ts @@ -11,7 +11,7 @@ import { setSoundEnabled, sPaper, sGood, sBad, sWar, sBell, sTick } from './soun import { seasonOf } from '../game/data/season' 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 { screen: Screen @@ -150,7 +150,8 @@ export const useGameStore = create((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 }) const st = get() st.world?.out.push(makeBus(st)) - const facade = st.facade ?? new GameFacade(world, slot) + // 新档永远建新门面(旧 facade 绑定旧 world,复用会回放错误目标) + const facade = new GameFacade(world, slot) set({ facade }) await st.saveNow('开局') }, diff --git a/src/renderer/ui/styles.css b/src/renderer/ui/styles.css index d2a700c..3de41f6 100644 --- a/src/renderer/ui/styles.css +++ b/src/renderer/ui/styles.css @@ -1355,6 +1355,29 @@ html[data-blade] .battle-lines { .modal-title { 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 { display: flex; flex-direction: column; diff --git a/tests/audit-regression.test.ts b/tests/audit-regression.test.ts index b493950..483c01e 100644 --- a/tests/audit-regression.test.ts +++ b/tests/audit-regression.test.ts @@ -82,7 +82,7 @@ describe('审计回归:P0 修复固化', () => { }) it('防御性修补后金钟罩不变(行为等价确认)', () => { - expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96') }) }) diff --git a/tests/clock.test.ts b/tests/clock.test.ts index d741544..ace8263 100644 --- a/tests/clock.test.ts +++ b/tests/clock.test.ts @@ -7,11 +7,11 @@ import { World } from '../src/renderer/game/engine/runtime/World' * 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。 * 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。 */ -// 0.1.15 深度审计最终基线:worldsim 修复(换代年首化/power年度/relation去漂/快讯节流/灵脉初始化)后固化。 +// 0.1.16 资源闭环基线:修为耗草/炼丹概率化/铸器/灵脉折算/NPC战力/灾变落效/渡劫药力/结盟后固化。 const GOLDEN: Record> = { - 'bell-seed-1': { 560: 'a977654a', 1200: 'faefa00a', 2160: '5b7b375b' }, - 'bell-seed-2': { 560: '1c41881c', 1200: 'a06af06c', 2160: '142d04c3' }, - 'bell-seed-3': { 560: 'ee501983', 1200: '2a899a90', 2160: '0e490e5c' } + 'bell-seed-1': { 560: '9531969e', 1200: 'cdd70056', 2160: '7e33a89d' }, + 'bell-seed-2': { 560: 'f2ecc3e1', 1200: 'dab62832', 2160: '03c02233' }, + 'bell-seed-3': { 560: 'aeefac96', 1200: '9eda1d0f', 2160: 'b3bcf822' } } const TIERS = [ diff --git a/tests/economy-loop-0.1.16.test.ts b/tests/economy-loop-0.1.16.test.ts new file mode 100644 index 0000000..9f978bf --- /dev/null +++ b/tests/economy-loop-0.1.16.test.ts @@ -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)) +} diff --git a/tests/facade-registry.test.ts b/tests/facade-registry.test.ts index 09f7bbc..4743c6b 100644 --- a/tests/facade-registry.test.ts +++ b/tests/facade-registry.test.ts @@ -162,7 +162,7 @@ describe('GameFacade 门面', () => { const f = new GameFacade(w, 1) const info = f.about() 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.systems).toBeGreaterThan(0) expect(info.plugins).toBeGreaterThanOrEqual(3) @@ -170,7 +170,7 @@ describe('GameFacade 门面', () => { it('默认配置金钟罩不受门面化影响', () => { PACK.reset() - expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96') }) }) diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index e469042..e8abc9e 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -80,9 +80,9 @@ describe('PluginCore 插件协议', () => { }) it('默认管线金钟罩不受插件层影响', () => { - expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a977654a') - expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('1c41881c') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ee501983') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e') + expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('f2ecc3e1') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96') }) it('facade 插件查询与 about.plugins', () => {