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 ExtractionDelayConfirm v1.1.1
Zichen-ExtractionDelayConfirm-1.1.1.dll
Decompiled 4 days 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 TMPro; using UnityEngine; using Zichen_ExtractionDelayConfirm.I18N; [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-ExtractionDelayConfirm-1.1.1")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+661e280f879dd7b1bef42dc059c1d2b942f89fee")] [assembly: AssemblyProduct("Zichen-ExtractionDelayConfirm-1.1.1")] [assembly: AssemblyTitle("Zichen-ExtractionDelayConfirm-1.1.1")] [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; } } } [BepInPlugin("zichen.extractiondelayconfirm", "Extraction Delay Confirm", "1.1.1")] public sealed class ZichenExtractionDelayConfirmPlugin : BaseUnityPlugin { private sealed class PendingExtractionState { public ExtractionPoint ExtractionPoint { get; } public Dictionary<int, PlayerConfirmTracker> PlayerTrackers { get; } = new Dictionary<int, PlayerConfirmTracker>(); public string PromptText { get; set; } public float NextCheckTime { get; set; } public bool ConfirmTriggered { get; set; } public int LastObservedHaul { get; set; } = -1; public bool HasZehsConfirmButton { get; set; } public PendingExtractionState(ExtractionPoint extractionPoint) { ExtractionPoint = extractionPoint; } } private sealed class PlayerConfirmTracker { public Vector3 AnchorPosition; public float StillTime; } public const string PluginGuid = "zichen.extractiondelayconfirm"; public const string PluginVersion = "1.1.1"; public const string PluginName = "Extraction Delay Confirm"; private const string ZehsConfirmPluginGuid = "com.github.zehsteam.ExtractionPointConfirmButton"; private const string ZehsConfirmAssemblyName = "com.github.zehsteam.ExtractionPointConfirmButton"; private const string ZehsConfirmPatchTypeName = "com.github.zehsteam.ExtractionPointConfirmButton.Patches.ExtractionPointPatch"; private const string InfoSection = "模组信息"; private const string EnhancerSection = "A.全局设置"; private const float ConfirmCheckIntervalSeconds = 0.1f; private const float PromptHeightOffset = 1.8f; private const float FixedMoveToleranceMeters = 0.1f; private static readonly FieldInfo ExtractionPointCurrentStateField = typeof(ExtractionPoint).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ExtractionPointIsShopField = typeof(ExtractionPoint).GetField("isShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo ExtractionPointStateSetMethod = typeof(ExtractionPoint).GetMethod("StateSet", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo ExtractionPointSuccessDelayField = typeof(ExtractionPoint).GetField("successDelay", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorCurrentHaulField = typeof(RoundDirector).GetField("currentHaul", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerAvatarIsDisabledField = typeof(PlayerAvatar).GetField("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly Dictionary<int, PendingExtractionState> pendingStates = new Dictionary<int, PendingExtractionState>(); private readonly HashSet<int> allowSuccessInstanceIds = new HashSet<int>(); private readonly List<int> pendingRemovalKeys = new List<int>(4); private readonly HashSet<int> scratchSeenPlayers = new HashSet<int>(); private readonly List<int> scratchPlayerRemovalKeys = new List<int>(8); private ConfigEntry<string> moduleNameInfo; private ConfigEntry<string> moduleVersionInfo; private ConfigEntry<bool> modEnabled; private ConfigEntry<I18NRegistry.DisplayLanguage> displayLanguage; private ConfigEntry<bool> showCountdownPrompt; private ConfigEntry<int> confirmRangeMeters; private ConfigEntry<int> standStillSeconds; private I18NRegistry _i18n; private I18NSwitcher _i18nSwitcher; private bool _autoLocaleChecked; private Harmony harmony; private GUIStyle promptStyle; private GUIStyle promptShadowStyle; private Font cachedChineseFont; private bool chineseFontResolved; private Camera cachedCamera; private int cachedCameraFrame = -1; private bool compatibilityChecked; private bool hasZehsConfirmButton; private Type zehsExtractionPointPatchType; private PropertyInfo zehsConfirmButtonsProperty; private MethodInfo zehsConfirmExtractionPointMethod; private MethodInfo zehsConfirmButtonOnClickMethod; public static ZichenExtractionDelayConfirmPlugin Instance { get; private set; } private I18NRegistry.DisplayLanguage CurrentLanguage { get { if (displayLanguage != null) { return displayLanguage.Value; } return I18NRegistry.DisplayLanguage.中文; } } private void Awake() { DetachFromManager(); Instance = this; ResetConfigIfVersionChanged(); BindConfig(); ResolveCompatibility(); ApplyPatches(); _i18nSwitcher = new I18NSwitcher(_i18n, harmony, "Extraction Delay Confirm", ((BaseUnityPlugin)this).Logger); _i18nSwitcher.InstallRepoConfigHook(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-extraction-delay-confirm loaded."); if (!_autoLocaleChecked) { ((MonoBehaviour)this).StartCoroutine(AutoFollowGameLocaleOnce()); } } private IEnumerator AutoFollowGameLocaleOnce() { float waited = 0f; while (waited < 20f) { waited += 0.2f; yield return (object)new WaitForSecondsRealtime(0.2f); if (LanguageDetector.TryReadGameLocaleCode(out var code)) { if (LanguageDetector.IsChineseCode(code) && _i18n?.LanguageEntry != null && _i18n.LanguageEntry.Value != I18NRegistry.DisplayLanguage.中文) { _i18n.LanguageEntry.Value = I18NRegistry.DisplayLanguage.中文; ((BaseUnityPlugin)this).Logger.LogInfo((object)"已根据游戏内语言设置自动切换为中文。"); } _autoLocaleChecked = true; yield break; } } _autoLocaleChecked = true; } private void Update() { //IL_0121: Unknown result type (might be due to invalid IL or missing references) if (!IsModEnabled()) { if (pendingStates.Count > 0) { ReleasePendingExtractions(); } } else { if (pendingStates.Count == 0) { return; } bool flag = IsHostAuthority(); float time = Time.time; int currentHaulValue = GetCurrentHaulValue(); List<PlayerAvatar> playerList = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList : null); pendingRemovalKeys.Clear(); foreach (KeyValuePair<int, PendingExtractionState> pendingState in pendingStates) { PendingExtractionState value = pendingState.Value; ExtractionPoint val = value?.ExtractionPoint; if (value == null || value.ConfirmTriggered || (Object)(object)val == (Object)null || !IsActiveGameplayExtractionPoint(val)) { pendingRemovalKeys.Add(pendingState.Key); continue; } if (!IsGoalReached(val, currentHaulValue)) { pendingRemovalKeys.Add(pendingState.Key); continue; } if (flag && !value.HasZehsConfirmButton) { KeepExtractionPending(val); } if (!(time < value.NextCheckTime)) { value.NextCheckTime = time + 0.1f; if (UpdateNearbyPlayers(value, ((Component)val).transform.position, 0.1f, currentHaulValue, playerList) && flag) { value.ConfirmTriggered = true; TriggerConfirmedExtraction(val); pendingRemovalKeys.Add(pendingState.Key); } } } for (int i = 0; i < pendingRemovalKeys.Count; i++) { int num = pendingRemovalKeys[i]; pendingStates.Remove(num); allowSuccessInstanceIds.Remove(num); } } } private void ReleasePendingExtractions() { pendingRemovalKeys.Clear(); int currentHaulValue = GetCurrentHaulValue(); foreach (KeyValuePair<int, PendingExtractionState> pendingState in pendingStates) { PendingExtractionState value = pendingState.Value; if ((Object)(object)value?.ExtractionPoint != (Object)null && !value.ConfirmTriggered && IsActiveGameplayExtractionPoint(value.ExtractionPoint) && IsGoalReached(value.ExtractionPoint, currentHaulValue) && !value.HasZehsConfirmButton) { AllowAndTriggerSuccess(value.ExtractionPoint); } pendingRemovalKeys.Add(pendingState.Key); } for (int i = 0; i < pendingRemovalKeys.Count; i++) { pendingStates.Remove(pendingRemovalKeys[i]); allowSuccessInstanceIds.Remove(pendingRemovalKeys[i]); } } private void OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if ((int)Event.current.type != 7 || !IsModEnabled() || showCountdownPrompt == null || !showCountdownPrompt.Value || pendingStates.Count == 0) { return; } Camera activeCamera = GetActiveCamera(); if ((Object)(object)activeCamera == (Object)null) { return; } EnsurePromptStyles(); foreach (PendingExtractionState value in pendingStates.Values) { if (!((Object)(object)value?.ExtractionPoint == (Object)null) && !value.ConfirmTriggered && !string.IsNullOrWhiteSpace(value.PromptText)) { Vector3 val = ((Component)value.ExtractionPoint).transform.position + new Vector3(0f, 1.8f, 0f); Vector3 val2 = activeCamera.WorldToScreenPoint(val); if (!(val2.z <= 0f)) { val2.y = (float)Screen.height - val2.y; DrawCenteredPrompt(new Vector2(val2.x, val2.y), value.PromptText); } } } } 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() { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } if (Instance == this) { Instance = null; } } private void BindConfig() { bool defaultChinese = LanguageDetector.DetectDefault(); _i18n = new I18NRegistry(((BaseUnityPlugin)this).Config); _i18n.BindLanguage(defaultChinese); moduleNameInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Name", "模组名称", "Extraction Delay Confirm", "延迟提取确认", 1000, DrawInfo); moduleVersionInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Version", "模组版本号", "1.1.1", "1.1.1", 990, DrawInfo); modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("A.全局设置", "模组启用", true, ConfigDescriptionWithOrder("关闭后整个模组全部功能彻底失效。达到提取金额后,不再自动提取,改为需要玩家在提取点附近站定确认。", 900)); displayLanguage = _i18n.LanguageEntry; showCountdownPrompt = ((BaseUnityPlugin)this).Config.Bind<bool>("A.全局设置", "显示倒计时提示", true, ConfigDescriptionWithOrder("开启后,安装了本模组的玩家会在提取点上方看到靠近提示和站立倒计时提示。", 880)); confirmRangeMeters = ((BaseUnityPlugin)this).Config.Bind<int>("A.全局设置", "确认范围/米", 4, ConfigDescriptionWithOrder("达到提取金额后,提取点多大范围内的玩家可以触发站定确认。默认 4 米。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), 870)); standStillSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("A.全局设置", "站定确认秒数", 3, ConfigDescriptionWithOrder("达到提取金额后,范围内任意玩家连续站定多久才会自动触发提取。默认 3 秒。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), 860)); } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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 //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown harmony = new Harmony("zichen.extractiondelayconfirm.patch"); if (ExtractionPointStateSetMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Failed to find ExtractionPoint.StateSet for patching."); return; } harmony.Patch((MethodBase)ExtractionPointStateSetMethod, new HarmonyMethod(typeof(ZichenExtractionDelayConfirmPlugin), "StateSetPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo = AccessTools.Method(typeof(ExtractionPoint), "Update", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ZichenExtractionDelayConfirmPlugin), "ExtractionPointUpdatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (zehsConfirmExtractionPointMethod != null) { harmony.Patch((MethodBase)zehsConfirmExtractionPointMethod, new HarmonyMethod(typeof(ZichenExtractionDelayConfirmPlugin), "ZehsConfirmExtractionPointPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private bool IsModEnabled() { if (modEnabled != null) { return modEnabled.Value; } return true; } private static bool IsStaticModEnabled() { ZichenExtractionDelayConfirmPlugin instance = Instance; if (!((Object)(object)instance == (Object)null)) { return instance.IsModEnabled(); } return true; } private static bool IsHostAuthority() { try { return SemiFunc.IsMasterClientOrSingleplayer(); } catch { return false; } } private static bool IsActiveGameplayExtractionPoint(ExtractionPoint extractionPoint) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 if ((Object)(object)extractionPoint == (Object)null || IsShopExtractionPoint(extractionPoint)) { return false; } if (ExtractionPointCurrentStateField?.GetValue(extractionPoint) is State val) { return (int)val == 2; } return false; } private static bool IsShopExtractionPoint(ExtractionPoint extractionPoint) { bool flag = default(bool); int num; if ((Object)(object)extractionPoint != (Object)null && ExtractionPointIsShopField != null) { object value = ExtractionPointIsShopField.GetValue(extractionPoint); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static int GetCurrentHaulValue() { if ((Object)(object)RoundDirector.instance != (Object)null && RoundDirectorCurrentHaulField != null) { object value = RoundDirectorCurrentHaulField.GetValue(RoundDirector.instance); if (value is int) { return (int)value; } } return 0; } private static bool IsGoalReached(ExtractionPoint extractionPoint, int currentHaul) { if ((Object)(object)extractionPoint != (Object)null && extractionPoint.haulGoal > 0) { return currentHaul >= extractionPoint.haulGoal; } return false; } private bool UpdateNearbyPlayers(PendingExtractionState state, Vector3 extractionPosition, float deltaTime, int currentHaul, List<PlayerAvatar> playerList) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) if (state.LastObservedHaul >= 0 && currentHaul != state.LastObservedHaul) { state.PlayerTrackers.Clear(); } state.LastObservedHaul = currentHaul; float num = Mathf.Max(1f, (float)confirmRangeMeters.Value); float num2 = num * num; float num3 = 0.010000001f; float num4 = Mathf.Max(1f, (float)standStillSeconds.Value); float num5 = 0f; int num6 = 0; if (playerList == null) { state.PlayerTrackers.Clear(); state.PromptText = BuildApproachPrompt((int)num); return false; } scratchSeenPlayers.Clear(); foreach (PlayerAvatar player in playerList) { if ((Object)(object)player == (Object)null || IsPlayerDisabled(player)) { continue; } Vector3 position = ((Component)player).transform.position; Vector3 val = position - extractionPosition; if (((Vector3)(ref val)).sqrMagnitude > num2) { continue; } num6++; int instanceID = ((Object)player).GetInstanceID(); scratchSeenPlayers.Add(instanceID); if (!state.PlayerTrackers.TryGetValue(instanceID, out var value)) { value = new PlayerConfirmTracker { AnchorPosition = position, StillTime = 0f }; state.PlayerTrackers[instanceID] = value; } else { val = position - value.AnchorPosition; if (((Vector3)(ref val)).sqrMagnitude > num3) { value.AnchorPosition = position; value.StillTime = 0f; } else { value.StillTime += deltaTime; } } num5 = Mathf.Max(num5, value.StillTime); if (value.StillTime >= num4) { state.PromptText = BuildConfirmedPrompt(); return true; } } if (state.PlayerTrackers.Count > 0) { scratchPlayerRemovalKeys.Clear(); foreach (int key in state.PlayerTrackers.Keys) { if (!scratchSeenPlayers.Contains(key)) { scratchPlayerRemovalKeys.Add(key); } } for (int i = 0; i < scratchPlayerRemovalKeys.Count; i++) { state.PlayerTrackers.Remove(scratchPlayerRemovalKeys[i]); } } state.PromptText = ((num6 <= 0) ? BuildApproachPrompt((int)num) : BuildCountdownPrompt(num4, num5)); return false; } private void TriggerConfirmedExtraction(ExtractionPoint extractionPoint) { if (!TryTriggerZehsConfirmButton(extractionPoint)) { AllowAndTriggerSuccess(extractionPoint); } } private void AllowAndTriggerSuccess(ExtractionPoint extractionPoint) { if ((Object)(object)extractionPoint == (Object)null || ExtractionPointStateSetMethod == null) { return; } int instanceID = ((Object)extractionPoint).GetInstanceID(); allowSuccessInstanceIds.Add(instanceID); try { ExtractionPointStateSetMethod.Invoke(extractionPoint, new object[1] { (object)(State)3 }); } catch (Exception ex) { allowSuccessInstanceIds.Remove(instanceID); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to trigger extraction success: " + ex.GetType().Name + ": " + ex.Message)); } } private void RegisterPendingState(ExtractionPoint extractionPoint, int currentHaul) { if ((Object)(object)extractionPoint == (Object)null) { return; } int instanceID = ((Object)extractionPoint).GetInstanceID(); if (pendingStates.TryGetValue(instanceID, out var value)) { if (value.LastObservedHaul < 0) { value.LastObservedHaul = currentHaul; } if (!value.HasZehsConfirmButton) { value.HasZehsConfirmButton = HasZehsConfirmButtonForExtractionPoint(extractionPoint); } } else { pendingStates[instanceID] = new PendingExtractionState(extractionPoint) { NextCheckTime = Time.time, LastObservedHaul = currentHaul, PromptText = BuildApproachPrompt((confirmRangeMeters != null) ? Mathf.Max(1, confirmRangeMeters.Value) : 4), HasZehsConfirmButton = HasZehsConfirmButtonForExtractionPoint(extractionPoint) }; } } private void ResolveCompatibility() { if (!compatibilityChecked) { compatibilityChecked = true; hasZehsConfirmButton = Chainloader.PluginInfos.ContainsKey("com.github.zehsteam.ExtractionPointConfirmButton"); if (hasZehsConfirmButton) { zehsExtractionPointPatchType = Type.GetType("com.github.zehsteam.ExtractionPointConfirmButton.Patches.ExtractionPointPatch, com.github.zehsteam.ExtractionPointConfirmButton", throwOnError: false); zehsConfirmButtonsProperty = zehsExtractionPointPatchType?.GetProperty("ConfirmButtons", BindingFlags.Static | BindingFlags.Public); zehsConfirmExtractionPointMethod = zehsExtractionPointPatchType?.GetMethod("ConfirmExtractionPoint", BindingFlags.Static | BindingFlags.Public); } } } private bool TryTriggerZehsConfirmButton(ExtractionPoint extractionPoint) { ResolveCompatibility(); if (!hasZehsConfirmButton || (Object)(object)extractionPoint == (Object)null) { return false; } try { if (zehsConfirmExtractionPointMethod != null) { zehsConfirmExtractionPointMethod.Invoke(null, new object[1] { extractionPoint }); return true; } object obj = TryGetZehsConfirmButton(extractionPoint); if (obj != null) { if ((object)zehsConfirmButtonOnClickMethod == null) { zehsConfirmButtonOnClickMethod = obj.GetType().GetMethod("OnClick", BindingFlags.Instance | BindingFlags.Public); } if (zehsConfirmButtonOnClickMethod != null) { MarkSuccessAllowed(extractionPoint); zehsConfirmButtonOnClickMethod.Invoke(obj, null); return true; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to trigger Zehs confirm compatibility path: " + ex.GetType().Name + ": " + ex.Message)); } return false; } private object TryGetZehsConfirmButton(ExtractionPoint extractionPoint) { if (!(zehsConfirmButtonsProperty?.GetValue(null) is IDictionary dictionary)) { return null; } if (!dictionary.Contains(extractionPoint)) { return null; } return dictionary[extractionPoint]; } private bool HasZehsConfirmButtonForExtractionPoint(ExtractionPoint extractionPoint) { ResolveCompatibility(); if (hasZehsConfirmButton) { return TryGetZehsConfirmButton(extractionPoint) != null; } return false; } private void MarkSuccessAllowed(ExtractionPoint extractionPoint) { if (!((Object)(object)extractionPoint == (Object)null)) { allowSuccessInstanceIds.Add(((Object)extractionPoint).GetInstanceID()); } } private static void KeepExtractionPending(ExtractionPoint extractionPoint) { if (!((Object)(object)extractionPoint == (Object)null) && !(ExtractionPointSuccessDelayField == null)) { ExtractionPointSuccessDelayField.SetValue(extractionPoint, 1.5f); } } private string BuildApproachPrompt(int range) { int num = Mathf.Max(1, range); if (CurrentLanguage != I18NRegistry.DisplayLanguage.English) { return $"已达金额,靠近提取点 {num} 米内开始提取"; } return $"Goal reached, approach within {num}m to start extraction"; } private string BuildCountdownPrompt(float requiredSeconds, float bestStillTime) { float num = Mathf.Max(0f, requiredSeconds - bestStillTime); if (CurrentLanguage != I18NRegistry.DisplayLanguage.English) { return $"站立别动 {num:0.0} 秒开始提取..."; } return $"Stand still {num:0.0}s to start extraction..."; } private string BuildConfirmedPrompt() { if (CurrentLanguage != I18NRegistry.DisplayLanguage.English) { return "确认完成,正在开始提取..."; } return "Confirmed, starting extraction..."; } private void EnsurePromptStyles() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (promptStyle == null || promptShadowStyle == null) { promptStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 28, fontStyle = (FontStyle)1, richText = false }; promptStyle.normal.textColor = new Color(1f, 0.92f, 0.35f, 1f); Font val = ResolveChineseFont(); if ((Object)(object)val != (Object)null) { promptStyle.font = val; } promptShadowStyle = new GUIStyle(promptStyle); promptShadowStyle.normal.textColor = new Color(0f, 0f, 0f, 0.95f); } } private Font ResolveChineseFont() { if (chineseFontResolved) { return cachedChineseFont; } chineseFontResolved = true; Font val = TryResolveAutoTranslatorFont(); if ((Object)(object)val != (Object)null) { cachedChineseFont = val; return cachedChineseFont; } cachedChineseFont = CreateChineseFontFromOsFonts(); return cachedChineseFont; } private Font TryResolveAutoTranslatorFont() { try { Type type = Type.GetType("XUnity.AutoTranslator.Plugin.Core.Fonts.FontCache, XUnity.AutoTranslator.Plugin.Core", throwOnError: false) ?? Type.GetType("XUnity.AutoTranslator.Plugin.Core.Fonts.FontCache, XUnity.AutoTranslator", throwOnError: false); if (type == null) { return null; } MethodInfo method = type.GetMethod("GetOrCreate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return null; } object? obj = method.Invoke(null, new object[1] { 28 }); return (Font)((obj is Font) ? obj : null); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to resolve Chinese font from AutoTranslator: " + ex.GetType().Name + ": " + ex.Message)); return null; } } private Font CreateChineseFontFromOsFonts() { try { Font val = Font.CreateDynamicFontFromOSFont(new string[11] { "Microsoft YaHei", "Microsoft YaHei UI", "微软雅黑", "SimHei", "黑体", "SimSun", "宋体", "Noto Sans CJK SC", "Noto Sans SC", "PingFang SC", "WenQuanYi Micro Hei" }, 28); if ((Object)(object)val != (Object)null) { ((Object)val).hideFlags = (HideFlags)61; return val; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to create Chinese font from OS fonts: " + ex.GetType().Name + ": " + ex.Message)); } return null; } private void DrawCenteredPrompt(Vector2 center, string text) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) Vector2 val = promptStyle.CalcSize(new GUIContent(text)); float num = Mathf.Max(16f, (float)promptStyle.fontSize * 0.6f); float num2 = 8f; Rect val3; Rect val2 = (val3 = new Rect(center.x - val.x * 0.5f - num2, center.y - val.y * 0.5f - num, val.x + num2 * 2f, val.y + num * 2f)); ((Rect)(ref val3)).x = ((Rect)(ref val3)).x + 2f; ((Rect)(ref val3)).y = ((Rect)(ref val3)).y + 2f; GUI.Label(val3, text, promptShadowStyle); GUI.Label(val2, text, promptStyle); } private Camera GetActiveCamera() { if (cachedCameraFrame == Time.frameCount) { return cachedCamera; } cachedCameraFrame = Time.frameCount; cachedCamera = Camera.main; if ((Object)(object)cachedCamera == (Object)null) { cachedCamera = Object.FindObjectOfType<Camera>(); } return cachedCamera; } private void DrawInfo(ConfigEntryBase entry) { GUILayout.Label(entry.BoxedValue?.ToString() ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); } private static ConfigDescription ConfigDescriptionWithOrder(string description, int order) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private static ConfigDescription ConfigDescriptionWithOrder(string description, AcceptableValueBase acceptableValues, int order) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private static bool StateSetPrefix(ExtractionPoint __instance, State newState) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 ZichenExtractionDelayConfirmPlugin instance = Instance; if ((Object)(object)instance == (Object)null || !instance.IsModEnabled() || !IsHostAuthority()) { return true; } if ((int)newState != 3) { return true; } if (!IsActiveGameplayExtractionPoint(__instance)) { return true; } int instanceID = ((Object)__instance).GetInstanceID(); if (instance.allowSuccessInstanceIds.Remove(instanceID)) { return true; } if (instance.HasZehsConfirmButtonForExtractionPoint(__instance)) { return true; } instance.RegisterPendingState(__instance, GetCurrentHaulValue()); return false; } private static void ExtractionPointUpdatePrefix(ExtractionPoint __instance) { ZichenExtractionDelayConfirmPlugin instance = Instance; if ((Object)(object)instance == (Object)null || !instance.IsModEnabled() || !IsActiveGameplayExtractionPoint(__instance)) { return; } int currentHaulValue = GetCurrentHaulValue(); if (IsGoalReached(__instance, currentHaulValue)) { instance.RegisterPendingState(__instance, currentHaulValue); if (IsHostAuthority() && !instance.HasZehsConfirmButtonForExtractionPoint(__instance)) { KeepExtractionPending(__instance); } } } private static void ZehsConfirmExtractionPointPrefix(ExtractionPoint extractionPoint) { ZichenExtractionDelayConfirmPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.IsModEnabled() && IsHostAuthority()) { instance.MarkSuccessAllowed(extractionPoint); } } private static bool IsPlayerDisabled(PlayerAvatar player) { if (!((Object)(object)player == (Object)null)) { bool flag = default(bool); int num; if (PlayerAvatarIsDisabledField != null) { object value = PlayerAvatarIsDisabledField.GetValue(player); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } return true; } private void ResetConfigIfVersionChanged() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string text = ReadConfigPluginVersion(configFilePath); if (!string.IsNullOrWhiteSpace(text) && !(text == "1.1.1")) { ResetConfigFileToDefaults(configFilePath); ((BaseUnityPlugin)this).Logger.LogWarning((object)"Config version changed. Old config was 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; } Match match = Regex.Match(File.ReadAllText(configPath), "(?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(); } } 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; } namespace Zichen_ExtractionDelayConfirm.I18N { internal static class LanguageDetector { public static readonly string[] DefaultChineseKeywords = new string[6] { "chinese", "简体", "繁體", "繁体", "汉化", "中文" }; public static readonly string[] DefaultChineseEnvironmentPluginGuids = new string[1] { "gravydevsupreme.xunity.autotranslator" }; private const string ChineseLocaleCodePrefix = "zh"; private static bool _reflectionResolved; private static Type _localizationSettingsType; private static PropertyInfo _selectedLocaleProp; private static PropertyInfo _localeIdentifierProp; private static PropertyInfo _identifierCodeProp; public static bool DetectDefault() { if (DetectFromGameLocale()) { return true; } return DetectDefault(DefaultChineseKeywords, DefaultChineseEnvironmentPluginGuids); } public static bool IsGameLocaleChinese() { return DetectFromGameLocale(); } public static bool TryReadGameLocaleCode(out string code) { code = null; try { code = TryReadGameActiveLocaleCode(); if (!string.IsNullOrEmpty(code)) { return true; } code = TryReadSelectedLocaleCode(); return !string.IsNullOrEmpty(code); } catch { code = null; return false; } } public static bool IsChineseCode(string code) { if (!string.IsNullOrEmpty(code)) { return code.StartsWith("zh", StringComparison.OrdinalIgnoreCase); } return false; } private static bool DetectFromGameLocale() { if (TryReadGameLocaleCode(out var code)) { return IsChineseCode(code); } return false; } private static string TryReadGameActiveLocaleCode() { try { Type type = Type.GetType("LocalizationManager, Assembly-CSharp"); if (type == null) { return null; } object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { return null; } object obj2 = type.GetField("ActiveLocale")?.GetValue(obj); if (obj2 == null) { return null; } return ReadLocaleCode(obj2.GetType().GetField("LocaleReference")?.GetValue(obj2)); } catch { return null; } } private static string TryReadSelectedLocaleCode() { try { ResolveReflectionCache(); if (_localizationSettingsType == null || _selectedLocaleProp == null) { return null; } return ReadLocaleCode(_selectedLocaleProp.GetValue(null, null)); } catch { return null; } } private static string ReadLocaleCode(object locale) { if (locale == null) { return null; } try { object obj = _localeIdentifierProp?.GetValue(locale, null); if (obj == null) { return null; } return _identifierCodeProp?.GetValue(obj, null) as string; } catch { return null; } } private static void ResolveReflectionCache() { if (_reflectionResolved) { return; } _reflectionResolved = true; try { _localizationSettingsType = Type.GetType("UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization"); if (_localizationSettingsType != null) { _selectedLocaleProp = _localizationSettingsType.GetProperty("SelectedLocale"); if (_selectedLocaleProp != null) { _localeIdentifierProp = _selectedLocaleProp.PropertyType?.GetProperty("Identifier"); _identifierCodeProp = (_localeIdentifierProp?.PropertyType)?.GetProperty("Code"); } } } catch { _localizationSettingsType = null; _selectedLocaleProp = null; } } public static bool DetectDefault(IEnumerable<string> chineseKeywords, IEnumerable<string> chineseEnvironmentPluginGuids) { string[] array = ToFilteredArray(chineseKeywords) ?? DefaultChineseKeywords; string[] array2 = ToFilteredArray(chineseEnvironmentPluginGuids) ?? DefaultChineseEnvironmentPluginGuids; 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; foreach (string value2 in array) { if (text.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } for (int j = 0; j < array2.Length; j++) { if (string.Equals(text, array2[j], StringComparison.OrdinalIgnoreCase)) { return true; } } } } catch { } return false; } private static string[] ToFilteredArray(IEnumerable<string> source) { if (source == null) { return null; } List<string> list = new List<string>(); foreach (string item in source) { if (!string.IsNullOrEmpty(item)) { list.Add(item); } } if (list.Count != 0) { return list.ToArray(); } return null; } } internal sealed class I18NRegistry { public sealed class CfgI18N { public string SectionEN; public string SectionCN; public string KeyEN; public string KeyCN; public string DescEN; public string DescCN; public ConfigurationManagerAttributes Attrs; public string ValueEN; public string ValueCN; public ConfigEntry<string> ReadOnlyEntry; } public enum DisplayLanguage { 中文, English } private readonly ConfigFile _config; private readonly ManualLogSource _logger; private readonly List<CfgI18N> _all = new List<CfgI18N>(20); private readonly Dictionary<string, CfgI18N> _bySection = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); private readonly Dictionary<string, Dictionary<string, CfgI18N>> _bySectionAndKey = new Dictionary<string, Dictionary<string, CfgI18N>>(StringComparer.Ordinal); public ConfigEntry<DisplayLanguage> LanguageEntry { get; private set; } public bool UseChinese { get { if (LanguageEntry != null) { return LanguageEntry.Value == DisplayLanguage.中文; } return false; } } public int RegisteredCount => _all.Count; public IReadOnlyList<CfgI18N> All => _all; public event Action LanguageChanged; public I18NRegistry(ConfigFile config, ManualLogSource logger = null) { _config = config ?? throw new ArgumentNullException("config"); _logger = logger; } public ConfigEntry<DisplayLanguage> BindLanguage(bool defaultChinese, string sectionEN = "Mod Info", string sectionCN = "模组信息") { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown DisplayLanguage displayLanguage = ((!defaultChinese) ? DisplayLanguage.English : DisplayLanguage.中文); CfgI18N cfgI18N = NewI18N(sectionEN, sectionCN, "Language", "语言", "Switch display language.", "切换显示语言。", readOnly: false); LanguageEntry = _config.Bind<DisplayLanguage>(sectionEN, "Language", displayLanguage, new ConfigDescription(cfgI18N.DescEN, (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs })); LanguageEntry.SettingChanged += delegate { RefreshAttrs(); InvokeLanguageChanged(); }; return LanguageEntry; } public ConfigEntry<T> Bind<T>(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, T defaultValue, AcceptableValueBase acceptableValues = null, int order = 0, Action<ConfigEntryBase> customDrawer = null, bool? showRangeAsPercent = null, bool? isAdvanced = null, bool? browsable = null) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, descEN, descCN, readOnly: false); if (order != 0) { cfgI18N.Attrs.Order = order; } if (customDrawer != null) { cfgI18N.Attrs.CustomDrawer = customDrawer; } if (showRangeAsPercent.HasValue) { cfgI18N.Attrs.ShowRangeAsPercent = showRangeAsPercent.Value; } if (isAdvanced.HasValue) { cfgI18N.Attrs.IsAdvanced = isAdvanced.Value; } if (browsable.HasValue) { cfgI18N.Attrs.Browsable = browsable.Value; } return _config.Bind<T>(sectEN, keyEN, defaultValue, new ConfigDescription(descEN, acceptableValues, new object[1] { cfgI18N.Attrs })); } public ConfigEntry<string> BindReadOnly(string sectEN, string sectCN, string keyEN, string keyCN, string valueEN, string valueCN, int order = 0, Action<ConfigEntryBase> customDrawer = null) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, "", "", readOnly: true); cfgI18N.ValueEN = valueEN; cfgI18N.ValueCN = valueCN; if (order != 0) { cfgI18N.Attrs.Order = order; } if (customDrawer != null) { cfgI18N.Attrs.CustomDrawer = customDrawer; } cfgI18N.ReadOnlyEntry = _config.Bind<string>(sectEN, keyEN, valueEN, new ConfigDescription("", (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs })); string text = (UseChinese ? valueCN : valueEN); if (cfgI18N.ReadOnlyEntry.Value != text) { cfgI18N.ReadOnlyEntry.Value = text; } cfgI18N.ReadOnlyEntry.SettingChanged += MakeReadOnlyValueGuard(cfgI18N); return cfgI18N.ReadOnlyEntry; } private EventHandler MakeReadOnlyValueGuard(CfgI18N i18n) { return delegate { if (i18n.ReadOnlyEntry != null) { string text = (UseChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text) { i18n.ReadOnlyEntry.Value = text; InvokeLanguageChanged(); } } }; } private void InvokeLanguageChanged() { Action action = this.LanguageChanged; if (action == null) { return; } Delegate[] invocationList = action.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action2 = (Action)invocationList[i]; try { action2(); } catch (Exception ex) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)("LanguageChanged 订阅方异常:" + ex.Message)); } } } } private CfgI18N NewI18N(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, bool readOnly) { CfgI18N cfgI18N = new CfgI18N { SectionEN = sectEN, SectionCN = sectCN, KeyEN = keyEN, KeyCN = keyCN, DescEN = descEN, DescCN = descCN, Attrs = (readOnly ? new ConfigurationManagerAttributes { ReadOnly = true, HideDefaultButton = true } : new ConfigurationManagerAttributes()) }; ApplyI18N(cfgI18N); _all.Add(cfgI18N); _bySection[sectEN] = cfgI18N; if (!_bySectionAndKey.TryGetValue(sectEN, out var value)) { value = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); _bySectionAndKey[sectEN] = value; } value[keyEN] = cfgI18N; return cfgI18N; } private void ApplyI18N(CfgI18N i18n) { bool useChinese = UseChinese; i18n.Attrs.Category = (useChinese ? i18n.SectionCN : i18n.SectionEN); i18n.Attrs.DispName = (useChinese ? i18n.KeyCN : i18n.KeyEN); i18n.Attrs.Description = (useChinese ? i18n.DescCN : i18n.DescEN); if (i18n.ReadOnlyEntry != null) { string text = (useChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text) { i18n.ReadOnlyEntry.Value = text; } } } public void RefreshAttrs() { for (int i = 0; i < _all.Count; i++) { ApplyI18N(_all[i]); } } public void RefreshNow() { RefreshAttrs(); InvokeLanguageChanged(); } public bool TryGetBySection(string sectEN, out CfgI18N i18n) { return _bySection.TryGetValue(sectEN, out i18n); } public bool TryGetByKey(string keyEN, out CfgI18N i18n) { i18n = null; foreach (KeyValuePair<string, Dictionary<string, CfgI18N>> item in _bySectionAndKey) { if (item.Value.TryGetValue(keyEN, out i18n)) { return true; } } return false; } } 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 sealed class I18NSwitcher { private sealed class LiveLabel { public WeakReference<TextMeshProUGUI> Tmp; public I18NRegistry.CfgI18N I18N; public bool IsSection; } private sealed class LiveValue { public WeakReference<Object> InputField; public I18NRegistry.CfgI18N I18N; } private sealed class LiveToggleButtons { public WeakReference<TextMeshProUGUI> LeftTmp; public WeakReference<TextMeshProUGUI> RightTmp; public bool IsLanguage; } private readonly I18NRegistry _registry; private readonly Harmony _harmony; private readonly ManualLogSource _logger; private readonly string _pluginName; private readonly string _pluginNameFixedForm; private readonly List<LiveLabel> _liveLabels = new List<LiveLabel>(64); private readonly List<LiveValue> _liveValues = new List<LiveValue>(8); private readonly List<LiveToggleButtons> _liveToggleButtons = new List<LiveToggleButtons>(16); private static readonly Dictionary<Type, FieldInfo> _labelTmpFieldCache = new Dictionary<Type, FieldInfo>(8); private static readonly Dictionary<Type, FieldInfo> _inputStringSystemFieldCache = new Dictionary<Type, FieldInfo>(2); private static readonly Dictionary<Type, MethodInfo> _inputStringSystemSetValueCache = new Dictionary<Type, MethodInfo>(2); private static readonly Dictionary<Type, FieldInfo> _toggleLeftTmpCache = new Dictionary<Type, FieldInfo>(4); private static readonly Dictionary<Type, FieldInfo> _toggleRightTmpCache = new Dictionary<Type, FieldInfo>(4); private static readonly object _reflectionCacheLock = new object(); private static readonly object _globalInstancesLock = new object(); private static readonly List<WeakReference<I18NSwitcher>> _globalInstances = new List<WeakReference<I18NSwitcher>>(2); public int LiveUiCount => _liveLabels.Count + _liveValues.Count + _liveToggleButtons.Count; public I18NSwitcher(I18NRegistry registry, Harmony harmony, string pluginName, ManualLogSource logger = null) { _registry = registry ?? throw new ArgumentNullException("registry"); _harmony = harmony ?? throw new ArgumentNullException("harmony"); _pluginName = pluginName ?? throw new ArgumentNullException("pluginName"); _pluginNameFixedForm = FixNamingForOwnership(pluginName); _logger = logger; _registry.LanguageChanged += RefreshLiveUi; } private static string FixNamingForOwnership(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(); } public void InstallRepoConfigHook() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MenuLib.MenuAPI"); if (type == null) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)"MenuLib 未安装,跳过 REPOConfig 双语劫持。"); } return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(I18NSwitcher), "PostfixDispatch", (Type[])null, (Type[])null)) { priority = 0 }; 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 { _harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch { } } } if (num > 0) { ManualLogSource logger2 = _logger; if (logger2 != null) { logger2.LogInfo((object)$"REPOConfig 双语显示已挂载({num} 个 UI 创建方法)。"); } } else { ManualLogSource logger3 = _logger; if (logger3 != null) { logger3.LogInfo((object)"REPOConfig 没找到可劫持的 UI 方法。"); } } RegisterGlobalInstance(this); } catch (Exception ex) { ManualLogSource logger4 = _logger; if (logger4 != null) { logger4.LogInfo((object)("REPOConfig 劫持失败:" + ex.Message)); } } } private static void RegisterGlobalInstance(I18NSwitcher inst) { lock (_globalInstancesLock) { _globalInstances.Add(new WeakReference<I18NSwitcher>(inst)); } } public static void PostfixDispatch(object[] __args, object __result) { try { if (__args == null || __args.Length == 0 || __result == null || !(__args[0] is string text) || string.IsNullOrEmpty(text)) { return; } List<I18NSwitcher> list = null; lock (_globalInstancesLock) { for (int num = _globalInstances.Count - 1; num >= 0; num--) { if (!_globalInstances[num].TryGetTarget(out var target)) { _globalInstances.RemoveAt(num); } else { (list ?? (list = new List<I18NSwitcher>(_globalInstances.Count))).Add(target); } } } if (list != null) { for (int i = 0; i < list.Count; i++) { list[i].TryRegisterLiveLabel(text, __result); } } } catch { } } private static string GetOwningPluginName(object uiElement) { try { object obj = ((uiElement is Component) ? uiElement : null); GameObject val = (GameObject)(((obj != null) ? ((Component)obj).gameObject : null) ?? ((uiElement is GameObject) ? uiElement : null)); if ((Object)(object)val == (Object)null) { return null; } Transform val2 = val.transform; while ((Object)(object)val2 != (Object)null) { string name = ((Object)((Component)val2).gameObject).name; if (name != null && name.StartsWith("Menu Page ", StringComparison.Ordinal)) { return name.Substring("Menu Page ".Length); } val2 = val2.parent; } } catch { } return null; } private void TryRegisterLiveLabel(string label, object uiElement) { string owningPluginName = GetOwningPluginName(uiElement); if ((owningPluginName != null && !string.Equals(owningPluginName, _pluginNameFixedForm, StringComparison.Ordinal)) || (!_registry.TryGetBySection(label, out var i18n) && !_registry.TryGetByKey(label, out i18n))) { return; } bool flag = string.Equals(label, i18n.SectionEN, StringComparison.Ordinal); TextMeshProUGUI labelTmp = GetLabelTmp(uiElement); if ((Object)(object)labelTmp == (Object)null) { return; } bool useChinese = _registry.UseChinese; string text = ((!flag) ? (useChinese ? i18n.KeyCN : i18n.KeyEN) : (useChinese ? 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 = flag }); if (!flag && i18n.ReadOnlyEntry != null) { Object val = (Object)((uiElement is Object) ? uiElement : null); if (val != null && HasInputStringSystemField(((object)val).GetType())) { _liveValues.Add(new LiveValue { InputField = new WeakReference<Object>(val), I18N = i18n }); string text2 = (useChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text2) { i18n.ReadOnlyEntry.Value = text2; } TrySetInputFieldValue(val, text2); } } if (flag) { return; } Type type = uiElement.GetType(); FieldInfo value; FieldInfo value2; lock (_reflectionCacheLock) { if (!_toggleLeftTmpCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "leftButtonTMP"); _toggleLeftTmpCache[type] = value; } if (!_toggleRightTmpCache.TryGetValue(type, out value2)) { value2 = AccessTools.Field(type, "rightButtonTMP"); _toggleRightTmpCache[type] = value2; } } if (value != null && value2 != null) { object? value3 = value.GetValue(uiElement); TextMeshProUGUI val2 = (TextMeshProUGUI)((value3 is TextMeshProUGUI) ? value3 : null); object? value4 = value2.GetValue(uiElement); TextMeshProUGUI val3 = (TextMeshProUGUI)((value4 is TextMeshProUGUI) ? value4 : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null) { _liveToggleButtons.Add(new LiveToggleButtons { LeftTmp = new WeakReference<TextMeshProUGUI>(val2), RightTmp = new WeakReference<TextMeshProUGUI>(val3), IsLanguage = false }); ApplyToggleButtonText(val2, val3, useChinese, isLanguage: false); } } } private static TextMeshProUGUI GetLabelTmp(object uiElement) { try { Type type = uiElement.GetType(); FieldInfo value; lock (_reflectionCacheLock) { if (!_labelTmpFieldCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "labelTMP"); _labelTmpFieldCache[type] = value; } } object? obj = value?.GetValue(uiElement); return (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); } catch { return null; } } private static bool HasInputStringSystemField(Type t) { lock (_reflectionCacheLock) { if (!_inputStringSystemFieldCache.TryGetValue(t, out var value)) { value = AccessTools.Field(t, "inputStringSystem"); _inputStringSystemFieldCache[t] = value; } return value != null; } } private static bool TrySetInputFieldValue(Object ifObj, string newVal) { try { Type type = ((object)ifObj).GetType(); FieldInfo value; lock (_reflectionCacheLock) { if (!_inputStringSystemFieldCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "inputStringSystem"); _inputStringSystemFieldCache[type] = value; } } if (value == null) { return false; } object value2 = value.GetValue(ifObj); if (value2 == null) { return false; } MethodInfo value3; lock (_reflectionCacheLock) { Type type2 = value2.GetType(); if (!_inputStringSystemSetValueCache.TryGetValue(type2, out value3)) { value3 = AccessTools.Method(type2, "SetValue", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); _inputStringSystemSetValueCache[type2] = value3; } } if (value3 == null) { return false; } value3.Invoke(value2, new object[2] { newVal ?? string.Empty, false }); return true; } catch { return false; } } private static void ApplyToggleButtonText(TextMeshProUGUI leftTmp, TextMeshProUGUI rightTmp, bool cn, bool isLanguage) { try { if (isLanguage) { ((TMP_Text)leftTmp).text = (cn ? "中文" : "Chinese"); ((TMP_Text)rightTmp).text = (cn ? "英文" : "English"); } else { ((TMP_Text)leftTmp).text = (cn ? "开" : "ON"); ((TMP_Text)rightTmp).text = (cn ? "关" : "OFF"); } } catch { } } private void RefreshLiveUi() { bool useChinese = _registry.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) ? (useChinese ? liveLabel.I18N.KeyCN : liveLabel.I18N.KeyEN) : (useChinese ? 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 = (useChinese ? liveValue.I18N.ValueCN : liveValue.I18N.ValueEN); if (!TrySetInputFieldValue(target2, newVal)) { _liveValues.RemoveAt(num2); } } } for (int num3 = _liveToggleButtons.Count - 1; num3 >= 0; num3--) { LiveToggleButtons liveToggleButtons = _liveToggleButtons[num3]; if (!liveToggleButtons.LeftTmp.TryGetTarget(out var target3) || (Object)(object)target3 == (Object)null || !liveToggleButtons.RightTmp.TryGetTarget(out var target4) || (Object)(object)target4 == (Object)null) { _liveToggleButtons.RemoveAt(num3); } else { ApplyToggleButtonText(target3, target4, useChinese, liveToggleButtons.IsLanguage); } } } } }