Exemple #1
0
        private static void CmdPrintCounts()
        {
            if (SaveData.Instance == null)
            {
                Engine.Commands.Log("No save loaded!");
                return;
            }

            LevelSetStats stats = SaveData.Instance.GetLevelSetStats();

            Engine.Commands.Log($"** Level Set Stats: {stats.Name} **");
            Engine.Commands.Log($"Max strawberry count = {stats.MaxStrawberries}");
            Engine.Commands.Log($"Max golden strawberry count = {stats.MaxGoldenStrawberries}");
            Engine.Commands.Log($"Max strawberry count including untracked = {stats.MaxStrawberriesIncludingUntracked}");
            Engine.Commands.Log($"Max cassettes = {stats.MaxCassettes}");
            Engine.Commands.Log($"Max crystal hearts = {stats.MaxHeartGems}");
            Engine.Commands.Log($"Max crystal hearts excluding C-sides = {stats.MaxHeartGemsExcludingCSides}");
            Engine.Commands.Log($"Chapter count = {stats.MaxCompletions}");
            Engine.Commands.Log("====");
            Engine.Commands.Log($"Owned strawberries = {stats.TotalStrawberries}");
            Engine.Commands.Log($"Owned golden strawberries = {stats.TotalGoldenStrawberries}");
            Engine.Commands.Log($"Owned cassettes = {stats.TotalCassettes}");
            Engine.Commands.Log($"Owned crystal hearts = {stats.TotalHeartGems}");
            Engine.Commands.Log($"Completed chapters = {stats.TotalCompletions}");
            Engine.Commands.Log("====");
            Engine.Commands.Log($"Completion percent = {stats.CompletionPercent}");
        }
Exemple #2
0
        private IEnumerator EnterFirstAreaRoutine()
        {
            // Replace ID 0 with SaveData.Instance.LastArea.ID

            Overworld overworld = fileSelect.Overworld;
            AreaData  area      = AreaData.Areas[SaveData.Instance.LastArea.ID];

            if (area.GetLevelSet() != "Celeste")
            {
                // Pretend that we've beaten Prologue.
                LevelSetStats stats = SaveData.Instance.GetLevelSetStatsFor("Celeste");
                stats.UnlockedAreas = 1;
                stats.AreasIncludingCeleste[0].Modes[0].Completed = true;
            }

            yield return(fileSelect.Leave(null));

            yield return(overworld.Mountain.EaseCamera(0, area.MountainIdle));

            yield return(0.3f);

            overworld.Mountain.EaseCamera(0, area.MountainZoom, 1f);
            yield return(0.4f);

            area.Wipe(overworld, false, null);
            overworld.RendererList.UpdateLists();
            overworld.RendererList.MoveToFront(overworld.Snow);

            yield return(0.5f);

            LevelEnter.Go(new Session(SaveData.Instance.LastArea), false);
        }
Exemple #3
0
        public void ctor(int index, OuiFileSelect fileSelect, SaveData data)
        {
            // Temporarily set the current save data to the file slot's save data.
            // This enables filtering the areas by the save data's current levelset.
            SaveData prev = SaveData.Instance;

            SaveData.Instance = data;

            orig_ctor(index, fileSelect, data);

            LevelSetStats stats = data?.GetLevelSetStats();

            if (stats != null)
            {
                StrawberriesCounter strawbs = Strawberries;
                strawbs.Amount    = stats.TotalStrawberries;
                strawbs.OutOf     = stats.MaxStrawberries;
                strawbs.ShowOutOf = stats.Name != "Celeste" || strawbs.OutOf <= 0;
            }

            SaveData.Instance = prev;
        }
Exemple #4
0
        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;
                        }
                    }
                }
            }
        }
Exemple #5
0
        public new void AfterInitialize()
        {
            // Vanilla / new saves don't have the LevelSets list.
            if (LevelSets == null)
            {
                LevelSets = new List <LevelSetStats>();
            }

            // Add missing LevelSetStats.
            foreach (AreaData area in AreaData.Areas)
            {
                string set = area.GetLevelSet();
                if (!LevelSets.Exists(other => other.Name == set))
                {
                    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 remove it to prevent any unwanted accesses.
                    // We previously kept the LevelSetStats around in case the levelset resurfaces later on, but as it turns out, this breaks some stuff.
                    LevelSets.RemoveAt(lsi);
                    lsi--;
                    continue;
                }

                int count = AreaData.Areas.Count(other => other.GetLevelSet() == set.Name);
                while (areas.Count < count)
                {
                    areas.Add(new AreaStats(offset + areas.Count));
                }
                while (areas.Count > count)
                {
                    areas.RemoveAt(areas.Count - 1);
                }
                for (int i = 0; i < count; i++)
                {
                    areas[i].ID = offset + i;
                    areas[i].SetSID(AreaData.Get(offset + i).GetSID());
                }

                int lastCompleted = -1;
                for (int i = 0; i < count; 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();
                }
            }

            // Order the levelsets to appear just as their areas appear in AreaData.Areas
            LevelSets.OrderBy(set => set.AreaOffset);

            // Carry over any progress from vanilla saves.
            if (LastArea_Unsafe.ID != 0)
            {
                LastArea_Safe = LastArea_Unsafe;
            }
            if (CurrentSession_Unsafe != 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 out of bounds areas.
            if (LastArea.ID < 0 || LastArea.ID >= AreaData.Areas.Count)
            {
                LastArea = AreaKey.Default;
            }

            // Debug mode shouldn't auto-enter into a level.
            if (DebugMode)
            {
                CurrentSession = null;
            }

            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);
                }
            }

            if (!AssistMode)
            {
                Assists = default(Assists);
            }

            // Disable the GameSpeed clamping - allow mods to "break" this.

            /*
             * if (Assists.GameSpeed < 5 || Assists.GameSpeed > 10) {
             *  Assists.GameSpeed = 10;
             * }
             */

            Everest.Invoke("LoadSaveData", FileSlot);
        }
