using 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 ReadOnlyEntry; public ConfigEntryBase Entry; public bool ChineseOnly; } public enum DisplayLanguage { 中文, English } internal sealed class LiveLabel { public WeakReference Tmp; public CfgI18N I18N; public bool IsSection; } internal sealed class LiveValue { public WeakReference InputField; public CfgI18N I18N; } internal sealed class LiveSliderOptions { public WeakReference Slider; public CfgI18N I18N; public string[] OptionsCN; public string[] OptionsEN; } internal sealed class LiveToggleButtons { public WeakReference LeftTmp; public WeakReference 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; private ConfigEntry moduleNameInfo; private ConfigEntry moduleVersionInfo; private ConfigEntry featureEnabled; private ConfigEntry lethalTransferEnabled; private ConfigEntry siphonMode; private ConfigEntry transferRateMultiplier; internal static readonly Dictionary _cfgByEnSection = new Dictionary(StringComparer.Ordinal); internal static readonly Dictionary _cfgByEnKey = new Dictionary(StringComparer.Ordinal); internal static readonly Dictionary> _cfgByKeyOnly = new Dictionary>(StringComparer.Ordinal); private static readonly List _cfgI18Ns = new List(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 _liveLabels = new List(64); private static readonly List _liveValues = new List(8); private static readonly List _liveSliderOptions = new List(8); private static readonly List _liveToggleButtons = new List(8); private static readonly Dictionary _labelTmpFieldCache = new Dictionary(8); private static FieldInfo _inputStringSystemField; private static MethodInfo _inputStringSystemSetValue; private static readonly Dictionary _pendingKeyMatchIndex = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary _enumNameCNtoEN = new Dictionary { ["中文"] = "Chinese", ["吸取队友血量"] = "Drain", ["平均分血量"] = "Balance", ["传输队友血量"] = "Transfer", ["禁用传输"] = "Disabled" }; private static readonly Dictionary _enumNameENtoCN = new Dictionary { ["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("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(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(__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(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(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 BindI18N(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, T defaultVal, AcceptableValueBase acceptableValues = null, int? order = null, Action 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)(object)(cfgI18N.Entry = (ConfigEntryBase)(object)((BaseUnityPlugin)this).Config.Bind(sectEN, keyEN, defaultVal, new ConfigDescription(descEN, acceptableValues, new object[1] { configurationManagerAttributes }))); } private ConfigEntry BindReadOnlyI18N(string sectEN, string sectCN, string keyEN, string keyCN, string valueEN, string valueCN, int? order = null, Action 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(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(2); _cfgByKeyOnly[keyEN] = value; } value.Add(cfgI18N); if (!string.IsNullOrEmpty(keyCN)) { if (!_cfgByKeyOnly.TryGetValue(keyCN, out var value2)) { value2 = new List(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(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 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 hashSet = new HashSet(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(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(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(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(val3), RightTmp = new WeakReference(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 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 { } } } }