IEnumerator LoadGame(string path) { GameManager.Instance.PlayerDeath.ClearDeathAnimation(); GameManager.Instance.PlayerMotor.CancelMovement = true; InputManager.Instance.ClearAllActions(); QuestMachine.Instance.ClearState(); stateManager.ClearSceneCache(); PlayerEntity playerEntity = GameManager.Instance.PlayerEntity; playerEntity.Reset(); // Read save data from files string saveDataJson = ReadSaveFile(Path.Combine(path, saveDataFilename)); string factionDataJson = ReadSaveFile(Path.Combine(path, factionDataFilename)); string questDataJson = ReadSaveFile(Path.Combine(path, questDataFilename)); string discoveryDataJson = ReadSaveFile(Path.Combine(path, discoveryDataFilename)); string conversationDataJson = ReadSaveFile(Path.Combine(path, conversationDataFilename)); string notebookDataJson = ReadSaveFile(Path.Combine(path, notebookDataFilename)); // Load backstory text playerEntity.BackStory = new List <string>(); if (File.Exists(Path.Combine(path, bioFileName))) { StreamReader file = new StreamReader(Path.Combine(path, bioFileName).ToString()); string line; while ((line = file.ReadLine()) != null) { playerEntity.BackStory.Add(line); } file.Close(); } // Deserialize JSON strings SaveData_v1 saveData = Deserialize(typeof(SaveData_v1), saveDataJson) as SaveData_v1; // Must have a serializable player if (!stateManager.SerializablePlayer) { yield break; } // Call start load event RaiseOnStartLoadEvent(saveData); // Immediately set date so world is loaded with correct season RestoreDateTimeData(saveData.dateAndTime); // Restore discovery data if (!string.IsNullOrEmpty(discoveryDataJson)) { Dictionary <int, PlayerGPS.DiscoveredLocation> discoveryData = Deserialize(typeof(Dictionary <int, PlayerGPS.DiscoveredLocation>), discoveryDataJson) as Dictionary <int, PlayerGPS.DiscoveredLocation>; GameManager.Instance.PlayerGPS.RestoreDiscoveryData(discoveryData); } else { // Clear discovery data when not in save, or live state will be retained from previous session GameManager.Instance.PlayerGPS.ClearDiscoveryData(); } // Must have PlayerEnterExit to respawn player at saved location PlayerEnterExit playerEnterExit = stateManager.SerializablePlayer.GetComponent <PlayerEnterExit>(); if (!playerEnterExit) { yield break; } // Restore building summary, house ownership, and guild membership early for interior layout code if (saveData.playerData.playerPosition.insideBuilding) { playerEnterExit.BuildingDiscoveryData = saveData.playerData.playerPosition.buildingDiscoveryData; playerEnterExit.IsPlayerInsideOpenShop = saveData.playerData.playerPosition.insideOpenShop; if (saveData.bankDeeds != null) { RestoreHousesData(saveData.bankDeeds.houses); } GameManager.Instance.GuildManager.RestoreMembershipData(saveData.playerData.guildMemberships); } // Restore faction data to player entity // This is done early as later objects may require faction information on restore if (!string.IsNullOrEmpty(factionDataJson)) { FactionData_v2 factionData = Deserialize(typeof(FactionData_v2), factionDataJson) as FactionData_v2; stateManager.RestoreFactionData(factionData); Debug.Log("LoadGame() restored faction state from save."); } else { Debug.Log("LoadGame() did not find saved faction data. Player will resume with default faction state."); } // Restore quest machine state if (!string.IsNullOrEmpty(questDataJson)) { QuestMachine.QuestMachineData_v1 questData = Deserialize(typeof(QuestMachine.QuestMachineData_v1), questDataJson) as QuestMachine.QuestMachineData_v1; QuestMachine.Instance.RestoreSaveData(questData); } // Restore conversation data (must be done after quest data restoration) if (!string.IsNullOrEmpty(conversationDataJson)) { TalkManager.SaveDataConversation conversationData = Deserialize(typeof(TalkManager.SaveDataConversation), conversationDataJson) as TalkManager.SaveDataConversation; GameManager.Instance.TalkManager.RestoreConversationData(conversationData); } else { GameManager.Instance.TalkManager.RestoreConversationData(null); } // Restore notebook data if (!string.IsNullOrEmpty(notebookDataJson)) { PlayerNotebook.NotebookData_v1 notebookData = Deserialize(typeof(PlayerNotebook.NotebookData_v1), notebookDataJson) as PlayerNotebook.NotebookData_v1; playerEntity.Notebook.RestoreNotebookData(notebookData); } // Restore player position to world playerEnterExit.RestorePositionHelper(saveData.playerData.playerPosition, true, false); //Restore Travel Map settings DaggerfallUI.Instance.DfTravelMapWindow.SetTravelMapFromSaveData(saveData.travelMapData); // Smash to black while respawning DaggerfallUI.Instance.FadeBehaviour.SmashHUDToBlack(); // Keep yielding frames until world is ready again while (playerEnterExit.IsRespawning) { yield return(new WaitForEndOfFrame()); } // Wait another frame so everthing has a chance to register yield return(new WaitForEndOfFrame()); // Restore save data to objects in newly spawned world RestoreSaveData(saveData); // Load automap state try { string automapDataJson = ReadSaveFile(Path.Combine(path, automapDataFilename)); Dictionary <string, Automap.AutomapDungeonState> automapState = null; if (!string.IsNullOrEmpty(automapDataJson)) { automapState = Deserialize(typeof(Dictionary <string, Automap.AutomapDungeonState>), automapDataJson) as Dictionary <string, Automap.AutomapDungeonState>; } if (automapState != null) { GameManager.Instance.InteriorAutomap.SetState(automapState); } } catch (Exception ex) { string message = string.Format("Failed to load automap state. Message: {0}", ex.Message); Debug.Log(message); } // Clear any orphaned quest items RemoveAllOrphanedItems(); // Check mod manager is available if (ModManager.Instance != null) { // Restore mod data foreach (Mod mod in ModManager.Instance.GetAllModsWithSaveData()) { string modDataPath = Path.Combine(path, GetModDataFilename(mod)); object modData; if (File.Exists(modDataPath)) { modData = Deserialize(mod.SaveDataInterface.SaveDataType, ReadSaveFile(modDataPath)); } else { modData = mod.SaveDataInterface.NewSaveData(); } mod.SaveDataInterface.RestoreSaveData(modData); } } // Clamp legal reputation playerEntity.ClampLegalReputations(); // Lower load in progress flag loadInProgress = false; // Fade out from black DaggerfallUI.Instance.FadeBehaviour.FadeHUDFromBlack(1.0f); // Raise OnLoad event RaiseOnLoadEvent(saveData); }
IEnumerator SaveGame(string saveName, string path) { // Build save data SaveData_v1 saveData = BuildSaveData(); // Build save info SaveInfo_v1 saveInfo = new SaveInfo_v1(); saveInfo.saveVersion = LatestSaveVersion; saveInfo.saveName = saveName; saveInfo.characterName = saveData.playerData.playerEntity.name; saveInfo.dateAndTime = saveData.dateAndTime; // Build faction data FactionData_v2 factionData = stateManager.GetPlayerFactionData(); // Build quest data QuestMachine.QuestMachineData_v1 questData = QuestMachine.Instance.GetSaveData(); // Get discovery data Dictionary <int, PlayerGPS.DiscoveredLocation> discoveryData = GameManager.Instance.PlayerGPS.GetDiscoverySaveData(); // Get conversation data TalkManager.SaveDataConversation conversationData = GameManager.Instance.TalkManager.GetConversationSaveData(); // Serialize save data to JSON strings string saveDataJson = Serialize(saveData.GetType(), saveData); string saveInfoJson = Serialize(saveInfo.GetType(), saveInfo); string factionDataJson = Serialize(factionData.GetType(), factionData); string questDataJson = Serialize(questData.GetType(), questData); string discoveryDataJson = Serialize(discoveryData.GetType(), discoveryData); string conversationDataJson = Serialize(conversationData.GetType(), conversationData); // Create screenshot for save // TODO: Hide UI for screenshot or use a different method yield return(new WaitForEndOfFrame()); Texture2D screenshot = new Texture2D(Screen.width, Screen.height); screenshot.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); screenshot.Apply(); // Save data to files WriteSaveFile(Path.Combine(path, saveDataFilename), saveDataJson); WriteSaveFile(Path.Combine(path, saveInfoFilename), saveInfoJson); WriteSaveFile(Path.Combine(path, factionDataFilename), factionDataJson); WriteSaveFile(Path.Combine(path, questDataFilename), questDataJson); WriteSaveFile(Path.Combine(path, discoveryDataFilename), discoveryDataJson); WriteSaveFile(Path.Combine(path, conversationDataFilename), conversationDataJson); // Save automap state try { Dictionary <string, DaggerfallAutomap.AutomapGeometryDungeonState> automapState = GameManager.Instance.InteriorAutomap.GetState(); string automapDataJson = Serialize(automapState.GetType(), automapState); WriteSaveFile(Path.Combine(path, automapDataFilename), automapDataJson); } catch (Exception ex) { string message = string.Format("Failed to save automap state. Message: {0}", ex.Message); Debug.Log(message); } // Save screenshot byte[] bytes = screenshot.EncodeToJPG(); File.WriteAllBytes(Path.Combine(path, screenshotFilename), bytes); // Raise OnSaveEvent RaiseOnSaveEvent(saveData); // Notify DaggerfallUI.Instance.PopupMessage(HardStrings.gameSaved); }
IEnumerator SaveGame(string saveName, string path, bool instantReload = false) { // Build save data SaveData_v1 saveData = BuildSaveData(); // Build save info SaveInfo_v1 saveInfo = new SaveInfo_v1(); saveInfo.saveVersion = LatestSaveVersion; saveInfo.saveName = saveName; saveInfo.characterName = saveData.playerData.playerEntity.name; saveInfo.dateAndTime = saveData.dateAndTime; // Build faction data FactionData_v2 factionData = stateManager.GetPlayerFactionData(); // Build quest data QuestMachine.QuestMachineData_v1 questData = QuestMachine.Instance.GetSaveData(); // Get discovery data Dictionary <int, PlayerGPS.DiscoveredLocation> discoveryData = GameManager.Instance.PlayerGPS.GetDiscoverySaveData(); // Get conversation data TalkManager.SaveDataConversation conversationData = GameManager.Instance.TalkManager.GetConversationSaveData(); // Get notebook data PlayerNotebook.NotebookData_v1 notebookData = GameManager.Instance.PlayerEntity.Notebook.GetNotebookSaveData(); // Serialize save data to JSON strings string saveDataJson = Serialize(saveData.GetType(), saveData); string saveInfoJson = Serialize(saveInfo.GetType(), saveInfo); string factionDataJson = Serialize(factionData.GetType(), factionData); string questDataJson = Serialize(questData.GetType(), questData); string discoveryDataJson = Serialize(discoveryData.GetType(), discoveryData); string conversationDataJson = Serialize(conversationData.GetType(), conversationData); string notebookDataJson = Serialize(notebookData.GetType(), notebookData); //// Attempt to hide UI for screenshot //bool rawImageEnabled = false; //UnityEngine.UI.RawImage rawImage = GUI.GetDiegeticCanvasRawImage(); //if (rawImage) //{ // rawImageEnabled = rawImage.enabled; // rawImage.enabled = false; //} // Create screenshot for save // TODO: Hide UI for screenshot or use a different method yield return(new WaitForEndOfFrame()); yield return(new WaitForEndOfFrame()); Texture2D screenshot = new Texture2D(Screen.width, Screen.height); screenshot.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); screenshot.Apply(); //// Restore UI after screenshot //if (rawImageEnabled) //{ // rawImage.enabled = true; //} // Save data to files WriteSaveFile(Path.Combine(path, saveDataFilename), saveDataJson); WriteSaveFile(Path.Combine(path, saveInfoFilename), saveInfoJson); WriteSaveFile(Path.Combine(path, factionDataFilename), factionDataJson); WriteSaveFile(Path.Combine(path, questDataFilename), questDataJson); WriteSaveFile(Path.Combine(path, discoveryDataFilename), discoveryDataJson); WriteSaveFile(Path.Combine(path, conversationDataFilename), conversationDataJson); WriteSaveFile(Path.Combine(path, notebookDataFilename), notebookDataJson); // Save backstory text if (!File.Exists(Path.Combine(path, bioFileName))) { StreamWriter file = new StreamWriter(Path.Combine(path, bioFileName).ToString()); foreach (string line in GameManager.Instance.PlayerEntity.BackStory) { file.WriteLine(line); } file.Close(); } // Save automap state try { Dictionary <string, Automap.AutomapDungeonState> automapState = GameManager.Instance.InteriorAutomap.GetState(); string automapDataJson = Serialize(automapState.GetType(), automapState); WriteSaveFile(Path.Combine(path, automapDataFilename), automapDataJson); } catch (Exception ex) { string message = string.Format("Failed to save automap state. Message: {0}", ex.Message); Debug.Log(message); } // Save mod data if (ModManager.Instance != null) { foreach (Mod mod in ModManager.Instance.GetAllModsWithSaveData()) { object modData = mod.SaveDataInterface.GetSaveData(); if (modData != null) { string modDataJson = Serialize(modData.GetType(), modData); WriteSaveFile(Path.Combine(path, GetModDataFilename(mod)), modDataJson); } else { File.Delete(Path.Combine(path, GetModDataFilename(mod))); } } } // Save screenshot byte[] bytes = screenshot.EncodeToJPG(); File.WriteAllBytes(Path.Combine(path, screenshotFilename), bytes); // Raise OnSaveEvent RaiseOnSaveEvent(saveData); // Notify DaggerfallUI.Instance.PopupMessage(HardStrings.gameSaved); // Reload this save instantly if requested if (instantReload) { Load(saveData.playerData.playerEntity.name, saveName); } }
IEnumerator LoadGame(string path) { GameManager.Instance.PlayerDeath.ClearDeathAnimation(); GameManager.Instance.PlayerMotor.CancelMovement = true; InputManager.Instance.ClearAllActions(); QuestMachine.Instance.ClearState(); stateManager.ClearSceneCache(); // Read save data from files string saveDataJson = ReadSaveFile(Path.Combine(path, saveDataFilename)); string factionDataJson = ReadSaveFile(Path.Combine(path, factionDataFilename)); string questDataJson = ReadSaveFile(Path.Combine(path, questDataFilename)); string discoveryDataJson = ReadSaveFile(Path.Combine(path, discoveryDataFilename)); string conversationDataJson = ReadSaveFile(Path.Combine(path, conversationDataFilename)); // Deserialize JSON strings SaveData_v1 saveData = Deserialize(typeof(SaveData_v1), saveDataJson) as SaveData_v1; // Must have a serializable player if (!stateManager.SerializablePlayer) { yield break; } // Call start load event RaiseOnStartLoadEvent(saveData); // Immediately set date so world is loaded with correct season RestoreDateTimeData(saveData.dateAndTime); // Restore discovery data if (!string.IsNullOrEmpty(discoveryDataJson)) { Dictionary <int, PlayerGPS.DiscoveredLocation> discoveryData = Deserialize(typeof(Dictionary <int, PlayerGPS.DiscoveredLocation>), discoveryDataJson) as Dictionary <int, PlayerGPS.DiscoveredLocation>; GameManager.Instance.PlayerGPS.RestoreDiscoveryData(discoveryData); } else { // Clear discovery data when not in save, or live state will be retained from previous session GameManager.Instance.PlayerGPS.ClearDiscoveryData(); } // Restore conversation data if (!string.IsNullOrEmpty(conversationDataJson)) { TalkManager.SaveDataConversation conversationData = Deserialize(typeof(TalkManager.SaveDataConversation), conversationDataJson) as TalkManager.SaveDataConversation; GameManager.Instance.TalkManager.RestoreConversationData(conversationData); } else { GameManager.Instance.TalkManager.RestoreConversationData(null); } // Must have PlayerEnterExit to respawn player at saved location PlayerEnterExit playerEnterExit = stateManager.SerializablePlayer.GetComponent <PlayerEnterExit>(); if (!playerEnterExit) { yield break; } // Check exterior doors are included in save, we need these to exit building bool hasExteriorDoors; if (saveData.playerData.playerPosition.exteriorDoors == null || saveData.playerData.playerPosition.exteriorDoors.Length == 0) { hasExteriorDoors = false; } else { hasExteriorDoors = true; } // Restore building summary & house ownership early for interior layout code if (saveData.playerData.playerPosition.insideBuilding) { playerEnterExit.BuildingDiscoveryData = saveData.playerData.playerPosition.buildingDiscoveryData; playerEnterExit.IsPlayerInsideOpenShop = saveData.playerData.playerPosition.insideOpenShop; if (saveData.bankDeeds != null) { RestoreHousesData(saveData.bankDeeds.houses); } } // Restore faction data to player entity // This is done early as later objects may require faction information on restore if (!string.IsNullOrEmpty(factionDataJson)) { FactionData_v2 factionData = Deserialize(typeof(FactionData_v2), factionDataJson) as FactionData_v2; stateManager.RestoreFactionData(factionData); Debug.Log("LoadGame() restored faction state from save."); } else { Debug.Log("LoadGame() did not find saved faction data. Player will resume with default faction state."); } // Restore quest machine state if (!string.IsNullOrEmpty(questDataJson)) { QuestMachine.QuestMachineData_v1 questData = Deserialize(typeof(QuestMachine.QuestMachineData_v1), questDataJson) as QuestMachine.QuestMachineData_v1; QuestMachine.Instance.RestoreSaveData(questData); } // Raise reposition flag if terrain sampler changed // This is required as changing terrain samplers will invalidate serialized player coordinates bool repositionPlayer = false; if (saveData.playerData.playerPosition.terrainSamplerName != DaggerfallUnity.Instance.TerrainSampler.ToString() || saveData.playerData.playerPosition.terrainSamplerVersion != DaggerfallUnity.Instance.TerrainSampler.Version) { repositionPlayer = true; if (DaggerfallUI.Instance.DaggerfallHUD != null) { DaggerfallUI.Instance.DaggerfallHUD.PopupText.AddText("Terrain sampler changed. Repositioning player."); } } // Raise reposition flag if player is supposed to start indoors but building has no doors if (saveData.playerData.playerPosition.insideBuilding && !hasExteriorDoors) { repositionPlayer = true; if (DaggerfallUI.Instance.DaggerfallHUD != null) { DaggerfallUI.Instance.DaggerfallHUD.PopupText.AddText("Building has no exterior doors. Repositioning player."); } } // Start the respawn process based on saved player location if (saveData.playerData.playerPosition.insideDungeon && !repositionPlayer) { // Start in dungeon playerEnterExit.RespawnPlayer( saveData.playerData.playerPosition.worldPosX, saveData.playerData.playerPosition.worldPosZ, true, false); } else if (saveData.playerData.playerPosition.insideBuilding && hasExteriorDoors && !repositionPlayer) { // Start in building playerEnterExit.RespawnPlayer( saveData.playerData.playerPosition.worldPosX, saveData.playerData.playerPosition.worldPosZ, saveData.playerData.playerPosition.insideDungeon, saveData.playerData.playerPosition.insideBuilding, saveData.playerData.playerPosition.exteriorDoors); } else { // Start outside playerEnterExit.RespawnPlayer( saveData.playerData.playerPosition.worldPosX, saveData.playerData.playerPosition.worldPosZ, false, false, null, repositionPlayer); } // Smash to black while respawning DaggerfallUI.Instance.SmashHUDToBlack(); // Keep yielding frames until world is ready again while (playerEnterExit.IsRespawning) { yield return(new WaitForEndOfFrame()); } // Wait another frame so everthing has a chance to register yield return(new WaitForEndOfFrame()); // Restore save data to objects in newly spawned world RestoreSaveData(saveData); // Load automap state try { string automapDataJson = ReadSaveFile(Path.Combine(path, automapDataFilename)); Dictionary <string, DaggerfallAutomap.AutomapGeometryDungeonState> automapState = null; if (!string.IsNullOrEmpty(automapDataJson)) { automapState = Deserialize(typeof(Dictionary <string, DaggerfallAutomap.AutomapGeometryDungeonState>), automapDataJson) as Dictionary <string, DaggerfallAutomap.AutomapGeometryDungeonState>; } if (automapState != null) { GameManager.Instance.InteriorAutomap.SetState(automapState); } } catch (Exception ex) { string message = string.Format("Failed to load automap state. Message: {0}", ex.Message); Debug.Log(message); } // Clear any orphaned quest items RemoveAllOrphanedQuestItems(); // Lower load in progress flag loadInProgress = false; // Fade out from black DaggerfallUI.Instance.FadeHUDFromBlack(1.0f); // Raise OnLoad event RaiseOnLoadEvent(saveData); }