/// <summary> /// Initializes mod save data backups if required (if Everest was just updated past 2104). /// </summary> private static void checkModSaveDataBackups() { Version previousVersion; try { previousVersion = new Version(CoreModule.Settings.CurrentVersion); } catch { // oops, version is null or can't be parsed for any reason. previousVersion = new Version(0, 0, 0); } if (previousVersion < new Version(1, 2109, 0)) { // user just upgraded: create mod save data backups. // (this is very similar to OverworldLoader.CheckVariantsPostcardAtLaunch) Logger.Log("core", $"User just upgraded from version {previousVersion}: creating mod save data backups."); for (int i = 0; i < 3; i++) // only the first 3 saves really matter. { if (!UserIO.Exists(SaveData.GetFilename(i))) { continue; } SaveData saveData = UserIO.Load <SaveData>(SaveData.GetFilename(i), backup: false); if (saveData != null) { saveData.AfterInitialize(); UserIO.Save <ModSaveData>(SaveData.GetFilename(saveData.FileSlot) + "-modsavedata", UserIO.Serialize(new ModSaveData(saveData as patch_SaveData))); } } UserIO.Close(); SaveData.Instance = null; } }
private static void LoadCommand(string command, string[] args) { try { if (SaveData.Instance == null || (!Manager.allowUnsafeInput && SaveData.Instance.FileSlot != -1)) { int slot = SaveData.Instance == null ? -1 : SaveData.Instance.FileSlot; SaveData data = UserIO.Load <SaveData>(SaveData.GetFilename(slot)); SaveData.Start(data, -1); } AreaMode mode = AreaMode.Normal; if (command == "hard") { mode = AreaMode.BSide; } else if (command == "rmx2") { mode = AreaMode.CSide; } int levelID = GetLevelID(args[0]); if (args.Length > 1) { if (!int.TryParse(args[1], out int x)) { string screen = args[1]; if (screen.StartsWith("lvl_")) { screen = screen.Substring(4); } if (args.Length > 2) { int checkpoint = int.Parse(args[2]); Load(mode, levelID, screen, checkpoint); } else { Load(mode, levelID, screen); } } else if (args.Length > 2) { int y = int.Parse(args[2]); Load(mode, levelID, new Vector2(x, y)); } } else { Load(mode, levelID); } } catch { } }
public static new void InitializeDebugMode(bool loadExisting = true) { SaveData save = null; if (loadExisting && UserIO.Open(UserIO.Mode.Read)) { save = UserIO.Load <SaveData>(GetFilename(-1)); UserIO.Close(); } save = save ?? new SaveData(); save.DebugMode = true; Start(save, -1); }
public override void Update() { orig_Update(); // Slightly dirty place to perform this, but oh well... if (CoreModule.Settings.QuickRestart != null) { int slot = CoreModule.Settings.QuickRestart.Value; CoreModule.Settings.QuickRestart = null; CoreModule.Instance.SaveSettings(); SaveData save = UserIO.Load <SaveData>(SaveData.GetFilename(slot)); if (save != null) { SaveData.Start(save, slot); if (slot == -1) { save.DebugMode = true; } if (save.CurrentSession?.InArea ?? false) { Engine.Scene = new LevelLoader(save.CurrentSession); } else { Overworld.Goto <OuiChapterSelect>(); } } } if (!updateChecked && Everest.Updater.HasUpdate && Everest.Updater.Newest != null && alpha >= 1f) { updateChecked = true; updateTex = Everest.Updater.Newest.Branch == "stable" ? GFX.Gui["areas/new"] : GFX.Gui["areas/new-yellow"]; Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, 0.3f, true); tween.OnUpdate = t => { updateAlpha = t.Percent; }; Add(tween); } }
private static void CmdGiveHearts(int amount = -1, string levelSet = null) { SaveData saveData = SaveData.Instance; if (saveData == null) { saveData = UserIO.Load <SaveData>(SaveData.GetFilename(-1)) ?? new SaveData(); SaveData.Start(saveData, -1); } if (amount == -1) { amount = saveData.LevelSetStats.MaxHeartGems; } if (levelSet.IsNullOrEmpty()) { levelSet = saveData.GetLevelSet(); } int num = 0; foreach (AreaStats areaStats in saveData.Areas_Safe.Where(stats => stats.LevelSet == levelSet)) { foreach (AreaModeStats areaStatsMode in areaStats.Modes) { if (num < amount) { areaStatsMode.HeartGem = true; num++; } else { areaStatsMode.HeartGem = false; } } } }
public static T Load <T>(string path) where T : class => UserIO.Load <T>(path, false);
public new void AfterInitialize() { // Vanilla / new saves don't have the LevelSets list. if (LevelSets == null) { LevelSets = new List <LevelSetStats>(); } if (LevelSetRecycleBin == null) { LevelSetRecycleBin = new List <LevelSetStats>(); } if (LevelSets.Count <= 1 && LevelSetRecycleBin.Count == 0 && !HasModdedSaveData) { // the save file doesn't have any mod save data (just created, overwritten by vanilla, or Everest just updated). // we want to carry mod save data that was backed up in the mod save file, if any. ModSaveData modSaveData = UserIO.Load <ModSaveData>(GetFilename(FileSlot) + "-modsavedata"); if (modSaveData != null) { modSaveData.CopyToCelesteSaveData(this); Logger.Log(LogLevel.Warn, "SaveData", $"{LevelSets.Count} level set(s) were restored from mod backup for save slot {FileSlot}"); } } HasModdedSaveData = true; if (Areas_Unsafe == null) { Areas_Unsafe = new List <AreaStats>(); } // Add missing LevelSetStats. foreach (AreaData area in AreaData.Areas) { string set = area.GetLevelSet(); if (!LevelSets.Exists(other => other.Name == set)) { LevelSetStats recycleBinLevelSet = LevelSetRecycleBin.FirstOrDefault(other => other.Name == set); if (recycleBinLevelSet != null) { // the level set is actually in the recycle bin - restore it. LevelSets.Add(recycleBinLevelSet); LevelSetRecycleBin.Remove(recycleBinLevelSet); } else { // create a new LevelSetStats entry. LevelSets.Add(new LevelSetStats { Name = set, UnlockedAreas = set == "Celeste" ? UnlockedAreas_Unsafe : 0 }); } } } // Fill each LevelSetStats with its areas. for (int lsi = 0; lsi < LevelSets.Count; lsi++) { LevelSetStats set = LevelSets[lsi]; set.SaveData = this; List <AreaStats> areas = set.Areas; if (set.Name == "Celeste") { areas = Areas_Unsafe; } int offset = set.AreaOffset; if (offset == -1) { // LevelSet gone - let's move it to the recycle bin. LevelSetStats levelSetAlreadyInRecycleBin = LevelSetRecycleBin.FirstOrDefault(other => other.Name == set.Name); if (levelSetAlreadyInRecycleBin != null) { // a level set with the same name already exists in the recycle bin - replace it. LevelSetRecycleBin.Remove(levelSetAlreadyInRecycleBin); } LevelSetRecycleBin.Add(set); // now, remove it to prevent any unwanted access. LevelSets.RemoveAt(lsi); lsi--; continue; } // Refresh all stat IDs based on their SIDs, sort, fill and remove leftovers. // Temporarily use ID_Unsafe; later ID_Safe to ID_Unsafe to resync the SIDs. // This keeps the stats bound to their SIDs, not their indices, while removing non-existent areas. int countRoots = AreaData.Areas.Count(other => other.GetLevelSet() == set.Name && string.IsNullOrEmpty(other?.GetMeta()?.Parent)); int countAll = AreaData.Areas.Count(other => other.GetLevelSet() == set.Name); // Fix IDs for (int i = 0; i < areas.Count; i++) { AreaData area = AreaDataExt.Get(areas[i]); if (!string.IsNullOrEmpty(area?.GetMeta()?.Parent)) { area = null; } ((patch_AreaStats)areas[i]).ID_Unsafe = area?.ID ?? int.MaxValue; } // Sort areas.Sort((a, b) => ((patch_AreaStats)a).ID_Unsafe - ((patch_AreaStats)b).ID_Unsafe); // Remove leftovers while (areas.Count > 0 && ((patch_AreaStats)areas[areas.Count - 1]).ID_Unsafe == int.MaxValue) { areas.RemoveAt(areas.Count - 1); } // Fill gaps for (int i = 0; i < countRoots; i++) { if (i >= areas.Count || ((patch_AreaStats)areas[i]).ID_Unsafe != offset + i) { areas.Insert(i, new AreaStats(offset + i)); } } // Duplicate parent stat refs into their respective children slots. for (int i = countRoots; i < countAll; i++) { if (i >= areas.Count) { areas.Insert(i, areas[AreaDataExt.Get(AreaData.Get(offset + i).GetMeta().Parent).ID - offset]); } } // Resync SIDs for (int i = 0; i < areas.Count; i++) { ((patch_AreaStats)areas[i]).ID_Safe = ((patch_AreaStats)areas[i]).ID_Unsafe; } int lastCompleted = -1; for (int i = 0; i < countRoots; i++) { if (areas[i].Modes[0].Completed) { lastCompleted = i; } } if (set.Name == "Celeste") { if (UnlockedAreas_Unsafe < lastCompleted + 1 && set.MaxArea >= lastCompleted + 1) { UnlockedAreas_Unsafe = lastCompleted + 1; } if (DebugMode) { UnlockedAreas_Unsafe = set.MaxArea; } } else { if (set.UnlockedAreas < lastCompleted + 1 && set.MaxArea >= lastCompleted + 1) { set.UnlockedAreas = lastCompleted + 1; } if (DebugMode) { set.UnlockedAreas = set.MaxArea; } } foreach (AreaStats area in areas) { area.CleanCheckpoints(); } } // Assign SaveData for the level sets in the recycle bin to prevent crashes. foreach (LevelSetStats set in LevelSetRecycleBin) { set.SaveData = this; } // Order the levelsets to appear just as their areas appear in AreaData.Areas LevelSets.Sort((set1, set2) => set1.AreaOffset.CompareTo(set2.AreaOffset)); // If there is no mod progress, carry over any progress from vanilla saves. if (LastArea_Safe.ID == 0) { LastArea_Safe = LastArea_Unsafe; } if (CurrentSession_Safe == null) { CurrentSession_Safe = CurrentSession_Unsafe; } // Trick unmodded instances of Celeste to thinking that we last selected prologue / played no level. LastArea_Unsafe = AreaKey.Default; CurrentSession_Unsafe = null; // Fix areas with missing SID (f.e. deleted or renamed maps). if (AreaData.Get(LastArea) == null) { LastArea = AreaKey.Default; } // Fix out of bounds areas. if (LastArea.ID < 0 || LastArea.ID >= AreaData.Areas.Count) { LastArea = AreaKey.Default; } if (string.IsNullOrEmpty(TheoSisterName)) { TheoSisterName = Dialog.Clean("THEO_SISTER_NAME", null); if (Name.IndexOf(TheoSisterName, StringComparison.InvariantCultureIgnoreCase) >= 0) { TheoSisterName = Dialog.Clean("THEO_SISTER_ALT_NAME", null); } } AssistModeChecks(); if (Version != null) { Version v = new Version(Version); if (v < new Version(1, 2, 1, 1)) { for (int id = 0; id < Areas_Unsafe.Count; id++) { AreaStats area = Areas_Unsafe[id]; if (area == null) { continue; } for (int modei = 0; modei < area.Modes.Length; modei++) { AreaModeStats mode = area.Modes[modei]; if (mode == null) { continue; } if (mode.BestTime > 0L) { mode.SingleRunCompleted = true; } mode.BestTime = 0L; mode.BestFullClearTime = 0L; } } } } }
private static void LoadCommand(string command, string[] args) { try { if (SaveData.Instance == null || (!Manager.AllowUnsafeInput && SaveData.Instance.FileSlot != -1)) { int slot = SaveData.Instance == null ? -1 : SaveData.Instance.FileSlot; SaveData data = UserIO.Load <SaveData>(SaveData.GetFilename(slot)); SaveData.Start(data, -1); // Complete Prologue if incomplete LevelSetStats stats = SaveData.Instance.GetLevelSetStatsFor("Celeste"); if (stats.UnlockedAreas == 0) { stats.UnlockedAreas = 1; stats.AreasIncludingCeleste[0].Modes[0].Completed = true; } } AreaMode mode = AreaMode.Normal; if (command == "hard") { mode = AreaMode.BSide; } else if (command == "rmx2") { mode = AreaMode.CSide; } int levelId = GetLevelId(args[0]); if (args.Length > 1) { if (!int.TryParse(args[1], out int x) || args.Length == 2) { string screen = args[1]; if (screen.StartsWith("lvl_")) { screen = screen.Substring(4); } if (args.Length > 2) { int checkpoint = int.Parse(args[2]); Load(mode, levelId, screen, checkpoint); } else { Load(mode, levelId, screen); } } else if (args.Length > 2) { int y = int.Parse(args[2]); Load(mode, levelId, new Vector2(x, y)); } } else { Load(mode, levelId); } } catch { } }
private static void LoadCommand(string command, string[] args) { try { if (SaveData.Instance == null || !Manager.AllowUnsafeInput && SaveData.Instance.FileSlot != -1) { SaveData data = SaveData.Instance ?? UserIO.Load <SaveData>(SaveData.GetFilename(-1)) ?? new SaveData(); if (SaveData.Instance?.FileSlot is { } slot&& slot != -1) { SaveData.TryDelete(-1); SaveData.LoadedModSaveDataIndex = -1; foreach (EverestModule module in Everest.Modules) { if (module._Session != null) { module._Session.Index = -1; } if (module._SaveData != null) { module._SaveData.Index = -1; } } SaveData.Instance = data; SaveData.Instance.FileSlot = -1; UserIO.SaveHandler(true, true); } else { SaveData.Start(data, -1); } // Complete Prologue if incomplete and make sure the return to map menu item will be shown LevelSetStats stats = data.GetLevelSetStatsFor("Celeste"); if (!data.Areas[0].Modes[0].Completed) { data.Areas[0].Modes[0].Completed = true; stats.UnlockedAreas++; } } AreaMode mode = AreaMode.Normal; if (command.Equals("hard", StringComparison.InvariantCultureIgnoreCase)) { mode = AreaMode.BSide; } else if (command.Equals("rmx2", StringComparison.InvariantCultureIgnoreCase)) { mode = AreaMode.CSide; } int levelId = GetLevelId(args[0]); if (args.Length > 1) { if (!double.TryParse(args[1], out double x) || args.Length == 2) { string screen = args[1]; if (screen.StartsWith("lvl_")) { screen = screen.Substring(4); } if (args.Length > 2) { int spawnpoint = int.Parse(args[2]); Load(mode, levelId, screen, spawnpoint); } else { Load(mode, levelId, screen); } } else if (args.Length > 2 && double.TryParse(args[2], out double y)) { Vector2 position = new((int)Math.Round(x), (int)Math.Round(y)); Vector2 remainder = new((float)(x - position.X), (float)(y - position.Y)); Vector2 speed = Vector2.Zero; if (args.Length > 3 && float.TryParse(args[3], out float speedX)) { speed.X = speedX; } if (args.Length > 4 && float.TryParse(args[4], out float speedY)) { speed.Y = speedY; } Load(mode, levelId, position, remainder, speed); } } else { Load(mode, levelId); } } catch {
public override void Update() { orig_Update(); // Slightly dirty place to perform this, but oh well... if (CoreModule.Settings.QuickRestart != null) { int slot = CoreModule.Settings.QuickRestart.Value; CoreModule.Settings.QuickRestart = null; CoreModule.Instance.SaveSettings(); SaveData save = UserIO.Load <SaveData>(SaveData.GetFilename(slot)); if (save != null) { SaveData.Start(save, slot); if (slot == -1) { save.DebugMode = true; } if (save.CurrentSession?.InArea ?? false) { LevelEnter.Go(save.CurrentSession, true); } else { Overworld.Goto <OuiChapterSelect>(); } } } if (!updateChecked && Everest.Updater.HasUpdate && Everest.Updater.Newest != null && alpha >= 1f) { updateChecked = true; updateTex = Everest.Updater.Newest.Branch == "stable" ? GFX.Gui["areas/new"] : GFX.Gui["areas/new-yellow"]; Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, 0.3f, true); tween.OnUpdate = t => { updateAlpha = t.Percent; }; Add(tween); } if (alpha >= 1f && Selected && Input.MenuRight && arrowToVanilla != null && warningMessageMenu == null) { switchingToVanillaBack = Math.Max(0f, switchingToVanillaBack - Engine.DeltaTime * 8f); switchingToVanilla += Engine.DeltaTime; if (switchingToVanilla >= switchingToVanillaDuration && !Everest.RestartVanilla) { if (CoreModule.Settings.RestartIntoVanillaWarningShown) { restartIntoVanilla(); } else { warningMessageMenu = new TextMenu(); Action onCancel = () => { // remove the menu Scene.Remove(warningMessageMenu); warningMessageMenu.Visible = false; warningMessageMenu = null; hideConfirmButton = false; // revert the "switch to vanilla" animation switchingToVanilla = 0f; switchingToVanillaBack = 0f; // fade the vanilla title screen back in alpha = 0f; Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, 0.6f, start: true); tween.OnUpdate = t => { alpha = t.Percent; textY = MathHelper.Lerp(1200f, 1000f, t.Eased); }; Add(tween); }; warningMessageMenu.OnESC = warningMessageMenu.OnCancel = () => { Audio.Play(SFX.ui_main_button_back); onCancel(); }; warningMessageMenu.Add(new TextMenu.Button(Dialog.Clean("MENU_TITLESCREEN_OK")).Pressed(() => { if (!CoreModule.Settings.RestartIntoVanillaWarningShown) { CoreModule.Settings.RestartIntoVanillaWarningShown = true; CoreModule.Instance.SaveSettings(); } warningMessageMenu.Focused = false; restartIntoVanilla(); })); warningMessageMenu.Add(new TextMenu.Button(Dialog.Clean("MENU_TITLESCREEN_CANCEL")).Pressed(onCancel)); Scene.Add(warningMessageMenu); hideConfirmButton = true; } } } else if (switchingToVanilla < switchingToVanillaDuration) { if (switchingToVanilla > 0f) { switchingToVanillaBack = Math.Max(switchingToVanilla, switchingToVanillaBack); } switchingToVanillaBack = Math.Max(0f, switchingToVanillaBack - Engine.DeltaTime * 4f); switchingToVanilla = 0f; } warningEase = Calc.Approach(warningEase, warningMessageMenu != null ? 1f : 0f, Engine.DeltaTime); }
public override void Update() { orig_Update(); // Slightly dirty place to perform this, but oh well... if (CoreModule.Settings.QuickRestart != null) { int slot = CoreModule.Settings.QuickRestart.Value; CoreModule.Settings.QuickRestart = null; if (CoreModule.Settings.SaveDataFlush ?? false) { CoreModule.Instance.ForceSaveDataFlush++; } CoreModule.Instance.SaveSettings(); SaveData save = UserIO.Load <SaveData>(SaveData.GetFilename(slot)); if (save != null) { SaveData.Start(save, slot); if (slot == -1) { save.DebugMode = true; } if (save.CurrentSession?.InArea ?? false) { LevelEnter.Go(save.CurrentSession, true); } else { Overworld.Goto <OuiChapterSelect>(); } } } if (!updateChecked && Everest.Updater.HasUpdate && Everest.Updater.Newest != null && alpha >= 1f) { updateChecked = true; updateTex = Everest.Updater.Newest.Branch == "stable" ? GFX.Gui["areas/new"] : GFX.Gui["areas/new-yellow"]; Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, 0.3f, true); tween.OnUpdate = t => { updateAlpha = t.Percent; }; Add(tween); } if (alpha >= 1f && Selected && Input.MenuRight && arrowToVanilla != null) { switchingToVanillaBack = Math.Max(0f, switchingToVanillaBack - Engine.DeltaTime * 8f); switchingToVanilla += Engine.DeltaTime; if (switchingToVanilla >= switchingToVanillaDuration && !Everest.RestartVanilla) { Everest.RestartVanilla = true; new FadeWipe(Scene, false, () => { Engine.Scene = new Scene(); if (Everest.Flags.IsXNA && Engine.Graphics.IsFullScreen) { Engine.SetWindowed(320 * Settings.Instance.WindowScale, 180 * Settings.Instance.WindowScale); } Engine.Instance.Exit(); }); } } else if (switchingToVanilla < switchingToVanillaDuration) { if (switchingToVanilla > 0f) { switchingToVanillaBack = Math.Max(switchingToVanilla, switchingToVanillaBack); } switchingToVanillaBack = Math.Max(0f, switchingToVanillaBack - Engine.DeltaTime * 4f); switchingToVanilla = 0f; } }
private static void LoadCommand(string command, string[] args) { try { if (SaveData.Instance == null || !Manager.AllowUnsafeInput && SaveData.Instance.FileSlot != -1) { int slot = SaveData.Instance == null ? -1 : SaveData.Instance.FileSlot; SaveData data = UserIO.Load <SaveData>(SaveData.GetFilename(slot)) ?? new SaveData(); SaveData.Start(data, -1); // Complete Prologue if incomplete LevelSetStats stats = SaveData.Instance.GetLevelSetStatsFor("Celeste"); if (stats.UnlockedAreas == 0) { stats.UnlockedAreas = 1; stats.AreasIncludingCeleste[0].Modes[0].Completed = true; } } AreaMode mode = AreaMode.Normal; if (command == "hard") { mode = AreaMode.BSide; } else if (command == "rmx2") { mode = AreaMode.CSide; } int levelId = GetLevelId(args[0]); if (args.Length > 1) { if (!double.TryParse(args[1], out double x) || args.Length == 2) { string screen = args[1]; if (screen.StartsWith("lvl_")) { screen = screen.Substring(4); } if (args.Length > 2) { int spawnpoint = int.Parse(args[2]); Load(mode, levelId, screen, spawnpoint); } else { Load(mode, levelId, screen); } } else if (args.Length > 2 && double.TryParse(args[2], out double y)) { Vector2 position = new((int)Math.Round(x), (int)Math.Round(y)); Vector2 remainder = new((float)(x - Math.Truncate(x) + (int)x - (int)Math.Round(x)), (float)(y - Math.Truncate(y) + (int)y - (int)Math.Round(y))); Vector2 speed = Vector2.Zero; if (args.Length > 3 && float.TryParse(args[3], out float speedX)) { speed.X = speedX; } if (args.Length > 4 && float.TryParse(args[4], out float speedY)) { speed.Y = speedY; } Load(mode, levelId, position, remainder, speed); } } else { Load(mode, levelId); } } catch { // ignored } }