Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HealthSiphon v1.0.2
Zichen-HealthSiphon-1.0.2.dll
Decompiled 7 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Zichen-HealthSiphon-1.0.2")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3274c5d2a08bb59f56ece67a09765db191f3e1bc")] [assembly: AssemblyProduct("Zichen-HealthSiphon-1.0.2")] [assembly: AssemblyTitle("Zichen-HealthSiphon-1.0.2")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Zichen_HealthSiphon { public enum HealthSiphonMode { 吸取队友血量, 平均分血量, 传输队友血量, 禁用传输 } [BepInPlugin("zichen.healthsiphon", "Health Siphon", "1.0.2")] public sealed class ZichenHealthSiphonPlugin : BaseUnityPlugin { internal sealed class CfgI18N { public string SectionCN; public string SectionEN; public string KeyCN; public string KeyEN; public string DisplayEN; public string DescCN; public string DescEN; public ConfigurationManagerAttributes Attrs; public string ValueEN; public string ValueCN; public ConfigEntry<string> ReadOnlyEntry; public ConfigEntryBase Entry; public bool ChineseOnly; } public enum DisplayLanguage { 中文, English } internal sealed class LiveLabel { public WeakReference<TextMeshProUGUI> Tmp; public CfgI18N I18N; public bool IsSection; } internal sealed class LiveValue { public WeakReference<Object> InputField; public CfgI18N I18N; } internal sealed class LiveSliderOptions { public WeakReference<Object> Slider; public CfgI18N I18N; public string[] OptionsCN; public string[] OptionsEN; } internal sealed class LiveToggleButtons { public WeakReference<TextMeshProUGUI> LeftTmp; public WeakReference<TextMeshProUGUI> RightTmp; } public const string PluginGuid = "zichen.healthsiphon"; public const string PluginName = "Health Siphon"; public const string PluginVersion = "1.0.2"; private static readonly FieldInfo PlayerHealthGrabStaticGrabObjectField = typeof(PlayerHealthGrab).GetField("staticGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthGrabPhysColliderField = typeof(PlayerHealthGrab).GetField("physCollider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthGrabColliderActiveField = typeof(PlayerHealthGrab).GetField("colliderActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthGrabGrabbingTimerField = typeof(PlayerHealthGrab).GetField("grabbingTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthGrabHideLerpField = typeof(PlayerHealthGrab).GetField("hideLerp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo StaticGrabObjectPlayerGrabbingField = typeof(StaticGrabObject).GetField("playerGrabbing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthHealthField = typeof(PlayerHealth).GetField("health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerHealthMaxHealthField = typeof(PlayerHealth).GetField("maxHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerAvatarIsTumblingField = typeof(PlayerAvatar).GetField("isTumbling", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerAvatarIsDisabledField = typeof(PlayerAvatar).GetField("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RunManagerLevelIsShopField = typeof(RunManager).GetField("levelIsShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private Harmony harmony; public ConfigEntry<DisplayLanguage> displayLanguage; private ConfigEntry<string> moduleNameInfo; private ConfigEntry<string> moduleVersionInfo; private ConfigEntry<bool> featureEnabled; private ConfigEntry<bool> lethalTransferEnabled; private ConfigEntry<HealthSiphonMode> siphonMode; private ConfigEntry<string> transferRateMultiplier; internal static readonly Dictionary<string, CfgI18N> _cfgByEnSection = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); internal static readonly Dictionary<string, CfgI18N> _cfgByEnKey = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); internal static readonly Dictionary<string, List<CfgI18N>> _cfgByKeyOnly = new Dictionary<string, List<CfgI18N>>(StringComparer.Ordinal); private static readonly List<CfgI18N> _cfgI18Ns = new List<CfgI18N>(64); private static readonly string[] _chineseModKeywords = new string[6] { "chinese", "简体", "繁體", "繁体", "汉化", "中文" }; private static readonly string[] _chineseEnvironmentPluginGuids = new string[1] { "gravydevsupreme.xunity.autotranslator" }; private static readonly List<LiveLabel> _liveLabels = new List<LiveLabel>(64); private static readonly List<LiveValue> _liveValues = new List<LiveValue>(8); private static readonly List<LiveSliderOptions> _liveSliderOptions = new List<LiveSliderOptions>(8); private static readonly List<LiveToggleButtons> _liveToggleButtons = new List<LiveToggleButtons>(8); private static readonly Dictionary<Type, FieldInfo> _labelTmpFieldCache = new Dictionary<Type, FieldInfo>(8); private static FieldInfo _inputStringSystemField; private static MethodInfo _inputStringSystemSetValue; private static readonly Dictionary<string, int> _pendingKeyMatchIndex = new Dictionary<string, int>(StringComparer.Ordinal); private static readonly Dictionary<string, string> _enumNameCNtoEN = new Dictionary<string, string> { ["中文"] = "Chinese", ["吸取队友血量"] = "Drain", ["平均分血量"] = "Balance", ["传输队友血量"] = "Transfer", ["禁用传输"] = "Disabled" }; private static readonly Dictionary<string, string> _enumNameENtoCN = new Dictionary<string, string> { ["English"] = "英语", ["Chinese"] = "中文" }; public static ZichenHealthSiphonPlugin Instance { get; private set; } private void Awake() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown DetachFromManager(); Instance = this; ResetConfigIfVersionChanged(); BindConfig(); harmony = new Harmony("zichen.healthsiphon.patch"); harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerHealthGrab), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ZichenHealthSiphonPlugin), "PlayerHealthGrabUpdatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(StaticGrabObject), "OnDisable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ZichenHealthSiphonPlugin), "StaticGrabObjectOnDisablePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); TryHookREPOConfig(harmony); if (displayLanguage != null) { displayLanguage.SettingChanged += delegate { RefreshAllI18N(); }; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-health-siphon loaded."); } private void DetachFromManager() { ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; if (Instance == this) { Instance = null; } } private void BindConfig() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown DisplayLanguage displayLanguage = DetectDefaultLanguage(); ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = 1100 }; CfgI18N cfgI18N = NewI18N("A.Mod Info", "A.模组信息", "Health Siphon Language", "语言", "Switch display language.", "切换显示语言。", configurationManagerAttributes, readOnly: false); this.displayLanguage = ((BaseUnityPlugin)this).Config.Bind<DisplayLanguage>("A.Mod Info", "Health Siphon Language", displayLanguage, new ConfigDescription(cfgI18N.DescEN, (AcceptableValueBase)null, new object[1] { configurationManagerAttributes })); cfgI18N.Entry = (ConfigEntryBase)(object)this.displayLanguage; moduleNameInfo = BindReadOnlyI18N("A.Mod Info", "A.模组信息", "HealthSiphonModName", "模组名称", "Health Siphon", "生命虹吸", 1000, null, "Mod Name"); moduleVersionInfo = BindReadOnlyI18N("A.Mod Info", "A.模组信息", "HealthSiphonModVersion", "模组版本号", "1.0.2", "1.0.2", 990, null, "Mod Version"); featureEnabled = BindI18N("B.Global Settings", "B.全局设置", "Mod Enabled", "模组启用", "Disable to turn off the whole mod.", "关闭后整个模组全部功能失效。", defaultVal: true, null, 900, null, "Mod Enabled"); siphonMode = BindI18N("C.Health Siphon", "C.生命虹吸", "Siphon Mode", "生命虹吸模式", "Choose how health is exchanged after grabbing a teammate.", "选择抓住队友后使用哪种血量交换方式。", HealthSiphonMode.吸取队友血量, null, 800, null, "Siphon Mode"); transferRateMultiplier = BindI18N("C.Health Siphon", "C.生命虹吸", "Transfer Rate Multiplier", "血量传输倍率", "Multiply the official single transfer amount of 10. For example, at 5x, up to 50 HP can be transferred at once. Only 1 to 10 can be selected.", "按官方单次 10 血量为 1 倍进行放大。例如 5 倍时,单次最多传输 50 血量,实际仍以 10 为单位传输。只能选择 1 到 10 倍。", "1", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[10] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }), 790, null, "Transfer Rate Multiplier"); lethalTransferEnabled = BindI18N("C.Health Siphon", "C.生命虹吸", "Lethal Transfer", "致命传输", "If enabled, transferring or draining health can reduce player's health to 0 and cause death. If disabled, players will always keep at least 1 HP.", "启用时,传输或吸取血量可以使玩家血量降为 0 并导致死亡。禁用时,玩家最少保留 1 滴血。", defaultVal: true, null, 600, null, "Lethal Transfer"); } private void ResetConfigIfVersionChanged() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (File.Exists(configFilePath)) { string text = ReadConfigPluginVersion(configFilePath); if (!(text == "1.0.2")) { ResetConfigFileToDefaults(configFilePath); ((BaseUnityPlugin)this).Logger.LogWarning((object)(string.IsNullOrWhiteSpace(text) ? "Old config without Mod Version, reset to defaults." : ("Config version " + text + " -> 1.0.2, reset to defaults."))); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to reset config by version: " + ex.Message)); } } private static string ReadConfigPluginVersion(string configPath) { if (!File.Exists(configPath)) { return null; } string input = File.ReadAllText(configPath); Match match = Regex.Match(input, "(?m)^HealthSiphonModVersion\\s*=\\s*(.+?)\\s*$"); if (!match.Success) { match = Regex.Match(input, "(?m)^Mod Version\\s*=\\s*(.+?)\\s*$"); } if (!match.Success) { match = Regex.Match(input, "(?m)^模组版本号\\s*=\\s*(.+?)\\s*$"); } if (!match.Success) { return null; } return match.Groups[1].Value.Trim(); } private void ResetConfigFileToDefaults(string configPath) { ((BaseUnityPlugin)this).Config.Clear(); if (File.Exists(configPath)) { File.Delete(configPath); } ((BaseUnityPlugin)this).Config.Reload(); } private bool IsFeatureEnabled() { if (featureEnabled != null) { return featureEnabled.Value; } return false; } public bool IsLethalTransferEnabled() { if (lethalTransferEnabled != null) { return lethalTransferEnabled.Value; } return false; } private int GetConfiguredTransferAmount() { int result = 1; if (transferRateMultiplier != null && !string.IsNullOrEmpty(transferRateMultiplier.Value)) { int.TryParse(transferRateMultiplier.Value, out result); } return Mathf.Clamp(result, 1, 10) * 10; } private HealthSiphonMode GetMode() { if (siphonMode != null) { return siphonMode.Value; } return HealthSiphonMode.传输队友血量; } private static bool PlayerHealthGrabUpdatePrefix(PlayerHealthGrab __instance) { ZichenHealthSiphonPlugin instance = Instance; if ((Object)(object)instance == (Object)null || (Object)(object)__instance == (Object)null) { return true; } if (!instance.IsFeatureEnabled()) { return true; } instance.RunCustomHealthGrabUpdate(__instance); return false; } private static void StaticGrabObjectOnDisablePrefix(StaticGrabObject __instance) { if ((Object)(object)__instance == (Object)null || __instance.playerGrabbing == null) { return; } try { foreach (PhysGrabber item in new List<PhysGrabber>(__instance.playerGrabbing)) { if (!((Object)(object)item == (Object)null)) { if (item.isLocal) { item.ReleaseObject(-1, 0.1f); } else { item.grabbed = false; } } } } catch (Exception arg) { ZichenHealthSiphonPlugin instance = Instance; if (instance != null) { ManualLogSource logger = ((BaseUnityPlugin)instance).Logger; if (logger != null) { logger.LogError((object)$"Error in StaticGrabObjectOnDisablePrefix: {arg}"); } } } } private void RunCustomHealthGrabUpdate(PlayerHealthGrab instance) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)instance.playerAvatar == (Object)null) { return; } float num = GetFieldValue(PlayerHealthGrabHideLerpField, instance, 0f); bool fieldValue = GetFieldValue(PlayerAvatarIsTumblingField, instance.playerAvatar, fallback: false); bool flag = (Object)(object)RunManager.instance != (Object)null && GetFieldValue(RunManagerLevelIsShopField, RunManager.instance, fallback: false); if (fieldValue || SemiFunc.RunIsShop() || flag || SemiFunc.RunIsArena()) { if (num < 1f) { num += Time.deltaTime * 5f; num = Mathf.Clamp(num, 0f, 1f); instance.hideTransform.localScale = new Vector3(1f, instance.hideCurve.Evaluate(num), 1f); if (num >= 1f) { ((Component)instance.hideTransform).gameObject.SetActive(false); } SetFieldValue(PlayerHealthGrabHideLerpField, instance, num); } } else if (num > 0f) { if (!((Component)instance.hideTransform).gameObject.activeSelf) { ((Component)instance.hideTransform).gameObject.SetActive(true); } num -= Time.deltaTime * 2f; num = Mathf.Clamp(num, 0f, 1f); instance.hideTransform.localScale = new Vector3(1f, instance.hideCurve.Evaluate(num), 1f); SetFieldValue(PlayerHealthGrabHideLerpField, instance, num); } bool flag2 = !GetFieldValue(PlayerAvatarIsDisabledField, instance.playerAvatar, fallback: false) && num <= 0f; bool flag3 = GetFieldValue(PlayerHealthGrabColliderActiveField, instance, fallback: true); if (flag3 != flag2) { flag3 = flag2; SetFieldValue(PlayerHealthGrabColliderActiveField, instance, flag3); object? obj = PlayerHealthGrabPhysColliderField?.GetValue(instance); Collider val = (Collider)((obj is Collider) ? obj : null); if ((Object)(object)val != (Object)null) { val.enabled = flag3; } } ((Component)instance).transform.position = instance.followTransform.position; ((Component)instance).transform.rotation = instance.followTransform.rotation; if (!flag3 || (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient)) { return; } object? obj2 = PlayerHealthGrabStaticGrabObjectField?.GetValue(instance); StaticGrabObject val2 = (StaticGrabObject)((obj2 is StaticGrabObject) ? obj2 : null); if ((Object)(object)val2 == (Object)null) { return; } if (StaticGrabObjectPlayerGrabbingField?.GetValue(val2) is IList { Count: not 0 } list) { float num2 = GetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, 0f) + Time.deltaTime; if (num2 >= 1f) { for (int i = 0; i < list.Count; i++) { object? obj3 = list[i]; PhysGrabber val3 = (PhysGrabber)((obj3 is PhysGrabber) ? obj3 : null); if (!((Object)(object)val3?.playerAvatar == (Object)null)) { ProcessSiphon(instance.playerAvatar, val3.playerAvatar); } } num2 = 0f; } SetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, num2); } else { SetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, 0f); } } private void ProcessSiphon(PlayerAvatar targetAvatar, PlayerAvatar sourceAvatar) { if ((Object)(object)targetAvatar == (Object)null || (Object)(object)sourceAvatar == (Object)null || (Object)(object)targetAvatar.playerHealth == (Object)null || (Object)(object)sourceAvatar.playerHealth == (Object)null) { return; } int configuredTransferAmount = GetConfiguredTransferAmount(); switch (GetMode()) { case HealthSiphonMode.禁用传输: break; case HealthSiphonMode.吸取队友血量: TransferHealth(targetAvatar, sourceAvatar, configuredTransferAmount, notifyDonor: false); break; case HealthSiphonMode.平均分血量: { int health = GetHealth(targetAvatar.playerHealth); int health2 = GetHealth(sourceAvatar.playerHealth); int num = NormalizeHealthForBalance(health); int num2 = NormalizeHealthForBalance(health2); if (num == num2) { break; } int balanceTransferAmount = GetBalanceTransferAmount(Mathf.Abs(num2 - num), configuredTransferAmount); if (balanceTransferAmount >= 10) { if (num2 > num) { TransferHealth(sourceAvatar, targetAvatar, balanceTransferAmount, notifyDonor: true); } else if (num > num2) { TransferHealth(targetAvatar, sourceAvatar, balanceTransferAmount, notifyDonor: false); } } break; } default: TransferHealth(sourceAvatar, targetAvatar, configuredTransferAmount, notifyDonor: true); break; } } private static void TransferHealth(PlayerAvatar donorAvatar, PlayerAvatar receiverAvatar, int desiredAmount, bool notifyDonor) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)donorAvatar == (Object)null || (Object)(object)receiverAvatar == (Object)null || (Object)(object)donorAvatar.playerHealth == (Object)null || (Object)(object)receiverAvatar.playerHealth == (Object)null) { return; } int health = GetHealth(donorAvatar.playerHealth); int health2 = GetHealth(receiverAvatar.playerHealth); int maxHealth = GetMaxHealth(receiverAvatar.playerHealth); if (health2 >= maxHealth) { return; } if ((Object)(object)Instance != (Object)null && Instance.IsLethalTransferEnabled()) { if (health <= 0) { return; } if (health <= 10) { int num = health; receiverAvatar.playerHealth.HealOther(num, true); donorAvatar.playerHealth.HurtOther(num, Vector3.zero, false, -1, false); if (notifyDonor) { donorAvatar.HealedOther(); } return; } int num2 = NormalizeTransferAmount(Math.Min(desiredAmount, Math.Min(maxHealth - health2, health))); if (num2 >= 10) { receiverAvatar.playerHealth.HealOther(num2, true); donorAvatar.playerHealth.HurtOther(num2, Vector3.zero, false, -1, false); if (notifyDonor) { donorAvatar.HealedOther(); } } } else { if (health <= 1) { return; } int num3 = NormalizeTransferAmount(Math.Min(desiredAmount, Math.Min(maxHealth - health2, health - 1))); if (num3 >= 10) { receiverAvatar.playerHealth.HealOther(num3, true); donorAvatar.playerHealth.HurtOther(num3, Vector3.zero, false, -1, false); if (notifyDonor) { donorAvatar.HealedOther(); } } } } private static int GetHealth(PlayerHealth playerHealth) { return GetFieldValue(PlayerHealthHealthField, playerHealth, 0); } private static int GetMaxHealth(PlayerHealth playerHealth) { return GetFieldValue(PlayerHealthMaxHealthField, playerHealth, 100); } private static int GetBalanceTransferAmount(int healthDifference, int maxTransferAmount) { int val = healthDifference / 2; return NormalizeTransferAmount(Math.Min(maxTransferAmount, val)); } private static int NormalizeTransferAmount(int amount) { if (amount < 10) { return 0; } return amount / 10 * 10; } private static int NormalizeHealthForBalance(int health) { if (health < 10) { return health; } return health / 10 * 10; } private static T GetFieldValue<T>(FieldInfo field, object instance, T fallback) { try { if (field != null) { object value = field.GetValue(instance); if (value is T) { return (T)value; } } } catch { } return fallback; } private static void SetFieldValue<T>(FieldInfo field, object instance, T value) { try { field?.SetValue(instance, value); } catch { } } public static bool UseChinese() { ZichenHealthSiphonPlugin instance = Instance; if ((Object)(object)instance == (Object)null || instance.displayLanguage == null) { return false; } return instance.displayLanguage.Value == DisplayLanguage.中文; } private ConfigEntry<T> BindI18N<T>(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, T defaultVal, AcceptableValueBase acceptableValues = null, int? order = null, Action<ConfigEntryBase> customDrawer = null, string displayEN = null) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes(); if (order.HasValue) { configurationManagerAttributes.Order = order.Value; } if (customDrawer != null) { configurationManagerAttributes.CustomDrawer = customDrawer; } CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, descEN, descCN, configurationManagerAttributes, readOnly: false); cfgI18N.DisplayEN = displayEN; ApplyI18N(cfgI18N); return (ConfigEntry<T>)(object)(cfgI18N.Entry = (ConfigEntryBase)(object)((BaseUnityPlugin)this).Config.Bind<T>(sectEN, keyEN, defaultVal, new ConfigDescription(descEN, acceptableValues, new object[1] { configurationManagerAttributes }))); } private ConfigEntry<string> BindReadOnlyI18N(string sectEN, string sectCN, string keyEN, string keyCN, string valueEN, string valueCN, int? order = null, Action<ConfigEntryBase> customDrawer = null, string displayEN = null) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { ReadOnly = true }; if (order.HasValue) { configurationManagerAttributes.Order = order.Value; } if (customDrawer != null) { configurationManagerAttributes.CustomDrawer = customDrawer; } CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, "", "", configurationManagerAttributes, readOnly: true); cfgI18N.ValueEN = valueEN; cfgI18N.ValueCN = valueCN; cfgI18N.DisplayEN = displayEN; ApplyI18N(cfgI18N); string text = (UseChinese() ? valueCN : valueEN); cfgI18N.ReadOnlyEntry = ((BaseUnityPlugin)this).Config.Bind<string>(sectEN, keyEN, text, new ConfigDescription("", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes })); cfgI18N.Entry = (ConfigEntryBase)(object)cfgI18N.ReadOnlyEntry; if (cfgI18N.ReadOnlyEntry.Value != text) { cfgI18N.ReadOnlyEntry.Value = text; } return cfgI18N.ReadOnlyEntry; } private static CfgI18N NewI18N(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, ConfigurationManagerAttributes attrs, bool readOnly) { CfgI18N cfgI18N = new CfgI18N { SectionEN = sectEN, SectionCN = sectCN, KeyEN = keyEN, KeyCN = keyCN, DescEN = descEN, DescCN = descCN, Attrs = attrs }; ApplyI18N(cfgI18N); _cfgI18Ns.Add(cfgI18N); _cfgByEnSection[sectEN] = cfgI18N; _cfgByEnKey[sectEN + "\0" + keyEN] = cfgI18N; if (!_cfgByKeyOnly.TryGetValue(keyEN, out var value)) { value = new List<CfgI18N>(2); _cfgByKeyOnly[keyEN] = value; } value.Add(cfgI18N); if (!string.IsNullOrEmpty(keyCN)) { if (!_cfgByKeyOnly.TryGetValue(keyCN, out var value2)) { value2 = new List<CfgI18N>(2); _cfgByKeyOnly[keyCN] = value2; } if (!value2.Contains(cfgI18N)) { value2.Add(cfgI18N); } } string text = FixNamingForLookup(keyEN); if (!string.Equals(text, keyEN, StringComparison.Ordinal)) { if (!_cfgByKeyOnly.TryGetValue(text, out var value3)) { value3 = new List<CfgI18N>(2); _cfgByKeyOnly[text] = value3; } if (!value3.Contains(cfgI18N)) { value3.Add(cfgI18N); } } return cfgI18N; } private static void ApplyI18N(CfgI18N i18n) { if (i18n?.Attrs == null) { return; } bool flag = UseChinese(); i18n.Attrs.Category = (flag ? i18n.SectionCN : i18n.SectionEN); i18n.Attrs.DispName = (flag ? i18n.KeyCN : (i18n.DisplayEN ?? i18n.KeyEN)); i18n.Attrs.Description = (flag ? i18n.DescCN : i18n.DescEN); if (i18n.ReadOnlyEntry != null) { string text = (flag ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text) { i18n.ReadOnlyEntry.Value = text; } } } private static void RefreshAllI18N() { for (int i = 0; i < _cfgI18Ns.Count; i++) { ApplyI18N(_cfgI18Ns[i]); } RefreshLiveLabels(); } private static string FixNamingForLookup(string input) { if (string.IsNullOrEmpty(input)) { return input; } input = Regex.Replace(input, "([a-z])([A-Z])", "$1 $2"); input = Regex.Replace(input, "([A-Z])([A-Z][a-z])", "$1 $2"); input = Regex.Replace(input, "\\s+", " "); input = Regex.Replace(input, "([A-Z]\\.)\\s([A-Z]\\.)", "$1$2"); return input.Trim(); } private static DisplayLanguage DetectDefaultLanguage() { try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { string text = pluginInfo.Key ?? string.Empty; PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = string.Empty; } string text2 = (string)obj; string[] chineseModKeywords = _chineseModKeywords; foreach (string value2 in chineseModKeywords) { if (text.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return DisplayLanguage.中文; } } chineseModKeywords = _chineseEnvironmentPluginGuids; foreach (string b in chineseModKeywords) { if (string.Equals(text, b, StringComparison.OrdinalIgnoreCase)) { return DisplayLanguage.中文; } } } } catch { } return DisplayLanguage.English; } internal static void TryHookREPOConfig(Harmony h) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MenuLib.MenuAPI"); if (type == null) { LogInfo("MenuLib 未安装,跳过 REPOConfig 双语劫持。"); return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(REPOConfigCreateHook), "Postfix", (Type[])null, (Type[])null)); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { "CreateREPOLabel", "CreateREPOToggle", "CreateREPOSlider", "CreateREPOInputField", "CreateREPOButton" }; int num = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!hashSet.Contains(declaredMethod.Name)) { continue; } ParameterInfo[] parameters = declaredMethod.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { try { h.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch { } } } if (num > 0) { LogInfo($"REPOConfig 双语显示已挂载({num} 个 UI 创建方法)。"); } else { LogInfo("REPOConfig 没找到可劫持 of UI 方法。"); } } catch (Exception ex) { LogInfo("REPOConfig 劫持失败:" + ex.Message); } } private static void LogInfo(string msg) { if ((Object)(object)Instance != (Object)null && ((BaseUnityPlugin)Instance).Logger != null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg); } } private static bool IsOwnEntry(CfgI18N i18n) { if (i18n == null || i18n.Entry == null || (Object)(object)Instance == (Object)null) { return false; } return i18n.Entry.ConfigFile == ((BaseUnityPlugin)Instance).Config; } private static bool IsRenderingOurMod() { return true; } internal static void RegisterLiveLabelIfMatched(string label, object uiElement) { if (string.IsNullOrEmpty(label) || uiElement == null) { return; } string text = label.Trim().Replace("▼", "").Replace("▶", "") .Replace(" ", ""); CfgI18N cfgI18N = null; for (int i = 0; i < _cfgI18Ns.Count; i++) { CfgI18N cfgI18N2 = _cfgI18Ns[i]; string value = cfgI18N2.SectionEN?.Replace(" ", ""); string value2 = cfgI18N2.SectionCN?.Replace(" ", ""); if ((!string.IsNullOrEmpty(value) && text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) || (!string.IsNullOrEmpty(value2) && text.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)) { cfgI18N = cfgI18N2; break; } } if (cfgI18N != null) { RegisterOneLiveLabel(uiElement, cfgI18N, isSection: true); return; } string key = label.Trim(); if (!_cfgByKeyOnly.TryGetValue(key, out var value3) || value3 == null || value3.Count == 0) { return; } if (value3.Count == 1) { RegisterOneLiveLabel(uiElement, value3[0], isSection: false); return; } if (!_pendingKeyMatchIndex.TryGetValue(key, out var value4)) { value4 = 0; } if (value4 >= value3.Count) { value4 = 0; } RegisterOneLiveLabel(uiElement, value3[value4], isSection: false); _pendingKeyMatchIndex[key] = value4 + 1; } private static void RegisterOneLiveLabel(object uiElement, CfgI18N i18n, bool isSection) { if (!IsOwnEntry(i18n) || !IsRenderingOurMod()) { return; } TextMeshProUGUI labelTmp = GetLabelTmp(uiElement); if ((Object)(object)labelTmp == (Object)null) { return; } bool flag = UseChinese(); string text = ((!isSection) ? (flag ? i18n.KeyCN : (i18n.DisplayEN ?? i18n.KeyEN)) : (flag ? i18n.SectionCN : i18n.SectionEN)); if (!string.IsNullOrEmpty(text)) { try { ((TMP_Text)labelTmp).text = text; } catch { } } _liveLabels.Add(new LiveLabel { Tmp = new WeakReference<TextMeshProUGUI>(labelTmp), I18N = i18n, IsSection = isSection }); if (!isSection && i18n.ReadOnlyEntry != null) { Object val = (Object)((uiElement is Object) ? uiElement : null); if (val != null && AccessTools.Field(uiElement.GetType(), "inputStringSystem") != null) { _liveValues.Add(new LiveValue { InputField = new WeakReference<Object>(val), I18N = i18n }); TrySetInputFieldValue(val, flag ? i18n.ValueCN : i18n.ValueEN); } } if (!isSection && i18n.Entry != null && i18n.Entry.SettingType.IsSubclassOf(typeof(Enum))) { Object val2 = (Object)((uiElement is Object) ? uiElement : null); if (val2 != null) { PropertyInfo propertyInfo = AccessTools.Property(((object)val2).GetType(), "stringOptions"); if (propertyInfo != null) { string[] names = Enum.GetNames(i18n.Entry.SettingType); string[] array = BuildEnumOptionsCN(names); string[] array2 = BuildEnumOptionsEN(names); _liveSliderOptions.Add(new LiveSliderOptions { Slider = new WeakReference<Object>(val2), I18N = i18n, OptionsCN = array, OptionsEN = array2 }); TrySetSliderOptions(val2, propertyInfo, flag ? array : array2); } } } Type type = uiElement.GetType(); FieldInfo fieldInfo = AccessTools.Field(type, "leftButtonTMP"); FieldInfo fieldInfo2 = AccessTools.Field(type, "rightButtonTMP"); if (fieldInfo != null && fieldInfo2 != null) { object? value = fieldInfo.GetValue(uiElement); TextMeshProUGUI val3 = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null); object? value2 = fieldInfo2.GetValue(uiElement); TextMeshProUGUI val4 = (TextMeshProUGUI)((value2 is TextMeshProUGUI) ? value2 : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null) { _liveToggleButtons.Add(new LiveToggleButtons { LeftTmp = new WeakReference<TextMeshProUGUI>(val3), RightTmp = new WeakReference<TextMeshProUGUI>(val4) }); ApplyToggleButtonText(val3, val4, flag); } } } private static TextMeshProUGUI GetLabelTmp(object uiElement) { try { Type type = uiElement.GetType(); if (!_labelTmpFieldCache.TryGetValue(type, out var value)) { value = AccessTools.Field(type, "labelTMP"); _labelTmpFieldCache[type] = value; } object? obj = value?.GetValue(uiElement); return (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); } catch { return null; } } internal static void RefreshLiveLabels() { bool flag = UseChinese(); for (int num = _liveLabels.Count - 1; num >= 0; num--) { LiveLabel liveLabel = _liveLabels[num]; if (!liveLabel.Tmp.TryGetTarget(out var target) || (Object)(object)target == (Object)null || (Object)(object)((Component)target).gameObject == (Object)null) { _liveLabels.RemoveAt(num); } else { string text = ((!liveLabel.IsSection) ? (flag ? liveLabel.I18N.KeyCN : (liveLabel.I18N.DisplayEN ?? liveLabel.I18N.KeyEN)) : (flag ? liveLabel.I18N.SectionCN : liveLabel.I18N.SectionEN)); try { ((TMP_Text)target).text = text; } catch { _liveLabels.RemoveAt(num); } } } for (int num2 = _liveValues.Count - 1; num2 >= 0; num2--) { LiveValue liveValue = _liveValues[num2]; if (!liveValue.InputField.TryGetTarget(out var target2) || target2 == (Object)null) { _liveValues.RemoveAt(num2); } else { string newVal = (flag ? liveValue.I18N.ValueCN : liveValue.I18N.ValueEN); if (!TrySetInputFieldValue(target2, newVal)) { _liveValues.RemoveAt(num2); } } } for (int num3 = _liveSliderOptions.Count - 1; num3 >= 0; num3--) { LiveSliderOptions liveSliderOptions = _liveSliderOptions[num3]; if (!liveSliderOptions.Slider.TryGetTarget(out var target3) || target3 == (Object)null) { _liveSliderOptions.RemoveAt(num3); } else { PropertyInfo propertyInfo = AccessTools.Property(((object)target3).GetType(), "stringOptions"); if (propertyInfo == null) { _liveSliderOptions.RemoveAt(num3); } else { TrySetSliderOptions(target3, propertyInfo, flag ? liveSliderOptions.OptionsCN : liveSliderOptions.OptionsEN); } } } for (int num4 = _liveToggleButtons.Count - 1; num4 >= 0; num4--) { LiveToggleButtons liveToggleButtons = _liveToggleButtons[num4]; if (!liveToggleButtons.LeftTmp.TryGetTarget(out var target4) || (Object)(object)target4 == (Object)null || !liveToggleButtons.RightTmp.TryGetTarget(out var target5) || (Object)(object)target5 == (Object)null) { _liveToggleButtons.RemoveAt(num4); } else { ApplyToggleButtonText(target4, target5, flag); } } _pendingKeyMatchIndex.Clear(); } private static bool TrySetInputFieldValue(Object ifObj, string newVal) { try { Type type = ((object)ifObj).GetType(); if (_inputStringSystemField == null || _inputStringSystemField.DeclaringType != type) { _inputStringSystemField = AccessTools.Field(type, "inputStringSystem"); } if (_inputStringSystemField == null) { return false; } object value = _inputStringSystemField.GetValue(ifObj); if (value == null) { return false; } if (_inputStringSystemSetValue == null || _inputStringSystemSetValue.DeclaringType != value.GetType()) { _inputStringSystemSetValue = AccessTools.Method(value.GetType(), "SetValue", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); } if (_inputStringSystemSetValue == null) { return false; } _inputStringSystemSetValue.Invoke(value, new object[2] { newVal ?? string.Empty, false }); return true; } catch { return false; } } private static string[] BuildEnumOptionsCN(string[] enumNames) { string[] array = new string[enumNames.Length]; for (int i = 0; i < enumNames.Length; i++) { array[i] = (_enumNameENtoCN.TryGetValue(enumNames[i], out var value) ? value : enumNames[i]); } return array; } private static string[] BuildEnumOptionsEN(string[] enumNames) { string[] array = new string[enumNames.Length]; for (int i = 0; i < enumNames.Length; i++) { array[i] = (_enumNameCNtoEN.TryGetValue(enumNames[i], out var value) ? value : enumNames[i]); } return array; } private static void TrySetSliderOptions(Object sliderObj, PropertyInfo prop, string[] options) { try { prop.SetValue(sliderObj, options); AccessTools.Method(((object)sliderObj).GetType(), "UpdateBarText", (Type[])null, (Type[])null)?.Invoke(sliderObj, null); } catch { } } private static void ApplyToggleButtonText(TextMeshProUGUI leftTmp, TextMeshProUGUI rightTmp, bool cn) { try { ((TMP_Text)leftTmp).text = (cn ? "开" : "ON"); ((TMP_Text)rightTmp).text = (cn ? "关" : "OFF"); } catch { } } } internal sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; } internal static class REPOConfigCreateHook { public static void Postfix(object[] __args, object __result) { try { if (__args != null && __args.Length != 0 && __result != null && __args[0] is string label) { ZichenHealthSiphonPlugin.RegisterLiveLabelIfMatched(label, __result); } } catch { } } } }