Decompiled source of Odins Long Drop v0.1.0

plugins/OdinsLongDrop.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("OdinsLongDrop")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+1ab61fc24b8bfec3aefe11a9768c25d4d04073ad")]
[assembly: AssemblyProduct("OdinsLongDrop")]
[assembly: AssemblyTitle("OdinsLongDrop")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 OdinsLongDrop
{
	internal static class HudText
	{
		private sealed class Scale : MonoBehaviour
		{
			private const float ReferenceWidth = 1920f;

			private const float ReferenceHeight = 1080f;

			private static readonly FieldRef<float> GameGuiScale = TryFindGuiScale();

			internal CanvasScaler Scaler;

			private float _logged = -1f;

			private void Update()
			{
				if (!((Object)(object)Scaler == (Object)null))
				{
					float num = ((GameGuiScale != null) ? GameGuiScale.Invoke() : 1f);
					if (num <= 0f)
					{
						num = 1f;
					}
					float num2 = Mathf.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f) * num * Plugin.TextScale.Value;
					if (Mathf.Abs(Scaler.scaleFactor - num2) > 0.0001f)
					{
						Scaler.scaleFactor = num2;
					}
					if (Plugin.DebugLog.Value && Mathf.Abs(_logged - num2) > 0.0001f)
					{
						_logged = num2;
						Plugin.Log.LogInfo((object)$"Text scale {num2:0.###}: screen {Screen.width}x{Screen.height}, game GUI scale {num:0.##}, TextScale {Plugin.TextScale.Value:0.##}.");
					}
				}
			}

			private static FieldRef<float> TryFindGuiScale()
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale");
				if (fieldInfo == null || fieldInfo.FieldType != typeof(float) || !fieldInfo.IsStatic)
				{
					Plugin.Log.LogWarning((object)"Couldn't find the game's GUI scale; text follows the screen size only.");
					return null;
				}
				return AccessTools.StaticFieldRefAccess<float>(fieldInfo);
			}
		}

		private static Canvas _canvas;

		public static TMP_Text Create(string name, Transform owner)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = (((Object)(object)MessageHud.instance != (Object)null) ? MessageHud.instance.m_messageCenterText : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.font == (Object)null || !EnsureCanvas(owner))
			{
				return null;
			}
			GameObject val2 = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(((Component)_canvas).transform, false);
			TextMeshProUGUI obj = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = val.font;
			((TMP_Text)obj).fontSharedMaterial = val.fontSharedMaterial;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)1;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).outlineWidth = 0.15f;
			((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0.5f, 0.5f);
			val3.anchoredPosition = Vector2.zero;
			return (TMP_Text)(object)obj;
		}

		private static bool EnsureCanvas(Transform owner)
		{
			//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_0026: 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)
			if ((Object)(object)_canvas != (Object)null)
			{
				return true;
			}
			GameObject val = new GameObject("OdinsLongDrop_Canvas");
			val.transform.SetParent(owner, false);
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 5000;
			CanvasScaler val2 = val.AddComponent<CanvasScaler>();
			val2.uiScaleMode = (ScaleMode)0;
			val.AddComponent<Scale>().Scaler = val2;
			Plugin.Log.LogInfo((object)"On-screen text layer created.");
			return true;
		}
	}
	internal sealed class LongDrop : MonoBehaviour
	{
		private enum Kind
		{
			None,
			Ledge,
			Falling
		}

		private const float ScanInterval = 0.1f;

		private const float SafeDrop = 4f;

		private const float FullDamageAfter = 16f;

		private const float FullDamage = 100f;

		private const float ProbeHeight = 0.6f;

		private const float Step = 0.5f;

		private const int SlopeWindow = 2;

		private const float LandingRun = 1.5f;

		private const float MaxLipDistance = 3f;

		private const float MinReach = 6f;

		private const float MaxReach = 60f;

		private const float AimRange = 200f;

		private const int ProfilePoints = 24;

		private const float LedgeLogEvery = 1f;

		private const float MaxRay = 1000f;

		private const float Linger = 0.35f;

		private const float NewDropAfter = 2f;

		private const float RemarkCooldown = 10f;

		private const float FeetPerMetre = 3.28084f;

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

		private static readonly string[] LedgeLines = new string[4] { "{0}. About {1} damage. You have {2}. Odin would take the stairs.", "{0} down, {2} health to pay with. The ground charges {1}.", "{0}. About {1} damage, and you have {2}. Odin is only mentioning it.", "That's {0}. The landing costs about {1}. You have {2}. Odin has done the sums." };

		private static readonly string[] FallingLines = new string[3] { "{0}. About {1} damage. You have {2}. Too late for the stairs.", "About {1} damage on the way, {2} health to meet it. Odin will tell the skalds it was deliberate.", "{0}, {1} damage, {2} health. Odin did mention it. Well, he's mentioning it now." };

		private Readout _readout;

		private OdinLine _line;

		private int _groundMask;

		private int _waterMask;

		private int _blockMask;

		private float _nextScan;

		private Kind _showing;

		private bool _showingFatal;

		private float _lastSeen = -100f;

		private bool _warned;

		private float _nextRemarkAt;

		private string _lastRemark;

		private float _nextLedgeLog;

		private readonly StringBuilder _why = new StringBuilder();

		internal static LongDrop Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			_readout = ((Component)this).gameObject.AddComponent<Readout>();
			_line = ((Component)this).gameObject.AddComponent<OdinLine>();
			_groundMask = LayerMask.GetMask(new string[7] { "Default", "static_solid", "Default_small", "piece", "terrain", "blocker", "vehicle" });
			_waterMask = LayerMask.GetMask(new string[1] { "Water" });
			_blockMask = LayerMask.GetMask(new string[5] { "piece", "Default", "static_solid", "Default_small", "terrain" });
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			if (Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 0.1f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !Plugin.Enabled.Value || Busy(localPlayer))
			{
				Clear(now: true);
				return;
			}
			float drop;
			Kind kind = Look(localPlayer, out drop);
			if (kind == Kind.None)
			{
				if (_showing == Kind.Ledge && !((Character)localPlayer).IsOnGround() && !((Character)localPlayer).IsSwimming())
				{
					_lastSeen = Time.time;
				}
				Clear(now: false);
			}
			else
			{
				Show(localPlayer, kind, drop);
			}
		}

		private Kind Look(Player player, out float drop)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: 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_006a: 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)
			drop = 0f;
			if (((Character)player).IsAttached() || ((Character)player).IsSwimming())
			{
				return Kind.None;
			}
			Vector3 position = ((Component)player).transform.position;
			float value = Plugin.MinDrop.Value;
			if (!((Character)player).IsOnGround())
			{
				if (!Plugin.ShowWhileFalling.Value)
				{
					return Kind.None;
				}
				float num = MaxAirAltitude.Invoke((Character)(object)player);
				if (num - position.y < value || !SurfaceBelow(position + Vector3.up * 0.1f, out var height))
				{
					return Kind.None;
				}
				drop = num - height;
				return Kind.Falling;
			}
			bool flag = Plugin.DebugLog.Value && Time.time >= _nextLedgeLog;
			_why.Length = 0;
			Kind result = Ledge(player, position, value, flag, out drop);
			if (flag && _why.Length > 0)
			{
				_nextLedgeLog = Time.time + 1f;
				Plugin.Log.LogInfo((object)_why.ToString());
			}
			return result;
		}

		private Kind Ledge(Player player, Vector3 feet, float min, bool log, out float drop)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			drop = 0f;
			Vector3 lookDir = ((Character)player).GetLookDir();
			float num = Mathf.Asin(Mathf.Clamp(0f - lookDir.y, -1f, 1f)) * 57.29578f;
			float value = Plugin.LookDownAngle.Value;
			if (num < value)
			{
				return Kind.None;
			}
			if (!Plugin.ShowAtLedge.Value)
			{
				if (log)
				{
					_why.Append("Ledge check: looking down, but ShowAtLedge is off in the config.");
				}
				return Kind.None;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(lookDir.x, 0f, lookDir.z);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				((Vector3)(ref val))..ctor(((Component)player).transform.forward.x, 0f, ((Component)player).transform.forward.z);
			}
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				return Kind.None;
			}
			((Vector3)(ref val)).Normalize();
			float value2 = Plugin.SlopeAngle.Value;
			float num2 = Mathf.Tan(value2 * ((float)Math.PI / 180f)) * 0.5f * 2f;
			string aim;
			float num3 = Reach(feet, out aim);
			int num4 = Mathf.CeilToInt(num3 / 0.5f);
			if (log)
			{
				_why.Append($"Ledge check, looking {num:0} deg down (needs {value:0}), slope limit {value2:0} deg, standing on {Describe(((Character)player).GetLastGroundCollider())} at y {feet.y:0.0}, {aim}:");
			}
			float[] array = new float[num4 + 1];
			array[0] = feet.y;
			Vector3 val2 = feet + Vector3.up * 0.6f;
			Vector3 val3 = val2;
			int num5 = -1;
			int num6 = -1;
			int num7 = 0;
			string text = null;
			RaycastHit val5 = default(RaycastHit);
			for (int i = 1; i <= num4; i++)
			{
				float num8 = (float)i * 0.5f;
				Vector3 val4 = val2 + val * num8;
				if (Physics.Linecast(val3, val4, ref val5, _blockMask, (QueryTriggerInteraction)1))
				{
					if (num5 < 0)
					{
						if (log)
						{
							_why.Append($" {num8:0.0} m: blocked by {Describe(((RaycastHit)(ref val5)).collider)} before any edge. No ledge.");
						}
						return Kind.None;
					}
					num6 = i - 1;
					text = $"runs into {Describe(((RaycastHit)(ref val5)).collider)} at {num8:0.0} m";
					break;
				}
				val3 = val4;
				if (!SurfaceBelow(val4, out var height, out var what))
				{
					height = array[i - 1];
				}
				array[i] = height;
				int num9 = Mathf.Max(0, i - 2);
				bool flag = array[num9] - height > num2 * (float)(i - num9) / 2f;
				if (num5 < 0)
				{
					if (flag)
					{
						num5 = i;
					}
					else if (num8 > 3f)
					{
						if (log)
						{
							_why.Append($" standable ground for {3f:0} m: not at an edge.{Profile(array, feet.y, i)}");
						}
						return Kind.None;
					}
				}
				else if (flag)
				{
					num7 = 0;
				}
				else
				{
					num7++;
					if ((float)num7 * 0.5f >= 1.5f)
					{
						num6 = i - num7 + 1;
						text = $"levels out at {(float)num6 * 0.5f:0.0} m on {what}";
						break;
					}
				}
			}
			if (num5 < 0)
			{
				if (log)
				{
					_why.Append($" no edge within {num3:0} m.{Profile(array, feet.y, num4)}");
				}
				return Kind.None;
			}
			if (num6 < 0)
			{
				num6 = num4;
				text = $"still going at {(float)num4 * 0.5f:0.0} m, so counted to the end of the line";
			}
			float num10 = array[Mathf.Max(0, num5 - 1)];
			drop = num10 - array[num6];
			if (log)
			{
				_why.Append($" edge at {(float)(num5 - 1) * 0.5f:0.0} m (y {num10:0.0}), {text}, y {array[num6]:0.0}: {drop:0.0} m down");
			}
			if (drop < min)
			{
				if (log)
				{
					_why.Append($", under MinDrop {min:0.#} m. No readout.{Profile(array, feet.y, num6)}");
				}
				drop = 0f;
				return Kind.None;
			}
			if (log)
			{
				_why.Append(". A ledge." + Profile(array, feet.y, num6));
			}
			return Kind.Ledge;
		}

		private float Reach(Vector3 feet, out string aim)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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)
			GameCamera instance = GameCamera.instance;
			if ((Object)(object)instance == (Object)null)
			{
				aim = "no camera";
				return 6f;
			}
			Transform transform = ((Component)instance).transform;
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(transform.position, transform.forward, ref val, 200f, _groundMask, (QueryTriggerInteraction)1))
			{
				aim = "crosshair on nothing";
				return 60f;
			}
			Vector3 val2 = ((RaycastHit)(ref val)).point - feet;
			val2.y = 0f;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			aim = $"crosshair on {Describe(((RaycastHit)(ref val)).collider)} {magnitude:0.0} m out, {feet.y - ((RaycastHit)(ref val)).point.y:0.0} m down";
			return Mathf.Clamp(magnitude + 0.5f, 6f, 60f);
		}

		private static string Profile(float[] heights, float feet, int last)
		{
			if (!Plugin.DebugLog.Value || last < 1)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(" Profile (m out: m down):");
			int num = Mathf.Max(1, Mathf.CeilToInt((float)last / 24f));
			for (int i = num; i <= last; i += num)
			{
				stringBuilder.Append($" {(float)i * 0.5f:0.#}: {feet - heights[i]:0.0}");
			}
			return stringBuilder.ToString();
		}

		private static string Describe(Collider collider)
		{
			if ((Object)(object)collider == (Object)null)
			{
				return "nothing known";
			}
			string text = LayerMask.LayerToName(((Component)collider).gameObject.layer);
			Transform root = ((Component)collider).transform.root;
			if (!((Object)(object)root != (Object)null) || !((Object)(object)root != (Object)(object)((Component)collider).transform))
			{
				return ((Object)collider).name + " (" + text + ")";
			}
			return ((Object)collider).name + " (" + text + ", in " + ((Object)root).name + ")";
		}

		private bool SurfaceBelow(Vector3 from, out float height)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string what;
			return SurfaceBelow(from, out height, out what);
		}

		private bool SurfaceBelow(Vector3 from, out float height, out string what)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			height = float.MinValue;
			what = null;
			bool result = false;
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(from, Vector3.down, ref val, 1000f, _groundMask, (QueryTriggerInteraction)1))
			{
				height = ((RaycastHit)(ref val)).point.y;
				what = (Plugin.DebugLog.Value ? Describe(((RaycastHit)(ref val)).collider) : null);
				result = true;
			}
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(from, Vector3.down, ref val2, 1000f, _waterMask, (QueryTriggerInteraction)2) && ((RaycastHit)(ref val2)).point.y > height)
			{
				height = ((RaycastHit)(ref val2)).point.y;
				what = "water";
				result = true;
			}
			return result;
		}

		private static float Damage(Player player, float drop)
		{
			if (drop <= 4f)
			{
				return 0f;
			}
			float num = Mathf.Clamp01((drop - 4f) / 16f) * 100f;
			float num2 = num;
			((Character)player).GetSEMan().ModifyFallDamage(num, ref num2);
			if (num2 <= 0f)
			{
				return 0f;
			}
			num2 *= Game.m_localDamgeTakenRate;
			if (!(num2 <= 0.1f))
			{
				return num2;
			}
			return 0f;
		}

		private void Show(Player player, Kind kind, float drop)
		{
			float health = ((Character)player).GetHealth();
			float num = Damage(player, drop);
			bool flag = num > 0f && health - num <= 0f && !((Character)player).InGodMode() && !((Character)player).InGhostMode();
			int num2 = Mathf.Max(1, Mathf.RoundToInt(num));
			int num3 = Mathf.CeilToInt(health);
			string text = Height(drop, spoken: false);
			string line = ((num <= 0f) ? (text + " drop, no damage") : ((!flag) ? $"{text} drop, about {num2} damage, {Mathf.Max(1, Mathf.CeilToInt(health - num))} health left" : $"{text} drop, about {num2} damage, you have {num3}"));
			string damage = ((num <= 0f) ? "no dmg" : $"~{num2} dmg");
			_readout.Show(text, damage, line, flag);
			if (_showing == Kind.None && Time.time - _lastSeen > 2f)
			{
				_warned = false;
			}
			if (kind != _showing || flag != _showingFatal)
			{
				Note(string.Format("{0}: {1:0.0} m, {2:0.0} damage, {3:0.0} health{4}.", kind, drop, num, health, flag ? ", would kill you" : ""));
			}
			_showing = kind;
			_showingFatal = flag;
			_lastSeen = Time.time;
			if (flag && !_warned)
			{
				_warned = true;
				Warn(kind, Height(drop, spoken: true), num2, num3);
			}
		}

		private void Clear(bool now)
		{
			if (_showing != Kind.None && (now || !(Time.time - _lastSeen < 0.35f)))
			{
				_readout.Hide();
				_showing = Kind.None;
				_showingFatal = false;
			}
		}

		private void Warn(Kind kind, string height, int damage, int health)
		{
			if (!Plugin.WarnIfFatal.Value)
			{
				return;
			}
			if (Time.time < _nextRemarkAt)
			{
				Note("Would kill you, but Odin spoke a moment ago. He's letting the red do the talking.");
				return;
			}
			string[] array = ((kind == Kind.Falling) ? FallingLines : LedgeLines);
			string text = null;
			for (int i = 0; i < 8; i++)
			{
				if (text != null && !(text == _lastRemark))
				{
					break;
				}
				text = string.Format(array[Random.Range(0, array.Length)], Capitalise(height), damage, health);
			}
			_lastRemark = text;
			_nextRemarkAt = Time.time + 10f;
			_line.Show(text);
			Note("Odin warned you: \"" + text + "\"");
		}

		private static string Height(float metres, bool spoken)
		{
			bool flag = Plugin.Units.Value == "feet";
			int num = Mathf.Max(1, Mathf.RoundToInt(flag ? (metres * 3.28084f) : metres));
			if (!spoken)
			{
				if (!flag)
				{
					return $"{num} m";
				}
				return $"{num} ft";
			}
			if (flag)
			{
				if (num != 1)
				{
					return $"{num} feet";
				}
				return "1 foot";
			}
			if (num != 1)
			{
				return $"{num} metres";
			}
			return "1 metre";
		}

		private static string Capitalise(string s)
		{
			if (s.Length <= 0)
			{
				return s;
			}
			return char.ToUpperInvariant(s[0]) + s.Substring(1);
		}

		private static bool Busy(Player player)
		{
			if (!((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !((Character)player).InIntro() && !((Character)player).IsDebugFlying() && !Menu.IsVisible() && !InventoryGui.IsVisible())
			{
				return Minimap.IsOpen();
			}
			return true;
		}

		private static void Note(string message)
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)message);
			}
		}
	}
	internal class OdinLine : MonoBehaviour
	{
		private const float ScreenY = 0.72f;

		private const float FadeIn = 0.2f;

		private const float Hold = 3.5f;

		private const float FadeOut = 0.8f;

		private const float TextSize = 21f;

		private static readonly Color Tint = new Color(1f, 0.86f, 0.62f);

		private TMP_Text _text;

		private float _elapsed = -1f;

		public void Show(string line)
		{
			if (EnsureText())
			{
				_text.text = line;
				_elapsed = 0f;
				SetAlpha(0f);
				((Component)_text).gameObject.SetActive(true);
			}
		}

		private void Update()
		{
			if (_elapsed < 0f || (Object)(object)_text == (Object)null)
			{
				return;
			}
			_elapsed += Time.deltaTime;
			float num = 4.5f;
			if (_elapsed >= num)
			{
				_elapsed = -1f;
				((Component)_text).gameObject.SetActive(false);
				return;
			}
			float num2 = Mathf.Min(Mathf.Clamp01(_elapsed / 0.2f), Mathf.Clamp01((num - _elapsed) / 0.8f));
			if (!HudVisible())
			{
				num2 = 0f;
			}
			SetAlpha(Mathf.SmoothStep(0f, 1f, num2));
		}

		private void SetAlpha(float alpha)
		{
			//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_0014: Unknown result type (might be due to invalid IL or missing references)
			Color tint = Tint;
			tint.a = alpha;
			((Graphic)_text).color = tint;
		}

		private bool EnsureText()
		{
			//IL_0041: 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_0051: 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_0066: 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)
			if ((Object)(object)_text != (Object)null)
			{
				return true;
			}
			_text = HudText.Create("OdinsLongDrop_Line", ((Component)this).transform);
			if ((Object)(object)_text == (Object)null)
			{
				return false;
			}
			RectTransform val = (RectTransform)_text.transform;
			val.anchorMin = new Vector2(0.5f, 0.72f);
			val.anchorMax = new Vector2(0.5f, 0.72f);
			val.sizeDelta = new Vector2(1400f, 160f);
			_text.fontSize = 21f;
			return true;
		}

		private static bool HudVisible()
		{
			if ((Object)(object)Hud.instance != (Object)null && !Hud.IsUserHidden())
			{
				return Hud.instance.IsVisible();
			}
			return false;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_text != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_text).gameObject);
			}
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinslongdrop", "Odin's Long Drop", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinslongdrop";

		public const string Name = "Odin's Long Drop";

		public const string Version = "0.1.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ShowAtLedge;

		internal static ConfigEntry<bool> ShowWhileFalling;

		internal static ConfigEntry<float> MinDrop;

		internal static ConfigEntry<bool> WarnIfFatal;

		internal static ConfigEntry<string> Units;

		internal static ConfigEntry<string> Style;

		internal static ConfigEntry<float> TextScale;

		internal static ConfigEntry<float> SlopeAngle;

		internal static ConfigEntry<float> LookDownAngle;

		internal static ConfigEntry<bool> DebugLog;

		private LongDrop _longDrop;

		private void Awake()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch.");
			ShowAtLedge = ((BaseUnityPlugin)this).Config.Bind<bool>("Readout", "ShowAtLedge", true, "Show the readout when you stand at an edge and look down over a drop of at least MinDrop.");
			SlopeAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Readout", "SlopeAngle", 38f, new ConfigDescription("Ground steeper than this, in degrees, counts as part of a drop, 20 to 80. 38 is the game's own limit: anything steeper and you slide down it instead of standing on it. Higher = only steeper slopes and cliffs count.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 80f), Array.Empty<object>()));
			LookDownAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Readout", "LookDownAngle", 14f, new ConfigDescription("How far below level, in degrees, you have to look for the ledge readout to check the ground ahead, 5 to 60.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>()));
			ShowWhileFalling = ((BaseUnityPlugin)this).Config.Bind<bool>("Readout", "ShowWhileFalling", true, "Show the readout once you've been falling for at least MinDrop.");
			MinDrop = ((BaseUnityPlugin)this).Config.Bind<float>("Readout", "MinDrop", 4f, new ConfigDescription("Smallest drop worth showing, in metres, 1 to 20. The game starts to hurt you above 4; below that the readout just says \"no damage\".", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			Units = ((BaseUnityPlugin)this).Config.Bind<string>("Readout", "Units", "metres", new ConfigDescription("Show heights in metres or feet.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "metres", "feet" }), Array.Empty<object>()));
			Style = ((BaseUnityPlugin)this).Config.Bind<string>("Readout", "Style", "BesideCrosshair", new ConfigDescription("Where the readout goes. BesideCrosshair: two short lines just left of the crosshair, the drop and the damage. BelowCrosshair: one line under the crosshair with the drop, the damage and the health you'd have left.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "BesideCrosshair", "BelowCrosshair" }), Array.Empty<object>()));
			TextScale = ((BaseUnityPlugin)this).Config.Bind<float>("Readout", "TextScale", 1f, new ConfigDescription("Size of the readout and Odin's warning line, 0.5 to 2. 1 = sized to match the game's own HUD, which already follows your screen height and the game's GUI scale setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			WarnIfFatal = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "WarnIfFatal", true, "When the landing would kill you, Odin says so in one line, once per drop. Off = the readout still turns red, Odin keeps quiet.");
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Write each readout Odin starts (drop, damage, health), each warning, the text scale, and, at most once a second while you look down, why a ledge did or didn't count (what each probe hit, its layer and the drop) to the BepInEx log.");
			_longDrop = ((Component)this).gameObject.AddComponent<LongDrop>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Long Drop 0.1.0 loaded. Odin has done the sums.");
		}

		private void OnDestroy()
		{
			if ((Object)(object)_longDrop != (Object)null)
			{
				Object.Destroy((Object)(object)_longDrop);
			}
		}
	}
	internal class Readout : MonoBehaviour
	{
		internal const string BesideCrosshair = "BesideCrosshair";

		internal const string BelowCrosshair = "BelowCrosshair";

		private const float BesideGap = 20f;

		private const float BesideTextSize = 17f;

		private const string DamageSize = "80%";

		private const float BelowScreenY = 0.4f;

		private const float BelowTextSize = 16f;

		private const float FadeIn = 0.15f;

		private const float FadeOut = 0.4f;

		private static readonly Color Safe = new Color(0.92f, 0.92f, 0.88f);

		private static readonly Color Fatal = new Color(1f, 0.32f, 0.26f);

		private TMP_Text _text;

		private bool _beside;

		private bool _laidOut;

		private bool _wanted;

		private bool _fatal;

		private float _alpha;

		internal static bool Beside => Plugin.Style.Value != "BelowCrosshair";

		public void Show(string drop, string damage, string line, bool fatal)
		{
			if (EnsureText())
			{
				bool beside = Beside;
				if (!_laidOut || beside != _beside)
				{
					Layout(beside);
				}
				string text = (beside ? (drop + "\n<size=80%>" + damage + "</size>") : line);
				if (_text.text != text)
				{
					_text.text = text;
				}
				_fatal = fatal;
				_wanted = true;
				((Component)_text).gameObject.SetActive(true);
			}
		}

		public void Hide()
		{
			_wanted = false;
		}

		private void Update()
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_text == (Object)null))
			{
				float num = ((_wanted && HudVisible()) ? 1f : 0f);
				float num2 = ((num > _alpha) ? 6.6666665f : 2.5f);
				_alpha = Mathf.MoveTowards(_alpha, num, num2 * Time.deltaTime);
				if (_alpha <= 0f && !_wanted)
				{
					((Component)_text).gameObject.SetActive(false);
					return;
				}
				Color color = (_fatal ? Fatal : Safe);
				color.a = Mathf.SmoothStep(0f, 1f, _alpha);
				((Graphic)_text).color = color;
			}
		}

		private bool EnsureText()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_text != (Object)null)
			{
				return true;
			}
			_text = HudText.Create("OdinsLongDrop_Readout", ((Component)this).transform);
			if ((Object)(object)_text == (Object)null)
			{
				return false;
			}
			((Graphic)_text).color = new Color(Safe.r, Safe.g, Safe.b, 0f);
			return true;
		}

		private void Layout(bool beside)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)_text.transform;
			Vector2 val2 = default(Vector2);
			if (beside)
			{
				((Vector2)(ref val2))..ctor(0.5f, 0.5f);
				val.anchorMax = val2;
				val.anchorMin = val2;
				val.pivot = new Vector2(1f, 0.5f);
				val.sizeDelta = new Vector2(180f, 60f);
				val.anchoredPosition = new Vector2(-20f, 0f);
				_text.alignment = (TextAlignmentOptions)516;
				_text.textWrappingMode = (TextWrappingModes)0;
				_text.lineSpacing = -12f;
				_text.fontSize = 17f;
			}
			else
			{
				((Vector2)(ref val2))..ctor(0.5f, 0.4f);
				val.anchorMax = val2;
				val.anchorMin = val2;
				val.pivot = new Vector2(0.5f, 0.5f);
				val.sizeDelta = new Vector2(700f, 60f);
				val.anchoredPosition = Vector2.zero;
				_text.alignment = (TextAlignmentOptions)514;
				_text.textWrappingMode = (TextWrappingModes)1;
				_text.lineSpacing = 0f;
				_text.fontSize = 16f;
			}
			_beside = beside;
			_laidOut = true;
		}

		private static bool HudVisible()
		{
			if ((Object)(object)Hud.instance != (Object)null && !Hud.IsUserHidden())
			{
				return Hud.instance.IsVisible();
			}
			return false;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_text != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_text).gameObject);
			}
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.0";
	}
}