Decompiled source of Unstuck v0.2.1

Unstuck.dll

Decompiled 6 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Unstuck;

[BepInPlugin("com.burks.unstuck", "Unstuck", "0.2.1")]
public class UnstuckPlugin : BaseUnityPlugin
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleEvent <>9__22_0;

		internal void <Update>b__22_0(ConsoleEventArgs a)
		{
			Begin(a.Context);
		}
	}

	public const string GUID = "com.burks.unstuck";

	public const string NAME = "Unstuck";

	public const string VERSION = "0.2.1";

	public static ManualLogSource Log;

	public static ConfigEntry<KeyCode> Key;

	public static ConfigEntry<float> WaitSeconds;

	public static ConfigEntry<float> CooldownSeconds;

	public static ConfigEntry<float> MaxDistance;

	public static ConfigEntry<float> MaxClimb;

	public static ConfigEntry<float> CalmSeconds;

	private static readonly FieldRef<Character, Rigidbody> Body = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body");

	private static readonly FieldRef<Character, float> MaxAir = AccessTools.FieldRefAccess<Character, float>("m_maxAirAltitude");

	private static bool _channel;

	private static float _lastMax = -1f;

	private static float _endsAt;

	private static float _readyAt;

	private static float _lastHurt = -999f;

	private static float _lastHealth = -1f;

	private static float _lastShown;

	private static Vector3 _startPos;

	private static bool _cmd;

	private void Awake()
	{
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Invalid comparison between Unknown and I4
		Log = ((BaseUnityPlugin)this).Logger;
		Key = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "Key", (KeyCode)0, "Optional hotkey. The console command 'unstuck' always works.");
		WaitSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "WaitSeconds", 10f, "You must stand still and unharmed this long before you are moved.");
		CooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "CooldownSeconds", 300f, "Time between uses.");
		MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "MaxDistance", 10f, "Furthest you can be moved, in metres. The nearest safe spot is always used.");
		MaxClimb = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "MaxClimb", 2f, "The new spot can be at most this many metres higher than where you are, so it cannot be used to climb.");
		CalmSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "CalmSeconds", 15f, "Not usable until this long after you were last hurt.");
		if ((int)SystemInfo.graphicsDeviceType == 4)
		{
			((Behaviour)this).enabled = false;
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Unstuck 0.2.1 loaded.");
		}
	}

	private void Update()
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			_channel = false;
			_lastHealth = -1f;
			return;
		}
		if (!_cmd)
		{
			_cmd = true;
			try
			{
				object obj = <>c.<>9__22_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs a)
					{
						Begin(a.Context);
					};
					<>c.<>9__22_0 = val;
					obj = (object)val;
				}
				new ConsoleCommand("unstuck", "free your character if it is stuck (stand still for a few seconds)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not add the 'unstuck' command: " + ex.Message));
			}
		}
		try
		{
			MenuButton.Ensure();
		}
		catch (Exception ex2)
		{
			if (!MenuButton.Failed)
			{
				MenuButton.Failed = true;
				Log.LogWarning((object)("Could not add the menu button, using a plain one instead: " + ex2.Message));
			}
		}
		float health = ((Character)localPlayer).GetHealth();
		float maxHealth = ((Character)localPlayer).GetMaxHealth();
		float num = ((_lastMax > 0f) ? Mathf.Max(0f, _lastMax - maxHealth) : 0f);
		if (_lastHealth >= 0f && _lastHealth - health > num + 1f)
		{
			_lastHurt = Time.time;
		}
		_lastHealth = health;
		_lastMax = maxHealth;
		try
		{
			if ((int)Key.Value != 0 && Input.GetKeyDown(Key.Value) && !Console.IsVisible() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus()))
			{
				Begin(null);
			}
			if (_channel)
			{
				Channel(localPlayer);
			}
		}
		catch (Exception ex3)
		{
			_channel = false;
			Log.LogWarning((object)("Unstuck failed: " + ex3));
		}
	}

	private static void Tell(Terminal ctx, string text)
	{
		if ((Object)(object)ctx != (Object)null)
		{
			ctx.AddString(text);
		}
		if ((Object)(object)MessageHud.instance != (Object)null)
		{
			MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
		}
	}

	private static string Blocked(Player me)
	{
		if (((Character)me).IsDead() || ((Character)me).IsTeleporting() || ((Character)me).InBed() || me.IsSleeping())
		{
			return "You can't do that right now.";
		}
		if (((Character)me).IsAttached())
		{
			return "Stand up or let go first.";
		}
		if (((Character)me).InInterior())
		{
			return "Unstuck doesn't work inside dungeons.";
		}
		if (Time.time - _lastHurt < Mathf.Max(0f, CalmSeconds.Value))
		{
			return "You were hurt too recently.";
		}
		if (me.IsTargeted() || me.IsSensed())
		{
			return "Not while enemies are aware of you.";
		}
		return null;
	}

	private void OnGUI()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		if (MenuButton.Failed && !((Object)(object)Player.m_localPlayer == (Object)null) && Menu.IsVisible() && GUI.Button(new Rect((float)Screen.width / 2f - 90f, (float)Screen.height - 90f, 180f, 40f), "Unstuck"))
		{
			MenuButton.Click();
		}
	}

	public static void Begin(Terminal ctx)
	{
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		if (_channel)
		{
			Tell(ctx, "Already freeing you - stand still.");
			return;
		}
		float num = _readyAt - Time.time;
		if (num > 0f)
		{
			Tell(ctx, "Unstuck is ready again in " + Mathf.CeilToInt(num) + " s.");
			return;
		}
		string text = Blocked(localPlayer);
		if (text != null)
		{
			Tell(ctx, text);
			return;
		}
		if (!FindSpot(localPlayer, out var _))
		{
			Tell(ctx, "No safe spot close by.");
			return;
		}
		_channel = true;
		_startPos = ((Component)localPlayer).transform.position;
		_endsAt = Time.time + Mathf.Clamp(WaitSeconds.Value, 3f, 120f);
		_lastShown = 0f;
		Tell(ctx, "Freeing you in " + Mathf.CeilToInt(_endsAt - Time.time) + " s. Stand still.");
	}

	private static void Channel(Player me)
	{
		//IL_0028: 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)
		//IL_0032: 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)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		string text = Blocked(me);
		if (text != null)
		{
			_channel = false;
			Tell(null, "Unstuck cancelled. " + text);
			return;
		}
		Vector3 val = ((Component)me).transform.position - _startPos;
		if (((Vector3)(ref val)).magnitude > 1.5f)
		{
			_channel = false;
			Tell(null, "Unstuck cancelled - you moved.");
			return;
		}
		float num = _endsAt - Time.time;
		if (num > 0f)
		{
			if (Time.time - _lastShown >= 1f)
			{
				_lastShown = Time.time;
				Tell(null, "Freeing you in " + Mathf.CeilToInt(num) + " s");
			}
			return;
		}
		_channel = false;
		if (!FindSpot(me, out var best))
		{
			Tell(null, "No safe spot close by.");
			return;
		}
		Vector3 position = ((Component)me).transform.position;
		((Component)me).transform.position = best;
		Rigidbody val2 = Body.Invoke((Character)(object)me);
		if ((Object)(object)val2 != (Object)null)
		{
			val2.position = best;
			val2.velocity = Vector3.zero;
		}
		Physics.SyncTransforms();
		MaxAir.Invoke((Character)(object)me) = best.y;
		_readyAt = Time.time + Mathf.Max(0f, CooldownSeconds.Value);
		Tell(null, "You are free.");
		ManualLogSource log = Log;
		string[] obj = new string[5] { "Moved ", null, null, null, null };
		val = best - position;
		obj[1] = ((Vector3)(ref val)).magnitude.ToString("0.0");
		obj[2] = " m (";
		obj[3] = (best.y - position.y).ToString("0.0");
		obj[4] = " m up).";
		log.LogInfo((object)string.Concat(obj));
	}

	private static bool FindSpot(Player me, out Vector3 best)
	{
		//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_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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: 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)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		best = Vector3.zero;
		if ((Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return false;
		}
		Vector3 position = ((Component)me).transform.position;
		float waterLevel = ZoneSystem.instance.m_waterLevel;
		float num = Mathf.Clamp(MaxDistance.Value, 3f, 30f);
		float num2 = Mathf.Clamp(MaxClimb.Value, 0f, 10f);
		bool flag = PrivateArea.CheckAccess(position, 0f, false, false);
		float num7 = default(float);
		Vector3 val2 = default(Vector3);
		GameObject val3 = default(GameObject);
		for (float num3 = 2f; num3 <= num + 0.01f; num3 += 1f)
		{
			int num4 = Mathf.Max(8, Mathf.RoundToInt(num3 * 4f));
			float num5 = Random.Range(0f, (float)Math.PI * 2f);
			for (int i = 0; i < num4; i++)
			{
				float num6 = num5 + (float)i * (float)Math.PI * 2f / (float)num4;
				Vector3 val = position + new Vector3(Mathf.Cos(num6), 0f, Mathf.Sin(num6)) * num3;
				if (ZNetScene.instance.IsAreaReady(val) && ZoneSystem.instance.GetSolidHeight(val, ref num7, ref val2, ref val3) && !(num7 < waterLevel - 0.5f) && !(num7 - position.y > num2) && !(position.y - num7 > 30f) && !(val2.y < 0.7f))
				{
					val.y = num7 + 0.3f;
					if ((!flag || PrivateArea.CheckAccess(val, 0f, false, false)) && !Physics.CheckCapsule(val + Vector3.up * 0.5f, val + Vector3.up * 1.5f, 0.3f, LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "terrain", "vehicle" })))
					{
						best = val;
						return true;
					}
				}
			}
		}
		return false;
	}
}
public static class MenuButton
{
	public static bool Failed;

	private static GameObject _button;

	public static void Ensure()
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Expected O, but got Unknown
		if (!Failed && !((Object)(object)_button != (Object)null) && !((Object)(object)Menu.instance == (Object)null))
		{
			Button settingsButton = Menu.instance.m_settingsButton;
			if ((Object)(object)settingsButton == (Object)null)
			{
				throw new Exception("no Settings button to copy");
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)settingsButton).gameObject, ((Component)settingsButton).transform.parent);
			((Object)val).name = "UnstuckButton";
			val.transform.SetSiblingIndex(((Component)settingsButton).transform.GetSiblingIndex() + 1);
			Button component = val.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			((UnityEvent)component.onClick).AddListener(new UnityAction(Click));
			TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].text = "Unstuck";
			}
			_button = val;
			UnstuckPlugin.Log.LogInfo((object)"Added the Unstuck button to the menu.");
		}
	}

	public static void Click()
	{
		try
		{
			if ((Object)(object)Menu.instance != (Object)null)
			{
				Menu.instance.Hide();
			}
		}
		catch (Exception)
		{
		}
		UnstuckPlugin.Begin(null);
	}
}