Exemple #6
0
        public new void AfterInitialize()
        {
            // Vanilla / new saves don't have the LevelSets list.
            if (LevelSets == null)
            {
                LevelSets = new List <LevelSetStats>();
            }

            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))
                {
                    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 remove it to prevent any unwanted accesses.
                    // We previously kept the LevelSetStats around in case the levelset resurfaces later on, but as it turns out, this breaks some stuff.
                    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 count = AreaData.Areas.Count(other => other.GetLevelSet() == set.Name);
                // Fix IDs
                for (int i = 0; i < areas.Count; i++)
                {
                    ((patch_AreaStats)areas[i]).ID_Unsafe = AreaDataExt.Get(areas[i])?.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 < count; i++)
                {
                    if (i >= areas.Count || ((patch_AreaStats)areas[i]).ID_Unsafe != offset + i)
                    {
                        areas.Insert(i, new AreaStats(offset + i));
                    }
                }
                // 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 < count; 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();
                }
            }

            // Order the levelsets to appear just as their areas appear in AreaData.Areas
            LevelSets.OrderBy(set => set.AreaOffset);

            // Carry over any progress from vanilla saves.
            if (LastArea_Unsafe.ID != 0)
            {
                LastArea_Safe = LastArea_Unsafe;
            }
            if (CurrentSession_Unsafe != 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 out of bounds areas.
            if (LastArea.ID < 0 || LastArea.ID >= AreaData.Areas.Count)
            {
                LastArea = AreaKey.Default;
            }

            // Debug mode shouldn't auto-enter into a level.
            if (DebugMode)
            {
                CurrentSession = null;
            }

            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;
                        }
                    }
                }
            }
        }
Exemple #7
0
        public new void Show()
        {
            // Temporarily set the current save data to the file slot's save data.
            // This enables filtering the areas by the save data's current levelset.
            SaveData prev = SaveData.Instance;

            SaveData.Instance = SaveData;

            LevelSetStats stats = SaveData?.GetLevelSetStats();

            if (stats != null)
            {
                StrawberriesCounter strawbs = Strawberries;
                strawbs.Amount    = stats.TotalStrawberries;
                strawbs.OutOf     = stats.MaxStrawberries;
                strawbs.ShowOutOf = stats.Name != "Celeste" || strawbs.OutOf <= 0;
                strawbs.CanWiggle = false;

                if (stats.Name == "Celeste")
                {
                    // never mess with vanilla.
                    maxStrawberryCount                   = 175;
                    maxGoldenStrawberryCount             = 25; // vanilla is wrong (there are 26 including dashless), but don't mess with vanilla.
                    maxStrawberryCountIncludingUntracked = 202;

                    maxCassettes = 8;
                    maxCrystalHeartsExcludingCSides = 16;
                    maxCrystalHearts = 24;

                    summitStamp   = SaveData.Areas[7].Modes[0].Completed;
                    farewellStamp = SaveData.Areas[10].Modes[0].Completed;
                }
                else
                {
                    // compute the counts for the current level set.
                    maxStrawberryCount                   = stats.MaxStrawberries;
                    maxGoldenStrawberryCount             = stats.MaxGoldenStrawberries;
                    maxStrawberryCountIncludingUntracked = stats.MaxStrawberriesIncludingUntracked;

                    maxCassettes     = stats.MaxCassettes;
                    maxCrystalHearts = stats.MaxHeartGems;
                    maxCrystalHeartsExcludingCSides = stats.MaxHeartGemsExcludingCSides;

                    // summit stamp is displayed if we finished all areas that are not interludes. (TotalCompletions filters interludes out.)
                    summitStamp   = stats.TotalCompletions >= stats.MaxCompletions;
                    farewellStamp = false; // what is supposed to be Farewell in mod campaigns anyway??
                }

                // save the values from the current level set. They will be patched in instead of SaveData.TotalXX.
                totalGoldenStrawberries = stats.TotalGoldenStrawberries; // The value saved on the file is global for all level sets.
                totalHeartGems          = stats.TotalHeartGems;          // this counts from all level sets.
                totalCassettes          = stats.TotalCassettes;          // this relies on SaveData.Instance.

                // redo what is done on the constructor. This keeps the area name and stats up-to-date with the latest area.
                FurthestArea = SaveData.UnlockedAreas;
                Cassettes.Clear();
                HeartGems.Clear();
                foreach (AreaStats areaStats in SaveData.Areas)
                {
                    if (areaStats.ID > SaveData.UnlockedAreas)
                    {
                        break;
                    }

                    if (!AreaData.Areas[areaStats.ID].Interlude && AreaData.Areas[areaStats.ID].CanFullClear)
                    {
                        bool[] hearts = new bool[3];
                        for (int i = 0; i < hearts.Length; i++)
                        {
                            hearts[i] = areaStats.Modes[i].HeartGem;
                        }
                        Cassettes.Add(areaStats.Cassette);
                        HeartGems.Add(hearts);
                    }
                }
            }

            SaveData.Instance = prev;

            orig_Show();
        }