using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using XUnity.AutoTranslator.Plugin.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("StatsUIPlugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("StatsUIPlugin")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f9a59617-535f-477a-b3f4-09334ba42a3d")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.1.1.0")] namespace StatsUIPlugin; internal class SPConfig { public static ConfigEntry EnableTranslation { get; private set; } public static ConfigEntry BaseSize { get; private set; } public static ConfigEntry MinSize { get; private set; } public static ConfigEntry ReducePerItem { get; private set; } public static ConfigEntry HeaderOffset { get; private set; } public static ConfigEntry NumOffset { get; private set; } public static ConfigEntry FontMode { get; private set; } public static ConfigEntry DebugMode { get; private set; } public static ConfigEntry AutoCheck { get; private set; } public static bool ConfigChanged { get; private set; } = true; internal static void Init(ConfigFile config) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown string text = "状态栏设置"; EnableTranslation = config.Bind(text, "状态栏翻译功能", true, new ConfigDescription("启用翻译功能", (AcceptableValueBase)(object)new AcceptableValueList(new bool[2] { true, false }), Array.Empty())); FontMode = config.Bind(text, "字体大小变更", true, new ConfigDescription("字体大小变更", (AcceptableValueBase)(object)new AcceptableValueList(new bool[2] { true, false }), Array.Empty())); AutoCheck = config.Bind(text, "检测mod升级项", true, new ConfigDescription("自动检测额外的mod升级", (AcceptableValueBase)(object)new AcceptableValueList(new bool[2] { true, false }), Array.Empty())); BaseSize = config.Bind(text, "基础字体大小", 35f, new ConfigDescription("基础字体大小", (AcceptableValueBase)(object)new AcceptableValueRange(8f, 48f), Array.Empty())); MinSize = config.Bind(text, "最小字体大小", 16f, new ConfigDescription("最小字体大小", (AcceptableValueBase)(object)new AcceptableValueRange(4f, 24f), Array.Empty())); ReducePerItem = config.Bind(text, "字体递减", 1.4f, new ConfigDescription("每多一项升级减少的字体大小", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); HeaderOffset = config.Bind(text, "标题偏移", 7f, new ConfigDescription("标题字体偏移", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 15f), Array.Empty())); NumOffset = config.Bind(text, "数字偏移", 0.4f, new ConfigDescription("数字字体偏移", (AcceptableValueBase)(object)new AcceptableValueRange(-2f, 2f), Array.Empty())); DebugMode = config.Bind(text, "调试模式", false, new ConfigDescription("调试模式", (AcceptableValueBase)(object)new AcceptableValueList(new bool[2] { true, false }), Array.Empty())); BaseSize.SettingChanged += delegate { ConfigChanged = true; }; MinSize.SettingChanged += delegate { ConfigChanged = true; }; ReducePerItem.SettingChanged += delegate { ConfigChanged = true; }; HeaderOffset.SettingChanged += delegate { ConfigChanged = true; }; NumOffset.SettingChanged += delegate { ConfigChanged = true; }; FontMode.SettingChanged += delegate { ConfigChanged = true; }; } internal static bool IsConfigChanged() { bool configChanged = ConfigChanged; ConfigChanged = false; return configChanged; } } internal class SPManager { private static readonly FieldInfo _playerUpgrades = AccessTools.Field(typeof(StatsUI), "playerUpgrades"); private static readonly FieldInfo _text = AccessTools.Field(typeof(StatsUI), "Text"); private static readonly FieldInfo _numbersText = AccessTools.Field(typeof(StatsUI), "textNumbers"); private static readonly FieldInfo _header = AccessTools.Field(typeof(StatsUI), "upgradesHeader"); private static readonly HashSet _failedTrans = new HashSet(); private static readonly string _bugFilePath = Path.Combine(Paths.ConfigPath, "Translation", "zh", "Bug.txt"); private static readonly StringBuilder _sb = new StringBuilder(512); private static readonly ConcurrentDictionary _transCache = new ConcurrentDictionary(); private static bool _hasModUpg = false; private static int _lastUpgHash; internal static void DetectModdedUpgrades() { try { if (!SPConfig.AutoCheck.Value) { _hasModUpg = true; return; } Type type = AccessTools.TypeByName("REPOLib.Modules.Upgrades"); if (type == null) { _hasModUpg = false; StatsUIPlugin.LogDebug($"未检测到 REPOLib,大概不需翻译"); return; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { if (assemblies[i].FullName.Contains("GoopUpgrades")) { _hasModUpg = true; StatsUIPlugin.LogDebug($"检测到 GoopUpgrades 升级项"); return; } } FieldInfo fieldInfo = AccessTools.Field(type, "_playerUpgrades"); if (fieldInfo == null) { _hasModUpg = false; } else if (fieldInfo.GetValue(null) is IDictionary dictionary) { _hasModUpg = dictionary.Count > 0; StatsUIPlugin.LogDebug($"检测到 {dictionary.Count} 个模组升级项"); } } catch (Exception ex) { _hasModUpg = false; StatsUIPlugin.Log.LogError((object)("REPOLib 检测失败:" + ex.Message)); } } internal static string GetTranslatedName(string displayName) { if (!_hasModUpg) { return displayName; } if (!SPConfig.EnableTranslation.Value) { return displayName; } if (string.IsNullOrEmpty(displayName)) { return displayName; } string text = displayName.ToUpper(); if (_transCache.TryGetValue(text, out var value)) { return value; } try { string text2 = default(string); if (AutoTranslator.Default.TryTranslate(text, ref text2)) { _transCache[text] = text2; StatsUIPlugin.LogDebug($"翻译:{text} → {text2}"); return text2; } } catch (Exception ex) { StatsUIPlugin.Log.LogError((object)("翻译失败:" + ex.Message)); } _transCache[text] = displayName; LogTranslationFailed(text); return displayName; } internal static void ProcessStatsUI(StatsUI instance) { try { if (!SPConfig.FontMode.Value) { return; } object? obj = _header?.GetValue(instance); TextMeshProUGUI val = (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); if (val != null && !((Behaviour)val).enabled) { return; } object? obj2 = _text?.GetValue(instance); TextMeshProUGUI text = (TextMeshProUGUI)((obj2 is TextMeshProUGUI) ? obj2 : null); object? obj3 = _numbersText?.GetValue(instance); TextMeshProUGUI val2 = (TextMeshProUGUI)((obj3 is TextMeshProUGUI) ? obj3 : null); Dictionary dictionary = _playerUpgrades?.GetValue(instance) as Dictionary; int num = ((dictionary != null && dictionary.Count != 0) ? dictionary.Values.Sum() : 0); if (num != _lastUpgHash || SPConfig.IsConfigChanged()) { UpdateFontSize(text, val2, val, dictionary?.Count ?? 0); StatsUIPlugin.LogDebug($"字体大小更改"); _sb.Clear(); foreach (KeyValuePair item in dictionary) { if (item.Value > 0) { _sb.Append("").Append(item.Value).Append("\n"); } } StatsUIPlugin.LogDebug($"数字拼接"); _lastUpgHash = num; } ((TMP_Text)val2).text = _sb.ToString(); } catch (Exception ex) { StatsUIPlugin.Log.LogError((object)("处理StatsUI出错:" + ex.Message + "\n" + ex.StackTrace)); } } private static void UpdateFontSize(TextMeshProUGUI Text, TextMeshProUGUI numbersText, TextMeshProUGUI headerText, int upgradeCount) { float num = ((upgradeCount > 5) ? ((float)(upgradeCount - 5) * SPConfig.ReducePerItem.Value) : 0f); float num3 = (((TMP_Text)Text).fontSize = Mathf.Clamp(SPConfig.BaseSize.Value - num, SPConfig.MinSize.Value, SPConfig.BaseSize.Value - num)); ((TMP_Text)numbersText).fontSize = num3 + SPConfig.NumOffset.Value; ((TMP_Text)headerText).fontSize = num3 + SPConfig.HeaderOffset.Value; } internal static void LogTranslationFailed(string key) { if (!_failedTrans.Contains(key)) { try { Directory.CreateDirectory(Path.GetDirectoryName(_bugFilePath)); File.AppendAllText(_bugFilePath, key + "=" + key + "\n", Encoding.UTF8); } catch (Exception ex) { StatsUIPlugin.Log.LogError((object)("写入Bug.txt失败:" + ex.Message)); } _failedTrans.Add(key); StatsUIPlugin.Log.LogWarning((object)("翻译失败 [" + key + "]:无对应中文,请清除其它翻译模组或联系汉化作者TooRed求助,QQ群:1050816144")); } } internal static void PlayerAvatarStart() { _lastUpgHash = 0; StatsUIPlugin.LogDebug($"重置状态栏缓存"); } } [BepInPlugin("StatsUIPlugin", "状态栏辅助插件", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class StatsUIPlugin : BaseUnityPlugin { [HarmonyPatch(typeof(StatsUI), "Fetch")] private static class StatsUIFetchPatch { private static void Postfix(StatsUI __instance) { SPManager.ProcessStatsUI(__instance); } } [HarmonyPatch(typeof(StatsUI), "GetUpgradeDisplayName")] private static class GetUpgradeDisplayNamePatch { private static void Postfix(ref string __result) { __result = SPManager.GetTranslatedName(__result); } } [HarmonyPatch(typeof(PlayerAvatar), "Start")] private static class HookPlayerAvatarStart { private static void Postfix() { SPManager.PlayerAvatarStart(); } } private Harmony _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; if (AutoTranslator.Default == null) { Log.LogError((object)"XUnity.AutoTranslator未加载!"); return; } _harmony = new Harmony("StatsUIPlugin"); _harmony.PatchAll(); SPConfig.Init(((BaseUnityPlugin)this).Config); SPManager.DetectModdedUpgrades(); Log.LogInfo((object)"加载成功!"); } internal static void LogDebug(FormattableString message) { if (SPConfig.DebugMode.Value) { Log.LogDebug((object)message.ToString()); Log.LogInfo((object)message.ToString()); } } }