文书档案 · 乱世生存开发规范 v1.32

乱世生存 · 开发规范 v1.32

建立时间:2026-06-25
核心目标:从单文件原型,拆分为职责分离的多文件架构,支持长期内容扩展

一、项目定位

非互动阅读器,而是阶段式开放世界

模板(引擎) + 种子(数据) → 状态驱动 → 引擎实时拼装场景。


二、六层路由坐标体系

2.1 路由结构


01 . 03 . 04 . 05 . 06 . 07
州   郡县  功能区 细分区  位置   元素

州(01-13):预定义,固定不变
郡县(01-99):按行政规划预定义
功能区(01-99):城门口/军营/流民区/城区/酒馆/客栈/商店/学校,按需
细分区(01-99):城内村/营内分区,按需,可跳级
位置(01-99):具体位置,按需,可跳级
元素(01-99):NPC/告示/门/物品,按需

2.2 层级含义

层级名称示例
0101=司隶 02=豫州 03=冀州 ... 12=幽州 13=交州
02郡县01=洛阳 03=许县 04=宛城 ...
03功能区01=城门口 02=军营前 03=流民区 04=城区 05=酒馆 ...
04细分区01=村 02=商铺区,按需,可跳级
05位置01=东侧 02=中央 03=西侧,按需
06元素01=NPC 02=告示 03=门 04=物品

2.3 ID 格式规则

2.4 第一阶段 ID 路由表


【02.03 — 豫州·许县】(豫州=02,许县=03)

02.03.01         许县城门口(场景自身)
02.03.01.01.01.01  守兵(NPC)
02.03.01.02.01.01  城门告示栏(告示)
02.03.01.03.01.01  流民聚集地·老妇(NPC)
02.03.01.03.02.01  流民聚集地·小孩(NPC)

02.03.02         许县·军营前(场景自身)
02.03.02.01.01.01  登记官(NPC)
02.03.02.02.01.01  募兵告示(告示)

未来扩展:酒馆(01.03.04)、城区(01.03.05)等,挂在 01.03 下。

2.5 占位骨架(未来按需解锁)

ID城市备注
02.04宛城南阳郡治,张曼成黄巾据点
03.02广宗太平道黄巾军主力,张角驻跸
01.01洛阳帝都,灵帝在位,十常侍乱政
07.01涿县刘备张飞故里

三、引擎执行顺序(级联加载)

第0层 · 初始化


世界环境(时间/季节/阶段)→ 滤镜参数
角色状态(团结/身份/声望)→ 热区可见性表

第1层 · 城市骨架


路由 01.03.01
→ 读取 world.js 中 01.03.01 的静态结构(locked/status/variants)
→ 判定:该城市是否允许进入

第2层 · 热区过滤


基于第0层团结参数 → 过滤热区列表
→ 输出:本次可交互的热区编号列表

第3层 · 热区内容


对每个可见热区:
→ 读取 NPC/bulletin 的元数据(季节变体路径/NPC名)
→ 决定渲染什么角色图/告示板

第4层 · 对话内容


玩家点击热区,触发 dialogueId
→ 读取 dialogues/ 中对应条目
→ 基于第0层角色状态(identity/reputation)→ 选条件匹配文案
→ 渲染:对话文本 + 选项列表

第5层 · 选项结算


玩家选择选项
→ 加载 effects/ 中对应效果文件
→ 消耗道具/变更身份/推进时间/切换场景
→ 回第0层

四、数据文件结构


_build/
├── index.html           # 不变的壳,<script> 标签引入
├── css/
│   └── style.css       # 不变的皮肤
├── js/
│   ├── config.js       # ID路由表 + 季节/阶段常量 + 时间规则
│   ├── world.js        # 城市骨架 + 场景 + 热区(area/索引/可见条件)
│   ├── dialogues/      # 第4层 — 对话/内容(文件名 = 路由ID)
│   │   └── 01.03/
│   │       ├── 01.03.01.01.01.01.js    # 许县城门口 · 守兵
│   │       ├── 01.03.01.02.01.01.js    # 许县城门口 · 告示栏
│   │       ├── 01.03.02.01.01.01.js    # 许县军营前 · 登记官
│   │       └── 01.03.02.02.01.01.js    # 许县军营前 · 募兵告示
│   ├── effects/        # 第5层 — 状态变更规则(按郡县分)
│   │   └── 01.03.effect.js              # 豫州效果(全许县共用)
│   ├── assets.js       # 路由 → 素材路径 + 季节变体映射
│   ├── state.js        # 运行时状态读写(内存,不落文件)
│   ├── dialog.js       # 对话 + 选项渲染
│   ├── scene.js        # 场景切换 + 热区
│   ├── time.js         # 时间推进
│   └── main.js         # 初始化 + 主循环
└── data/
    └── prototype_scene.json   # 临时:过渡期场景数据(后续拆分)

五、config.js 路由表(核心常量)

