Decompiled source of Better Stats v0.9.2
BetterStatsMod.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BetterStatsMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.9.2.0")] [assembly: AssemblyInformationalVersion("0.9.2+6f338016fa16cdb7ae5a4e6a963d45471eefad4a")] [assembly: AssemblyProduct("BetterStatsMod")] [assembly: AssemblyTitle("BetterStatsMod")] [assembly: AssemblyVersion("0.9.2.0")] namespace BetterStatsModGenerated { internal static class ModInfo { public const string GUID = "com.zelevlin.betterstats"; public const string NAME = "Better_Stats"; public const string VERSION = "0.9.2"; } } namespace BetterStatsMod { [BepInPlugin("com.zelevlin.betterstats", "Better_Stats", "0.9.2")] public sealed class Plugin : BasePlugin { internal static ManualLogSource LogSrc; internal static FileLogger FileLog; internal static Harmony Harmony; internal static bool EnableDebugLogs; internal static bool EnableVerboseLogs; internal static readonly bool IsDebugBuild; public override void Load() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown LogSrc = ((BasePlugin)this).Log; if (IsDebugBuild) { InitFileLogger(); LogInfo("Loaded Better_Stats v0.9.2"); } try { Harmony = new Harmony("com.zelevlin.betterstats"); PatchHeadquarters(); if (IsDebugBuild) { OverlayUi.Ensure(); OverlayState.Refresh(); ItemsExperiment.Initialize(); LogInfo("Init finished"); } } catch (Exception value) { LogError($"Load() exception: {value}"); } } private void InitFileLogger() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { string text = Path.GetDirectoryName(typeof(Plugin).Assembly.Location); if (string.IsNullOrEmpty(text)) { text = Paths.PluginPath; } string text2 = Path.Combine(text, "BetterStatsMod.log"); FileLog = new FileLogger(text2); FileLog.Info("FileLogger initialized at " + text2); } catch (Exception ex) { ManualLogSource logSrc = LogSrc; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(24, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("FileLogger init failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } logSrc.LogWarning(val); } } internal static void LogInfo(string message) { if (IsDebugBuild && (EnableVerboseLogs || !IsNoisyLog(message))) { ManualLogSource logSrc = LogSrc; if (logSrc != null) { logSrc.LogInfo((object)message); } FileLog?.Info(message); } } internal static void LogWarning(string message) { if (IsDebugBuild) { ManualLogSource logSrc = LogSrc; if (logSrc != null) { logSrc.LogWarning((object)message); } FileLog?.Warn(message); } } internal static void LogError(string message) { if (IsDebugBuild) { ManualLogSource logSrc = LogSrc; if (logSrc != null) { logSrc.LogError((object)message); } FileLog?.Error(message); } } internal static void LogAutoSelect(string message) { if (IsDebugBuild && EnableDebugLogs && !string.IsNullOrEmpty(message)) { string text = "AutoSelect: " + message; ManualLogSource logSrc = LogSrc; if (logSrc != null) { logSrc.LogInfo((object)text); } FileLog?.Info(text); } } private static bool IsNoisyLog(string message) { if (string.IsNullOrEmpty(message)) { return false; } if (!message.StartsWith("Auto select ", StringComparison.Ordinal) && !message.StartsWith("Auto-select ", StringComparison.Ordinal) && !message.StartsWith("TryComputeDamageParamWithTempEquip", StringComparison.Ordinal) && !message.StartsWith("TrySetEquipItem", StringComparison.Ordinal) && !message.StartsWith("DamageCalculatorResolver", StringComparison.Ordinal) && !message.StartsWith("DamageParamResolver", StringComparison.Ordinal) && !message.StartsWith("StatusDisplay set", StringComparison.Ordinal) && !message.StartsWith("NormalUnitModel update", StringComparison.Ordinal) && !message.StartsWith("HeroModel update", StringComparison.Ordinal) && !message.StartsWith("Input=OnSlotDecide", StringComparison.Ordinal) && !message.StartsWith("Input=Decide", StringComparison.Ordinal) && !message.StartsWith("Manual reselect", StringComparison.Ordinal)) { return message.StartsWith("HQ unit dump", StringComparison.Ordinal); } return true; } private static void PatchHeadquarters() { PatchMethod("P2.Bases.HeadQuarters.NormalUnitUI", "update", "NormalUnitUIUpdate_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.NormalUnitUI", "update", "NormalUnitUIUpdate_Postfix"); PatchMethod("P2.Bases.HeadQuarters.NormalUnitEquipSlot", "update", "NormalUnitEquipSlotUpdate_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.NormalUnitEquipSlot", "update", "NormalUnitEquipSlotUpdate_Postfix"); PatchMethod("P2.Bases.HeadQuarters.UnitEquipSlot", "update", "UnitEquipSlotUpdate_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.UnitEquipSlot", "update", "UnitEquipSlotUpdate_Postfix"); PatchMethod("P2.Bases.HeadQuarters.NormalUnitModel", "onChangeEquip", "NormalUnitModelOnChangeEquip_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.NormalUnitModel", "onChangeEquip", "NormalUnitModelOnChangeEquip_Postfix"); PatchMethod("P2.Bases.HeadQuarters.HeroModel", "onChangeEquip", "HeroModelOnChangeEquip_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.HeroModel", "onChangeEquip", "HeroModelOnChangeEquip_Postfix"); PatchMethod("P2.Bases.HeadQuarters.NormalUnitModel", "update", "NormalUnitModelUpdate_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.NormalUnitModel", "update", "NormalUnitModelUpdate_Postfix"); PatchMethod("P2.Bases.HeadQuarters.HeroModel", "update", "HeroModelUpdate_Postfix"); PatchMethod("P2S.Bases.HeadQuarters.HeroModel", "update", "HeroModelUpdate_Postfix"); PatchMethod("P2.Bases.HeadQuarters.HeadQuartersMainObserver", "update", "Update_Postfix", typeof(HeadQuartersMainObserverHooks)); PatchMethod("P2S.Bases.HeadQuarters.HeadQuartersMainObserver", "update", "Update_Postfix", typeof(HeadQuartersMainObserverHooks)); PatchMethodWithPrefix("P2.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Prefix", typeof(SquadOrgViewHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Postfix", typeof(SquadOrgViewHooks)); PatchMethodWithPrefix("P2S.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Prefix", typeof(SquadOrgViewHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Postfix", typeof(SquadOrgViewHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadSelectModel", "getCurAddingParam", "SquadSelectModelGetCurAddingParam_Postfix", typeof(SquadSelectHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadSelectModel", "getCurAddingParam", "SquadSelectModelGetCurAddingParam_Postfix", typeof(SquadSelectHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadSelectObserver", "getCurAddingParam", "SquadSelectObserverGetCurAddingParam_Postfix", typeof(SquadSelectHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadSelectObserver", "getCurAddingParam", "SquadSelectObserverGetCurAddingParam_Postfix", typeof(SquadSelectHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadStatusView", "update", "SquadStatusViewUpdate_Postfix", typeof(SquadStatusViewHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadStatusView", "update", "SquadStatusViewUpdate_Postfix", typeof(SquadStatusViewHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadSelectView", "update", "SquadSelectViewUpdate_Postfix", typeof(SquadStatusViewHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadSelectView", "update", "SquadSelectViewUpdate_Postfix", typeof(SquadStatusViewHooks)); PatchMethod("P2.Bases.HeadQuarters.SquadOrgObserver", "getUnitAddingParam", "GetUnitAddingParam_Postfix", typeof(SquadOrgObserverHooks)); PatchMethod("P2S.Bases.HeadQuarters.SquadOrgObserver", "getUnitAddingParam", "GetUnitAddingParam_Postfix", typeof(SquadOrgObserverHooks)); Type type = FindTypeByName("P2.Game.Unit.UnitAddingParam") ?? FindTypeByName("P2S.Game.Unit.UnitAddingParam"); PatchMethod("P2.Bases.Organization.Managed.SquadStatusDisplay", "notifySquadChange", "NotifySquadChange_Postfix", typeof(SquadStatusDisplayHooks), (type == null) ? null : new Type[1] { type }); PatchMethod("P2S.Bases.Organization.Managed.SquadStatusDisplay", "notifySquadChange", "NotifySquadChange_Postfix", typeof(SquadStatusDisplayHooks), (type == null) ? null : new Type[1] { type }); PatchMethod("UnityEngine.UI.CanvasUpdateRegistry", "PerformUpdate", "CanvasUpdateRegistryPerformUpdate_Postfix", typeof(OverlayPulse)); PatchMethodWithPrefix("P2.Bases.Organization.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks)); PatchMethod("P2.Bases.Organization.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks)); PatchMethodWithPrefix("P2S.Bases.Organization.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks)); PatchMethod("P2S.Bases.Organization.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks)); PatchMethodWithPrefix("P2.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks)); PatchMethod("P2.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks)); PatchMethodWithPrefix("P2S.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks)); PatchMethod("P2S.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks)); PatchMethodWithPrefix("P2.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks)); PatchMethod("P2.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks)); PatchMethodWithPrefix("P2S.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks)); PatchMethod("P2S.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks)); PatchMethodWithPrefix("P2.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks)); PatchMethod("P2.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks)); PatchMethodWithPrefix("P2S.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks)); PatchMethod("P2S.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks)); } private static void PatchMethod(string typeName, string methodName, string postfixName, Type postfixType = null, Type[] parameterTypes = null) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown Type type = FindTypeByName(typeName); if (type == null) { LogInfo("Type not found: " + typeName); return; } MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null)); if (methodInfo == null) { LogWarning(typeName + "." + methodName + " not found"); return; } Type type2 = postfixType ?? typeof(HeadquartersHooks); MethodInfo method = type2.GetMethod(postfixName, BindingFlags.Static | BindingFlags.Public); if (method == null) { LogWarning("Postfix not found: " + type2.FullName + "." + postfixName); return; } HarmonyMethod val = new HarmonyMethod(method); Harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogInfo($"Patched: {typeName}.{methodName} -> {postfixName}"); } private static void PatchMethodWithPrefix(string typeName, string methodName, string prefixName, Type prefixType = null, Type[] parameterTypes = null) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown Type type = FindTypeByName(typeName); if (type == null) { LogInfo("Type not found: " + typeName); return; } MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null)); if (methodInfo == null) { LogWarning(typeName + "." + methodName + " not found"); return; } Type type2 = prefixType ?? typeof(HeadquartersHooks); MethodInfo method = type2.GetMethod(prefixName, BindingFlags.Static | BindingFlags.Public); if (method == null) { LogWarning("Prefix not found: " + type2.FullName + "." + prefixName); return; } HarmonyMethod val = new HarmonyMethod(method); Harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogInfo($"Patched prefix: {typeName}.{methodName} -> {prefixName}"); } internal static Type FindTypeByName(string fullName) { string[] array = new string[8] { "UnityEditor", "BepInEx", "0Harmony", "Harmony", "System", "mscorlib", "netstandard", "Mono." }; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string name = assembly.GetName().Name; bool flag = false; string[] array2 = array; foreach (string value in array2) { if (name.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (flag) { continue; } try { Type type = assembly.GetType(fullName, throwOnError: false); if (type != null) { return type; } } catch { } } return null; } } public static class HeadquartersHooks { private static int _lastUiState = int.MinValue; private static int _lastEquipIndex = int.MinValue; private static int _lastSelectSlot = int.MinValue; public static void NormalUnitUIUpdate_Postfix(object __instance, uint dt) { if (Plugin.EnableDebugLogs) { int? num = ReflectionUtil.TryGetInt(__instance, "state_"); if (num.HasValue && num.Value != _lastUiState) { _lastUiState = num.Value; Plugin.LogInfo($"NormalUnitUI state={_lastUiState}"); } } } public static void NormalUnitEquipSlotUpdate_Postfix(object __instance, uint dt) { TrackEquipSelection("NormalUnitEquipSlot", __instance); } public static void UnitEquipSlotUpdate_Postfix(object __instance, uint dt) { TrackEquipSelection("UnitEquipSlot", __instance); } private static void TrackEquipSelection(string label, object instance) { if (!Plugin.EnableDebugLogs) { return; } int? num = ReflectionUtil.TryGetInt(instance, "curSelectSlot_"); int? num2 = null; bool flag = false; if (num.HasValue) { num2 = ReflectionUtil.TryGetArrayElementInt(ReflectionUtil.TryGetObject(instance, "equipIndex_"), num.Value); } if (num2.HasValue && num2.Value != _lastEquipIndex) { _lastEquipIndex = num2.Value; flag = true; } if (num.HasValue && num.Value != _lastSelectSlot) { _lastSelectSlot = num.Value; flag = true; } if (flag) { int? num3 = null; int? num4 = null; object obj = ReflectionUtil.TryGetObject(instance, "model_"); if (obj != null && _lastSelectSlot >= 0) { num3 = ReflectionUtil.TryInvokeInt(obj, "getEquipItemId", (uint)_lastSelectSlot); num4 = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(obj, "itemMenu_"), "itemId_"); } Plugin.LogInfo($"{label} slot={_lastSelectSlot} equipIndex={_lastEquipIndex} selectedItemId={num4} equippedItemId={num3}"); OverlayState.UpdateSelection(_lastSelectSlot, _lastEquipIndex, num4, num3, null); } } public static void NormalUnitModelOnChangeEquip_Postfix(object __instance, uint equipIndex, string name) { if (Plugin.EnableDebugLogs) { int? num = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(__instance, "itemMenu_"), "itemId_"); int? num2 = ReflectionUtil.TryGetInt(__instance, "slotID_"); int? num3 = ReflectionUtil.TryGetInt(__instance, "unitID_"); int? equippedItemId = null; if (num2.HasValue && num2.Value >= 0) { equippedItemId = ReflectionUtil.TryInvokeInt(__instance, "getEquipItemId", (uint)num2.Value); } Plugin.LogInfo($"NormalUnitModel onChangeEquip equipIndex={equipIndex} name={name} slotId={num2} unitId={num3} selectedItemId={num}"); OverlayState.UpdateOnChangeEquip(equipIndex, name, num2, num3, num, equippedItemId); } } public static void HeroModelOnChangeEquip_Postfix(object __instance, uint equipIndex, string name) { if (Plugin.EnableDebugLogs) { int? num = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(__instance, "itemMenu_"), "itemId_"); int? num2 = ReflectionUtil.TryGetInt(__instance, "slotID_"); int? num3 = ReflectionUtil.TryGetInt(__instance, "unitID_"); int? equippedItemId = null; if (num2.HasValue && num2.Value >= 0) { equippedItemId = ReflectionUtil.TryInvokeInt(__instance, "getEquipItemId", (uint)num2.Value); } Plugin.LogInfo($"HeroModel onChangeEquip equipIndex={equipIndex} name={name} slotId={num2} unitId={num3} selectedItemId={num}"); OverlayState.UpdateOnChangeEquip(equipIndex, name, num2, num3, num, equippedItemId); } } public static void NormalUnitModelUpdate_Postfix(object __instance, uint dt) { OverlayState.UpdateFromModel(__instance, "NormalUnitModel"); } public static void HeroModelUpdate_Postfix(object __instance, uint dt) { OverlayState.UpdateFromModel(__instance, "HeroModel"); } } public static class OverlayPulse { public static void CanvasUpdateRegistryPerformUpdate_Postfix() { OverlayUi.Ensure(); OverlayState.Refresh(); OverlayState.UpdatePendingDoubleConfirm(); } } public static class HeadQuartersMainObserverHooks { public static void Update_Postfix(object __instance, uint dt) { object obj = ReflectionUtil.TryGetObject(__instance, "squadOrgObserver_"); OverlayState.SetSquadOrgObserver(obj); if (obj != null) { OverlayState.NotifyHeadquartersActive(); } } } public static class SquadOrgViewHooks { public static bool RenderCursor_Prefix(uint selectIndex) { if (OverlayState.IsCursorMovementSuppressed()) { return false; } return true; } public static void RenderCursor_Postfix(uint selectIndex) { if (!OverlayState.IsCursorMovementSuppressed()) { OverlayState.SetActiveUnitCursorIndex(selectIndex); } } } public static class ItemMenuHooks { public static void Decide_Prefix(uint itemId) { Plugin.LogInfo($"Input=Decide prefix itemId={itemId} {OverlayState.BuildSelectionSnapshot()}"); } public static void Decide_Postfix(uint itemId) { if (OverlayState.TryConsumeAutoSelectCommit(itemId)) { Plugin.LogInfo($"Input=Auto itemId={itemId}"); } else { Plugin.LogInfo($"Input=Manual itemId={itemId}"); OverlayState.NotifyManualEquipCommit(itemId); } } } public static class ItemSelectWindowHooks { public static void OnSlotDecide_Prefix(object __instance) { Plugin.LogInfo("Input=OnSlotDecide prefix " + OverlayState.BuildSelectionSnapshot()); } public static void OnSlotDecide_Postfix(object __instance) { Plugin.LogInfo("Input=OnSlotDecide postfix " + OverlayState.BuildSelectionSnapshot()); } } public static class SquadSelectHooks { public static void SquadSelectModelGetCurAddingParam_Postfix(ref object __0, bool __result) { OverlayState.UpdateHoverAddingParam(__result ? __0 : null, "SquadSelectModel"); } public static void SquadSelectObserverGetCurAddingParam_Postfix(ref object __0, bool __result) { OverlayState.UpdateHoverAddingParam(__result ? __0 : null, "SquadSelectObserver"); } } public static class SquadStatusViewHooks { public static void SquadStatusViewUpdate_Postfix(object __instance, uint dt) { OverlayState.UpdateStatusDisplayFromView(__instance); } public static void SquadSelectViewUpdate_Postfix(object __instance, uint dt) { OverlayState.UpdateStatusDisplayFromView(__instance); object obj = ReflectionUtil.TryGetObject(__instance, "model_"); int? num = ReflectionUtil.TryGetInt(obj, "unitID_"); if (num.HasValue && num.Value >= 0 && ReflectionUtil.TryInvokeBoolWithOutObject(obj, "getCurAddingParam", out var outValue)) { OverlayState.UpdateHoverAddingParam(outValue, "SquadSelectView"); } } } public static class SquadStatusDisplayHooks { public static void NotifySquadChange_Postfix(object __instance, object unitAddingParam) { OverlayState.UpdateStatusDisplayFromDisplay(__instance); OverlayState.UpdateHoverAddingParam(unitAddingParam, "SquadStatusDisplay"); } } public static class SquadOrgObserverHooks { public static void GetUnitAddingParam_Postfix(uint slotID, object unitAddingParam) { if (OverlayState.ShouldUseSquadOrgAddingParam(slotID)) { OverlayState.UpdateHoverAddingParam(unitAddingParam, "SquadOrgObserver"); } } } public static class OverlayState { private static bool EnableAutoSelectOverlay = true; private static bool EnableAutoSelectFromHover = true; private static bool EnableAutoSelectDoubleConfirm = true; private const int HeroSquadIndex = 1; private const float AutoSelectDoubleConfirmDelaySeconds = 0.1f; private const float HeadquartersActiveTimeoutSeconds = 1.5f; private static float _lastHeadquartersHeartbeatTime = -1f; private static int _slot = -1; private static int _equipIndex = -1; private static int? _selectedItemId; private static int? _hoverItemId; private static int _hoverIndex = -1; private static int? _equippedItemId; private static int? _confirmedSelectedItemId; private static string _lastEquipName = string.Empty; private static uint? _lastEquipIndex; private static int? _unitId; private static string _source = "-"; private static string _activeModelSource; private static string _activeUnitName; private static int? _activeUnitId; private static int? _activeSlotId; private static int? _activeSquadIndex; private static int? _activeCursorIndex; private static int? _lastActiveSquadForRefresh; private static bool _activeIsHero; private static bool _activeIsNormalUnit; private static bool _activeOpenItemMenu; private static bool _activeHasUnitId; private static object _itemMenu; private static object _itemOperator; private static object _squad; private static object _equippedDamageParam; private static object _autoSelectedDamageParam; private static object _statusDisplay; private static object _lastEquippedDamageParam; private static object _lastAutoSelectedDamageParam; private static int? _lastEquippedDamageItemId; private static object _lastDamageSquad; private static int? _lastDamageUnitId; private static object _lastStatusDisplay; private static object _hoverUnitAddingParam; private static object _lastHoverUnitAddingParam; private static string _lastAddingParamSource; private static float _lastAddingParamTime = -1f; private static int? _lastAutoSelectedItemIdForParam; private static int? _lastEquippedItemIdForParam; private static object _lastLoggedStatusDisplay; private static object _lastLoggedAutoSelectedParam; private static object _squadOrgObserver; private static object _squadModel; private static bool _didDumpSquadOrgUnits; private static float _lastDumpAttemptTime = -1f; private static int? _heroUnitIndexInSquad; private static int? _lastHeroSlotId; private static int? _lastHeroSelectedItemId; private static int? _lastHeroEquippedItemId; private static int? _lastHeroHoverItemId; private static int? _lastNormalSlotId; private static int? _lastNormalSelectedItemId; private static int? _lastNormalEquippedItemId; private static int? _lastNormalHoverItemId; private static object _lastStatsItemOperator; private static int? _autoSelectedStatsItemId; private static string _autoSelectedStatsText = string.Empty; private static int? _equippedStatsItemId; private static string _equippedStatsText = string.Empty; private static object _autoSelectedStatsDamageParam; private static object _equippedStatsDamageParam; private static int? _lastActiveUnitForEquippedStats; private static int? _differenceStatsAutoItemId; private static int? _differenceStatsEquippedItemId; private static string _differenceStatsText = string.Empty; private static string[] _differenceStatsReleaseLines = Array.Empty<string>(); private static object _lastDifferenceAutoParam; private static object _lastDifferenceEquippedParam; private static bool _equippedStatsNeedsUpdate = true; private static string _equippedStatsUpdateReason = "-"; private static int? _lastManualEquipItemId; private static int? _lastAutoSelectedHoverItemId; private static int _lastAutoSelectedHoverIndex = -1; private static object _itemSelectWindow; private static bool _autoSelectCommitPending; private static int? _autoSelectCommitItemId; private static float _pendingSecondConfirmAt = -1f; private static int _pendingSecondConfirmHoverIndex = -1; private static int? _pendingSecondConfirmItemId; private static bool _suppressSquadOrgCursorMovement; private static int? _pendingReselectItemId; public static bool IsCursorMovementSuppressed() { return _suppressSquadOrgCursorMovement; } private static void SetCursorMovementSuppressed(bool suppress) { _suppressSquadOrgCursorMovement = suppress; } public static void UpdateSelection(int slot, int equipIndex, int? selectedItemId, int? equippedItemId, int? unitId) { _slot = slot; _equipIndex = equipIndex; if (selectedItemId.HasValue) { _selectedItemId = selectedItemId; } if (equippedItemId.HasValue) { _equippedItemId = equippedItemId; } if (unitId.HasValue) { _unitId = unitId; } int? confirmedSelectedItemId = ResolveConfirmedSelected(_selectedItemId, _equippedItemId); if (confirmedSelectedItemId.HasValue) { _confirmedSelectedItemId = confirmedSelectedItemId; } Refresh(); } public static void UpdateOnChangeEquip(uint equipIndex, string name, int? slotId, int? unitId, int? selectedItemId, int? equippedItemId) { _lastEquipIndex = equipIndex; _lastEquipName = name ?? string.Empty; if (slotId.HasValue) { _slot = slotId.Value; } if (unitId.HasValue) { _unitId = unitId; } if (selectedItemId.HasValue) { _selectedItemId = selectedItemId; } if (equippedItemId.HasValue) { _equippedItemId = equippedItemId; } int? confirmedSelectedItemId = ResolveConfirmedSelected(_selectedItemId, _equippedItemId); if (confirmedSelectedItemId.HasValue) { _confirmedSelectedItemId = confirmedSelectedItemId; } Refresh(); } public static void UpdateFromModel(object model, string source) { if (model == null) { return; } bool flag = false; int? num = ReflectionUtil.TryGetInt(model, "slotID_"); int? num2 = ReflectionUtil.TryGetInt(model, "unitID_"); string text = ReflectionUtil.TryGetString(model, "name_"); object obj = ReflectionUtil.TryInvokeObject(model, "getSquad") ?? ReflectionUtil.TryGetObject(model, "squad_"); object obj2 = ReflectionUtil.TryGetObject(model, "itemMenu_"); int? num3 = ReflectionUtil.TryGetInt(obj2, "itemId_"); int? num4 = null; int num5 = -1; int? num6 = null; bool flag2 = string.Equals(source, "HeroModel", StringComparison.Ordinal); bool hasValue = num2.HasValue; bool flag3 = !flag2; bool valueOrDefault = ReflectionUtil.TryGetBool(model, "isOpenItemMenu_").GetValueOrDefault(); if (_squadModel != null) { int? num7 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_"); if (num7.HasValue) { _activeSquadIndex = num7.Value; } } if (_activeSquadIndex.GetValueOrDefault() == 1 && flag3) { return; } if (flag3 && hasValue && (!string.Equals(_activeModelSource, source, StringComparison.Ordinal) || _activeUnitId != num2)) { _activeModelSource = source; _activeIsHero = false; _activeIsNormalUnit = true; _activeOpenItemMenu = valueOrDefault; _activeHasUnitId = true; _activeUnitId = num2; _activeUnitName = text; _activeSlotId = num; _itemMenu = null; _itemSelectWindow = null; _itemOperator = null; _lastAutoSelectedHoverItemId = null; _lastAutoSelectedHoverIndex = -1; _autoSelectCommitPending = false; _autoSelectCommitItemId = null; _lastAddingParamSource = null; _lastAddingParamTime = -1f; } if (valueOrDefault) { bool flag4 = flag2 && !hasValue; bool flag5 = string.Equals(_activeModelSource, "HeroModel", StringComparison.Ordinal); if (_squadModel != null) { int? num8 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_"); if (num8.HasValue) { _activeSquadIndex = num8.Value; } } if (_activeSquadIndex.GetValueOrDefault() == 1 && flag3) { return; } if (flag2 || flag3 || flag4 || !_activeIsNormalUnit || !_activeOpenItemMenu) { _activeModelSource = source; _activeIsHero = flag2 && !hasValue; _activeIsNormalUnit = flag3; _activeOpenItemMenu = true; _activeHasUnitId = hasValue; _activeUnitId = (hasValue ? num2 : null); _activeUnitName = text; _activeSlotId = num; _itemMenu = null; _itemSelectWindow = null; _itemOperator = null; _lastAutoSelectedHoverItemId = null; _lastAutoSelectedHoverIndex = -1; _autoSelectCommitPending = false; _autoSelectCommitItemId = null; _lastAddingParamSource = null; _lastAddingParamTime = -1f; if (flag2 && !flag5) { MarkEquippedStatsDirty("hero-open"); _lastActiveUnitForEquippedStats = null; } } } else { if (_activeModelSource != null && !string.Equals(source, _activeModelSource, StringComparison.Ordinal)) { return; } if (string.Equals(_activeModelSource, source, StringComparison.Ordinal)) { _activeOpenItemMenu = false; _activeIsHero = flag2 && !hasValue; _activeIsNormalUnit = flag3; _activeHasUnitId = hasValue; if (!string.IsNullOrEmpty(text)) { _activeUnitName = text; } if (num2.HasValue) { _activeUnitId = num2; } if (num.HasValue) { _activeSlotId = num; } } } if (num.HasValue && num.Value >= 0) { num6 = ReflectionUtil.TryInvokeInt(model, "getEquipItemId", (uint)num.Value); } if (obj2 != null) { if (obj2 != _itemMenu) { _itemMenu = obj2; _itemOperator = ReflectionUtil.TryGetObject(obj2, "itemOperator_"); _activeModelSource = source; _lastAutoSelectedHoverItemId = null; _lastAutoSelectedHoverIndex = -1; _autoSelectCommitPending = false; _autoSelectCommitItemId = null; flag = true; } object obj3 = ReflectionUtil.TryGetObject(obj2, "itemSelectWindow_"); if (obj3 != _itemSelectWindow) { _itemSelectWindow = obj3; _lastAutoSelectedHoverItemId = null; _lastAutoSelectedHoverIndex = -1; } if (obj3 != null) { int? num9 = ReflectionUtil.TryGetInt(obj3, "selectIndex_"); if (num9.HasValue) { num5 = num9.Value; object obj4 = ReflectionUtil.TryGetListElement(ReflectionUtil.TryGetObject(obj3, "belongingItemIdArray_"), num5); num4 = ReflectionUtil.TryGetInt(obj4, "itemId"); if (!num4.HasValue && obj4 != null) { try { num4 = Convert.ToInt32(obj4); } catch { } } } } } if (num.HasValue && num.Value != _slot) { _slot = num.Value; flag = true; } if (num2.HasValue && num2 != _unitId) { _unitId = num2; flag = true; } if (flag2 && !hasValue && _unitId.HasValue) { _unitId = null; flag = true; } if (num3.HasValue && num3 != _selectedItemId) { TryHandleManualReselectPattern(_selectedItemId, num3); _selectedItemId = num3; flag = true; } if (num4.HasValue && num4 != _hoverItemId) { _hoverItemId = num4; flag = true; TryAutoSelectHoverItem(num4, num5); } if (num5 != _hoverIndex) { _hoverIndex = num5; flag = true; } if (num6.HasValue && num6 != _equippedItemId) { _equippedItemId = num6; flag = true; } TryClearAutoSelectPendingOnModelChange(num3, num6); if (_source != source) { _source = source; flag = true; } if (obj != null && obj != _squad) { _squad = obj; flag = true; } int? num10 = ResolveConfirmedSelected(num3, num6); if (num10.HasValue && num10 != _confirmedSelectedItemId) { _confirmedSelectedItemId = num10; flag = true; } if (!flag) { return; } bool flag6 = string.Equals(source, "HeroModel", StringComparison.Ordinal); int? num11 = (flag6 ? _lastHeroSlotId : _lastNormalSlotId); int? num12 = (flag6 ? _lastHeroSelectedItemId : _lastNormalSelectedItemId); int? num13 = (flag6 ? _lastHeroEquippedItemId : _lastNormalEquippedItemId); int? num14 = (flag6 ? _lastHeroHoverItemId : _lastNormalHoverItemId); if (num != num11 || num3 != num12 || num6 != num13 || (EnableAutoSelectOverlay && num4 != num14)) { string value = (EnableAutoSelectOverlay ? $" hoverItemId={num4} hoverIndex={num5}" : string.Empty); Plugin.LogInfo($"{source} update slotId={num} unitId={num2} selectedItemId={num3}{value} equippedItemId={num6}"); if (flag6) { _lastHeroSlotId = num; _lastHeroSelectedItemId = num3; _lastHeroEquippedItemId = num6; _lastHeroHoverItemId = num4; } else { _lastNormalSlotId = num; _lastNormalSelectedItemId = num3; _lastNormalEquippedItemId = num6; _lastNormalHoverItemId = num4; } } } public static void Refresh() { OverlayUi.SetText("Better Stats\nSource: " + _source + "\nActive unit: " + FormatActiveUnit() + "\nSelected item: " + FormatNullable(_selectedItemId) + "\n" + $"Hover item: {FormatNullable(_hoverItemId)} (index {FormatInt(_hoverIndex)})\n" + "Equipped item: " + FormatNullable(_equippedItemId) + "\n" + $"Slot: {FormatInt(_slot)} EquipIndex: {FormatInt(_equipIndex)} Squad: {FormatNullable(_activeSquadIndex)}\n" + "UnitId: " + FormatNullable(_unitId) + "\nLast equip change: " + FormatEquipChange()); if (_lastDamageSquad != _squad || _lastDamageUnitId != _unitId || _lastEquippedDamageItemId != _equippedItemId) { _lastDamageSquad = _squad; _lastDamageUnitId = _unitId; _lastEquippedDamageItemId = _equippedItemId; _equippedDamageParam = DamageCalculatorResolver.TryCalcUnitDamage(_squad, _unitId); } if (_lastStatusDisplay != _statusDisplay || _lastHoverUnitAddingParam != _hoverUnitAddingParam) { _lastStatusDisplay = _statusDisplay; _lastHoverUnitAddingParam = _hoverUnitAddingParam; _lastAutoSelectedItemIdForParam = null; _lastEquippedItemIdForParam = null; } if (_activeIsNormalUnit) { TryResolveNormalUnitAddingParam(); } if (_activeSquadIndex != _lastActiveSquadForRefresh) { _lastActiveSquadForRefresh = _activeSquadIndex; MarkEquippedStatsDirty("squad-change"); _autoSelectedStatsItemId = null; _equippedStatsItemId = null; _autoSelectedStatsDamageParam = null; _equippedStatsDamageParam = null; _lastAutoSelectedDamageParam = null; _lastEquippedDamageParam = null; _differenceStatsAutoItemId = null; _differenceStatsEquippedItemId = null; _lastDifferenceAutoParam = null; _lastDifferenceEquippedParam = null; _lastActiveUnitForEquippedStats = null; } if (_activeIsNormalUnit && _lastActiveUnitForEquippedStats != _activeUnitId) { _lastActiveUnitForEquippedStats = _activeUnitId; MarkEquippedStatsDirty("unit-change"); } int? equippedItemId = _equippedItemId; if (EnableAutoSelectOverlay && (_lastAutoSelectedItemIdForParam != equippedItemId || _lastAutoSelectedDamageParam != _autoSelectedDamageParam)) { Plugin.LogInfo($"Auto select compute slot={_slot} autoSelectedItemId={equippedItemId} statusDisplay={_statusDisplay?.GetType().FullName ?? "-"} addingParam={_hoverUnitAddingParam?.GetType().FullName ?? "-"}"); _autoSelectedDamageParam = UnitAddingParamResolver.TryComputeDamageParamWithTempEquip(_statusDisplay, _hoverUnitAddingParam, _slot, equippedItemId, _itemOperator); _lastAutoSelectedItemIdForParam = equippedItemId; Plugin.LogInfo("Auto select result damageParam=" + ((_autoSelectedDamageParam == null) ? "-" : _autoSelectedDamageParam.GetType().FullName)); } if (_lastEquippedItemIdForParam != _equippedItemId || _lastEquippedDamageParam != _equippedDamageParam) { _equippedDamageParam = UnitAddingParamResolver.TryComputeDamageParamWithTempEquip(_statusDisplay, _hoverUnitAddingParam, _slot, _equippedItemId, _itemOperator); _lastEquippedItemIdForParam = _equippedItemId; _lastEquippedDamageParam = _equippedDamageParam; } if (_lastStatsItemOperator != _itemOperator) { _lastStatsItemOperator = _itemOperator; _autoSelectedStatsItemId = null; _equippedStatsItemId = null; _autoSelectedStatsDamageParam = null; _equippedStatsDamageParam = null; _differenceStatsAutoItemId = null; _differenceStatsEquippedItemId = null; _lastDifferenceAutoParam = null; _lastDifferenceEquippedParam = null; } if (_equippedStatsNeedsUpdate && (_equippedStatsItemId != _equippedItemId || _lastEquippedDamageParam != _equippedDamageParam)) { _equippedStatsText = ItemStatResolver.BuildStatsFromDamageParam("Equipped stats", _equippedDamageParam, _equippedItemId); _equippedStatsItemId = _equippedItemId; _equippedStatsDamageParam = _equippedDamageParam; _equippedStatsNeedsUpdate = false; Plugin.LogInfo($"Equipped stats updated itemId={_equippedItemId} reason={_equippedStatsUpdateReason}"); } if (EnableAutoSelectOverlay && _autoSelectedDamageParam != null) { if (_autoSelectedStatsItemId != equippedItemId || _lastAutoSelectedDamageParam != _autoSelectedDamageParam) { _autoSelectedStatsText = ItemStatResolver.BuildStatsFromDamageParam("Auto selected stats", _autoSelectedDamageParam, equippedItemId); _autoSelectedStatsItemId = equippedItemId; _autoSelectedStatsDamageParam = _autoSelectedDamageParam; _lastAutoSelectedDamageParam = _autoSelectedDamageParam; } } else { _autoSelectedStatsText = string.Empty; _autoSelectedStatsItemId = null; _autoSelectedStatsDamageParam = null; _lastAutoSelectedDamageParam = null; } if (EnableAutoSelectOverlay && _autoSelectedStatsDamageParam != null && _equippedStatsDamageParam != null) { if (_differenceStatsAutoItemId != _autoSelectedStatsItemId || _differenceStatsEquippedItemId != _equippedStatsItemId || _lastDifferenceAutoParam != _autoSelectedStatsDamageParam || _lastDifferenceEquippedParam != _equippedStatsDamageParam) { _differenceStatsText = ItemStatResolver.BuildStatsDifference("Stats difference", _autoSelectedStatsDamageParam, _equippedStatsDamageParam, _autoSelectedStatsItemId, _equippedStatsItemId); _differenceStatsReleaseLines = ItemStatResolver.BuildStatsDifferenceNumberLines(_autoSelectedStatsDamageParam, _equippedStatsDamageParam, _autoSelectedStatsItemId, _equippedStatsItemId); _differenceStatsAutoItemId = _autoSelectedStatsItemId; _differenceStatsEquippedItemId = _equippedStatsItemId; _lastDifferenceAutoParam = _autoSelectedStatsDamageParam; _lastDifferenceEquippedParam = _equippedStatsDamageParam; } } else { _differenceStatsText = string.Empty; _differenceStatsReleaseLines = Array.Empty<string>(); _differenceStatsAutoItemId = null; _differenceStatsEquippedItemId = null; _lastDifferenceAutoParam = null; _lastDifferenceEquippedParam = null; } OverlayUi.SetStatsText(_equippedStatsText, _autoSelectedStatsText, _differenceStatsText); OverlayUi.SetAutoSelectedVisible(EnableAutoSelectOverlay); bool num = IsHeadquartersActive(); ReleaseOverlayUi.SetVisible(num); ReleaseOverlayUi.SetText(num ? _differenceStatsReleaseLines : Array.Empty<string>()); TryDumpSquadOrgUnits(); } private static string FormatNullable(int? value) { if (!value.HasValue) { return "-"; } return value.Value.ToString(); } private static string FormatInt(int value) { if (value < 0) { return "-"; } return value.ToString(); } private static string FormatEquipChange() { if (_lastEquipIndex.HasValue || !string.IsNullOrEmpty(_lastEquipName)) { return _lastEquipName + " (" + (_lastEquipIndex?.ToString() ?? "-") + ")"; } return "-"; } private static void MarkEquippedStatsDirty(string reason) { _equippedStatsNeedsUpdate = true; _equippedStatsUpdateReason = (string.IsNullOrEmpty(reason) ? "-" : reason); } private static string FormatActiveUnit() { string value = (string.IsNullOrEmpty(_activeUnitName) ? "-" : _activeUnitName); string value2 = (_activeUnitId.HasValue ? _activeUnitId.Value.ToString() : "-"); string value3 = (_activeHasUnitId ? "Unit" : (_activeIsHero ? "Hero" : "Unit")); string value4 = (_activeSlotId.HasValue ? _activeSlotId.Value.ToString() : "-"); return $"{value3} {value} (slot {value4}, id {value2})"; } private static string FormatBool(bool? value) { if (!value.HasValue) { return "-"; } if (!value.Value) { return "false"; } return "true"; } private static void TryAutoSelectHoverItem(int? hoverItemId, int hoverIndex) { if (!EnableAutoSelectFromHover || !hoverItemId.HasValue || _itemMenu == null || hoverIndex < 0) { return; } int value = hoverItemId.Value; if (value < 0 || (_lastAutoSelectedHoverItemId == value && _lastAutoSelectedHoverIndex == hoverIndex)) { return; } if ((_selectedItemId.HasValue && _selectedItemId.Value == value) || (_confirmedSelectedItemId.HasValue && _confirmedSelectedItemId.Value == value)) { _lastAutoSelectedHoverItemId = value; _lastAutoSelectedHoverIndex = hoverIndex; return; } Plugin.LogInfo($"Auto-select hover item {value}"); _lastAutoSelectedHoverItemId = value; _lastAutoSelectedHoverIndex = hoverIndex; _autoSelectCommitPending = true; _autoSelectCommitItemId = value; SetCursorMovementSuppressed(suppress: true); if (_itemSelectWindow != null) { ReflectionUtil.TrySetInt(_itemSelectWindow, "selectIndex_", hoverIndex); ReflectionUtil.TryInvokeObject(_itemSelectWindow, "onSlotDecide"); if (EnableAutoSelectDoubleConfirm) { ScheduleDoubleConfirm(hoverIndex, value); } } else { ReflectionUtil.TryInvokeObject(_itemMenu, "decide", (uint)value); if (EnableAutoSelectDoubleConfirm) { ScheduleDoubleConfirm(hoverIndex, value); } } } internal static bool TryConsumeAutoSelectCommit(uint itemId) { if (!_autoSelectCommitPending) { return false; } if (_autoSelectCommitItemId.HasValue && _autoSelectCommitItemId.Value != itemId) { _autoSelectCommitPending = false; _autoSelectCommitItemId = null; Plugin.LogInfo($"Auto-select commit mismatch itemId={itemId}"); return false; } _autoSelectCommitPending = false; _autoSelectCommitItemId = null; Plugin.LogInfo($"Auto-select commit consumed itemId={itemId}"); if (!EnableAutoSelectDoubleConfirm) { SetCursorMovementSuppressed(suppress: false); } return true; } private static void TryHandleManualReselectPattern(int? prevSelected, int? nextSelected) { if (!nextSelected.HasValue || _autoSelectCommitPending) { return; } int value = nextSelected.Value; int? num = prevSelected; if (value < 0) { if (num.HasValue && num.Value >= 0) { _pendingReselectItemId = num.Value; Plugin.LogInfo($"Manual reselect armed itemId={num.Value}"); } } else if (_pendingReselectItemId.HasValue && num.HasValue && num.Value < 0) { if (_pendingReselectItemId.Value == value) { _pendingReselectItemId = null; MarkEquippedStatsDirty("manual-reselect"); _lastManualEquipItemId = value; Plugin.LogInfo($"Manual reselect detected itemId={value}"); } else { _pendingReselectItemId = null; } } } internal static string BuildSelectionSnapshot() { return $"selected={FormatNullable(_selectedItemId)} equipped={FormatNullable(_equippedItemId)} hover={FormatNullable(_hoverItemId)} hoverIndex={FormatInt(_hoverIndex)} slot={FormatInt(_slot)} unit={FormatNullable(_unitId)}"; } private static void TryClearAutoSelectPendingOnModelChange(int? selectedItemId, int? equippedItemId) { if (_autoSelectCommitPending && _autoSelectCommitItemId.HasValue) { int value = _autoSelectCommitItemId.Value; if ((selectedItemId.HasValue && selectedItemId.Value == value) || (equippedItemId.HasValue && equippedItemId.Value == value)) { _autoSelectCommitPending = false; _autoSelectCommitItemId = null; Plugin.LogInfo($"Auto-select commit cleared by model change itemId={value}"); } } } internal static void NotifyManualEquipCommit(uint itemId) { MarkEquippedStatsDirty("manual-commit"); _lastManualEquipItemId = (int)itemId; if (_activeIsHero) { if (!_equippedItemId.HasValue || _equippedItemId.Value != (int)itemId) { _equippedItemId = (int)itemId; } } Plugin.LogInfo($"Manual equip commit detected for item {itemId}"); } internal static void UpdatePendingDoubleConfirm() { if (!(_pendingSecondConfirmAt < 0f) && !(Time.realtimeSinceStartup < _pendingSecondConfirmAt)) { int pendingSecondConfirmHoverIndex = _pendingSecondConfirmHoverIndex; int? pendingSecondConfirmItemId = _pendingSecondConfirmItemId; ClearPendingDoubleConfirm(); Plugin.LogAutoSelect($"triggering double confirm itemId={pendingSecondConfirmItemId} index={pendingSecondConfirmHoverIndex} at {Time.realtimeSinceStartup:0.000}"); bool value = InputUtil.SendSpaceKey(); Plugin.LogInfo($"Auto-select double confirm (space) itemId={pendingSecondConfirmItemId} index={pendingSecondConfirmHoverIndex} sent={value}"); Plugin.LogAutoSelect($"space key send result={value}"); } } private static void ScheduleDoubleConfirm(int hoverIndex, int itemId) { SetCursorMovementSuppressed(suppress: true); _pendingSecondConfirmHoverIndex = hoverIndex; _pendingSecondConfirmItemId = itemId; float value = (_pendingSecondConfirmAt = Time.realtimeSinceStartup + 0.1f); Plugin.LogInfo($"Auto-select double confirm scheduled itemId={itemId} index={hoverIndex}"); Plugin.LogAutoSelect($"scheduled itemId={itemId} index={hoverIndex} at {Time.realtimeSinceStartup:0.000} -> {value:0.000}"); } private static void ClearPendingDoubleConfirm() { _pendingSecondConfirmAt = -1f; _pendingSecondConfirmHoverIndex = -1; _pendingSecondConfirmItemId = null; SetCursorMovementSuppressed(suppress: false); } public static void UpdateStatusDisplayFromView(object view) { if (view == null) { return; } object obj = ReflectionUtil.TryGetObject(view, "squadStatusDisplay_") ?? ReflectionUtil.TryInvokeObject(view, "getDisplay") ?? ReflectionUtil.TryGetObject(view, "display_"); if (obj != null && obj != _statusDisplay) { _statusDisplay = obj; if (Plugin.EnableDebugLogs && _lastLoggedStatusDisplay != obj) { _lastLoggedStatusDisplay = obj; Plugin.LogInfo("StatusDisplay set: " + obj.GetType().FullName); } } } public static void UpdateStatusDisplayFromDisplay(object display) { if (display != null && _statusDisplay != display) { _statusDisplay = display; if (Plugin.EnableDebugLogs && _lastLoggedStatusDisplay != display) { _lastLoggedStatusDisplay = display; Plugin.LogInfo("StatusDisplay set: " + display.GetType().FullName); } } } public static void UpdateHoverAddingParam(object addingParam, string source) { if (_hoverUnitAddingParam != addingParam) { _hoverUnitAddingParam = addingParam; _lastAddingParamSource = source; _lastAddingParamTime = Time.realtimeSinceStartup; if (EnableAutoSelectOverlay && Plugin.EnableDebugLogs && _lastLoggedAutoSelectedParam != addingParam) { _lastLoggedAutoSelectedParam = addingParam; Plugin.LogInfo("Auto select adding param: " + ((addingParam == null) ? "-" : addingParam.GetType().FullName)); } } } public static bool ShouldUseSquadOrgAddingParam(uint slotId) { if (!_activeIsNormalUnit) { return false; } if (_slot < 0) { return true; } return slotId == (uint)_slot; } private static bool IsNormalUnitAddingParamSource(string source) { if (!string.Equals(source, "SquadOrgObserver", StringComparison.Ordinal)) { return string.Equals(source, "SquadModelUnit", StringComparison.Ordinal); } return true; } private static int ResolveUnitIndex(object unitArray) { int num = ReflectionUtil.TryGetListCount(unitArray); int? activeUnitId = _activeUnitId; int? num2 = null; if (activeUnitId.HasValue) { num2 = activeUnitId.Value; if (num2.Value < 0) { num2 = null; } } int? num3 = null; if (activeUnitId.HasValue && new int?(activeUnitId.Value + 1).Value < 0) { num3 = null; } int?[] array = new int?[1] { num2 }; for (int i = 0; i < array.Length; i++) { int? num4 = array[i]; if (num4.HasValue && (num <= 0 || num4.Value < num) && num4.Value >= 0) { return num4.Value; } } return -1; } public static void SetSquadOrgObserver(object observer) { if (observer != null && observer != _squadOrgObserver) { _squadOrgObserver = observer; _squadModel = ReflectionUtil.TryGetObject(observer, "paragetoSquadModel_"); _didDumpSquadOrgUnits = false; } } public static void NotifyHeadquartersActive() { _lastHeadquartersHeartbeatTime = Time.realtimeSinceStartup; } public static void SetActiveUnitCursorIndex(uint selectIndex) { _activeCursorIndex = (int)selectIndex; } private static bool IsHeadquartersActive() { if (_lastHeadquartersHeartbeatTime < 0f) { return false; } return Time.realtimeSinceStartup - _lastHeadquartersHeartbeatTime <= 1.5f; } private static void TryDumpSquadOrgUnits() { if (_squadOrgObserver == null || _didDumpSquadOrgUnits || (_lastDumpAttemptTime >= 0f && Time.realtimeSinceStartup - _lastDumpAttemptTime < 1f)) { return; } _lastDumpAttemptTime = Time.realtimeSinceStartup; try { object obj = ReflectionUtil.TryGetObject(_squadOrgObserver, "paragetoSquadModel_"); if (obj == null || !ReflectionUtil.TryInvokeBool(obj, "isReadyOrganization").GetValueOrDefault()) { return; } object listObj = ReflectionUtil.TryInvokeObject(obj, "getSquadArray"); int num = ReflectionUtil.TryGetListCount(listObj); if (num <= 0) { return; } Plugin.LogInfo($"HQ unit dump: squads={num}"); for (int i = 0; i < num; i++) { object obj2 = ReflectionUtil.TryGetListElement(listObj, i); object listObj2 = ReflectionUtil.TryGetObject(obj2, "unitArray_"); int num2 = ReflectionUtil.TryGetListCount(listObj2); string text = ReflectionUtil.TryGetString(obj2, "unitName_"); bool? value = ReflectionUtil.TryGetBool(obj2, "isHero_"); int? value2 = ReflectionUtil.TryGetInt(obj2, "heroIndex_"); Plugin.LogInfo($"HQ squad={i} name={text ?? "-"} units={num2} squadHero={FormatBool(value)} heroIndex={FormatNullable(value2)}"); for (int j = 0; j < num2; j++) { object obj3 = ReflectionUtil.TryGetListElement(listObj2, j); string text2 = ReflectionUtil.TryGetString(obj3, "name_"); bool? value3 = ReflectionUtil.TryGetBool(obj3, "isHero_"); object obj4 = ReflectionUtil.TryGetObject(obj3, "unitAddingParam_"); int? value4 = ReflectionUtil.TryGetInt(obj4, "weaponId"); int? value5 = ReflectionUtil.TryGetInt(obj4, "cap"); Plugin.LogInfo($"HQ squad={i} unit={j} name={text2 ?? "-"} hero={FormatBool(value3)} weaponId={FormatNullable(value4)} cap={FormatNullable(value5)}"); } } _didDumpSquadOrgUnits = true; } catch (Exception ex) { Plugin.LogWarning("HQ unit dump failed: " + ex.Message); } } private static void TryResolveNormalUnitAddingParam() { if (_squadModel == null || (!_activeUnitId.HasValue && !_activeSlotId.HasValue)) { return; } try { object obj = ReflectionUtil.TryInvokeObject(_squadModel, "getCurSquad"); if (obj == null) { int? num = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_"); if (num.HasValue && num.Value >= 0) { obj = ReflectionUtil.TryGetListElement(ReflectionUtil.TryInvokeObject(_squadModel, "getSquadArray"), num.Value); } } object obj2 = ReflectionUtil.TryGetObject(obj, "unitArray_"); int num2 = ResolveUnitIndex(obj2); int? num3 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_"); if (num3.HasValue) { _activeSquadIndex = num3.Value; } int? num4 = ReflectionUtil.TryGetInt(obj, "heroIndex_"); if (ReflectionUtil.TryGetBool(obj, "isHero_").GetValueOrDefault() && num4.HasValue && num4.Value >= 0) { _heroUnitIndexInSquad = num4.Value; } else { _heroUnitIndexInSquad = null; } if (_heroUnitIndexInSquad.HasValue && num2 == _heroUnitIndexInSquad.Value) { _activeIsHero = true; _activeIsNormalUnit = false; _activeHasUnitId = false; _activeUnitId = null; _lastAddingParamSource = null; _lastAddingParamTime = -1f; _lastActiveUnitForEquippedStats = null; return; } object obj3 = ReflectionUtil.TryGetListElement(obj2, num2); if (obj3 == null && _activeSlotId.HasValue) { obj3 = ReflectionUtil.TryGetListElement(obj2, _activeSlotId.Value); } object obj4 = ReflectionUtil.TryGetObject(obj3, "unitAddingParam_") ?? ReflectionUtil.TryInvokeObject(obj3, "getAddingParam"); if (obj4 != null) { string text = ReflectionUtil.TryGetString(obj3, "name_"); if (!string.IsNullOrEmpty(text)) { _activeUnitName = text; } UpdateHoverAddingParam(obj4, "SquadModelUnit"); } } catch (Exception ex) { if (Plugin.EnableDebugLogs) { Plugin.LogWarning("Resolve normal unit adding param failed: " + ex.Message); } } } private static int ResolveSquadSlotCount() { int? num = ReflectionUtil.TryGetStaticInt(Plugin.FindTypeByName("P2.Bases.HeadQuarters.NormalUnitModel") ?? Plugin.FindTypeByName("P2S.Bases.HeadQuarters.NormalUnitModel"), "slotNum_g"); if (num.HasValue && num.Value > 0) { return num.Value; } return ReflectionUtil.TryGetStaticInt(Plugin.FindTypeByName("P2.Bases.HeadQuarters.HeroModel") ?? Plugin.FindTypeByName("P2S.Bases.HeadQuarters.HeroModel"), "slotNum_g").GetValueOrDefault(); } private static int? ResolveConfirmedSelected(int? selectedItemId, int? equippedItemId) { if (selectedItemId.HasValue && selectedItemId.Value >= 0) { return selectedItemId; } if (equippedItemId.HasValue && equippedItemId.Value >= 0) { return equippedItemId; } return null; } } internal static class InputUtil { private struct INPUT { public int type; public InputUnion u; } [StructLayout(LayoutKind.Explicit)] private struct InputUnion { [FieldOffset(0)] public KEYBDINPUT ki; } private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } private const int INPUT_KEYBOARD = 1; private const uint KEYEVENTF_KEYUP = 2u; private const uint KEYEVENTF_SCANCODE = 8u; private const ushort VK_SPACE = 32; private const ushort SCAN_SPACE = 57; [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); [DllImport("user32.dll", SetLastError = true)] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); internal static bool SendSpaceKey() { if (TrySendInputVkSpace()) { Plugin.LogAutoSelect("SendSpaceKey via vk input succeeded"); return true; } if (TrySendInputScanSpace()) { Plugin.LogAutoSelect("SendSpaceKey via scan input succeeded"); return true; } int lastWin32Error = Marshal.GetLastWin32Error(); Plugin.LogWarning($"SendInput failed for space. LastError={lastWin32Error}"); try { keybd_event(32, 57, 0u, UIntPtr.Zero); keybd_event(32, 57, 2u, UIntPtr.Zero); Plugin.LogAutoSelect("SendSpaceKey via keybd_event succeeded"); return true; } catch (Exception ex) { Plugin.LogWarning("keybd_event failed for space: " + ex.Message); Plugin.LogAutoSelect("SendSpaceKey via keybd_event failed: " + ex.Message); return false; } } private static bool TrySendInputVkSpace() { INPUT[] array = new INPUT[2] { new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 32, wScan = 0, dwFlags = 0u, time = 0u, dwExtraInfo = IntPtr.Zero } } }, new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 32, wScan = 0, dwFlags = 2u, time = 0u, dwExtraInfo = IntPtr.Zero } } } }; return SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>()) == array.Length; } private static bool TrySendInputScanSpace() { INPUT[] array = new INPUT[2] { new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0, wScan = 57, dwFlags = 8u, time = 0u, dwExtraInfo = IntPtr.Zero } } }, new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0, wScan = 57, dwFlags = 10u, time = 0u, dwExtraInfo = IntPtr.Zero } } } }; return SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>()) == array.Length; } } public static class ItemStatResolver { private static Type _laboCommonType; private static Type _weaponParamType; private static MethodInfo _getInstanceMethod; private static MethodInfo _getWeaponParamMethod; private static MethodInfo _getDamageParamByIdsMethod; private static MethodInfo _getDamageParamByNameMethod; private static Dictionary<int, string> _equipNameById; private static bool _equipNameLoaded; public static string BuildStats(string title, int? itemId, object itemOperator) { if (!itemId.HasValue || itemId.Value < 0) { return title + "\nItem: -"; } ItemStats itemStats = TryGetStats(itemId.Value, itemOperator); if (itemStats != null) { string value = FormatDamage(itemStats); string value2 = FormatFloat(itemStats.AttackSpeed) + " (wait " + FormatFloat(itemStats.AttackWait) + ")"; string value3 = FormatTriplePercent(itemStats.CritRatio, itemStats.KnockbackRatio, itemStats.CncRatio); string value4 = FormatTriplePercent(itemStats.IgniteRatio, itemStats.FreezeRatio, itemStats.SleepRatio); string value5 = FormatTriplePercent(itemStats.ResistIgnite, itemStats.ResistFreeze, itemStats.ResistSleep); return $"{title}\nItem: {itemId}\nHP: {FormatFloat(itemStats.Hp)}\nDamage: {value}\nAtk Speed: {value2}\nCrit/KB/Cnc: {value3}\nIgn/Frz/Sleep: {value4}\nResist I/F/S: {value5}"; } return $"{title}\nItem: {itemId}\nStats: -"; } public static string BuildStatsFromDamageParam(string title, object damageParam, int? itemId) { if (damageParam == null) { return title + "\nItem: " + FormatItemId(itemId) + "\nStats: -"; } ItemStats itemStats = BuildStatsFromDamageParam(damageParam); if (itemStats == null) { return title + "\nItem: " + FormatItemId(itemId) + "\nStats: -"; } string value = FormatDamage(itemStats); string value2 = FormatFloat(itemStats.AttackSpeed) + " (wait " + FormatFloat(itemStats.AttackWait) + ")"; string value3 = FormatTriplePercent(itemStats.CritRatio, itemStats.KnockbackRatio, itemStats.CncRatio); string value4 = FormatTriplePercent(itemStats.IgniteRatio, itemStats.FreezeRatio, itemStats.SleepRatio); string value5 = FormatTriplePercent(itemStats.ResistIgnite, itemStats.ResistFreeze, itemStats.ResistSleep); return $"{title}\nItem: {FormatItemId(itemId)}\nHP: {FormatFloat(itemStats.Hp)}\nDamage: {value}\nAtk Speed: {value2}\nCrit/KB/Cnc: {value3}\nIgn/Frz/Sleep: {value4}\nResist I/F/S: {value5}"; } public static string BuildStatsDifference(string title, object autoDamageParam, object equippedDamageParam, int? autoItemId, int? equippedItemId) { if (autoDamageParam != null && equippedDamageParam != null) { ItemStats itemStats = BuildStatsFromDamageParam(autoDamageParam); ItemStats itemStats2 = BuildStatsFromDamageParam(equippedDamageParam); if (itemStats != null && itemStats2 != null) { string value = ColorizeSignedTokens(FormatSignedFloat(Diff(itemStats.Hp, itemStats2.Hp))); string value2 = ColorizeSignedTokens(FormatDamageDifference(itemStats, itemStats2)); float? value3 = Diff(itemStats.AttackSpeed, itemStats2.AttackSpeed); float? value4 = Diff(itemStats.AttackWait, itemStats2.AttackWait); string text = ColorizeAttackSpeedSignedFloat(value3); string text2 = ColorizeAttackSpeedSignedFloat(value4); string value5 = text + " (wait " + text2 + ")"; string value6 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.CritRatio, itemStats2.CritRatio), Diff(itemStats.KnockbackRatio, itemStats2.KnockbackRatio), Diff(itemStats.CncRatio, itemStats2.CncRatio))); string value7 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.IgniteRatio, itemStats2.IgniteRatio), Diff(itemStats.FreezeRatio, itemStats2.FreezeRatio), Diff(itemStats.SleepRatio, itemStats2.SleepRatio))); string value8 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.ResistIgnite, itemStats2.ResistIgnite), Diff(itemStats.ResistFreeze, itemStats2.ResistFreeze), Diff(itemStats.ResistSleep, itemStats2.ResistSleep))); return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nHP: {value}\nDamage: {value2}\nAtk Speed: {value5}\nCrit/KB/Cnc: {value6}\nIgn/Frz/Sleep: {value7}\nResist I/F/S: {value8}"; } return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nStats: -"; } return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nStats: -"; } public static string[] BuildStatsDifferenceNumberLines(object autoDamageParam, object equippedDamageParam, int? autoItemId, int? equippedItemId) { if (autoDamageParam != null && equippedDamageParam != null) { ItemStats itemStats = BuildStatsFromDamageParam(autoDamageParam); ItemStats itemStats2 = BuildStatsFromDamageParam(equippedDamageParam); if (itemStats != null && itemStats2 != null) { string text = ColorizeSignedTokens(FormatSignedFloat(Diff(itemStats.Hp, itemStats2.Hp))); string text2 = ColorizeSignedTokens(FormatDamageDifference(itemStats, itemStats2)); string text3 = ColorizeAttackSpeedSignedFloat(Diff(itemStats.AttackWait, itemStats2.AttackWait)); string text4 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.CritRatio, itemStats2.CritRatio))); string text5 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.KnockbackRatio, itemStats2.KnockbackRatio))); string text6 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.CncRatio, itemStats2.CncRatio))); string text7 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.IgniteRatio, itemStats2.IgniteRatio))); string text8 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.FreezeRatio, itemStats2.FreezeRatio))); string text9 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.SleepRatio, itemStats2.SleepRatio))); string text10 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistIgnite, itemStats2.ResistIgnite))); string text11 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistFreeze, itemStats2.ResistFreeze))); string text12 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistSleep, itemStats2.ResistSleep))); return new string[12] { text, text2, text3, text10, text11, text12, text4, text5, text6, text7, text8, text9 }; } return new string[12] { "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-" }; } return new string[12] { "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-" }; } public static string TryGetEquipName(int itemId) { return TryGetEquipNameByItemId(itemId); } public static string BuildStatsSummary(object damageParam) { if (damageParam == null) { return "-"; } ItemStats itemStats = BuildStatsFromDamageParam(damageParam); if (itemStats == null) { return "-"; } return $"HP={FormatFloat(itemStats.Hp)} Dmg={FormatDamage(itemStats)} AtkSpd={FormatFloat(itemStats.AttackSpeed)} Crit={FormatPercent(itemStats.CritRatio)} KB={FormatPercent(itemStats.KnockbackRatio)} Cnc={FormatPercent(itemStats.CncRatio)}"; } private static ItemStats TryGetStats(int itemId, object itemOperator) { if (itemOperator == null) { return null; } object obj = ReflectionUtil.TryInvokeObject(itemOperator, "getItemParam", itemId); if (obj == null) { return null; } string text = ReflectionUtil.TryGetString(obj, "modelName"); string text2 = ReflectionUtil.TryGetString(obj, "userString"); string text3 = ReflectionUtil.TryGetString(obj, "iconModelName"); string text4 = TryGetEquipNameByItemId(itemId); object obj2 = TryGetWeaponParamForItem(itemId, text4, text2, text, text3); object obj3 = null; if (obj2 != null) { object obj4 = ReflectionUtil.TryGetObject(obj2, "statusRefParam"); int? categoryId = ReflectionUtil.TryGetInt(obj4, "categoryId"); int? paramId = ReflectionUtil.TryGetInt(obj4, "paramId"); obj3 = TryGetDamageParamByIds(categoryId, paramId); } if (obj3 == null) { if (Plugin.EnableDebugLogs) { Plugin.LogInfo($"Stats not found for itemId={itemId} modelName={text} userString={text2} iconModelName={text3}"); } return null; } return BuildStatsFromDamageParam(obj3); } private static ItemStats BuildStatsFromDamageParam(object damageParam) { ItemStats itemStats = new ItemStats(); object obj = ReflectionUtil.TryGetObject(damageParam, "attackBase"); itemStats.Hp = ReflectionUtil.TryGetFloat(obj, "hitPoint"); itemStats.AttackWait = ReflectionUtil.TryGetFloat(obj, "attackWait"); if (itemStats.AttackWait.HasValue && itemStats.AttackWait.Value > 0.0001f) { itemStats.AttackSpeed = 1f / itemStats.AttackWait.Value; } object obj2 = ReflectionUtil.TryGetObject(damageParam, "attackCalc"); itemStats.MinDamage = ReflectionUtil.TryGetFloat(obj2, "minBaseDamage"); itemStats.MaxDamage = ReflectionUtil.TryGetFloat(obj2, "maxBaseDamage"); itemStats.AttackPower = ReflectionUtil.TryGetFloat(obj2, "attackNBPower"); object ratioObj = ReflectionUtil.TryGetObject(damageParam, "attackRatio"); itemStats.CritRatio = GetRatio(ratioObj, 0); itemStats.KnockbackRatio = GetRatio(ratioObj, 1); itemStats.CncRatio = GetRatio(ratioObj, 2); itemStats.IgniteRatio = GetRatio(ratioObj, 3); itemStats.SleepRatio = GetRatio(ratioObj, 4); itemStats.FreezeRatio = GetRatio(ratioObj, 5); object ratioObj2 = ReflectionUtil.TryGetObject(damageParam, "sufferAvoidRatio"); itemStats.ResistIgnite = GetRatio(ratioObj2, 3); itemStats.ResistSleep = GetRatio(ratioObj2, 4); itemStats.ResistFreeze = GetRatio(ratioObj2, 5); return itemStats; } private static object TryGetWeaponParamForItem(int itemId, params string[] names) { object obj = null; if (names == null || names.Length == 0) { return null; } foreach (string text in names) { if (string.IsNullOrEmpty(text)) { continue; } object obj2 = TryGetWeaponParam(text); if (obj2 != null) { if (MatchesItemId(obj2, itemId)) { return obj2; } if (obj == null) { obj = obj2; } } } return obj; } private static bool MatchesItemId(object weaponParam, int itemId) { int? num = ReflectionUtil.TryGetInt(weaponParam, "itemId"); if (num.HasValue) { return num.Value == itemId; } return false; } private static float? GetRatio(object ratioObj, int index) { if (ratioObj == null) { return null; } return ReflectionUtil.TryGetArrayElementFloat(ReflectionUtil.TryGetObject(ratioObj, "ratio"), index); } private static object TryGetWeaponParam(string resourceName) { if (string.IsNullOrEmpty(resourceName)) { return null; } EnsureLaboCommon(); if (_laboCommonType == null || _weaponParamType == null || _getInstanceMethod == null || _getWeaponParamMethod == null) { return null; } object obj = _getInstanceMethod.Invoke(null, null); if (obj == null) { return null; } try { return _getWeaponParamMethod.MakeGenericMethod(_weaponParamType).Invoke(obj, new object[1] { resourceName }); } catch { return null; } } private static string TryGetEquipNameByItemId(int itemId) { EnsureLaboCommon(); if (_laboCommonType == null) { return null; } if (!_equipNameLoaded) { _equipNameLoaded = true; _equipNameById = new Dictionary<int, string>(); string[] array = new string[9] { "equipParamName1", "equipParamName2", "equipParamName3", "equipParamName4", "equipParamName5", "equipParamName6", "equipParamName7", "equipParamName8", "equipParamName9" }; foreach (string name in array) { FieldInfo field = _laboCommonType.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { continue; } object obj = null; try { obj = field.GetValue(null); } catch { } if (obj == null) { continue; } int? num = ReflectionUtil.TryGetCollectionLength(obj); if (!num.HasValue) { continue; } for (int j = 0; j < num.Value; j++) { object obj3 = ReflectionUtil.TryGetListElement(obj, j); if (obj3 != null) { int? num2 = ReflectionUtil.TryGetInt(obj3, "id"); string value = ReflectionUtil.TryGetString(obj3, "str"); if (num2.HasValue && num2.Value >= 0 && !string.IsNullOrEmpty(value) && !_equipNameById.ContainsKey(num2.Value)) { _equipNameById[num2.Value] = value; } } } } } if (_equipNameById != null && _equipNameById.TryGetValue(itemId, out var value2)) { return value2; } return null; } private static object TryGetDamageParamByIds(int? categoryId, int? paramId) { if (!categoryId.HasValue || !paramId.HasValue) { return null; } EnsureLaboCommon(); if (_getDamageParamByIdsMethod == null) { return null; } return _getDamageParamByIdsMethod.Invoke(null, new object[2] { (uint)categoryId.Value, (uint)paramId.Value }); } private static object TryGetDamageParamByName(string name) { if (string.IsNullOrEmpty(name)) { return null; } EnsureLaboCommon(); if (_getDamageParamByNameMethod == null) { return null; } return _getDamageParamByNameMethod.Invoke(null, new object[1] { name }); } private static void EnsureLaboCommon() { if (_laboCommonType == null) { _laboCommonType = Plugin.FindTypeByName("P2.LaboCommon") ?? Plugin.FindTypeByName("P2S.LaboCommon"); } if (_weaponParamType == null) { _weaponParamType = Plugin.FindTypeByName("P2.Game.Unit.WeaponParam.BaseParam") ?? Plugin.FindTypeByName("P2S.Game.Unit.WeaponParam.BaseParam"); } if (!(_laboCommonType == null)) { if ((object)_getInstanceMethod == null) { _getInstanceMethod = _laboCommonType.GetMethod("getInstance", BindingFlags.Static | BindingFlags.Public); } if ((object)_getWeaponParamMethod == null) { _getWeaponParamMethod = _laboCommonType.GetMethod("getWeaponParam", BindingFlags.Instance | BindingFlags.Public); } if ((object)_getDamageParamByIdsMethod == null) { _getDamageParamByIdsMethod = _laboCommonType.GetMethod("getDamageParam_List", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(uint), typeof(uint) }, null); } if ((object)_getDamageParamByNameMethod == null) { _getDamageParamByNameMethod = _laboCommonType.GetMethod("getDamageParam_Param", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null) ?? _laboCommonType.GetMethod("getDamageParam_List", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); } } } private static string FormatDamage(ItemStats stats) { string text = "-"; if (stats.MinDamage.HasValue || stats.MaxDamage.HasValue) { float? value = stats.MinDamage ?? stats.MaxDamage; float? value2 = stats.MaxDamage ?? stats.MinDamage; if (value.HasValue && value2.HasValue) { text = ((Math.Abs(value.Value - value2.Value) < 0.001f) ? FormatFloat(value) : (FormatFloat(value) + "-" + FormatFloat(value2))); } } if (text == "-" && stats.AttackPower.HasValue) { text = FormatFloat(stats.AttackPower); } return text; } private static string FormatDamageDifference(ItemStats autoStats, ItemStats equippedStats) { float? num = Diff(autoStats.MinDamage, equippedStats.MinDamage); float? num2 = Diff(autoStats.MaxDamage, equippedStats.MaxDamage); string text = "-"; if (num.HasValue || num2.HasValue) { float? value = num ?? num2; float? value2 = num2 ?? num; if (value.HasValue && value2.HasValue) { text = ((Math.Abs(value.Value - value2.Value) < 0.001f) ? FormatSignedFloat(value) : (FormatSignedFloat(value) + "-" + FormatSignedFloat(value2))); } } float? value3 = Diff(autoStats.AttackPower, equippedStats.AttackPower); if (text == "-" && value3.HasValue) { text = FormatSignedFloat(value3); } return text; } private static string FormatFloat(float? value) { if (!value.HasValue) { return "-"; } return value.Value.ToString("0.##"); } private static string FormatItemId(int? itemId) { if (!itemId.HasValue || itemId.Value < 0) { return "-"; } return itemId.Value.ToString(); } private static string FormatPercent(float? ratio) { if (!ratio.HasValue) { return "-"; } return (ratio.Value * 100f).ToString("0.#") + "%"; } private static float? Diff(float? left, float? right) { if (!left.HasValue || !right.HasValue) { return null; } return left.Value - right.Value; } private static string FormatSignedFloat(float? value) { if (!value.HasValue) { return "-"; } float value2 = value.Value; if (Math.Abs(value2) < 0.0001f) { return "0"; } string text = Math.Abs(value2).ToString("0.##"); if (!(value2 > 0f)) { return "-" + text; } return "+" + text; } private static string ColorizeAttackSpeedSignedFloat(float? value) { if (!value.HasValue) { return "-"; } float value2 = value.Value; if (Math.Abs(value2) < 0.0001f) { return "0"; } string value3 = Math.Abs(value2).ToString("0.##"); string value4 = ((value2 > 0f) ? "#ff6666" : "#55ff55"); return $"<color={value4}>{((value2 > 0f) ? "+" : "-")}{value3}</color>"; } private static string FormatSignedPercent(float? ratio) { if (!ratio.HasValue) { return "-"; } float num = ratio.Value * 100f; if (Math.Abs(num) < 0.0001f) { return "0%"; } string text = Math.Abs(num).ToString("0.#") + "%"; if (!(num > 0f)) { return "-" + text; } return "+" + text; } private static string FormatTripleSignedPercent(float? a, float? b, float? c) { return $"{FormatSignedPercent(a)}/{FormatSignedPercent(b)}/{FormatSignedPercent(c)}"; } private static string ColorizeSignedTokens(string text) { if (string.IsNullOrEmpty(text)) { return text; } StringBuilder stringBuilder = new StringBuilder(text.Length + 16); int i = 0; while (i < text.Length) { char c = text[i]; if ((c == '+' || c == '-') && i + 1 < text.Length && (char.IsDigit(text[i + 1]) || text[i + 1] == '.')) { int num = i; for (i++; i < text.Length; i++) { char c2 = text[i]; if (!char.IsDigit(c2) && c2 != '.' && c2 != ',' && c2 != '%') { break; } } string text2 = text.Substring(num, i - num); string value = ((text2[0] == '+') ? "#55ff55" : "#ff6666"); stringBuilder.Append("<color=").Append(value).Append(">") .Append(text2) .Append("</color>"); } else { stringBuilder.Append(c); i++; } } return stringBuilder.ToString(); } private static string FormatTriplePercent(float? a, float? b, float? c) { return $"{FormatPercent(a)}/{FormatPercent(b)}/{FormatPercent(c)}"; } } public sealed class ItemStats { public float? Hp; public float? MinDamage; public float? MaxDamage; public float? AttackPower; public float? AttackWait; public float? AttackSpeed; public float? CritRatio; public float? KnockbackRatio; public float? CncRatio; public float? IgniteRatio; public float? FreezeRatio; public float? SleepRatio; public float? ResistIgnite; public float? ResistFreeze; public float? ResistSleep; } public static class OverlayUi { private static GameObject _root; private static Text _text; private static Text _equippedText; private static Text _autoSelectedText; private static Text _differenceText; private static GameObject _autoSelectedPanel; private static GameObject _differencePanel; private static string _content = string.Empty; private static string _equippedContent = string.Empty; private static string _autoSelectedContent = string.Empty; private static string _differenceContent = string.Empty; public static void Ensure() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) if (Plugin.IsDebugBuild && !((Object)(object)_root != (Object)null)) { _root = new GameObject("BetterStatsOverlay"); Object.DontDestroyOnLoad((Object)(object)_root); Canvas obj = _root.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 9999; CanvasScaler obj2 = _root.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.matchWidthOrHeight = 0.5f; _root.AddComponent<GraphicRaycaster>(); _text = CreateText("MainText", new Vector2(20f, 50f), new Vector2(420f, 240f)); _equippedText = CreatePanelWithText("EquippedStatsPanel", "EquippedStatsText", new Vector2(460f, 50f), new Vector2(360f, 240f)); _autoSelectedText = CreatePanelWithText("AutoSelectedStatsPanel", "AutoSelectedStatsText", new Vector2(840f, 50f), new Vector2(360f, 240f)); _differenceText = CreatePanelWithText("DifferenceStatsPanel", "DifferenceStatsText", new Vector2(1220f, 50f), new Vector2(360f, 240f)); _autoSelectedPanel = (((Object)(object)_autoSelectedText != (Object)null) ? ((Component)((Component)_autoSelectedText).transform.parent).gameObject : null); _differencePanel = (((Object)(object)_differenceText != (Object)null) ? ((Component)((Component)_differenceText).transform.parent).gameObject : null); _text.text = _content; _equippedText.text = _equippedContent; _autoSelectedText.text = _autoSelectedContent; _differenceText.text = _differenceContent; } } public static void SetText(string text) { _content = text ?? string.Empty; if ((Object)(object)_text == (Object)null) { Ensure(); } if ((Object)(object)_text != (Object)null) { _text.text = _content; } } public static void SetStatsText(string equipped, string autoSelected, string difference) { _equippedContent = equipped ?? string.Empty; _autoSelectedContent = autoSelected ?? string.Empty; _differenceContent = difference ?? string.Empty; if ((Object)(object)_equippedText == (Object)null || (Object)(object)_autoSelectedText == (Object)null || (Object)(object)_differenceText == (Object)null) { Ensure(); } if ((Object)(object)_equippedText != (Object)null) { _equippedText.text = _equippedContent; } if ((Object)(object)_autoSelectedText != (Object)null) { _autoSelectedText.text = _autoSelectedContent; } if ((Object)(object)_differenceText != (Object)null) { _differenceText.text = _differenceContent; } } public static void SetAutoSelectedVisible(bool visible) { if ((Object)(object)_autoSelectedPanel == (Object)null) { Ensure(); } if ((Object)(object)_autoSelectedPanel != (Object)null) { _autoSelectedPanel.SetActive(visible); } if ((Object)(object)_differencePanel != (Object)null) { _differencePanel.SetActive(visible); } } private static Text CreateText(string name, Vector2 position, Vector2 size) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(_root.transform, false); Text obj = val.AddComponent<Text>(); obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); obj.fontSize = 22; obj.alignment = (TextAnchor)0; ((Graphic)obj).color = new Color(1f, 1f, 1f, 0.95f); obj.supportRichText = true; RectTransform rectTransform = ((Graphic)obj).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 0f); rectTransform.pivot = new Vector2(0f, 0f); rectTransform.anchoredPosition = position; rectTransform.sizeDelta = size; return obj; } private static Text CreatePanelWithText(string panelName, string textName, Vector2 position, Vector2 size) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(panelName); val.transform.SetParent(_root.transform, false); ((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.7f); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 0f); component.anchorMax = new Vector2(0f, 0f); component.pivot = new Vector2(0f, 0f); component.anchoredPosition = position; component.sizeDelta = size; GameObject val2 = new GameObject(textName); val2.transform.SetParent(val.transform, false); Text obj = val2.AddComponent<Text>(); obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); obj.fontSize = 22; obj.alignment = (TextAnchor)0; ((Graphic)obj).color = new Color(1f, 1f, 1f, 0.95f); obj.supportRichText = true; RectTransform rectTransform = ((Graphic)obj).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0f, 0f); rectTransform.offsetMin = new Vector2(8f, 8f); rectTransform.offsetMax = new Vector2(-8f, -8f); return obj; } } public static class ReleaseOverlayUi { private const int LineCount = 12; private static readonly string[] DefaultLines = new string[12] { "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-" }; private static GameObject _root; private static Text[] _texts; private static string[] _content = Array.Empty<string>(); private static bool _visible; private static bool IsReady() { if ((Object)(object)_root == (Object)null) { return false; } if (_texts == null || _texts.Length == 0) { return false; } for (int i = 0; i < _texts.Length; i++) { if ((Object)(object)_texts[i] == (Object)null) { return false; } } return true; } public static void SetVisible(bool visible) { if (_visible == visible) { return; } _visible = visible; if (!_visible) { if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } return; } Ensure(); if ((Object)(object)_root != (Object)null) { _root.SetActive(true); } UpdateTexts(); } public static void Ensure() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_007d: 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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) if (_visible && !IsReady()) { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } _root = new GameObject("BetterStatsReleaseOverlay"); Object.DontDestroyOnLoad((Object)(object)_root); Canvas obj = _root.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 9998; CanvasScaler obj2 = _root.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.matchWidthOrHeight = 0.5f; _root.AddComponent<GraphicRaycaster>(); _texts = (Text[])(object)new Text[12]; Vector2[] array = (Vector2[])(object)new Vector2[12] { new Vector2(-480f, 250f), new Vector2(-480f, 200f), new Vector2(-480f, 150f), new Vector2(-480f, 100f), new Vector2(-480f, 50f), new Vector2(-480f, 0f), new Vector2(35f, 250f), new Vector2(35f, 200f), new Vector2(35f, 150f), new Vector2(35f, 100f), new Vector2(35f, 50f), new Vector2(35f, 0f) }; for (int i = 0; i < 12; i++) { Vector2 anchoredPosition = ((i < array.Length) ? array[i] : Vector2.zero); GameObject val = new GameObject($"ReleaseDifferencePanel{i}"); val.transform.SetParent(_root.transform, false); ((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.5f); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = anchoredPosition; float num = ((i == 1) ? 100f : 80f); component.sizeDelta = new Vector2(num, 32f); GameObject val2 = new GameObject($"ReleaseDifferenceLine{i}"); val2.transform.SetParent(val.transform, false); Text val3 = val2.AddComponent<Text>(); val3.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val3.fontSize = 26; val3.alignment = (TextAnchor)4; ((Graphic)val3).color = new Color(1f, 1f, 1f, 0.98f); val3.supportRichText = true; Shadow obj3 = val2.AddComponent<Shadow>(); obj3.effectColor = new Color(0f, 0f, 0f, 0.9f); obj3.effectDistance = new Vector2(1f, -1f); RectTransform rectTransform = ((Graphic)val3).rectTransform; rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchoredPosition = Vector2.zero; rectTransform.sizeDelta = new Vector2(num, 32f); _texts[i] = val3; } } } public static void SetText(string[] lines) { _content = lines ?? Array.Empty<string>(); if (_visible) { Ensure(); UpdateTexts(); } } private static void UpdateTexts() { if (IsReady() && _texts != null) { string[] array = _content; if (array.Length == 0) { array = DefaultLines; } int num = Math.Min(_texts.Length, array.Length); for (int i = 0; i < num; i++) { _texts[i].text = array[i]; } for (int j = num; j < _texts.Length; j++) { _texts[j].text = string.Empty; } } } } public static class ReflectionUtil { public static int? TryGetInt(object obj, string name) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return Convert.ToInt32(field.GetValue(obj)); } catch { } } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { try { return Convert.ToInt32(property.GetValue(obj, null)); } catch { } } return null; } public static float? TryGetFloat(object obj, string name) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return Convert.ToSingle(field.GetValue(obj)); } catch { } } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { try { return Convert.ToSingle(property.GetValue(obj, null)); } catch { } } return null; } public static string TryGetString(object obj, string name) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return Convert.ToString(field.GetValue(obj)); } catch { } } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { try { return Convert.ToString(property.GetValue(obj, null)); } catch { } } return null; } public static bool? TryGetBool(object obj, string name) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return Convert.ToBoolean(field.GetValue(obj)); } catch { } } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { try { return Convert.ToBoolean(property.GetValue(obj, null)); } catch { } } return null; } public static int? TryGetStaticInt(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return Convert.ToInt32(field.GetValue(null)); } catch { } } PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { try { return Convert.ToInt32(property.GetValue(null, null)); } catch { } } return null; } public static int TryGetListCount(object listObj) { if (listObj == null) { return 0; } if (listObj is IList list) { return list.Count; } Type type = listObj.GetType(); PropertyInfo property = type.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { try { return Convert.ToInt32(property.GetValue(listObj, null)); } catch { } } MethodInfo method = type.GetMethod("get_Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { try { return Convert.ToInt32(method.Invoke(listObj, null)); } catch { } } return 0; } public static bool? TryInvokeBool(object obj, string name, params object[] args) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (string.Equals(methodInfo2.Name, name, StringComparison.Ordinal) && !(methodInfo2.ReturnType != typeof(bool)) && methodInfo2.GetParameters().Length == args.Length) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { return null; } try { object obj2 = methodInfo.Invoke(obj, args); bool flag = default(bool); int num; if (obj2 is bool) { flag = (bool)obj2; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return null; } } public static object TryGetObject(object obj, string name) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } Type type = obj.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return field.GetValue(obj); } catch { } }