The BepInEx console will not appear when launching like it does for other games on Thunderstore (you can turn it back on in your BepInEx.cfg file). If your PEAK crashes on startup, add -dx12 to your launch parameters.
Decompiled source of PingItems v1.3.2
PingItems.dll
Decompiled 11 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PingItems.Configuration; using PingItems.Core; using PingItems.Services; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PingItems")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.2.0")] [assembly: AssemblyInformationalVersion("1.3.2")] [assembly: AssemblyProduct("Ping Items")] [assembly: AssemblyTitle("PingItems")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PingItems { [BepInPlugin("com.memiczny.peak.pingitems", "Ping Items", "1.3.2")] [BepInProcess("PEAK.exe")] public class Plugin : BaseUnityPlugin { public static class PluginInfo { public const string PLUGIN_GUID = "com.memiczny.peak.pingitems"; public const string PLUGIN_NAME = "Ping Items"; public const string PLUGIN_VERSION = "1.3.2"; } public static ManualLogSource Logger { get; private set; } public static Plugin Instance { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; try { PingItemsConfig.Initialize(((BaseUnityPlugin)this).Config); Logger.LogInfo((object)"Configuration initialized"); ApplyHarmonyPatches(); InitializeUISystem(); PingService.Initialize(); Logger.LogInfo((object)"Ping service initialized"); Logger.LogInfo((object)"Ping Items v1.3.2 loaded successfully!"); } catch (Exception arg) { Logger.LogError((object)$"Failed to initialize plugin: {arg}"); } } private static void ApplyHarmonyPatches() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) try { new Harmony("com.memiczny.peak.pingitems").PatchAll(); Logger.LogInfo((object)"Harmony patches applied successfully"); } catch (Exception arg) { Logger.LogError((object)$"Failed to apply Harmony patches: {arg}"); throw; } } private static void InitializeUISystem() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown try { if ((Object)(object)UIManager.Instance == (Object)null) { GameObject val = new GameObject("PingItems_UIManager"); val.AddComponent<UIManager>(); Object.DontDestroyOnLoad((Object)val); } Logger.LogInfo((object)"UI system initialized"); } catch (Exception arg) { Logger.LogError((object)$"Failed to initialize UI system: {arg}"); throw; } } } } namespace PingItems.Services { public static class PingService { private static readonly List<PingHighlighter> _activeHighlights = new List<PingHighlighter>(); private static readonly Dictionary<GameObject, PingHighlighter> _itemToGroupMapping = new Dictionary<GameObject, PingHighlighter>(); public static void Initialize() { _activeHighlights.Clear(); Application.quitting += Cleanup; } public static void Cleanup() { foreach (PingHighlighter item in from h in _activeHighlights.AsEnumerable() where (Object)(object)h != (Object)null select h) { item.Cleanup(); } _activeHighlights.Clear(); _itemToGroupMapping.Clear(); Application.quitting -= Cleanup; } public static void ProcessPing(Vector3 pingPosition, Color playerColor, string playerName, Character pingingCharacter = null) { //IL_0000: 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_0058: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) try { List<IPingable> list = PingableFactory.FindPingablesForPing(pingPosition, pingingCharacter, PingItemsConfig.DetectionRadius.Value, PingItemsConfig.LuggageDetectionRadius.Value); if (list.Count != 0) { PingHighlighter pingHighlighter = FindExistingGroupForPingables(list); if ((Object)(object)pingHighlighter != (Object)null) { RefreshExistingGroup(pingHighlighter, list, pingPosition); } else if (PingItemsConfig.EnableItemGrouping.Value) { ProcessGroupedPings(list, pingPosition, playerColor, playerName); } else { ProcessIndividualPings(list, pingPosition, playerColor, playerName); } } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error processing ping: " + ex.Message)); } } public static void RemoveHighlight(PingHighlighter highlighter) { if ((Object)(object)highlighter == (Object)null) { return; } try { if (_activeHighlights.Remove(highlighter)) { CleanupGroupMapping(highlighter); UIManager instance = UIManager.Instance; if ((Object)(object)instance != (Object)null) { highlighter.Cleanup(); instance.ReleaseHighlighter(highlighter); } else { highlighter.Cleanup(); Object.Destroy((Object)(object)((Component)highlighter).gameObject); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to remove highlighter: " + ex.Message)); } } private static void ProcessGroupedPings(List<IPingable> pingables, Vector3 pingPosition, Color playerColor, string playerName) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) foreach (List<IPingable> item in GroupPingablesByType(ExpandItemClusters(pingables))) { CreateOrRefreshGroupedHighlight(item, pingPosition, playerColor, playerName); } } private static void ProcessIndividualPings(List<IPingable> pingables, Vector3 pingPosition, Color playerColor, string playerName) { //IL_0010: 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) foreach (IPingable pingable in pingables) { CreateOrRefreshHighlight(pingable, pingPosition, playerColor, playerName); } } private static PingHighlighter FindExistingGroupForPingables(List<IPingable> pingables) { foreach (IPingable pingable in pingables) { GameObject gameObject = pingable.GetGameObject(); if ((Object)(object)gameObject != (Object)null && _itemToGroupMapping.TryGetValue(gameObject, out var value)) { return value; } } return null; } private static void RefreshExistingGroup(PingHighlighter existingGroup, List<IPingable> newPingables, Vector3 pingPosition) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (existingGroup == null || !existingGroup.IsGroupedPing) { return; } try { List<IPingable> currentGroupItems = GetGroupItems(existingGroup); List<IPingable> source = ExpandItemClusters(currentGroupItems.Concat(newPingables.Where((IPingable newItem) => !ContainsItem(currentGroupItems, newItem))).ToList()); string itemType = source.FirstOrDefault()?.GetDisplayName(); if (!string.IsNullOrEmpty(itemType)) { List<IPingable> list = (from item in source where item.GetDisplayName() == itemType group item by item.GetGameObject() into @group select @group.First()).ToList(); if (list.Count != currentGroupItems.Count) { UpdateGroupMapping(existingGroup, list); existingGroup.Init(list, pingPosition); } else { existingGroup.Refresh(pingPosition); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to refresh group ping: " + ex.Message)); } } private static List<IPingable> ExpandItemClusters(List<IPingable> initialItems) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (initialItems.Count == 0) { return initialItems; } try { List<IPingable> allItems = PingableFactory.FindPingablesNearPosition(Vector3.zero, float.MaxValue, float.MaxValue); List<IPingable> list = new List<IPingable>(); HashSet<string> hashSet = new HashSet<string>(); foreach (IPingable initialItem in initialItems) { string displayName = initialItem.GetDisplayName(); if (hashSet.Add(displayName)) { List<IPingable> collection = FindCompleteClusterForType(initialItem, allItems); list.AddRange(collection); } } return (from item in list group item by item.GetGameObject() into @group select @group.First()).ToList(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Cluster expansion failed, using original items: " + ex.Message)); return initialItems; } } private static List<IPingable> FindCompleteClusterForType(IPingable seedItem, List<IPingable> allItems) { HashSet<IPingable> processed = new HashSet<IPingable>(); return BuildItemCluster(seedItem, allItems, processed); } private static List<IPingable> GetGroupItems(PingHighlighter groupHighlight) { List<IPingable> list = new List<IPingable>(); foreach (KeyValuePair<GameObject, PingHighlighter> item in _itemToGroupMapping) { if ((Object)(object)item.Value == (Object)(object)groupHighlight) { IPingable pingable = CreatePingableFromGameObject(item.Key); if (pingable != null) { list.Add(pingable); } } } return list; } private static void UpdateGroupMapping(PingHighlighter groupHighlight, List<IPingable> items) { foreach (KeyValuePair<GameObject, PingHighlighter> item in _itemToGroupMapping.Where((KeyValuePair<GameObject, PingHighlighter> kvp) => (Object)(object)kvp.Value == (Object)(object)groupHighlight).ToList()) { _itemToGroupMapping.Remove(item.Key); } foreach (IPingable item2 in items) { GameObject gameObject = item2.GetGameObject(); if ((Object)(object)gameObject != (Object)null) { _itemToGroupMapping[gameObject] = groupHighlight; } } } private static IPingable CreatePingableFromGameObject(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } Item component = gameObject.GetComponent<Item>(); if ((Object)(object)component != (Object)null) { return new ItemPingableAdapter(component); } Luggage component2 = gameObject.GetComponent<Luggage>(); if ((Object)(object)component2 != (Object)null) { return new LuggagePingableAdapter(component2); } MirageLuggage component3 = gameObject.GetComponent<MirageLuggage>(); if ((Object)(object)component3 != (Object)null) { return new MirageLuggagePingableAdapter(component3); } return null; } private static bool ContainsItem(List<IPingable> items, IPingable targetItem) { GameObject targetGameObject = targetItem?.GetGameObject(); if ((Object)(object)targetGameObject == (Object)null) { return false; } return items.Any((IPingable item) => (Object)(object)item?.GetGameObject() == (Object)(object)targetGameObject); } private static void CleanupGroupMapping(PingHighlighter highlighter) { foreach (KeyValuePair<GameObject, PingHighlighter> item in _itemToGroupMapping.Where((KeyValuePair<GameObject, PingHighlighter> kvp) => (Object)(object)kvp.Value == (Object)(object)highlighter).ToList()) { _itemToGroupMapping.Remove(item.Key); } } private static void CreateOrRefreshHighlight(IPingable pingable, Vector3 pingPoint, Color playerColor, string playerName) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) try { GameObject gameObject = pingable.GetGameObject(); if ((Object)(object)gameObject == (Object)null) { return; } PingHighlighter pingHighlighter = _activeHighlights.FirstOrDefault((PingHighlighter h) => (Object)(object)h?.TargetGameObject == (Object)(object)gameObject); if ((Object)(object)pingHighlighter != (Object)null) { pingHighlighter.Refresh(pingPoint); return; } PingHighlighter pingHighlighter2 = CreateNewHighlight(pingable, pingPoint, playerColor, playerName); if ((Object)(object)pingHighlighter2 != (Object)null) { _activeHighlights.Add(pingHighlighter2); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to create highlight for " + pingable?.GetDisplayName() + ": " + ex.Message)); } } private static PingHighlighter CreateNewHighlight(IPingable pingable, Vector3 pingPoint, Color playerColor, string playerName) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) try { UIManager instance = UIManager.Instance; if ((Object)(object)instance == (Object)null) { return null; } PingHighlighter pingHighlighter = instance.RentHighlighter(); if ((Object)(object)pingHighlighter == (Object)null) { return null; } pingHighlighter.PlayerColor = playerColor; pingHighlighter.PlayerName = playerName; pingHighlighter.Init(pingable, pingPoint); return pingHighlighter; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to create highlight: " + ex.Message)); return null; } } private static List<List<IPingable>> GroupPingablesByType(List<IPingable> pingables) { List<List<IPingable>> list = new List<List<IPingable>>(); HashSet<IPingable> hashSet = new HashSet<IPingable>(); foreach (IPingable pingable in pingables) { if (!hashSet.Contains(pingable)) { List<IPingable> list2 = BuildItemCluster(pingable, pingables, hashSet); if (list2.Count > 0) { list.Add(list2); } } } return list; } private static List<IPingable> BuildItemCluster(IPingable startItem, List<IPingable> allItems, HashSet<IPingable> processed) { List<IPingable> list = new List<IPingable>(); Queue<IPingable> queue = new Queue<IPingable>(); string displayName = startItem.GetDisplayName(); float value = PingItemsConfig.GroupingRadius.Value; queue.Enqueue(startItem); while (queue.Count > 0) { IPingable pingable = queue.Dequeue(); if (processed.Contains(pingable)) { continue; } processed.Add(pingable); list.Add(pingable); foreach (IPingable item in from n in FindItemNeighbors(pingable, allItems, displayName, value, processed) where !processed.Contains(n) select n) { queue.Enqueue(item); } } return list; } private static List<IPingable> FindItemNeighbors(IPingable centerItem, List<IPingable> allItems, string itemType, float radius, HashSet<IPingable> processed) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_003c: Unknown result type (might be due to invalid IL or missing references) List<IPingable> list = new List<IPingable>(); Vector3 center = centerItem.GetCenter(); foreach (IPingable allItem in allItems) { if (!processed.Contains(allItem) && allItem != centerItem && !(allItem.GetDisplayName() != itemType) && Vector3.Distance(center, allItem.GetCenter()) <= radius) { list.Add(allItem); } } return list; } private static void CreateOrRefreshGroupedHighlight(List<IPingable> group, Vector3 pingPoint, Color playerColor, string playerName) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (group != null && group.Count == 0) { return; } try { GameObject primaryGameObject = group[0].GetGameObject(); if ((Object)(object)primaryGameObject == (Object)null) { return; } PingHighlighter pingHighlighter = _activeHighlights.FirstOrDefault((PingHighlighter h) => (Object)(object)h?.TargetGameObject == (Object)(object)primaryGameObject); if ((Object)(object)pingHighlighter != (Object)null) { pingHighlighter.Init(group, pingPoint); UpdateGroupMapping(pingHighlighter, group); return; } PingHighlighter pingHighlighter2 = CreateNewGroupedHighlight(group, pingPoint, playerColor, playerName); if ((Object)(object)pingHighlighter2 != (Object)null) { _activeHighlights.Add(pingHighlighter2); UpdateGroupMapping(pingHighlighter2, group); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to create grouped highlight: " + ex.Message)); } } private static PingHighlighter CreateNewGroupedHighlight(List<IPingable> group, Vector3 pingPoint, Color playerColor, string playerName) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) try { UIManager instance = UIManager.Instance; if ((Object)(object)instance == (Object)null) { return null; } PingHighlighter pingHighlighter = instance.RentHighlighter(); if ((Object)(object)pingHighlighter == (Object)null) { return null; } pingHighlighter.PlayerColor = playerColor; pingHighlighter.PlayerName = playerName; pingHighlighter.Init(group, pingPoint); return pingHighlighter; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to create grouped highlight: " + ex.Message)); return null; } } } } namespace PingItems.Patches { [HarmonyPatch(typeof(GUIManager), "InitReticleList")] public static class GUIManagerPatch { [HarmonyPostfix] public static void Postfix(GUIManager __instance) { try { CaptureReticle(__instance); CaptureFont(__instance); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Error in GUIManager patch: {arg}"); } } private static void CaptureReticle(GUIManager guiManager) { try { FieldInfo field = typeof(GUIManager).GetField("reticleDefault", BindingFlags.Instance | BindingFlags.Public); if (field != null) { object? value = field.GetValue(guiManager); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { Plugin.Logger.LogInfo((object)("Successfully captured reticle: " + ((Object)val).name)); UIManager.Instance?.SetReticlePrefab(val); return; } } if ((Object)(object)guiManager.reticleDefault != (Object)null) { Plugin.Logger.LogInfo((object)("Successfully captured reticle via direct access: " + ((Object)guiManager.reticleDefault).name)); UIManager.Instance?.SetReticlePrefab(guiManager.reticleDefault); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to capture reticle: " + ex.Message)); } } private static void CaptureFont(GUIManager guiManager) { try { TMP_FontAsset val = (from textComponent in ((Component)guiManager).GetComponentsInChildren<TMP_Text>(true) select textComponent.font).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => (Object)(object)f != (Object)null)); if ((Object)(object)val != (Object)null) { UIManager.Instance?.SetGameFont(val); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to capture font: " + ex.Message)); } } } [HarmonyPatch(typeof(PointPinger), "ReceivePoint_Rpc")] public static class PointPingerPatch { [HarmonyPostfix] public static void Postfix(PointPinger __instance, Vector3 point, Vector3 hitNormal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { Color pingingPlayerColor = GetPingingPlayerColor(__instance); string pingingPlayerName = GetPingingPlayerName(__instance); Character pingingCharacter = __instance?.character; PingService.ProcessPing(point, pingingPlayerColor, pingingPlayerName, pingingCharacter); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Error in PointPinger patch: {arg}"); } } private static Color GetPingingPlayerColor(PointPinger pointPinger) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0069: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)pointPinger?.character?.refs?.customization != (Object)null) { return pointPinger.character.refs.customization.PlayerColor; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get pinging player color: " + ex.Message)); } return Color.cyan; } private static string GetPingingPlayerName(PointPinger pointPinger) { try { if (pointPinger != null) { Character character = pointPinger.character; if (((character != null) ? character.characterName : null) != null) { return pointPinger.character.characterName; } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get pinging player name: " + ex.Message)); } return "Unknown"; } } } namespace PingItems.Core { public interface IPingable { Vector3 GetCenter(); string GetDisplayName(); bool CanBePinged(); GameObject GetGameObject(); } public class ItemPingableAdapter : IPingable { private readonly Item _item; public Item Item => _item; public ItemPingableAdapter(Item item) { _item = item ?? throw new ArgumentNullException("item"); } public Vector3 GetCenter() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _item.Center(); } public string GetDisplayName() { try { return _item.GetItemName((ItemInstanceData)null); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get item name for " + ((Object)_item).name + ": " + ex.Message)); return ((Object)_item).name; } } public GameObject GetGameObject() { if ((Object)(object)_item == (Object)null) { return null; } try { if ((Object)(object)_item == (Object)null) { return null; } return ((Component)_item).gameObject; } catch { return null; } } public bool CanBePinged() { if ((Object)(object)_item == (Object)null) { return false; } try { if ((Object)(object)((Component)_item).gameObject == (Object)null || !((Component)_item).gameObject.activeInHierarchy) { return false; } if ((Object)(object)((Component)_item).transform == (Object)null) { return false; } Renderer component = ((Component)_item).GetComponent<Renderer>(); if ((Object)(object)component != (Object)null && !component.enabled) { return false; } return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Error checking if item can be pinged: " + ex.Message)); return false; } } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj.GetType() != GetType()) { return false; } ItemPingableAdapter itemPingableAdapter = (ItemPingableAdapter)obj; return _item == itemPingableAdapter._item; } public override int GetHashCode() { return ((object)_item)?.GetHashCode() ?? 0; } } public class LuggagePingableAdapter : IPingable { private readonly Luggage _luggage; public Luggage Luggage => _luggage; public LuggagePingableAdapter(Luggage luggage) { _luggage = luggage ?? throw new ArgumentNullException("luggage"); } public Vector3 GetCenter() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _luggage.Center(); } public string GetDisplayName() { try { return _luggage.GetName(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get luggage name for " + ((Object)_luggage).name + ": " + ex.Message)); return ((Object)_luggage).name; } } public bool CanBePinged() { if ((Object)(object)_luggage != (Object)null && ((Component)_luggage).gameObject.activeInHierarchy) { return _luggage.IsInteractible((Character)null); } return false; } public GameObject GetGameObject() { return ((Component)_luggage).gameObject; } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj.GetType() != GetType()) { return false; } LuggagePingableAdapter luggagePingableAdapter = (LuggagePingableAdapter)obj; return _luggage == luggagePingableAdapter._luggage; } public override int GetHashCode() { return ((object)_luggage)?.GetHashCode() ?? 0; } } public class MirageLuggagePingableAdapter : IPingable { private readonly MirageLuggage _mirageLuggage; public MirageLuggage MirageLuggage => _mirageLuggage; public MirageLuggagePingableAdapter(MirageLuggage mirageLuggage) { _mirageLuggage = mirageLuggage ?? throw new ArgumentNullException("mirageLuggage"); } public Vector3 GetCenter() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return ((Component)_mirageLuggage).transform.position; } public string GetDisplayName() { try { return LocalizedText.GetText("LUGGAGE", true) ?? "Luggage"; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get mirage luggage name for " + ((Object)_mirageLuggage).name + ": " + ex.Message)); return "Luggage"; } } public bool CanBePinged() { if ((Object)(object)_mirageLuggage != (Object)null && ((Component)_mirageLuggage).gameObject.activeInHierarchy) { return ((Behaviour)_mirageLuggage).enabled; } return false; } public GameObject GetGameObject() { return ((Component)_mirageLuggage).gameObject; } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj.GetType() != GetType()) { return false; } MirageLuggagePingableAdapter mirageLuggagePingableAdapter = (MirageLuggagePingableAdapter)obj; return _mirageLuggage == mirageLuggagePingableAdapter._mirageLuggage; } public override int GetHashCode() { return ((object)_mirageLuggage)?.GetHashCode() ?? 0; } } public static class PingableFactory { public static List<IPingable> FindPingablesForPing(Vector3 pingPosition, Character pingingCharacter, float itemRadius, float luggageRadius) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) List<IPingable> allPingables = new List<IPingable>(); List<IPingable> collection = FindPingablesUnderCursor(); allPingables.AddRange(collection); List<IPingable> source = FindPingablesNearPosition(pingPosition, itemRadius, luggageRadius); allPingables.AddRange(source.Where((IPingable proximityPingable) => !ContainsSameObject(allPingables, proximityPingable))); return allPingables; } public static List<IPingable> FindPingablesNearPosition(Vector3 position, float itemRadius, float luggageRadius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) List<IPingable> pingables = new List<IPingable>(); (from item in Object.FindObjectsByType<Item>((FindObjectsSortMode)0).AsEnumerable() where IsObjectNearPosition(((Component)item).gameObject, position, itemRadius) select item).ToList().ForEach(delegate(Item item) { ItemPingableAdapter itemPingableAdapter = new ItemPingableAdapter(item); if (itemPingableAdapter.CanBePinged()) { pingables.Add(itemPingableAdapter); } }); Luggage.ALL_LUGGAGE.Where((Luggage luggage) => (Object)(object)luggage != (Object)null && IsObjectNearPosition(((Component)luggage).gameObject, position, luggageRadius)).ToList().ForEach(delegate(Luggage luggage) { LuggagePingableAdapter luggagePingableAdapter = new LuggagePingableAdapter(luggage); if (luggagePingableAdapter.CanBePinged()) { pingables.Add(luggagePingableAdapter); } }); (from mirageLuggage in Object.FindObjectsByType<MirageLuggage>((FindObjectsSortMode)0) where IsObjectNearPosition(((Component)mirageLuggage).gameObject, position, luggageRadius) select mirageLuggage).ToList().ForEach(delegate(MirageLuggage mirageLuggage) { MirageLuggagePingableAdapter mirageLuggagePingableAdapter = new MirageLuggagePingableAdapter(mirageLuggage); if (mirageLuggagePingableAdapter.CanBePinged()) { pingables.Add(mirageLuggagePingableAdapter); } }); return pingables; } private static List<IPingable> FindPingablesUnderCursor() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) List<IPingable> list = new List<IPingable>(); Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return list; } try { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((float)Screen.width / 2f, (float)Screen.height / 2f, 0f); RaycastHit[] array = Physics.RaycastAll(main.ScreenPointToRay(val), PingItemsConfig.PingVisibilityDistance.Value); for (int i = 0; i < array.Length; i++) { IPingable pingable = CreatePingableFromHit(array[i]); if (pingable != null && pingable.CanBePinged()) { list.Add(pingable); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Error during raycast detection: " + ex.Message)); } return list; } private static IPingable CreatePingableFromHit(RaycastHit hit) { GameObject gameObject = ((Component)((RaycastHit)(ref hit)).collider).gameObject; Item componentInParent = gameObject.GetComponentInParent<Item>(); if ((Object)(object)componentInParent != (Object)null) { return new ItemPingableAdapter(componentInParent); } Luggage componentInParent2 = gameObject.GetComponentInParent<Luggage>(); if ((Object)(object)componentInParent2 != (Object)null) { return new LuggagePingableAdapter(componentInParent2); } MirageLuggage componentInParent3 = gameObject.GetComponentInParent<MirageLuggage>(); if ((Object)(object)componentInParent3 != (Object)null) { return new MirageLuggagePingableAdapter(componentInParent3); } return null; } private static bool ContainsSameObject(List<IPingable> pingables, IPingable targetPingable) { GameObject targetGameObject = targetPingable?.GetGameObject(); if ((Object)(object)targetGameObject == (Object)null) { return false; } return pingables.Any((IPingable p) => (Object)(object)p?.GetGameObject() == (Object)(object)targetGameObject); } private static bool IsObjectNearPosition(GameObject gameObject, Vector3 position, float radius) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy) { return false; } return Vector3.Distance(gameObject.transform.position, position) <= radius; } } public class PingHighlighter : MonoBehaviour { [CompilerGenerated] private sealed class <LifetimeCoroutine>d__45 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PingHighlighter <>4__this; private float <elapsed>5__2; private float <displayDuration>5__3; private float <fadeOutDuration>5__4; private float <fadeElapsed>5__5; private float <startingDistanceAlpha>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LifetimeCoroutine>d__45(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; PingHighlighter pingHighlighter = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <elapsed>5__2 = 0f; <displayDuration>5__3 = PingItemsConfig.DisplayDuration.Value; <fadeOutDuration>5__4 = PingItemsConfig.FadeOutDuration.Value; goto IL_0092; case 1: <>1__state = -1; goto IL_0092; case 2: { <>1__state = -1; goto IL_0158; } IL_0092: if (<elapsed>5__2 < <displayDuration>5__3 && pingHighlighter._targetPingable != null && pingHighlighter._targetPingable.CanBePinged()) { pingHighlighter.UpdateVisualElements(); <elapsed>5__2 += Time.deltaTime; <>2__current = null; <>1__state = 1; return true; } if (pingHighlighter._targetPingable == null || !pingHighlighter._targetPingable.CanBePinged()) { break; } pingHighlighter._isFadingOut = true; <fadeElapsed>5__5 = 0f; <startingDistanceAlpha>5__6 = pingHighlighter.CalculateDistanceBasedAlpha(); goto IL_0158; IL_0158: if (<fadeElapsed>5__5 < <fadeOutDuration>5__4 && pingHighlighter._targetPingable != null && pingHighlighter._isFadingOut && pingHighlighter._targetPingable.CanBePinged()) { pingHighlighter.UpdateVisualElementsPositionsOnly(); float num2 = pingHighlighter.CalculateDistanceBasedAlpha(); float num3 = <fadeElapsed>5__5 / <fadeOutDuration>5__4; float num4 = Mathf.Lerp(<startingDistanceAlpha>5__6, 0f, num3); float alpha = Mathf.Min(num2, num4); pingHighlighter.SetAlpha(alpha); <fadeElapsed>5__5 += Time.deltaTime; <>2__current = null; <>1__state = 2; return true; } break; } pingHighlighter.ReturnToPool(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private IPingable _targetPingable; private GameObject _targetGameObject; private List<IPingable> _groupedPingables; private GameObject _reticleIndicator; private TextMeshProUGUI _label; private Coroutine _lifetimeCoroutine; private bool _isFadingOut; private bool _isCleanedUp; private float _lastKnownDistance = -1f; public Color PlayerColor { get; set; } = Color.cyan; public string PlayerName { get; internal set; } public GameObject TargetGameObject => _targetGameObject; public bool IsGroupedPing { get { if (_groupedPingables != null) { return _groupedPingables.Count > 1; } return false; } } public int GroupCount => _groupedPingables?.Count ?? 1; public void Init(IPingable pingable, Vector3 pingPoint) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) Init(new List<IPingable> { pingable }, pingPoint); } public void Init(List<IPingable> pingables, Vector3 pingPoint) { if (pingables == null || pingables.Count == 0) { Plugin.Logger.LogWarning((object)"Cannot initialize highlighter with empty pingable list"); return; } IPingable pingable = pingables[0]; GameObject val = pingable?.GetGameObject(); if ((Object)(object)_targetGameObject == (Object)(object)val && _isFadingOut) { _groupedPingables = new List<IPingable>(pingables); ResetToFullVisibility(); RestartLifetimeTimer(); return; } Cleanup(); _groupedPingables = new List<IPingable>(pingables); _targetPingable = pingable; _targetGameObject = val; _isFadingOut = false; _isCleanedUp = false; _lastKnownDistance = -1f; if (_groupedPingables != null) { foreach (IPingable groupedPingable in _groupedPingables) { if (groupedPingable is ItemPingableAdapter itemPingableAdapter) { Item item = itemPingableAdapter.Item; item.OnStateChange = (Action<ItemState>)Delegate.Combine(item.OnStateChange, new Action<ItemState>(OnItemStateChanged)); } } } CreateVisualElements(); RestartLifetimeTimer(); } private void OnItemStateChanged(ItemState newState) { try { if (!IsGroupedPing) { PingService.RemoveHighlight(this); } else { UpdateGroupAfterItemChange(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"Error in OnItemStateChanged: {arg}"); try { if ((Object)(object)((Component)this).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)this).gameObject); } } catch { } } } private void UpdateGroupAfterItemChange() { if (_groupedPingables != null) { List<IPingable> list = _groupedPingables.Where((IPingable p) => p?.CanBePinged() ?? false).ToList(); if (list.Count == 0) { PingService.RemoveHighlight(this); return; } _groupedPingables = list; _targetPingable = list[0]; _targetGameObject = _targetPingable.GetGameObject(); _lastKnownDistance = -1f; UpdateVisualElements(); } } public void Refresh(Vector3 pingPoint) { if (_isFadingOut) { ResetToFullVisibility(); } _lastKnownDistance = -1f; RestartLifetimeTimer(); UpdateVisualElements(); } public void Cleanup() { if (_isCleanedUp) { return; } _isCleanedUp = true; if (_groupedPingables != null) { foreach (IPingable groupedPingable in _groupedPingables) { if (groupedPingable is ItemPingableAdapter itemPingableAdapter) { try { Item item = itemPingableAdapter.Item; item.OnStateChange = (Action<ItemState>)Delegate.Remove(item.OnStateChange, new Action<ItemState>(OnItemStateChanged)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Error unsubscribing from item state change: " + ex.Message)); } } } } StopLifetimeTimer(); DestroyVisualElements(); ResetState(); } private void CreateVisualElements() { if (_targetPingable != null) { UIManager instance = UIManager.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Logger.LogError((object)"UIManager not available for creating visual elements"); return; } CreateReticleIndicator(instance); CreateLabel(instance); UpdateVisualElements(); } } private void CreateReticleIndicator(UIManager uiManager) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) _reticleIndicator = uiManager.CreateReticleIndicator(PlayerColor); if ((Object)(object)_reticleIndicator != (Object)null) { PositionReticleAboveItem(); } } private void CreateLabel(UIManager uiManager) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) _label = uiManager.RentLabel(); if ((Object)(object)_label != (Object)null) { ((Graphic)_label).color = PlayerColor; UpdateLabelContent(); PositionLabelOnScreen(); } else { Plugin.Logger.LogWarning((object)("Label creation failed during game startup - showing reticle-only ping for " + (_targetPingable?.GetDisplayName() ?? "unknown item"))); } } private void UpdateVisualElements() { if (_targetPingable != null) { float alpha = CalculateDistanceBasedAlpha(); if ((Object)(object)_reticleIndicator != (Object)null) { PositionReticleAboveItem(); ApplyAlphaToReticle(alpha); } if ((Object)(object)_label != (Object)null) { PositionLabelOnScreen(); ApplyAlphaToLabel(alpha); UpdateLabelContentIfNeeded(); } } } private void UpdateVisualElementsPositionsOnly() { if (_targetPingable != null) { if ((Object)(object)_reticleIndicator != (Object)null) { PositionReticleAboveItem(); } if ((Object)(object)_label != (Object)null) { PositionLabelOnScreen(); UpdateLabelContentIfNeeded(); } } } private void PositionReticleAboveItem() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_reticleIndicator == (Object)null || _targetPingable == null) { return; } Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } Vector3 center = _targetPingable.GetCenter(); Vector3 val = main.WorldToScreenPoint(center); if (val.z < 0f) { _reticleIndicator.SetActive(false); return; } RectTransform component = _reticleIndicator.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { UIManager instance = UIManager.Instance; if ((Object)(object)instance?.CanvasRect != (Object)null) { Vector2 anchoredPosition = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(instance.CanvasRect, Vector2.op_Implicit(val), (Camera)null, ref anchoredPosition); anchoredPosition.y += 30f; component.anchoredPosition = anchoredPosition; } } _reticleIndicator.SetActive(true); } private void UpdateLabelContent() { if (!((Object)(object)_label == (Object)null) && _targetPingable != null) { string text = ((TMP_Text)_label).text; string text2 = BuildLabelText(); if (text != text2) { ((TMP_Text)_label).text = text2; } } } private void UpdateLabelContentWithDistance(float distance) { if (!((Object)(object)_label == (Object)null) && _targetPingable != null) { string text = ((TMP_Text)_label).text; string text2 = BuildLabelText(distance); if (text != text2) { ((TMP_Text)_label).text = text2; } } } private void UpdateLabelContentIfNeeded() { if (!((Object)(object)_label == (Object)null) && _targetPingable != null) { float distanceToPlayer = GetDistanceToPlayer(); float value = PingItemsConfig.DistanceUpdateThreshold.Value; if (_lastKnownDistance < 0f || Mathf.Abs(distanceToPlayer - _lastKnownDistance) >= value) { _lastKnownDistance = distanceToPlayer; UpdateLabelContentWithDistance(distanceToPlayer); } } } private string BuildLabelText(float? distance = null) { List<string> list = new List<string>(); if (PingItemsConfig.ShowPlayerName.Value && !string.IsNullOrEmpty(PlayerName)) { list.Add(PlayerName); } if (PingItemsConfig.ShowItemNames.Value) { string localizedItemName = GetLocalizedItemName(); if (!string.IsNullOrEmpty(localizedItemName)) { if (IsGroupedPing) { list.Add($"{GroupCount}x {localizedItemName}"); } else { list.Add(localizedItemName); } } } if (PingItemsConfig.ShowDistance.Value) { float num = distance ?? GetDistanceToPlayer(); list.Add($"{num:F1}m"); } return string.Join("\\n", list); } private string GetLocalizedItemName() { if (_targetPingable == null) { return string.Empty; } try { return _targetPingable.GetDisplayName(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to get localized object name: " + ex.Message)); GameObject gameObject = _targetPingable.GetGameObject(); return ((gameObject != null) ? ((Object)gameObject).name : null) ?? "Unknown"; } } private float GetDistanceToPlayer() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (_targetPingable == null) { return 0f; } Vector3 center = _targetPingable.GetCenter(); Vector3 localPlayerPosition = GetLocalPlayerPosition(); return Vector3.Distance(center, localPlayerPosition); } private static Vector3 GetLocalPlayerPosition() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null) { return localCharacter.Center; } Player localPlayer = Player.localPlayer; object obj; if (localPlayer == null) { obj = null; } else { Character character = localPlayer.character; obj = ((character != null) ? ((Component)character).transform : null); } if ((Object)obj != (Object)null) { return ((Component)localPlayer.character).transform.position; } Camera main = Camera.main; if ((Object)(object)main != (Object)null) { return ((Component)main).transform.position; } Plugin.Logger.LogWarning((object)"Unable to determine player position - using Vector3.zero"); return Vector3.zero; } private void PositionLabelOnScreen() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_label == (Object)null || _targetPingable == null) { return; } UIManager instance = UIManager.Instance; Camera main = Camera.main; if ((Object)(object)instance?.CanvasRect == (Object)null || (Object)(object)main == (Object)null) { ((Component)_label).gameObject.SetActive(false); return; } Vector3 center = _targetPingable.GetCenter(); Vector3 val = main.WorldToScreenPoint(center); if (val.z > 0f) { Vector2 anchoredPosition = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(instance.CanvasRect, Vector2.op_Implicit(val), (Camera)null, ref anchoredPosition); anchoredPosition.y -= 60f; ((TMP_Text)_label).rectTransform.anchoredPosition = anchoredPosition; ((Component)_label).gameObject.SetActive(true); } else { ((Component)_label).gameObject.SetActive(false); } } private void RestartLifetimeTimer() { StopLifetimeTimer(); _lifetimeCoroutine = ((MonoBehaviour)this).StartCoroutine(LifetimeCoroutine()); } private void StopLifetimeTimer() { if (_lifetimeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_lifetimeCoroutine); _lifetimeCoroutine = null; } } [IteratorStateMachine(typeof(<LifetimeCoroutine>d__45))] private IEnumerator LifetimeCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LifetimeCoroutine>d__45(0) { <>4__this = this }; } private void SetAlpha(float alpha) { //IL_0054: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_reticleIndicator != (Object)null) { Image[] componentsInChildren = _reticleIndicator.GetComponentsInChildren<Image>(); foreach (Image obj in componentsInChildren) { Color color = ((Graphic)obj).color; color.a = alpha; ((Graphic)obj).color = color; } } if ((Object)(object)_label != (Object)null) { Color color2 = ((Graphic)_label).color; color2.a = alpha; ((Graphic)_label).color = color2; } } private void ResetToFullVisibility() { _isFadingOut = false; SetAlpha(1f); } private float CalculateDistanceBasedAlpha() { if (_targetPingable == null) { return 1f; } float distanceToPlayer = GetDistanceToPlayer(); float value = PingItemsConfig.PingVisibilityDistance.Value; if (distanceToPlayer <= value * 0.8f) { return 1f; } if (distanceToPlayer >= value) { return 0f; } float num = value * 0.8f; float num2 = value - num; float num3 = (distanceToPlayer - num) / num2; return Mathf.SmoothStep(1f, 0f, num3); } private void ApplyAlphaToReticle(float alpha) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_reticleIndicator == (Object)null)) { Image[] componentsInChildren = _reticleIndicator.GetComponentsInChildren<Image>(); foreach (Image obj in componentsInChildren) { Color playerColor = PlayerColor; playerColor.a = alpha; ((Graphic)obj).color = playerColor; } } } private void ApplyAlphaToLabel(float alpha) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) if (!((Object)(object)_label == (Object)null)) { Color color = ((Graphic)_label).color; color.a = alpha; ((Graphic)_label).color = color; } } private void DestroyVisualElements() { if ((Object)(object)_reticleIndicator != (Object)null) { Object.Destroy((Object)(object)_reticleIndicator); _reticleIndicator = null; } if ((Object)(object)_label != (Object)null) { UIManager instance = UIManager.Instance; if ((Object)(object)instance != (Object)null) { instance.ReleaseLabel(_label); } else { Object.Destroy((Object)(object)((Component)_label).gameObject); } _label = null; } } private void ResetState() { _targetPingable = null; _targetGameObject = null; _isFadingOut = false; _isCleanedUp = false; _lastKnownDistance = -1f; } private void ReturnToPool() { PingService.RemoveHighlight(this); } private void OnDestroy() { Cleanup(); } } internal class UIManager : MonoBehaviour { private readonly Queue<PingHighlighter> _highlighterPool = new Queue<PingHighlighter>(); private readonly Queue<TextMeshProUGUI> _labelPool = new Queue<TextMeshProUGUI>(); private TMP_FontAsset _cachedGameFont; private Canvas _overlayCanvas; private RectTransform _canvasRect; private GameObject _reticlePrefab; public static UIManager Instance { get; private set; } public RectTransform CanvasRect { get { EnsureCanvas(); return _canvasRect; } } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)this); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); InitializeUI(); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void InitializeUI() { EnsureCanvas(); CaptureGameResources(); } private void EnsureCanvas() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (!((Object)(object)_overlayCanvas != (Object)null)) { GameObject val = GameObject.Find("PingItems_OverlayCanvas"); if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject("PingItems_OverlayCanvas"); _overlayCanvas = val2.AddComponent<Canvas>(); _overlayCanvas.renderMode = (RenderMode)0; _overlayCanvas.sortingOrder = 1000; val2.AddComponent<CanvasScaler>(); val2.AddComponent<GraphicRaycaster>(); } else { _overlayCanvas = val.GetComponent<Canvas>() ?? val.AddComponent<Canvas>(); } _canvasRect = ((Component)_overlayCanvas).GetComponent<RectTransform>(); } } private void CaptureGameResources() { } public void SetReticlePrefab(GameObject reticle) { if ((Object)(object)reticle != (Object)null) { _reticlePrefab = reticle; } } public void SetGameFont(TMP_FontAsset font) { if ((Object)(object)font != (Object)null) { _cachedGameFont = font; Plugin.Logger.LogInfo((object)("Game font captured: " + ((Object)font).name)); } } public PingHighlighter RentHighlighter() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (_highlighterPool.Count > 0) { PingHighlighter pingHighlighter = _highlighterPool.Dequeue(); if (IsHighlighterValid(pingHighlighter)) { ((Component)pingHighlighter).gameObject.SetActive(true); return pingHighlighter; } Plugin.Logger.LogWarning((object)"Discarded invalid highlighter from pool during game startup"); } return new GameObject("PingHighlighter").AddComponent<PingHighlighter>(); } public void ReleaseHighlighter(PingHighlighter highlighter) { if (!((Object)(object)highlighter == (Object)null)) { ((Component)highlighter).gameObject.SetActive(false); _highlighterPool.Enqueue(highlighter); } } public GameObject CreateReticleIndicator(Color playerColor) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) EnsureCanvas(); if ((Object)(object)_reticlePrefab != (Object)null) { return CreateGameReticle(playerColor); } return CreateFallbackReticle(playerColor); } public TextMeshProUGUI RentLabel() { EnsureCanvas(); if (_labelPool.Count > 0) { TextMeshProUGUI val = _labelPool.Dequeue(); if (IsLabelValid(val)) { ResetLabel(val); return val; } Plugin.Logger.LogWarning((object)"Discarded invalid label from pool during game startup"); } TextMeshProUGUI obj = CreateNewLabel(); if ((Object)(object)obj == (Object)null) { Plugin.Logger.LogWarning((object)"Failed to create new label during game startup - ping may show only reticle"); } return obj; } public void ReleaseLabel(TextMeshProUGUI label) { if (!((Object)(object)label == (Object)null)) { ((Component)label).gameObject.SetActive(false); ((TMP_Text)label).text = string.Empty; _labelPool.Enqueue(label); } } private GameObject CreateGameReticle(Color playerColor) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(_reticlePrefab); ((Object)val).name = "PingReticle"; val.transform.SetParent(((Component)_overlayCanvas).transform, false); RectTransform component = val.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { Vector2 sizeDelta = component.sizeDelta; if (sizeDelta == Vector2.zero) { ((Vector2)(ref sizeDelta))..ctor(50f, 50f); } component.sizeDelta = sizeDelta * PingItemsConfig.ReticleScale.Value; } else { val.transform.localScale = Vector3.one * PingItemsConfig.ReticleScale.Value; } ApplyReticleEffects(val, playerColor); return val; } private GameObject CreateFallbackReticle(Color playerColor) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0032: 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_0052: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("PingReticleFallback"); val.transform.SetParent(((Component)_overlayCanvas).transform, false); val.AddComponent<RectTransform>().sizeDelta = new Vector2(50f, 50f) * PingItemsConfig.ReticleScale.Value; Image obj = val.AddComponent<Image>(); ((Graphic)obj).color = playerColor; TrySetCircleSprite(obj); if (PingItemsConfig.EnableOutlines.Value) { AddOutlineEffect(val); } return val; } private static void ApplyReticleEffects(GameObject reticle, Color playerColor) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) Image[] componentsInChildren = reticle.GetComponentsInChildren<Image>(); foreach (Image val in componentsInChildren) { ((Graphic)val).color = playerColor; if (PingItemsConfig.EnableOutlines.Value) { AddOutlineEffect(((Component)val).gameObject); } } Renderer[] componentsInChildren2 = reticle.GetComponentsInChildren<Renderer>(); foreach (Renderer val2 in componentsInChildren2) { if ((Object)(object)val2.sharedMaterial != (Object)null) { Material val3 = new Material(val2.sharedMaterial); SetMaterialColor(val3, playerColor); val2.sharedMaterial = val3; } } } private TextMeshProUGUI CreateNewLabel() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) try { EnsureCanvas(); if ((Object)(object)_canvasRect == (Object)null) { Plugin.Logger.LogError((object)"Cannot create label: Canvas rect is null during game startup"); return null; } GameObject val = new GameObject("PingLabel"); val.transform.SetParent((Transform)(object)_canvasRect, false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).fontSize = PingItemsConfig.LabelFontSize.Value; ((Graphic)val2).raycastTarget = false; if ((Object)(object)_cachedGameFont != (Object)null) { ((TMP_Text)val2).font = _cachedGameFont; } if (PingItemsConfig.EnableOutlines.Value) { ApplyTextOutline(val2); } return val2; } catch (Exception ex) { Plugin.Logger.LogError((object)("Failed to create new label during game startup: " + ex.Message)); return null; } } private void ResetLabel(TextMeshProUGUI label) { if ((Object)(object)label == (Object)null || (Object)(object)((Component)label).gameObject == (Object)null) { Plugin.Logger.LogWarning((object)"Attempted to reset null or destroyed label"); return; } try { ((Component)label).gameObject.SetActive(true); ((TMP_Text)label).text = string.Empty; ((TMP_Text)label).fontSize = PingItemsConfig.LabelFontSize.Value; if ((Object)(object)_cachedGameFont != (Object)null && (Object)(object)((TMP_Text)label).font != (Object)(object)_cachedGameFont) { ((TMP_Text)label).font = _cachedGameFont; } if (PingItemsConfig.EnableOutlines.Value) { ApplyTextOutline(label); } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error resetting label: " + ex.Message)); throw; } } private static bool IsLabelValid(TextMeshProUGUI label) { if ((Object)(object)label == (Object)null) { return false; } try { return (Object)(object)((Component)label).gameObject != (Object)null; } catch { return false; } } private static bool IsHighlighterValid(PingHighlighter highlighter) { if ((Object)(object)highlighter == (Object)null) { return false; } try { return (Object)(object)((Component)highlighter).gameObject != (Object)null; } catch { return false; } } private static void AddOutlineEffect(GameObject uiElement) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Outline val = uiElement.GetComponent<Outline>(); if ((Object)(object)val == (Object)null) { val = uiElement.AddComponent<Outline>(); } ((Shadow)val).effectColor = Color.white; ((Shadow)val).effectDistance = new Vector2(1.5f, -1.5f); ((Shadow)val).useGraphicAlpha = true; } private static void ApplyTextOutline(TextMeshProUGUI textComponent) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)((TMP_Text)textComponent).fontMaterial == (Object)null)) { Material val = new Material(((TMP_Text)textComponent).fontMaterial); if (val.HasProperty("_OutlineWidth")) { val.SetFloat("_OutlineWidth", PingItemsConfig.OutlineWidth.Value); } if (val.HasProperty("_OutlineColor")) { val.SetColor("_OutlineColor", Color.white); } if (val.HasProperty("_OutlineSoftness")) { val.SetFloat("_OutlineSoftness", 0.05f); } ((TMP_Text)textComponent).fontMaterial = val; } } private static void TrySetCircleSprite(Image image) { try { Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>(); foreach (Sprite val in array) { string text = ((Object)val).name.ToLower(); if (text.Contains("circle") || text.Contains("dot") || text.Contains("round")) { image.sprite = val; break; } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to find circle sprite: " + ex.Message)); } } private static void SetMaterialColor(Material material, Color color) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) try { if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } if (material.HasProperty("_MainColor")) { material.SetColor("_MainColor", color); } material.renderQueue = 4000; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to set material color: " + ex.Message)); } } } } namespace PingItems.Configuration { public static class PingItemsConfig { private static string DISPLAY_SECTION = "Display"; private static string DETECTION_SECTION = "Detection"; private static string VISUAL_SECTION = "Visual"; public static ConfigEntry<float> DisplayDuration { get; private set; } public static ConfigEntry<float> FadeOutDuration { get; private set; } public static ConfigEntry<float> ReticleScale { get; private set; } public static ConfigEntry<float> LabelFontSize { get; private set; } public static ConfigEntry<float> DetectionRadius { get; private set; } public static ConfigEntry<float> LuggageDetectionRadius { get; private set; } public static ConfigEntry<bool> ShowPlayerName { get; private set; } public static ConfigEntry<bool> ShowDistance { get; private set; } public static ConfigEntry<bool> ShowItemNames { get; private set; } public static ConfigEntry<float> DistanceUpdateThreshold { get; private set; } public static ConfigEntry<float> PingVisibilityDistance { get; private set; } public static ConfigEntry<bool> EnableItemGrouping { get; private set; } public static ConfigEntry<float> GroupingRadius { get; private set; } public static ConfigEntry<bool> EnableOutlines { get; private set; } public static ConfigEntry<float> OutlineWidth { get; private set; } public static void Initialize(ConfigFile config) { DisplayDuration = config.Bind<float>(DISPLAY_SECTION, "Duration", 10f, "How long the ping indicator remains visible (seconds)"); FadeOutDuration = config.Bind<float>(DISPLAY_SECTION, "FadeOutDuration", 2f, "Duration of the fade-out animation (seconds)"); ReticleScale = config.Bind<float>(DISPLAY_SECTION, "ReticleScale", 5f, "Scale multiplier for reticle size (1.0 = original size)"); ShowPlayerName = config.Bind<bool>(DISPLAY_SECTION, "ShowPlayerName", true, "Show the name of the player who pinged in the label"); ShowDistance = config.Bind<bool>(DISPLAY_SECTION, "ShowDistance", true, "Show distance to item in the label"); ShowItemNames = config.Bind<bool>(DISPLAY_SECTION, "ShowItemNames", true, "Show localized item names in the label"); LabelFontSize = config.Bind<float>(DISPLAY_SECTION, "LabelFontSize", 32f, "Font size for item labels"); DetectionRadius = config.Bind<float>(DETECTION_SECTION, "Radius", 1.2f, "Maximum distance from ping to detect items (game units)"); LuggageDetectionRadius = config.Bind<float>(DETECTION_SECTION, "LuggageRadius", 2.2f, "Maximum distance from ping to detect luggage objects (game units). Should be larger than Radius since luggage is bigger."); DistanceUpdateThreshold = config.Bind<float>(DETECTION_SECTION, "DistanceUpdateThreshold", 0.05f, "Distance change threshold to trigger label updates (meters). Lower = more sensitive, higher = less sensitive"); PingVisibilityDistance = config.Bind<float>(DETECTION_SECTION, "PingVisibilityDistance", 100f, "Maximum distance from ping point to show the ping indicator (in game units)."); EnableItemGrouping = config.Bind<bool>(DETECTION_SECTION, "EnableItemGrouping", true, "Group multiple items of the same type into a single ping with count (e.g., '3x Coconut')"); GroupingRadius = config.Bind<float>(DETECTION_SECTION, "GroupingRadius", 5f, "Maximum distance between items of the same type to group them together (game units)"); EnableOutlines = config.Bind<bool>(VISUAL_SECTION, "EnableOutlines", true, "Enable outline effects on text and reticles"); OutlineWidth = config.Bind<float>(VISUAL_SECTION, "OutlineWidth", 0.065f, "Width of text outline effect"); } } }