5.1 区域定义


const REGIONS = {
  SILU:     { id: '01', name: '司隶' },
  YUZWOU:   { id: '02', name: '豫州' },  // 注意:豫州=02,非03(03=冀州)
  JIZHOU:   { id: '03', name: '冀州' },
  JINGZHOU: { id: '04', name: '荆州' },
  YANZHOU:  { id: '05', name: '兖州' },
  XUZHOU:   { id: '06', name: '徐州' },
  YOUZHOU:  { id: '07', name: '幽州' },
  QINGZHOU: { id: '08', name: '青州' },
  YIZHOU:   { id: '09', name: '益州' },
  LIANGZHOU:{ id: '10', name: '凉州' },
  BINGZHOU: { id: '11', name: '并州' },
  YANGZHOU: { id: '12', name: '扬州' },
  JIAOZHOU: { id: '13', name: '交州' },
};

5.2 城市定义


const CITIES = {
  XUXIAN:   { id: '01.03', region: '02', name: '许县',   locked: false },
  WANCHENG: { id: '01.04', region: '02', name: '宛城',   locked: true  },
  GUANGZONG:{ id: '01.07', region: '03', name: '广宗',   locked: true  },
  LUOYANG:  { id: '01.01', region: '01', name: '洛阳',   locked: true  },
};

5.3 热区元素定义


const ELEMENT_IDS = {
  // 许县城门口
  GUARD_GATE:    '01.03.01.01.01.01',
  BULLETIN_GATE: '01.03.01.02.01.01',
  REFUGEE_OLD:   '01.03.01.03.01.01',
  REFUGEE_CHILD: '01.03.01.03.02.01',
  // 许县军营前
  REGISTRAR:      '01.03.02.01.01.01',
  BULLETIN_CAMP:  '01.03.02.02.01.01',
};

5.4 时间常量


const TIME = {
  TU_PER_DAY:    360,
  TU_PER_PERIOD: { DAWN: 60, NOON: 120, DUSK: 60, NIGHT: 120 },
  HUNGER_DECAY:  { interval: 60, delta: -10 },
  FATIGUE_INCREASE:{ interval: 30, delta: 15 },
  HEALTH_DECAY:  { condition: 'hunger<30', duration: 60, delta: -5 },
};

5.5 团结状态阈值


const SOLIDARITY = {
  ACTIVE:         { threshold: 20,  label: '互助活跃' },
  STRAINED:       { threshold: -20, label: '关系紧张' },
  BROKEN:         { threshold: -50, label: '互助崩溃' },
  INSTITUTIONALIZED:{ threshold: null, label: '制度化' },
};

六、world.js 热区结构


const WORLD = {
  cities: {
    '01.03': {
      id: '01.03',
      name: '许县',
      region: '02',
      locked: false,
      year: 184,
      status: 'rebel_zone',
      variants: ['许县', '许县城'],
      scenes: {
        '01.03.01': {
          id: '01.03.01',
          name: '城门口',
          type: 'gate',
          hotzones: [
            {
              id: '01.03.01.01.01.01',
              type: 'npc',
              area: { x: 5, y: 48, w: 28, h: 32 },
              dialogueId: '01.03.01.01.01.01',
              visibleWhen: null, // null = 始终可见
            },
            {
              id: '01.03.01.02.01.01',
              type: 'bulletin',
              area: { x: 70, y: 42, w: 25, h: 22 },
              dialogueId: '01.03.01.02.01.01',
              visibleWhen: null,
            },
            {
              id: '01.03.01.03.01.01',
              type: 'npc',
              area: { x: 20, y: 70, w: 25, h: 25 },
              dialogueId: '01.03.01.03.01.01',
              visibleWhen: { solidarity: '>=-10' }, // 团结不太低时出现
            },
            {
              id: '01.03.01.03.02.01',
              type: 'npc',
              area: { x: 55, y: 72, w: 20, h: 22 },
              dialogueId: '01.03.01.03.02.01',
              visibleWhen: { solidarity: '>=-10' },
            },
          ],
        },
        '01.03.02': {
          id: '01.03.02',
          name: '军营前',
          type: 'camp_entrance',
          hotzones: [
            {
              id: '01.03.02.01.01.01',
              type: 'npc',
              area: { x: 5, y: 48, w: 28, h: 32 },
              dialogueId: '01.03.02.01.01.01',
              visibleWhen: null,
            },
            {
              id: '01.03.02.02.01.01',
              type: 'bulletin',
              area: { x: 70, y: 42, w: 25, h: 22 },
              dialogueId: '01.03.02.02.01.01',
              visibleWhen: null,
            },
          ],
        },
      },
    },
  },
};

七、dialogues/ 对话文件格式

文件名 = dialogueId,引擎直接用文件名定位。


