Decompiled source of Odins Knot v0.1.3

plugins/OdinsKnot.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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("OdinsKnot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+fa48d092a3f42bb3cd633149ecfaa21059f55fa9")]
[assembly: AssemblyProduct("OdinsKnot")]
[assembly: AssemblyTitle("OdinsKnot")]
[assembly: AssemblyVersion("0.1.3.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 OdinsKnot
{
	internal sealed class Knot : MonoBehaviour
	{
		private sealed class NoteReceiver : TextReceiver
		{
			private readonly Player _player;

			private readonly Action<string> _onSet;

			public NoteReceiver(Player player, Action<string> onSet)
			{
				_player = player;
				_onSet = onSet;
			}

			public string GetText()
			{
				return GetNote(_player);
			}

			public void SetText(string text)
			{
				SetNote(_player, text);
				_onSet?.Invoke(GetNote(_player));
			}
		}

		private const string NoteKey = "OdinsKnot.Note";

		private const string LastSeenKey = "OdinsKnot.LastSeen";

		private const int MaxLength = 120;

		private const float RecapDelay = 3f;

		private const float ClearFor = 3f;

		private const float GiveUpAfter = 600f;

		private const float ScanInterval = 0.25f;

		private const float StickDeadZone = 0.2f;

		private RecapScroll _scroll;

		private bool _asking;

		private bool _leaving;

		private long _recapPlayerId;

		private float _recapAt = -1f;

		private TimeSpan? _away;

		private int _recappedGame;

		private float _recapGiveUpAt;

		private float _clearSince = -1f;

		private float _nextScan;

		private bool _held;

		internal static Knot Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			_scroll?.Hide();
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (_scroll != null)
			{
				if (_scroll.IsOpen && MovePressed())
				{
					_scroll.Dismiss();
				}
				_scroll.Tick(Blocked());
			}
			Player localPlayer = Player.m_localPlayer;
			if (Plugin.Enabled.Value && !((Object)(object)localPlayer == (Object)null) && !_asking)
			{
				if (Pressed(Plugin.KnotKey.Value) && CanOpen(localPlayer))
				{
					_scroll?.Hide();
					Edit(localPlayer);
				}
				else if (_recapAt >= 0f && Time.unscaledTime >= _recapAt && localPlayer.GetPlayerID() == _recapPlayerId)
				{
					TryRecap(localPlayer);
				}
			}
		}

		internal void OnLocalDamaged()
		{
			_scroll?.Dismiss();
		}

		private void TryRecap(Player player)
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= _recapGiveUpAt)
			{
				_recapAt = -1f;
				Plugin.Log.LogInfo((object)"Enemies stayed near; the login scroll was dropped. Press the knot key to read your knot.");
			}
			else
			{
				if (unscaledTime < _nextScan)
				{
					return;
				}
				_nextScan = unscaledTime + 0.25f;
				if (EnemyNear(player, Plugin.SafeRadius.Value))
				{
					if (!_held)
					{
						Plugin.Log.LogInfo((object)"Enemies near at login; the scroll will wait until it's safe.");
					}
					_held = true;
					_clearSince = -1f;
				}
				else
				{
					if (!CanShowRecap(player))
					{
						return;
					}
					if (_held)
					{
						if (_clearSince < 0f)
						{
							_clearSince = unscaledTime;
						}
						if (unscaledTime - _clearSince < 3f)
						{
							return;
						}
					}
					_recapAt = -1f;
					ShowRecap(player);
				}
			}
		}

		private static bool EnemyNear(Player player, float radius)
		{
			//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_004f: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			if (radius <= 0f)
			{
				return false;
			}
			float num = radius * radius;
			Vector3 position = ((Component)player).transform.position;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player) && !allCharacter.IsDead())
				{
					Vector3 val = ((Component)allCharacter).transform.position - position;
					if (!(((Vector3)(ref val)).sqrMagnitude > num) && BaseAI.IsEnemy((Character)(object)player, allCharacter))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool MovePressed()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (Typing() || Blocked())
			{
				return false;
			}
			if (!ZInput.GetButton("Forward") && !ZInput.GetButton("Backward") && !ZInput.GetButton("Left") && !ZInput.GetButton("Right") && !ZInput.GetButton("Jump") && !ZInput.GetButtonDown("JoyJump"))
			{
				Vector2 joyLeftStick = ZInput.GetJoyLeftStick();
				return ((Vector2)(ref joyLeftStick)).sqrMagnitude > 0.040000003f;
			}
			return true;
		}

		internal static string GetNote(Player player)
		{
			if (!((Object)(object)player != (Object)null) || !player.m_customData.TryGetValue("OdinsKnot.Note", out var value))
			{
				return "";
			}
			return value;
		}

		internal static void SetNote(Player player, string text)
		{
			if (!((Object)(object)player == (Object)null))
			{
				string text2 = (text ?? "").Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ')
					.Trim();
				if (text2.Length > 120)
				{
					text2 = text2.Substring(0, 120);
				}
				if (text2.Length == 0)
				{
					player.m_customData.Remove("OdinsKnot.Note");
				}
				else
				{
					player.m_customData["OdinsKnot.Note"] = text2;
				}
			}
		}

		internal static void Stamp(Player player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				player.m_customData["OdinsKnot.LastSeen"] = DateTime.UtcNow.Ticks.ToString();
			}
		}

		private void Edit(Player player)
		{
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				return;
			}
			string before = GetNote(player);
			TextInput.instance.RequestText((TextReceiver)(object)new NoteReceiver(player, delegate(string note)
			{
				if (!(note == before))
				{
					string text = ((note.Length == 0) ? "Knot untied." : ("Knot tied: " + note));
					MessageHud instance = MessageHud.instance;
					if (instance != null)
					{
						instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
					}
				}
			}), "Odin's Knot: what are you doing?", 120);
		}

		internal bool BeforeLeave(Action leave)
		{
			if (_leaving)
			{
				return true;
			}
			if (_asking)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!Plugin.Enabled.Value || !Plugin.AskOnLogout.Value || (Object)(object)localPlayer == (Object)null || (Object)(object)TextInput.instance == (Object)null || (Object)(object)Game.instance == (Object)null || Game.instance.IsShuttingDown())
			{
				return true;
			}
			Menu instance = Menu.instance;
			if (instance != null)
			{
				instance.Hide();
			}
			_scroll?.Hide();
			((MonoBehaviour)this).StartCoroutine(AskThenLeave(localPlayer, leave));
			return false;
		}

		private IEnumerator AskThenLeave(Player player, Action leave)
		{
			_asking = true;
			TextInput.instance.RequestText((TextReceiver)(object)new NoteReceiver(player, null), "Odin asks: what were you doing?", 120);
			yield return null;
			while ((Object)(object)TextInput.instance != (Object)null && (Object)(object)TextInput.instance.m_panel != (Object)null && TextInput.instance.m_panel.activeSelf)
			{
				yield return null;
			}
			_asking = false;
			Plugin.Log.LogInfo((object)((GetNote(player).Length > 0) ? "Knot tied for next time." : "Left without a knot."));
			_leaving = true;
			try
			{
				leave();
			}
			finally
			{
				_leaving = false;
			}
		}

		internal void OnSpawned(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)Game.instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)Game.instance).GetInstanceID();
			if (instanceID == _recappedGame)
			{
				return;
			}
			_recappedGame = instanceID;
			_recapAt = -1f;
			_held = false;
			_clearSince = -1f;
			_away = null;
			if (player.m_customData.TryGetValue("OdinsKnot.LastSeen", out var value) && long.TryParse(value, out var result))
			{
				TimeSpan timeSpan = DateTime.UtcNow - new DateTime(result, DateTimeKind.Utc);
				if (timeSpan > TimeSpan.Zero)
				{
					_away = timeSpan;
				}
			}
			if (Plugin.Enabled.Value && Plugin.ShowRecap.Value && (GetNote(player).Length != 0 || _away.HasValue))
			{
				_recapPlayerId = player.GetPlayerID();
				_recapAt = Time.unscaledTime + 3f;
				_recapGiveUpAt = _recapAt + 600f;
			}
		}

		private void ShowRecap(Player player)
		{
			if (_scroll == null)
			{
				_scroll = RecapScroll.Create(((Component)this).transform);
			}
			if (_scroll == null)
			{
				_recapAt = Time.unscaledTime + 1f;
			}
			else
			{
				_scroll.Show(BuildRecap(player), Plugin.RecapSeconds.Value);
			}
		}

		private string BuildRecap(Player player)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<size=21><color=#E8C27A>Odin's Knot</color></size>\n");
			string note = GetNote(player);
			if (note.Length > 0)
			{
				stringBuilder.Append("<size=19>“").Append(Escape(note)).Append("”</size>\n\n");
			}
			else
			{
				stringBuilder.Append("<i>You tied no knot last time.</i>\n\n");
			}
			stringBuilder.Append("<size=15>");
			stringBuilder.Append("You're in ").Append(BiomeName(player.GetCurrentBiome()));
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val != null && val.HaveCustomSpawnPoint())
			{
				stringBuilder.Append(", ").Append(Distance(Vector3.Distance(((Component)player).transform.position, val.GetCustomSpawnPoint()))).Append(" from your bed.\n");
			}
			else
			{
				stringBuilder.Append(". You have no bed in this world.\n");
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			List<ItemData> list = ((inventory != null) ? (from i in inventory.GetAllItems()
				where i != null && i.m_shared != null && i.GetWeight(-1) > 0f
				orderby i.GetWeight(-1) descending
				select i).Take(3).ToList() : null);
			if (list != null && list.Count > 0)
			{
				stringBuilder.Append("In your bag: ").Append(string.Join(", ", list.Select(Describe))).Append(".\n");
			}
			else
			{
				stringBuilder.Append("Your bag is light.\n");
			}
			if (_away.HasValue)
			{
				stringBuilder.Append("You've been away ").Append(Duration(_away.Value)).Append('.');
			}
			stringBuilder.Append("</size>");
			return stringBuilder.ToString().TrimEnd(new char[1] { '\n' });
		}

		private static string Describe(ItemData item)
		{
			string arg = Escape((Localization.instance != null) ? Localization.instance.Localize(item.m_shared.m_name) : item.m_shared.m_name);
			string arg2 = ((item.m_stack > 1) ? (" ×" + item.m_stack) : "");
			return $"{arg}{arg2} <color=#BFAE8E>({item.GetWeight(-1):0.#} weight)</color>";
		}

		private unsafe static string BiomeName(Biome biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if ((int)biome == 0)
			{
				return "the wilds";
			}
			string text = "$biome_" + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant();
			string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
			return "the " + ((text2.StartsWith("[") || text2.StartsWith("$")) ? ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString() : text2);
		}

		private static string Distance(float meters)
		{
			if (!(meters >= 1000f))
			{
				return $"{Mathf.RoundToInt(meters)} m";
			}
			return $"{meters / 1000f:0.0} km";
		}

		private static string Duration(TimeSpan t)
		{
			if (t.TotalMinutes < 1.0)
			{
				return "only a moment";
			}
			if (t.TotalHours < 1.0)
			{
				return Plural((int)t.TotalMinutes, "minute");
			}
			if (t.TotalDays < 1.0)
			{
				return Plural((int)t.TotalHours, "hour") + ((t.Minutes > 0) ? (", " + Plural(t.Minutes, "minute")) : "");
			}
			return Plural((int)t.TotalDays, "day") + ((t.Hours > 0) ? (", " + Plural(t.Hours, "hour")) : "");
		}

		private static string Plural(int n, string word)
		{
			if (n != 1)
			{
				return $"{n} {word}s";
			}
			return "1 " + word;
		}

		private static string Escape(string s)
		{
			return s.Replace("<", "<noparse><</noparse>");
		}

		private static bool Blocked()
		{
			if (!Menu.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen() && !TextInput.IsVisible())
			{
				return Console.IsVisible();
			}
			return true;
		}

		private static bool Typing()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible())
			{
				return TextInput.IsVisible();
			}
			return true;
		}

		private static bool CanOpen(Player player)
		{
			if (!((Character)player).IsDead() && !Blocked() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !StoreGui.IsVisible())
			{
				return !Hud.IsPieceSelectionVisible();
			}
			return false;
		}

		private static bool CanShowRecap(Player player)
		{
			if (!((Character)player).IsDead() && !((Character)player).InIntro() && !Blocked() && (Object)(object)Hud.instance != (Object)null && Hud.instance.IsVisible())
			{
				return !Hud.IsUserHidden();
			}
			return false;
		}

		private static bool Pressed(KeyboardShortcut key)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			if ((int)((KeyboardShortcut)(ref key)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref key)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref key)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Menu), "Logout")]
	internal static class LogoutPatch
	{
		private static bool Prefix(Menu __instance)
		{
			if (!((Object)(object)Knot.Instance == (Object)null))
			{
				return Knot.Instance.BeforeLeave((Action)__instance.Logout);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Menu), "QuitGame")]
	internal static class QuitPatch
	{
		private static bool Prefix(Menu __instance)
		{
			if (!((Object)(object)Knot.Instance == (Object)null))
			{
				return Knot.Instance.BeforeLeave(delegate
				{
					Traverse.Create((object)__instance).Method("QuitGame", Array.Empty<object>()).GetValue();
				});
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SpawnPatch
	{
		private static void Postfix(Player __instance)
		{
			Knot.Instance?.OnSpawned(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class DamagePatch
	{
		private static void Postfix(Character __instance)
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Knot.Instance?.OnLocalDamaged();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Save")]
	internal static class SavePatch
	{
		private static void Prefix(Player __instance)
		{
			Knot.Stamp(__instance);
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinsknot", "Odin's Knot", "0.1.3")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinsknot";

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

		public const string Version = "0.1.3";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyboardShortcut> KnotKey;

		internal static ConfigEntry<bool> AskOnLogout;

		internal static ConfigEntry<bool> ShowRecap;

		internal static ConfigEntry<float> RecapSeconds;

		internal static ConfigEntry<float> SafeRadius;

		internal static ConfigEntry<float> TextScale;

		private Harmony _harmony;

		private Knot _knot;

		private void Awake()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch.");
			KnotKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "KnotKey", new KeyboardShortcut((KeyCode)39, Array.Empty<KeyCode>()), "Read or edit your knot at any time. Enter keeps the text, Esc leaves it as it was.");
			AskOnLogout = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AskOnLogout", true, "When you log out or quit from the menu, Odin asks what you were doing. Esc skips it and keeps your previous knot.");
			ShowRecap = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowRecap", true, "Show the scroll when you log in: your knot, your biome, the distance to your bed, the three heaviest stacks in your bag and how long you've been away.");
			RecapSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RecapSeconds", 12f, new ConfigDescription("How long the login scroll stays up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 30f), Array.Empty<object>()));
			SafeRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SafeRadius", 30f, new ConfigDescription("If an enemy is this close when you log in, in metres, the scroll waits until none has been near for a few seconds (for up to 10 minutes). 0 shows it straight away, enemies or not.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			TextScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "TextScale", 1f, new ConfigDescription("Size of the login scroll, 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>()));
			_harmony = new Harmony("hevyh2o.valheim.odinsknot");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			_knot = ((Component)this).gameObject.AddComponent<Knot>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Knot 0.1.3 loaded. Tie a knot before you go.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_knot != (Object)null)
			{
				Object.Destroy((Object)(object)_knot);
			}
		}
	}
	internal sealed class RecapScroll
	{
		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 (_logged < 0f)
					{
						_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 const float Width = 476f;

		private const float Padding = 20f;

		private const float FadeIn = 0.6f;

		private const float FadeOut = 1.2f;

		private const float QuickFade = 0.2f;

		private static Canvas _canvas;

		private readonly CanvasGroup _group;

		private readonly RectTransform _panel;

		private readonly TMP_Text _text;

		private float _shownAt;

		private float _seconds;

		private float _dismissedAt = -1f;

		private float _dismissAlpha;

		public bool IsOpen { get; private set; }

		private RecapScroll(CanvasGroup group, RectTransform panel, TMP_Text text)
		{
			_group = group;
			_panel = panel;
			_text = text;
		}

		public static RecapScroll Create(Transform owner)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_008f: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: 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("OdinsKnot_Scroll", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(((Component)_canvas).transform, false);
			RectTransform val3 = (RectTransform)val2.transform;
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 1f);
			val3.anchorMax = val4;
			val3.anchorMin = val4;
			val3.pivot = new Vector2(0.5f, 1f);
			val3.anchoredPosition = new Vector2(0f, -150f);
			Image obj = val2.AddComponent<Image>();
			((Graphic)obj).color = new Color(0.13f, 0.09f, 0.05f, 0.86f);
			((Graphic)obj).raycastTarget = false;
			Outline obj2 = val2.AddComponent<Outline>();
			((Shadow)obj2).effectColor = new Color(0.78f, 0.6f, 0.3f, 0.75f);
			((Shadow)obj2).effectDistance = new Vector2(3f, -3f);
			CanvasGroup val5 = val2.AddComponent<CanvasGroup>();
			val5.alpha = 0f;
			val5.interactable = false;
			val5.blocksRaycasts = false;
			GameObject val6 = new GameObject("OdinsKnot_ScrollText", new Type[1] { typeof(RectTransform) });
			val6.transform.SetParent((Transform)(object)val3, false);
			TextMeshProUGUI val7 = val6.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val7).font = val.font;
			((TMP_Text)val7).fontSharedMaterial = val.fontSharedMaterial;
			((TMP_Text)val7).fontSize = 17f;
			((Graphic)val7).color = new Color(0.96f, 0.9f, 0.76f);
			((TMP_Text)val7).alignment = (TextAlignmentOptions)258;
			((TMP_Text)val7).textWrappingMode = (TextWrappingModes)1;
			((TMP_Text)val7).richText = true;
			((Graphic)val7).raycastTarget = false;
			((TMP_Text)val7).outlineWidth = 0.15f;
			((TMP_Text)val7).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200);
			RectTransform val8 = (RectTransform)val6.transform;
			val8.anchorMin = Vector2.zero;
			val8.anchorMax = Vector2.one;
			val8.offsetMin = new Vector2(20f, 20f);
			val8.offsetMax = new Vector2(-20f, -20f);
			val2.SetActive(false);
			return new RecapScroll(val5, val3, (TMP_Text)(object)val7);
		}

		public void Show(string body, float seconds)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			_text.text = body;
			Vector2 preferredValues = _text.GetPreferredValues(body, 436f, 0f);
			_panel.sizeDelta = new Vector2(476f, preferredValues.y + 40f);
			_shownAt = Time.unscaledTime;
			_seconds = seconds;
			_group.alpha = 0f;
			_dismissedAt = -1f;
			((Component)_panel).gameObject.SetActive(true);
			IsOpen = true;
		}

		public void Hide()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				((Component)_panel).gameObject.SetActive(false);
			}
			IsOpen = false;
		}

		public void Dismiss()
		{
			if (IsOpen && !(_dismissedAt >= 0f))
			{
				_dismissedAt = Time.unscaledTime;
				_dismissAlpha = _group.alpha;
			}
		}

		public void Tick(bool blocked)
		{
			if (!IsOpen)
			{
				return;
			}
			if (_dismissedAt >= 0f)
			{
				float num = (Time.unscaledTime - _dismissedAt) / 0.2f;
				if (num >= 1f)
				{
					Hide();
				}
				else
				{
					_group.alpha = (blocked ? 0f : (_dismissAlpha * (1f - num)));
				}
				return;
			}
			float num2 = Time.unscaledTime - _shownAt;
			if (num2 >= _seconds)
			{
				Hide();
				return;
			}
			float num3 = Mathf.Min(new float[3]
			{
				1f,
				num2 / 0.6f,
				(_seconds - num2) / 1.2f
			});
			_group.alpha = (blocked ? 0f : Mathf.Clamp01(num3));
		}

		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("OdinsKnot_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 scroll layer created.");
			return true;
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.3";
	}
}