Esempio n. 1
0
 public static AreaData Get(AreaStats stats)
 {
     if (stats.GetSID() == null)
     {
         return(Get(stats.ID));
     }
     return(Get(stats.GetSID()));
 }
Esempio n. 2
0
    private void Awake()
    {
        areaStats = I_areaStats;
        areaGen   = I_areaGen;
        areaVis   = I_areaVis;
        chars     = new List <Character>();
        waitEF    = new WaitForEndOfFrame();

        StartCoroutine(InitGame());
        StartCoroutine(InitLevel());
    }
Esempio n. 3
0
        public override void Added(Scene scene)
        {
            base.Added(scene);

            // fix the bloom to match golden alpha
            DynData <Strawberry> self = new DynData <Strawberry>(this);

            self.Get <BloomPoint>("bloom").Alpha = 0.5f;

            if (CollabMapDataProcessor.SilverBerries.ContainsKey(levelSet))
            {
                int missingBerries = 0;
                int totalBerries   = CollabMapDataProcessor.SilverBerries[levelSet].Count;
                foreach (KeyValuePair <string, EntityID> requiredSilver in CollabMapDataProcessor.SilverBerries[levelSet])
                {
                    // check if the silver was collected.
                    AreaStats stats = SaveData.Instance.GetAreaStatsFor(AreaData.Get(requiredSilver.Key).ToKey());
                    if (!stats.Modes[0].Strawberries.Contains(requiredSilver.Value))
                    {
                        // this berry wasn't collected!
                        missingBerries++;
                    }
                }

                if (missingBerries != 0)
                {
                    // some berries are missing, spawn the hologram instead of the berry.
                    HoloRainbowBerry hologram = new HoloRainbowBerry(Position, totalBerries - missingBerries, totalBerries);
                    scene.Add(hologram);

                    RemoveSelf();
                }
                else
                {
                    // all berries are here! check if we should play the unlock cutscene.
                    if (!CollabModule.Instance.SaveData.CombinedRainbowBerries.Contains((scene as Level).Session.Area.GetSID()))
                    {
                        // spawn the hologram for the animation...
                        HoloRainbowBerry hologram = new HoloRainbowBerry(Position, totalBerries, totalBerries);
                        hologram.Tag = Tags.FrozenUpdate;
                        scene.Add(hologram);

                        // make rainbow berry invisible for now...
                        Visible    = false;
                        Collidable = false;
                        self.Get <BloomPoint>("bloom").Visible = (self.Get <VertexLight>("light").Visible = false);

                        // now we wait for the player to enter the trigger. filling the HologramForCutscene field will tell the trigger to create the cutscene.
                        HologramForCutscene  = hologram;
                        CutsceneTotalBerries = totalBerries;
                    }
                }
            }
        }
Esempio n. 4
0
        [DataRow("/route/111859673/side-dish", "")] //Statistics aren't generated for routes
        public void TestStatisticsParse(string url, string expectedStatistics)
        {
            if (!url.Contains(Utilities.MPBASEURL))
            {
                url = Utilities.MPBASEURL + url;
            }

            AreaStats testStats = Parsers.PopulateStatistics(Utilities.GetHtmlDoc(url));

            Assert.AreEqual(expectedStatistics, testStats.ToString());
        }
Esempio n. 5
0
        public void ctor(AreaKey area, string checkpoint = null, AreaStats oldStats = null)
        {
            AreaData areaData = AreaData.Get(area);

            if (area.Mode == AreaMode.Normal)
            {
                areaData.RestoreASideAreaData();
            }
            else
            {
                areaData.OverrideASideMeta(area.Mode);
            }
            orig_ctor(area, checkpoint, oldStats);
        }
Esempio n. 6
0
 public extern void orig_ctor(AreaKey area, string checkpoint = null, AreaStats oldStats = null);
Esempio n. 7
0
 public static AreaData Get(AreaStats stats)
 {
     return(Get(stats.GetSID()));
 }
