tongfu-data-structure-and-table-spec
在无服务器的纯本地化 Web 端游戏架构中,我们推荐将数据结构划分为**“只读静态配置表(Static Metadata)”和“读写动态状态表(Dynamic State)”**。
虽然游戏运行在浏览器本地(LocalStorage/IndexedDB),但依然需要按照关系型数据库的设计思想进行 schema 规范化,以便于前端代码的数据检索、深拷贝与状态重置。
以下是《同福客栈》全局账户、成就系统、单周目存档以及 Buff 运行态的完整数据结构与表格设计方案。
《同福客栈》数据结构与表格设计方案 (JSON / TypeScript 规范)
一、 存档与配置关系图 (Data Architecture Map)
在前端运行态中,静态配置在游戏初始化时一次性加载(通常为只读 JSON),动态数据在游戏运行中实时修改并定期序列化写入本地存储。
静态配置表 (Static Configs - 只读) 动态数据表 (Dynamic States - 读写) ┌────────────────────────────────┐ ┌──────────────────────────────────┐ │ 1. StaticAchievementConfig │ │ 3. GlobalProfile │ │ (成就定义表 - 静态JSON) │ ◄──────────│ (全局账号数据 - LocalStorage) │ ├────────────────────────────────┤ ├──────────────────────────────────┤ │ 2. StaticBuffConfig │ │ 4. PlaythroughSaveSlot │ │ (Buff定义表 - 静态JSON) │ ◄──────────│ (单局存档槽位 - LocalStorage) │ └────────────────────────────────┘ └──────────────────────────────────┘ │ ▼ 包含 ┌──────────────────────────────────┐ │ - StaffState (员工状态) │ │ - RuntimeBuff (运行时Buff实例) │ │ - PlaythroughCounters (单局计数器)│ └──────────────────────────────────┘
二、 全局数据模型设计 (Global Data Schema)
2.1 全局账户数据表 (GlobalProfile)
- 存储 Key:tongfu_global_profile
- 用途:记录跨周目不重置的玩家全局数据(阅历、已解锁成就、永久特权)。
| 字段名 (Field) | 数据类型 (Type) | 必填 | 默认值 | 说明 |
| :--------------------- | :--------------- | :-: | :--: | :-------------------------- |
| legacyPoints | Integer | 是 | 0 | 全局可支配的“江湖阅历”点数余额 |
| unlockedAchievements | Array<String> | 是 | [] | 已解锁的成就 ID 数组 (关联成就配置表) |
| purchasedLegacies | Array<String> | 是 | [] | 永久解锁的特权 ID 数组 |
| globalCounters | Object | 是 | {} | 跨周目累积计数器,K-V 结构(见下文 2.2 示例) |
| lastSavedAt | Long (Timestamp) | 是 | - | 上次保存时间戳 |
| signature | String | 是 | - | 数字签名,用于防篡改校验 |
📝 账户全局计数器 (globalCounters) K-V 映射规范:
用于记录如“累计赚取100w两银子”等跨周目成就。
- silver_gained_total: 历史累计获得银两数
- brawls_prevented_total: 历史累计制止斗殴次数
- fines_paid_total: 历史累计缴纳罚款次数
- playthroughs_completed: 历史累计通关周目数
2.2 静态成就配置表 (StaticAchievementConfig)
- 存储介质:静态资源包 configs/achievements.json (只读)
- 用途:定义所有成就的达成判定条件与奖励。
| 字段名 (Field) | 数据类型 (Type) | 必填 | 说明 |
| :--------------- | :---------------- | :-: | :------------------------------------------------------------- |
| id | String | 是 | 唯一主键(如 ACH_MON_100W) |
| name | String | 是 | 成就展示名称(如“富甲关中”) |
| desc | String | 是 | 成就达成说明(描述文案) |
| category | Enum | 是 | 判定模式:CUMULATIVE (累积型) / THRESHOLD (阈值型) / SPECIAL (特殊触发) |
| triggerEvent | String | 是 | 绑定的事件总线事件(如 EVENT_SILVER_CHANGE) |
| conditionParam | String | 是 | 判定所需的数值字段(对应 globalCounters 或 PlaythroughCounters 中的 key) |
| targetValue | Integer | 是 | 达标目标数值(如 1000000) |
| rewardPoints | Integer | 是 | 奖励的江湖阅历点数 |
| unlockRewardId | String (Nullable) | 否 | 达成后额外解锁的道具/菜谱/挂饰 ID |
三、 单周目存档模型设计 (Playthrough Data Schema)
每个存档槽位作为一个整体,序列化为 JSON 字符串存储在 LocalStorage 中。
- 存储 Key:tongfu_save_slot_1 / tongfu_save_slot_2 / tongfu_save_slot_3
3.1 核心槽位表 (PlaythroughSaveSlot)
| 字段名 (Field) | 数据类型 (Type) | 必填 | 默认值 | 说明 |
| :---------------- | :------------------- | :-: | :---------------: | :------------------------- |
| slotId | Integer | 是 | - | 槽位 ID (1-3) |
| currentDay | Integer | 是 | 1 | 当前经营天数 |
| currentHour | Integer | 是 | 6 | 当前游戏小时(早 6 点开盘 - 晚 22 点打烊) |
| gameStage | Enum | 是 | NORMAL_BUSINESS | 对应状态机中的九大客栈经营状态 |
| silver | Double | 是 | 100.0 | 当前手头持有的银两余额 |
| healthScore | Integer | 是 | 80 | 当前客栈综合健康分 (0-100) |
| cleanliness | Double | 是 | 80.0 | 运行时环境清洁度 (0-100) |
| security | Double | 是 | 70.0 | 运行时安防值守值 (0-100) |
| reputation | Double | 是 | 50.0 | 运行时口碑与声望 (0-100) |
| negativePr | Double | 是 | 0.0 | 运行时舆论负面值 (0-100) |
| ingredientStock | Double | 是 | 3.0 | 食材充足天数 (单位: 天) |
| ingredientFresh | Double | 是 | 90.0 | 库存食材平均新鲜度 (0-100) |
| staffs | Array<StaffState> | 是 | [] | 运行时员工数据表(见 3.2) |
| runtimeBuffs | Array<RuntimeBuff> | 是 | [] | 当前客栈全局挂载的活动 Buff 列表(见四) |
| counters | Object | 是 | - | 单周目运行时计数器表(见 3.3) |
| signature | String | 是 | - | 单槽位防篡改签名 |
3.2 员工运行时状态表 (StaffState)
嵌套在 PlaythroughSaveSlot.staffs 数组中。
| 字段名 (Field) | 数据类型 (Type) | 必填 | 默认值 | 说明 |
| :----------- | :------------------- | :-: | :-----: | :----------------------------------------------- |
| staffId | String | 是 | - | 员工唯一 ID (如 STAFF_LBA) |
| name | String | 是 | - | 员工名称(如“白展堂”) |
| job | Enum | 是 | - | 岗位:COOK (厨子), WAITER (跑堂), ACCOUNTANT (账房) |
| mood | Double | 是 | 100.0 | 运行时心情值 (0-100) |
| isOnDuty | Boolean | 是 | true | 当前是否在岗(轮班状态) |
| staffBuffs | Array<RuntimeBuff> | 是 | [] | 挂载在该员工身上的个人 Buff 列表 |
3.3 单周目专用计数器表 (PlaythroughCounters)
嵌套在 PlaythroughSaveSlot.counters 中。周目结束时,部分数据累加至全局,随后本表清空。
| 字段名 (Field) | 数据类型 (Type) | 必填 | 默认值 | 说明 |
| :------------------------ | :---------- | :-: | :---: | :----------------------- |
| silver_gained_run | Double | 是 | 0.0 | 本周目累计赚取的银两 |
| thieves_caught_run | Integer | 是 | 0 | 本周目累计抓住小偷次数 |
| consecutive_clean_days | Integer | 是 | 0 | 清洁度连续大于 80 的天数(不满足则重置) |
| consecutive_guo_mood_90 | Integer | 是 | 0 | 郭芙蓉心情连续大于 90 的天数(用于成就判定) |
| fines_paid_run | Double | 是 | 0.0 | 本周目累计缴纳的罚款 |
| pr_crisis_triggered | Integer | 是 | 0 | 本周目触发食物中毒/舆论危机的次数 |
四、 Buff 运行态数据结构 (Runtime Buff Schema)
Buff 分为“配置态”和“运行态”。运行态实例包含时间刻、来源等动态信息,存在于客栈全局或员工状态中。
4.1 运行时 Buff 实例结构 (RuntimeBuff)
| 字段名 (Field) | 数据类型 (Type) | 必填 | 默认值 | 说明 |
| :------------------ | :-------------- | :-: | :------: | :----------------------------------------------------- |
| instanceId | String | 是 | - | 运行时实例 ID(生成随机 UUID 防止堆叠混淆) |
| configId | String | 是 | - | 关联的配置 ID (如 BUFF_INN_SAN_CRISIS) |
| tags | Array<String> | 是 | [] | 继承并持有的系统标签(如 ["#SanitationCrisis"]) |
| durationRemaining | Double | 是 | -1 | 剩余游戏小时数。-1 代表永久挂载,依靠条件卸载 |
| sourceType | Enum | 是 | SYSTEM | 来源类型:SYSTEM (数值触发) / EVENT (随机事件) / STAFF (员工技能) |
| modifiers | Array<Object> | 是 | [] | 动态属性修饰符快照 |
📝 modifiers 核心属性修饰器数据规范:
[ { "targetAttribute": "cleanliness_decay_rate", "calcType": "MULTIPLY", "value": 0.5 }, { "targetAttribute": "security_recovery_rate", "calcType": "ADD", "value": 6.0 } ]
五、 数据流动示例:事件触发与成就解锁 (Data Flow)
以下展示当跑堂(老白)点住小偷时,各数据表字段的级联变动过程,帮助开发人员理清状态维护逻辑:
- 业务系统触发捕盗事件
- 派发事件
GameEventBus.emit('EVENT_THIEF_CAUGHT', { count: 1 })│ ▼
- 单局数据更新
playthrough_slot_1.counters.thieves_caught_run增加 1 (单局计数器) │ ▼
- 全局数据更新
global_profile.global_counters.thieves_caught_total增加 1 (全局计数器) │ ▼
- 成就系统检查
- 遍历
StaticAchievementConfig,发现绑定事件EVENT_THIEF_CAUGHT的成就: ID:ACH_LBA_DOU(盗圣在此), 目标值: 100 - 检查
global_profile.unlockedAchievements是否已存在该 ID │ ├─► [已存在] ──────► 结束数据流 │ └─► [未存在] ──────► 执行 5. │ ▼
- 达成解锁
global_profile.unlockedAchievements压入'ACH_LBA_DOU'global_profile.legacyPoints增加该成就对应奖励+150- 使用新数据对
global_profile生成新签名signature - 保存回浏览器 LocalStorage,派发弹窗UI通知。