Decompiled source of Odins Thread v0.1.0

plugins/OdinsThread.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
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("OdinsThread")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+09b78a1d38205390742e0f7810357cfaa3cad2c2")]
[assembly: AssemblyProduct("OdinsThread")]
[assembly: AssemblyTitle("OdinsThread")]
[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 OdinsThread
{
	internal sealed class MapLayers : MonoBehaviour
	{
		private const float ConfirmSeconds = 3f;

		private const string CutLabel = "Cut the thread";

		private const string ConfirmLabel = "Cut it? Click again.";

		private const string DoneLabel = "Thread cut.";

		private Minimap _map;

		private TrailGraphic _small;

		private TrailGraphic _large;

		private GameObject _button;

		private TMP_Text _buttonText;

		private float _confirmUntil = -1f;

		private float _labelResetAt = -1f;

		private void Update()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)instance != (Object)(object)_map || (Object)(object)_small == (Object)null || (Object)(object)_large == (Object)null)
			{
				Build(instance);
			}
			bool flag = Plugin.Enabled.Value && (Object)(object)Trail.Instance != (Object)null && !Trail.Instance.Hidden;
			SetActive(_small, flag && Plugin.OnMinimap.Value);
			SetActive(_large, flag && Plugin.OnMap.Value);
			if ((Object)(object)_button == (Object)null)
			{
				EnsureButton(instance);
			}
			if ((Object)(object)_button != (Object)null)
			{
				bool flag2 = Plugin.Enabled.Value && Plugin.OnMap.Value;
				if (_button.activeSelf != flag2)
				{
					_button.SetActive(flag2);
				}
				if (_labelResetAt > 0f && Time.unscaledTime >= _labelResetAt)
				{
					_labelResetAt = -1f;
					_confirmUntil = -1f;
					_buttonText.text = "Cut the thread";
				}
			}
		}

		private void Build(Minimap map)
		{
			_map = map;
			if ((Object)(object)_small != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_small).gameObject);
			}
			if ((Object)(object)_large != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_large).gameObject);
			}
			_small = Create(map.m_mapImageSmall, large: false);
			_large = Create(map.m_mapImageLarge, large: true);
			Plugin.Log.LogInfo((object)"Thread laid over the minimap and the big map.");
		}

		private static TrailGraphic Create(RawImage mapImage, bool large)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(large ? "OdinsThread_Large" : "OdinsThread_Small", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)mapImage).transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.pivot = Vector2.zero;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			TrailGraphic trailGraphic = val.AddComponent<TrailGraphic>();
			trailGraphic.Map = mapImage;
			trailGraphic.Large = large;
			return trailGraphic;
		}

		private static void SetActive(TrailGraphic layer, bool on)
		{
			if ((Object)(object)layer != (Object)null && ((Component)layer).gameObject.activeSelf != on)
			{
				((Component)layer).gameObject.SetActive(on);
			}
		}

		private void EnsureButton(Minimap map)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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)
			//IL_00c4: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: 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_018b: 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_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: 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)
			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) && !((Object)(object)map.m_largeRoot == (Object)null))
			{
				_button = new GameObject("OdinsThread_Cut", new Type[1] { typeof(RectTransform) });
				_button.transform.SetParent(map.m_largeRoot.transform, false);
				RectTransform val2 = (RectTransform)_button.transform;
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0f, 1f);
				val2.anchorMax = val3;
				val2.anchorMin = val3;
				val2.pivot = new Vector2(0f, 1f);
				val2.anchoredPosition = new Vector2(24f, -24f);
				val2.sizeDelta = new Vector2(240f, 40f);
				Image val4 = _button.AddComponent<Image>();
				((Graphic)val4).color = new Color(0f, 0f, 0f, 0.55f);
				Button obj = _button.AddComponent<Button>();
				((Selectable)obj).targetGraphic = (Graphic)(object)val4;
				((UnityEvent)obj.onClick).AddListener(new UnityAction(OnCut));
				GameObject val5 = new GameObject("Label", new Type[1] { typeof(RectTransform) });
				val5.transform.SetParent(_button.transform, false);
				RectTransform val6 = (RectTransform)val5.transform;
				val6.anchorMin = Vector2.zero;
				val6.anchorMax = Vector2.one;
				val3 = (val6.offsetMin = (val6.offsetMax = Vector2.zero));
				_buttonText = (TMP_Text)(object)val5.AddComponent<TextMeshProUGUI>();
				_buttonText.font = val.font;
				_buttonText.fontSharedMaterial = val.fontSharedMaterial;
				_buttonText.fontSize = 20f;
				_buttonText.alignment = (TextAlignmentOptions)514;
				((Graphic)_buttonText).color = new Color(1f, 0.86f, 0.62f);
				((Graphic)_buttonText).raycastTarget = false;
				_buttonText.text = "Cut the thread";
			}
		}

		private void OnCut()
		{
			if (!((Object)(object)Trail.Instance == (Object)null))
			{
				if (_confirmUntil > 0f && Time.unscaledTime <= _confirmUntil)
				{
					Trail.Instance.Clear();
					_buttonText.text = "Thread cut.";
					_confirmUntil = -1f;
					_labelResetAt = Time.unscaledTime + 3f;
				}
				else
				{
					_confirmUntil = Time.unscaledTime + 3f;
					_labelResetAt = _confirmUntil;
					_buttonText.text = "Cut it? Click again.";
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_small != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_small).gameObject);
			}
			if ((Object)(object)_large != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_large).gameObject);
			}
			if ((Object)(object)_button != (Object)null)
			{
				Object.Destroy((Object)(object)_button);
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
	internal static class PortalPatch
	{
		private static void Prefix(Player player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				Trail.PortalPending = true;
			}
		}

		private static void Postfix()
		{
			Trail.PortalPending = false;
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	internal static class TeleportPatch
	{
		private static void Postfix(Player __instance, bool __result)
		{
			if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Trail.Instance?.OnTeleportStart(__instance, Trail.PortalPending);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class DeathPatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Trail.Instance?.OnDeath(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Shutdown")]
	internal static class ShutdownPatch
	{
		private static void Prefix()
		{
			Trail.Instance?.Save("logging out");
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinsthread", "Odin's Thread", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinsthread";

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

		public const string Version = "0.1.0";

		internal static ManualLogSource Log;

		internal static Harmony Patcher;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyboardShortcut> ToggleKey;

		internal static ConfigEntry<bool> OnMinimap;

		internal static ConfigEntry<bool> OnMap;

		internal static ConfigEntry<string> Colour;

		internal static ConfigEntry<float> Opacity;

		internal static ConfigEntry<bool> MarkDeaths;

		internal static ConfigEntry<bool> MarkPortals;

		internal static ConfigEntry<float> Spacing;

		internal static ConfigEntry<int> MaxDays;

		internal static ConfigEntry<int> MaxPoints;

		internal static ConfigEntry<bool> DebugLog;

		private Trail _trail;

		private void Awake()
		{
			//IL_003b: 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)
			//IL_00eb: Expected O, but got Unknown
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Off = nothing is recorded and nothing is drawn. The trail you already have is kept.");
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", KeyboardShortcut.Empty, "Hides or shows the thread on both maps. Recording carries on while it's hidden. Unbound by default; any key or combination works, e.g. F7 or T + LeftAlt.");
			OnMinimap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "OnMinimap", true, "Draw the thread on the minimap.");
			OnMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "OnMap", true, "Draw the thread on the big map, with the \"Cut the thread\" button.");
			Colour = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "Colour", "gold", "Colour of the thread: gold, amber, silver, white, red, blue, green, purple, or #RRGGBB.");
			Opacity = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "Opacity", 0.6f, new ConfigDescription("How strong today's trail is, 0.1 to 1. Older trail is fainter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			MarkDeaths = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "MarkDeaths", true, "A small cross where you died.");
			MarkPortals = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "MarkPortals", true, "A small ring at both ends of every portal trip.");
			Spacing = ((BaseUnityPlugin)this).Config.Bind<float>("Trail", "Spacing", 4f, new ConfigDescription("Metres you move between trail points, 1 to 20. Straight stretches are thinned out further.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			MaxDays = ((BaseUnityPlugin)this).Config.Bind<int>("Trail", "MaxDays", 10, new ConfigDescription("In-game days before old trail is forgotten, 0 to 100. 0 = keep everything (it still fades, and MaxPoints still applies).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			MaxPoints = ((BaseUnityPlugin)this).Config.Bind<int>("Trail", "MaxPoints", 20000, new ConfigDescription("Most trail points kept per character per world, 1000 to 100000. The oldest go first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 100000), Array.Empty<object>()));
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Write loads, saves, breaks in the thread, deaths and portal trips to the BepInEx log.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				Trail.Instance?.SettingsChanged();
			};
			Patcher = new Harmony("hevyh2o.valheim.odinsthread");
			Patcher.PatchAll(typeof(Plugin).Assembly);
			_trail = ((Component)this).gameObject.AddComponent<Trail>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Thread 0.1.0 loaded. Odin is holding the other end.");
		}

		private void OnDestroy()
		{
			Harmony patcher = Patcher;
			if (patcher != null)
			{
				patcher.UnpatchSelf();
			}
			if ((Object)(object)_trail != (Object)null)
			{
				Object.Destroy((Object)(object)_trail);
			}
		}
	}
	internal sealed class Trail : MonoBehaviour
	{
		private const float JumpMetres = 60f;

		private const float StraightTolerance = 0.75f;

		private const float MaxStraightRun = 60f;

		private const float SaveInterval = 60f;

		private const float PruneInterval = 30f;

		private const float DefaultDayLength = 1200f;

		internal static bool PortalPending;

		internal readonly List<TrailPoint> Points = new List<TrailPoint>();

		private string _path;

		private ZNet _net;

		private PlayerProfile _profile;

		private Player _player;

		private Vector3 _lastFramePos;

		private bool _breakNext = true;

		private bool _teleporting;

		private bool _teleportIsPortal;

		private bool _dirty;

		private float _nextSave;

		private float _nextPrune;

		private MapLayers _layers;

		private static readonly Dictionary<string, Color> Named = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase)
		{
			["gold"] = new Color(0.91f, 0.76f, 0.35f),
			["amber"] = new Color(1f, 0.62f, 0.2f),
			["silver"] = new Color(0.8f, 0.82f, 0.86f),
			["white"] = Color.white,
			["red"] = new Color(0.86f, 0.3f, 0.26f),
			["blue"] = new Color(0.4f, 0.66f, 0.95f),
			["green"] = new Color(0.5f, 0.78f, 0.38f),
			["purple"] = new Color(0.66f, 0.5f, 0.86f)
		};

		internal static Trail Instance { get; private set; }

		internal int Revision { get; private set; }

		internal bool Hidden { get; private set; }

		internal Color BaseColour { get; private set; } = new Color(0.91f, 0.76f, 0.35f);

		internal static float DayLength
		{
			get
			{
				if (!((Object)(object)EnvMan.instance != (Object)null) || EnvMan.instance.m_dayLengthSec <= 0)
				{
					return 1200f;
				}
				return EnvMan.instance.m_dayLengthSec;
			}
		}

		internal static double Now
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0.0;
				}
				return ZNet.instance.GetTimeSeconds();
			}
		}

		private void Awake()
		{
			Instance = this;
			_layers = ((Component)this).gameObject.AddComponent<MapLayers>();
			ReadColour();
		}

		private void OnDestroy()
		{
			Save("unloading");
			if ((Object)(object)_layers != (Object)null)
			{
				Object.Destroy((Object)(object)_layers);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void OnApplicationQuit()
		{
			Save("quitting");
		}

		internal void SettingsChanged()
		{
			ReadColour();
			Trim();
			Revision++;
		}

		private void Update()
		{
			//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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				if ((Object)(object)_player != (Object)null)
				{
					_player = null;
					_breakNext = true;
					Save("player gone");
				}
			}
			else if (EnsureLoaded())
			{
				if ((Object)(object)localPlayer != (Object)(object)_player)
				{
					_player = localPlayer;
					_breakNext = true;
					_teleporting = false;
					_lastFramePos = ((Component)localPlayer).transform.position;
				}
				HandleToggleKey(localPlayer);
				if (Time.time >= _nextPrune)
				{
					_nextPrune = Time.time + 30f;
					Trim();
				}
				if (_dirty && Time.time >= _nextSave)
				{
					Save("autosave");
				}
				if (Plugin.Enabled.Value)
				{
					Record(localPlayer);
				}
			}
		}

		private void Record(Player player)
		{
			//IL_0006: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			if (((Character)player).IsDead() || ((Character)player).InIntro())
			{
				_breakNext = true;
				_lastFramePos = position;
				return;
			}
			if (((Character)player).IsTeleporting())
			{
				_teleporting = true;
				_lastFramePos = position;
				return;
			}
			if (_teleporting)
			{
				_teleporting = false;
				_breakNext = false;
				_lastFramePos = position;
				if (!Character.InInterior(position))
				{
					Add(position, (byte)(1 | (_teleportIsPortal ? 4 : 0)));
					Debug(_teleportIsPortal ? ("Came out of a portal at " + Where(position) + ".") : ("Arrived at " + Where(position) + "."));
				}
				else
				{
					_breakNext = true;
				}
				_teleportIsPortal = false;
				return;
			}
			if (Character.InInterior(position))
			{
				_breakNext = true;
				_lastFramePos = position;
				return;
			}
			Vector3 val = Flat(position - _lastFramePos);
			if (((Vector3)(ref val)).sqrMagnitude > 3600f)
			{
				val = Flat(position - _lastFramePos);
				Debug($"Jumped {((Vector3)(ref val)).magnitude:0} m in one frame. The thread breaks.");
				_breakNext = true;
			}
			_lastFramePos = position;
			if (_breakNext || Points.Count == 0)
			{
				_breakNext = false;
				Add(position, 1);
				return;
			}
			TrailPoint value = Points[Points.Count - 1];
			float value2 = Plugin.Spacing.Value;
			if (Sqr(position.x - value.X) + Sqr(position.z - value.Z) < value2 * value2)
			{
				return;
			}
			if (Points.Count >= 2 && value.Flags == 0)
			{
				TrailPoint trailPoint = Points[Points.Count - 2];
				if (!trailPoint.Has(2) && !trailPoint.Has(4) && Sqr(position.x - trailPoint.X) + Sqr(position.z - trailPoint.Z) < 3600f && DistanceToLine(value.X, value.Z, trailPoint.X, trailPoint.Z, position.x, position.z) < 0.75f)
				{
					value.X = position.x;
					value.Z = position.z;
					value.Time = Now;
					Points[Points.Count - 1] = value;
					Changed();
					return;
				}
			}
			Add(position, 0);
		}

		internal void OnTeleportStart(Player player, bool portal)
		{
			//IL_001b: 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)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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)
			if (EnsureLoaded() && Plugin.Enabled.Value)
			{
				Vector3 position = ((Component)player).transform.position;
				_teleporting = true;
				_teleportIsPortal = portal;
				if (!Character.InInterior(position))
				{
					Add(position, (byte)((_breakNext ? 1u : 0u) | (uint)(portal ? 4 : 0)));
					_breakNext = true;
					Debug(portal ? ("Into a portal at " + Where(position) + ".") : ("Teleporting from " + Where(position) + "."));
				}
			}
		}

		internal void OnDeath(Player player)
		{
			//IL_001b: 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)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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 (EnsureLoaded() && Plugin.Enabled.Value)
			{
				Vector3 position = ((Component)player).transform.position;
				if (!Character.InInterior(position))
				{
					Add(position, (byte)((_breakNext ? 1u : 0u) | 2u));
				}
				_breakNext = true;
				Debug("Died at " + Where(position) + ". Marked.");
				Save("death");
			}
		}

		internal void Clear()
		{
			Points.Clear();
			_breakNext = true;
			Changed();
			if (_path != null)
			{
				try
				{
					TrailStore.Delete(_path);
					_dirty = false;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Couldn't delete " + _path + ": " + ex.Message));
				}
			}
			Plugin.Log.LogInfo((object)"Thread cut. This character's trail in this world is gone.");
		}

		private void Add(Vector3 pos, byte flags)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Points.Add(new TrailPoint
			{
				X = pos.x,
				Z = pos.z,
				Time = Now,
				Flags = flags
			});
			if (Points.Count > Plugin.MaxPoints.Value)
			{
				Trim();
			}
			Changed();
		}

		private void Changed()
		{
			Revision++;
			if (!_dirty)
			{
				_dirty = true;
				_nextSave = Time.time + 60f;
			}
		}

		private void Trim()
		{
			int i = 0;
			int value = Plugin.MaxDays.Value;
			if (value > 0 && (Object)(object)ZNet.instance != (Object)null)
			{
				for (double num = Now - (double)value * (double)DayLength; i < Points.Count && Points[i].Time < num; i++)
				{
				}
			}
			int value2 = Plugin.MaxPoints.Value;
			if (Points.Count - i > value2)
			{
				i = Points.Count - value2 + value2 / 20;
			}
			if (i > 0)
			{
				i = Math.Min(i, Points.Count);
				Points.RemoveRange(0, i);
				if (Points.Count > 0)
				{
					TrailPoint value3 = Points[0];
					value3.Flags |= 1;
					Points[0] = value3;
				}
				Changed();
				Debug($"Forgot {i} old points; {Points.Count} left.");
			}
		}

		private bool EnsureLoaded()
		{
			ZNet instance = ZNet.instance;
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (_path != null && (Object)(object)instance == (Object)(object)_net && val == _profile)
			{
				return true;
			}
			string text = TrailStore.PathFor(val, instance);
			if (text == null)
			{
				return false;
			}
			_net = instance;
			_profile = val;
			if (text == _path)
			{
				return true;
			}
			if (_path != null)
			{
				Save("changing worlds");
			}
			_path = text;
			Points.Clear();
			_dirty = false;
			_breakNext = true;
			try
			{
				Points.AddRange(TrailStore.Load(text));
				Plugin.Log.LogInfo((object)$"Loaded {Points.Count} trail points from {Path.GetFileName(text)}.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't read " + Path.GetFileName(text) + " (" + ex.Message + "). Starting a fresh thread; the old file is kept as .bad."));
				try
				{
					File.Copy(text, text + ".bad", overwrite: true);
				}
				catch (Exception)
				{
				}
				Points.Clear();
			}
			Revision++;
			return true;
		}

		internal void Save(string why)
		{
			if (_path == null || !_dirty)
			{
				return;
			}
			try
			{
				TrailStore.Save(_path, Points);
				_dirty = false;
				Debug($"Saved {Points.Count} points ({why}).");
			}
			catch (Exception ex)
			{
				_nextSave = Time.time + 60f;
				Plugin.Log.LogWarning((object)("Couldn't save the thread to " + _path + ": " + ex.Message));
			}
		}

		private void HandleToggleKey(Player player)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Pressed(Plugin.ToggleKey.Value) && !Typing())
			{
				Hidden = !Hidden;
				Revision++;
				((Character)player).Message((MessageType)1, Hidden ? "Odin's Thread: hidden. Still recording." : "Odin's Thread: shown", 0, (Sprite)null, false);
			}
		}

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

		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;
		}

		private void ReadColour()
		{
			//IL_0029: 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_0055: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			string text = (Plugin.Colour.Value ?? "").Trim();
			if (Named.TryGetValue(text, out var value))
			{
				BaseColour = value;
				return;
			}
			Color val = default(Color);
			if (ColorUtility.TryParseHtmlString(text, ref val) || ColorUtility.TryParseHtmlString("#" + text, ref val))
			{
				BaseColour = new Color(val.r, val.g, val.b);
				return;
			}
			BaseColour = Named["gold"];
			Plugin.Log.LogWarning((object)("Colour \"" + text + "\" isn't a colour Odin knows. Using gold."));
		}

		private static Vector3 Flat(Vector3 v)
		{
			//IL_0000: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(v.x, 0f, v.z);
		}

		private static float Sqr(float v)
		{
			return v * v;
		}

		private static string Where(Vector3 p)
		{
			//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 $"({p.x:0}, {p.z:0})";
		}

		private static float DistanceToLine(float px, float pz, float ax, float az, float bx, float bz)
		{
			float num = bx - ax;
			float num2 = bz - az;
			float num3 = Mathf.Sqrt(num * num + num2 * num2);
			if (num3 < 0.001f)
			{
				return Mathf.Sqrt(Sqr(px - ax) + Sqr(pz - az));
			}
			return Mathf.Abs(num * (az - pz) - (ax - px) * num2) / num3;
		}

		private static void Debug(string message)
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)message);
			}
		}
	}
	internal sealed class TrailGraphic : MaskableGraphic
	{
		private const float MinStep = 1.5f;

		private const float RefreshSeconds = 5f;

		private const int MaxQuads = 16000;

		private static readonly Color DeathColour = new Color(0.85f, 0.22f, 0.16f);

		private static readonly Color PortalColour = new Color(0.55f, 0.78f, 1f);

		internal RawImage Map;

		internal bool Large;

		private Rect _lastUv;

		private Vector2 _lastSize;

		private int _lastRevision = -1;

		private float _nextRefresh;

		private bool _warned;

		private readonly List<Vector2> _deaths = new List<Vector2>();

		private readonly List<float> _deathAlpha = new List<float>();

		private readonly List<Vector2> _portals = new List<Vector2>();

		private readonly List<float> _portalAlpha = new List<float>();

		private readonly UIVertex[] _quad = (UIVertex[])(object)new UIVertex[4];

		private int _quads;

		protected override void Awake()
		{
			((UIBehaviour)this).Awake();
			((Graphic)this).raycastTarget = false;
		}

		private void LateUpdate()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Trail instance = Trail.Instance;
			if (!((Object)(object)Map == (Object)null) && !((Object)(object)instance == (Object)null))
			{
				Rect uvRect = Map.uvRect;
				Rect rect = ((Graphic)this).rectTransform.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				if (uvRect != _lastUv || size != _lastSize || instance.Revision != _lastRevision || Time.unscaledTime >= _nextRefresh)
				{
					_lastUv = uvRect;
					_lastSize = size;
					_lastRevision = instance.Revision;
					_nextRefresh = Time.unscaledTime + 5f;
					((Graphic)this).SetVerticesDirty();
				}
			}
		}

		protected override void OnPopulateMesh(VertexHelper vh)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			vh.Clear();
			Trail instance = Trail.Instance;
			Minimap instance2 = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)Map == (Object)null || instance.Points.Count == 0)
			{
				return;
			}
			Rect uvRect = Map.uvRect;
			Rect rect = ((Graphic)this).rectTransform.rect;
			if (((Rect)(ref uvRect)).width <= 0f || ((Rect)(ref uvRect)).height <= 0f || ((Rect)(ref rect)).width <= 0f || ((Rect)(ref rect)).height <= 0f)
			{
				return;
			}
			float num = (Large ? 2.5f : 2f);
			float num2 = instance2.m_textureSize / 2;
			float num3 = instance2.m_textureSize;
			float pixelSize = instance2.m_pixelSize;
			float num4 = ((Rect)(ref rect)).width / ((Rect)(ref uvRect)).width;
			float num5 = ((Rect)(ref rect)).height / ((Rect)(ref uvRect)).height;
			double now = Trail.Now;
			float dayLength = Trail.DayLength;
			int value = Plugin.MaxDays.Value;
			float value2 = Plugin.Opacity.Value;
			Color baseColour = instance.BaseColour;
			bool value3 = Plugin.MarkDeaths.Value;
			bool value4 = Plugin.MarkPortals.Value;
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((Rect)(ref rect)).xMin - num, ((Rect)(ref rect)).yMin - num, ((Rect)(ref rect)).width + 2f * num, ((Rect)(ref rect)).height + 2f * num);
			_deaths.Clear();
			_deathAlpha.Clear();
			_portals.Clear();
			_portalAlpha.Clear();
			_quads = 0;
			Vector2 val = default(Vector2);
			bool flag = false;
			List<TrailPoint> points = instance.Points;
			Vector2 val2 = default(Vector2);
			for (int i = 0; i < points.Count; i++)
			{
				TrailPoint trailPoint = points[i];
				float num6 = (trailPoint.X / pixelSize + num2) / num3;
				float num7 = (trailPoint.Z / pixelSize + num2) / num3;
				((Vector2)(ref val2))..ctor(((Rect)(ref rect)).xMin + (num6 - ((Rect)(ref uvRect)).xMin) * num4, ((Rect)(ref rect)).yMin + (num7 - ((Rect)(ref uvRect)).yMin) * num5);
				float num8 = Fade(now - trailPoint.Time, dayLength, value) * value2;
				if (trailPoint.Has(2) && value3 && ((Rect)(ref rect)).Contains(val2))
				{
					_deaths.Add(val2);
					_deathAlpha.Add(Mathf.Max(num8, 0.5f));
				}
				if (trailPoint.Has(4) && value4 && ((Rect)(ref rect)).Contains(val2))
				{
					_portals.Add(val2);
					_portalAlpha.Add(Mathf.Max(num8, 0.5f));
				}
				if (trailPoint.Has(1) || !flag)
				{
					val = val2;
					flag = true;
					continue;
				}
				if ((trailPoint.Flags & 6) == 0)
				{
					Vector2 val3 = val2 - val;
					if (((Vector2)(ref val3)).sqrMagnitude < 2.25f && i < points.Count - 1)
					{
						continue;
					}
				}
				Vector2 a = val;
				Vector2 b = val2;
				val = val2;
				if (Clip(ref a, ref b, r))
				{
					baseColour.a = num8;
					if (!AddSegment(vh, a, b, num, baseColour))
					{
						break;
					}
				}
			}
			float num9 = (Large ? 5f : 3.5f);
			for (int j = 0; j < _portals.Count; j++)
			{
				Color portalColour = PortalColour;
				portalColour.a = _portalAlpha[j];
				Ring(vh, _portals[j], num9, num, portalColour);
			}
			for (int k = 0; k < _deaths.Count; k++)
			{
				Color deathColour = DeathColour;
				deathColour.a = _deathAlpha[k];
				Cross(vh, _deaths[k], num9 + 1f, num + 0.5f, deathColour);
			}
		}

		private static float Fade(double ageSeconds, float day, int maxDays)
		{
			float num = Mathf.Max(0f, (float)(ageSeconds / (double)day));
			if (maxDays > 0)
			{
				return Mathf.Lerp(1f, 0.15f, num / (float)maxDays);
			}
			return Mathf.Max(0.25f, 1f - num * 0.075f);
		}

		private bool AddSegment(VertexHelper vh, Vector2 a, Vector2 b, float width, Color colour)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0057: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			float magnitude = ((Vector2)(ref val)).magnitude;
			if (magnitude < 0.0001f)
			{
				return true;
			}
			Vector2 val2 = new Vector2(0f - val.y, val.x) / magnitude * (width * 0.5f);
			Vector2 val3 = val / magnitude * (width * 0.35f);
			return Quad(vh, a - val3 - val2, a - val3 + val2, b + val3 + val2, b + val3 - val2, colour);
		}

		private bool Quad(VertexHelper vh, Vector2 v0, Vector2 v1, Vector2 v2, Vector2 v3, Color colour)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_005d: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (_quads >= 16000)
			{
				if (!_warned)
				{
					_warned = true;
					Plugin.Log.LogInfo((object)"The thread is too busy to draw in full at this zoom. Zoom in for the rest.");
				}
				return false;
			}
			_quads++;
			Color32 colour2 = Color32.op_Implicit(colour);
			_quad[0] = Vertex(v0, colour2);
			_quad[1] = Vertex(v1, colour2);
			_quad[2] = Vertex(v2, colour2);
			_quad[3] = Vertex(v3, colour2);
			vh.AddUIVertexQuad(_quad);
			return true;
		}

		private static UIVertex Vertex(Vector2 pos, Color32 colour)
		{
			//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_0008: Unknown result type (might be due to invalid IL or missing references)
			//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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			UIVertex simpleVert = UIVertex.simpleVert;
			simpleVert.position = Vector2.op_Implicit(pos);
			simpleVert.color = colour;
			return simpleVert;
		}

		private void Cross(VertexHelper vh, Vector2 at, float size, float width, Color colour)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(size, size);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(size, 0f - size);
			AddSegment(vh, at - val, at + val, width, colour);
			AddSegment(vh, at - val2, at + val2, width, colour);
		}

		private void Ring(VertexHelper vh, Vector2 at, float radius, float width, Color colour)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_005d: 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)
			Vector2 a = at + new Vector2(radius, 0f);
			for (int i = 1; i <= 10; i++)
			{
				float num = (float)i * (float)Math.PI * 2f / 10f;
				Vector2 val = at + new Vector2(Mathf.Cos(num), Mathf.Sin(num)) * radius;
				AddSegment(vh, a, val, width * 0.8f, colour);
				a = val;
			}
		}

		private static bool Clip(ref Vector2 a, ref Vector2 b, Rect r)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_00dc: 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)
			float t = 0f;
			float t2 = 1f;
			float num = b.x - a.x;
			float num2 = b.y - a.y;
			if (!ClipEdge(0f - num, a.x - ((Rect)(ref r)).xMin, ref t, ref t2))
			{
				return false;
			}
			if (!ClipEdge(num, ((Rect)(ref r)).xMax - a.x, ref t, ref t2))
			{
				return false;
			}
			if (!ClipEdge(0f - num2, a.y - ((Rect)(ref r)).yMin, ref t, ref t2))
			{
				return false;
			}
			if (!ClipEdge(num2, ((Rect)(ref r)).yMax - a.y, ref t, ref t2))
			{
				return false;
			}
			Vector2 val = a;
			if (t2 < 1f)
			{
				b = val + new Vector2(num, num2) * t2;
			}
			if (t > 0f)
			{
				a = val + new Vector2(num, num2) * t;
			}
			return true;
		}

		private static bool ClipEdge(float p, float q, ref float t0, ref float t1)
		{
			if (Mathf.Approximately(p, 0f))
			{
				return q >= 0f;
			}
			float num = q / p;
			if (p < 0f)
			{
				if (num > t1)
				{
					return false;
				}
				if (num > t0)
				{
					t0 = num;
				}
			}
			else
			{
				if (num < t0)
				{
					return false;
				}
				if (num < t1)
				{
					t1 = num;
				}
			}
			return true;
		}
	}
	internal struct TrailPoint
	{
		public const byte Start = 1;

		public const byte Death = 2;

		public const byte Portal = 4;

		public float X;

		public float Z;

		public double Time;

		public byte Flags;

		public bool Has(byte flag)
		{
			return (Flags & flag) != 0;
		}
	}
	internal static class TrailStore
	{
		private const uint Magic = 1380471887u;

		private const int FormatVersion = 1;

		internal static string Folder => Path.Combine(Paths.ConfigPath, "OdinsThread");

		internal static string PathFor(PlayerProfile profile, ZNet net)
		{
			if (profile == null || (Object)(object)net == (Object)null)
			{
				return null;
			}
			string worldName = net.GetWorldName();
			if (string.IsNullOrEmpty(worldName))
			{
				return null;
			}
			string text = profile.GetFilename();
			if (string.IsNullOrEmpty(text))
			{
				text = profile.GetName();
			}
			string path = $"{Clean(text)}__{Clean(worldName)}_{net.GetWorldUID():x}.thread";
			return Path.Combine(Folder, path);
		}

		internal static List<TrailPoint> Load(string path)
		{
			List<TrailPoint> list = new List<TrailPoint>();
			if (!File.Exists(path))
			{
				return list;
			}
			using BinaryReader binaryReader = new BinaryReader(File.OpenRead(path));
			if (binaryReader.ReadUInt32() != 1380471887)
			{
				throw new InvalidDataException("not an Odin's Thread file");
			}
			int num = binaryReader.ReadInt32();
			if (num != 1)
			{
				throw new InvalidDataException($"file version {num}, this build reads {1}");
			}
			int num2 = binaryReader.ReadInt32();
			if (num2 < 0 || num2 > 10000000)
			{
				throw new InvalidDataException($"implausible point count {num2}");
			}
			list.Capacity = num2;
			for (int i = 0; i < num2; i++)
			{
				list.Add(new TrailPoint
				{
					X = binaryReader.ReadSingle(),
					Z = binaryReader.ReadSingle(),
					Time = binaryReader.ReadDouble(),
					Flags = binaryReader.ReadByte()
				});
			}
			return list;
		}

		internal static void Save(string path, List<TrailPoint> points)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			using (BinaryWriter binaryWriter = new BinaryWriter(File.Create(text)))
			{
				binaryWriter.Write(1380471887u);
				binaryWriter.Write(1);
				binaryWriter.Write(points.Count);
				foreach (TrailPoint point in points)
				{
					binaryWriter.Write(point.X);
					binaryWriter.Write(point.Z);
					binaryWriter.Write(point.Time);
					binaryWriter.Write(point.Flags);
				}
			}
			if (File.Exists(path))
			{
				File.Replace(text, path, null);
			}
			else
			{
				File.Move(text, path);
			}
		}

		internal static void Delete(string path)
		{
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}

		private static string Clean(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char c in s)
			{
				stringBuilder.Append((Array.IndexOf(invalidFileNameChars, c) >= 0) ? '-' : c);
			}
			return stringBuilder.ToString().Trim();
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.0";
	}
}