Esempio n. 8
0
        // Mods can't access patch_ classes directly.
        // We thus expose any new members through extensions.

        public static AreaKey ToKey(this AreaStats self, AreaMode mode)
        => new AreaKey(self.ID, mode).SetSID(self.GetSID());
        public static List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet)
        {
            List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>();
            int rowCount = 0;
            OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet);

            pages.Add(currentPage);

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allMapsDone = true;

            bool allLevelsDone       = true;
            bool allSpeedBerriesDone = true;

            string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0";

            foreach (AreaStats item in SaveData.Instance.Areas_Safe)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (!areaData.Interlude_Safe)
                {
                    if (LobbyHelper.IsHeartSide(areaData.GetSID()))
                    {
                        if (allMapsDone || item.TotalTimePlayed > 0)
                        {
                            // add a separator, like the one between regular maps and Farewell
                            currentPage.table.AddRow();
                        }
                        else
                        {
                            // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now.
                            continue;
                        }
                    }

                    string strawberryText = null;
                    if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0)
                    {
                        strawberryText = item.TotalStrawberries.ToString();
                        if (item.Modes[0].Completed)
                        {
                            strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries;
                        }
                    }
                    else
                    {
                        strawberryText = "-";
                    }

                    Row row = currentPage.table.AddRow()
                              .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor))
                              .Add(null)
                              .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                              .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor));

                    if (item.TotalTimePlayed > 0)
                    {
                        row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }


                    AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                    if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) &&
                        levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) &&
                        stats.Modes[0].Strawberries.Contains(berryID))
                    {
                        // silver berry was obtained!
                        row.Add(new IconCell("CollabUtils2/silver_strawberry"));
                    }
                    else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level)))
                    {
                        // golden berry was obtained!
                        row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                    }
                    else if (item.Modes[0].SingleRunCompleted)
                    {
                        row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                        sumOfBestDeaths += item.Modes[0].BestDeaths;
                    }
                    else
                    {
                        // the player didn't ever do a single run.
                        row.Add(new IconCell("dot"));
                        allLevelsDone = false;
                    }

                    if (item.TotalTimePlayed > 0)
                    {
                        row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (CollabModule.Instance.Settings.BestTimeToDisplayInJournal == CollabSettings.BestTimeInJournal.SpeedBerry)
                    {
                        if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) &&
                            CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB))
                        {
                            row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB)));
                            row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB)));
                            sumOfBestTimes += speedBerryPB;
                        }
                        else
                        {
                            row.Add(new IconCell("dot")).Add(null);
                            allSpeedBerriesDone = false;
                        }
                    }
                    else
                    {
                        if (item.Modes[0].BestTime > 0f)
                        {
                            row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null);
                            sumOfBestTimes += item.Modes[0].BestTime;
                        }
                        else
                        {
                            row.Add(new IconCell("dot")).Add(null);
                            allSpeedBerriesDone = false;
                        }
                    }

                    totalStrawberries += item.TotalStrawberries;
                    totalDeaths       += item.Modes[0].Deaths;
                    totalTime         += item.TotalTimePlayed;

                    if (!item.Modes[0].HeartGem)
                    {
                        allMapsDone = false;
                    }

                    rowCount++;
                    if (rowCount > 11)
                    {
                        // split the next zones into another page.
                        rowCount    = 0;
                        currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet);
                        pages.Add(currentPage);
                    }
                }
            }

            if (currentPage.table.Rows > 1)
            {
                currentPage.table.AddRow();
                Row totalsRow = currentPage.table.AddRow()
                                .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null)
                                .Add(null)
                                .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null);

                for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
                {
                    totalsRow.Add(null);
                }
                totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor));
                currentPage.table.AddRow();
            }

            return(pages);
        }
Esempio n. 10
0
 public static string GetLevelSet(this AreaStats self)
 => ((patch_AreaStats)self).LevelSet;
Esempio n. 11
0
 public static string GetSID(this AreaStats self)
 => ((patch_AreaStats)self).SID;
        private static void onNewSession(On.Celeste.Session.orig_ctor_AreaKey_string_AreaStats orig, Session self, AreaKey area, string checkpoint, AreaStats oldStats)
        {
            orig(self, area, checkpoint, oldStats);

            if (Everest.Content.Map.TryGetValue("Maps/" + area.GetSID(), out ModAsset asset) && asset.TryGetMeta(out RandomizedFlagsMapMeta meta))
            {
                double diceRoll = new Random().NextDouble();
                foreach (KeyValuePair <string, float> flag in meta?.CollabUtilsRandomizedFlags ?? new Dictionary <string, float>())
                {
                    if (diceRoll < flag.Value)
                    {
                        self.SetFlag(flag.Key, true);
                        break;
                    }
                    diceRoll -= flag.Value;
                }
            }
        }
