Skip to content

Commit 6489f51

Browse files
Option: show room success rate colors in chapter graph (#140)
* Success rate colors in graph (no way to turn off) * Use success rate rather than golden success rate * Use default colors * Use same successRate as `{room:successRate}` format * Add setting toggle for showing success rate colors in graph * Move room success rate calculation used by graph overlay to `SuccessRateStat` * 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` * Reset stat cache in `Events.OnAfterSavingStats` * Dim success rate colors in graph overlay for past rooms when using standard room indicator * Chapter graph success rate colors flashing with non-explicit indicator * Change graph overlay color switching to use sinosoidal animation * Update variable name to match code style * GraphOverlay blink to darker colors * Graph overlay start blinking animation at same brightness level as is currently being shown
1 parent d9ef0cd commit 6489f51

8 files changed

Lines changed: 131 additions & 8 deletions

File tree

ConsistencyTracker.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ private void EventsOnRunStarted() {
858858
}
859859

860860
private void EventsOnRunEnded(bool died, bool won) {
861-
ChokeRateStat.ChokeRateData = null; //Reset caching
861+
StatManager.InvalidateCachedStats();
862862
IngameOverlay?.SetGoldenState(false);
863863
}
864864
private void Events_OnChangedRoom(string roomName, bool isPreviousRoom) {
@@ -930,8 +930,8 @@ private void ChangeChapter(Session session) {
930930
//fix for SpeedrunTool savestate inconsistency
931931
TouchedBerries.Clear();
932932

933-
//Reset caching of choke rate data
934-
ChokeRateStat.ChokeRateData = null;
933+
//Reset caching
934+
StatManager.InvalidateCachedStats();
935935

936936
//Cause initial stats calculation
937937
SetNewRoom(CurrentRoomName, false);
@@ -1603,7 +1603,7 @@ public void RemoveRoomGoldenBerryDeaths(bool removeOne = false) {
16031603
}
16041604

16051605
//Reset cached choke rate data for graph
1606-
ChokeRateStat.ChokeRateData = null;
1606+
ChokeRateStat.InvalidateCache();
16071607
SaveChapterStats();
16081608
}
16091609
public void WipeChapterGoldenBerryDeaths() {

ConsistencyTrackerSettings.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,9 @@ public void CreateExternalOverlayEntry(TextMenu menu, bool inGame) {
11751175
[SettingIgnore]
11761176
public bool IngameOverlayGraphShowGoldenPbBar { get; set; } = true;
11771177

1178+
[SettingIgnore]
1179+
public bool IngameOverlayGraphShowSuccessRateColors { get; set; } = false;
1180+
11781181
[SettingIgnore]
11791182
public bool IngameOverlayGraphCurrentRoomExplicit { get; set; } = true;
11801183

@@ -1434,6 +1437,13 @@ public void CreateIngameOverlayEntry(TextMenu menu, bool inGame) {
14341437
}
14351438
});
14361439
subMenu.AddDescription(menu, menuItem, "Shows your PB room as a golden bar at the bottom of the graph.");
1440+
1441+
subMenu.Add(menuItem = new TextMenu.OnOff("Graph Show Success Rate Colors", IngameOverlayGraphShowSuccessRateColors) {
1442+
OnValueChange = v => {
1443+
IngameOverlayGraphShowSuccessRateColors = v;
1444+
}
1445+
});
1446+
subMenu.AddDescription(menu, menuItem, "Colors bars in graph to match room success rate color.");
14371447

14381448
subMenu.Add(menuItem = new TextMenu.OnOff("Graph Current Room Indicator Explicit", IngameOverlayGraphCurrentRoomExplicit) {
14391449
OnValueChange = v => {

Entities/GraphOverlay.cs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ public class GraphOverlay : Entity {
1414
private static readonly int WIDTH = 1920;
1515
private static readonly int HEIGHT = 1080;
1616

17+
// red, yellow, green, lightgreen
18+
private static readonly Color[] SUCCESS_RATE_COLORS = {
19+
Color.Red, Color.Yellow, Color.Green, Color.LightGreen, // STANDARD
20+
new Color(105, 0, 0), new Color(100, 99, 0), new Color(0, 47, 0), new Color(0, 101, 0), // VISITED
21+
};
22+
private static int roomLastFrame = 0;
23+
private static float blinkT = 0.0f;
24+
1725
private static ConsistencyTrackerModule Mod => ConsistencyTrackerModule.Instance;
1826

1927
private static bool Enabled => Mod.ModSettings.IngameOverlayGraphEnabled;
@@ -29,6 +37,7 @@ public class GraphOverlay : Entity {
2937
private static int BackgroundDim => Mod.ModSettings.IngameOverlayGraphBackgroundDim;
3038

3139
private Dictionary<RoomInfo, Tuple<int, float, int, float>> ChokeRateData { get; set; }
40+
private Dictionary<RoomInfo, float> SuccessRateData { get; set; }
3241
private int HighestDifficulty { get; set; }
3342
private RoomInfo PbRoom { get; set; }
3443
private RoomInfo PbRoomSession { get; set; }
@@ -43,6 +52,7 @@ public GraphOverlay() {
4352
}
4453

4554
private void EventsOnAfterSavingStats() {
55+
StatManager.InvalidateCachedStats();
4656
UpdateOverlay();
4757
}
4858

@@ -55,6 +65,7 @@ public void UpdateOverlay() {
5565
if (path == null || stats == null) return;
5666

5767
ChokeRateData = ChokeRateStat.GetRoomData(path, stats);
68+
SuccessRateData = SuccessRateStat.GetRoomData(path, stats);
5869
PbRoom = StatsUtil.GetFurthestGoldenRun(path, stats);
5970
PbRoomSession = StatsUtil.GetFurthestGoldenRunSession(path, stats);
6071

@@ -121,6 +132,8 @@ public override void Render() {
121132
int availableBarHeight = Height - (ShowGoldenPbBar ? 3 + 1 : 0) - (currentRoomIndicatorExplicit ? 3 + 1 : 0);
122133
int barWidth = (availableBarWidth - ((barCount - 1) * BarSpacing)) / barCount;
123134
int paddingX = (availableBarWidth - (barWidth * barCount) - ((barCount - 1) * BarSpacing)) / 2;
135+
136+
bool showSuccessRateColors = Mod.ModSettings.IngameOverlayGraphShowSuccessRateColors;
124137

125138
//Walk path and draw the bars
126139
int barsDrawn = 0;
@@ -152,6 +165,54 @@ public override void Render() {
152165
if (!visitedCurrent) {
153166
barColor = Color.Gray;
154167
}
168+
bool isCurrentRoom = currentRoom != null && rInfo.RoomNumberInChapter == currentRoom.RoomNumberInChapter;
169+
170+
//Color code for success rate display
171+
if (showSuccessRateColors) {
172+
barHeight = Math.Max(3, barHeight);
173+
174+
if (SuccessRateData.ContainsKey(rInfo)) {
175+
float successRate = SuccessRateData[rInfo];
176+
if (float.IsNaN(successRate)) {
177+
barColor = Color.Gray;
178+
} else {
179+
int colorIndex;
180+
if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarLightGreenPercent / 100 - 0.001)) {
181+
colorIndex = 3;
182+
} else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarGreenPercent / 100 - 0.001)) {
183+
colorIndex = 2;
184+
} else if (successRate > ((float)Mod.ModSettings.LiveDataChapterBarYellowPercent / 100 - 0.001)) {
185+
colorIndex = 1;
186+
} else {
187+
colorIndex = 0;
188+
}
189+
190+
if (currentRoomIndicatorExplicit) {
191+
barColor = SUCCESS_RATE_COLORS[colorIndex];
192+
} else {
193+
if (isCurrentRoom) {
194+
// Current room; blink
195+
blinkT += Engine.RawDeltaTime * 3.1415f;
196+
if (roomLastFrame < rInfo.RoomNumberInChapter) {
197+
// Went backwards.
198+
blinkT = 0.0f;
199+
} else if (roomLastFrame > rInfo.RoomNumberInChapter) {
200+
// Went forwards.
201+
blinkT = 3.1415f;
202+
}
203+
float t = ((float) Math.Cos(blinkT)) * 0.5f + 0.5f;
204+
var baseColor = SUCCESS_RATE_COLORS[colorIndex];
205+
var brightColor = SUCCESS_RATE_COLORS[colorIndex + 4];
206+
barColor = Util.LerpColors(brightColor, baseColor, t);
207+
roomLastFrame = rInfo.RoomNumberInChapter;
208+
} else {
209+
barColor = SUCCESS_RATE_COLORS[colorIndex + (visitedCurrent ? 0 : 4)];
210+
}
211+
}
212+
}
213+
214+
}
215+
}
155216

156217
//Draw checkpoint indicator over the empty space before this bar
157218
if (!isFirstCheckpoint && rInfo.RoomNumberInCP == 1 && Mod.ModSettings.IngameOverlayGraphShowCheckpointIndicator) {
@@ -195,7 +256,7 @@ public override void Render() {
195256
}
196257

197258
//Current room indicator
198-
if (currentRoom != null && rInfo.RoomNumberInChapter == currentRoom.RoomNumberInChapter && currentRoomIndicatorExplicit) {
259+
if (isCurrentRoom && currentRoomIndicatorExplicit) {
199260
int heightOffset = ShowGoldenPbBar ? 3 + 1 : 0;
200261
Draw.Rect(paddingX + position.X + (barWidth * barsDrawn) + (BarSpacing * barsDrawn) + beforeBarsOffset,
201262
position.Y + availableBarHeight + heightOffset + 1,
@@ -286,4 +347,4 @@ private static Vector2 ResolvePosition(StatTextPosition pos, int width, int heig
286347
return position;
287348
}
288349
}
289-
}
350+
}

Models/ChapterStats.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,13 +745,17 @@ public void AddAttempt(bool success) {
745745
}
746746

747747
PreviousAttempts.Add(success);
748+
749+
SuccessRateStat.InvalidateCache();
748750
}
749751

750752
public void RemoveLastAttempt() {
751753
if (PreviousAttempts.Count <= 0) {
752754
return;
753755
}
754756
PreviousAttempts.RemoveAt(PreviousAttempts.Count-1);
757+
758+
SuccessRateStat.InvalidateCache();
755759
}
756760

757761
public long GetTimeForCategory(TimeCategory category) {

Stats/ChokeRateStat.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public override string FormatSummary(PathInfo chapterPath, ChapterStats chapterS
265265
return null;
266266
}
267267

268-
public static Dictionary<RoomInfo, Tuple<int, float, int, float>> ChokeRateData { get; set; } = null;
268+
private static Dictionary<RoomInfo, Tuple<int, float, int, float>> ChokeRateData = null;
269269
/// <summary>
270270
/// Gets the following data for every room: Golden Entries, Golden Success Rate, Golden Entries Session, Golden Success Rate Session
271271
/// </summary>
@@ -320,6 +320,12 @@ public static Dictionary<RoomInfo, Tuple<int, float, int, float>> GetRoomData(Pa
320320
ChokeRateData = roomData;
321321
return roomData;
322322
}
323+
/// <summary>
324+
/// Deletes cached room choke rate data, resulting in it being recomputed next time it is requested.
325+
/// </summary>
326+
public static void InvalidateCache() {
327+
ChokeRateData = null;
328+
}
323329

324330
/// <summary>
325331
/// Gets the following data for every room: Golden Entries Session, Golden Success Rate Session

Stats/StatManager.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,15 @@ public bool DeleteFormat(string formatName) {
419419
Mod.SaveChapterStats();
420420
return true;
421421
}
422+
423+
/// <summary>
424+
/// Resets the caches used by stats shown in the graph overlay.
425+
/// Currently, resets cache for `ChokeRateStat` and `SuccessRateStat`.
426+
/// </summary>
427+
public static void InvalidateCachedStats() {
428+
ChokeRateStat.InvalidateCache();
429+
SuccessRateStat.InvalidateCache();
430+
}
422431
#endregion
423432

424433
#region Format file IO

Stats/SuccessRateStat.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,34 @@ public override string FormatSummary(PathInfo chapterPath, ChapterStats chapterS
8080
return null;
8181
}
8282

83+
private static Dictionary<RoomInfo, float> SuccessRateData = null;
84+
/// <summary>
85+
/// Gets the success rate data (based on fraction of successes in latest `StatManager.AttemptCount` attempts) for each room.
86+
/// </summary>
87+
public static Dictionary<RoomInfo, float> GetRoomData(PathInfo chapterPath, ChapterStats chapterStats) {
88+
if (SuccessRateData != null) return SuccessRateData;
89+
90+
int attemptCount = StatManager.AttemptCount;
91+
var roomData = new Dictionary<RoomInfo, float>();
92+
93+
foreach (CheckpointInfo cpInfo in chapterPath.Checkpoints) {
94+
foreach (RoomInfo rInfo in cpInfo.Rooms) {
95+
RoomStats data = chapterStats.GetRoom(rInfo);
96+
float successRate = data.AverageSuccessOverN(attemptCount);
97+
roomData.Add(rInfo, successRate);
98+
}
99+
}
100+
101+
SuccessRateData = roomData;
102+
return roomData;
103+
}
104+
/// <summary>
105+
/// Deletes cached room success rate data, resulting in it being recomputed next time it is requested.
106+
/// </summary>
107+
public static void InvalidateCache() {
108+
SuccessRateData = null;
109+
}
110+
83111

84112
//success-rate;Room SR: {room:successRate} | CP: {checkpoint:successRate} | Total: {chapter:successRate}
85113
public override List<KeyValuePair<string, string>> GetPlaceholderExplanations() {

Utility/Util.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,5 +189,10 @@ public static string ColorToHex(Color color) {
189189
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
190190
}
191191

192+
public static Color LerpColors(Color a, Color b, float t) {
193+
float am = 1.0f - t;
194+
return new Color((int) (a.R * am + b.R * t), (int) (a.G * am + b.G * t), (int) (a.B * am + b.B * t), (int) (a.A * am + b.A * t));
195+
}
196+
192197
}
193-
}
198+
}

0 commit comments

Comments
 (0)