Decompiled source of MonsterPathVisualisation v0.4.4
MonsterPathVisualisation.dll
Decompiled a month 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.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppTMPro; using Kp0hyc.Labyrinthine.Shared; using MelonLoader; using MelonLoader.Preferences; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(MonsterPathVisualisation), "Monster Path Visualisation", "0.4.4", "kp0hyc", null)] [assembly: MelonGame("Valko Game Studios", "Labyrinthine")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("MonsterPathVisualisation")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MonsterPathVisualisation")] [assembly: AssemblyTitle("MonsterPathVisualisation")] [assembly: AssemblyVersion("1.0.0.0")] public class MonsterPathVisualisation : MelonMod { private sealed class NameListSecondaryPanelState { public readonly int InstanceId; public readonly Vector2 SizeDelta; public readonly Vector2 AnchoredPosition; public readonly Vector3 LocalPosition; public readonly Vector2 Pivot; public NameListSecondaryPanelState(GameObject panel, RectTransform rectTransform) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) InstanceId = ((!((Object)(object)panel == (Object)null)) ? ((Object)panel).GetInstanceID() : 0); SizeDelta = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.sizeDelta); AnchoredPosition = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.anchoredPosition); LocalPosition = (Vector3)(((Object)(object)rectTransform == (Object)null) ? default(Vector3) : ((Transform)rectTransform).localPosition); Pivot = (Vector2)(((Object)(object)rectTransform == (Object)null) ? new Vector2(0.5f, 0.5f) : rectTransform.pivot); } public void Restore(RectTransform rectTransform) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rectTransform == (Object)null)) { rectTransform.sizeDelta = SizeDelta; rectTransform.anchoredPosition = AnchoredPosition; ((Transform)rectTransform).localPosition = LocalPosition; } } } private sealed class NameListRowTransformState { public readonly Vector2 AnchoredPosition; public readonly Vector3 LocalPosition; public readonly Vector2 AnchorMin; public readonly Vector2 AnchorMax; public readonly Vector2 Pivot; public readonly Vector2 SizeDelta; public readonly Vector3 LocalScale; public readonly Quaternion LocalRotation; public NameListRowTransformState(RectTransform rectTransform) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_010e: Unknown result type (might be due to invalid IL or missing references) AnchoredPosition = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.anchoredPosition); LocalPosition = (Vector3)(((Object)(object)rectTransform == (Object)null) ? default(Vector3) : ((Transform)rectTransform).localPosition); AnchorMin = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.anchorMin); AnchorMax = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.anchorMax); Pivot = (Vector2)(((Object)(object)rectTransform == (Object)null) ? new Vector2(0.5f, 0.5f) : rectTransform.pivot); SizeDelta = (Vector2)(((Object)(object)rectTransform == (Object)null) ? default(Vector2) : rectTransform.sizeDelta); LocalScale = (Vector3)(((Object)(object)rectTransform == (Object)null) ? new Vector3(1f, 1f, 1f) : ((Transform)rectTransform).localScale); LocalRotation = (((Object)(object)rectTransform == (Object)null) ? Quaternion.identity : ((Transform)rectTransform).localRotation); } } private sealed class ExtraNameListRow { public readonly GameObject Element; public readonly object Text; public ExtraNameListRow(GameObject element, object text) { Element = element; Text = text; } } private readonly struct PathEntry { public readonly string Key; public readonly string Label; public readonly TrailColor Color; public PathEntry(string key, string label, TrailColor color) { Key = key; Label = label; Color = color; } } private readonly struct TrailColor { public readonly byte R; public readonly byte G; public readonly byte B; public readonly byte A; public TrailColor(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } } private sealed class MonsterIcon { public readonly string Key; public readonly int Width; public readonly int Height; public readonly TrailColor[] Pixels; public MonsterIcon(string key, int width, int height, TrailColor[] pixels) { Key = key; Width = width; Height = height; Pixels = pixels; } } private readonly struct MonsterIconDrawRequest { public readonly int PixelX; public readonly int PixelY; public readonly int Number; public readonly TrailColor Color; public readonly string Label; public MonsterIconDrawRequest(int pixelX, int pixelY, int number, TrailColor color, string label) { PixelX = pixelX; PixelY = pixelY; Number = number; Color = color; Label = label; } } private readonly struct PlayerIconDrawRequest { public readonly int PixelX; public readonly int PixelY; public readonly string Key; public readonly TrailColor Color; public PlayerIconDrawRequest(int pixelX, int pixelY, string key, TrailColor color) { PixelX = pixelX; PixelY = pixelY; Key = key; Color = color; } } private readonly struct WorldPoint { public readonly float X; public readonly float Z; public readonly float Time; public WorldPoint(float x, float z, float time = 0f) { X = x; Z = z; Time = time; } } private sealed class MonsterTrail { private readonly List<WorldPoint> _points = new List<WorldPoint>(); private readonly HashSet<int> _segmentStarts = new HashSet<int>(); private readonly List<int> _deathIndexes = new List<int>(); private bool _wasDead; public readonly int ColorIndex; public string Label { get; private set; } public int Count => _points.Count; public bool HasDeaths => _deathIndexes.Count > 0; public float DurationSeconds { get { if (_points.Count != 0) { return _points[_points.Count - 1].Time; } return 0f; } } public MonsterTrail(string label, int colorIndex) { Label = label; ColorIndex = colorIndex; } public void UpdateLabelIfBetter(string label) { if (!string.IsNullOrWhiteSpace(label) && (string.IsNullOrWhiteSpace(Label) || string.Equals(Label, "Player", StringComparison.OrdinalIgnoreCase) || string.Equals(Label, "Monster", StringComparison.OrdinalIgnoreCase) || string.Equals(Label, "Local Player", StringComparison.OrdinalIgnoreCase))) { Label = label; } } public void Add(float x, float z, float elapsedSeconds, float minTeleportDistance) { WorldPoint worldPoint = new WorldPoint(x, z, elapsedSeconds); if (_points.Count > 0) { WorldPoint a = _points[_points.Count - 1]; float num = Distance(a, worldPoint); if (num < 0.2f && Math.Abs(elapsedSeconds - a.Time) < 0.001f) { return; } if (num > minTeleportDistance) { _segmentStarts.Add(_points.Count); } } _points.Add(worldPoint); } public bool UpdateDeadState(bool isDead) { if (isDead && !_wasDead && _points.Count > 0) { int num = _points.Count - 1; if (_deathIndexes.Count == 0 || _deathIndexes[_deathIndexes.Count - 1] != num) { _deathIndexes.Add(num); } _wasDead = true; return true; } _wasDead = isDead; return false; } public void GetReplayPoints(float normalizedTime, float replayDurationSeconds, float maxStepWorldSpace, List<WorldPoint> output) { output.Clear(); if (_points.Count == 0) { return; } float targetTime = ValueConversion.Clamp01(normalizedTime) * Math.Max(0.001f, replayDurationSeconds); int lastIndexAtOrBefore = GetLastIndexAtOrBefore(targetTime); if (lastIndexAtOrBefore < 0) { return; } output.Add(_points[0]); for (int i = 1; i <= lastIndexAtOrBefore; i++) { WorldPoint previous = _points[i - 1]; WorldPoint worldPoint = _points[i]; if (_segmentStarts.Contains(i)) { output.Add(worldPoint); } else { AddInterpolated(previous, worldPoint, maxStepWorldSpace, output); } } } public bool TryGetReplayPoint(float normalizedTime, float replayDurationSeconds, out WorldPoint point) { point = default(WorldPoint); if (_points.Count == 0) { return false; } float num = ValueConversion.Clamp01(normalizedTime) * Math.Max(0.001f, replayDurationSeconds); int lastIndexAtOrBefore = GetLastIndexAtOrBefore(num); if (lastIndexAtOrBefore < 0) { return false; } if (lastIndexAtOrBefore >= _points.Count - 1) { point = _points[lastIndexAtOrBefore]; return true; } int num2 = lastIndexAtOrBefore + 1; if (_segmentStarts.Contains(num2)) { point = _points[lastIndexAtOrBefore]; return true; } WorldPoint worldPoint = _points[lastIndexAtOrBefore]; WorldPoint worldPoint2 = _points[num2]; float num3 = worldPoint2.Time - worldPoint.Time; if (num3 <= 0.001f) { point = worldPoint; return true; } float num4 = ValueConversion.Clamp01((num - worldPoint.Time) / num3); point = new WorldPoint(worldPoint.X + (worldPoint2.X - worldPoint.X) * num4, worldPoint.Z + (worldPoint2.Z - worldPoint.Z) * num4, num); return true; } public bool TryGetVisibleDeathPoint(float normalizedTime, float replayDurationSeconds, out WorldPoint point, out int deathIndex) { point = default(WorldPoint); deathIndex = -1; if (_points.Count == 0 || _deathIndexes.Count == 0) { return false; } float targetTime = ValueConversion.Clamp01(normalizedTime) * Math.Max(0.001f, replayDurationSeconds); int lastIndexAtOrBefore = GetLastIndexAtOrBefore(targetTime); if (lastIndexAtOrBefore < 0) { return false; } for (int i = 0; i < _deathIndexes.Count; i++) { int num = _deathIndexes[i]; if (num <= lastIndexAtOrBefore) { deathIndex = i; point = _points[Math.Max(0, Math.Min(_points.Count - 1, num))]; } } return deathIndex >= 0; } public void GetVisibleDeathIndexes(float normalizedTime, float replayDurationSeconds, List<int> output) { output.Clear(); if (_points.Count == 0 || _deathIndexes.Count == 0) { return; } float targetTime = ValueConversion.Clamp01(normalizedTime) * Math.Max(0.001f, replayDurationSeconds); int lastIndexAtOrBefore = GetLastIndexAtOrBefore(targetTime); if (lastIndexAtOrBefore < 0) { return; } for (int i = 0; i < _deathIndexes.Count; i++) { if (_deathIndexes[i] <= lastIndexAtOrBefore) { output.Add(i); } } } private int GetLastIndexAtOrBefore(float targetTime) { int result = -1; int num = 0; int num2 = _points.Count - 1; while (num <= num2) { int num3 = num + (num2 - num) / 2; if (_points[num3].Time <= targetTime) { result = num3; num = num3 + 1; } else { num2 = num3 - 1; } } return result; } private static void AddInterpolated(WorldPoint previous, WorldPoint current, float maxStepWorldSpace, List<WorldPoint> output) { float num = Distance(previous, current); if (!(num <= 0.001f)) { int num2 = Math.Max(1, (int)Math.Ceiling(num / Math.Max(0.25f, maxStepWorldSpace))); for (int i = 1; i <= num2; i++) { float num3 = (float)i / (float)num2; output.Add(new WorldPoint(previous.X + (current.X - previous.X) * num3, previous.Z + (current.Z - previous.Z) * num3, previous.Time + (current.Time - previous.Time) * num3)); } } } private static float Distance(WorldPoint a, WorldPoint b) { float num = a.X - b.X; float num2 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2); } } private const string HarmonyId = "kp0hyc.monsterpathvisualisation"; private const string PrefCategoryName = "MonsterPathVisualisation"; private const string MonsterObjectNamesResourceName = "MonsterPathVisualisation.MonsterNames.json"; private const int BuiltInNameListSlotCount = 8; private const float PlaybackLoopSeconds = 5f; private const float PlaybackEndHoldSeconds = 1.5f; private const float SaveIntervalSeconds = 0.45f; private const float MinTeleportDistanceWorld = 35f; private const int TrailExtraPixels = 0; private const int MinimumFallbackPlayerTrailSamples = 30; private const int NameListSecondaryLabelDownshiftRows = 0; private static readonly Dictionary<string, MonsterTrail> s_trails = new Dictionary<string, MonsterTrail>(); private static readonly Dictionary<string, MonsterTrail> s_playerTrails = new Dictionary<string, MonsterTrail>(); private static readonly Dictionary<string, MonsterIcon> s_playerIcons = new Dictionary<string, MonsterIcon>(StringComparer.Ordinal); private static readonly Dictionary<string, DateTime> s_playerIconRetryUtc = new Dictionary<string, DateTime>(StringComparer.Ordinal); private static readonly List<object> s_monsterLookupBuffer = new List<object>(); private static readonly List<object> s_playerLookupBuffer = new List<object>(); private static readonly List<WorldPoint> s_replayBuffer = new List<WorldPoint>(); private static readonly List<int> s_replayDeathIndexes = new List<int>(); private static readonly List<PlayerIconDrawRequest> s_playerIconDrawRequests = new List<PlayerIconDrawRequest>(); private static readonly List<PathEntry> s_playerEntries = new List<PathEntry>(); private static readonly List<PathEntry> s_monsterEntries = new List<PathEntry>(); private static readonly List<PathEntry> s_nameListEntries = new List<PathEntry>(); private static readonly List<ExtraNameListRow> s_extraNameListRows = new List<ExtraNameListRow>(); private static readonly Dictionary<int, NameListRowTransformState> s_nameListRowTransformStates = new Dictionary<int, NameListRowTransformState>(); private static readonly Dictionary<string, DateTime> s_nextNameListDiagnosticsBySignature = new Dictionary<string, DateTime>(StringComparer.Ordinal); private static readonly Dictionary<string, DateTime> s_nextRectTransformDiagnosticsBySignature = new Dictionary<string, DateTime>(StringComparer.Ordinal); private static readonly Dictionary<string, DateTime> s_nextPlayerTrailDiagnosticsBySignature = new Dictionary<string, DateTime>(StringComparer.Ordinal); private static readonly List<object> s_cachedMonsterGameObjectSourceObjects = new List<object>(); private static readonly Dictionary<string, bool> s_pathVisibility = new Dictionary<string, bool>(); private static readonly Dictionary<string, MonsterIcon> s_monsterIcons = new Dictionary<string, MonsterIcon>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, string> s_knownMonsterObjectNameMap; private static readonly Dictionary<string, string> s_monsterIconAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "ashoni", "oni" }, { "paleoni", "oni" }, { "palewitch", "witch" }, { "blackpigman", "pigman" }, { "smolderingwickerman", "wickerman" }, { "smoulderingwickerman", "wickerman" }, { "trenchstalker", "stalker" }, { "cryptstalker", "stalker" }, { "fastzombie", "zombie" }, { "slowzombie", "zombie" }, { "crawlingzombie", "zombie" }, { "bride", "bridelady" }, { "lady", "bridelady" } }; private static readonly TrailColor[] s_palette = new TrailColor[6] { new TrailColor(byte.MaxValue, 62, 42, 178), new TrailColor(byte.MaxValue, 151, 32, 178), new TrailColor(224, 46, byte.MaxValue, 178), new TrailColor(72, 216, byte.MaxValue, 178), new TrailColor(byte.MaxValue, 230, 58, 178), new TrailColor(116, byte.MaxValue, 92, 178) }; private static MelonPreferences_Entry<bool> s_verboseLogs; private static MelonPreferences_Entry<bool> s_debugPlayerPath; private static DateTime s_nextRecordUtc = DateTime.MinValue; private static DateTime s_recordingStartUtc = DateTime.MinValue; private static DateTime s_nextReflectionWarningUtc = DateTime.MinValue; private static DateTime s_nextDrawWarningUtc = DateTime.MinValue; private static int s_nextColorIndex; private static int s_lastLoggedActiveCount = -1; private static int s_lastLoggedTrailCount = -1; private static bool s_loggedFirstDraw; private static bool s_paused; private static bool s_showFullPath = true; private static bool s_monsterIconsEnabled = true; private static float s_scrubTime; private static float s_playbackSpeed = 1f; private static DateTime s_lastPlaybackUpdateUtc = DateTime.MinValue; private static DateTime s_nextPlaybackRedrawUtc = DateTime.MinValue; private static DateTime s_playbackEndHoldUntilUtc = DateTime.MinValue; private static DateTime s_lastEndMapDrawUtc = DateTime.MinValue; private static object s_lastUiInstance; private static object s_extraNameListUi; private static NameListSecondaryPanelState s_nameListSecondaryPanelState; private static object s_lastTexture; private static object s_lastPathRecorders; private static string s_lastPathRecordersSource; private static object s_lastTilePositionOffset; private static float s_lastPixelsPerTile; private static bool s_inRealMazeScene; private static string s_currentRealMazeSceneName; private static DateTime s_nextMonsterGameObjectSourceScanUtc = DateTime.MinValue; private static int s_fastMonsterGameObjectSourceScansRemaining; private static string s_lastMonsterGameObjectSourceDumpSignature; private static DateTime s_nextMonsterGameObjectSourceDumpUtc = DateTime.MinValue; private static Type s_pathVisualisationUiType; private static Type s_pathVisualisationManagerType; private static Type s_mazeGeneratorType; private static Type s_playerNetworkSyncType; private static Type s_gameManagerType; private static Type s_endScreenUiType; private static Type s_aiControllerType; private static Type s_monsterNetworkSyncType; private static Type s_color32Type; private static Type s_rectType; private static Type s_colorType; private static Type s_guiType; private static Type s_texture2DType; private static Type s_textureType; private static Type s_screenType; private static Type s_inputType; private static MethodInfo s_addPlayersToTextureMethod; private static MethodInfo s_addPlayerDotMethod; private static MethodInfo s_guiDrawTexture; private static MethodInfo s_guiLabel; private static MethodInfo s_guiButton; private static MethodInfo s_guiToggle; private static MethodInfo s_guiHorizontalSlider; private static MethodInfo s_textureSetPixels; private static MethodInfo s_textureSetPixel; private static MethodInfo s_textureApply; private static MethodInfo s_inputGetMouseButtonDown; private static MethodInfo s_clearTextureCoroutineMethod; private static PropertyInfo s_guiColorProperty; private static PropertyInfo s_guiSkinProperty; private static PropertyInfo s_whiteTextureProperty; private static PropertyInfo s_screenWidthProperty; private static PropertyInfo s_screenHeightProperty; private static PropertyInfo s_inputMousePositionProperty; private static PropertyInfo s_textureWidthProperty; private static PropertyInfo s_textureHeightProperty; private static ConstructorInfo s_color32Ctor; private static ConstructorInfo s_rectCtor; private static ConstructorInfo s_colorCtor; private static object s_whiteTexture; private static object s_clearColor; private static Array s_clearPixels; private static int s_clearPixelsWidth; private static int s_clearPixelsHeight; private static bool s_unityReflectionReady; private static bool s_textureSetPixelsFailed; private static bool s_clearWarningLogged; private static bool s_allowTextureClearCoroutine; private static bool s_internalGamePlayerDraw; private static Texture2D s_batchTexture; private static Il2CppStructArray<Color32> s_batchPixels; private static int s_batchWidth; private static int s_batchHeight; private static bool s_batchDrawActive; private static DateTime s_nextBatchWarningUtc = DateTime.MinValue; private static DateTime s_nextGuiWarningUtc = DateTime.MinValue; private static DateTime s_nextPlayerDebugLogUtc = DateTime.MinValue; private static DateTime s_nextPlayerRenderStatusUtc = DateTime.MinValue; private static DateTime s_nextMonsterDiagnosticsUtc = DateTime.MinValue; private static DateTime s_nextPlayerNameDiagnosticsUtc = DateTime.MinValue; private static DateTime s_nextEndMapDiagnosticsUtc = DateTime.MinValue; private static DateTime s_nextMonsterIconDebugUtc = DateTime.MinValue; private static DateTime s_nextPlayerIconDebugUtc = DateTime.MinValue; private static DateTime s_nextNameListStructureDiagnosticsUtc = DateTime.MinValue; private static DateTime s_nextNameListPlacementDiagnosticsUtc = DateTime.MinValue; private static DateTime s_nextRectTransformDiagnosticsUtc = DateTime.MinValue; private static string s_lastPlayerRenderStatus; private static string s_lastMonsterDiagnosticsStatus; private static string s_lastPlayerNameDiagnosticsStatus; private static string s_lastEndMapDiagnosticsStatus; private static string s_lastNameListStructureDiagnosticsStatus; private static string s_lastNameListPlacementDiagnosticsStatus; private static float s_cachedNameListRowSpacing; private static string s_cachedNameListRowSpacingSource; private static int s_cachedNameListRowSpacingPanelInstanceId; private static int s_lastLoggedPlayerTrailCount = -1; private static int s_lastMonsterIconDrawCount; private static bool s_monsterIconsLoaded; private static bool s_scaledImguiFontActive; private static int s_savedGuiButtonFontSize; private static int s_savedGuiLabelFontSize; private static int s_savedGuiToggleFontSize; private static void DrawControlPanel() { float scale = UiScale(); BeginScaledImguiFont(scale); try { float height = S(84f, scale); GetControlPanelLayout(height, out var x, out var y, out var width); DrawSolid(x, y, width, height, Color(0.02f, 0.02f, 0.02f, 0.58f)); if (Button(x + S(10f, scale), y + S(8f, scale), S(86f, scale), S(22f, scale), s_paused ? "Resume" : "Pause")) { s_paused = !s_paused; ResetPlaybackClock(); RedrawLastTexture(); } if (Button(x + S(104f, scale), y + S(8f, scale), S(124f, scale), S(22f, scale), s_showFullPath ? "Full path" : "Current dots")) { s_showFullPath = !s_showFullPath; RedrawLastTexture(); } if (Button(x + S(238f, scale), y + S(8f, scale), S(96f, scale), S(22f, scale), ShouldUseMonsterIcons() ? "Icons on" : "Icons off")) { bool flag = (s_monsterIconsEnabled = !ShouldUseMonsterIcons()); s_lastMonsterIconDrawCount = 0; LogMonsterIconStatus("toggle:" + flag, "enabled=" + flag + " fullPath=" + s_showFullPath + " trails=" + s_trails.Count); RedrawLastTexture(); } Label(x + S(10f, scale), y + S(36f, scale), S(48f, scale), S(20f, scale), "speed"); DrawPlaybackSpeedButton(x + S(60f, scale), y + S(34f, scale), S(46f, scale), S(22f, scale), 1f, "1x"); DrawPlaybackSpeedButton(x + S(112f, scale), y + S(34f, scale), S(56f, scale), S(22f, scale), 0.5f, "0.5x"); DrawPlaybackSpeedButton(x + S(174f, scale), y + S(34f, scale), S(66f, scale), S(22f, scale), 0.25f, "0.25x"); DrawPlaybackSpeedButton(x + S(246f, scale), y + S(34f, scale), S(56f, scale), S(22f, scale), 0.1f, "0.1x"); Label(x + S(314f, scale), y + S(36f, scale), S(92f, scale), S(20f, scale), "time " + (int)(ValueConversion.Clamp01(s_scrubTime) * 100f) + "%"); float num = HorizontalSlider(x + S(10f, scale), y + S(60f, scale), width - S(20f, scale), S(18f, scale), ValueConversion.Clamp01(s_scrubTime), 0f, 1f); if (Math.Abs(num - s_scrubTime) > 0.001f) { s_scrubTime = ValueConversion.Clamp01(num); s_paused = true; ResetPlaybackClock(); RedrawLastTexture(); } DrawNameList("ongui"); } finally { EndScaledImguiFont(); } } private static bool ShouldUseMonsterIcons() { return s_monsterIconsEnabled; } private static void DrawPlaybackSpeedButton(float x, float y, float width, float height, float speed, string label) { bool flag = Math.Abs(s_playbackSpeed - speed) < 0.001f; if (Button(x, y, width, height, flag ? ("[" + label + "]") : label)) { SetPlaybackSpeed(speed); } } private static void SetPlaybackSpeed(float speed) { s_playbackSpeed = ValueConversion.Clamp(speed, 0.05f, 4f); ResetPlaybackClock(); } private static void ResetPlaybackClock() { s_lastPlaybackUpdateUtc = DateTime.MinValue; s_nextPlaybackRedrawUtc = DateTime.MinValue; s_playbackEndHoldUntilUtc = DateTime.MinValue; } private static void GetControlPanelLayout(float height, out float x, out float y, out float width) { float scale = UiScale(); x = S(70f, scale); y = S(12f, scale); width = Math.Min(500f, (float)ScreenWidth() - x * 2f); if (TryGetPathImageGuiRect(s_lastUiInstance, out var x2, out var y2, out var width2, out var _)) { float val = S(500f, scale); float val2 = S(460f, scale); float num = Math.Min(val, Math.Max(val2, width2)); float value = x2 + (width2 - num) * 0.5f; float num2 = y2 - height - S(8f, scale); if (!(num2 < S(8f, scale))) { width = Math.Min(num, (float)ScreenWidth() - S(24f, scale)); x = ValueConversion.Clamp(value, S(12f, scale), Math.Max(S(12f, scale), (float)ScreenWidth() - width - S(12f, scale))); y = num2; } } } private static bool TryGetPathImageGuiRect(object ui, out float x, out float y, out float width, out float height) { x = 0f; y = 0f; width = 0f; height = 0f; try { object fieldOrProp = Il2CppReflection.GetFieldOrProp(ui, "pathImage"); RawImage val = (RawImage)((fieldOrProp is RawImage) ? fieldOrProp : null); RectTransform val2 = (((Object)(object)val == (Object)null) ? null : ((Graphic)val).rectTransform); if ((Object)(object)val2 == (Object)null) { return false; } object fieldOrProp2 = Il2CppReflection.GetFieldOrProp(val2, "rect"); object fieldOrProp3 = Il2CppReflection.GetFieldOrProp(val2, "position"); object fieldOrProp4 = Il2CppReflection.GetFieldOrProp(val2, "pivot"); object fieldOrProp5 = Il2CppReflection.GetFieldOrProp(val2, "lossyScale"); if (!ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp2, "width"), out var result) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp2, "height"), out var result2) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp3, "x"), out var result3) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp3, "y"), out var result4) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp4, "x"), out var result5) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp4, "y"), out var result6)) { return false; } float result7; float num = (ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp5, "x"), out result7) ? Math.Abs(result7) : 1f); float result8; float num2 = (ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(fieldOrProp5, "y"), out result8) ? Math.Abs(result8) : 1f); float num3 = Math.Abs(result * num); float num4 = Math.Abs(result2 * num2); float num5 = result3 - result5 * num3; float num6 = num5 + num3; float num7 = result4 - result6 * num4; float num8 = num7 + num4; if (num6 <= num5 || num8 <= num7) { return false; } x = num5; y = (float)ScreenHeight() - num8; width = num6 - num5; height = num8 - num7; return width > 40f && height > 40f; } catch { return false; } } private static bool TryGetTextureDimensions(object texture, out int width, out int height) { width = 0; height = 0; if (texture == null) { return false; } try { Type type = texture.GetType(); if ((object)s_textureWidthProperty == null) { s_textureWidthProperty = type.GetProperty("width", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if ((object)s_textureHeightProperty == null) { s_textureHeightProperty = type.GetProperty("height", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } return ValueConversion.TryConvertInt(s_textureWidthProperty?.GetValue(texture), out width) && ValueConversion.TryConvertInt(s_textureHeightProperty?.GetValue(texture), out height) && width > 0 && height > 0; } catch { return false; } } public override void OnInitializeMelon() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_00b6: 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_00de: Expected O, but got Unknown //IL_00de: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown try { MelonPreferences_Category obj = MelonPreferences.CreateCategory("MonsterPathVisualisation"); s_verboseLogs = obj.CreateEntry<bool>("VerboseLogs", false, "Log detailed MonsterPathVisualisation diagnostics.", (string)null, false, false, (ValueValidator)null, (string)null); s_debugPlayerPath = obj.CreateEntry<bool>("DebugPlayerPath", false, "Log focused player path diagnostics.", (string)null, false, false, (ValueValidator)null, (string)null); Harmony val = new Harmony("kp0hyc.monsterpathvisualisation"); Type pathVisualisationUiType = GetPathVisualisationUiType(); MethodInfo methodInfo = (s_addPlayersToTextureMethod = pathVisualisationUiType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddPlayersToTexture" && m.GetParameters().Length == 5)); if (methodInfo == null) { MelonLogger.Warning("Could not find PathVisualisationUI.AddPlayersToTexture; monster paths will not draw on the end map."); } else { val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(MonsterPathVisualisation).GetMethod("AddPlayersToTexturePrefix", BindingFlags.Static | BindingFlags.NonPublic)), new HarmonyMethod(typeof(MonsterPathVisualisation).GetMethod("AddPlayersToTexturePostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogVerbose("Patched PathVisualisationUI.AddPlayersToTexture."); } MethodInfo methodInfo2 = pathVisualisationUiType?.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((Type t) => t.Name.IndexOf("ClearTextureCoroutine", StringComparison.OrdinalIgnoreCase) >= 0)?.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(MonsterPathVisualisation).GetMethod("ClearTextureCoroutineMoveNextPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogVerbose("Patched PathVisualisationUI.ClearTextureCoroutine.MoveNext."); } LogVerbose("MonsterPathVisualisation initialized."); } catch (Exception ex) { MelonLogger.Error("Init failed: " + ex); } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (string.Equals(sceneName, GetActiveSceneName(), StringComparison.OrdinalIgnoreCase)) { ResetRecording(); UpdateRealMazeSceneState(sceneName); } } public override void OnUpdate() { try { DateTime utcNow = DateTime.UtcNow; DriveEndMapPlayback(utcNow); HandleNameListClickProbe(utcNow); if (!(utcNow < s_nextRecordUtc)) { s_nextRecordUtc = utcNow.AddSeconds(ValueConversion.Clamp(0.45f, 0.1f, 5f)); if (!IsMazeAvailable()) { LogMonsterDiagnostics("record-skip:no-maze", "recording skipped because maze data is unavailable; playerTrails=" + s_playerTrails.Count + " monsterTrails=" + s_trails.Count); return; } if (!IsRealMazeRunActive()) { LogMonsterDiagnostics("record-skip:not-real-maze", "recording skipped because active scene is not a real loaded maze; playerTrails=" + s_playerTrails.Count + " monsterTrails=" + s_trails.Count); return; } if (IsCaseOverActive()) { LogMonsterDiagnostics("record-skip:case-over", "recording skipped because case-over screen is active; playerTrails=" + s_playerTrails.Count + " monsterTrails=" + s_trails.Count); return; } float recordingElapsedSeconds = GetRecordingElapsedSeconds(utcNow); RecordCurrentPlayers(recordingElapsedSeconds); RecordCurrentMonsters(recordingElapsedSeconds); } } catch (Exception ex) { LogReflectionWarning("recording failed: " + ex.GetType().Name + " " + ex.Message); } } public override void OnGUI() { if (s_lastUiInstance == null || (DateTime.UtcNow - s_lastEndMapDrawUtc).TotalSeconds > 30.0 || !EnsureUnityReflection()) { return; } try { DrawControlPanel(); } catch (Exception ex) { if (DateTime.UtcNow >= s_nextGuiWarningUtc) { s_nextGuiWarningUtc = DateTime.UtcNow.AddSeconds(10.0); MelonLogger.Warning("control panel draw failed: " + ex.GetType().Name + " " + ex.Message); } } } private static void Increment(Dictionary<string, int> counts, string key) { if (counts != null) { key = (string.IsNullOrWhiteSpace(key) ? "unknown" : key); counts.TryGetValue(key, out var value); counts[key] = value + 1; } } private static void SetCount(Dictionary<string, int> counts, string key, int amount) { if (counts != null) { counts[string.IsNullOrWhiteSpace(key) ? "unknown" : key] = amount; } } private static string FormatCounts(Dictionary<string, int> counts) { if (counts == null || counts.Count == 0) { return "{}"; } return "{" + string.Join(", ", from p in counts orderby p.Key select p.Key + "=" + p.Value) + "}"; } private static bool AddPlayersToTexturePrefix(object __instance, object[] __args, [HarmonyArgument(2)] ref float normalizedTime, ref bool __state) { __state = false; try { if (__instance == null || __args == null || __args.Length < 5) { return true; } if (s_internalGamePlayerDraw) { LogPlayerRenderStatus("native-internal", "native internal call"); return true; } float num = (normalizedTime = GetEffectiveNormalizedTime(normalizedTime)); __args[2] = num; s_lastUiInstance = __instance; s_lastEndMapDrawUtc = DateTime.UtcNow; string source; object obj = ResolvePathRecorders(__instance, __args[1], out source); object[] array = (object[])__args.Clone(); array[1] = obj; RememberDrawArgs(array, source); RefreshPlayerEntries(__instance, obj, source); RefreshMonsterEntries(); SyncGameNameList(__instance, "addplayers-prefix"); int collectionCount = Il2CppReflection.GetCollectionCount(obj); LogEndMapDiagnostics("state:" + collectionCount + ":" + s_playerTrails.Count + ":" + s_trails.Count + ":" + s_showFullPath + ":" + s_paused, "end-map state recorders=" + collectionCount + " recorderSource=" + source + " playerTrails=" + s_playerTrails.Count + " monsterTrails=" + s_trails.Count + " playerEntries=" + s_playerEntries.Count + " monsterEntries=" + s_monsterEntries.Count + " fullPath=" + s_showFullPath + " paused=" + s_paused + " speed=" + s_playbackSpeed.ToString("0.##") + " time=" + ValueConversion.Clamp01(num).ToString("0.00")); bool flag = ShouldUseCustomPlayerDraw(); if (s_paused || flag) { HideAllDeathIcons(__instance); } if (!flag) { LogPlayerRenderStatus("native-custom-disabled", "native renderer because custom player draw is disabled"); return true; } bool flag2 = BeginPathTextureBatch(__args[0]); if (!flag2 && !ClearPathTexture(__instance, __args[0])) { LogPlayerRenderStatus("fallback-clear-failed", "native renderer fallback because texture clear failed"); return true; } if (!DrawSelectedPlayers(__instance, array, num, !s_showFullPath)) { if (flag2) { AbortPathTextureBatch(); } LogPlayerRenderStatus("fallback-player-draw-failed", "native renderer fallback because custom player draw failed"); return true; } DrawSelectedMonsters(__instance, array, num); DrawQueuedPlayerTextureIcons(__instance, array[0]); if (flag2) { if (!FinishPathTextureBatch()) { ClearPathTexture(__instance, __args[0]); LogPlayerRenderStatus("fallback-batch-upload-failed", "native renderer fallback because batch texture upload failed"); return true; } } else { ApplyTexture(__args[0]); } __state = true; LogPlayerRenderStatus("custom:" + flag2 + ":" + s_showFullPath + ":" + 0, "custom renderer mode=" + (s_showFullPath ? "full path" : "current dots") + " batch=" + flag2 + " extraPixels=" + 0 + " time=" + ValueConversion.Clamp01(num).ToString("0.00")); return false; } catch (Exception ex) { AbortPathTextureBatch(); if (DateTime.UtcNow >= s_nextDrawWarningUtc) { s_nextDrawWarningUtc = DateTime.UtcNow.AddSeconds(10.0); MelonLogger.Warning("end-map player draw override failed: " + ex.GetType().Name + " " + ex.Message); } return true; } } private static bool ClearTextureCoroutineMoveNextPrefix(ref bool __result) { if (s_allowTextureClearCoroutine) { return true; } if (s_lastUiInstance != null && ShouldBlockGameTextureClear() && (DateTime.UtcNow - s_lastEndMapDrawUtc).TotalSeconds < 30.0) { __result = false; return false; } return true; } private static void RememberDrawArgs(object[] args, string pathRecordersSource) { if (args != null && args.Length >= 5) { s_lastTexture = args[0]; s_lastPathRecorders = args[1]; s_lastPathRecordersSource = pathRecordersSource; s_lastTilePositionOffset = args[3]; if (ValueConversion.TryConvertFloat(args[4], out var result)) { s_lastPixelsPerTile = result; } } } private static void DriveEndMapPlayback(DateTime now) { if (s_paused || !HasRecentEndMapContext(now)) { s_lastPlaybackUpdateUtc = DateTime.MinValue; return; } float num = s_scrubTime; AdvancePlaybackClock(now); if (!(Math.Abs(s_scrubTime - num) < 0.0001f) && !((now - s_lastEndMapDrawUtc).TotalSeconds < 0.03) && !(now < s_nextPlaybackRedrawUtc)) { s_nextPlaybackRedrawUtc = now.AddSeconds(1.0 / 30.0); RedrawLastTexture(); } } private static bool HasRecentEndMapContext(DateTime now) { if (s_lastUiInstance != null && s_lastTexture != null && s_lastPathRecorders != null && s_lastTilePositionOffset != null && s_lastPixelsPerTile > 0f) { return (now - s_lastEndMapDrawUtc).TotalSeconds <= 30.0; } return false; } private static void RedrawLastTexture() { if (s_lastUiInstance == null || s_lastTexture == null || s_lastPathRecorders == null || s_lastTilePositionOffset == null || s_lastPixelsPerTile <= 0f) { return; } try { s_lastEndMapDrawUtc = DateTime.UtcNow; object[] args = new object[5] { s_lastTexture, s_lastPathRecorders, ValueConversion.Clamp01(s_scrubTime), s_lastTilePositionOffset, s_lastPixelsPerTile }; SyncGameNameList(s_lastUiInstance, "redraw-last-texture"); if (ShouldUseCustomPlayerDraw()) { bool flag = BeginPathTextureBatch(s_lastTexture); if (!flag && !ClearPathTexture(s_lastUiInstance, s_lastTexture)) { return; } HideAllDeathIcons(s_lastUiInstance); DrawSelectedPlayers(s_lastUiInstance, args, ValueConversion.Clamp01(s_scrubTime), !s_showFullPath); DrawSelectedMonsters(s_lastUiInstance, args, ValueConversion.Clamp01(s_scrubTime)); DrawQueuedPlayerTextureIcons(s_lastUiInstance, s_lastTexture); if (flag) { if (!FinishPathTextureBatch()) { RedrawWithGamePlayerRenderer(args); } } else { ApplyTexture(s_lastTexture); } } else { RedrawWithGamePlayerRenderer(args); } } catch { AbortPathTextureBatch(); } } private static void RedrawWithGamePlayerRenderer(object[] args) { if (ClearPathTexture(s_lastUiInstance, s_lastTexture)) { HideAllDeathIcons(s_lastUiInstance); if (!TryInvokeGamePlayerRenderer(s_lastUiInstance, args)) { HideAllDeathIcons(s_lastUiInstance); DrawSelectedPlayers(s_lastUiInstance, args, ValueConversion.Clamp01(s_scrubTime), currentOnly: false); DrawSelectedMonsters(s_lastUiInstance, args, ValueConversion.Clamp01(s_scrubTime)); DrawQueuedPlayerTextureIcons(s_lastUiInstance, s_lastTexture); } ApplyTexture(s_lastTexture); } } private static bool TryInvokeGamePlayerRenderer(object ui, object[] args) { if (ui == null || args == null || args.Length < 5) { return false; } try { if (s_addPlayersToTextureMethod == null || !s_addPlayersToTextureMethod.DeclaringType.IsInstanceOfType(ui)) { s_addPlayersToTextureMethod = ui.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddPlayersToTexture" && m.GetParameters().Length == 5); } if (s_addPlayersToTextureMethod == null) { return false; } s_internalGamePlayerDraw = true; try { s_addPlayersToTextureMethod.Invoke(ui, args); } finally { s_internalGamePlayerDraw = false; } return true; } catch { s_internalGamePlayerDraw = false; return false; } } private static void AddPlayersToTexturePostfix(object __instance, object[] __args, bool __state, [HarmonyArgument(2)] float normalizedTime) { try { if (__instance == null || __args == null || __args.Length < 5 || __state) { return; } object obj = __args[0]; if (obj == null || !ValueConversion.TryConvertInt(Il2CppReflection.GetFieldOrProp(__args[3], "x"), out var _) || !ValueConversion.TryConvertInt(Il2CppReflection.GetFieldOrProp(__args[3], "y"), out var _) || !ValueConversion.TryConvertFloat(__args[4], out var result3)) { return; } EnsureDrawReflection(__instance); if (!(s_addPlayerDotMethod == null) && !(s_color32Ctor == null) && !(GetMazeTileSize() <= 0f) && !(result3 <= 0f)) { float value = normalizedTime; if (ValueConversion.TryConvertFloat(__args[2], out var result4)) { value = result4; } if (s_paused) { value = s_scrubTime; } normalizedTime = ValueConversion.Clamp01(value); int trailIndex; int num = DrawSelectedMonsters(__instance, __args, normalizedTime, out trailIndex); int num2 = DrawQueuedPlayerTextureIcons(__instance, obj); if (num > 0 || num2 > 0) { ApplyTexture(obj); } if (!s_loggedFirstDraw && IsMonsterChangeLoggingEnabled()) { s_loggedFirstDraw = true; MelonLogger.Msg("Drew monster paths on end map: trails=" + trailIndex + " dots=" + num); } } } catch (Exception ex) { if (DateTime.UtcNow >= s_nextDrawWarningUtc) { s_nextDrawWarningUtc = DateTime.UtcNow.AddSeconds(10.0); MelonLogger.Warning("end-map draw failed: " + ex.GetType().Name + " " + ex.Message); } } } private static float GetEffectiveNormalizedTime(float normalizedTime) { if (s_paused) { s_lastPlaybackUpdateUtc = DateTime.MinValue; return ValueConversion.Clamp01(s_scrubTime); } AdvancePlaybackClock(DateTime.UtcNow); return ValueConversion.Clamp01(s_scrubTime); } private static void AdvancePlaybackClock(DateTime now) { if (s_lastPlaybackUpdateUtc == DateTime.MinValue) { s_lastPlaybackUpdateUtc = now; return; } float num = ValueConversion.Clamp((float)(now - s_lastPlaybackUpdateUtc).TotalSeconds, 0f, 0.25f); s_lastPlaybackUpdateUtc = now; if (num <= 0f) { return; } if (s_playbackEndHoldUntilUtc != DateTime.MinValue) { if (!(now < s_playbackEndHoldUntilUtc)) { s_playbackEndHoldUntilUtc = DateTime.MinValue; s_scrubTime = 0f; } return; } float num2 = ValueConversion.Clamp(s_playbackSpeed, 0.05f, 4f); float num3 = s_scrubTime + num * num2 / 5f; if (num3 >= 1f) { s_scrubTime = 1f; s_playbackEndHoldUntilUtc = now.AddSeconds(1.5); } else { s_scrubTime = ValueConversion.Clamp01(num3); } } private static bool ShouldUseCustomPlayerDraw() { return true; } private static bool ShouldBlockGameTextureClear() { return true; } private static object ResolvePathRecorders(object ui, object argPathRecorders, out string source) { source = "manager-field:pathRecorders"; string source2; object pathVisualisationManagerPathRecorders = GetPathVisualisationManagerPathRecorders(out source2); if (pathVisualisationManagerPathRecorders != null && Il2CppReflection.GetCollectionCount(pathVisualisationManagerPathRecorders) > 0) { source = source2; return pathVisualisationManagerPathRecorders; } source = "arg-wrapper"; object fieldOrProp = Il2CppReflection.GetFieldOrProp(ui, "pathRecorders"); int collectionCount = Il2CppReflection.GetCollectionCount(fieldOrProp); if (fieldOrProp != null && collectionCount > 0) { source = "ui-field:pathRecorders"; return fieldOrProp; } object fieldOrProp2 = Il2CppReflection.GetFieldOrProp(ui, "PathRecorders"); int collectionCount2 = Il2CppReflection.GetCollectionCount(fieldOrProp2); if (fieldOrProp2 != null && collectionCount2 > 0) { source = "ui-property:PathRecorders"; return fieldOrProp2; } LogPathRecorderResolverDiagnostics(ui, argPathRecorders, fieldOrProp, fieldOrProp2, collectionCount, collectionCount2, pathVisualisationManagerPathRecorders, source2); if (argPathRecorders != null) { return argPathRecorders; } source = "none"; return null; } private static object GetPathVisualisationManagerPathRecorders(out string source) { source = "manager-field:pathRecorders"; object staticFieldOrProp = Il2CppReflection.GetStaticFieldOrProp(GetPathVisualisationManagerType(), "Instance"); if (staticFieldOrProp == null) { source = "manager:none"; return null; } object fieldOrProp = Il2CppReflection.GetFieldOrProp(staticFieldOrProp, "pathRecorders"); if (fieldOrProp != null && Il2CppReflection.GetCollectionCount(fieldOrProp) > 0) { source = "manager-field:pathRecorders"; return fieldOrProp; } object fieldOrProp2 = Il2CppReflection.GetFieldOrProp(staticFieldOrProp, "PathRecorders"); if (fieldOrProp2 != null && Il2CppReflection.GetCollectionCount(fieldOrProp2) > 0) { source = "manager-property:PathRecorders"; return fieldOrProp2; } source = ((fieldOrProp != null) ? "manager-field:pathRecorders-empty" : "manager-empty"); return fieldOrProp ?? fieldOrProp2; } private static void LogPathRecorderResolverDiagnostics(object ui, object argPathRecorders, object fieldPathRecorders, object propertyPathRecorders, int fieldPathRecorderCount, int propertyPathRecorderCount, object managerPathRecorders, string managerSource) { if (IsPlayerDebugLoggingEnabled()) { int num = ((ui != null) ? RuntimeHelpers.GetHashCode(ui) : 0); LogPlayerTrailDiagnostics("resolver:" + num + ":" + managerSource + ":" + Il2CppReflection.GetCollectionCount(managerPathRecorders) + ":" + Il2CppReflection.GetCollectionCount(argPathRecorders) + ":" + fieldPathRecorderCount + ":" + propertyPathRecorderCount, "path recorder resolver ui=" + DescribeObjectForLog(ui) + " managerSource=" + managerSource + " manager={" + DescribePathRecorderCandidate(managerPathRecorders) + "} arg={" + DescribePathRecorderCandidate(argPathRecorders) + "} field.pathRecorders={" + DescribePathRecorderCandidate(fieldPathRecorders) + "} property.PathRecorders={" + DescribePathRecorderCandidate(propertyPathRecorders) + "} direct={" + DescribeUiPathRecorderDirectAccess(ui) + "}"); } } private static string DescribePathRecorderCandidate(object value) { if (value == null) { return "null"; } return "type=" + SafeLogToken(value.GetType().FullName ?? value.GetType().Name) + " count=" + Il2CppReflection.GetCollectionCount(value) + " access={" + Il2CppReflection.DescribeCollectionAccessForLog(value, IsPlayerDebugLoggingEnabled()) + "}"; } private static string DescribeUiPathRecorderDirectAccess(object ui) { if (ui == null) { return "ui-null"; } try { Type type = ui.GetType(); List<string> values = new List<string> { "type=" + SafeLogToken(type.FullName ?? type.Name), DescribeDirectFieldAccess(ui, type, "pathRecorders"), DescribeDirectPropertyAccess(ui, type, "PathRecorders"), DescribeDirectMethodAccess(ui, type, "get_PathRecorders"), "members=[" + FormatPathRecorderMemberHints(type) + "]" }; return string.Join(" ", values); } catch (Exception ex) { return "direct-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message); } } private static string DescribeDirectFieldAccess(object ui, Type type, string name) { try { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return "field." + name + "=missing"; } try { return "field." + name + "=" + SafeLogToken(field.FieldType.FullName ?? field.FieldType.Name) + ":" + DescribePathRecorderCandidate(field.GetValue(ui)); } catch (Exception ex) { return "field." + name + "=" + SafeLogToken(field.FieldType.FullName ?? field.FieldType.Name) + ":get-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message); } } catch (Exception ex2) { return "field." + name + "=lookup-failed:" + ex2.GetType().Name + ":" + SafeLogToken(ex2.Message); } } private static string DescribeDirectPropertyAccess(object ui, Type type, string name) { try { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { return "property." + name + "=missing"; } try { return "property." + name + "=" + SafeLogToken(property.PropertyType.FullName ?? property.PropertyType.Name) + ":" + DescribePathRecorderCandidate(property.GetValue(ui)); } catch (Exception ex) { return "property." + name + "=" + SafeLogToken(property.PropertyType.FullName ?? property.PropertyType.Name) + ":get-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message); } } catch (Exception ex2) { return "property." + name + "=lookup-failed:" + ex2.GetType().Name + ":" + SafeLogToken(ex2.Message); } } private static string DescribeDirectMethodAccess(object ui, Type type, string name) { try { MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == name && m.GetParameters().Length == 0); if (methodInfo == null) { return "method." + name + "=missing"; } try { return "method." + name + "=" + SafeLogToken(methodInfo.ReturnType.FullName ?? methodInfo.ReturnType.Name) + ":" + DescribePathRecorderCandidate(methodInfo.Invoke(ui, Array.Empty<object>())); } catch (Exception ex) { return "method." + name + "=" + SafeLogToken(methodInfo.ReturnType.FullName ?? methodInfo.ReturnType.Name) + ":invoke-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message); } } catch (Exception ex2) { return "method." + name + "=lookup-failed:" + ex2.GetType().Name + ":" + SafeLogToken(ex2.Message); } } private static string FormatPathRecorderMemberHints(Type type) { try { List<string> list = (from m in type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where m.Name.IndexOf("path", StringComparison.OrdinalIgnoreCase) >= 0 || m.Name.IndexOf("record", StringComparison.OrdinalIgnoreCase) >= 0 select m.MemberType.ToString() + ":" + m.Name).Distinct().Take(30).ToList(); return (list.Count == 0) ? "none" : string.Join(",", list); } catch (Exception ex) { return "member-hints-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message); } } private static void QueuePlayerIcon(List<PlayerIconDrawRequest> requests, string key, WorldPoint point, int offsetX, int offsetY, float pixelsPerTile, float tileSize, object color) { if (requests != null && !string.IsNullOrEmpty(key)) { int pixelX = ValueConversion.FloorToInt(((float)offsetX + point.X / tileSize + 0.5f) * pixelsPerTile); int pixelY = ValueConversion.FloorToInt(((float)offsetY + point.Z / tileSize + 0.5f) * pixelsPerTile); requests.Add(new PlayerIconDrawRequest(pixelX, pixelY, key, TrailColorFromColor32(color, new TrailColor(240, 240, 240, byte.MaxValue)))); } } private static int DrawQueuedPlayerTextureIcons(object ui, object texture) { if (s_playerIconDrawRequests.Count == 0 || ui == null || texture == null) { return 0; } int width; int height; bool hasBounds = TryGetTextureDimensions(texture, out width, out height); int num = 0; int num2 = 0; foreach (PlayerIconDrawRequest s_playerIconDrawRequest in s_playerIconDrawRequests) { if (s_playerIcons.TryGetValue(s_playerIconDrawRequest.Key, out var value) && value != null) { num += DrawPlayerImageIcon(ui, texture, s_playerIconDrawRequest.PixelX, s_playerIconDrawRequest.PixelY, value, hasBounds, width, height); num2++; } else { num += DrawGeneratedPlayerIcon(ui, texture, s_playerIconDrawRequest.PixelX, s_playerIconDrawRequest.PixelY, s_playerIconDrawRequest.Color, hasBounds, width, height); } } LogPlayerIconStatus("drawn:" + s_playerIconDrawRequests.Count + ":" + num2 + ":" + num, "player icons drawn=" + num + " requests=" + s_playerIconDrawRequests.Count + " avatars=" + num2 + " cached=" + s_playerIcons.Count); return num; } private static int DrawPlayerImageIcon(object ui, object texture, int centerX, int centerY, MonsterIcon icon, bool hasBounds, int textureWidth, int textureHeight) { int num = centerX - icon.Width / 2; int num2 = centerY - icon.Height / 2; return 0 + DrawPixelRect(color: Color32(new TrailColor(0, 0, 0, byte.MaxValue)), ui: ui, texture: texture, x: num - 1, y: num2 - 1, width: icon.Width + 2, height: icon.Height + 2, hasBounds: hasBounds, textureWidth: textureWidth, textureHeight: textureHeight) + DrawMonsterImageIcon(ui, texture, centerX, centerY, icon, hasBounds, textureWidth, textureHeight); } private static int DrawGeneratedPlayerIcon(object ui, object texture, int centerX, int centerY, TrailColor color, bool hasBounds, int textureWidth, int textureHeight) { object color2 = Color32(new TrailColor(0, 0, 0, byte.MaxValue)); object color3 = Color32(OpaqueTrailColor(color)); object color4 = Color32(new TrailColor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)); return 0 + DrawPixelRect(ui, texture, centerX - 5, centerY - 5, 11, 11, color2, hasBounds, textureWidth, textureHeight) + DrawPixelRect(ui, texture, centerX - 4, centerY - 4, 9, 9, color3, hasBounds, textureWidth, textureHeight) + DrawPixelRect(ui, texture, centerX - 2, centerY - 2, 5, 5, color4, hasBounds, textureWidth, textureHeight); } private static void CachePlayerIcon(string key, object player) { if (string.IsNullOrEmpty(key)) { LogPlayerIconStatus("cache-empty-key", "player avatar cache skipped: empty key"); } else { if (s_playerIcons.ContainsKey(key) || (s_playerIconRetryUtc.TryGetValue(key, out var value) && DateTime.UtcNow < value)) { return; } if (player == null) { LogPlayerIconStatus("cache-no-player:" + key, "player avatar cache skipped key=" + key + ": player object is null"); DelayPlayerIconRetry(key); return; } Texture2D playerAvatarTexture = GetPlayerAvatarTexture(player); if ((Object)(object)playerAvatarTexture == (Object)null) { LogPlayerIconStatus("cache-no-avatar:" + key, "player avatar cache skipped key=" + key + ": no avatar texture on " + GetPlayerObjectLabel(player)); DelayPlayerIconRetry(key); return; } MonsterIcon monsterIcon = CreatePlayerAvatarIcon(key, playerAvatarTexture); if (monsterIcon == null) { LogPlayerIconStatus("cache-null-icon:" + key, "player avatar cache skipped key=" + key + ": avatar texture could not be converted"); DelayPlayerIconRetry(key); return; } s_playerIconRetryUtc.Remove(key); s_playerIcons[key] = monsterIcon; LogPlayerIconStatus("cached:" + key, "cached player avatar key=" + key + " size=" + monsterIcon.Width + "x" + monsterIcon.Height + " cached=" + s_playerIcons.Count); } } private static void DelayPlayerIconRetry(string key) { if (!string.IsNullOrEmpty(key)) { s_playerIconRetryUtc[key] = DateTime.UtcNow.AddSeconds(15.0); } } private static Texture2D GetPlayerAvatarTexture(object player) { object fieldOrProp = Il2CppReflection.GetFieldOrProp(LabelUtilities.GetPlayerClientData(player), "Avatar"); Texture2D val = (Texture2D)((fieldOrProp is Texture2D) ? fieldOrProp : null); if ((Object)(object)val != (Object)null) { return val; } object fieldOrProp2 = Il2CppReflection.GetFieldOrProp(player, "playerAvatar"); RawImage val2 = (RawImage)((fieldOrProp2 is RawImage) ? fieldOrProp2 : null); if (!((Object)(object)val2 == (Object)null)) { Texture texture = val2.texture; return (Texture2D)(object)((texture is Texture2D) ? texture : null); } return null; } private static MonsterIcon CreatePlayerAvatarIcon(string key, Texture2D source) { try { if (TryCreatePlayerAvatarIconFromTexture(source, "player:" + key, 14, out var icon)) { return icon; } LogPlayerIconStatus("direct-pixels-failed:" + key, "player avatar direct pixel read failed key=" + key + " source={" + DescribeTextureForLog((Texture)(object)source) + "}; trying readable copy"); Texture2D readable = null; try { if (TryCopyTextureToReadable(source, out readable) && TryCreatePlayerAvatarIconFromTexture(readable, "player:" + key, 14, out icon)) { return icon; } LogPlayerIconStatus("copy-pixels-failed:" + key, "player avatar readable-copy pixel read failed key=" + key + " source={" + DescribeTextureForLog((Texture)(object)source) + "}"); return null; } finally { if ((Object)(object)readable != (Object)null) { Object.Destroy((Object)(object)readable); } } } catch (Exception ex) { LogPlayerIconStatus("cache-failed:" + key, "player avatar cache failed key=" + key + ": " + ex.GetType().Name + " " + ex.Message); return null; } } private static bool TryCreatePlayerAvatarIconFromTexture(Texture2D source, string key, int iconSize, out MonsterIcon icon) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) icon = null; if ((Object)(object)source == (Object)null || iconSize <= 0) { return false; } int num = Math.Max(1, ((Texture)source).width); int num2 = Math.Max(1, ((Texture)source).height); Il2CppStructArray<Color32> pixels = source.GetPixels32(); if (pixels == null || ((Il2CppArrayBase<Color32>)(object)pixels).Length < num * num2) { return false; } TrailColor[] array = new TrailColor[iconSize * iconSize]; for (int i = 0; i < iconSize; i++) { int num3 = (iconSize - 1 - i) * Math.Max(0, num2 - 1) / Math.Max(1, iconSize - 1); for (int j = 0; j < iconSize; j++) { int num4 = j * Math.Max(0, num - 1) / Math.Max(1, iconSize - 1); Color32 val = ((Il2CppArrayBase<Color32>)(object)pixels)[num3 * num + num4]; array[i * iconSize + j] = new TrailColor(val.r, val.g, val.b, (byte)((val.a >= 24) ? byte.MaxValue : 0)); } } icon = new MonsterIcon(key, iconSize, iconSize, array); return true; } private static int DrawMonsterTextureIcon(object ui, object texture, int centerX, int centerY, int number, TrailColor color, string label) { int width; int height; bool hasBounds = TryGetTextureDimensions(texture, out width, out height); MonsterIcon monsterIcon = ResolveMonsterIcon(label); if (monsterIcon != null) { return DrawMonsterImageIcon(ui, texture, centerX, centerY, monsterIcon, hasBounds, width, height); } return DrawGeneratedMonsterIcon(ui, texture, centerX, centerY, number, color, hasBounds, width, height); } private static int DrawMonsterImageIcon(object ui, object texture, int centerX, int centerY, MonsterIcon icon, bool hasBounds, int textureWidth, int textureHeight) { int num = 0; int num2 = centerX - icon.Width / 2; int num3 = centerY - icon.Height / 2; object color = Color32(new TrailColor(0, 0, 0, byte.MaxValue)); num += DrawPixelRect(ui, texture, num2, num3, icon.Width, icon.Height, color, hasBounds, textureWidth, textureHeight); for (int i = 0; i < icon.Height; i++) { for (int j = 0; j < icon.Width; j++) { TrailColor color2 = icon.Pixels[i * icon.Width + j]; if (color2.A >= 24 && DrawTexturePixel(ui, texture, num2 + j, num3 + i, Color32(OpaqueTrailColor(color2)), hasBounds, textureWidth, textureHeight)) { num++; } } } return num; } private static int DrawGeneratedMonsterIcon(object ui, object texture, int centerX, int centerY, int number, TrailColor color, bool hasBounds, int textureWidth, int textureHeight) { object color2 = Color32(new TrailColor(0, 0, 0, byte.MaxValue)); object color3 = Color32(new TrailColor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)); object color4 = Color32(OpaqueTrailColor(color)); return 0 + DrawPixelRect(ui, texture, centerX - 5, centerY - 5, 11, 11, color2, hasBounds, textureWidth, textureHeight) + DrawPixelRect(ui, texture, centerX - 4, centerY - 4, 9, 9, color4, hasBounds, textureWidth, textureHeight) + DrawMonsterIconNumber(ui, texture, centerX, centerY, number, color3, hasBounds, textureWidth, textureHeight); } private static int DrawPixelRect(object ui, object texture, int x, int y, int width, int height, object color, bool hasBounds, int textureWidth, int textureHeight) { int num = 0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (DrawTexturePixel(ui, texture, x + j, y + i, color, hasBounds, textureWidth, textureHeight)) { num++; } } } return num; } private static int DrawMonsterIconNumber(object ui, object texture, int centerX, int centerY, int number, object color, bool hasBounds, int textureWidth, int textureHeight) { string text = Math.Max(0, Math.Min(99, number)).ToString(); int num = text.Length * 3 + Math.Max(0, text.Length - 1); int num2 = centerX - num / 2; int y = centerY - 2; int num3 = 0; for (int i = 0; i < text.Length; i++) { num3 += DrawDigitGlyph(ui, texture, num2 + i * 4, y, text[i], color, hasBounds, textureWidth, textureHeight); } return num3; } private static int DrawDigitGlyph(object ui, object texture, int x, int y, char digit, object color, bool hasBounds, int textureWidth, int textureHeight) { int[] digitGlyph = GetDigitGlyph(digit); int num = 0; for (int i = 0; i < digitGlyph.Length; i++) { int num2 = digitGlyph[i]; for (int j = 0; j < 3; j++) { if ((num2 & (1 << 2 - j)) != 0 && DrawTexturePixel(ui, texture, x + j, y + i, color, hasBounds, textureWidth, textureHeight)) { num++; } } } return num; } private static bool DrawTexturePixel(object ui, object texture, int x, int y, object color, bool hasBounds, int textureWidth, int textureHeight) { if (hasBounds && (x < 0 || y < 0 || x >= textureWidth || y >= textureHeight)) { return false; } if (!TryDrawBatchPixel(texture, x, y, color)) { s_addPlayerDotMethod.Invoke(ui, new object[5] { texture, x, y, color, 1f }); } return true; } private static int[] GetDigitGlyph(char digit) { return digit switch { '0' => new int[5] { 7, 5, 5, 5, 7 }, '1' => new int[5] { 2, 6, 2, 2, 7 }, '2' => new int[5] { 7, 1, 7, 4, 7 }, '3' => new int[5] { 7, 1, 7, 1, 7 }, '4' => new int[5] { 5, 5, 7, 1, 1 }, '5' => new int[5] { 7, 4, 7, 1, 7 }, '6' => new int[5] { 7, 4, 7, 5, 7 }, '7' => new int[5] { 7, 1, 1, 1, 1 }, '8' => new int[5] { 7, 5, 7, 5, 7 }, '9' => new int[5] { 7, 5, 7, 1, 7 }, _ => new int[5], }; } private static MonsterIcon ResolveMonsterIcon(string label) { EnsureMonsterIconsLoaded(); if (s_monsterIcons.Count == 0) { return null; } string monsterIconKey = GetMonsterIconKey(label); if (!string.IsNullOrWhiteSpace(monsterIconKey) && s_monsterIcons.TryGetValue(monsterIconKey, out var value)) { return value; } if (!s_monsterIcons.TryGetValue("fallback", out var value2)) { return null; } return value2; } private static string GetMonsterIconKey(string label) { string text = NormalizeIconKey(label); if (string.IsNullOrWhiteSpace(text)) { return "fallback"; } if (s_monsterIconAliases.TryGetValue(text, out var value)) { return value; } if (s_monsterIcons.ContainsKey(text)) { return text; } if (text.Contains("wickerman")) { return "wickerman"; } if (text.Contains("pigman")) { return "pigman"; } if (text.Contains("stalker")) { return "stalker"; } if (text.Contains("zombie")) { return "zombie"; } if (text.Contains("witch")) { return "witch"; } if (text.Contains("oni")) { return "oni"; } if (text.Contains("smiley")) { return "smiley"; } if (text.Contains("marionette")) { return "marionette"; } if (text.Contains("clubfoot")) { return "clubfoot"; } if (text.Contains("partygoer")) { return "partygoer"; } if (text.Contains("scarecrow")) { return "scarecrow"; } if (text.Contains("centipede")) { return "centipede"; } if (text.Contains("archiver")) { return "archiver"; } if (text.Contains("bride") || text.Contains("lady")) { return "bridelady"; } return "fallback"; } private static string NormalizeIconKey(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return new string(value.ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray()); } private static void EnsureMonsterIconsLoaded() { if (s_monsterIconsLoaded) { return; } s_monsterIconsLoaded = true; s_monsterIcons.Clear(); string text = FindMonsterIconFolder(); if (string.IsNullOrWhiteSpace(text)) { LogMonsterIconStatus("image-icons:no-folder", "image icon folder not found; using generated icons"); return; } string[] files = Directory.GetFiles(text, "*.png"); foreach (string path in files) { string text2 = NormalizeIconKey(Path.GetFileNameWithoutExtension(path)); if (!string.IsNullOrWhiteSpace(text2)) { MonsterIcon monsterIcon = LoadMonsterIcon(path, text2); if (monsterIcon != null) { s_monsterIcons[text2] = monsterIcon; } } } LogMonsterIconStatus("image-icons:loaded:" + s_monsterIcons.Count, "loaded image icons=" + s_monsterIcons.Count + " folder=" + text); } private static string FindMonsterIconFolder() { string path = Path.GetDirectoryName(typeof(MonsterPathVisualisation).Assembly.Location) ?? string.Empty; string currentDirectory = Directory.GetCurrentDirectory(); return new string[4] { Path.Combine(path, "img"), Path.Combine(path, "MonsterPathVisualisation", "img"), Path.Combine(currentDirectory, "img"), Path.Combine(currentDirectory, "UserData", "MonsterPathVisualisation", "img") }.FirstOrDefault((string path2) => Directory.Exists(path2) && Directory.GetFiles(path2, "*.png").Length != 0); } private static MonsterIcon LoadMonsterIcon(string path, string key) { try { if (TryDecodePngIcon(File.ReadAllBytes(path), key, out var icon)) { return icon; } LogMonsterIconStatus("image-icons:unsupported:" + key, "unsupported PNG icon " + Path.GetFileName(path)); return null; } catch (Exception ex) { LogMonsterIconStatus("image-icons:error:" + key, "icon load failed " + Path.GetFileName(path) + ": " + ex.GetType().Name + " " + ex.Message); return null; } } private static bool TryCopyTextureToReadable(Texture2D source, out Texture2D readable) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) readable = null; if ((Object)(object)source == (Object)null) { return false; } RenderTexture active = null; RenderTexture val = null; try { active = RenderTexture.active; val = RenderTexture.GetTemporary(64, 64, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)source, val); RenderTexture.active = val; readable = new Texture2D(64, 64, (TextureFormat)4, false); readable.ReadPixels(new Rect(0f, 0f, 64f, 64f), 0, 0, false); readable.Apply(false, false); return true; } catch (Exception ex) { LogPlayerIconStatus("copy-failed:" + DescribeTextureForLog((Texture)(object)source), "player avatar readable copy failed: " + ex.GetType().Name + " " + ex.Message + " source={" + DescribeTextureForLog((Texture)(object)source) + "}"); if ((Object)(object)readable != (Object)null) { try { Object.Destroy((Object)(object)readable); } catch { } } readable = null; return false; } finally { try { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } } catch { } } } private static string DescribeTextureForLog(Texture texture) { if ((Object)(object)texture == (Object)null) { return "null"; } try { return "name='" + ((Object)texture).name + "' size=" + texture.width + "x" + texture.height + " type=" + ((object)texture).GetType().Name; } catch { return ((object)texture).GetType().Name; } } private static string DescribeObjectForLog(object value) { if (value == null) { return "null"; } try { string text = value.GetType().FullName ?? value.GetType().Name; if (ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(value, "x"), out var result) && ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(value, "y"), out var result2)) { return text + "(" + result.ToString("0.###") + "," + result2.ToString("0.###") + ")"; } return text + " '" + value?.ToString() + "'"; } catch { return value.GetType().Name; } } private static bool TryDecodePngIcon(byte[] data, string key, out MonsterIcon icon) { icon = null; if (data == null || data.Length < 33) { return false; } byte[] array = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }; for (int i = 0; i < array.Length; i++) { if (data[i] != array[i]) { return false; } } MemoryStream memoryStream = new MemoryStream(); byte[] array2 = null; byte[] array3 = null; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num7; int num6; for (num6 = 8; num6 + 8 <= data.Length; num6 += num7 + 4) { num7 = ReadBigEndianInt(data, num6); num6 += 4; if (num7 < 0 || num6 + 4 + num7 + 4 > data.Length) { return false; } string text = Encoding.ASCII.GetString(data, num6, 4); num6 += 4; if (text == "IHDR" && num7 >= 13) { num = ReadBigEndianInt(data, num6); num2 = ReadBigEndianInt(data, num6 + 4); num3 = data[num6 + 8]; num4 = data[num6 + 9]; num5 = data[num6 + 12]; continue; } switch (text) { case "PLTE": array2 = new byte[num7]; Buffer.BlockCopy(data, num6, array2, 0, num7); continue; case "tRNS": array3 = new byte[num7]; Buffer.BlockCopy(data, num6, array3, 0, num7); continue; case "IDAT": memoryStream.Write(data, num6, num7); continue; default: continue; case "IEND": break; } break; } if (num <= 0 || num2 <= 0 || num3 != 8 || num5 != 0 || memoryStream.Length <= 6) { return false; } int num8 = num4 switch { 0 => 1, 2 => 3, 3 => 1, 4 => 2, 6 => 4, _ => 0, }; if (num8 <= 0 || (num4 == 3 && array2 == null)) { return false; } byte[] array4 = memoryStream.ToArray(); byte[] array5; try { using MemoryStream stream = new MemoryStream(array4, 2, array4.Length - 6); using DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream2 = new MemoryStream(); deflateStream.CopyTo(memoryStream2); array5 = memoryStream2.ToArray(); } catch { return false; } int num9 = num * num8; if (array5.Length < (num9 + 1) * num2) { return false; } TrailColor[] array6 = new TrailColor[num * num2]; byte[] array7 = new byte[num9]; byte[] array8 = new byte[num9]; int num10 = 0; for (int j = 0; j < num2; j++) { byte filter = array5[num10++]; Buffer.BlockCopy(array5, num10, array8, 0, num9); num10 += num9; if (!UnfilterPngScanline(array8, array7, num8, filter)) { return false; } for (int k = 0; k < num; k++) { int num11 = k * num8; byte b = byte.MaxValue; byte r; byte g; byte b2; switch (num4) { case 0: r = (g = (b2 = array8[num11])); break; case 2: r = array8[num11]; g = array8[num11 + 1]; b2 = array8[num11 + 2]; break; case 3: { byte b3 = array8[num11]; int num12 = b3 * 3; if (num12 + 2 >= array2.Length) { return false; } r = array2[num12]; g = array2[num12 + 1]; b2 = array2[num12 + 2]; if (array3 != null && b3 < array3.Length) { b = array3[b3]; } break; } case 4: r = (g = (b2 = array8[num11])); b = array8[num11 + 1]; break; case 6: r = array8[num11]; g = array8[num11 + 1]; b2 = array8[num11 + 2]; b = array8[num11 + 3]; break; default: return false; } array6[j * num + k] = new TrailColor(r, g, b2, (byte)((b >= 24) ? byte.MaxValue : 0)); } byte[] array9 = array7; array7 = array8; array8 = array9; } icon = new MonsterIcon(key, num, num2, array6); return true; } private static int ReadBigEndianInt(byte[] data, int offset) { return (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]; } private static bool UnfilterPngScanline(byte[] current, byte[] previous, int bytesPerPixel, int filter) { switch (filter) { case 0: return true; case 1: { for (int l = 0; l < current.Length; l++) { int num2 = ((l >= bytesPerPixel) ? current[l - bytesPerPixel] : 0); current[l] = (byte)(current[l] + num2); } return true; } case 2: { for (int j = 0; j < current.Length; j++) { current[j] += previous[j]; } return true; } case 3: { for (int k = 0; k < current.Length; k++) { int num = ((k >= bytesPerPixel) ? current[k - bytesPerPixel] : 0); byte b = previous[k]; current[k] = (byte)(current[k] + (num + b >> 1)); } return true; } case 4: { for (int i = 0; i < current.Length; i++) { int left = ((i >= bytesPerPixel) ? current[i - bytesPerPixel] : 0); byte up = previous[i]; int upperLeft = ((i >= bytesPerPixel) ? previous[i - bytesPerPixel] : 0); current[i] = (byte)(current[i] + PaethPredictor(left, up, upperLeft)); } return true; } default: return false; } } private static int PaethPredictor(int left, int up, int upperLeft) { int num = left + up - upperLeft; int num2 = Math.Abs(num - left); int num3 = Math.Abs(num - up); int num4 = Math.Abs(num - upperLeft); if (num2 <= num3 && num2 <= num4) { return left; } if (num3 > num4) { return upperLeft; } return up; } private static bool IsCaseOverActive() { try { object staticFieldOrProp = Il2CppReflection.GetStaticFieldOrProp(GetEndScreenUIType(), "Instance"); if (staticFieldOrProp == null) { return false; } bool result; return ValueConversion.TryConvertBool(Il2CppReflection.GetFieldOrProp(staticFieldOrProp, "IsVisible"), out result) && result; } catch { return false; } } private static bool IsPlayerSyncCandidate(object obj) { if (obj == null || LooksLikeMonsterObject(obj)) { return false; } Type playerNetworkSyncType = GetPlayerNetworkSyncType(); if (playerNetworkSyncType != null && playerNetworkSyncType.IsInstanceOfType(obj)) { return true; } string text = obj.GetType().FullName ?? obj.GetType().Name; if (text.EndsWith("PlayerNetworkSync", StringComparison.Ordinal) || text.EndsWith(".PlayerNetworkSync", StringComparison.Ordinal)) { return true; } if (Il2CppReflection.GetFieldOrProp(obj, "ClientData") == null && Il2CppReflection.GetFieldOrProp(obj, "isDead") == null) { return Il2CppReflection.GetFieldOrProp(obj, "NetworkisDead") != null; } return true; } private static bool LooksLikeMonsterObject(object obj) { if (obj == null) { return false; } if (TryGetKnownMonsterObjectLabel(obj, out var _)) { return !HasMonsterLikeAncestor(obj); } return false; } private static bool TryGetPlayerDeadState(object player, out bool isDead) { isDead = false; if (!ValueConversion.TryConvertBool(Il2CppReflection.GetFieldOrProp(player, "IsDead"), out isDead) && !ValueConversion.TryConvertBool(Il2CppReflection.GetFieldOrProp(player, "NetworkisDead"), out isDead)) { return ValueConversion.TryConvertBool(Il2CppReflection.GetFieldOrProp(player, "isDead"), out isDead); } return true; } private static object GetMonsterNetworkSyncObject(object monster) { if (monster == null) { return null; } Type monsterNetworkSyncType = GetMonsterNetworkSyncType(); if (monsterNetworkSyncType != null && monsterNetworkSyncType.IsInstanceOfType(monster)) { return monster; } return Il2CppReflection.GetFieldOrProp(monster, "MonsterNetworkSync") ?? Il2CppReflection.GetFieldOrProp(monster, "_MonsterNetworkSync_k__BackingField"); } private static object GetMonsterControllerObject(object monster) { if (monster == null) { return null; } Type aIControllerType = GetAIControllerType(); if (aIControllerType != null && aIControllerType.IsInstanceOfType(monster)) { return monster; } object monsterNetworkSyncObject = GetMonsterNetworkSyncObject(monster); return Il2CppReflection.GetFieldOrProp(monster, "AIController") ?? Il2CppReflection.GetFieldOrProp(monster, "_AIController_k__BackingField") ?? Il2CppReflection.GetFieldOrProp(monsterNetworkSyncObject, "AIController") ?? Il2CppReflection.GetFieldOrProp(monsterNetworkSyncObject, "_AIController_k__BackingField"); } private static string GetMonsterKey(object monster) { return "object:" + RuntimeHelpers.GetHashCode(monster); } private static string GetPlayerRecorderTrailKey(object recorder, int index) { return GetPlayerObjectKey(Il2CppReflection.GetFieldOrProp(recorder, "player")) ?? ("player-index:" + index); } private static string GetLocalPlayerKey() { string playerObjectKey = GetPlayerObjectKey(Il2CppReflection.GetStaticFieldOrProp(GetPlayerNetworkSyncType(), "instance")); if (!string.IsNullOrEmpty(playerObjectKey)) { return playerObjectKey; } return GetPlayerObjectKey(Il2CppReflection.GetFieldOrProp(Il2CppReflection.GetStaticFieldOrProp(GetGameManagerType(), "instance"), "mainPlayer")); } private static string GetPlayerObjectKey(object player) { if (player == null) { return null; } if (ValueConversion.TryConvertLong(Il2CppReflection.GetFieldOrProp(player, "netId"), out var result) && result > 0) { return "net:" + result; } object playerClientData = LabelUtilities.GetPlayerClientData(player); if (ValueConversion.TryConvertLong(Il2CppReflection.GetFieldOrProp(playerClientData, "SteamID") ?? Il2CppReflection.GetFieldOrProp(playerClientData, "SteamId") ?? Il2CppReflection.GetFieldOrProp(playerClientData, "steamID") ?? Il2CppReflection.GetFieldOrProp(playerClientData, "ID"), out var result2) && result2 > 0) { return "steam:" + result2; } if (ValueConversion.TryConvertLong(Il2CppReflection.GetFieldOrProp(player, "ID") ?? Il2CppReflection.GetFieldOrProp(player, "PlayerID") ?? Il2CppReflection.GetFieldOrProp(player, "playerID") ?? Il2CppReflection.GetFieldOrProp(player, "ActorNumber"), out var result3) && result3 > 0) { return "player:" + result3; } return "object:" + RuntimeHelpers.GetHashCode(player); } private static string GetMonsterLabel(object monster) { if (TryGetKnownMonsterObjectLabel(monster, out var label)) { return label; } object obj = GetMonsterControllerObject(monster) ?? monster; object fieldOrProp = Il2CppReflection.GetFieldOrProp(obj, "MonsterType"); if (fieldOrProp == null) { fieldOrProp = Il2CppReflection.GetFieldOrProp(obj, "monsterType"); } if (fieldOrProp != null) { return CanonicalMonsterLabel(fieldOrProp.ToString(), "Monster"); } return CanonicalMonsterLabel(LabelUtilities.GetObjectName(monster), "Monster"); } private static string CanonicalMonsterLabel(string label, string fallback) { if (string.IsNullOrWhiteSpace(label)) { return fallback; } string text = NormalizeMonsterObjectName(label); if (text.Length > 0 && KnownMonsterObjectNameMap().TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } return LabelUtilities.CleanObjectLabel(label); } private static string GetPlayerObjectLabel(object player) { string source; return GetPlayerObjectLabel(player, out source); } private static string GetPlayerObjectLabel(object player, out string source) { return ResolvePlayerLabel(player, null, -1, out source); } private static string GetPlayerLabel(object ui, object recorder, int index) { object fieldOrProp = Il2CppReflection.GetFieldOrProp(recorder, "player"); string source; string text = ResolvePlayerLabel(fieldOrProp, ui, index, out source); if (source.StartsWith("fallback", StringComparison.OrdinalIgnoreCase)) { LogPlayerNameDiagnostics("fallback:" + index + ":" + LabelUtilities.DescribePlayerObject(fieldOrProp), "index=" + index + " source=" + source + " label='" + text + "' details=" + LabelUtilities.DescribePlayerNameSources(fieldOrProp, ui, index)); } return text; } private static string ResolvePlayerLabel(object player, object ui, int index, out string source) { string clientDataName = LabelUtilities.GetClientDataName(LabelUtilities.GetPlayerClientData(player, out var source2), out var source3); if (LabelUtilities.IsUsefulPlayerLabel(clientDataName)) { source = source2 + "." + source3; return clientDataName; } clientDataName = LabelUtilities.GetTextValue(Il2CppReflection.GetFieldOrProp(player, "namePlate")); if (LabelUtilities.IsUsefulPlayerLabel(clientDataName)) { source = "namePlate.text"; return clientDataName; } clientDataName = LabelUtilities.GetEndMapPlayerName(ui, index); if (LabelUtilities.IsUsefulPlayerLabel(clientDataName)) { source = "endMap.playerNameUIs"; return clientDataName; } clientDataName = Il2CppReflection.GetFieldOrProp(Il2CppReflection.GetFieldOrProp(player, "gameObject") ?? player, "name") as string; if (LabelUtilities.IsUsefulPlayerLabel(clientDataName)) { source = "gameObject.name"; return clientDataName; } source = ((index >= 0) ? "fallback:index" : "fallback:generic"); if (index < 0) { return "Player"; } return "Player " + (index + 1); } private static void LogReflectionWarning(string message) { if (!(DateTime.UtcNow < s_nextReflectionWarningUtc)) { s_nextReflectionWarningUtc = DateTime.UtcNow.AddSeconds(10.0); MelonLogger.Warning(message); } } private static void LogPlayerRenderStatus(string signature, string message) { if (IsVerboseLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(signature == s_lastPlayerRenderStatus) || !(utcNow < s_nextPlayerRenderStatusUtc)) { s_lastPlayerRenderStatus = signature; s_nextPlayerRenderStatusUtc = utcNow.AddSeconds(4.0); MelonLogger.Msg("Player path renderer: " + message); } } } private static void LogMonsterIconStatus(string signature, string message) { if (IsVerboseLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(utcNow < s_nextMonsterIconDebugUtc)) { s_nextMonsterIconDebugUtc = utcNow.AddSeconds(3.0); MelonLogger.Msg("Monster icon debug: " + message); } } } private static void LogPlayerIconStatus(string signature, string message) { if (IsVerboseLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(utcNow < s_nextPlayerIconDebugUtc)) { s_nextPlayerIconDebugUtc = utcNow.AddSeconds(3.0); MelonLogger.Msg("Player icon debug: " + message); } } } private static void LogVerbose(string message) { if (IsVerboseLoggingEnabled()) { MelonLogger.Msg(message); } } private static bool IsVerboseLoggingEnabled() { if (s_verboseLogs != null) { return s_verboseLogs.Value; } return false; } private static bool IsMonsterChangeLoggingEnabled() { return IsVerboseLoggingEnabled(); } private static bool IsPlayerDebugLoggingEnabled() { if (s_debugPlayerPath == null || !s_debugPlayerPath.Value) { return IsVerboseLoggingEnabled(); } return true; } private static void LogPlayerDebug(string message) { if (IsPlayerDebugLoggingEnabled() && !(DateTime.UtcNow < s_nextPlayerDebugLogUtc)) { s_nextPlayerDebugLogUtc = DateTime.UtcNow.AddSeconds(3.0); MelonLogger.Msg("Player path debug: " + message); } } private static void LogPlayerTrailDiagnostics(string signature, string message) { if (IsPlayerDebugLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!s_nextPlayerTrailDiagnosticsBySignature.TryGetValue(signature, out var value) || !(utcNow < value)) { s_nextPlayerTrailDiagnosticsBySignature[signature] = utcNow.AddSeconds(5.0); MelonLogger.Msg("Player trail diagnostics: " + message); } } } private static void AddPlayerRecorderDebug(List<string> rows, int index, string label, string recordedKey, string localKey, string matchedKey, MonsterTrail matchedTrail, int rawPositionCount, string decision) { if (rows != null && rows.Count < 8) { bool flag = !string.IsNullOrEmpty(recordedKey) && !string.IsNullOrEmpty(localKey) && string.Equals(recordedKey, localKey, StringComparison.OrdinalIgnoreCase); rows.Add("#" + index + " label='" + SafeLogToken(label) + "' key=" + (string.IsNullOrEmpty(recordedKey) ? "{none}" : recordedKey) + " local=" + flag + " raw=" + rawPositionCount + " match=" + (string.IsNullOrEmpty(matchedKey) ? "{none}" : matchedKey) + "#" + (matchedTrail?.Count ?? 0) + " decision=" + decision); } } private static string FormatPlayerRecorderDebugRows(List<string> rows) { if (rows == null || rows.Count == 0) { return string.Empty; } return " recorderDetails=[" + string.Join(" | ", rows) + "]"; } private static string FormatPathEntryKeysForLog(IReadOnlyList<PathEntry> entries) { if (entries == null || entries.Count == 0) { return "none"; } List<string> list = (from entry in entries.Take(12) select entry.Key ?? "{none}").ToList(); if (entries.Count > 12) { list.Add("+" + (entries.Count - 12)); } return string.Join(",", list); } private static string FormatPathEntriesForLog(IReadOnlyList<PathEntry> entries) { if (entries == null || entries.Count == 0) { return "[]"; } List<string> list = (from entry in entries.Take(12) select (entry.Key ?? "{none}") + "='" + SafeLogToken(entry.Label) + "'").ToList(); if (entries.Count > 12) { list.Add("+" + (entries.Count - 12) + " more"); } return "[" + string.Join("; ", list) + "]"; } private static string SafeLogToken(string value) { if (!string.IsNullOrWhiteSpace(value)) { return value.Replace("|", "/").Replace("\r", " ").Replace("\n", " ") .Trim(); } return string.Empty; } private static void LogMonsterDiagnostics(string signature, string message) { if (IsMonsterChangeLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(signature == s_lastMonsterDiagnosticsStatus) || !(utcNow < s_nextMonsterDiagnosticsUtc)) { s_lastMonsterDiagnosticsStatus = signature; s_nextMonsterDiagnosticsUtc = utcNow.AddSeconds(8.0); MelonLogger.Msg("Monster path diagnostics: " + message); } } } private static void LogPlayerNameDiagnostics(string signature, string message) { if (IsPlayerDebugLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(signature == s_lastPlayerNameDiagnosticsStatus) || !(utcNow < s_nextPlayerNameDiagnosticsUtc)) { s_lastPlayerNameDiagnosticsStatus = signature; s_nextPlayerNameDiagnosticsUtc = utcNow.AddSeconds(8.0); MelonLogger.Msg("Player name diagnostics: " + message); } } } private static void LogEndMapDiagnostics(string signature, string message) { if (IsVerboseLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!(signature == s_lastEndMapDiagnosticsStatus) || !(utcNow < s_nextEndMapDiagnosticsUtc)) { s_lastEndMapDiagnosticsStatus = signature; s_nextEndMapDiagnosticsUtc = utcNow.AddSeconds(6.0); MelonLogger.Msg("End-map path diagnostics: " + message); } } } private static void LogNameListDiagnostics(string signature, string message) { if (IsVerboseLoggingEnabled()) { DateTime utcNow = DateTime.UtcNow; if (!s_nextNameListDiagnosticsBySignature.TryGetValue(signature, out var value) || !(utcNow < value)) { s_nextNameListDiagnosticsBySignature[signature] = utcNow.AddSeconds(6.0); MelonLogger.Msg("Name-list diagnostics: " + message); } } } private static void DrawNameList(string source) { List<PathEntry> nameListEntries = GetNameListEntries(); if (nameListEntries.Count > 0) { SyncGameNameList(s_lastUiInstance, nameListEntries, source); } } private static List<PathEntry> GetNameListEntries() { s_nameListEntries.Clear(); s_nameListEntries.AddRange(s_playerEntries); s_nameListEntries.AddRange(s_monsterEntries); return s_nameListEntries; } private static void HandleNameListClickProbe(DateTime now) { if (s_lastUiInstance == null || (now - s_lastEndMapDrawUtc).TotalSeconds > 30.0 || !TryGetMouseButtonDown(out var x, out var y, out var source)) { return; } List<PathEntry> nameListEntries = GetNameListEntries(); if (nameListEntries.Count <= 0) { return; } object fieldOrProp = Il2CppReflection.GetFieldOrProp(s_lastUiInstance, "playerNameUIs"); int num = Math.Min(nameListEntries.Count, Il2CppReflection.GetCollectionCount(fieldOrProp) + GetExtraNameListRowCount(s_lastUiInstance)); if (num <= 0) { return; } int count = nameListEntries.Count; int columnCount = Math.Max(1, (int)Math.Ceiling((float)count / 5f)); float uiScale = NameListScale(Math.Min(5, count), columnCount); int num2 = Math.Min(nameListEntries.Count, num); float num3 = x; float num4 = (float)ScreenHeight() - y; int num5 = 0; int num6 = 0; int num7 = -1; List<string> rows = (IsVerboseLoggingEnabled() ? new List<string>() : null); for (int i = 0; i < num2; i++) { if (TryGetGameNameListClickTargetRect(i, uiScale, out var x2, out var y2, out var width, out var height, out var source2)) { num5++; bool flag = num3 >= x2 && num3 <= x2 + width && num4 >= y2 && num4 <= y2 + height; AddNameListClickDiagnostic(rows, i, nameListEntries[i].Label, x2, y2, width, height, flag, source2); if (flag && num7 < 0) { num7 = i; } } else { num6++; } } if (num7 >= 0) { PathEntry pathEntry = nameListEntries[num7]; s_pathVisibility[pathEntry.Key] = !IsPathVisible(pathEntry.Key); RedrawLastTexture(); } if (IsVerboseLoggingEnabled()) { MelonLogger.Msg("[NameListClickProbe] mouse=" + (int)num3 + "," + (int)num4 + " raw=" + (int)x + "," + (int)y + " input=" + source + " entries=" + nameListEntries.Count + " synced=" + num + " candidates=" + num5 + " missingRects=" + num6 + " hit=" + ((num7 >= 0) ? (num7 + " '" + SafeLogToken(nameListEntries[num7].Label) + "'") : "none") + FormatNameListClickDiagnostics(rows)); } } private static bool TryGetMouseButtonDown(out float x, out float y, out string source) { x = 0f; y = 0f; source = "none"; if (!EnsureUnityReflection()) { source = "unity-reflection-not-ready"; return false; } if (s_inputType == null || s_inputGetMouseButtonDown == null || s_inputMousePositionProperty == null) { source = "input-reflection-missing"; LogNameListDiagnostics("click-input-missing", "click probe input unavailable inputType=" + ((s_inputType == null) ? "none" : s_inputType.FullName) + " getMouseButtonDown=" + ((s_inputGetMouseButtonDown == null) ? "none" : "ok") + " mousePosition=" + ((s_inputMousePositionProperty == null) ? "none" : "ok")); return false; } try { if (!Convert.ToBoolean(s_inputGetMouseButtonDown.Invoke(null, new object[1] { 0 }))) { return false; } object value = s_inputMousePositionProperty.GetValue(null); if (!ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(value, "x"), out x) || !ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(value, "y"), out y)) { source = "mouse-position-unreadable"; LogNameListDiagnostics("click-input-mouse-position-unreadable:" + ((value == null) ? "none" : value.GetType().FullName), "[NameListClickProbe] input=mouse-position-unreadable type=" + ((value == null) ? "none" : value.GetType().FullName)); return false; } source = "UnityEngine.Input(reflection)"; return true; } catch (Exception ex) { source = "input-exception"; LogNameListDiagnostics("click-input-exception:" + ex.GetType().Name, "click probe input failed: " + ex.GetType().Name + " " + ex.Message); return false; } } private static bool TryGetGameNameListClickTargetRect(int index, float uiScale, out float x, out float y, out float width, out float height, out string source) { source = "none"; float x2; float y2; float width2; float height2; bool flag = TryGetGameNameListElementGuiRect(index, out x2, out y2, out width2, out height2); if (TryGetGameNameListTextGuiRect(index, out var x3, out var y3, out var width3, out var height3)) { float num = Math.Max(height3 + S(8f, uiScale), S(22f, uiScale)); x = Math.Max(0f, x3 - num); y = y3; width = width3 + num; height = height3; if (flag && width2 > 1f && height2 > 1f) { UnionGuiRects(ref x, ref y, ref width, ref height, x2, y2, Math.Min(width2, S(260f, uiScale)), height2); ExpandGuiRect(ref x, ref y, ref width, ref height, S(4f, uiScale), S(3f, uiScale)); source = "element-text-union"; if (width > 1f) { return height > 1f; } return false; } source = "text-expanded-left"; if (width > 1f) { return height > 1f; } return false; } if (flag) { x = x2; y = y2; width = Math.Min(width2, S(260f, uiScale)); height = height2; ExpandGuiRect(ref x, ref y, ref width, ref height, S(4f, uiScale), S(3f, uiScale)); source = "element"; if (width > 1f) { return height > 1f; } return false; } x = 0f; y = 0f; width = 0f; height = 0f; return false; } private static void UnionGuiRects(ref float x, ref float y, ref float width, ref float height, float otherX, float otherY, float otherWidth, float otherHeight) { float num = Math.Min(x, otherX); float num2 = Math.Min(y, otherY); float num3 = Math.Max(x + width, otherX + otherWidth); float num4 = Math.Max(y + height, otherY + otherHeight); x = num; y = num2; width = Math.Max(0f, num3 - num); height = Math.Max(0f, num4 - num2); } private static void ExpandGuiRect(ref float x, ref float y, ref float width, ref float height, float horizontal, float vertical) { x = Math.Max(0f, x - Math.Max(0f, horizontal)); y = Math.Max(0f, y - Math.Max(0f, vertical)); width += Math.Max(0f, horizontal) * 2f; height += Math.Max(0f, vertical) * 2f; } private static bool TryGetGameNameListElementGuiRect(int index, out float x, out float y, out float width, out float height) { x = 0f; y = 0f; width = 0f; height = 0f; object fieldOrProp = Il2CppReflection.GetFieldOrProp(s_lastUiInstance, "playerNameUIs"); int collectionCount = Il2CppReflection.GetCollectionCount(fieldOrProp); string source; if (index >= collectionCount) { ExtraNameListRow extraNameListRow = GetExtraNameListRow(index - collectionCount); if (extraNameListRow != null) { return TryGetObjectGuiRect(extraNameListRow.Element, out x, out y, out width, out height, out source); } return false; } if (!Il2CppReflection.TryGetIndexedItem(fieldOrProp, index, out var item) || item == null) { return false; } return TryGetObjectGuiRect(Il2CppReflection.GetFieldOrProp(item, "element"), out x, out y, out width, out height, out source); } private static void LogNameListSecondaryPanelDiagnostics(GameObject panel, RectTransform rectTransform, int entryCount, int extraRowLevels, float extension, strin