// 02.03.02.01.01.01.js — 登记官
const DIALOGUE = {
  id: '02.03.02.01.01.01',
  speaker: 'registrar',
  name: '登记官',
  content: {
    default: {
      refugee:     '流民?有没有凭证?',
      recruit:     '新兵?去左边队列报到。',
      recruit_fav: '看你这身板,能扛刀吗?好,站左边。',
    },
  },
  conditions: [
    { key: 'recruit_fav', check: { f: 'reputation', op: '>=', v: 20 } },
    { key: 'recruit',     check: { f: 'identity', op: '==', v: 'recruit' } },
    { key: 'refugee',     check: { f: 'identity', op: '==', v: 'refugee' } },
  ],
  choices: [
    {
      id: 'enlist',
      text: '报名入伍',
      require: { inventory: '短刀' },
      effectFile: '02.03.effect',
      effectId: 'enlist',
      next: null,
    },
    {
      id: 'lie_identity',
      text: '隐瞒身份报名',
      effectFile: '02.03.effect',
      effectId: 'lie_identity',
      next: null,
    },
    {
      id: 'leave_camp_r',
      text: '返回城门',
      effectFile: '02.03.effect',
      effectId: 'leave_camp',
      next: '02.03.01',
    },
  ],
};

八、effects/ 效果文件格式


// 01.03.effect.js — 豫州效果
const EFFECTS = {
  // 入伍
  enlist: {
    type: 'sequence',
    steps: [
      { action: 'consume_item', target: '短刀' },
      { action: 'set_identity', value: 'recruit' },
      { action: 'unlock_scene', target: '02.03.03' },
      { action: 'add_mark', key: 'enlisted', value: true },
      { action: 'notify', text: '身份已变更为:新兵' },
    ],
  },
  // 隐瞒入伍
  lie_identity: {
    type: 'sequence',
    steps: [
      { action: 'set_identity', value: 'recruit' },
      { action: 'add_mark', key: 'enlisted', value: true },
      { action: 'add_mark', key: 'hidden_enlist', value: true },
      { action: 'notify', text: '身份已变更为:新兵(隐瞒)' },
    ],
  },
  // 离开军营
  leave_camp: {
    type: 'navigate',
    target: '02.03.01',
  },
};

效果 action 类型清单

action参数说明
consume_itemtarget: 物品名消耗道具
set_identityvalue: 身份ID变更身份
add_markkey, value设置标记位
unlock_scenetarget: 场景ID解锁场景
navigatetarget: 场景ID切换场景
advance_timedelta: TU数推进时间
modify_statef(字段), delta增减状态值
notifytext: 文字弹出通知
game_overreason游戏结束

九、assets.js 素材映射


const ASSETS = {
  // 热区元素 → 素材路径(季节变体)
  '02.03.01.01.01.01': {
    type: 'npc',
    base: 'npc_guard_gate',
    seasons: {
      spring: 'npc_guard_gate_sp.webp',
      summer: 'npc_guard_gate_su.webp',
      autumn: 'npc_guard_gate_au.webp',
      winter: 'npc_guard_gate_wi.webp',
    },
  },
  '02.03.01.02.01.01': {
    type: 'bulletin',
    base: 'bulletin_gate',
    seasons: {
      spring: 'bulletin_gate_sp.webp',
      summer: 'bulletin_gate_su.webp',
      autumn: 'bulletin_gate_au.webp',
      winter: 'bulletin_gate_wi.webp',
    },
  },
  // ... 其余按需补充
};

// 场景背景图
const SCENE_BACKGROUNDS = {
  '02.03.01': 'bg_xuxian_gate.webp',
  '02.03.02': 'bg_xuxian_camp.webp',
};

十、执行顺序与文件依赖


index.html
  └─ <script src="js/config.js">           ← 路由表 + 常量
  └─ <script src="js/assets.js">          ← 素材映射
  └─ <script src="js/world.js">           ← 城市骨架 + 热区
  └─ <script src="js/state.js">           ← 运行时状态(内存)
  └─ <script src="js/time.js">            ← 时间系统
  └─ <script src="js/scene.js">          ← 场景渲染 + 热区点击
  └─ <script src="js/dialog.js">          ← 对话 + 选项
  └─ <script src="js/main.js">            ← 初始化入口

dialogues/ 和 effects/ 中的文件,通过动态 import() 加载(script 标签法),不写入 HTML。


十一、当前阶段状态

已完成(原型验证)

待重建(v1.32 架构) — 全部完成 ✅


十二、历史决策记录

日期决策理由
2026-06-25废弃 ES6 import,走 <script> 标签方案不依赖 CORS 配置,兼容所有部署环境
2026-06-25地图数据按 World>Region>City>Scene 四层拆分支持长期内容扩展,占位骨架防拍脑袋
2026-06-25effects/ 按郡县分文件逐级按需新增时只改一文件
2026-06-25路由 ID 格式:2位补零,. 分隔,6层固定整齐易grep,扩展性足

—— 人与AI协作记录 ——