Esempio n. 13
0
        public static List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet, bool showOnlyDiscovered)
        {
            bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn(levelSet);

            List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>();
            int rowCount = 0;

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            int  sumOfBestDashes   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allMapsDone = true;

            bool allLevelsDone       = true;
            bool allSpeedBerriesDone = true;

            string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0";

            int mapsPerPage = 12;
            int mapAmount   = SaveData.Instance.Areas_Safe.Where(item => !AreaData.Get(item.ID_Safe).Interlude_Safe &&
                                                                 (!showOnlyDiscovered || item.TotalTimePlayed > 0)).Count();

            // we want to display the map icons if they're not actually all the same. ^^'
            bool displayIcons = AreaData.Areas
                                .Where(area => !area.Interlude_Safe)
                                .Select(area => area.Icon)
                                .Distinct()
                                .Count() > 1;

            OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons);

            pages.Add(currentPage);

            if (mapAmount >= mapsPerPage)
            {
                // we want the last page to contain at least 2 maps.
                while (mapAmount % mapsPerPage < 2)
                {
                    mapsPerPage--;
                }
            }

            List <AreaStats> sortedMaps = new List <AreaStats>(SaveData.Instance.Areas_Safe)
                                          .Where(map => !AreaData.Get(map).Interlude_Safe)
                                          .ToList();

            // sort maps by icon name if all of their icons start with [number]-...
            // because then we know the ordering is intentional (and not accidental like easy > hard > medium)
            Regex startsWithNumber = new Regex(".*/[0-9]+-.*");

            if (sortedMaps.Select(map => AreaData.Get(map).Icon ?? "").All(icon => startsWithNumber.IsMatch(icon)))
            {
                sortedMaps.Sort((a, b) => {
                    AreaData adata = AreaData.Get(a);
                    AreaData bdata = AreaData.Get(b);

                    bool aHeartSide = LobbyHelper.IsHeartSide(a.GetSID());
                    bool bHeartSide = LobbyHelper.IsHeartSide(b.GetSID());

                    // heart sides should appear last.
                    if (aHeartSide && !bHeartSide)
                    {
                        return(1);
                    }
                    if (!aHeartSide && bHeartSide)
                    {
                        return(-1);
                    }

                    // sort by icon name, then by map bin name.
                    return(adata.Icon == bdata.Icon ? adata.Name.CompareTo(bdata.Name) : adata.Icon.CompareTo(bdata.Icon));
                });
            }

            foreach (AreaStats item in sortedMaps)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (LobbyHelper.IsHeartSide(areaData.GetSID()))
                {
                    if (allMapsDone || item.TotalTimePlayed > 0)
                    {
                        // add a separator, like the one between regular maps and Farewell
                        currentPage.table.AddRow();
                    }
                    else
                    {
                        // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now.
                        continue;
                    }
                }

                if (showOnlyDiscovered && item.TotalTimePlayed <= 0)
                {
                    // skip the map, because it was not discovered yet.
                    // since it wasn't discovered, we can already say all maps weren't done though.
                    allMapsDone         = false;
                    allLevelsDone       = false;
                    allSpeedBerriesDone = false;
                    continue;
                }

                string strawberryText = null;
                if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0)
                {
                    strawberryText = item.TotalStrawberries.ToString();
                    if (item.Modes[0].Completed)
                    {
                        strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries;
                    }
                }
                else
                {
                    strawberryText = "-";
                }

                Row row = currentPage.table.AddRow()
                          .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor));

                if (displayIcons)
                {
                    row.Add(null).Add(new IconCellFromGui(GFX.Gui.Has(areaData.Icon) ? areaData.Icon : "areas/null", 60f, 50f));
                }

                row.Add(null)
                .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor));

                if (item.TotalTimePlayed > 0)
                {
                    row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                }
                else
                {
                    row.Add(new IconCell("dot"));
                }

                AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) &&
                    levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) &&
                    stats.Modes[0].Strawberries.Contains(berryID))
                {
                    // silver berry was obtained!
                    row.Add(new IconCell("CollabUtils2/silver_strawberry"));
                }
                else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level)))
                {
                    // golden berry was obtained!
                    row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                }
                else if (item.Modes[0].SingleRunCompleted)
                {
                    row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    sumOfBestDeaths += item.Modes[0].BestDeaths;
                }
                else
                {
                    // the player didn't ever do a single run.
                    row.Add(new IconCell("dot"));
                    allLevelsDone = false;
                }

                if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                {
                    if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted)
                    {
                        row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)),
                                             currentPage.TextJustify, 0.5f, currentPage.TextColor));
                        sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item);
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }
                }

                if (item.TotalTimePlayed > 0)
                {
                    row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                }
                else
                {
                    row.Add(new IconCell("dot"));
                }

                if (displaySpeedBerryColumn)
                {
                    if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) &&
                        CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB))
                    {
                        row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB)));
                        row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB)));
                        sumOfBestTimes += speedBerryPB;
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }
                }
                else
                {
                    if (item.Modes[0].BestTime > 0f)
                    {
                        row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null);
                        sumOfBestTimes += item.Modes[0].BestTime;
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }
                }

                totalStrawberries += item.TotalStrawberries;
                totalDeaths       += item.Modes[0].Deaths;
                totalTime         += item.TotalTimePlayed;

                if (!item.Modes[0].HeartGem)
                {
                    allMapsDone = false;
                }

                rowCount++;
                if (rowCount >= mapsPerPage)
                {
                    // split the next zones into another page.
                    rowCount    = 0;
                    currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons);
                    pages.Add(currentPage);
                }
            }

            if (currentPage.table.Rows > 1)
            {
                currentPage.table.AddRow();
                Row totalsRow = currentPage.table.AddRow()
                                .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null);

                if (displayIcons)
                {
                    totalsRow.Add(null).Add(null);
                }

                totalsRow.Add(null)
                .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor));

                if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                {
                    totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || allLevelsDone ? Dialog.Deaths(sumOfBestDashes) : "-",
                                               currentPage.TextJustify, 0.6f, currentPage.TextColor));
                }

                totalsRow
                .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null);

                for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
                {
                    totalsRow.Add(null);
                }
                totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor));
                currentPage.table.AddRow();
            }

            return(pages);
        }
