Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LookWhatIGot v1.0.0
LookWhatIGot.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using LookWhatIGot.Patches; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LookWhatIGot")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("LookWhatIGot")] [assembly: AssemblyTitle("LookWhatIGot")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace LookWhatIGot { public static class HoldingLabelManager { private const bool UseAnchorLayer = false; private const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static FieldInfo grabbedObjectField; private static FieldInfo valueField; private static Font labelFont; private static bool initialized; private static readonly Dictionary<Transform, GameObject> labels = new Dictionary<Transform, GameObject>(); private static PhysGrabObject lastGrabbed; private static float nextCleanup; private static readonly HashSet<string> UselessNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Rigidbody", "Physics", "Mesh", "Model", "Object", "Collider", "Colliders", "Body", "Root", "Visuals", "Graphics" }; private static readonly HashSet<string> LevelPrefixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Museum", "Manor", "Wizard", "Arena", "Arctic", "Swamp", "Mansion", "Hotel", "Space", "Lab", "Fight", "Level", "Generic", "Valuable" }; private static bool OnlyShowValuables => LookWhatIGotPlugin.CfgOnlyShowValuables?.Value ?? true; private static bool DebugPinToCamera => LookWhatIGotPlugin.CfgDebugPinToCamera?.Value ?? false; private static float LabelHeight => LookWhatIGotPlugin.CfgLabelHeight?.Value ?? 2.3f; private static int LabelLayer => LookWhatIGotPlugin.CfgLabelLayer?.Value ?? 0; public static void Initialize() { if (!initialized) { initialized = true; CacheFont(); CacheReflection(); } } private static void CacheFont() { string[] array = new string[2] { "LegacyRuntime.ttf", "Arial.ttf" }; foreach (string text in array) { try { labelFont = Resources.GetBuiltinResource<Font>(text); if ((Object)(object)labelFont != (Object)null) { LookWhatIGotPlugin.Dbg("Using builtin font: " + text); return; } } catch (Exception) { } } labelFont = Font.CreateDynamicFontFromOSFont("Arial", 32); if ((Object)(object)labelFont == (Object)null) { LookWhatIGotPlugin.Log.LogError((object)"No font found - labels will be invisible."); } else { LookWhatIGotPlugin.Dbg("Using OS font: Arial"); } } private static void CacheReflection() { FieldInfo[] fields = typeof(PhysGrabber).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(PhysGrabObject).IsAssignableFrom(fieldInfo.FieldType)) { grabbedObjectField = fieldInfo; LookWhatIGotPlugin.Dbg("Grabbed-object field: " + fieldInfo.Name); break; } } if (grabbedObjectField == null) { LookWhatIGotPlugin.Log.LogError((object)"No PhysGrabObject field on PhysGrabber!"); DumpFields(typeof(PhysGrabber)); } string[] array = new string[3] { "dollarValueCurrent", "dollarValue", "valueCurrent" }; string[] array2 = array; foreach (string name in array2) { valueField = typeof(ValuableObject).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (valueField != null) { LookWhatIGotPlugin.Dbg("Value field: " + valueField.Name); break; } } if (valueField == null) { FieldInfo[] fields2 = typeof(ValuableObject).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo2 in fields2) { string text = fieldInfo2.Name.ToLowerInvariant(); if ((fieldInfo2.FieldType == typeof(float) || fieldInfo2.FieldType == typeof(int)) && (text.Contains("dollar") || text.Contains("value"))) { valueField = fieldInfo2; LookWhatIGotPlugin.Dbg("Value field (fallback): " + fieldInfo2.Name); break; } } } if (valueField == null) { LookWhatIGotPlugin.Log.LogWarning((object)"No value field on ValuableObject."); DumpFields(typeof(ValuableObject)); } } private static void DumpFields(Type t) { StringBuilder stringBuilder = new StringBuilder("--- fields on " + t.Name + " ---\n"); FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { stringBuilder.AppendLine(" " + fieldInfo.FieldType.Name + " " + fieldInfo.Name); } LookWhatIGotPlugin.Log.LogInfo((object)stringBuilder.ToString()); } public static void Tick(PhysGrabber grabber) { PhysGrabObject grabbedObject = GetGrabbedObject(grabber); if ((Object)(object)grabbedObject != (Object)(object)lastGrabbed) { OnLocalGrabChanged(grabbedObject); lastGrabbed = grabbedObject; } UpdateBillboards(); PeriodicCleanup(); } private static void OnLocalGrabChanged(PhysGrabObject grabbedObj) { Transform val = ResolveLocalAnchor(); ValuableObject val2 = (((Object)(object)grabbedObj != (Object)null) ? FindValuable(grabbedObj) : null); if ((Object)(object)grabbedObj == (Object)null || (OnlyShowValuables && (Object)(object)val2 == (Object)null)) { if ((Object)(object)val != (Object)null) { HideLabel(val); } LabelNetwork.BroadcastHide(); return; } string itemName = CleanItemName(grabbedObj, val2); int value = ReadValue(val2); if ((Object)(object)val != (Object)null) { ShowLabel(val, itemName, value); } LabelNetwork.BroadcastShow(itemName, value); } private static PhysGrabObject GetGrabbedObject(PhysGrabber grabber) { if (grabbedObjectField == null) { return null; } object? value = grabbedObjectField.GetValue(grabber); return (PhysGrabObject)((value is PhysGrabObject) ? value : null); } private static ValuableObject FindValuable(PhysGrabObject obj) { ValuableObject component = ((Component)obj).GetComponent<ValuableObject>(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)obj).GetComponentInParent<ValuableObject>(); } private static string CleanItemName(PhysGrabObject obj, ValuableObject valuable) { string text = (((Object)(object)valuable != (Object)null) ? ((Object)((Component)valuable).gameObject).name : ((Object)((Component)obj).gameObject).name); text = text.Replace("(Clone)", "").Trim(); if (UselessNames.Contains(text)) { Transform parent = ((Component)obj).transform.parent; while ((Object)(object)parent != (Object)null) { string text2 = ((Object)parent).name.Replace("(Clone)", "").Trim(); if (!UselessNames.Contains(text2)) { text = text2; break; } parent = parent.parent; } } text = SplitPascalCase(text); string[] array = text.Split(new char[3] { ' ', '_', '-' }, StringSplitOptions.RemoveEmptyEntries); int i; for (i = 0; i < array.Length - 1 && LevelPrefixes.Contains(array[i]); i++) { } StringBuilder stringBuilder = new StringBuilder(); for (int j = i; j < array.Length; j++) { if (stringBuilder.Length > 0) { stringBuilder.Append(' '); } stringBuilder.Append(TitleCaseWord(array[j])); } string text3 = stringBuilder.ToString().Trim(); return (text3.Length > 0) ? text3 : text; } private static string SplitPascalCase(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length + 8); for (int i = 0; i < s.Length; i++) { if (i > 0 && char.IsUpper(s[i]) && !char.IsUpper(s[i - 1]) && s[i - 1] != ' ') { stringBuilder.Append(' '); } stringBuilder.Append(s[i]); } return stringBuilder.ToString(); } private static string TitleCaseWord(string w) { if (w.Length == 0) { return w; } if (w.Length <= 3 && w.ToUpperInvariant() == w) { return w; } return char.ToUpper(w[0], CultureInfo.InvariantCulture) + w.Substring(1).ToLowerInvariant(); } private static int ReadValue(ValuableObject valuable) { if ((Object)(object)valuable == (Object)null || valueField == null) { return 0; } object value = valueField.GetValue(valuable); if (value is float num) { return Mathf.RoundToInt(num); } if (value is int result) { return result; } return 0; } private static Transform ResolveLocalAnchor() { if (DebugPinToCamera) { Camera camera = GetCamera(); return ((Object)(object)camera != (Object)null) ? ((Component)camera).transform : null; } PhysGrabber instance = PhysGrabber.instance; if ((Object)(object)instance == (Object)null) { return null; } PlayerAvatar componentInParent = ((Component)instance).GetComponentInParent<PlayerAvatar>(); if ((Object)(object)componentInParent != (Object)null) { return ((Component)componentInParent).transform; } return ((Component)instance).transform.root; } internal static Camera GetCamera() { if ((Object)(object)Camera.main != (Object)null) { return Camera.main; } if ((Object)(object)Camera.current != (Object)null) { return Camera.current; } return (Camera.allCamerasCount > 0) ? Camera.allCameras[0] : null; } private static void UpdateBillboards() { //IL_0034: 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_0071: Unknown result type (might be due to invalid IL or missing references) if (labels.Count == 0 || DebugPinToCamera) { return; } Camera camera = GetCamera(); if ((Object)(object)camera == (Object)null) { return; } Quaternion rotation = ((Component)camera).transform.rotation; foreach (KeyValuePair<Transform, GameObject> label in labels) { if ((Object)(object)label.Value != (Object)null) { label.Value.transform.rotation = rotation; } } } private static void PeriodicCleanup() { if (Time.time < nextCleanup) { return; } nextCleanup = Time.time + 5f; List<Transform> list = null; foreach (KeyValuePair<Transform, GameObject> label in labels) { if ((Object)(object)label.Key == (Object)null || (Object)(object)label.Value == (Object)null) { list = list ?? new List<Transform>(); list.Add(label.Key); } } if (list == null) { return; } foreach (Transform item in list) { labels.Remove(item); } LookWhatIGotPlugin.Dbg($"Cleaned up {list.Count} stale label(s)"); } public static void ShowLabel(Transform anchor, string itemName, int value) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0071: 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) //IL_0082: 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_0093: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_0185: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)anchor == (Object)null)) { HideLabel(anchor); GameObject val = new GameObject("LookWhatIGot_Label"); val.layer = LabelLayer; val.transform.SetParent(anchor, false); val.transform.localPosition = (DebugPinToCamera ? new Vector3(0f, 0f, 2f) : new Vector3(0f, LabelHeight, 0f)); val.transform.localRotation = Quaternion.identity; Vector3 lossyScale = anchor.lossyScale; val.transform.localScale = new Vector3(Mathf.Approximately(lossyScale.x, 0f) ? 1f : (1f / lossyScale.x), Mathf.Approximately(lossyScale.y, 0f) ? 1f : (1f / lossyScale.y), Mathf.Approximately(lossyScale.z, 0f) ? 1f : (1f / lossyScale.z)); TextMesh val2 = val.AddComponent<TextMesh>(); val2.text = ((value > 0) ? $"Look what I got\n{itemName}\n${value}" : ("Look what I got\n" + itemName)); val2.font = labelFont; val2.fontSize = 64; val2.characterSize = 0.03f; val2.anchor = (TextAnchor)4; val2.alignment = (TextAlignment)1; val2.color = new Color(1f, 0.92f, 0.2f); val2.fontStyle = (FontStyle)1; MeshRenderer component = val.GetComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null && (Object)(object)labelFont != (Object)null) { ((Renderer)component).material = labelFont.material; ((Renderer)component).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)component).receiveShadows = false; } labels[anchor] = val; LookWhatIGotPlugin.Dbg($"Label shown -> {itemName} (${value}) parent={((Object)anchor).name}"); } } public static void HideLabel(Transform anchor) { if (!((Object)(object)anchor == (Object)null) && labels.TryGetValue(anchor, out GameObject value)) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } labels.Remove(anchor); } } } public static class LabelNetwork { private const byte EventCode = 173; private static bool subscribed; private static string lastSentName; private static int lastSentValue = -1; private static bool lastSentVisible; public static void Initialize() { if (subscribed) { return; } try { PhotonNetwork.NetworkingClient.EventReceived += OnEvent; subscribed = true; LookWhatIGotPlugin.Dbg($"Photon event handler registered (code {(byte)173})"); } catch (Exception ex) { LookWhatIGotPlugin.Log.LogError((object)("Could not register Photon handler: " + ex.Message)); } } public static void BroadcastShow(string itemName, int value) { if (!lastSentVisible || !(lastSentName == itemName) || lastSentValue != value) { lastSentVisible = true; lastSentName = itemName; lastSentValue = value; Send(visible: true, itemName, value); } } public static void BroadcastHide() { if (lastSentVisible) { lastSentVisible = false; lastSentName = null; lastSentValue = -1; Send(visible: false, string.Empty, 0); } } private static void Send(bool visible, string itemName, int value) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0057: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsConnected || !PhotonNetwork.InRoom) { return; } try { object[] array = new object[4] { PhotonNetwork.LocalPlayer.ActorNumber, visible, itemName, value }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; PhotonNetwork.RaiseEvent((byte)173, (object)array, val, SendOptions.SendReliable); } catch (Exception ex) { LookWhatIGotPlugin.Log.LogError((object)("RaiseEvent failed: " + ex.Message)); } } private static void OnEvent(EventData photonEvent) { if (photonEvent.Code != 173) { return; } try { if (!(photonEvent.CustomData is object[] array) || array.Length < 4) { LookWhatIGotPlugin.Log.LogWarning((object)"Malformed label event ignored."); return; } int num = (int)array[0]; bool flag = (bool)array[1]; string itemName = (string)array[2]; int value = (int)array[3]; if (num != PhotonNetwork.LocalPlayer.ActorNumber) { PlayerAvatar val = FindAvatarByActor(num); if ((Object)(object)val == (Object)null) { LookWhatIGotPlugin.Log.LogWarning((object)$"No PlayerAvatar found for actor {num}"); } else if (flag) { HoldingLabelManager.ShowLabel(((Component)val).transform, itemName, value); } else { HoldingLabelManager.HideLabel(((Component)val).transform); } } } catch (Exception arg) { LookWhatIGotPlugin.Log.LogError((object)$"Event handling failed: {arg}"); } } private static PlayerAvatar FindAvatarByActor(int actorNumber) { PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>(); PlayerAvatar[] array2 = array; foreach (PlayerAvatar val in array2) { PhotonView val2 = ((Component)val).GetComponent<PhotonView>() ?? ((Component)val).GetComponentInParent<PhotonView>() ?? ((Component)val).GetComponentInChildren<PhotonView>(); if ((Object)(object)val2 != (Object)null && val2.Owner != null && val2.Owner.ActorNumber == actorNumber) { return val; } } return null; } } [BepInPlugin("com.odinplus.lookwhatigot", "Look What I Got", "1.0.0")] public class LookWhatIGotPlugin : BaseUnityPlugin { private const string MyGuid = "com.odinplus.lookwhatigot"; private const string PluginName = "Look What I Got"; private const string VersionString = "1.0.0"; private const string BuildTag = "complete. Structural integrity questionable. Confidence level: 0%."; internal static ManualLogSource Log; private readonly Harmony harmony = new Harmony("com.odinplus.lookwhatigot"); internal static ConfigEntry<bool> CfgOnlyShowValuables; internal static ConfigEntry<float> CfgLabelHeight; internal static ConfigEntry<int> CfgLabelLayer; internal static ConfigEntry<bool> CfgVerbose; internal static ConfigEntry<bool> CfgDebugPinToCamera; internal static bool Verbose => CfgVerbose?.Value ?? false; private void BindConfig() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown CfgOnlyShowValuables = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Only Show Valuables", true, "Ignore doors, hinges, carts and other non-treasure. Turn off if you want every single grab announced."); CfgLabelHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "Label Height", 2.3f, new ConfigDescription("How far above the player the text floats.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 6f), Array.Empty<object>())); CfgLabelLayer = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "Label Layer", 0, new ConfigDescription("Render layer for the text. Only worth touching if labels vanish behind geometry.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 31), Array.Empty<object>())); CfgVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Verbose Logging", false, "Full diagnostic output in the BepInEx console. Leave off unless something's broken."); CfgDebugPinToCamera = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug Pin To Camera", false, "Pins the label in front of your own eyes instead of on your avatar. Testing aid only."); } internal static void Dbg(string message) { if (Verbose && Log != null) { Log.LogInfo((object)message); } } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; BindConfig(); Log.LogInfo((object)"Look What I Got v1.0.0 loaded! (build complete. Structural integrity questionable. Confidence level: 0%.)"); HoldingLabelManager.Initialize(); LabelNetwork.Initialize(); GrabPatches.ApplyPatches(harmony); } } } namespace LookWhatIGot.Patches { public static class GrabPatches { private const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; public static void ApplyPatches(Harmony harmony) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown MethodInfo methodInfo = null; string[] array = new string[3] { "Update", "LateUpdate", "FixedUpdate" }; foreach (string text in array) { methodInfo = AccessTools.Method(typeof(PhysGrabber), text, (Type[])null, (Type[])null); if (methodInfo != null) { LookWhatIGotPlugin.Dbg("Patching PhysGrabber." + text); break; } } if (methodInfo == null) { LookWhatIGotPlugin.Log.LogError((object)"PhysGrabber has no Update/LateUpdate/FixedUpdate to patch!"); DumpMethods(typeof(PhysGrabber)); return; } try { MethodInfo methodInfo2 = AccessTools.Method(typeof(GrabPatches), "TickPostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LookWhatIGotPlugin.Dbg("Patch applied successfully."); } catch (Exception arg) { LookWhatIGotPlugin.Log.LogError((object)$"Patch failed: {arg}"); } } private static void TickPostfix(PhysGrabber __instance) { try { if (!((Object)(object)__instance != (Object)(object)PhysGrabber.instance)) { HoldingLabelManager.Tick(__instance); } } catch (Exception arg) { LookWhatIGotPlugin.Log.LogError((object)$"Tick threw: {arg}"); } } private static void DumpMethods(Type t) { StringBuilder stringBuilder = new StringBuilder("--- methods on " + t.Name + " ---\n"); MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { stringBuilder.AppendLine($" {methodInfo.ReturnType.Name} {methodInfo.Name}({methodInfo.GetParameters().Length} args)"); } LookWhatIGotPlugin.Log.LogInfo((object)stringBuilder.ToString()); } } }