From e2b5e00aa5593dc919bb4159b5bb8ffb3f8bc6bc Mon Sep 17 00:00:00 2001
From: "openai-code-agent[bot]" <242516109+Codex@users.noreply.github.com>
Date: Sat, 28 Mar 2026 04:50:06 +0000
Subject: [PATCH] refactor: standardize stat formatting for determinism
Co-authored-by: mfoltz <12994433+mfoltz@users.noreply.github.com>
---
Services/CanvasService.cs | 57 ++++++++++++++++++++++++++++-----------
Services/DataService.cs | 17 +++++++++++-
2 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/Services/CanvasService.cs b/Services/CanvasService.cs
index c2d19c4..4692f80 100644
--- a/Services/CanvasService.cs
+++ b/Services/CanvasService.cs
@@ -9,6 +9,7 @@
using Stunlock.Core;
using StunShared.UI;
using System.Collections;
+using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using TMPro;
@@ -1065,7 +1066,7 @@ public static string GetBloodStatInfo(int i, string statType)
float classMultiplier = ClassSynergy(bloodStat, _classType, _classStatSynergies);
statValue *= (1 + (_prestigeStatMultiplier * _legacyPrestige)) * classMultiplier * ((float)_legacyLevel / _legacyMaxLevel);
- string displayString = $"{BloodStatTypeAbbreviations[bloodStat]}: {(statValue * 100).ToString("F0") + "%"}";
+ string displayString = FormatBloodStatBar(bloodStat, statValue);
int statModificationId = ModificationIds.GenerateId(1, (int)bloodStat, statValue);
UnitStatType unitStatType = (UnitStatType)Enum.Parse(typeof(UnitStatType), bloodStat.ToString());
@@ -2295,21 +2296,48 @@ public static float ClassSynergy(T statType, PlayerClass classType, Dictionar
return 1f;
}
- public static string FormatWeaponStatBar(WeaponStatType weaponStat, float statValue)
+ private const string StatFormatInteger = "integer";
+ private const string StatFormatDecimal = "decimal";
+ private const string StatFormatPercentage = "percentage";
+ private static string FormatStatValue(float statValue, string format)
{
- string statValueString = WeaponStatFormats[weaponStat] switch
+ return format switch
{
- "integer" => ((int)statValue).ToString(),
- "decimal" => statValue.ToString("0.#"),
- "percentage" => (statValue * 100f).ToString("0.#") + "%",
- _ => statValue.ToString(),
+ StatFormatInteger => ((int)statValue).ToString(CultureInfo.InvariantCulture),
+ StatFormatDecimal => statValue.ToString("0.#", CultureInfo.InvariantCulture),
+ StatFormatPercentage => (statValue * 100f).ToString("0.#", CultureInfo.InvariantCulture) + "%",
+ _ => statValue.ToString(CultureInfo.InvariantCulture),
};
+ }
+ private static string GetAbbreviation(T statType, IReadOnlyDictionary abbreviations) where T : struct, Enum
+ {
+ return abbreviations.TryGetValue(statType, out string abbreviation)
+ ? abbreviation
+ : statType.ToString();
+ }
+ public static string FormatWeaponStatBar(WeaponStatType weaponStat, float statValue)
+ {
+ string format = WeaponStatFormats.TryGetValue(weaponStat, out string weaponFormat)
+ ? weaponFormat
+ : StatFormatPercentage;
+ string statValueString = FormatStatValue(statValue, format);
+ string abbreviation = GetAbbreviation(weaponStat, WeaponStatTypeAbbreviations);
- return $"{WeaponStatTypeAbbreviations[weaponStat]}: {statValueString}";
+ return $"{abbreviation}: {statValueString}";
+ }
+ public static string FormatBloodStatBar(BloodStatType bloodStat, float statValue)
+ {
+ string format = BloodStatFormats.TryGetValue(bloodStat, out string bloodFormat)
+ ? bloodFormat
+ : StatFormatPercentage;
+ string statValueString = FormatStatValue(statValue, format);
+ string abbreviation = GetAbbreviation(bloodStat, BloodStatTypeAbbreviations);
+
+ return $"{abbreviation}: {statValueString}";
}
public static string FormatAttributeValue(UnitStatType unitStatType, float statValue)
{
- string statString = $"+{statValue * 100f:F0}%";
+ string statString = $"+{FormatStatValue(statValue, StatFormatPercentage)}";
if (Enum.TryParse(unitStatType.ToString(), out WeaponStatType weaponStatType))
statString = FormatWeaponAttribute(weaponStatType, statValue);
@@ -2318,13 +2346,10 @@ public static string FormatAttributeValue(UnitStatType unitStatType, float statV
}
public static string FormatWeaponAttribute(WeaponStatType weaponStat, float statValue)
{
- string statValueString = WeaponStatFormats[weaponStat] switch
- {
- "integer" => ((int)statValue).ToString(),
- "decimal" => statValue.ToString("0.#"),
- "percentage" => (statValue * 100f).ToString("0.#") + "%",
- _ => statValue.ToString(),
- };
+ string format = WeaponStatFormats.TryGetValue(weaponStat, out string weaponFormat)
+ ? weaponFormat
+ : StatFormatPercentage;
+ string statValueString = FormatStatValue(statValue, format);
return $"+{statValueString}";
}
diff --git a/Services/DataService.cs b/Services/DataService.cs
index 5e35ff9..ae17e2c 100644
--- a/Services/DataService.cs
+++ b/Services/DataService.cs
@@ -205,6 +205,21 @@ public enum BloodStatType
{ "AbilityAttackSpeed", "AAS" },
{ "CorruptionDamageReduction", "CDR" }
};
+ public static readonly Dictionary BloodStatFormats = new()
+ {
+ { BloodStatType.HealingReceived, "percentage" },
+ { BloodStatType.DamageReduction, "percentage" },
+ { BloodStatType.PhysicalResistance, "percentage" },
+ { BloodStatType.SpellResistance, "percentage" },
+ { BloodStatType.ResourceYield, "percentage" },
+ { BloodStatType.ReducedBloodDrain, "percentage" },
+ { BloodStatType.SpellCooldownRecoveryRate, "percentage" },
+ { BloodStatType.WeaponCooldownRecoveryRate, "percentage" },
+ { BloodStatType.UltimateCooldownRecoveryRate, "percentage" },
+ { BloodStatType.MinionDamage, "percentage" },
+ { BloodStatType.AbilityAttackSpeed, "percentage" },
+ { BloodStatType.CorruptionDamageReduction, "percentage" }
+ };
public static Dictionary _familiarStatValues = [];
public enum FamiliarStatType
@@ -495,4 +510,4 @@ public static void ParsePlayerData(List playerData)
ShiftSpellData shiftSpellData = new(playerData[index]);
_shiftSpellIndex = shiftSpellData.ShiftSpellIndex;
}
-}
\ No newline at end of file
+}