Esempio n. 14
0
        // Mods can't access patch_ classes directly.
        // We thus expose any new members through extensions.

        public static AreaData Get(AreaStats stats)
        => patch_AreaData.Get(stats);
Esempio n. 15
0
        public OuiJournalCollabProgressInOverworld(OuiJournal journal)
            : base(journal)
        {
            bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn();

            PageTexture = "page";
            table       = new Table()
                          .AddColumn(new TextCell(Dialog.Clean("journal_progress"), new Vector2(0f, 0.5f), 1f, Color.Black * 0.7f, 420f))
                          .AddColumn(new EmptyCell(20f))
                          .AddColumn(new EmptyCell(64f))
                          .AddColumn(new IconCell("strawberry", 150f))
                          .AddColumn(new IconCell("skullblue", 100f))
                          .AddColumn(new IconCell("CollabUtils2MinDeaths/SpringCollab2020/1-Beginner", 100f));

            if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
            {
                table.AddColumn(new IconCell("max480/DashCountMod/dashes", 80f));
            }

            table
            .AddColumn(new IconCell("time", 220f))
            .AddColumn(new IconCell("CollabUtils2/speed_berry_pbs_heading", 220f))
            .AddColumn(new EmptyCell(30f));

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            int  sumOfBestDashes   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allLevelsDone               = true;
            bool allSpeedBerriesDone         = true;
            bool allMapsCompletedInSingleRun = true;

            foreach (AreaStats item in SaveData.Instance.Areas_Safe)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (areaData.GetLevelSet() == SaveData.Instance.LevelSet)
                {
                    string        lobbyMapLevelSetName = LobbyHelper.GetLobbyLevelSet(areaData.GetSID());
                    LevelSetStats lobbyMapLevelSet     = null;
                    if (lobbyMapLevelSetName != null)
                    {
                        lobbyMapLevelSet = SaveData.Instance.GetLevelSetStatsFor(lobbyMapLevelSetName);
                    }

                    int  lobbyStrawberries                = item.TotalStrawberries;
                    int  lobbyTotalStrawberries           = areaData.Mode[0].TotalStrawberries;
                    int  lobbyDeaths                      = item.Modes[0].Deaths;
                    int  lobbySumOfBestDeaths             = 0;
                    int  lobbySumOfBestDashes             = OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() ? OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item) : 0;
                    long lobbyTotalTime                   = item.TotalTimePlayed;
                    long lobbySumOfBestTimes              = 0;
                    bool lobbyLevelsDone                  = true;
                    int  lobbySpeedBerryLevel             = 1;
                    bool lobbySilverBerriesObtained       = true;
                    bool lobbyAllMapsCompletedInSingleRun = true;

                    foreach (AreaStats lobbyMap in lobbyMapLevelSet?.Areas ?? new List <AreaStats>())
                    {
                        AreaData lobbyAreaData = AreaData.Get(lobbyMap.ID_Safe);
                        lobbyStrawberries                += lobbyMap.TotalStrawberries;
                        lobbyTotalStrawberries           += lobbyAreaData.Mode[0].TotalStrawberries;
                        lobbyDeaths                      += lobbyMap.Modes[0].Deaths;
                        lobbyTotalTime                   += lobbyMap.TotalTimePlayed;
                        lobbyAllMapsCompletedInSingleRun &= lobbyMap.Modes[0].SingleRunCompleted;

                        if (displaySpeedBerryColumn)
                        {
                            if (CollabMapDataProcessor.SpeedBerries.TryGetValue(lobbyMap.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo mapSpeedBerryInfo) &&
                                CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(lobbyMap.GetSID(), out long mapSpeedBerryPB))
                            {
                                lobbySpeedBerryLevel = Math.Max(getRankLevel(mapSpeedBerryInfo, mapSpeedBerryPB), lobbySpeedBerryLevel);
                                lobbySumOfBestTimes += mapSpeedBerryPB;
                            }
                            else
                            {
                                lobbySpeedBerryLevel = 4;
                            }
                        }
                        else
                        {
                            if (lobbyMap.Modes[0].BestTime > 0f)
                            {
                                lobbySumOfBestTimes += lobbyMap.Modes[0].BestTime;
                            }
                            else
                            {
                                lobbySpeedBerryLevel = 4;
                            }
                        }

                        bool goldenBerryNotObtained = !lobbyMap.Modes[0].Strawberries.Any(berry => lobbyAreaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level));
                        bool silverBerryNotObtained = !CollabMapDataProcessor.SilverBerries.TryGetValue(lobbyMap.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) ||
                                                      !levelSetBerries.TryGetValue(lobbyMap.GetSID(), out EntityID berryID) ||
                                                      !lobbyMap.Modes[0].Strawberries.Contains(berryID);

                        if (goldenBerryNotObtained && silverBerryNotObtained)
                        {
                            lobbySilverBerriesObtained = false;
                            lobbySumOfBestDeaths      += lobbyMap.Modes[0].BestDeaths;
                        }

                        lobbySumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(lobbyMap);

                        if (!lobbyMap.Modes[0].HeartGem)
                        {
                            lobbyLevelsDone = false;
                        }
                    }

                    string strawberryText = null;
                    if (lobbyStrawberries > 0 || lobbyTotalStrawberries > 0)
                    {
                        strawberryText = lobbyStrawberries.ToString();
                        if (lobbyLevelsDone)
                        {
                            strawberryText = strawberryText + "/" + lobbyTotalStrawberries;
                        }
                    }
                    else
                    {
                        strawberryText = "-";
                    }

                    string heartTexturePath = lobbyMapLevelSetName ?? areaData.GetSID();
                    string heartTexture     = MTN.Journal.Has("CollabUtils2Hearts/" + heartTexturePath) ? "CollabUtils2Hearts/" + heartTexturePath : "heartgem0";

                    string areaName = Dialog.Clean(areaData.Name);
                    if (Dialog.Has(areaData.Name + "_journal"))
                    {
                        areaName = Dialog.Clean(areaData.Name + "_journal");
                    }

                    Row row = table.AddRow()
                              .Add(new TextCell(areaName, new Vector2(1f, 0.5f), 0.6f, TextColor))
                              .Add(null)
                              .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                              .Add(new TextCell(strawberryText, TextJustify, 0.5f, TextColor));

                    if (lobbyTotalTime > 0)
                    {
                        row.Add(new TextCell(Dialog.Deaths(lobbyDeaths), TextJustify, 0.5f, TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (lobbyLevelsDone)
                    {
                        AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                        if (lobbyMapLevelSet == null)
                        {
                            row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), TextJustify, 0.5f, TextColor));
                            sumOfBestDeaths += item.Modes[0].BestDeaths;
                        }
                        else if (lobbySilverBerriesObtained)
                        {
                            row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                        }
                        else if (lobbyAllMapsCompletedInSingleRun)
                        {
                            row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDeaths), TextJustify, 0.5f, TextColor));
                        }
                        else
                        {
                            row.Add(new IconCell("dot"));
                        }
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                        allLevelsDone = false;
                    }


                    if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                    {
                        if (lobbyMapLevelSet == null)
                        {
                            if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted)
                            {
                                row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)),
                                                     TextJustify, 0.5f, TextColor));
                                sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item);
                            }
                            else
                            {
                                row.Add(new IconCell("dot"));
                            }
                        }
                        else if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || lobbyAllMapsCompletedInSingleRun)
                        {
                            row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDashes), TextJustify, 0.5f, TextColor));
                        }
                        else
                        {
                            row.Add(new IconCell("dot"));
                        }
                    }

                    if (lobbyTotalTime > 0)
                    {
                        row.Add(new TextCell(Dialog.Time(lobbyTotalTime), TextJustify, 0.5f, TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (lobbyMapLevelSet == null)
                    {
                        row.Add(new TextCell("-", TextJustify, 0.5f, TextColor)).Add(null);
                    }
                    else if (lobbySpeedBerryLevel < 4)
                    {
                        if (displaySpeedBerryColumn)
                        {
                            row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, getRankColor(lobbySpeedBerryLevel)));
                            row.Add(new IconCell(getRankIcon(lobbySpeedBerryLevel)));
                            sumOfBestTimes += lobbySumOfBestTimes;
                        }
                        else
                        {
                            row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, TextColor)).Add(null);
                            sumOfBestTimes += lobbySumOfBestTimes;
                        }
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }

                    totalStrawberries           += lobbyStrawberries;
                    totalDeaths                 += lobbyDeaths;
                    sumOfBestDeaths             += lobbySumOfBestDeaths;
                    sumOfBestDashes             += lobbySumOfBestDashes;
                    totalTime                   += lobbyTotalTime;
                    allMapsCompletedInSingleRun &= lobbyAllMapsCompletedInSingleRun;

                    if (!lobbyLevelsDone)
                    {
                        allLevelsDone = false;
                    }
                }
            }

            table.AddRow();
            Row totalsRow = table.AddRow()
                            .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, TextColor)).Add(null)
                            .Add(null)
                            .Add(new TextCell(totalStrawberries.ToString(), TextJustify, 0.6f, TextColor))
                            .Add(new TextCell(Dialog.Deaths(totalDeaths), TextJustify, 0.6f, TextColor))
                            .Add(new TextCell(allLevelsDone && allMapsCompletedInSingleRun ? Dialog.Deaths(sumOfBestDeaths) : "-", TextJustify, 0.6f, TextColor));

            if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
            {
                totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || (allLevelsDone && allMapsCompletedInSingleRun) ?
                                           Dialog.Deaths(sumOfBestDashes) : "-", TextJustify, 0.6f, TextColor));
            }

            totalsRow
            .Add(new TextCell(Dialog.Time(totalTime), TextJustify, 0.6f, TextColor))
            .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", TextJustify, 0.6f, TextColor)).Add(null);

            for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
            {
                totalsRow.Add(null);
            }
            totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), TextJustify, 0.6f, TextColor));
            table.AddRow();
        }
Esempio n. 16
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;
                        }
                    }
                }
            }
        }
Esempio n. 17
0
 public static AreaStats SetSID(this AreaStats self, string value)
 {
     ((patch_AreaStats)self).SID = value;
     return(self);
 }
Esempio n. 18
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;
                        }
                    }
                }
            }
        }
 internal static int GetLevelDashesForJournalProgress(AreaStats stats)
 {
     return(stats.BestTotalDashes);
 }