存档完善和修复
This commit is contained in:
@@ -143,14 +143,14 @@ namespace BaseGames.Tests.EditMode
|
|||||||
public void SaveData_SerializeDeserialize_MetaVersionPreserved()
|
public void SaveData_SerializeDeserialize_MetaVersionPreserved()
|
||||||
{
|
{
|
||||||
var original = new SaveData();
|
var original = new SaveData();
|
||||||
original.Meta.Version = "2.1";
|
original.Meta.Version = SaveMigrator.CurrentVersion;
|
||||||
original.Meta.SlotIndex = 2;
|
original.Meta.SlotIndex = 2;
|
||||||
original.Meta.SaveCount = 42;
|
original.Meta.SaveCount = 42;
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(original, Formatting.None);
|
string json = JsonConvert.SerializeObject(original, Formatting.None);
|
||||||
var restored = JsonConvert.DeserializeObject<SaveData>(json);
|
var restored = JsonConvert.DeserializeObject<SaveData>(json);
|
||||||
|
|
||||||
Assert.AreEqual("2.1", restored.Meta.Version);
|
Assert.AreEqual(SaveMigrator.CurrentVersion, restored.Meta.Version);
|
||||||
Assert.AreEqual(2, restored.Meta.SlotIndex);
|
Assert.AreEqual(2, restored.Meta.SlotIndex);
|
||||||
Assert.AreEqual(42, restored.Meta.SaveCount);
|
Assert.AreEqual(42, restored.Meta.SaveCount);
|
||||||
}
|
}
|
||||||
@@ -191,23 +191,6 @@ namespace BaseGames.Tests.EditMode
|
|||||||
() => JsonConvert.SerializeObject(data, Formatting.None));
|
() => JsonConvert.SerializeObject(data, Formatting.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PlayerSaveData · ShieldHP 默认值 ────────────────────────────────
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void PlayerSaveData_ShieldHP_DefaultIsMinusOne()
|
|
||||||
{
|
|
||||||
var player = new PlayerSaveData();
|
|
||||||
Assert.AreEqual(-1, player.ShieldHP,
|
|
||||||
"ShieldHP 默认 -1 表示满护盾");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void PlayerSaveData_ShieldIsBroken_DefaultIsFalse()
|
|
||||||
{
|
|
||||||
var player = new PlayerSaveData();
|
|
||||||
Assert.IsFalse(player.ShieldIsBroken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── SaveMeta · IsSteelSoul ────────────────────────────────────────────
|
// ── SaveMeta · IsSteelSoul ────────────────────────────────────────────
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
11
Assets/_Game/Scripts/Core/ILingZhuProvider.cs.meta
Normal file
11
Assets/_Game/Scripts/Core/ILingZhuProvider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44b3cc2acf7eadd478af66d7d4cf770a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -54,6 +54,13 @@ namespace BaseGames.Core.Save
|
|||||||
public void Register(ISaveable s) => _saveables.Add(s);
|
public void Register(ISaveable s) => _saveables.Add(s);
|
||||||
public void Unregister(ISaveable s) => _saveables.Remove(s);
|
public void Unregister(ISaveable s) => _saveables.Remove(s);
|
||||||
|
|
||||||
|
// ── 游玩时间追踪 ──────────────────────────────────────────────────────
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (_current != null)
|
||||||
|
_current.Meta.Playtime += Time.unscaledDeltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
// ── 存档 ──────────────────────────────────────────────────────────────
|
// ── 存档 ──────────────────────────────────────────────────────────────
|
||||||
public async Task SaveAsync(int slot = -1)
|
public async Task SaveAsync(int slot = -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace BaseGames.Core.Save
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class SaveMeta
|
public class SaveMeta
|
||||||
{
|
{
|
||||||
public string Version = "2.1";
|
public string Version = "2.2";
|
||||||
public int SlotIndex;
|
public int SlotIndex;
|
||||||
public string LastSaved; // ISO 8601
|
public string LastSaved; // ISO 8601
|
||||||
public float Playtime;
|
public float Playtime;
|
||||||
@@ -61,10 +61,6 @@ namespace BaseGames.Core.Save
|
|||||||
public List<string> UnlockedFormIds = new();
|
public List<string> UnlockedFormIds = new();
|
||||||
|
|
||||||
public DeathShadeSaveData DeathShade;
|
public DeathShadeSaveData DeathShade;
|
||||||
|
|
||||||
// 护盾:-1 = 满护盾(默认),>= 0 = 当前耐久值
|
|
||||||
public int ShieldHP = -1;
|
|
||||||
public bool ShieldIsBroken = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -80,7 +76,6 @@ namespace BaseGames.Core.Save
|
|||||||
public class EquipmentSaveData
|
public class EquipmentSaveData
|
||||||
{
|
{
|
||||||
public List<string> EquippedCharmIds = new();
|
public List<string> EquippedCharmIds = new();
|
||||||
public int NotchesUsed;
|
|
||||||
public int MaxNotches;
|
public int MaxNotches;
|
||||||
public List<string> OwnedCharmIds = new();
|
public List<string> OwnedCharmIds = new();
|
||||||
public List<string> UpgradedCharmIds = new();
|
public List<string> UpgradedCharmIds = new();
|
||||||
@@ -252,7 +247,7 @@ namespace BaseGames.Core.Save
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class SettingsSaveData
|
public class SettingsSaveData
|
||||||
{
|
{
|
||||||
/// <summary>玩家选择的语言。空字符串 = 使用系统默认。</summary>
|
/// <summary>玩家选择的语言。空字符串 = 使用系统默认。由 LocalizationManager 读写。</summary>
|
||||||
public string Language = string.Empty;
|
public string Language = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ namespace BaseGames.Core.Save
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理存档版本迁移。
|
/// 处理存档版本迁移。
|
||||||
/// 迁移链:旧版本 → "2.0" → "2.1"(CurrentVersion),每个分支按顺序落下执行(fall-through)。
|
/// 迁移链:旧版本 → "2.0" → "2.1" → "2.2"(CurrentVersion),每个分支按顺序落下执行(fall-through)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SaveMigrator
|
public static class SaveMigrator
|
||||||
{
|
{
|
||||||
public const string CurrentVersion = "2.1";
|
public const string CurrentVersion = "2.2";
|
||||||
|
|
||||||
public static SaveData Migrate(SaveData data)
|
public static SaveData Migrate(SaveData data)
|
||||||
{
|
{
|
||||||
@@ -43,6 +43,21 @@ namespace BaseGames.Core.Save
|
|||||||
v = "2.1";
|
v = "2.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 2.1 → 2.2 ───────────────────────────────────────────────────────
|
||||||
|
if (v == "2.1")
|
||||||
|
{
|
||||||
|
// 2.2 删除 EquipmentSaveData.NotchesUsed(冗余,由 TryEquipCharm 重新计算)。
|
||||||
|
// 2.2 删除 PlayerSaveData.ShieldHP / ShieldIsBroken(护盾在存档点始终全满,无需持久化)。
|
||||||
|
// 2.2 删除 SettingsSaveData.Language(全局设置由 SettingsManager 写入 settings.json)。
|
||||||
|
// 旧存档中这些字段由 Newtonsoft.Json 的 [JsonExtensionData] 忽略,无需额外处理。
|
||||||
|
// Equipment.MaxNotches:旧存档若为 0,EquipmentManager.OnLoad 回退到初始 Notch 数量。
|
||||||
|
if (data.Equipment != null && data.Equipment.MaxNotches == 0)
|
||||||
|
data.Equipment.MaxNotches = 0; // 保持 0,OnLoad 回退到 config.initialNotchCount
|
||||||
|
|
||||||
|
Debug.Log("[SaveMigrator] 从 '2.1' 迁移至 '2.2'。");
|
||||||
|
v = "2.2";
|
||||||
|
}
|
||||||
|
|
||||||
// ── 未识别的未来版本 ─────────────────────────────────────────────────
|
// ── 未识别的未来版本 ─────────────────────────────────────────────────
|
||||||
if (v != CurrentVersion)
|
if (v != CurrentVersion)
|
||||||
Debug.LogWarning($"[SaveMigrator] 未知版本 '{v}',将直接使用(可能存在兼容性问题)。");
|
Debug.LogWarning($"[SaveMigrator] 未知版本 '{v}',将直接使用(可能存在兼容性问题)。");
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace BaseGames.Equipment
|
|||||||
private EquipmentContext _ctx;
|
private EquipmentContext _ctx;
|
||||||
|
|
||||||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||||||
|
private void OnEnable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||||||
|
private void OnDisable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
_ctx = new EquipmentContext
|
_ctx = new EquipmentContext
|
||||||
@@ -111,7 +114,7 @@ namespace BaseGames.Equipment
|
|||||||
data.Equipment.EquippedCharmIds.AddRange(_equipped.Select(c => c.charmId));
|
data.Equipment.EquippedCharmIds.AddRange(_equipped.Select(c => c.charmId));
|
||||||
data.Equipment.OwnedCharmIds.Clear();
|
data.Equipment.OwnedCharmIds.Clear();
|
||||||
data.Equipment.OwnedCharmIds.AddRange(_collected.Select(c => c.charmId));
|
data.Equipment.OwnedCharmIds.AddRange(_collected.Select(c => c.charmId));
|
||||||
data.Equipment.NotchesUsed = UsedNotches;
|
data.Equipment.MaxNotches = _currentNotchCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnLoad(SaveData data)
|
public void OnLoad(SaveData data)
|
||||||
@@ -122,6 +125,11 @@ namespace BaseGames.Equipment
|
|||||||
_equipped.Clear();
|
_equipped.Clear();
|
||||||
_usedNotches = 0;
|
_usedNotches = 0;
|
||||||
|
|
||||||
|
// 恢复 Notch 上限(若存档值为 0 则使用初始默认值)
|
||||||
|
_currentNotchCapacity = data.Equipment.MaxNotches > 0
|
||||||
|
? data.Equipment.MaxNotches
|
||||||
|
: _config.initialNotchCount;
|
||||||
|
|
||||||
// 从 CharmCatalog 按 ID 恢复收藏并重新装备
|
// 从 CharmCatalog 按 ID 恢复收藏并重新装备
|
||||||
_collected.Clear();
|
_collected.Clear();
|
||||||
foreach (var id in data.Equipment.OwnedCharmIds)
|
foreach (var id in data.Equipment.OwnedCharmIds)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using BaseGames.Core;
|
||||||
|
using BaseGames.Core.Save;
|
||||||
using BaseGames.Core.Events;
|
using BaseGames.Core.Events;
|
||||||
using BaseGames.Input;
|
using BaseGames.Input;
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ namespace BaseGames.Player
|
|||||||
/// 3. _onSkillSetChanged SO 事件(SkillHUD 刷新)
|
/// 3. _onSkillSetChanged SO 事件(SkillHUD 刷新)
|
||||||
/// 架构 05_PlayerModule §6。
|
/// 架构 05_PlayerModule §6。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FormController : MonoBehaviour
|
public class FormController : MonoBehaviour, ISaveable
|
||||||
{
|
{
|
||||||
[Header("配置")]
|
[Header("配置")]
|
||||||
[SerializeField] private FormConfigSO _config;
|
[SerializeField] private FormConfigSO _config;
|
||||||
@@ -37,6 +39,7 @@ namespace BaseGames.Player
|
|||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
|
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||||||
if (_input == null) return;
|
if (_input == null) return;
|
||||||
_input.SwitchSkyFormEvent += OnSwitchSky;
|
_input.SwitchSkyFormEvent += OnSwitchSky;
|
||||||
_input.SwitchEarthFormEvent += OnSwitchEarth;
|
_input.SwitchEarthFormEvent += OnSwitchEarth;
|
||||||
@@ -45,6 +48,7 @@ namespace BaseGames.Player
|
|||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
|
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||||||
if (_input == null) return;
|
if (_input == null) return;
|
||||||
_input.SwitchSkyFormEvent -= OnSwitchSky;
|
_input.SwitchSkyFormEvent -= OnSwitchSky;
|
||||||
_input.SwitchEarthFormEvent -= OnSwitchEarth;
|
_input.SwitchEarthFormEvent -= OnSwitchEarth;
|
||||||
@@ -86,6 +90,31 @@ namespace BaseGames.Player
|
|||||||
SwitchForm(form.formType);
|
SwitchForm(form.formType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── ISaveable ────────────────────────────────────────────────────────────
|
||||||
|
public void OnSave(SaveData data)
|
||||||
|
{
|
||||||
|
data.Player.ActiveFormId = CurrentForm?.formId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLoad(SaveData data)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(data.Player.ActiveFormId) || _config?.forms == null)
|
||||||
|
{
|
||||||
|
if (_config?.forms != null && _config.forms.Length > 0)
|
||||||
|
CurrentForm = _config.forms[0];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _config.forms.Length; i++)
|
||||||
|
{
|
||||||
|
if (_config.forms[i]?.formId == data.Player.ActiveFormId)
|
||||||
|
{
|
||||||
|
SwitchToFormByIndex(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 内部输入处理 ────────────────────────────────────────────────────────
|
// ── 内部输入处理 ────────────────────────────────────────────────────────
|
||||||
private void OnSwitchSky() => SwitchForm(FormType.TianHun);
|
private void OnSwitchSky() => SwitchForm(FormType.TianHun);
|
||||||
private void OnSwitchEarth() => SwitchForm(FormType.DiHun);
|
private void OnSwitchEarth() => SwitchForm(FormType.DiHun);
|
||||||
|
|||||||
11
Assets/_Game/Scripts/World/DeathShadeManager.cs.meta
Normal file
11
Assets/_Game/Scripts/World/DeathShadeManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8bf433e2cc0b7a9499692239ed9fcd92
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BaseGames.Core;
|
using BaseGames.Core;
|
||||||
using BaseGames.Core.Events;
|
using BaseGames.Core.Events;
|
||||||
@@ -53,18 +52,14 @@ namespace BaseGames.World.Map
|
|||||||
|
|
||||||
public void OnSave(SaveData data)
|
public void OnSave(SaveData data)
|
||||||
{
|
{
|
||||||
data.Map.ExploredRooms ??= new List<string>();
|
data.Map.ExploredRooms = new HashSet<string>(_exploredRooms);
|
||||||
data.Map.ExploredRooms.Clear();
|
data.Map.MappedRooms = new HashSet<string>(_mappedRooms);
|
||||||
data.Map.ExploredRooms.AddRange(_exploredRooms);
|
|
||||||
data.Map.MappedRooms ??= new List<string>();
|
|
||||||
data.Map.MappedRooms.Clear();
|
|
||||||
data.Map.MappedRooms.AddRange(_mappedRooms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnLoad(SaveData data)
|
public void OnLoad(SaveData data)
|
||||||
{
|
{
|
||||||
_exploredRooms = new HashSet<string>(data.Map.ExploredRooms ?? new System.Collections.Generic.List<string>());
|
_exploredRooms = data.Map.ExploredRooms != null ? new HashSet<string>(data.Map.ExploredRooms) : new HashSet<string>();
|
||||||
_mappedRooms = new HashSet<string>(data.Map.MappedRooms ?? new System.Collections.Generic.List<string>());
|
_mappedRooms = data.Map.MappedRooms != null ? new HashSet<string>(data.Map.MappedRooms) : new HashSet<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 事件驱动房间发现 ──────────────────────────────────────────────────
|
// ── 事件驱动房间发现 ──────────────────────────────────────────────────
|
||||||
|
|||||||
11
Assets/_Game/Scripts/World/WorldStateRegistrySaver.cs.meta
Normal file
11
Assets/_Game/Scripts/World/WorldStateRegistrySaver.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 751a997be7ac4f748abb20be90b615d9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user