Decompiled source of LootPulse v1.0.7

plugins/HiarlyScripter-LootPulse/LootPulse.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[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("LootPulse")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+775dd9c5dfdb8222af9eee00db9dd7384abd60f0")]
[assembly: AssemblyProduct("LootPulse")]
[assembly: AssemblyTitle("LootPulse")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LootPulse;

[BepInPlugin("com.hiarlyscripter.repo.lootpulse", "LootPulse", "1.0.6")]
public sealed class LootPulsePlugin : BaseUnityPlugin
{
	internal static ConfigEntry<KeyCode> ScanKey;

	internal static ConfigEntry<float> ScanRange;

	internal static ConfigEntry<float> CooldownSeconds;

	internal static ConfigEntry<bool> EnableVisualBrackets;

	internal static ConfigEntry<bool> EnableMapIcons;

	internal static ConfigEntry<bool> RequireLevelOrRun;

	internal static ConfigEntry<bool> VerboseLogging;

	private float _lastScanTime = -999f;

	private int _lastTickFrame = -1;

	private bool _harmonyLoopLogged;

	private bool _pluginLoopLogged;

	private static string _patchedMethodName;

	private static bool _discoverApiChecked;

	private static MethodInfo _discoverWithState;

	private static MethodInfo _discoverNoParam;

	private static object _discoverStateValue;

	internal static LootPulsePlugin Instance { get; private set; }

	internal static ManualLogSource Log { get; private set; }

	private void Awake()
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Expected O, but got Unknown
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_024c: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		ScanKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Scanner", "ScanKey", (KeyCode)102, "Key that triggers the valuable scanner.");
		ScanRange = ((BaseUnityPlugin)this).Config.Bind<float>("Scanner", "ScanRange", 30f, new ConfigDescription("Detection radius in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 200f), Array.Empty<object>()));
		CooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Scanner", "CooldownSeconds", 4f, new ConfigDescription("Minimum seconds between consecutive scans.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
		EnableVisualBrackets = ((BaseUnityPlugin)this).Config.Bind<bool>("Scanner", "EnableVisualBrackets", true, "Display visual discovery brackets on valuables found.");
		EnableMapIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("Scanner", "EnableMapIcons", true, "Add valuable icon to the map when scanning.");
		RequireLevelOrRun = ((BaseUnityPlugin)this).Config.Bind<bool>("Scanner", "RequireLevelOrRun", true, "Only scan during active runs (generated level). Disable only for testing outside a level.");
		VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Scanner", "VerboseLogging", false, "Log details for each individually processed item.");
		Log.LogInfo((object)$"v1.0.6 loaded. Press {ScanKey.Value} to scan valuables.");
		Log.LogInfo((object)($"Config | key={ScanKey.Value} | range={ScanRange.Value} | cooldown={CooldownSeconds.Value} | " + $"visualBrackets={EnableVisualBrackets.Value} | mapIcons={EnableMapIcons.Value} | " + $"requireLevelOrRun={RequireLevelOrRun.Value} | verbose={VerboseLogging.Value}"));
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		Log.LogInfo((object)"Build marker: v1.0.6-release");
		Log.LogInfo((object)("Assembly location: " + executingAssembly.Location));
		try
		{
			Log.LogInfo((object)$"Assembly file time UTC: {File.GetLastWriteTimeUtc(executingAssembly.Location):yyyy-MM-dd HH:mm:ss}");
		}
		catch
		{
		}
		InstallHarmonyPatch(new Harmony("com.hiarlyscripter.repo.lootpulse"));
	}

	private void Update()
	{
		if (!_pluginLoopLogged)
		{
			_pluginLoopLogged = true;
			Log.LogInfo((object)"Update loop active. (plugin MonoBehaviour)");
		}
		Tick("Plugin.Update");
	}

	internal void TickFromGameLoop(string source)
	{
		if (!_harmonyLoopLogged)
		{
			_harmonyLoopLogged = true;
			Log.LogInfo((object)("Harmony game loop active: " + source));
		}
		Tick(source);
	}

	private void Tick(string source)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Invalid comparison between Unknown and I4
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		int frameCount = Time.frameCount;
		if (frameCount == _lastTickFrame)
		{
			return;
		}
		_lastTickFrame = frameCount;
		bool keyDown = Input.GetKeyDown(ScanKey.Value);
		if (!keyDown && (int)ScanKey.Value != 102)
		{
			keyDown = Input.GetKeyDown((KeyCode)102);
		}
		if (!keyDown)
		{
			return;
		}
		Log.LogInfo((object)$"Scan key detected: {ScanKey.Value} | source={source}");
		float time = Time.time;
		float num = time - _lastScanTime;
		if (num < CooldownSeconds.Value)
		{
			Log.LogInfo((object)$"Scan blocked: cooldown active ({CooldownSeconds.Value - num:F1}s remaining)");
			return;
		}
		if (RequireLevelOrRun.Value)
		{
			bool flag = false;
			try
			{
				flag = SemiFunc.RunIsLevel();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("RunIsLevel check failed (" + ex.Message + "). Proceeding without validation."));
				flag = true;
			}
			if (!flag)
			{
				Log.LogInfo((object)"Scan blocked: not in active level/run (RequireLevelOrRun=true)");
				return;
			}
		}
		_lastScanTime = time;
		RunScan();
	}

	private static void InstallHarmonyPatch(Harmony harmony)
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Expected O, but got Unknown
		(string, string)[] obj = new(string, string)[3]
		{
			("GameDirector", "Update"),
			("PlayerAvatar", "Update"),
			("PlayerController", "Update")
		};
		HarmonyMethod val = new HarmonyMethod(typeof(LootPulsePlugin).GetMethod("HarmonyTickPostfix", BindingFlags.Static | BindingFlags.NonPublic));
		(string, string)[] array = obj;
		for (int i = 0; i < array.Length; i++)
		{
			var (text, text2) = array[i];
			try
			{
				Type type = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int j = 0; j < assemblies.Length; j++)
				{
					type = assemblies[j].GetType(text);
					if (type != null)
					{
						break;
					}
				}
				if (type == null)
				{
					Log.LogWarning((object)("Harmony fallback: type not found — " + text));
					continue;
				}
				MethodInfo method = type.GetMethod(text2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method == null)
				{
					Log.LogWarning((object)("Harmony fallback: method not found — " + text + "." + text2));
					continue;
				}
				harmony.Patch((MethodBase)method, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_patchedMethodName = text + "." + text2;
				Log.LogInfo((object)("Harmony tick patch installed: " + _patchedMethodName));
				string watchedMethod = _patchedMethodName;
				ThreadPool.QueueUserWorkItem(delegate
				{
					Thread.Sleep(60000);
					if ((Object)(object)Instance != (Object)null && !Instance._harmonyLoopLogged)
					{
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogWarning((object)("WARNING: Harmony patch installed but no game loop tick observed yet. Patched method: " + watchedMethod));
						}
					}
				});
				return;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Harmony patch failed for " + text + "." + text2 + ": " + ex.Message));
			}
		}
		Log.LogError((object)"ERROR: no valid game loop method found. LootPulse cannot scan.");
	}

	private static void HarmonyTickPostfix()
	{
		Instance?.TickFromGameLoop(_patchedMethodName);
	}

	private void RunScan()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		Vector3 zero = Vector3.zero;
		try
		{
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if ((Object)(object)val == (Object)null)
			{
				Log.LogInfo((object)"Scan blocked: local player not found");
				return;
			}
			if ((Object)(object)val.playerTransform == (Object)null)
			{
				Log.LogInfo((object)"Scan blocked: local player not found (transform null)");
				return;
			}
			zero = val.playerTransform.position;
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Scan blocked: local player not found (" + ex.Message + ")"));
			return;
		}
		List<PhysGrabObject> list = null;
		bool flag = false;
		try
		{
			list = SemiFunc.PhysGrabObjectAllValuablesWithinRange(ScanRange.Value, zero, false, default(LayerMask));
			flag = true;
		}
		catch (Exception ex2)
		{
			Log.LogWarning((object)("PhysGrabObjectAllValuablesWithinRange failed (" + ex2.Message + "). Using fallback."));
			try
			{
				list = FindValuablesFallback(zero, ScanRange.Value);
			}
			catch (Exception ex3)
			{
				Log.LogWarning((object)("Scan blocked: scan API unavailable (" + ex3.Message + ")"));
				return;
			}
		}
		if (list == null)
		{
			Log.LogWarning((object)"Scan blocked: scan API unavailable (null result)");
			return;
		}
		int count = list.Count;
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		Log.LogInfo((object)($"Scan executed | origin={zero:F1} | range={ScanRange.Value}m | " + string.Format("api={0} | found={1}", flag ? "SemiFunc" : "FindObjectsOfType", count)));
		foreach (PhysGrabObject item in list)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			try
			{
				ValuableObject component = ((Component)item).GetComponent<ValuableObject>();
				if ((Object)(object)component == (Object)null)
				{
					if (VerboseLogging.Value)
					{
						Log.LogDebug((object)("No ValuableObject on: " + ((Object)item).name));
					}
					continue;
				}
				if (VerboseLogging.Value)
				{
					Log.LogDebug((object)("Processing: " + ((Object)item).name));
				}
				if (EnableVisualBrackets.Value && TryApplyBrackets(component))
				{
					num++;
				}
				if (EnableMapIcons.Value && TryAddToMap(component))
				{
					num2++;
				}
			}
			catch (Exception ex4)
			{
				num3++;
				Log.LogWarning((object)("Error processing " + ((item != null) ? ((Object)item).name : null) + ": " + ex4.Message));
			}
		}
		Log.LogInfo((object)$"Result | brackets={num}/{count} | map={num2}/{count} | errors={num3}");
	}

	private static bool TryApplyBrackets(ValuableObject vo)
	{
		try
		{
			ResolveDiscoverApi();
			if (_discoverWithState != null)
			{
				_discoverWithState.Invoke(vo, new object[1] { _discoverStateValue });
				return true;
			}
			if (_discoverNoParam != null)
			{
				_discoverNoParam.Invoke(vo, null);
				return true;
			}
			return false;
		}
		catch (Exception ex)
		{
			if (VerboseLogging.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("Discover exception: " + ex.Message));
				}
			}
			return false;
		}
	}

	private static void ResolveDiscoverApi()
	{
		if (_discoverApiChecked)
		{
			return;
		}
		_discoverApiChecked = true;
		Type typeFromHandle = typeof(ValuableObject);
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public;
		Type type = null;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			type = assemblies[i].GetType("ValuableDiscoverGraphic");
			if (type != null)
			{
				break;
			}
		}
		if (type != null)
		{
			Type nestedType = type.GetNestedType("State", BindingFlags.Public);
			if (nestedType != null && nestedType.IsEnum)
			{
				MethodInfo method = typeFromHandle.GetMethod("Discover", bindingAttr, null, new Type[1] { nestedType }, null);
				if (method != null)
				{
					_discoverWithState = method;
					_discoverStateValue = Enum.Parse(nestedType, "Discover");
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)"Discover API resolved: ValuableObject.Discover(ValuableDiscoverGraphic.State)");
					}
					return;
				}
			}
		}
		MethodInfo method2 = typeFromHandle.GetMethod("Discover", bindingAttr, null, Type.EmptyTypes, null);
		if (method2 != null)
		{
			_discoverNoParam = method2;
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"Discover API resolved: ValuableObject.Discover() [no-param fallback]");
			}
		}
		else
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogWarning((object)"Discover API not found in any form. Visual brackets disabled.");
			}
		}
	}

	private static bool TryAddToMap(ValuableObject vo)
	{
		try
		{
			Map instance = Map.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				if (VerboseLogging.Value)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogDebug((object)"Map.Instance is null.");
					}
				}
				return false;
			}
			instance.AddValuable(vo);
			return true;
		}
		catch (Exception ex)
		{
			if (VerboseLogging.Value)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogDebug((object)("Map.AddValuable failed: " + ex.Message));
				}
			}
			return false;
		}
	}

	private static List<PhysGrabObject> FindValuablesFallback(Vector3 origin, float range)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		List<PhysGrabObject> list = new List<PhysGrabObject>();
		float num = range * range;
		ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
		foreach (ValuableObject val in array)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Vector3 val2 = ((Component)val).transform.position - origin;
			if (!(((Vector3)(ref val2)).sqrMagnitude > num))
			{
				PhysGrabObject component = ((Component)val).GetComponent<PhysGrabObject>();
				if ((Object)(object)component != (Object)null)
				{
					list.Add(component);
				}
			}
		}
		ManualLogSource log = Log;
		if (log != null)
		{
			log.LogInfo((object)$"Fallback: {list.Count} valuables found via FindObjectsOfType.");
		}
		return list;
	}
}