v0.1.24: 寰宇初构——世界种子生成器 + 插件时轮锚点(1034 全绿)

【世界种子生成器(NPC 数量不再定死)】
- sim/worldgen.ts:generateWorld(seed) 确定性塑造天下——NPC 4~8 家随机
  (老牌世家模板 + 词库组合新贵,名字去重)、初始关系网(1-2世仇+1盟友)、
  开局 era 三态、区域风味、市场偏移 ±15%
- 独立派生 rng(seed::worldgen)——World.rng 主序列零消耗:同 seed 世界可重放、
  玩法随机序列不受生成器移动(金钟罩玩法序保护)
- state.worldGen 落档(normalize 旧档按 seed 重放=同世界,存档兼容)
- creation 初始化 npcFamilies/initSim 关系网/era/池偏置全走 worldgen——开局即恩怨
- 局内补位目标 = 开局家数(4~8),生灭闭环持续

【插件深化(0.1.24 第二组钩子)】
- PluginContext.beforePhase/afterPhase(时轮锚点:phase 前后挂勾,卸载全摘)
- PluginContext.addNpcTemplate(世界模板注入——词库插件化)
- plugin-public 文档同步;示例 Plugin 契约升级

【测试适配(世界名单随机化)】
- 静态 npc id 假设全面改动态取家(anyNpc helper 共享);
- 难度梯度用例改「同 seed 不同难度」比较;worldgen 确定性/范围/去重/关系对称 7 例

【测试】1034 全绿(45 套件);金钟罩 0.1.24 基线固化(worldgen 随机世界)+ worldAnnals 等;
build 通过
This commit is contained in:
2026-08-23 16:15:22 +08:00
parent 04ef80faa5
commit ec25bf92fc
28 changed files with 382 additions and 72 deletions
@@ -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.23',
version: '0.1.24',
modules: this.world.systemList().length,
systems: this.world.systemList().filter((s) => s.enabled).length,
plugins: this.world.pluginList().length,
+23
View File
@@ -15,6 +15,8 @@ import { POSTS } from '../../data/posts'
import { aspirationById as aspirationOf } from '../../data/aspirations'
import { computeLegacy, resolveLegacy, peakRealmIndex, grandTechniqueCount, LegacyArch } from '../narrative/legacy'
import { needsTribulation, tribulationEventId } from './Systems/tribulation'
import { generateWorld } from '../sim/worldgen'
import { registerNpcDef } from '../../data/npcs'
import { fire } from './Systems/events'
import { createWorldState, findInheritor } from './creation'
import { SYSTEM_DEFS, SystemDef } from './capabilities'
@@ -61,6 +63,19 @@ export function worldSimOf(w: World): WorldSim {
}
export function normalizeGameState(state: GameState): GameState {
// 0.1.24 世界种子:旧档缺 worldGen → 按 seed 重放(同一世界)并注册动态 def
if (!state.worldGen && state.seed) {
const wg = generateWorld(state.seed)
state.worldGen = {
relations: wg.relations,
eras: wg.eras,
marketOffset: wg.marketOffset,
regionFlavor: wg.regionFlavor,
alliances: wg.alliances,
feuds: wg.feuds,
npcCount: wg.npcs.length
}
}
// 老版本存档(<0.1.1)缺少新增字段,加载时补齐,避免运行期 undefined 崩溃
if (!state.finance) state.finance = { accum: 0 }
if (!state.yearStats) state.yearStats = { births: 0, deaths: 0 }
@@ -172,6 +187,14 @@ export class World {
clock: self.clock,
register: (phase, fn: SystemHook) => self.clock.register(phase, fn),
onYearStart: (fn: SystemHook) => self.clock.onYearStart(fn),
beforePhase: (phase, fn) => self.clock.beforePhase(phase, fn),
afterPhase: (phase, fn) => self.clock.afterPhase(phase, fn),
addNpcTemplate: (def) => {
registerNpcDef(def)
if (self.state.worldGen?.relations && !self.state.worldGen.relations[def.id]) {
self.state.worldGen.relations[def.id] = {}
}
},
addCapability: (cap) => {
// 0.1.23:能力卡注册入 World 实例(防跨档全局泄漏);UI 全局清单读 SYSTEM_DEFS 展示不受影响
if (!self.systems[cap.id]) self.systems[cap.id] = { enabled: true }
+12 -2
View File
@@ -2,7 +2,7 @@ import { Character, GameState, NpcFamilyState, RealmMajor } from '../../types/do
import { Rng, seedToRng } from '../kernel/rng'
import { randomSurname, MALE_GIVEN, FEMALE_GIVEN } from '../kernel/names'
import { newCharacter } from './pcgen'
import { NPCS } from '../../data/npcs'
import { generateWorld } from '../sim/worldgen'
import { World } from './World'
export interface NewGameOptions {
@@ -21,9 +21,19 @@ export function createWorldState(opts: NewGameOptions): GameState {
const stones = diff === 'easy' ? 1200 : diff === 'normal' ? 800 : 550
const npcStrength = diff === 'easy' ? 0.9 : diff === 'normal' ? 1 : 1.15
const worldGen = generateWorld(opts.seed)
const state: GameState = {
schemaVersion: 1,
seed: opts.seed,
worldGen: {
relations: worldGen.relations,
eras: worldGen.eras,
marketOffset: worldGen.marketOffset,
regionFlavor: worldGen.regionFlavor,
alliances: worldGen.alliances,
feuds: worldGen.feuds,
npcCount: worldGen.npcs.length
},
rng: rng.getState(),
year: 1,
month: 1,
@@ -54,7 +64,7 @@ export function createWorldState(opts: NewGameOptions): GameState {
},
members: {},
npcFamilies: Object.fromEntries(
NPCS.map((n) => [
worldGen.npcs.map((n) => [
n.id,
{
id: n.id,