From 2b142105cb6aacd6b62f7b855f9ea5e23acbafeb Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Mon, 9 Mar 2026 13:40:18 -0400 Subject: [PATCH 1/8] Success rate colors in graph (no way to turn off) --- Entities/GraphOverlay.cs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index 11c44df..57accdb 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -150,6 +150,35 @@ public override void Render() { if (!visitedCurrent) { barColor = Color.Gray; } + + //Color code for success rate display + if (true) { + barHeight = Math.Max(3, barHeight); + + if (ChokeRateData.ContainsKey(rInfo)) { + var data = ChokeRateData[rInfo]; + float successRate = data.Item2; + if (float.IsNaN(successRate)) { + barColor = new Color(0.33f, 0.33f, 0.33f); // #555555 + } else if ((double)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - successRate < 0.0001) { + // Light green + barColor = new Color(0.91f, 1.00f, 0.88f); // #63bd59 + + } else if ((double)Mod.ModSettings.LiveDataChapterBarGreenPercent / 100 - successRate < 0.0001) { + // Green + barColor = new Color(0.80f, 1.00f, 0.75f); // #caffbf + + } else if ((double)Mod.ModSettings.LiveDataChapterBarYellowPercent / 100 - successRate < 0.0001) { + // Yellow + barColor = new Color(0.99f, 1.00f, 0.71f); // #fdffb6 + + } else { + // Red + barColor = new Color(1.00f, 0.68f, 0.68f); // #ffadad + } + + } + } //Draw checkpoint indicator over the empty space before this bar if (!isFirstCheckpoint && rInfo.RoomNumberInCP == 1 && Mod.ModSettings.IngameOverlayGraphShowCheckpointIndicator) { @@ -284,4 +313,4 @@ private static Vector2 ResolvePosition(StatTextPosition pos, int width, int heig return position; } } -} \ No newline at end of file +} From 63657d04f90306d11cff5a6b53ad72c774f4626c Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Mon, 9 Mar 2026 16:09:40 -0400 Subject: [PATCH 2/8] Use success rate rather than golden success rate --- Entities/GraphOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index 57accdb..d54c79a 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -156,8 +156,8 @@ public override void Render() { barHeight = Math.Max(3, barHeight); if (ChokeRateData.ContainsKey(rInfo)) { - var data = ChokeRateData[rInfo]; - float successRate = data.Item2; + RoomStats data = stats.GetRoom(rInfo); + float successRate = data.AverageSuccessOverSelectedN(); if (float.IsNaN(successRate)) { barColor = new Color(0.33f, 0.33f, 0.33f); // #555555 } else if ((double)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - successRate < 0.0001) { From 09ce17d426258be2a0c37a76c31f18279ae66d1b Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Mon, 9 Mar 2026 16:19:20 -0400 Subject: [PATCH 3/8] Use default colors --- Entities/GraphOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index d54c79a..b26722a 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -159,22 +159,22 @@ public override void Render() { RoomStats data = stats.GetRoom(rInfo); float successRate = data.AverageSuccessOverSelectedN(); if (float.IsNaN(successRate)) { - barColor = new Color(0.33f, 0.33f, 0.33f); // #555555 + barColor = Color.Gray; } else if ((double)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - successRate < 0.0001) { // Light green - barColor = new Color(0.91f, 1.00f, 0.88f); // #63bd59 + barColor = Color.LightGreen; } else if ((double)Mod.ModSettings.LiveDataChapterBarGreenPercent / 100 - successRate < 0.0001) { // Green - barColor = new Color(0.80f, 1.00f, 0.75f); // #caffbf + barColor = Color.Green; } else if ((double)Mod.ModSettings.LiveDataChapterBarYellowPercent / 100 - successRate < 0.0001) { // Yellow - barColor = new Color(0.99f, 1.00f, 0.71f); // #fdffb6 + barColor = Color.Yellow; } else { // Red - barColor = new Color(1.00f, 0.68f, 0.68f); // #ffadad + barColor = Color.Red; } } From 8d77f96ae2218d19eb28b7088a61d302ee4e376a Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Sat, 14 Mar 2026 20:19:42 -0400 Subject: [PATCH 4/8] Use same successRate as `{room:successRate}` format --- Entities/GraphOverlay.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index b26722a..0ed1feb 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -157,21 +157,19 @@ public override void Render() { if (ChokeRateData.ContainsKey(rInfo)) { RoomStats data = stats.GetRoom(rInfo); - float successRate = data.AverageSuccessOverSelectedN(); + int attemptCount = StatManager.AttemptCount; + float successRate = data.AverageSuccessOverN(attemptCount); if (float.IsNaN(successRate)) { barColor = Color.Gray; - } else if ((double)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - successRate < 0.0001) { + } else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - 0.001)) { // Light green barColor = Color.LightGreen; - - } else if ((double)Mod.ModSettings.LiveDataChapterBarGreenPercent / 100 - successRate < 0.0001) { + } else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarGreenPercent / 100 - 0.001)) { // Green barColor = Color.Green; - - } else if ((double)Mod.ModSettings.LiveDataChapterBarYellowPercent / 100 - successRate < 0.0001) { + } else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarYellowPercent / 100 - 0.001)) { // Yellow barColor = Color.Yellow; - } else { // Red barColor = Color.Red; From 3684ac8879f45bfc10c93b7682592c8418f5ddfc Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Sat, 13 Jun 2026 11:26:15 -0400 Subject: [PATCH 5/8] Add setting toggle for showing success rate colors in graph --- ConsistencyTrackerSettings.cs | 10 ++++++++++ Entities/GraphOverlay.cs | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ConsistencyTrackerSettings.cs b/ConsistencyTrackerSettings.cs index 8cf339f..0442c05 100644 --- a/ConsistencyTrackerSettings.cs +++ b/ConsistencyTrackerSettings.cs @@ -1175,6 +1175,9 @@ public void CreateExternalOverlayEntry(TextMenu menu, bool inGame) { [SettingIgnore] public bool IngameOverlayGraphShowGoldenPbBar { get; set; } = true; + [SettingIgnore] + public bool IngameOverlayGraphShowSuccessRateColors { get; set; } = false; + [SettingIgnore] public bool IngameOverlayGraphCurrentRoomExplicit { get; set; } = true; @@ -1434,6 +1437,13 @@ public void CreateIngameOverlayEntry(TextMenu menu, bool inGame) { } }); subMenu.AddDescription(menu, menuItem, "Shows your PB room as a golden bar at the bottom of the graph."); + + subMenu.Add(menuItem = new TextMenu.OnOff("Graph Show Success Rate Colors", IngameOverlayGraphShowSuccessRateColors) { + OnValueChange = v => { + IngameOverlayGraphShowSuccessRateColors = v; + } + }); + subMenu.AddDescription(menu, menuItem, "Colors bars in graph to match room success rate color."); subMenu.Add(menuItem = new TextMenu.OnOff("Graph Current Room Indicator Explicit", IngameOverlayGraphCurrentRoomExplicit) { OnValueChange = v => { diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index f43c58b..c2197c1 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -121,6 +121,8 @@ public override void Render() { int availableBarHeight = Height - (ShowGoldenPbBar ? 3 + 1 : 0) - (currentRoomIndicatorExplicit ? 3 + 1 : 0); int barWidth = (availableBarWidth - ((barCount - 1) * BarSpacing)) / barCount; int paddingX = (availableBarWidth - (barWidth * barCount) - ((barCount - 1) * BarSpacing)) / 2; + + bool showSuccessRateColors = Mod.ModSettings.IngameOverlayGraphShowSuccessRateColors; //Walk path and draw the bars int barsDrawn = 0; @@ -154,7 +156,7 @@ public override void Render() { } //Color code for success rate display - if (true) { + if (showSuccessRateColors) { barHeight = Math.Max(3, barHeight); if (ChokeRateData.ContainsKey(rInfo)) { From ac6fb09149489f5628cc8b87843aafe736f58891 Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Sun, 26 Jul 2026 11:49:48 -0400 Subject: [PATCH 6/8] Move room success rate calculation used by graph overlay to `SuccessRateStat` --- Entities/GraphOverlay.cs | 2 ++ Stats/SuccessRateStat.cs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index c2197c1..9235142 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -29,6 +29,7 @@ public class GraphOverlay : Entity { private static int BackgroundDim => Mod.ModSettings.IngameOverlayGraphBackgroundDim; private Dictionary> ChokeRateData { get; set; } + private Dictionary SuccessRateData { get; set; } private int HighestDifficulty { get; set; } private RoomInfo PbRoom { get; set; } private RoomInfo PbRoomSession { get; set; } @@ -55,6 +56,7 @@ public void UpdateOverlay() { if (path == null || stats == null) return; ChokeRateData = ChokeRateStat.GetRoomData(path, stats); + SuccessRateData = SuccessRateStat.GetRoomData(path, stats); PbRoom = StatsUtil.GetFurthestGoldenRun(path, stats); PbRoomSession = StatsUtil.GetFurthestGoldenRunSession(path, stats); diff --git a/Stats/SuccessRateStat.cs b/Stats/SuccessRateStat.cs index 65622c4..8874f20 100644 --- a/Stats/SuccessRateStat.cs +++ b/Stats/SuccessRateStat.cs @@ -80,6 +80,26 @@ public override string FormatSummary(PathInfo chapterPath, ChapterStats chapterS return null; } + /// + /// Gets the success rate data (based on fraction of successes in latest `StatManager.AttemptCount` attempts) for each room. + /// + public static Dictionary GetRoomData(PathInfo chapterPath, ChapterStats chapterStats) { + // TODO: cache stats. + + int attemptCount = StatManager.AttemptCount; + var roomData = new Dictionary(); + + foreach (CheckpointInfo cpInfo in chapterPath.Checkpoints) { + foreach (RoomInfo rInfo in cpInfo.Rooms) { + RoomStats data = chapterStats.GetRoom(rInfo); + float successRate = data.AverageSuccessOverN(attemptCount); + roomData.Add(rInfo, successRate); + } + } + + return roomData; + } + //success-rate;Room SR: {room:successRate} | CP: {checkpoint:successRate} | Total: {chapter:successRate} public override List> GetPlaceholderExplanations() { From f12a1477295e823029e21455ea1669d7358820d9 Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Sun, 26 Jul 2026 12:55:34 -0400 Subject: [PATCH 7/8] Cache success rate data for graph overlay Also, adjust `ChokeRateStat` caching code to match All caches are invalidated on run end or chapter change. `SuccessRateStat` cache is also invaludated in: - `RoomStats.AddAttempt` - `RoomStats.RemoveLastAttempt` --- ConsistencyTracker.cs | 8 ++++---- Entities/GraphOverlay.cs | 6 ++---- Models/ChapterStats.cs | 4 ++++ Stats/ChokeRateStat.cs | 8 +++++++- Stats/StatManager.cs | 9 +++++++++ Stats/SuccessRateStat.cs | 10 +++++++++- 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ConsistencyTracker.cs b/ConsistencyTracker.cs index b253b96..f3b5cdd 100644 --- a/ConsistencyTracker.cs +++ b/ConsistencyTracker.cs @@ -858,7 +858,7 @@ private void EventsOnRunStarted() { } private void EventsOnRunEnded(bool died, bool won) { - ChokeRateStat.ChokeRateData = null; //Reset caching + StatManager.InvalidateCachedStats(); IngameOverlay?.SetGoldenState(false); } private void Events_OnChangedRoom(string roomName, bool isPreviousRoom) { @@ -930,8 +930,8 @@ private void ChangeChapter(Session session) { //fix for SpeedrunTool savestate inconsistency TouchedBerries.Clear(); - //Reset caching of choke rate data - ChokeRateStat.ChokeRateData = null; + //Reset caching + StatManager.InvalidateCachedStats(); //Cause initial stats calculation SetNewRoom(CurrentRoomName, false); @@ -1591,7 +1591,7 @@ public void RemoveRoomGoldenBerryDeaths(bool removeOne = false) { } //Reset cached choke rate data for graph - ChokeRateStat.ChokeRateData = null; + ChokeRateStat.InvalidateCache(); SaveChapterStats(); } public void WipeChapterGoldenBerryDeaths() { diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index 9235142..42125df 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -161,10 +161,8 @@ public override void Render() { if (showSuccessRateColors) { barHeight = Math.Max(3, barHeight); - if (ChokeRateData.ContainsKey(rInfo)) { - RoomStats data = stats.GetRoom(rInfo); - int attemptCount = StatManager.AttemptCount; - float successRate = data.AverageSuccessOverN(attemptCount); + if (SuccessRateData.ContainsKey(rInfo)) { + float successRate = SuccessRateData[rInfo]; if (float.IsNaN(successRate)) { barColor = Color.Gray; } else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - 0.001)) { diff --git a/Models/ChapterStats.cs b/Models/ChapterStats.cs index 36fa73e..ac3029c 100644 --- a/Models/ChapterStats.cs +++ b/Models/ChapterStats.cs @@ -745,6 +745,8 @@ public void AddAttempt(bool success) { } PreviousAttempts.Add(success); + + SuccessRateStat.InvalidateCache(); } public void RemoveLastAttempt() { @@ -752,6 +754,8 @@ public void RemoveLastAttempt() { return; } PreviousAttempts.RemoveAt(PreviousAttempts.Count-1); + + SuccessRateStat.InvalidateCache(); } public long GetTimeForCategory(TimeCategory category) { diff --git a/Stats/ChokeRateStat.cs b/Stats/ChokeRateStat.cs index d2bc377..6e8aaa0 100644 --- a/Stats/ChokeRateStat.cs +++ b/Stats/ChokeRateStat.cs @@ -265,7 +265,7 @@ public override string FormatSummary(PathInfo chapterPath, ChapterStats chapterS return null; } - public static Dictionary> ChokeRateData { get; set; } = null; + private static Dictionary> ChokeRateData = null; /// /// Gets the following data for every room: Golden Entries, Golden Success Rate, Golden Entries Session, Golden Success Rate Session /// @@ -320,6 +320,12 @@ public static Dictionary> GetRoomData(Pa ChokeRateData = roomData; return roomData; } + /// + /// Deletes cached room choke rate data, resulting in it being recomputed next time it is requested. + /// + public static void InvalidateCache() { + ChokeRateData = null; + } /// /// Gets the following data for every room: Golden Entries Session, Golden Success Rate Session diff --git a/Stats/StatManager.cs b/Stats/StatManager.cs index 6ff4d91..1bd1235 100644 --- a/Stats/StatManager.cs +++ b/Stats/StatManager.cs @@ -419,6 +419,15 @@ public bool DeleteFormat(string formatName) { Mod.SaveChapterStats(); return true; } + + /// + /// Resets the caches used by stats shown in the graph overlay. + /// Currently, resets cache for `ChokeRateStat` and `SuccessRateStat`. + /// + public static void InvalidateCachedStats() { + ChokeRateStat.InvalidateCache(); + SuccessRateStat.InvalidateCache(); + } #endregion #region Format file IO diff --git a/Stats/SuccessRateStat.cs b/Stats/SuccessRateStat.cs index 8874f20..0f0bf23 100644 --- a/Stats/SuccessRateStat.cs +++ b/Stats/SuccessRateStat.cs @@ -80,11 +80,12 @@ public override string FormatSummary(PathInfo chapterPath, ChapterStats chapterS return null; } + private static Dictionary SuccessRateData = null; /// /// Gets the success rate data (based on fraction of successes in latest `StatManager.AttemptCount` attempts) for each room. /// public static Dictionary GetRoomData(PathInfo chapterPath, ChapterStats chapterStats) { - // TODO: cache stats. + if (SuccessRateData != null) return SuccessRateData; int attemptCount = StatManager.AttemptCount; var roomData = new Dictionary(); @@ -97,8 +98,15 @@ public static Dictionary GetRoomData(PathInfo chapterPath, Chap } } + SuccessRateData = roomData; return roomData; } + /// + /// Deletes cached room success rate data, resulting in it being recomputed next time it is requested. + /// + public static void InvalidateCache() { + SuccessRateData = null; + } //success-rate;Room SR: {room:successRate} | CP: {checkpoint:successRate} | Total: {chapter:successRate} From b8632f9697a868f5da25dcabea9909f4f9264afd Mon Sep 17 00:00:00 2001 From: NonbinaryCoder Date: Thu, 6 Aug 2026 11:38:29 -0400 Subject: [PATCH 8/8] Reset stat cache in `Events.OnAfterSavingStats` --- Entities/GraphOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Entities/GraphOverlay.cs b/Entities/GraphOverlay.cs index 42125df..c5a079a 100644 --- a/Entities/GraphOverlay.cs +++ b/Entities/GraphOverlay.cs @@ -44,6 +44,7 @@ public GraphOverlay() { } private void EventsOnAfterSavingStats() { + StatManager.InvalidateCachedStats(); UpdateOverlay(); }