Decompiled source of Odins Latch v0.1.1

plugins/OdinsLatch.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
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("OdinsLatch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+3f51590bcc71bad50962201797350c250e270175")]
[assembly: AssemblyProduct("OdinsLatch")]
[assembly: AssemblyTitle("OdinsLatch")]
[assembly: AssemblyVersion("0.1.1.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 OdinsLatch
{
	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("OdinsLatch_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 Latch : MonoBehaviour
	{
		private enum Reason
		{
			None,
			Enemy,
			Raid,
			Night,
			Horn,
			Dusk,
			Always
		}

		private sealed class Tracked
		{
			public Door Door;

			public ZNetView View;

			public string Name;

			public float UsedAt;

			public float DueSince = -1f;

			public float AskedAt = -1f;

			public string HeldBy;
		}

		private const float ScanInterval = 0.25f;

		private const float OpenGrace = 3f;

		private const float RetryAfter = 4f;

		private const double HornWindow = 0.45;

		private const float NightStartsAt = 0.85f;

		private const float NightEndsAt = 0.15f;

		internal const int Unknown = int.MinValue;

		private static readonly Dictionary<Reason, string[]> Lines = new Dictionary<Reason, string[]>
		{
			[Reason.Enemy] = new string[6] { "Greydwarfs don't knock.", "Trolls don't knock either. Door shut.", "{0} nearby. The door is shut. It can knock.", "Shut. The {0} will have to try the wall.", "Visitors. The pointy kind. Door shut.", "Odin shut the door. The {0} was looking at it." },
			[Reason.Raid] = new string[3] { "They've come for your base. They can start with the door.", "Raid on. Door shut. The rest is your job.", "A raid is no time for draughts." },
			[Reason.Night] = new string[4] { "Night's here. So is the latch.", "Dark out. Door shut. Odin's rule, not yours.", "The sun has gone home. So has the door.", "Night. The dark can stay outside." },
			[Reason.Horn] = new string[2] { "The horn has sounded. The door listened, at least.", "Horn's gone. Door's shut. One of you was paying attention." },
			[Reason.Dusk] = new string[2] { "Dusk. Odin shut the door before the night could try the handle.", "Sun's going. Door's shut. One of you was paying attention." },
			[Reason.Always] = new string[2] { "You left it open. Odin didn't.", "Door shut. Odin hung nine nights on a tree. He can manage a latch." }
		};

		private static readonly string[] Shared = new string[2] { "Born in a barn, were we?", "Shut. You're welcome." };

		private readonly List<Tracked> _doors = new List<Tracked>();

		private readonly List<Character> _near = new List<Character>();

		private OdinLine _line;

		private float _nextScan;

		private float _nextRemarkAt;

		private string _lastRemark;

		internal static Latch Instance { get; private set; }

		internal bool Closing { get; private set; }

		private void Awake()
		{
			Instance = this;
			_line = ((Component)this).gameObject.AddComponent<OdinLine>();
		}

		private void Start()
		{
			DuskHornLink.TryHook(Plugin.Patcher);
		}

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

		internal static int StateOf(Door door)
		{
			ZNetView val = (((Object)(object)door != (Object)null) ? ((Component)door).GetComponent<ZNetView>() : null);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return int.MinValue;
			}
			return val.GetZDO().GetInt(ZDOVars.s_state, 0);
		}

		internal void OnUsed(Door door, int stateBefore)
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || stateBefore == int.MinValue)
			{
				return;
			}
			int num = _doors.FindIndex((Tracked t) => (Object)(object)t.Door == (Object)(object)door);
			if (stateBefore != 0)
			{
				if (num >= 0)
				{
					Note("You shut " + _doors[num].Name + " yourself. Odin stops watching it.");
					_doors.RemoveAt(num);
				}
				return;
			}
			if ((Object)(object)door.m_keyItem != (Object)null || door.m_canNotBeClosed)
			{
				Note("Opened " + Name(door) + ": it needs a key or can't be shut. Odin leaves it alone.");
				return;
			}
			Tracked tracked = ((num >= 0) ? _doors[num] : new Tracked
			{
				Door = door,
				View = ((Component)door).GetComponent<ZNetView>(),
				Name = Name(door)
			});
			tracked.UsedAt = Time.time;
			tracked.DueSince = -1f;
			tracked.AskedAt = -1f;
			tracked.HeldBy = null;
			if (num < 0)
			{
				_doors.Add(tracked);
			}
			Note($"Opened {tracked.Name} at {Where(((Component)door).transform.position)}. Odin is watching it ({_doors.Count} in all).");
		}

		private void Update()
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_doors.Clear();
			}
			else
			{
				if (!Plugin.Enabled.Value || Time.time < _nextScan)
				{
					return;
				}
				_nextScan = Time.time + 0.25f;
				if (((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || _doors.Count == 0)
				{
					return;
				}
				Reason everywhere = ClockReason();
				Reason reason = Reason.None;
				string creature = null;
				int num = 0;
				for (int num2 = _doors.Count - 1; num2 >= 0; num2--)
				{
					Tracked tracked = _doors[num2];
					if ((Object)(object)tracked.Door == (Object)null || (Object)(object)tracked.View == (Object)null || !tracked.View.IsValid())
					{
						_doors.RemoveAt(num2);
					}
					else if (tracked.View.GetZDO().GetInt(ZDOVars.s_state, 0) == 0)
					{
						if (Time.time - tracked.UsedAt > 3f)
						{
							if (tracked.AskedAt < 0f)
							{
								Note(tracked.Name + " is shut (not by Odin). Odin stops watching it.");
							}
							_doors.RemoveAt(num2);
						}
					}
					else
					{
						Vector3 position = ((Component)tracked.Door).transform.position;
						Character enemy;
						Reason reason2 = WhyShut(localPlayer, position, everywhere, out enemy);
						if (reason2 == Reason.None)
						{
							tracked.DueSince = -1f;
						}
						else
						{
							if (tracked.DueSince < 0f)
							{
								tracked.DueSince = Time.time;
							}
							if (!(Time.time - tracked.DueSince < Plugin.CloseDelay.Value) && (!(tracked.AskedAt >= 0f) || !(Time.time - tracked.AskedAt < 4f)))
							{
								string text = KeptOpen(localPlayer, tracked.Door, position);
								if (text != null)
								{
									if (text != tracked.HeldBy)
									{
										Note($"{tracked.Name}: {reason2}, but {text}. Odin waits.");
									}
									tracked.HeldBy = text;
								}
								else
								{
									tracked.HeldBy = null;
									if (Shut(localPlayer, tracked))
									{
										Note("Shut " + tracked.Name + " at " + Where(position) + ": " + Describe(reason2, enemy, position, localPlayer) + ".");
										if (num == 0 || reason2 < reason)
										{
											reason = reason2;
											creature = (((Object)(object)enemy != (Object)null) ? ShownName(enemy) : null);
										}
										num++;
									}
								}
							}
						}
					}
				}
				if (num > 0)
				{
					Remark(reason, creature);
				}
			}
		}

		private static Reason ClockReason()
		{
			if ((Object)(object)EnvMan.instance != (Object)null && (Object)(object)ZNet.instance != (Object)null)
			{
				if (Plugin.CloseAtNight.Value && EnvMan.IsNight())
				{
					return Reason.Night;
				}
				if (Plugin.FollowDuskHorn.Value && DuskHornLink.Hooked && !EnvMan.IsNight() && ZNet.instance.GetTimeSeconds() - DuskHornLink.LastHornAt < 0.45 * (double)EnvMan.instance.m_dayLengthSec)
				{
					return Reason.Horn;
				}
				if (Plugin.CloseAtNight.Value && Plugin.DuskMinutes.Value > 0f && SecondsToNight() <= (double)(Plugin.DuskMinutes.Value * 60f))
				{
					return Reason.Dusk;
				}
			}
			if (!Plugin.AlwaysClose.Value)
			{
				return Reason.None;
			}
			return Reason.Always;
		}

		private Reason WhyShut(Player player, Vector3 pos, Reason everywhere, out Character enemy)
		{
			//IL_0015: 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_0062: 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)
			enemy = null;
			Reason reason = everywhere;
			if (Plugin.CloseNearEnemies.Value && (Object)(object)(enemy = NearestEnemy(player, pos, Plugin.EnemyRadius.Value)) != (Object)null)
			{
				reason = Reason.Enemy;
			}
			else if (Plugin.CloseInRaids.Value && RaidCovers(pos))
			{
				reason = Reason.Raid;
			}
			if (reason == Reason.None)
			{
				return Reason.None;
			}
			if (Plugin.WalkAway.Value && Vector3.Distance(((Component)player).transform.position, pos) <= Plugin.WalkAwayDistance.Value)
			{
				return Reason.None;
			}
			return reason;
		}

		private static double SecondsToNight()
		{
			double num = EnvMan.instance.m_dayLengthSec;
			if (num <= 0.0)
			{
				return double.MaxValue;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			double num2 = timeSeconds / num - Math.Floor(timeSeconds / num);
			if (num2 >= 0.8500000238418579 || num2 <= 0.15000000596046448)
			{
				return 0.0;
			}
			return (0.8500000238418579 - num2) * num;
		}

		private static bool RaidCovers(Vector3 pos)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			RandomEvent val = (((Object)(object)RandEventSystem.instance != (Object)null) ? RandEventSystem.instance.GetCurrentRandomEvent() : null);
			if (val != null)
			{
				return Utils.DistanceXZ(pos, val.m_pos) < val.m_eventRange;
			}
			return false;
		}

		private static Character NearestEnemy(Player player, Vector3 pos, float radius)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			Character result = null;
			float num = float.MaxValue;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player) && !allCharacter.IsPlayer() && !allCharacter.IsDead() && !allCharacter.IsTamed())
				{
					float num2 = Mathf.Max(0f, Vector3.Distance(((Component)allCharacter).transform.position, pos) - allCharacter.GetRadius());
					if (!(num2 > radius) && !(num2 >= num) && IsThreat(player, allCharacter))
					{
						result = allCharacter;
						num = num2;
					}
				}
			}
			return result;
		}

		private static bool IsThreat(Player player, Character other)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0026: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			if (!BaseAI.IsEnemy((Character)(object)player, other))
			{
				return false;
			}
			BaseAI baseAI = other.GetBaseAI();
			if ((Object)(object)baseAI == (Object)null)
			{
				return false;
			}
			Faction faction = other.GetFaction();
			if ((int)faction != 1)
			{
				if ((int)faction == 12)
				{
					return false;
				}
				if (!baseAI.IsAlerted() && !baseAI.HaveTarget())
				{
					return baseAI.HuntPlayer();
				}
				return true;
			}
			if (baseAI is MonsterAI)
			{
				return baseAI.HaveTarget();
			}
			return false;
		}

		private string KeptOpen(Player player, Door door, Vector3 pos)
		{
			//IL_001c: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)door.m_keyItem != (Object)null || door.m_canNotBeClosed)
			{
				return "it needs a key or can't be shut";
			}
			if (Character.InInterior(pos))
			{
				return "it's in a dungeon";
			}
			if (Location.IsInsideLocation(pos, 0f) && !BuiltByPlayer(door))
			{
				return "it's a location's own door";
			}
			if (Excluded(door))
			{
				return "it's in Exclude";
			}
			if (door.m_checkGuardStone && !PrivateArea.CheckAccess(pos, 0f, false, false))
			{
				return "a ward you can't use covers it";
			}
			if (!View(door).HasOwner())
			{
				return "nobody owns it yet";
			}
			float value = Plugin.DoorwayRadius.Value;
			if (value <= 0f)
			{
				return null;
			}
			if (Vector3.Distance(((Component)player).transform.position, pos) < value)
			{
				return "you're in the doorway";
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && (Object)(object)allPlayer != (Object)(object)player && !((Character)allPlayer).IsDead() && Vector3.Distance(((Component)allPlayer).transform.position, pos) < value)
				{
					return "another player is in the doorway";
				}
			}
			_near.Clear();
			Character.GetCharactersInRange(pos, value, _near);
			foreach (Character item in _near)
			{
				if ((Object)(object)item != (Object)null && !item.IsDead() && item.IsTamed())
				{
					return "a tamed " + ShownName(item) + " is in the doorway";
				}
			}
			return null;
		}

		private static bool BuiltByPlayer(Door door)
		{
			Piece component = ((Component)door).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				return component.IsPlacedByPlayer();
			}
			return false;
		}

		private static bool Excluded(Door door)
		{
			string[] array = (Plugin.Exclude.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && (string.Equals(Utils.GetPrefabName(((Component)door).gameObject), text, StringComparison.OrdinalIgnoreCase) || string.Equals(Localize(door.m_name), text, StringComparison.OrdinalIgnoreCase) || string.Equals((door.m_name ?? "").TrimStart(new char[1] { '$' }), text.TrimStart(new char[1] { '$' }), StringComparison.OrdinalIgnoreCase)))
				{
					return true;
				}
			}
			return false;
		}

		private bool Shut(Player player, Tracked t)
		{
			if (t.View.GetZDO().GetInt(ZDOVars.s_state, 0) == 0)
			{
				return false;
			}
			Closing = true;
			bool flag;
			try
			{
				flag = t.Door.Interact((Humanoid)(object)player, false, false);
			}
			finally
			{
				Closing = false;
			}
			if (flag)
			{
				t.AskedAt = Time.time;
			}
			else
			{
				Note(t.Name + " is still swinging. Odin tries again shortly.");
			}
			return flag;
		}

		private void Remark(Reason why, string creature)
		{
			if (!Plugin.ShowRemarks.Value || Time.time < _nextRemarkAt || !Lines.TryGetValue(why, out var value))
			{
				return;
			}
			string text = null;
			for (int i = 0; i < 8; i++)
			{
				if (text != null && !(text == _lastRemark))
				{
					break;
				}
				int num = Random.Range(0, value.Length + Shared.Length);
				string text2 = ((num < value.Length) ? value[num] : Shared[num - value.Length]);
				if (!text2.Contains("{0}") || creature != null)
				{
					text = string.Format(text2, creature);
				}
			}
			if (text != null)
			{
				_lastRemark = text;
				_nextRemarkAt = Time.time + Plugin.RemarkCooldown.Value;
				_line.Show(text);
			}
		}

		private static ZNetView View(Door door)
		{
			return ((Component)door).GetComponent<ZNetView>();
		}

		private static string Name(Door door)
		{
			return Utils.GetPrefabName(((Component)door).gameObject);
		}

		private static string ShownName(Character c)
		{
			return Localize(c.m_name);
		}

		private static string Localize(string token)
		{
			if (Localization.instance == null)
			{
				return (token ?? "").TrimStart(new char[1] { '$' });
			}
			return Localization.instance.Localize(token ?? "");
		}

		private static string Where(Vector3 pos)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return $"({pos.x:0}, {pos.z:0})";
		}

		private static string Describe(Reason why, Character enemy, Vector3 pos, Player player)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			string arg;
			switch (why)
			{
			case Reason.Enemy:
				arg = $"{enemy.m_name} {Vector3.Distance(((Component)enemy).transform.position, pos):0} m from it";
				break;
			case Reason.Raid:
			{
				RandomEvent val = (((Object)(object)RandEventSystem.instance != (Object)null) ? RandEventSystem.instance.GetCurrentRandomEvent() : null);
				arg = "raid " + ((val != null) ? val.m_name : "?");
				break;
			}
			case Reason.Dusk:
				arg = $"dusk, night in {SecondsToNight():0} s";
				break;
			default:
				arg = why.ToString().ToLowerInvariant();
				break;
			}
			return $"{arg}; you're {Vector3.Distance(((Component)player).transform.position, pos):0} m away";
		}

		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 = 2.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 = 3.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("OdinsLatch_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);
			}
		}
	}
	[HarmonyPatch(typeof(Door), "Interact")]
	internal static class DoorInteractPatch
	{
		private static void Prefix(Door __instance, out int __state)
		{
			__state = Latch.StateOf(__instance);
		}

		private static void Postfix(Door __instance, Humanoid character, bool hold, bool __result, int __state)
		{
			if (!(!__result || hold) && !((Object)(object)character == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && !((Object)(object)Latch.Instance == (Object)null) && !Latch.Instance.Closing)
			{
				Latch.Instance.OnUsed(__instance, __state);
			}
		}
	}
	internal static class DuskHornLink
	{
		internal const string DuskHornGuid = "hevyh2o.valheim.odinsduskhorn";

		private const string HornMethod = "OdinsDuskHorn.Horn:Play";

		internal static double LastHornAt = double.MinValue;

		internal static bool Hooked { get; private set; }

		internal static void TryHook(Harmony harmony)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			if (Hooked || harmony == null || !Chainloader.PluginInfos.ContainsKey("hevyh2o.valheim.odinsduskhorn"))
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method("OdinsDuskHorn.Horn:Play", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogInfo((object)"Odin's Dusk Horn is here, but its horn has moved. Doors follow the clock instead.");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(DuskHornLink), "OnHorn", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Hooked = true;
				Plugin.Log.LogInfo((object)"Odin's Dusk Horn found. Doors shut when the horn sounds.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogInfo((object)("Odin's Dusk Horn found, but its horn couldn't be followed (" + ex.GetType().Name + "). Doors follow the clock instead."));
			}
		}

		private static void OnHorn()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				LastHornAt = ZNet.instance.GetTimeSeconds();
			}
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinslatch", "Odin's Latch", "0.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinslatch";

		public const string Name = "Odin's Latch";

		public const string Version = "0.1.1";

		internal static ManualLogSource Log;

		internal static Harmony Patcher;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> CloseDelay;

		internal static ConfigEntry<float> DoorwayRadius;

		internal static ConfigEntry<string> Exclude;

		internal static ConfigEntry<bool> CloseNearEnemies;

		internal static ConfigEntry<float> EnemyRadius;

		internal static ConfigEntry<bool> CloseAtNight;

		internal static ConfigEntry<float> DuskMinutes;

		internal static ConfigEntry<bool> FollowDuskHorn;

		internal static ConfigEntry<bool> CloseInRaids;

		internal static ConfigEntry<bool> WalkAway;

		internal static ConfigEntry<float> WalkAwayDistance;

		internal static ConfigEntry<bool> AlwaysClose;

		internal static ConfigEntry<bool> ShowRemarks;

		internal static ConfigEntry<float> RemarkCooldown;

		internal static ConfigEntry<float> TextScale;

		internal static ConfigEntry<bool> DebugLog;

		private Latch _latch;

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Expected O, but got Unknown
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Expected O, but got Unknown
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch.");
			CloseDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CloseDelay", 2f, new ConfigDescription("Seconds Odin waits once a reason to close a door turns up, 0 to 30. 0 = straight away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			DoorwayRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DoorwayRadius", 2.5f, new ConfigDescription("Odin never shuts a door while you, another player or a tamed creature is this close to it, in metres, 0 to 6. 0 = he doesn't look.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 6f), Array.Empty<object>()));
			Exclude = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Exclude", "", "Doors Odin leaves alone, by prefab name (wood_door, iron_grate) or name as the game shows it (Wood door), separated by commas. Empty = every door you can close.");
			CloseNearEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseNearEnemies", true, "Shut the door when an enemy is within EnemyRadius of it and awake: alerted, hunting or after something. Animals only count while they're attacking. Tamed creatures never do.");
			EnemyRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "EnemyRadius", 30f, new ConfigDescription("How close to the door, in metres, an enemy has to be, 5 to 80.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>()));
			CloseAtNight = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseAtNight", true, "Shut the door at night, and for the last DuskMinutes before it.");
			DuskMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "DuskMinutes", 2f, new ConfigDescription("Only with CloseAtNight: how many minutes before nightfall count as night already, 0 to 6. Minutes of the game's clock as you play it (a whole Valheim day is 20). 0 = night only.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 6f), Array.Empty<object>()));
			FollowDuskHorn = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "FollowDuskHorn", true, "If Odin's Dusk Horn is installed, shut the door when its horn sounds, until night takes over. Does nothing without Dusk Horn.");
			CloseInRaids = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseInRaids", true, "Shut the door while a raid or other event is on and the door is inside its area.");
			WalkAway = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "WalkAway", false, "Wait until you're more than WalkAwayDistance from the door before shutting it. Odin still only shuts it for one of the reasons above.");
			WalkAwayDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "WalkAwayDistance", 10f, new ConfigDescription("Only with WalkAway: metres you have to be from the door, 3 to 50.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 50f), Array.Empty<object>()));
			AlwaysClose = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "AlwaysClose", false, "Shut the doors you open in daylight too, with no danger about: a plain door closer. The doorway check still applies.");
			ShowRemarks = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "ShowRemarks", true, "Odin says a word when he shuts a door for you.");
			RemarkCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "RemarkCooldown", 30f, new ConfigDescription("Seconds between remarks, 0 to 300. Doors shut in between are shut quietly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			TextScale = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "TextScale", 1f, new ConfigDescription("Size of Odin's remarks, 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>()));
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Write each door you open, each door Odin shuts and why, and what kept a door open, to the BepInEx log.");
			Patcher = new Harmony("hevyh2o.valheim.odinslatch");
			Patcher.PatchAll(typeof(Plugin).Assembly);
			_latch = ((Component)this).gameObject.AddComponent<Latch>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Latch 0.1.1 loaded. Odin is minding the door.");
		}

		private void OnDestroy()
		{
			Harmony patcher = Patcher;
			if (patcher != null)
			{
				patcher.UnpatchSelf();
			}
			if ((Object)(object)_latch != (Object)null)
			{
				Object.Destroy((Object)(object)_latch);
			}
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.1";
	}
}