Archived on 2026-10-24. The Godot rewrite is cancelled. Migration decisions and work checklists below are withdrawn. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.
Save Data And Progression Analysis¶
Purpose¶
This system stores player progression, settings, unlocks, achievements, shop upgrades, run counters, and platform-related identity data.
Current Unity Implementation¶
Main scripts:
| Path | Script | Role |
|---|---|---|
Assets/Script/App/Core/AppInitiator.cs |
AppInitiator |
Starts service/save loading before main menu. |
Assets/Script/App/Core/SaveManager.cs |
SaveManager |
Owns loaded PlayerData and GameConfig; handles new save, legacy migration, reset, backup. |
Assets/Script/App/Data/Save.cs |
Save |
Static facade into SaveManager and Steam nickname. |
Assets/Script/TinyChaos/Services/Save/SaveService.cs |
SaveService |
File IO, new/legacy paths, JSON/binary read, backup. |
Assets/Script/TinyChaos/Services/Save/SavePathResolver.cs |
SavePathResolver, NewSavePathResolver |
Save path rules. |
Assets/Script/TinyChaos/DataModels/PlayerData.cs |
PlayerData |
Progression save model. |
Assets/Script/TinyChaos/DataModels/GameConfig.cs |
GameConfig |
Settings save model. |
Assets/Script/Shared/GameDataCount.cs |
GameDataCount |
Run counters and achievement trigger conditions. |
Assets/Script/App/Managers/AchievementManager.cs |
AchievementManager |
Loads/saves achievements, rewards unlocks, Steam sync. |
Assets/Script/App/Managers/ShopManager.cs |
ShopManager |
Loads/saves shop upgrades and applies shop modifiers. |
Assets/Script/Game/Resources/GameDataLoader.cs |
GameDataLoader |
Loads and saves unlock states to/from resources and save dictionaries. |
Key Data¶
PlayerData:
versionsoulPointchaosFragmentunlockedChar: Dictionary<string, bool>unlockedAchievement: Dictionary<string, bool>unlockedSpecialUpgrade: Dictionary<string, bool>shopUpgrade: Dictionary<string, int>unlockedStage: Dictionary<string, bool>totalDataCounter: Dictionary<string, int>legacyMigratedisFirstLaunch
GameConfig:
- volume: master, music, sfx, footstep
- graphics: width, height, fullscreen, vsync
- damage indicator enabled/limit
- language selection
- keybind preset list
Save static facade exposes:
- currencies and dictionaries,
- config fields,
- save/reset calls,
NicknamefromSteamFriends.GetPersonaName().
Runtime Flow¶
Startup¶
AppInitiator.RunStartupFlow()reads Steam id ifSteamManager.Initialized.ServicesRoot.InitializeServices(steamId)createsAudioServiceandSaveService.SaveManager.StartupAsync(betaVersion)loads/migrates player data and config.SaveManager.OnSaveReadynotifies systems such asAchievementManagerandShopManager.GameSettings.Instance.LoadSettingsFirstTime()applies saved settings.
Player Data Loading¶
- If the new encrypted save exists,
LoadNewEncryptedSave()reads encrypted text, decrypts, and deserializesPlayerData. - If only legacy binary save exists,
MigrateLegacySaveAsync()reads all legacy binary streams, merges data, saves into the new path. - If no save exists,
CreateNewPlayerData()initializes defaults from current data assets and saves.
Default Unlocks¶
PlayerData.InitializeDefaults():
- unlocks
SOLDIER,KNIGHT, andCELESTIALby default unless beta mode unlocks all. - unlocks
1_EASYand1_MEDIUMstages by default unless beta mode unlocks all. - unlocks special upgrades unless they require achievements.
- creates empty counters and shop/achievement dictionaries.
Run End Progression¶
GameManager.GameOver():
- Adds soul and chaos fragment rewards.
- Adds run counters through
GameEvents.OnDataCount. - Calls
GameDataCount.GameEndValueToSaveData(). - Calls
GameDataCount.GameDataToCheckAchievement(). - Calls
Save.SaveData(). - If boss defeated,
UnlockStage()mutates level/character assets and callsGameDataLoader.SaveResources().
Achievements¶
GameDataCount.GameDataToCheckAchievement()checks run and total thresholds.- It emits
GameEvents.OnAchievementTrigger(id). AchievementManager.UpdateAchievementProgress()unlocks the achievement, grants soul/upgrade rewards, queues popup, syncs Steam, and saves.
Shop¶
ShopManager.LoadDataShop()copies saved levels intoShopUpgrade.currentUpgradeLevel.- Buying/refunding updates
Save.SoulPoint, shop levels, and saves. ShopManager.ApplyShopModifiers()converts shop levels intoCharacterModifiersonGameSession.
Dependencies¶
- Save data depends on
GameDataLoaderbeing initialized before default save creation. - Unlock state is duplicated between save dictionaries and mutable ScriptableObject fields.
- Steam is used for nickname and achievements.
SaveServiceperforms synchronous file IO for most reads/writes.AchievementManagerandShopManagerare static and subscribe toSaveManager.OnSaveReady.
Problems / Risks¶
- Runtime resource mutation can drift from save data if not carefully loaded/saved.
Save.Nicknamedirectly calls Steam. If Steam is unavailable, nickname behavior needs confirmation.SaveServiceuses synchronous file operations; likely fine for small data, but avoid during gameplay hot paths.- Legacy migration uses
BinaryFormatter, which should not be carried forward. - Achievement and shop managers are static and event subscriptions are not obviously unsubscribed.
GameDataCount.GameEndValueToSaveData()appears to callGameEndCheckBestValue(GameManager.instance.level, DataCountType.BestScore); this may be intended as best level but usesBestScore. Needs confirmation.- Save dictionaries rely on string ids; id migrations need explicit mapping.
Migration Notes for Godot¶
Preserve:
- Save data shape and ids for compatibility where possible.
- Currencies: soul points and chaos fragments.
- Unlock dictionaries and counter ids.
- Old shop upgrade levels and costs only as save-conversion/reference data.
- Achievement ids and rewards.
- Stage unlock behavior.
Redesign:
SaveService
- load/save JSON or encrypted JSON
- versioned migration pipeline
- no BinaryFormatter equivalent
ProgressionState
- authoritative runtime copy loaded from save
- no resource mutation as source of truth
AchievementService
- evaluates rules or receives trigger ids
- queues popup signals
- optional platform adapter
ShopSaveConverter
- reads old shop upgrade levels during legacy save migration
- refunds old shop values into soul points
- does not recreate the old shop UI/system
Do not copy:
- Static
Savefacade as the only access path. - Steam calls inside generic save accessors.
- Runtime ScriptableObject/resource mutation as save authority.
Open Questions¶
- Exact legacy
.datplayer data and.jsonconfig schema to import. - Exact soul point refund value calculation for each old shop level.
- Whether Steam nickname should remain mandatory.
- Which counter ids are final and which are old/deprecated.