chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,216 @@
# 13 · 存档系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**存档数据结构Schema、读写契约、版本迁移与存档槽管理。
---
## 目录
1. [存档设计原则](#1-存档设计原则)
2. [存档数据 Schema](#2-存档数据-schema)
3. [存档读写契约](#3-存档读写契约)
4. [存档触发时机](#4-存档触发时机)
5. [版本迁移规范](#5-版本迁移规范)
6. [存档槽管理](#6-存档槽管理)
---
## 1. 存档设计原则
| 原则 | 说明 |
|------|------|
| **单一来源** | 存档数据是游戏状态的唯一持久化来源 |
| **原子性** | 存档要么完整写入,要么不写(不产生损坏的存档)|
| **版本兼容** | 旧版存档可被新版游戏读取(向前兼容)|
| **与引擎无关** | 存档格式为纯数据JSON / 结构化二进制),不依赖引擎序列化 |
---
## 2. 存档数据 Schema
### 2.1 顶层结构
```
DataModel SaveData {
──── 元信息 ────────────────────────
schemaVersion : Integer // 用于版本迁移
saveSlot : Integer // 存档槽编号1-3
lastSaveTime : String // ISO 8601 时间戳
playTimeSecs : Integer // 游戏内游玩时长(秒)
──── 玩家状态 ──────────────────────
player : PlayerSaveData
──── 世界状态 ──────────────────────
world : WorldSaveData
──── 进程 ──────────────────────────
progression : ProgressionSaveData
──── 经济 ──────────────────────────
economy : EconomySaveData
}
```
### 2.2 玩家存档数据
```
DataModel PlayerSaveData {
currentHP : Integer
maxHP : Integer
maxSoulPower : Integer
maxSpiritPower : Integer
springCount : Integer
geoAmount : Integer
currentFormId : ID
unlockedFormIds : List<ID>
equippedCharmIds: List<ID> // 当前装备的护符
inventoryItems : List<{itemId: ID, count: Integer}>
lastBonfireId : ID // 死亡后重生位置
lastRoomId : ID // 关闭游戏前所在房间
lastPosition : Vector2 // 关闭游戏前的精确位置
}
```
### 2.3 世界存档数据
```
DataModel WorldSaveData {
visitedRoomIds : List<ID>
activatedBonfireIds: List<ID>
defeatedBossIds : List<ID>
permanentlyDeadEnemyIds: List<ID> // 不刷新的特殊敌人
openedGateIds : List<ID>
activatedSwitchIds: List<ID>
collectedCollectibleIds: List<ID>
geoShade : Optional<GeoShadeState> // 见 09_ProgressionSystem
}
```
### 2.4 进程存档数据
```
DataModel ProgressionSaveData {
unlockedAbilityIds: List<ID>
mainQuestStage : Integer
completedQuestIds: List<ID>
worldFlags : Map<String, Boolean> // 任意世界状态标记
notchCount : Integer
purchasedNotchCount: Integer
loreFoundIds : List<ID>
secretsFoundIds : List<ID>
lootPityCounters: Map<ID, Integer> // 保底计数itemId → 未掉落次数)
}
```
### 2.5 经济存档数据
```
DataModel EconomySaveData {
ownedCharmIds : List<ID> // 已获得(非已装备)的护符
shopPurchaseHistory: Map<ID, List<ID>> // shopId → 已购买 itemId 列表
}
```
---
## 3. 存档读写契约
```
Interface ISaveSystem {
// 写入
save(data: SaveData, slot: Integer) → SaveResult
// 读取
load(slot: Integer) → Optional<SaveData>
// 检查
slotExists(slot: Integer) → Boolean
getSlotMetadata(slot: Integer) → Optional<SaveSlotMeta>
// 删除(需二次确认)
deleteSlot(slot: Integer) → Void
}
DataModel SaveSlotMeta {
slot : Integer
lastSaveTime : String
playTimeSecs : Integer
lastRoomName : String
playerHP : Integer
maxHP : Integer
}
DataModel SaveResult {
success : Boolean
error : Optional<String>
}
```
---
## 4. 存档触发时机
| 触发事件 | 存档类型 | 说明 |
|---------|---------|------|
| `OnBonfireRested` | 完整存档 | 最主要的存档时机 |
| `OnCollectiblePickedUp`(关键道具)| 完整存档 | 防止关键进程丢失 |
| `OnBossDefeated` | 完整存档 | Boss 死亡立即存档 |
| `OnAbilityUnlocked` | 完整存档 | 防止能力解锁丢失 |
| 游戏退出(发出退出信号)| 位置快照 | 保存位置,不算正式存档进度 |
> **设计决策**:不提供手动存档按钮
> **原因**:存档点机制是游戏节奏设计的一部分,强制存档会破坏张力
---
## 5. 版本迁移规范
### 5.1 版本号规则
- `schemaVersion` 每次修改 SaveData 结构时递增
- 不向后兼容(新版存档无法在旧版游戏中读取)
- 向前兼容(旧版存档可被新版游戏迁移后读取)
### 5.2 迁移契约
```
Interface ISaveMigrator {
canMigrate(fromVersion: Integer, toVersion: Integer) → Boolean
migrate(data: RawSaveData, fromVersion: Integer) → SaveData
}
```
### 5.3 迁移策略
| 变更类型 | 处理方式 |
|---------|---------|
| 新增字段 | 旧存档中该字段赋默认值 |
| 删除字段 | 读取时忽略多余字段 |
| 字段重命名 | 迁移器执行映射 |
| 字段类型变更 | 迁移器执行转换逻辑 |
| 无法迁移 | 提示玩家,提供新开局选项(不自动删除)|
---
## 6. 存档槽管理
| 规格项 | 值 |
|--------|-----|
| 存档槽数量 | 3 个 |
| 每槽独立 | 完全独立,互不影响 |
| 新游戏 | 选择存档槽 → 若已有存档显示警告 → 确认后覆盖 |
| 存档文件位置 | 用户数据目录(不与游戏安装目录混存)|
| 备份 | 写入前备份旧文件为 `.bak`,写入成功后删除备份 |