Decompiled source of DeadReckoning v0.1.0

BepInEx/plugins/DeadReckoning/DeadReckoning.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace DeadReckoning;

internal static class BaseRange
{
	private static readonly FieldRef<List<CraftingStation>> AllStations = AccessTools.StaticFieldRefAccess<List<CraftingStation>>(AccessTools.Field(typeof(CraftingStation), "m_allStations"));

	private static int _cachedFrame = -1;

	private static bool _cachedAtBase;

	internal static bool LocalPlayerAtBase()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return true;
		}
		if (Time.frameCount == _cachedFrame)
		{
			return _cachedAtBase;
		}
		_cachedFrame = Time.frameCount;
		_cachedAtBase = AtBase(((Component)localPlayer).transform.position);
		return _cachedAtBase;
	}

	internal static bool AtBase(Vector3 point)
	{
		//IL_003c: 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_004f: 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_005d: Unknown result type (might be due to invalid IL or missing references)
		List<CraftingStation> list = AllStations.Invoke();
		if (list == null)
		{
			return false;
		}
		float value = DeadReckoningPlugin.BasePadding.Value;
		foreach (CraftingStation item in list)
		{
			if (!((Object)(object)item == (Object)null))
			{
				Vector3 position = ((Component)item).transform.position;
				float num = item.GetStationBuildRange() + value;
				point.y = position.y;
				if (Vector3.Distance(position, point) < num)
				{
					return true;
				}
			}
		}
		return false;
	}
}
[HarmonyPatch]
internal static class GhostPatches
{
	private static readonly FieldRef<Player, GameObject> PlacementGhost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

	private static readonly FieldRef<Player, GameObject> PlacementMarker = AccessTools.FieldRefAccess<Player, GameObject>("m_placementMarkerInstance");

	private static readonly FieldRef<Player, PlacementStatus> PlacementStatus = AccessTools.FieldRefAccess<Player, PlacementStatus>("m_placementStatus");

	private static bool _pickPending;

	private static float _pickYaw;

	private static float _pickOffset;

	private static float _pickCandidateYaw = float.NaN;

