v0.1.27: 活水长流——生灭盘活 + 修为回环 + 世鉴 + MOD 生态闭环(1054 全绿)

【世界模型(180 年实测驱动的机制修复)】
- 生灭可达化:探子模拟实测证实——原覆灭/新贵 183 年零触发(力量通胀+景气自校正);
  修复:脆弱判定放宽(power<110 弱即衰 / warLosses≥2 战损衰)+ yearGrowth 按 era 调制
  (盛世 0.7/平世 1/乱世 -0.8/末法 -1.4——乱世真换血)+ 顶峰衰(power≥700 年首 8% 回落)
- 修为↔市场回环:修行月耗灵草 tradeSettle(-n) 抽走世界池(最后半环闭合)
- 世鉴 sagaAnnals:50/100 年宏观对比(开局 vs 现在/era 路径/最强家更替)——独立数组零漂
- 世界数值 MOD 口:worldNum 聚合读(灾年概率/供需/贸易/新贵/秘灵 5 项可覆写可复位)
- 世界面板显性化:世温/景气/衰微倒计时;修复 WorldPanel 救济按钮 rng 单源违规

【插件/MOD】
- 依赖拓扑自动安装(registry 内先装、栈深 3);MOD source 挂载 + exportModBundle
  + IPC mods:write + 设置页「导出当前 MOD 集」

