建立时间:2026-06-25
核心目标:从单文件原型,拆分为职责分离的多文件架构,支持长期内容扩展
非互动阅读器,而是阶段式开放世界。
模板(引擎) + 种子(数据) → 状态驱动 → 引擎实时拼装场景。
01 . 03 . 04 . 05 . 06 . 07
州 郡县 功能区 细分区 位置 元素
州(01-13):预定义,固定不变
郡县(01-99):按行政规划预定义
功能区(01-99):城门口/军营/流民区/城区/酒馆/客栈/商店/学校,按需
细分区(01-99):城内村/营内分区,按需,可跳级
位置(01-99):具体位置,按需,可跳级
元素(01-99):NPC/告示/门/物品,按需
| 层级 | 名称 | 示例 |
|---|---|---|
| 01 | 州 | 01=司隶 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=物品 |
01、03). 分隔,不写前导零(如 01.03.01 而非 01.03.01.00.00.00)00 表示该层级的"自身"(如 01.03.02.00 = 许县军营前这个场景本身)XX.XX.XX.01.01.00
【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 下。
| ID | 城市 | 备注 |
| 02.04 | 宛城 | 南阳郡治,张曼成黄巾据点 |
| 03.02 | 广宗 | 太平道黄巾军主力,张角驻跸 |
| 01.01 | 洛阳 | 帝都,灵帝在位,十常侍乱政 |
| 07.01 | 涿县 | 刘备张飞故里 |
世界环境(时间/季节/阶段)→ 滤镜参数
角色状态(团结/身份/声望)→ 热区可见性表
路由 01.03.01
→ 读取 world.js 中 01.03.01 的静态结构(locked/status/variants)
→ 判定:该城市是否允许进入
基于第0层团结参数 → 过滤热区列表
→ 输出:本次可交互的热区编号列表
对每个可见热区:
→ 读取 NPC/bulletin 的元数据(季节变体路径/NPC名)
→ 决定渲染什么角色图/告示板
玩家点击热区,触发 dialogueId
→ 读取 dialogues/ 中对应条目
→ 基于第0层角色状态(identity/reputation)→ 选条件匹配文案
→ 渲染:对话文本 + 选项列表
玩家选择选项
→ 加载 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 # 临时:过渡期场景数据(后续拆分)
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: '交州' },
};
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 },
};
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',
};
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 },
};
const SOLIDARITY = {
ACTIVE: { threshold: 20, label: '互助活跃' },
STRAINED: { threshold: -20, label: '关系紧张' },
BROKEN: { threshold: -50, label: '互助崩溃' },
INSTITUTIONALIZED:{ threshold: null, label: '制度化' },
};
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,
},
],
},
},
},
},
};
文件名 = 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',
},
],
};
// 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 | 参数 | 说明 |
| consume_item | target: 物品名 | 消耗道具 |
| set_identity | value: 身份ID | 变更身份 |
| add_mark | key, value | 设置标记位 |
| unlock_scene | target: 场景ID | 解锁场景 |
| navigate | target: 场景ID | 切换场景 |
| advance_time | delta: TU数 | 推进时间 |
| modify_state | f(字段), delta | 增减状态值 |
| notify | text: 文字 | 弹出通知 |
| game_over | reason | 游戏结束 |
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。
js/config.js — 路由表写入 ✅js/world.js — 许县城/许县场景结构写入 ✅js/dialogues/02.03/ — 6个对话文件(守兵/告示栏/老妇/孩童/登记官/募兵告示) ✅js/effects/02.03.effect.js — 豫州效果 ✅js/assets.js — 许县素材映射(占位符) ✅index.html — 干净壳 ✅js/state.js — 运行时状态管理 ✅js/time.js — 时间系统 ✅js/scene.js — 场景渲染+热区 ✅js/dialog.js — 对话+选项系统 ✅js/main.js — 初始化入口 ✅| 日期 | 决策 | 理由 |
| 2026-06-25 | 废弃 ES6 import,走 <script> 标签方案 | 不依赖 CORS 配置,兼容所有部署环境 |
| 2026-06-25 | 地图数据按 World>Region>City>Scene 四层拆分 | 支持长期内容扩展,占位骨架防拍脑袋 |
| 2026-06-25 | effects/ 按郡县分文件 | 逐级按需新增时只改一文件 |
| 2026-06-25 | 路由 ID 格式:2位补零,. 分隔,6层固定 | 整齐易grep,扩展性足 |
—— 人与AI协作记录 ——