v0.1.34b: 插件新卡+MOD 功法效果口/patch 层/冲突日志

【插件】SYSTEM_DEFS + worldview 观察卡/tech 功法卡(14);PluginContext + addTechniqueDef(全字段入 PACK 数组, grade≤3 自动池) + onWorldSimTick 观察回调(零 rng 零时序——proxy 追踪注销);
【MOD】ModPackData.techniques 补 4 流派效果字段;新增 patch 层(target 数值 mult 0.5~2/add——install 应用 uninstall 复原,validate 规则进预检);同 id 功法冲突 warn 日志
【门面】Runtime 未动;卸载回滚栈 patchUndo
This commit is contained in:
2026-08-23 22:09:38 +08:00
parent 3fb64f6e78
commit a7fe166e85
6 changed files with 80 additions and 3 deletions
+45
View File
@@ -178,6 +178,8 @@ export class World {
clock: GameClock
systems: Record<string, { enabled: boolean }>
plugins: PluginManager
/** 0.1.34 世界观察回调(只读洞察) */
simTickObservers: Array<(year: number, month: number, state: unknown) => void> = []
private eventPools = new Map<string, EventDef[]>()
kernel?: import('../kernel/Kernel').Kernel
@@ -243,6 +245,14 @@ export class World {
addAspirationDef: (def) => registerAspirationDef(def),
addFormationDef: (def) => registerFormationDef(def),
addTraitDef: (def) => registerTraitDef(def),
addTechniqueDef: (def) => {
// 入数据包(MISSIONS 式 mergeAdd 通道——同 id 跳过;grade≤3 自动进求学/秘境池)
PACK.mergeAdd('techniques', [def])
},
onWorldSimTick: (fn) => {
self.simTickObservers.push(fn)
return () => { self.simTickObservers = self.simTickObservers.filter((f) => f !== fn) }
},
addCapability: (cap) => {
// 0.1.23:能力卡注册入 World 实例(防跨档全局泄漏);UI 全局清单读 SYSTEM_DEFS 展示不受影响
if (!self.systems[cap.id]) self.systems[cap.id] = { enabled: true }
@@ -458,6 +468,11 @@ export class World {
this.trackStats()
this.clampState()
this.pruneYearFlags()
// 0.1.34 世界观察回调(插件/MOD 只读洞察——零 rng 零时序影响)
if (this.simTickObservers.length > 0) {
const obs = this.simTickObservers
for (const fn of obs) fn(s.year, s.month, s)
}
}
applyEventChoice(eventId: string, idx: number): boolean {
@@ -901,6 +916,36 @@ export class World {
return Math.round(top * bonus)
}
/** 0.1.34 MOD 数值 patchmult 0.5~2 / add 实数——作用于数据包表项;卸载复原) */
applyModPatch(entries: Array<{ target: 'technique' | 'item' | 'building'; id: string; mult?: number; add?: number }>): void {
const undo: Array<() => void> = []
for (const p of entries) {
if (p.target === 'technique') {
const arr = pack().techniques.filter((x) => x.id === p.id)
if (arr.length === 0) continue
const t = arr[0] as unknown as Record<string, number>
const vals: Array<[keyof typeof t, number]> = []
for (const key of ['expBonus', 'powerBonus', 'critChance', 'guardBonus', 'tribBonus', 'secretBonus'] as const) {
const v = t[key]
if (typeof v !== 'number') continue
vals.push([key as never, v])
t[key as never] = Math.max(-5, Math.min(2, p.mult !== undefined ? v * p.mult : v + (p.add ?? 0)))
}
for (const [key, v] of vals) undo.push(() => { t[key as never] = v })
} else if (p.target === 'item') {
const it = (pack().items as Record<string, { basePrice?: number }>)[p.id]
if (!it || typeof it.basePrice !== 'number') continue
const orig = it.basePrice
undo.push(() => { it.basePrice = orig })
it.basePrice = Math.max(1, Math.round(it.basePrice * (p.mult ?? 1) + (p.add ?? 0)))
}
}
this.patchUndo.push(...undo)
}
/** 撤销栈(MOD 卸载时由 modManager uninstall 调用) */
patchUndo: Array<() => void> = []
ancestralRite(): boolean {
const fam = this.state.family
const last = (fam.flag['lastRiteYear'] as number | undefined) ?? -999