	[HarmonyTranspiler]
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> UpdatePlacementGhostTranspiler(IEnumerable<CodeInstruction> instructions)
	{
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Expected O, but got Unknown
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		FieldInfo placeRotation = AccessTools.Field(typeof(Player), "m_placeRotation");
		MethodInfo euler = AccessTools.Method(typeof(Quaternion), "Euler", new Type[3]
		{
			typeof(float),
			typeof(float),
			typeof(float)
		}, (Type[])null);
		MethodInfo methodInfo = AccessTools.Method(typeof(GhostPatches), "Adjust", (Type[])null, (Type[])null);
		int num = list.FindIndex((CodeInstruction c) => c.opcode == OpCodes.Ldfld && c.operand is FieldInfo fieldInfo && fieldInfo == placeRotation);
		int num2 = ((num < 0) ? (-1) : list.FindIndex(num, (CodeInstruction c) => CodeInstructionExtensions.Calls(c, euler)));
		int num3 = ((num2 < 0) ? (-1) : list.FindIndex(num2, IsStloc));
		if (num < 0 || num2 < 0 || num2 - num > 8 || num3 < 0 || num3 - num2 > 4)
		{
			DeadReckoningPlugin.Log.LogError((object)($"[DeadReckoning] Player.UpdatePlacementGhost has an unexpected shape (ldfld {num}, Euler {num2}, stloc {num3}); " + "the placement ghost keeps its vanilla heading everywhere."));
			return list;
		}
		CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
		val.labels.AddRange(list[num3].labels);
		list[num3].labels.Clear();
		list.Insert(num3, val);
		list.Insert(num3 + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo));
		return list;
	}

	private static bool IsStloc(CodeInstruction code)
	{
		if (!(code.opcode == OpCodes.Stloc) && !(code.opcode == OpCodes.Stloc_S) && !(code.opcode == OpCodes.Stloc_0) && !(code.opcode == OpCodes.Stloc_1) && !(code.opcode == OpCodes.Stloc_2))
		{
			return code.opcode == OpCodes.Stloc_3;
		}
		return true;
	}

	internal static Quaternion Adjust(Quaternion rotation, Player player)
	{
		//IL_0016: 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_0035: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
		{
			return rotation;
		}
		if (!DeadReckoningPlugin.Active)
		{
			ResetPick();
			return rotation;
		}
		if (BaseRange.LocalPlayerAtBase())
		{
			ResetPick();
			return rotation;
		}
		Quaternion lookYaw = ((Character)player).GetLookYaw();
		float y = ((Quaternion)(ref lookYaw)).eulerAngles.y;
		if (_pickPending)
		{
			_pickPending = false;
			_pickOffset = Mathf.DeltaAngle(0f, _pickYaw - y - ((Quaternion)(ref rotation)).eulerAngles.y);
		}
		return Quaternion.Euler(0f, y + _pickOffset, 0f) * rotation;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Player), "SetSelectedPiece", new Type[] { typeof(Piece) })]
	private static void SetSelectedPiecePrefix(Piece p)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		_pickCandidateYaw = ((DeadReckoningPlugin.Active && Object.op_Implicit((Object)(object)p)) ? ((Component)p).transform.eulerAngles.y : float.NaN);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "CopyPiece")]
	private static void CopyPiecePostfix(bool __result)
	{
		if (DeadReckoningPlugin.Active && __result && !float.IsNaN(_pickCandidateYaw))
		{
			RequestPick(_pickCandidateYaw);
		}
		_pickCandidateYaw = float.NaN;
	}

	internal static void RequestPick(float worldYaw)
	{
		if (DeadReckoningPlugin.Active)
		{
			_pickPending = true;
			_pickYaw = worldYaw;
		}
	}

	private static void ResetPick()
	{
		_pickPending = false;
		_pickOffset = 0f;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "SetupPlacementGhost")]
	private static void SetupPlacementGhostPostfix()
	{
		ResetPick();
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
	[HarmonyPriority(0)]
	private static void UpdatePlacementGhostPostfix(Player __instance)
	{
		if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
		{
			return;
		}
		GameObject val = PlacementGhost.Invoke(__instance);
		if (Object.op_Implicit((Object)(object)val) && ShouldHide(__instance))
		{
			val.SetActive(false);
			GameObject val2 = PlacementMarker.Invoke(__instance);
			if (Object.op_Implicit((Object)(object)val2))
			{
				val2.SetActive(false);
			}
			PlacementStatus.Invoke(__instance) = (PlacementStatus)12;
		}
	}

	private static bool ShouldHide(Player player)
	{
		//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)
		if (!DeadReckoningPlugin.Active)
		{
			return false;
		}
		if (BaseRange.LocalPlayerAtBase())
		{
			return false;
		}
		if (DeadReckoningPlugin.HideOnShip.Value && (((Character)player).IsAttachedToShip() || ((Character)player).InNumShipVolumes > 0))
		{
			return true;
		}
		if (DeadReckoningPlugin.HideWhenMoving.Value)
		{
			Vector3 velocity = ((Character)player).GetVelocity();
			velocity.y = 0f;
			if (((Vector3)(ref velocity)).magnitude > DeadReckoningPlugin.MovingThreshold.Value)
			{
				return true;
			}
		}
		return false;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	[HarmonyPriority(0)]
	private static void UpdatePlacementPostfix(Player __instance)
	{
		if (GizmoBridge.Present && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && DeadReckoningPlugin.Active && !BaseRange.LocalPlayerAtBase())
		{
			GizmoBridge.HideGizmos();
		}
	}
}
internal static class GizmoBridge
{
	internal const string GizmoGuid = "bruce.valheim.comfymods.gizmo";

	private static Action _hideGizmos;

	internal static bool Present { get; private set; }

	internal static void Initialize(Harmony harmony)
	{
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Expected O, but got Unknown
		if (!Chainloader.PluginInfos.TryGetValue("bruce.valheim.comfymods.gizmo", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			return;
		}
		try
		{
			Type type = ((object)value.Instance).GetType().Assembly.GetType("ComfyGizmo.RotationManager");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "HideGizmos", Type.EmptyTypes, (Type[])null));
			if (methodInfo == null)
			{
				DeadReckoningPlugin.Log.LogWarning((object)($"[DeadReckoning] Gizmo {value.Metadata.Version} is loaded but ComfyGizmo.RotationManager.HideGizmos() " + "was not found; its rings stay visible away from a base."));
				return;
			}
			_hideGizmos = (Action)Delegate.CreateDelegate(typeof(Action), methodInfo);
			MethodInfo methodInfo2 = AccessTools.Method(type, "MatchPieceRotation", new Type[1] { typeof(Piece) }, (Type[])null);
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(GizmoBridge), "MatchPieceRotationPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				DeadReckoningPlugin.Log.LogWarning((object)"[DeadReckoning] ComfyGizmo.RotationManager.MatchPieceRotation(Piece) was not found; Gizmo's copy-rotation key is not treated as a pick.");
			}
			Present = true;
			DeadReckoningPlugin.Log.LogInfo((object)$"[DeadReckoning] Gizmo {value.Metadata.Version} detected; its rings are hidden away from a base.");
		}
		catch (Exception arg)
		{
			Present = false;
			_hideGizmos = null;
			DeadReckoningPlugin.Log.LogWarning((object)$"[DeadReckoning] Gizmo integration disabled: {arg}");
		}
	}

	internal static void HideGizmos()
	{
		if (_hideGizmos == null)
		{
			return;
		}
		try
		{
			_hideGizmos();
		}
		catch (Exception arg)
		{
			Present = false;
			_hideGizmos = null;
			DeadReckoningPlugin.Log.LogWarning((object)$"[DeadReckoning] hiding Gizmo's rings failed; integration disabled: {arg}");
		}
	}

	private static void MatchPieceRotationPostfix(Piece __0)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		if (DeadReckoningPlugin.Active && Object.op_Implicit((Object)(object)__0))
		{
			GhostPatches.RequestPick(((Component)__0).transform.eulerAngles.y);
		}
	}
}
internal static class NumericSettings
{
	private sealed class FiniteRange : AcceptableValueBase
	{
		private readonly string _setting;