【测试】1054 全绿(47 套件,+7:乱世压力覆灭/顶峰衰/era 负增/世鉴/数值口/导出/拓扑);
【金钟罩】0.1.27 基线重算(双基线 18 值,生灭/回环受控变更);build 通过
This commit is contained in:
2026-08-23 17:02:46 +08:00
parent d941df5c3d
commit 8b6f2f7189
23 changed files with 261 additions and 41 deletions
+38 -8
View File
@@ -1,6 +1,6 @@
/** WorldSim —— 世界自进化引擎(game/engine/sim/WorldSim.ts */
import { World } from '../runtime/World'
import { WorldSimState, NpcDynamics, WORLDSIM, ERA_CONF, ERA_DURA, makeWorldSimState, NpcStance, REGION_TIDE, calamityNames, calamityFamilyOf, calamityEffectOf, CalamityName, POOL_BASE } from './worldsim-data'
import { WorldSimState, NpcDynamics, WORLDSIM, ERA_CONF, ERA_DURA, makeWorldSimState, NpcStance, REGION_TIDE, calamityNames, calamityFamilyOf, calamityEffectOf, CalamityName, POOL_BASE, worldNum } from './worldsim-data'
import { pack } from '../../data/registry'
import { ITEMS } from '../../data/items'
import { npcById, getNpcDefs, registerNpcDef, unregisterNpcDef } from '../../data/npcs'
@@ -40,7 +40,7 @@ export class WorldSim {
)
// 秘境灵气(自动恢复 + 探索消耗由 missions 在探索时扣;灾年灵脉闭锁 0.5 恢复)
const qiRecover = s.calamity ? WORLDSIM.secretRecover * 0.5 : s.tide > 0.85 ? WORLDSIM.secretRecoverHi : WORLDSIM.secretRecover
const qiRecover = s.calamity ? worldNum('secretRecover') * 0.5 : s.tide > 0.85 ? worldNum('secretRecoverHi') : worldNum('secretRecover')
for (const key of Object.keys(s.secretQi)) {
s.secretQi[key] = clamp(s.secretQi[key] + qiRecover - (s.secretQi[key] > 70 ? 2 : 0), 0, 100)
}
@@ -87,7 +87,7 @@ export class WorldSim {
// ---- D. 灾年(年签一掷 + 持续渐退;B12) ----
// 剩数递减
if ((s.calamityLeft ?? 0) > 0) s.calamityLeft = (s.calamityLeft ?? 0) - 1
const calamityP = WORLDSIM.calamityChance * ERA_CONF[s.era ?? 'pingshi'].calamityMult
const calamityP = worldNum('calamityChance') * ERA_CONF[s.era ?? 'pingshi'].calamityMult
if (this.w.state.month === 1 && rng.chance(calamityP)) {
const cl = rng.pick(calamityNames())
s.calamity = cl
@@ -138,6 +138,24 @@ export class WorldSim {
if (distAge > 18 && !this.w.state.npcFamilies[s.distress.id]?.allied) s.distress = undefined
}
// ---- D1.6 世鉴(50/100 年:开局 vs 现在——世界变过多少) ----
if (this.w.state.month === 1 && (this.w.state.year % 50 === 0 || this.w.state.year % 100 === 0)) {
const y = this.w.state.year
const wg = this.w.state.worldGen
const initial = wg?.npcCount ?? 0
const now = Object.keys(this.w.state.npcFamilies).length
const topNow = Math.max(...Object.values(this.w.state.npcFamilies).map((n) => n.power))
const topNowName = Object.values(this.w.state.npcFamilies).sort((a, b) => b.power - a.power)[0]?.name ?? '?'
const eraName = ERA_CONF[s.era ?? 'pingshi'].name
const kind = y % 100 === 0 ? 'centennial' : 'quinquagenary'
const text = `${y}年世鉴:天下历经${eraName}之世,${
now < initial ? `原有${initial}家凋零至${now}家——大浪淘沙` : now > initial ? `新版图扩展至${now}家——后浪迭起` : `格局维持${now}家之衡`
};当世最强为 ${topNowName}${topNow})。`
if (!s.sagaAnnals) s.sagaAnnals = []
s.sagaAnnals.push({ year: y, kind, text })
this.w.log('info', `【世鉴】${text}`)
}
// ---- D2. 天下十年一鉴(史官综述;入 newsFeed 展示 + 独立留档) ----
if (this.w.state.month === 1 && this.w.state.year % 10 === 0) {
const text = decadeChronicle(s, this.w.state.year)
@@ -329,8 +347,8 @@ function worldBreath(w: World, s: WorldSimState): void {
// 超卖潮:池过剩 > 1.8×base 时世界产能自发回调(谷贱伤农)
const saturated = cur > base * 1.8
if (saturated) overflow = true
const supply = base * WORLDSIM.worldSupplyRate * supplySpan * eraSupply * (saturated ? 0.7 : 1)
const demand = base * WORLDSIM.worldDemandRate * eraDemand
const supply = base * worldNum('worldSupplyRate') * supplySpan * eraSupply * (saturated ? 0.7 : 1)
const demand = base * worldNum('worldDemandRate') * eraDemand
s.marketPool[id] = clamp(cur + supply - demand, base * 0.12, base * WORLDSIM.tradeCeilPct)
}
// 超卖播报(年一次)——玩家能看到"别把市场玩崩"
@@ -357,7 +375,7 @@ function npcTrade(w: World, s: WorldSimState, noise: number): void {
const tradeMult = stance2 === 'expand' ? 1.3 : stance2 === 'endure' ? 0.7 : 1
// W3 区域灵势:各 era 修正(e.g. 乱世北岳玄影峰灵衰 0.08)
const regionMod = 1 + (REGION_TIDE[id]?.[(s.era ?? 'pingshi') as 'shengshi'] ?? 0)
const rate = WORLDSIM.npcTradeRate * (isCalamity ? 0.7 : 1) * tradeMult * regionMod
const rate = worldNum('npcTradeRate') * (isCalamity ? 0.7 : 1) * tradeMult * regionMod
// 景气驱动(1-2):入不敷出则衰、仓廪常足则旺
let prosperity = dyn.prosperity ?? 50
// 卖:NPC 抛货 → 池增;有货自给,景气微涨
@@ -458,7 +476,10 @@ function evolveNpc(w: World, s: WorldSimState, noise: number): void {
}
// W1 生灭:衰微累积/覆灭附庸/新贵补位(年首)
if (w.state.month === 1) {
if (!npc.allied && npc.power < 58 && (dyn.prosperity ?? 50) < 38) {
// 残衰减判定(0.1.27 可达化):弱即衰(盛世会被增长拉回不灭、乱世真灭——世道越乱换血越烈)
const fragile = npc.power < 110
const battleWorn = (dyn.warLosses ?? 0) >= 2 && npc.power < 160
if (!npc.allied && (fragile || battleWorn)) {
dyn.declineYears = (dyn.declineYears ?? 0) + 1
if (dyn.declineYears >= 8) {
// 覆灭:最强邻家分食
@@ -483,10 +504,17 @@ function evolveNpc(w: World, s: WorldSimState, noise: number): void {
// 新贵补位:家数 < 开局目标(4~8 的种子格)且几率(乱世更频)——世界会新生
const aliveCount = Object.keys(w.state.npcFamilies).length
const cap = Math.min(8, w.state.worldGen?.npcCount ?? 4)
if (aliveCount < cap && w.rng.chance(WORLDSIM.greatNewbornChance * (s.era === 'luanshi' ? 2 : 1))) {
if (aliveCount < cap && w.rng.chance(worldNum('greatNewbornChance') * (s.era === 'luanshi' ? 2 : 1))) {
spawnNewbornDynasty(w, s)
}
}
// 顶峰衰(0.1.27):绝顶不可久踞——power>=700 年首回落(豪强被制衡)
if (w.state.month === 1 && npc.power >= 700 && w.rng.chance(0.08)) {
npc.power = Math.round(npc.power * 0.9)
dyn.lastEvent = '顶峰回落'
dyn.lastEventYear = w.state.year
s.newsFeed.push({ year: w.state.year, month: 1, src: '史官', text: `${def.name} 盛极而衰,锋芒微敛。`, kind: 'annal' })
}
// 关系漂移:年首各 ±5(邻近的讲合、世仇的愈深)
for (const oid of ids) {
if (oid === id) continue
@@ -526,6 +554,8 @@ function evolveNpc(w: World, s: WorldSimState, noise: number): void {
const loser = winner === npc ? foe : npc
winner.power = Math.min(900, Math.round(winner.power * 1.06))
loser.power = Math.max(52, Math.round(loser.power * 0.82))
;(s.npcDyn[loser.id] as { warLosses?: number }).warLosses = ((s.npcDyn[loser.id] as { warLosses?: number }).warLosses ?? 0) + 1
;(s.npcDyn[winner.id] as { warLosses?: number }).warLosses = 0
// 打残广播:天下敢弱必胜之——邻家对败者关系趋冷、对胜者暗生敬畏
for (const [oid, otherDyn] of Object.entries(s.npcDyn)) {
if (oid === loser.id) continue