Files
zeling_v2/Docs/DesignSpec/13_SaveSystem.md
2026-05-08 11:04:00 +08:00

217 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`,写入成功后删除备份 |