		private readonly float _maximum;

		private readonly float _defaultValue;

		private readonly Action<string> _warn;

		internal FiniteRange(string setting, float maximum, float defaultValue, Action<string> warn)
			: base(typeof(float))
		{
			_setting = setting;
			_maximum = maximum;
			_defaultValue = defaultValue;
			_warn = warn;
		}

		public override object Clamp(object value)
		{
			float num = (float)value;
			float num2 = Normalize(num, _maximum, _defaultValue);
			if (!num.Equals(num2))
			{
				string text = ((float.IsNaN(num) || float.IsInfinity(num)) ? "non-finite input; using default" : ("outside 0–" + Format(_maximum) + "; clamped to nearest boundary"));
				_warn("[DeadReckoning] " + _setting + ": corrected " + Format(num) + " to " + Format(num2) + " (" + text + ").");
			}
			return num2;
		}

		public override bool IsValid(object value)
		{
			if (value is float num && !float.IsNaN(num) && !float.IsInfinity(num) && num >= 0f)
			{
				return num <= _maximum;
			}
			return false;
		}

		public override string ToDescriptionString()
		{
			return "# Acceptable range: finite values from 0 to " + Format(_maximum) + " inclusive; out-of-range values are clamped; NaN and infinities use " + Format(_defaultValue) + ".";
		}

		private static string Format(float value)
		{
			return value.ToString("R", CultureInfo.InvariantCulture);
		}
	}

	internal const float BasePaddingDefault = 0f;

	internal const float BasePaddingMaximum = 100f;

	internal const float MovingThresholdDefault = 1.5f;

	internal const float MovingThresholdMaximum = 20f;

	internal static ConfigEntry<float> BindBasePadding(ConfigFile config, Action<string> warn)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		return config.Bind<float>("Ghost", "BasePadding", 0f, new ConfigDescription("Metres added to every crafting station's build range when deciding whether the player is at a base. Inside that range the placement ghost keeps its vanilla world heading; outside it the ghost turns with the camera. Range: 0–100 inclusive. Finite values outside the range are clamped; NaN and infinities use the default 0. Corrections are logged.", (AcceptableValueBase)(object)new FiniteRange("Ghost.BasePadding", 100f, 0f, warn), Array.Empty<object>()));
	}

	internal static ConfigEntry<float> BindMovingThreshold(ConfigFile config, Action<string> warn)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		return config.Bind<float>("Ghost", "MovingThreshold", 1.5f, new ConfigDescription("Horizontal speed in metres per second above which HideWhenMoving hides the ghost. The default sits just above walking pace. Range: 0–20 inclusive. Finite values outside the range are clamped; NaN and infinities use the default 1.5. Corrections are logged.", (AcceptableValueBase)(object)new FiniteRange("Ghost.MovingThreshold", 20f, 1.5f, warn), Array.Empty<object>()));
	}

	internal static float Normalize(float value, float maximum, float defaultValue)
	{
		if (float.IsNaN(value) || float.IsInfinity(value))
		{
			return defaultValue;
		}
		return Math.Max(0f, Math.Min(maximum, value));
	}
}
[BepInPlugin("com.omgbrews.deadreckoning", "DeadReckoning", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class DeadReckoningPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.omgbrews.deadreckoning";

	public const string PluginName = "DeadReckoning";

	public const string PluginVersion = "0.1.0";

	internal static ManualLogSource Log;

	private static bool? _lastActive;

	internal static ConfigEntry<float> BasePadding;

	internal static ConfigEntry<bool> HideOnShip;

	internal static ConfigEntry<bool> HideWhenMoving;

	internal static ConfigEntry<float> MovingThreshold;

	internal static ConfigEntry<bool> ReplayAtDungeonExit;

	internal const float CameraTurnSeconds = 3.5f;

	private Harmony _harmony;

	internal static bool Active
	{
		get
		{
			bool noMap = Game.m_noMap;
			if (_lastActive != noMap)
			{
				_lastActive = noMap;
				Log.LogInfo((object)(noMap ? "[DeadReckoning] active: no map" : "[DeadReckoning] inactive: map enabled"));
			}
			return noMap;
		}
	}

	private void Awake()
	{
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		BasePadding = NumericSettings.BindBasePadding(((BaseUnityPlugin)this).Config, delegate(string message)
		{
			Log.LogWarning((object)message);
		});
		HideOnShip = ((BaseUnityPlugin)this).Config.Bind<bool>("Ghost", "HideOnShip", true, "Hide the placement ghost and marker while aboard a ship away from a base, so a raised hammer shows nothing that could serve as a heading reference at sea.");
		HideWhenMoving = ((BaseUnityPlugin)this).Config.Bind<bool>("Ghost", "HideWhenMoving", false, "Also hide the ghost and marker on foot away from a base while moving faster than MovingThreshold.");
		MovingThreshold = NumericSettings.BindMovingThreshold(((BaseUnityPlugin)this).Config, delegate(string message)
		{
			Log.LogWarning((object)message);
		});
		ReplayAtDungeonExit = ((BaseUnityPlugin)this).Config.Bind<bool>("Vegvisir", "ReplayAtDungeonExit", true, "After touching a vegvisir inside a dungeon, leave through the door facing the boss location it revealed. The bearing is saved with the character until it is used or the character dies. Disabling stops recording and replay and deletes the saved bearing on each character loaded while disabled, even in map-enabled worlds. Characters not loaded while disabled keep their bearing. Quit the game, edit in r2modman, and restart to change this setting.");
		_harmony = new Harmony("com.omgbrews.deadreckoning");
		_harmony.PatchAll(typeof(GhostPatches));
		_harmony.PatchAll(typeof(VegvisirPatches));
		GizmoBridge.Initialize(_harmony);
		Log.LogInfo((object)("[DeadReckoning] DeadReckoning 0.1.0 loaded" + (GizmoBridge.Present ? " (Gizmo detected)" : "")));
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
[HarmonyPatch]
internal static class VegvisirPatches
{
	internal const string RecordKey = "deadreckoning.vegvisir";

	private static readonly FieldRef<Player, bool> Teleporting = AccessTools.FieldRefAccess<Player, bool>("m_teleporting");

	private static readonly FieldRef<Player, bool> DistantTeleport = AccessTools.FieldRefAccess<Player, bool>("m_distantTeleport");

	private static readonly FieldRef<Player, Vector3> TeleportFromPos = AccessTools.FieldRefAccess<Player, Vector3>("m_teleportFromPos");

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Game), "RPC_DiscoverLocationResponse")]
	private static void RPC_DiscoverLocationResponse(Vector3 pos)
	{
		//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_0047: 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_007b: 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_0092: Unknown result type (might be due to invalid IL or missing references)
		if (DeadReckoningPlugin.ReplayAtDungeonExit.Value && DeadReckoningPlugin.Active)
		{
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && ((Character)localPlayer).InInterior())
			{
				long worldUID = ZNet.instance.GetWorldUID();
				Vector2s zone = ZoneSystem.GetZone(((Component)localPlayer).transform.position);
				localPlayer.m_customData["deadreckoning.vegvisir"] = Serialize(worldUID, zone, pos);
				DeadReckoningPlugin.Log.LogInfo((object)$"[DeadReckoning] vegvisir bearing recorded: world {worldUID}, zone {ZoneLabel(zone)}, target ({pos.x:F0}, {pos.z:F0})");
			}
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(PlayerProfile), "LoadPlayerData", new Type[] { typeof(Player) })]
	private static void LoadPlayerDataPostfix(Player player)
	{
		if (!DeadReckoningPlugin.ReplayAtDungeonExit.Value && Object.op_Implicit((Object)(object)player) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && player.m_customData.Remove("deadreckoning.vegvisir"))
		{
			DeadReckoningPlugin.Log.LogInfo((object)"[DeadReckoning] vegvisir bearing cleared on character load: ReplayAtDungeonExit disabled");
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	private static void UpdateTeleportPrefix(Player __instance, out bool __state)
	{
		__state = Teleporting.Invoke(__instance);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	private static void UpdateTeleportPostfix(Player __instance, bool __state)
	{
		//IL_004a: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00a5: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: 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_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: 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_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		if (!DeadReckoningPlugin.Active || !__state || Teleporting.Invoke(__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !DeadReckoningPlugin.ReplayAtDungeonExit.Value || DistantTeleport.Invoke(__instance))
		{
			return;
		}
		Vector3 position = ((Component)__instance).transform.position;
		if (!Character.InInterior(TeleportFromPos.Invoke(__instance)) || Character.InInterior(position) || !__instance.m_customData.TryGetValue("deadreckoning.vegvisir", out var value))
		{
			return;
		}
		long worldUID = ZNet.instance.GetWorldUID();
		Vector2s zone = ZoneSystem.GetZone(TeleportFromPos.Invoke(__instance));
		if (!TryParse(value, out var world, out var zone2, out var v) || world != worldUID || zone2 != zone)
		{
			__instance.m_customData.Remove("deadreckoning.vegvisir");
			DeadReckoningPlugin.Log.LogInfo((object)$"[DeadReckoning] vegvisir bearing cleared: recorded {RecordLabel(value)}, exiting world {worldUID}, zone {ZoneLabel(zone)} (missing or mismatched dungeon identity)");
			return;
		}
		Vector3 val = v - position;
		val.y = 0f;
		if (((Vector3)(ref val)).sqrMagnitude < 0.01f || float.IsNaN(((Vector3)(ref val)).sqrMagnitude))
		{
			__instance.m_customData.Remove("deadreckoning.vegvisir");
			DeadReckoningPlugin.Log.LogInfo((object)$"[DeadReckoning] vegvisir bearing cleared: recorded world {world}, zone {ZoneLabel(zone2)}, exiting world {worldUID}, zone {ZoneLabel(zone)} (no valid bearing)");
			return;
		}
		((Character)__instance).SetLookDir(val, 3.5f);
		__instance.m_customData.Remove("deadreckoning.vegvisir");
		float num = Mathf.Repeat(Mathf.Atan2(val.x, val.z) * 57.29578f, 360f);
		DeadReckoningPlugin.Log.LogInfo((object)($"[DeadReckoning] vegvisir bearing replayed: recorded world {world}, zone {ZoneLabel(zone2)}, exiting world {worldUID}, zone {ZoneLabel(zone)}, from ({position.x:F0}, {position.z:F0}) " + $"toward ({v.x:F0}, {v.z:F0}), heading {num:F0} deg from north, sweep {3.5f:F1} s"));
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "OnDeath")]
	private static void OnDeathPostfix(Player __instance)
	{
		//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_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		if (DeadReckoningPlugin.Active && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && __instance.m_customData.TryGetValue("deadreckoning.vegvisir", out var value))
		{
			__instance.m_customData.Remove("deadreckoning.vegvisir");
			long worldUID = ZNet.instance.GetWorldUID();
			Vector2s zone = ZoneSystem.GetZone(((Component)__instance).transform.position);
			DeadReckoningPlugin.Log.LogInfo((object)$"[DeadReckoning] vegvisir bearing cleared on death: recorded {RecordLabel(value)}, current world {worldUID}, zone {ZoneLabel(zone)}");
		}
	}

	private static string Serialize(long world, Vector2s zone, Vector3 v)
	{
		CultureInfo invariantCulture = CultureInfo.InvariantCulture;
		return "2;" + world.ToString(invariantCulture) + ";" + zone.x.ToString(invariantCulture) + "," + zone.y.ToString(invariantCulture) + ";" + v.x.ToString("R", invariantCulture) + "," + v.y.ToString("R", invariantCulture) + "," + v.z.ToString("R", invariantCulture);
	}

	private static bool TryParse(string s, out long world, out Vector2s zone, out Vector3 v)
	{
		//IL_0005: 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)
		world = 0L;
		zone = default(Vector2s);
		v = Vector3.zero;
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		string[] array = s.Split(new char[1] { ';' });
		if (array.Length != 4 || array[0] != "2")
		{
			return false;
		}
		string[] array2 = array[2].Split(new char[1] { ',' });
		string[] array3 = array[3].Split(new char[1] { ',' });
		if (array2.Length != 2 || array3.Length != 3)
		{
			return false;
		}
		CultureInfo invariantCulture = CultureInfo.InvariantCulture;
		if (!long.TryParse(array[1], NumberStyles.Integer, invariantCulture, out world))
		{
			return false;
		}
		if (!short.TryParse(array2[0], NumberStyles.Integer, invariantCulture, out zone.x))
		{
			return false;
		}
		if (!short.TryParse(array2[1], NumberStyles.Integer, invariantCulture, out zone.y))
		{
			return false;
		}
		if (!float.TryParse(array3[0], NumberStyles.Float, invariantCulture, out v.x))
		{
			return false;
		}
		if (!float.TryParse(array3[1], NumberStyles.Float, invariantCulture, out v.y))
		{
			return false;
		}
		if (!float.TryParse(array3[2], NumberStyles.Float, invariantCulture, out v.z))
		{
			return false;
		}
		if (float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z) || float.IsInfinity(v.x) || float.IsInfinity(v.y) || float.IsInfinity(v.z))
		{
			return false;
		}
		return true;
	}

	private static string ZoneLabel(Vector2s zone)
	{
		//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 $"({zone.x}, {zone.y})";
	}

	private static string RecordLabel(string record)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		if (!TryParse(record, out var world, out var zone, out var _))
		{
			return "world unknown, zone unknown (legacy or invalid record)";
		}
		return $"world {world}, zone {ZoneLabel(zone)}";
	}
}
internal static class ModBuildVersion
{
	internal const string Value = "0.1.0";
}