Decompiled source of FearNoSpear v1.1.3

FearNoSpear.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("FearNoSpear")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("FearNoSpear")]
[assembly: AssemblyCopyright("Copyright 2026 sighsorry")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.3")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 FearNoSpear
{
	[BepInPlugin("sighsorry.FearNoSpear", "FearNoSpear", "1.1.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class FearNoSpearPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		public enum IndicatorStyle
		{
			BeamAndHud,
			Beam,
			Hud,
			Off
		}

		public const string Author = "sighsorry";

		public const string ModName = "FearNoSpear";

		public const string PluginGuid = "sighsorry.FearNoSpear";

		public const string PluginName = "FearNoSpear";

		public const string ModVersion = "1.1.3";

		public const string PluginVersion = "1.1.3";

		internal static ManualLogSource Log = null;

		internal static FearNoSpearConfig Cfg = null;

		private static readonly ConfigSync Sync = new ConfigSync("sighsorry.FearNoSpear")
		{
			DisplayName = "FearNoSpear",
			CurrentVersion = "1.1.3",
			MinimumRequiredVersion = "1.1.3"
		};

		private static ConfigEntry<Toggle> _serverConfigLocked = null;

		private Harmony? _harmony;

		private FileSystemWatcher? _watcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private const long ReloadDelayTicks = 10000000L;

		internal static bool IsShuttingDown { get; private set; }

		internal static bool HasSyncedAdminPermission
		{
			get
			{
				if (Sync.InitialSyncDone && !Sync.IsSourceOfTruth)
				{
					return Sync.IsAdmin;
				}
				return false;
			}
		}

		private static string ConfigFileName => "sighsorry.FearNoSpear.cfg";

		private static string ConfigFileFullPath => Path.Combine(Paths.ConfigPath, ConfigFileName);

		private void Awake()
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_serverConfigLocked = BindConfig("General", "Lock Configuration", Toggle.On, "Locks OwnerOnlyTombstones to the server's config when the mod is installed on a server. Personal indicator and death-pin settings are not locked. Spear rescue and thrower-only auto-pickup use fixed rules and have no config switches.");
			Sync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
			Cfg = new FearNoSpearConfig(this);
			ReflectionCache.Initialize(((BaseUnityPlugin)this).Logger);
			_harmony = new Harmony("sighsorry.FearNoSpear");
			_harmony.PatchAll();
			SetupWatcher();
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FearNoSpear 1.1.3 loaded");
		}

		private void OnDestroy()
		{
			IsShuttingDown = true;
			SpearIndicator.Clear();
			SpearLocator.Clear();
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnApplicationQuit()
		{
			IsShuttingDown = true;
		}

		private void LateUpdate()
		{
			SpearIndicator.Update();
		}

		internal ConfigEntry<T> BindConfig<T>(string group, string name, T value, string description, bool synchronizedSetting = true, AcceptableValueBase? acceptableValues = null)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			string text = (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]");
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, new ConfigDescription(description + text, acceptableValues, Array.Empty<object>()));
			Sync.AddConfigEntry<T>(val).SynchronizedConfig = synchronizedSetting;
			return val;
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName)
			{
				IncludeSubdirectories = true,
				SynchronizingObject = ThreadingHelper.SynchronizingObject,
				EnableRaisingEvents = true
			};
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					Log.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					Log.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true);
					Log.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
			_lastConfigReloadTime = now;
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
		}
	}
	internal sealed class FearNoSpearConfig
	{
		internal const float MinimumInitialTtlSeconds = 60f;

		internal const int MaxLocationResults = 5;

		internal const int MaxTombstoneResults = 5;

		internal const int MaxIndicatorResults = 10;

		internal readonly ConfigEntry<bool> CleanDeathPins;

		internal readonly ConfigEntry<bool> OwnerOnlyTombstones;

		internal readonly ConfigEntry<FearNoSpearPlugin.IndicatorStyle> SpearIndicatorStyle;

		internal readonly ConfigEntry<FearNoSpearPlugin.IndicatorStyle> TombstoneIndicatorStyle;

		internal readonly ConfigEntry<int> MaxDisplayedSpears;

		internal readonly ConfigEntry<int> MaxDisplayedTombstones;

		internal FearNoSpearConfig(FearNoSpearPlugin plugin)
		{
			SpearIndicatorStyle = plugin.BindConfig("General", "SpearIndicatorStyle", FearNoSpearPlugin.IndicatorStyle.BeamAndHud, "Display your thrown spears and supported SecondaryAttacks copied throws, including ImpactBurst. BeamAndHud (default): a 500-meter column plus an item icon and distance. Beam: column only, subject to render distance and terrain. Hud: icon, distance, and screen-edge directions, visible through terrain and beyond world rendering distance. Off: stops weapon indicators and weapon queries, not protection or tombstone indicators. No real lights or forced zone loading are used. This is a personal setting.", synchronizedSetting: false);
			MaxDisplayedSpears = plugin.BindConfig("General", "MaxDisplayedSpears", 1, "Maximum number of your landed or rescued thrown weapons to display, nearest first. Includes supported SecondaryAttacks copied throws. Range: 1 to 5; default: 1. Nearby drops are followed as they move; distant server positions refresh every 5 seconds. Weapons and tombstones share one request. Use SpearIndicatorStyle = Off to disable weapon indicators. This is a personal setting.", synchronizedSetting: false, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 5));
			TombstoneIndicatorStyle = plugin.BindConfig("General", "TombstoneIndicatorStyle", FearNoSpearPlugin.IndicatorStyle.BeamAndHud, "Shows your newest remaining tombstones in this world, up to MaxDisplayedTombstones. BeamAndHud (default), Beam, Hud, or Off. HUD markers use the death icon, distance on the right, and elapsed in-game days/hours below the icon, such as 2d 12h. Age uses the world's clock and day length, not real calendar time; sleeping advances it. Nearby positions follow the tombstone; distant positions refresh from the server every 5 seconds. Fully recovered tombstones disappear from the display. Independent of CleanDeathPins and map pins. This is a personal setting.", synchronizedSetting: false);
			MaxDisplayedTombstones = plugin.BindConfig("General", "MaxDisplayedTombstones", 1, "Maximum number of your remaining tombstones to display, newest first rather than nearest first. Range: 1 to 5; default: 1. Independent of MaxDisplayedSpears. Changing this count does not add server requests. Use TombstoneIndicatorStyle = Off to hide tombstone indicators. This is a personal setting.", synchronizedSetting: false, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 5));
			CleanDeathPins = plugin.BindConfig("General", "CleanDeathPins", value: true, "Removes your vanilla death map pin when your tombstone is recovered, and removes the pin after a short grace period when a death creates no tombstone. Affects your own map only. This personal preference is not synchronized or locked by the server.", synchronizedSetting: false);
			OwnerOnlyTombstones = plugin.BindConfig("General", "OwnerOnlyTombstones", value: true, "Prevents players from opening or auto-looting a tombstone owned by another player. A server administrator or the local host can bypass this mod's owner restriction while debug mode is active. On a dedicated server, FearNoSpear must be installed on the server and ServerSync must finish confirming administrator access. This does not enable debug mode or override other mods' locks, inventory capacity, or containers already in use. When the optional Clan mod is installed, members of the local player's active Clan roster, including Guests, may also recover it. Tombstones without a valid owner ID remain accessible. This setting is synchronized and enabled by default.");
		}
	}
	internal static class ReflectionCache
	{
		internal static FieldInfo? F_ttl;

		internal static FieldInfo? F_vel;

		internal static FieldInfo? F_nview;

		internal static FieldInfo? F_didHit;

		internal static FieldInfo? F_weapon;

		internal static FieldInfo? F_spawnItem;

		internal static FieldInfo? F_respawnItemOnHit;

		internal static FieldInfo? F_groundHitOnly;

		internal static FieldInfo? F_projectileOwner;

		internal static FieldInfo? F_characterNView;

		internal static FieldInfo? F_tombstoneContainer;

		internal static MethodInfo? M_spawnOnHit;

		internal static MethodInfo? M_tombstoneGetOwner;

		internal static FieldInfo? F_minimapPins;

		internal static void Initialize(ManualLogSource log)
		{
			F_ttl = AccessTools.Field(typeof(Projectile), "m_ttl");
			F_vel = AccessTools.Field(typeof(Projectile), "m_vel");
			F_nview = AccessTools.Field(typeof(Projectile), "m_nview");
			F_didHit = AccessTools.Field(typeof(Projectile), "m_didHit");
			F_weapon = AccessTools.Field(typeof(Projectile), "m_weapon");
			F_spawnItem = AccessTools.Field(typeof(Projectile), "m_spawnItem");
			F_respawnItemOnHit = AccessTools.Field(typeof(Projectile), "m_respawnItemOnHit");
			F_groundHitOnly = AccessTools.Field(typeof(Projectile), "m_groundHitOnly");
			F_projectileOwner = AccessTools.Field(typeof(Projectile), "m_owner");
			F_characterNView = AccessTools.Field(typeof(Character), "m_nview");
			F_tombstoneContainer = AccessTools.Field(typeof(TombStone), "m_container");
			M_spawnOnHit = AccessTools.Method(typeof(Projectile), "SpawnOnHit", new Type[3]
			{
				typeof(GameObject),
				typeof(Collider),
				typeof(Vector3)
			}, (Type[])null);
			M_tombstoneGetOwner = AccessTools.Method(typeof(TombStone), "GetOwner", (Type[])null, (Type[])null);
			F_minimapPins = AccessTools.Field(typeof(Minimap), "m_pins");
			WarnMissing(log, "F_ttl", F_ttl);
			WarnMissing(log, "F_vel", F_vel);
			WarnMissing(log, "F_nview", F_nview);
			WarnMissing(log, "F_didHit", F_didHit);
			WarnMissing(log, "F_weapon", F_weapon);
			WarnMissing(log, "F_spawnItem", F_spawnItem);
			WarnMissing(log, "F_respawnItemOnHit", F_respawnItemOnHit);
			WarnMissing(log, "F_groundHitOnly", F_groundHitOnly);
			WarnMissing(log, "F_projectileOwner", F_projectileOwner);
			WarnMissing(log, "F_characterNView", F_characterNView);
			WarnMissing(log, "F_tombstoneContainer", F_tombstoneContainer);
			WarnMissing(log, "M_spawnOnHit", M_spawnOnHit);
			WarnMissing(log, "M_tombstoneGetOwner", M_tombstoneGetOwner);
			WarnMissing(log, "F_minimapPins", F_minimapPins);
		}

		private static void WarnMissing(ManualLogSource log, string label, MemberInfo? member)
		{
			if (member == null)
			{
				log.LogWarning((object)("Reflection member not found: " + label));
			}
		}

		internal static T Get<T>(FieldInfo? field, object target, T fallback)
		{
			if (field == null || target == null)
			{
				return fallback;
			}
			try
			{
				return (T)((field.GetValue(target) is T val) ? ((object)val) : ((object)fallback));
			}
			catch
			{
				return fallback;
			}
		}

		internal static void Set<T>(FieldInfo? field, object target, T value)
		{
			if (field == null || target == null)
			{
				return;
			}
			try
			{
				field.SetValue(target, value);
			}
			catch
			{
			}
		}

		internal static ZNetView? GetNView(Projectile projectile)
		{
			return Get<ZNetView>(F_nview, projectile, null);
		}

		internal static ZNetView? GetNView(Character character)
		{
			return Get<ZNetView>(F_characterNView, character, null);
		}

		internal static Character? GetProjectileOwner(Projectile projectile)
		{
			return Get<Character>(F_projectileOwner, projectile, null);
		}

		internal static Container? GetTombstoneContainer(TombStone tombstone)
		{
			return Get<Container>(F_tombstoneContainer, tombstone, null);
		}

		internal static long GetTombstoneOwner(TombStone tombstone)
		{
			if (M_tombstoneGetOwner == null || (Object)(object)tombstone == (Object)null)
			{
				return 0L;
			}
			try
			{
				return (M_tombstoneGetOwner.Invoke(tombstone, null) is long num) ? num : 0;
			}
			catch
			{
				return 0L;
			}
		}
	}
	internal static class SpearProjectileDetector
	{
		internal static bool IsRecoverableProjectile(Projectile projectile)
		{
			if ((Object)(object)projectile == (Object)null)
			{
				return false;
			}
			if (!ReflectionCache.Get(ReflectionCache.F_respawnItemOnHit, projectile, fallback: false))
			{
				return false;
			}
			return ReflectionCache.Get<ItemData>(ReflectionCache.F_spawnItem, projectile, null) != null;
		}

		internal static bool IsTrackedSpearProjectile(Projectile projectile)
		{
			if (!IsRecoverableProjectile(projectile))
			{
				return false;
			}
			ItemData item = ReflectionCache.Get<ItemData>(ReflectionCache.F_spawnItem, projectile, null);
			ItemData item2 = ReflectionCache.Get<ItemData>(ReflectionCache.F_weapon, projectile, null);
			if (IsSpearItem(item) || IsSpearItem(item2))
			{
				return true;
			}
			ZNetView nView = ReflectionCache.GetNView(projectile);
			if ((Object)(object)nView != (Object)null && nView.IsValid() && nView.GetZDO().GetBool("SecondaryAttacks_CopiedThrowProjectile", false))
			{
				return true;
			}
			return ContainsSpearToken(((Object)projectile).name ?? string.Empty);
		}

		internal static bool IsSpearItem(ItemData? item)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if (item?.m_shared == null)
			{
				return false;
			}
			if ((int)item.m_shared.m_skillType == 5)
			{
				return true;
			}
			return ContainsSpearToken(item.m_shared.m_name);
		}

		private static bool ContainsSpearToken(string? value)
		{
			if (value == null || value.Length == 0)
			{
				return false;
			}
			return value.IndexOf("spear", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		internal static string DescribeProjectile(Projectile projectile)
		{
			ItemData obj = ReflectionCache.Get<ItemData>(ReflectionCache.F_spawnItem, projectile, null);
			ItemData val = ReflectionCache.Get<ItemData>(ReflectionCache.F_weapon, projectile, null);
			string text = obj?.m_shared?.m_name ?? "<null>";
			string text2 = val?.m_shared?.m_name ?? "<null>";
			string text3 = ((object)Unsafe.As<SkillType, SkillType>(ref obj?.m_shared?.m_skillType)/*cast due to .constrained prefix*/).ToString() ?? ((object)Unsafe.As<SkillType, SkillType>(ref val?.m_shared?.m_skillType)/*cast due to .constrained prefix*/).ToString() ?? "<unknown>";
			return "projectile=" + ((Object)projectile).name + ", spawnItem=" + text + ", weapon=" + text2 + ", skill=" + text3;
		}
	}
	[HarmonyPatch(typeof(Projectile), "Setup")]
	internal static class ProjectileSetupPatch
	{
		[HarmonyAfter(new string[] { "sighsorry.SecondaryAttacks" })]
		private static void Postfix(Projectile __instance)
		{
			SpearSafetyTracker.GetOrArmIfTracked(__instance);
		}
	}
	[HarmonyPatch(typeof(Projectile), "FixedUpdate")]
	internal static class ProjectileFixedUpdatePatch
	{
		private static bool Prefix(Projectile __instance)
		{
			SpearSafetyTracker spearSafetyTracker = ((Component)__instance).GetComponent<SpearSafetyTracker>() ?? SpearSafetyTracker.GetOrArmIfTracked(__instance);
			if ((Object)(object)spearSafetyTracker == (Object)null)
			{
				return true;
			}
			return !spearSafetyTracker.TryTtlRescueAndDestroyIfNeeded();
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Pickup", new Type[]
	{
		typeof(GameObject),
		typeof(bool),
		typeof(bool)
	})]
	internal static class HumanoidPickupPatch
	{
		private static void Prefix(Humanoid __instance, GameObject go, out string? __state)
		{
			__state = null;
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)go == (Object)null))
			{
				ItemDrop component = go.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null) && component.m_itemData != null && SpearThrowerMetadata.ReadFromDrop(component) != 0L)
				{
					__state = SpearItemIdentity.BuildDropRecordKey(component);
				}
			}
		}

		private static void Postfix(bool __result, string? __state)
		{
			if (__result && __state != null && __state.Length != 0)
			{
				SpearLocator.ForgetRecoveredTarget(__state);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		private static void Postfix()
		{
			SpearLocator.Clear();
			SpearIndicator.Clear();
			DeathPinCleaner.Clear();
			SpearNetwork.ClearSession();
			SpearNetwork.RegisterRpcs();
		}
	}
	[HarmonyPatch(typeof(Game), "Update")]
	internal static class GameUpdatePatch
	{
		private static void Postfix()
		{
			DeathPinCleaner.UpdatePendingDeath();
			SpearLocator.Update();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Start")]
	internal static class ZNetStartPatch
	{
		private static void Postfix()
		{
			SpearNetwork.RegisterRpcs();
		}
	}
	[HarmonyPatch]
	internal static class ProjectileOnHitPatch
	{
		private static bool Prepare()
		{
			return AccessTools.Method(typeof(Projectile), "OnHit", (Type[])null, (Type[])null) != null;
		}

		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method(typeof(Projectile), "OnHit", (Type[])null, (Type[])null);
		}

		private static void Postfix(Projectile __instance)
		{
			if (ReflectionCache.Get(ReflectionCache.F_didHit, __instance, fallback: false))
			{
				((Component)__instance).GetComponent<SpearSafetyTracker>()?.MarkNormalHit();
			}
		}
	}
	[HarmonyPatch]
	internal static class ZNetSceneDestroyPatch
	{
		private static bool Prepare()
		{
			return AccessTools.Method(typeof(ZNetScene), "Destroy", new Type[1] { typeof(GameObject) }, (Type[])null) != null;
		}

		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method(typeof(ZNetScene), "Destroy", new Type[1] { typeof(GameObject) }, (Type[])null);
		}

		private static void Prefix(GameObject __0)
		{
			if ((Object)(object)__0 == (Object)null)
			{
				return;
			}
			SpearSafetyTracker spearSafetyTracker = __0.GetComponent<SpearSafetyTracker>();
			if ((Object)(object)spearSafetyTracker == (Object)null)
			{
				Projectile component = __0.GetComponent<Projectile>();
				if ((Object)(object)component != (Object)null)
				{
					spearSafetyTracker = SpearSafetyTracker.GetOrArmIfTracked(component);
				}
			}
			spearSafetyTracker?.TryRescue("ZNetScene.Destroy before hit");
		}
	}
	internal static class DeathPinCleaner
	{
		private sealed class PendingDeath
		{
			internal long PlayerId;

			internal Vector3 Position;

			internal float StartedAt;
		}

		private const float RecentDeathSeconds = 8f;

		private const float DeathPinRemoveRadius = 32f;

		private static readonly Dictionary<int, Vector3> TombstoneDeathPositions = new Dictionary<int, Vector3>();

		private static readonly HashSet<int> CleanedTombstones = new HashSet<int>();

		private static PendingDeath? _pendingDeath;

		internal static void Clear()
		{
			TombstoneDeathPositions.Clear();
			CleanedTombstones.Clear();
			_pendingDeath = null;
		}

		internal static void BeginLocalDeath(Player player)
		{
			//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)
			if (IsEnabled() && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				long localPlayerId = GetLocalPlayerId();
				if (localPlayerId != 0L)
				{
					_pendingDeath = new PendingDeath
					{
						PlayerId = localPlayerId,
						Position = ((Component)player).transform.position,
						StartedAt = Time.time
					};
				}
			}
		}

		internal static void UpdatePendingDeath()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			PendingDeath pendingDeath = _pendingDeath;
			if (pendingDeath != null)
			{
				if (!IsEnabled())
				{
					_pendingDeath = null;
				}
				else if (!(Time.time - pendingDeath.StartedAt < 8f))
				{
					_pendingDeath = null;
					RemoveNearestDeathPin(pendingDeath.Position);
				}
			}
		}

		internal static void RegisterTombstone(TombStone tombstone)
		{
			//IL_002d: 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_0083: 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_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_0071: 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)
			if (!IsEnabled() || (Object)(object)tombstone == (Object)null)
			{
				return;
			}
			long tombstoneOwner = ReflectionCache.GetTombstoneOwner(tombstone);
			if (tombstoneOwner != 0L && tombstoneOwner == GetLocalPlayerId())
			{
				int instanceID = ((Object)tombstone).GetInstanceID();
				Vector3 val = GetTombstonePinPosition(tombstone);
				PendingDeath pendingDeath = _pendingDeath;
				if (pendingDeath != null && pendingDeath.PlayerId == tombstoneOwner && Time.time - pendingDeath.StartedAt <= 8f && Vector3.SqrMagnitude(val - pendingDeath.Position) <= 1024f)
				{
					val = pendingDeath.Position;
					_pendingDeath = null;
				}
				TombstoneDeathPositions[instanceID] = val;
			}
		}

		internal static void CleanForRecoveredTombstone(TombStone tombstone)
		{
			//IL_003d: 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)
			if (IsEnabled() && IsLocalTombstone(tombstone))
			{
				int instanceID = ((Object)tombstone).GetInstanceID();
				if (CleanedTombstones.Add(instanceID))
				{
					Vector3 value;
					Vector3 position = (TombstoneDeathPositions.TryGetValue(instanceID, out value) ? value : GetTombstonePinPosition(tombstone));
					TombstoneDeathPositions.Remove(instanceID);
					RemoveNearestDeathPin(position);
				}
			}
		}

		internal static void CleanIfTombstoneIsEmpty(TombStone tombstone)
		{
			if (IsEnabled() && IsLocalTombstone(tombstone) && IsTombstoneEmpty(tombstone))
			{
				CleanForRecoveredTombstone(tombstone);
			}
		}

		private static bool IsEnabled()
		{
			return FearNoSpearPlugin.Cfg.CleanDeathPins.Value;
		}

		private static bool IsLocalTombstone(TombStone tombstone)
		{
			if ((Object)(object)tombstone == (Object)null)
			{
				return false;
			}
			long tombstoneOwner = ReflectionCache.GetTombstoneOwner(tombstone);
			long localPlayerId = GetLocalPlayerId();
			if (tombstoneOwner != 0L)
			{
				return tombstoneOwner == localPlayerId;
			}
			return false;
		}

		private static long GetLocalPlayerId()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return localPlayer.GetPlayerID();
			}
			Game instance = Game.instance;
			PlayerProfile val = (((Object)(object)instance != (Object)null) ? instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return 0L;
			}
			return val.GetPlayerID();
		}

		private static bool IsTombstoneEmpty(TombStone tombstone)
		{
			Container tombstoneContainer = ReflectionCache.GetTombstoneContainer(tombstone);
			Inventory val = (((Object)(object)tombstoneContainer != (Object)null) ? tombstoneContainer.GetInventory() : null);
			if (val != null)
			{
				return val.NrOfItems() <= 0;
			}
			return false;
		}

		private static Vector3 GetTombstonePinPosition(TombStone tombstone)
		{
			//IL_0047: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)tombstone).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				ZDO zDO = component.GetZDO();
				if (zDO != null && zDO.IsValid())
				{
					return zDO.GetVec3(ZDOVars.s_spawnPoint, ((Component)tombstone).transform.position);
				}
			}
			return ((Component)tombstone).transform.position;
		}

		private static bool RemoveNearestDeathPin(Vector3 position)
		{
			//IL_0007: 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)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (!(ReflectionCache.F_minimapPins?.GetValue(instance) is List<PinData> source))
			{
				return false;
			}
			PinData val = (from pin in source
				where pin != null && pin.m_save && (int)pin.m_type == 4
				where Vector3.Distance(pin.m_pos, position) <= 32f
				orderby Vector3.SqrMagnitude(pin.m_pos - position)
				select pin).FirstOrDefault();
			if (val == null)
			{
				return false;
			}
			instance.RemovePin(val);
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerOnDeathDeathPinPatch
	{
		private static void Prefix(Player __instance)
		{
			DeathPinCleaner.BeginLocalDeath(__instance);
		}
	}
	[HarmonyPatch(typeof(TombStone), "Setup", new Type[]
	{
		typeof(string),
		typeof(long)
	})]
	internal static class TombStoneSetupDeathPinPatch
	{
		private static void Postfix(TombStone __instance)
		{
			DeathPinCleaner.RegisterTombstone(__instance);
			SpearLocator.ObserveTombstone(__instance);
		}
	}
	[HarmonyPatch(typeof(TombStone), "Awake")]
	internal static class TombStoneIndicatorAwakePatch
	{
		private static void Postfix(TombStone __instance)
		{
			SpearLocator.ObserveTombstone(__instance);
		}
	}
	[HarmonyPatch(typeof(TombStone), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class TombStoneOwnerOnlyInteractPatch
	{
		private static bool Prefix(TombStone __instance, Humanoid __0, bool __1, ref bool __result)
		{
			if (!FearNoSpearPlugin.Cfg.OwnerOnlyTombstones.Value)
			{
				return true;
			}
			long tombstoneOwner = ReflectionCache.GetTombstoneOwner(__instance);
			if (tombstoneOwner == 0L)
			{
				return true;
			}
			Player val = (Player)(object)((__0 is Player) ? __0 : null);
			if ((Object)(object)val != (Object)null && (val.GetPlayerID() == tombstoneOwner || HasAdminDebugBypass(val) || ClanTombstoneCompatibility.IsMemberOfLocalClan(val, tombstoneOwner)))
			{
				return true;
			}
			if (!__1 && (Object)(object)__0 != (Object)null)
			{
				((Character)__0).Message((MessageType)2, "$msg_cantpickup", 0, (Sprite)null, false);
			}
			__result = false;
			return false;
		}

		private static bool HasAdminDebugBypass(Player player)
		{
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && Player.m_debugMode && (Object)(object)ZNet.instance != (Object)null)
			{
				if (!ZNet.instance.IsServer())
				{
					return FearNoSpearPlugin.HasSyncedAdminPermission;
				}
				return true;
			}
			return false;
		}
	}
	internal static class ClanTombstoneCompatibility
	{
		internal const string PluginGuid = "sighsorry.Clan";

		private const int MinimumApiVersion = 5;

		private static bool _initialized;

		private static bool _available;

		private static PropertyInfo? _currentProperty;

		private static PropertyInfo? _isReadyProperty;

		private static PropertyInfo? _membersProperty;

		private static PropertyInfo? _memberPlayerIdProperty;

		internal static bool IsMemberOfLocalClan(Player player, long ownerId)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ownerId == 0L)
			{
				return false;
			}
			EnsureInitialized();
			if (!_available)
			{
				return false;
			}
			try
			{
				object value = _currentProperty.GetValue(null, null);
				if (value == null)
				{
					goto IL_006e;
				}
				object value2 = _isReadyProperty.GetValue(value, null);
				if (!(value2 is bool) || !(bool)value2 || !(_membersProperty.GetValue(value, null) is IEnumerable enumerable))
				{
					goto IL_006e;
				}
				foreach (object item in enumerable)
				{
					if (item != null && _memberPlayerIdProperty.GetValue(item, null) is long num && num == ownerId)
					{
						return true;
					}
				}
				goto end_IL_002a;
				IL_006e:
				return false;
				end_IL_002a:;
			}
			catch (Exception ex)
			{
				_available = false;
				FearNoSpearPlugin.Log.LogWarning((object)("Clan tombstone compatibility was disabled after an API read failed: " + ex.GetType().Name + ": " + ex.Message));
			}
			return false;
		}

		private static void EnsureInitialized()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			if (!Chainloader.PluginInfos.TryGetValue("sighsorry.Clan", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				return;
			}
			try
			{
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				Type type = assembly.GetType("Clan.ClanApi", throwOnError: false);
				int num = (((type?.GetField("ApiVersion", BindingFlags.Static | BindingFlags.Public))?.GetValue(null) is int num2) ? num2 : 0);
				if (num < 5)
				{
					FearNoSpearPlugin.Log.LogWarning((object)$"Clan tombstone compatibility requires Clan API v{5} or newer; found v{num}.");
					return;
				}
				_currentProperty = type.GetProperty("Current", BindingFlags.Static | BindingFlags.Public);
				Type obj = _currentProperty?.PropertyType;
				_isReadyProperty = obj?.GetProperty("IsReady", BindingFlags.Instance | BindingFlags.Public);
				_membersProperty = obj?.GetProperty("Members", BindingFlags.Instance | BindingFlags.Public);
				PropertyInfo? membersProperty = _membersProperty;
				_memberPlayerIdProperty = (((object)membersProperty != null && membersProperty.PropertyType.IsGenericType) ? _membersProperty.PropertyType.GetGenericArguments().FirstOrDefault() : assembly.GetType("Clan.ClanMemberInfo", throwOnError: false))?.GetProperty("PlayerId", BindingFlags.Instance | BindingFlags.Public);
				_available = _currentProperty != null && _isReadyProperty?.PropertyType == typeof(bool) && _membersProperty != null && _memberPlayerIdProperty?.PropertyType == typeof(long);
				if (!_available)
				{
					FearNoSpearPlugin.Log.LogWarning((object)"Clan tombstone compatibility could not bind the required Clan API members.");
				}
			}
			catch (Exception ex)
			{
				FearNoSpearPlugin.Log.LogWarning((object)("Clan tombstone compatibility could not initialize: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(TombStone), "OnTakeAllSuccess")]
	internal static class TombStoneTakeAllDeathPinPatch
	{
		private static void Postfix(TombStone __instance)
		{
			DeathPinCleaner.CleanForRecoveredTombstone(__instance);
			SpearLocator.HideEmptyTombstone(__instance);
		}
	}
	[HarmonyPatch(typeof(TombStone), "UpdateDespawn")]
	internal static class TombStoneUpdateDespawnDeathPinPatch
	{
		private static void Prefix(TombStone __instance)
		{
			DeathPinCleaner.CleanIfTombstoneIsEmpty(__instance);
			SpearLocator.HideEmptyTombstone(__instance);
		}
	}
	internal static class SpearItemIdentity
	{
		internal static string BuildDropRecordKey(ItemDrop drop)
		{
			string text = TryGetZdoKey(((Component)drop).GetComponent<ZNetView>());
			if (text != null && text.Length > 0)
			{
				return BuildDropRecordKey(text);
			}
			return $"drop-local:{((Object)drop).GetInstanceID()}";
		}

		internal unsafe static string BuildWorldDropRecordKey(ZDOID zdoId)
		{
			return BuildDropRecordKey(((object)(*(ZDOID*)(&zdoId))/*cast due to .constrained prefix*/).ToString());
		}

		internal static string BuildTombstoneRecordKey(ZDOID zdoId)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return $"tomb:{zdoId}";
		}

		internal static string? TryGetZdoKey(ZNetView? nview)
		{
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			if (zDO == null || !zDO.IsValid())
			{
				return null;
			}
			return ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
		}

		private static string BuildDropRecordKey(string zdoKey)
		{
			return "drop:" + zdoKey;
		}
	}
	internal static class SpearLocator
	{
		internal readonly struct Target
		{
			internal readonly string Key;

			internal readonly Vector3 Position;

			internal readonly Sprite? Icon;

			internal readonly LocationKind Kind;

			internal readonly long CreatedTicks;

			internal Target(string key, Vector3 position, Sprite? icon, LocationKind kind = LocationKind.Weapon, long createdTicks = 0L)
			{
				//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)
				Key = key;
				Position = position;
				Icon = icon;
				Kind = kind;
				CreatedTicks = createdTicks;
			}
		}

		private const float PollSeconds = 5f;

		private const float RequestTimeoutSeconds = 2f;

		private static readonly FieldInfo? DropInstances = typeof(ItemDrop).GetField("s_instances", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly Dictionary<string, ItemDrop> LoadedDrops = new Dictionary<string, ItemDrop>();

		private static readonly HashSet<TombStone> ObservedTombstones = new HashSet<TombStone>();

		private static readonly Dictionary<string, TombStone> LoadedTombstones = new Dictionary<string, TombStone>();

		private static readonly List<Target> NewestTombstones = new List<Target>(5);

		private static readonly List<Target> SelectedTargets = new List<Target>(10);

		private static readonly Dictionary<string, float> PickedUpUntil = new Dictionary<string, float>();

		private static readonly List<SpearLocationRecord> ServerRecords = new List<SpearLocationRecord>();

		private static long _playerId;

		private static int _sequence;

		private static int _pendingRequest;

		private static float _deadline;

		private static float _nextPoll;

		private static float _nextLoadedScan;

		private static float _serverRecordsExpire;

		private static bool _weaponsEnabled;

		private static bool _tombstonesEnabled;

		private static bool _seedTombstones = true;

		private static bool _selectionDirty = true;

		private static int _selectedWeaponLimit;

		private static int _selectedTombstoneLimit;

		internal static void Clear()
		{
			LoadedDrops.Clear();
			ObservedTombstones.Clear();
			LoadedTombstones.Clear();
			NewestTombstones.Clear();
			SelectedTargets.Clear();
			_selectionDirty = true;
			_selectedWeaponLimit = (_selectedTombstoneLimit = 0);
			_seedTombstones = true;
			_weaponsEnabled = (_tombstonesEnabled = false);
			PickedUpUntil.Clear();
			ServerRecords.Clear();
			_playerId = 0L;
			_pendingRequest = 0;
			_deadline = (_nextPoll = (_nextLoadedScan = (_serverRecordsExpire = 0f)));
		}

		internal static void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			bool flag = FearNoSpearPlugin.Cfg.SpearIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off;
			bool flag2 = FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off;
			if ((Object)(object)localPlayer == (Object)null || localPlayer.GetPlayerID() == 0L || (!flag && !flag2))
			{
				if (_playerId != 0L)
				{
					Clear();
				}
				return;
			}
			if (_playerId != localPlayer.GetPlayerID())
			{
				Clear();
				_playerId = localPlayer.GetPlayerID();
			}
			if (_weaponsEnabled != flag || _tombstonesEnabled != flag2)
			{
				_weaponsEnabled = flag;
				_tombstonesEnabled = flag2;
				_pendingRequest = 0;
				_nextPoll = (_nextLoadedScan = 0f);
				ServerRecords.Clear();
				ObservedTombstones.Clear();
				_seedTombstones = true;
			}
			float unscaledTime = Time.unscaledTime;
			if (_pendingRequest != 0 && unscaledTime >= _deadline)
			{
				_pendingRequest = 0;
				_nextPoll = unscaledTime + 15f;
			}
			if (ServerRecords.Count > 0 && unscaledTime > _serverRecordsExpire)
			{
				ServerRecords.Clear();
				_selectionDirty = true;
			}
			RefreshLoadedDrops(localPlayer);
			if (_pendingRequest == 0 && unscaledTime >= _nextPoll)
			{
				Request(localPlayer);
			}
		}

		private static void Request(Player player)
		{
			_pendingRequest = ++_sequence;
			if (_pendingRequest == 0)
			{
				_pendingRequest = ++_sequence;
			}
			_deadline = Time.unscaledTime + 2f;
			_nextPoll = Time.unscaledTime + 5f;
			if (!SpearNetwork.RequestServerSpearLocation(player, _pendingRequest, _weaponsEnabled, _tombstonesEnabled))
			{
				_pendingRequest = 0;
				_nextPoll = Time.unscaledTime + 15f;
			}
		}

		internal static void ReceiveServerRecords(int requestId, List<SpearLocationRecord> records)
		{
			if (requestId == _pendingRequest && _pendingRequest != 0)
			{
				_pendingRequest = 0;
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && localPlayer.GetPlayerID() == _playerId && (FearNoSpearPlugin.Cfg.SpearIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off || FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off))
				{
					ServerRecords.Clear();
					ServerRecords.AddRange(records);
					_serverRecordsExpire = Time.unscaledTime + 10f;
					RefreshLoadedDrops(localPlayer, force: true);
				}
			}
		}

		internal static void ForgetRecoveredTarget(string recordKey)
		{
			if (_playerId != 0L && !string.IsNullOrEmpty(recordKey))
			{
				LoadedDrops.Remove(recordKey);
				LoadedTombstones.Remove(recordKey);
				ServerRecords.RemoveAll((SpearLocationRecord record) => record.Key == recordKey);
				PickedUpUntil[recordKey] = Time.unscaledTime + 10f;
				_selectionDirty = true;
			}
		}

		private static void RefreshLoadedDrops(Player player, bool force = false)
		{
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			if (!force && Time.unscaledTime < _nextLoadedScan)
			{
				return;
			}
			_nextLoadedScan = Time.unscaledTime + 0.5f;
			_selectionDirty = true;
			LoadedDrops.Clear();
			LoadedTombstones.Clear();
			foreach (string item2 in (from pair in PickedUpUntil
				where pair.Value <= Time.unscaledTime
				select pair.Key).ToList())
			{
				PickedUpUntil.Remove(item2);
			}
			if (_weaponsEnabled)
			{
				IEnumerable<ItemDrop> enumerable = DropInstances?.GetValue(null) as List<ItemDrop>;
				foreach (ItemDrop item3 in enumerable ?? Object.FindObjectsByType<ItemDrop>((FindObjectsSortMode)0))
				{
					if (!((Object)(object)item3 == (Object)null) && SpearThrowerMetadata.ReadFromDrop(item3) == player.GetPlayerID())
					{
						string text = SpearItemIdentity.BuildDropRecordKey(item3);
						if (!string.IsNullOrEmpty(text) && !PickedUpUntil.ContainsKey(text))
						{
							LoadedDrops[text] = item3;
						}
					}
				}
			}
			if (!_tombstonesEnabled)
			{
				return;
			}
			if (_seedTombstones)
			{
				TombStone[] array = Object.FindObjectsByType<TombStone>((FindObjectsSortMode)0);
				foreach (TombStone item in array)
				{
					ObservedTombstones.Add(item);
				}
				_seedTombstones = false;
			}
			ObservedTombstones.RemoveWhere((TombStone tombstone) => (Object)(object)tombstone == (Object)null);
			foreach (TombStone observedTombstone in ObservedTombstones)
			{
				ZNetView component = ((Component)observedTombstone).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					ZDO zDO = component.GetZDO();
					if (zDO.GetLong(ZDOVars.s_owner, 0L) == player.GetPlayerID())
					{
						LoadedTombstones[SpearItemIdentity.BuildTombstoneRecordKey(zDO.m_uid)] = observedTombstone;
					}
				}
			}
		}

		internal static void ObserveTombstone(TombStone tombstone)
		{
			if (FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				ObservedTombstones.Add(tombstone);
			}
		}

		internal static void HideEmptyTombstone(TombStone tombstone)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (_playerId == 0L || !IsEmpty(tombstone))
			{
				return;
			}
			ZNetView component = ((Component)tombstone).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				ZDO zDO = component.GetZDO();
				if (zDO.GetLong(ZDOVars.s_owner, 0L) == _playerId)
				{
					ForgetRecoveredTarget(SpearItemIdentity.BuildTombstoneRecordKey(zDO.m_uid));
				}
			}
		}

		private static bool IsEmpty(TombStone tombstone)
		{
			Container tombstoneContainer = ReflectionCache.GetTombstoneContainer(tombstone);
			if ((Object)(object)tombstoneContainer != (Object)null)
			{
				return tombstoneContainer.GetInventory().NrOfItems() == 0;
			}
			return false;
		}

		internal static void GetTargets(Player player, List<Target> targets, int limit)
		{
			//IL_006e: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			targets.Clear();
			limit = ((FearNoSpearPlugin.Cfg.SpearIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off) ? Mathf.Clamp(limit, 1, 5) : 0);
			int num = ((FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value != FearNoSpearPlugin.IndicatorStyle.Off) ? Mathf.Clamp(FearNoSpearPlugin.Cfg.MaxDisplayedTombstones.Value, 1, 5) : 0);
			if (_selectionDirty || _selectedWeaponLimit != limit || _selectedTombstoneLimit != num)
			{
				SelectTargets(((Component)player).transform.position, limit, num);
				_selectionDirty = false;
				_selectedWeaponLimit = limit;
				_selectedTombstoneLimit = num;
			}
			foreach (Target selectedTarget in SelectedTargets)
			{
				Vector3 position = selectedTarget.Position;
				TombStone value2;
				if (selectedTarget.Kind == LocationKind.Weapon && LoadedDrops.TryGetValue(selectedTarget.Key, out ItemDrop value))
				{
					if ((Object)(object)value == (Object)null)
					{
						_selectionDirty = true;
						continue;
					}
					position = ((Component)value).transform.position;
				}
				else if (selectedTarget.Kind == LocationKind.Tombstone && LoadedTombstones.TryGetValue(selectedTarget.Key, out value2))
				{
					if ((Object)(object)value2 == (Object)null)
					{
						_selectionDirty = true;
						continue;
					}
					if (IsEmpty(value2))
					{
						ForgetRecoveredTarget(selectedTarget.Key);
						continue;
					}
					position = ((Component)value2).transform.position;
				}
				targets.Add(new Target(selectedTarget.Key, position, selectedTarget.Icon, selectedTarget.Kind, selectedTarget.CreatedTicks));
			}
		}

		private static void SelectTargets(Vector3 origin, int limit, int tombstoneLimit)
		{
			//IL_00d1: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: 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_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			SelectedTargets.Clear();
			NewestTombstones.Clear();
			if (limit > 0)
			{
				foreach (KeyValuePair<string, ItemDrop> loadedDrop in LoadedDrops)
				{
					ItemDrop value = loadedDrop.Value;
					if (!((Object)(object)value == (Object)null))
					{
						AddSelected(SelectedTargets, new Target(loadedDrop.Key, ((Component)value).transform.position, GetIcon(value.m_itemData, value.m_itemData.m_variant), LocationKind.Weapon, 0L), origin, limit, newest: false);
					}
				}
			}
			Sprite icon = ((tombstoneLimit > 0 && (Object)(object)Minimap.instance != (Object)null) ? Minimap.instance.m_icons.Find((SpriteData val2) => (int)val2.m_name == 4).m_icon : null);
			if (tombstoneLimit > 0)
			{
				foreach (KeyValuePair<string, TombStone> loadedTombstone in LoadedTombstones)
				{
					TombStone value2 = loadedTombstone.Value;
					if ((Object)(object)value2 == (Object)null || PickedUpUntil.ContainsKey(loadedTombstone.Key) || IsEmpty(value2))
					{
						continue;
					}
					ZNetView component = ((Component)value2).GetComponent<ZNetView>();
					if ((Object)(object)component == (Object)null || !component.IsValid())
					{
						continue;
					}
					long num = component.GetZDO().GetLong(ZDOVars.s_timeOfDeath, 0L);
					if (num > 0)
					{
						DateTime maxValue = DateTime.MaxValue;
						if (num <= maxValue.Ticks)
						{
							AddSelected(NewestTombstones, new Target(loadedTombstone.Key, ((Component)value2).transform.position, icon, LocationKind.Tombstone, num), origin, tombstoneLimit, newest: true);
						}
					}
				}
			}
			foreach (SpearLocationRecord serverRecord in ServerRecords)
			{
				if (PickedUpUntil.ContainsKey(serverRecord.Key))
				{
					continue;
				}
				if (serverRecord.Kind == LocationKind.Tombstone)
				{
					if (tombstoneLimit != 0 && !LoadedTombstones.ContainsKey(serverRecord.Key))
					{
						AddSelected(NewestTombstones, new Target(serverRecord.Key, serverRecord.Position, icon, serverRecord.Kind, serverRecord.CreatedTicks), origin, tombstoneLimit, newest: true);
					}
				}
				else if (limit != 0 && !LoadedDrops.ContainsKey(serverRecord.Key))
				{
					GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(serverRecord.PrefabHash) : null);
					AddSelected(SelectedTargets, new Target(serverRecord.Key, serverRecord.Position, GetIcon((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<ItemDrop>()?.m_itemData, serverRecord.Variant), LocationKind.Weapon, 0L), origin, limit, newest: false);
				}
			}
			SelectedTargets.AddRange(NewestTombstones);
		}

		private static void AddSelected(List<Target> targets, Target candidate, Vector3 origin, int limit, bool newest)
		{
			//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_0045: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(candidate.Key))
			{
				return;
			}
			for (int i = 0; i < targets.Count; i++)
			{
				if (targets[i].Key == candidate.Key)
				{
					return;
				}
			}
			Vector3 val = candidate.Position - origin;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			int j;
			for (j = 0; j < targets.Count; j++)
			{
				int num;
				if (!newest)
				{
					val = targets[j].Position - origin;
					num = sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
				}
				else
				{
					long createdTicks = targets[j].CreatedTicks;
					num = createdTicks.CompareTo(candidate.CreatedTicks);
				}
				int num2 = num;
				if (num2 < 0 || (num2 == 0 && string.CompareOrdinal(candidate.Key, targets[j].Key) < 0))
				{
					break;
				}
			}
			if (j < limit)
			{
				targets.Insert(j, candidate);
				if (targets.Count > limit)
				{
					targets.RemoveAt(limit);
				}
			}
		}

		private static Sprite? GetIcon(ItemData? item, int variant)
		{
			Sprite[] array = item?.m_shared?.m_icons;
			if (array == null || array.Length == 0)
			{
				return null;
			}
			return array[Mathf.Clamp(variant, 0, array.Length - 1)];
		}
	}
	internal static class SpearIndicator
	{
		private sealed class Marker
		{
			internal string Key = string.Empty;

			internal float NextTextAt;

			internal float NextAgeAt;

			internal Vector3 BeamPosition;

			internal float BeamWidth;

			internal bool BeamPositionSet;

			internal LocationKind? BeamKind;

			internal readonly LineRenderer Beam;

			internal readonly RectTransform Label;

			internal readonly RectTransform Pointer;

			internal readonly Image Icon;

			internal readonly TextMeshProUGUI Distance;

			internal readonly TextMeshProUGUI Age;

			internal Marker(Transform root, Transform canvas, Material material, TMP_FontAsset font)
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Expected O, but got Unknown
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_011c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Expected O, but got Unknown
				//IL_015e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0165: Unknown result type (might be due to invalid IL or missing references)
				//IL_0167: 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_016f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0186: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c8: Expected O, but got Unknown
				//IL_0202: Unknown result type (might be due to invalid IL or missing references)
				//IL_021c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0258: Unknown result type (might be due to invalid IL or missing references)
				//IL_025e: Expected O, but got Unknown
				//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0304: Unknown result type (might be due to invalid IL or missing references)
				//IL_030b: Expected O, but got Unknown
				//IL_035e: Unknown result type (might be due to invalid IL or missing references)
				//IL_037e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0383: Unknown result type (might be due to invalid IL or missing references)
				//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0415: Unknown result type (might be due to invalid IL or missing references)
				//IL_041c: Expected O, but got Unknown
				//IL_049b: Unknown result type (might be due to invalid IL or missing references)
				//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_04f7: Unknown result type (might be due to invalid IL or missing references)
				//IL_0516: Unknown result type (might be due to invalid IL or missing references)
				GameObject val = new GameObject("Spear Beam", new Type[1] { typeof(LineRenderer) });
				val.transform.SetParent(root, false);
				Beam = val.GetComponent<LineRenderer>();
				((Renderer)Beam).sharedMaterial = material;
				((Renderer)Beam).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)Beam).receiveShadows = false;
				((Renderer)Beam).lightProbeUsage = (LightProbeUsage)0;
				((Renderer)Beam).reflectionProbeUsage = (ReflectionProbeUsage)0;
				Beam.useWorldSpace = true;
				Beam.positionCount = 2;
				Beam.alignment = (LineAlignment)0;
				Beam.textureMode = (LineTextureMode)0;
				Beam.numCapVertices = (Beam.numCornerVertices = 0);
				Beam.startColor = Color.white;
				Beam.endColor = new Color(1f, 1f, 1f, 0.15f);
				GameObject val2 = new GameObject("Spear", new Type[1] { typeof(RectTransform) });
				val2.transform.SetParent(canvas, false);
				Label = val2.GetComponent<RectTransform>();
				RectTransform label = Label;
				RectTransform label2 = Label;
				RectTransform label3 = Label;
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0.5f, 0.5f);
				label3.pivot = val3;
				label.anchorMin = (label2.anchorMax = val3);
				Label.sizeDelta = new Vector2(192f, 96f);
				GameObject val5 = new GameObject("Edge direction", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(SpearEdgeArrow)
				});
				val5.transform.SetParent((Transform)(object)Label, false);
				((Graphic)val5.GetComponent<SpearEdgeArrow>()).raycastTarget = false;
				Pointer = val5.GetComponent<RectTransform>();
				Pointer.sizeDelta = new Vector2(24f, 28f);
				Pointer.anchoredPosition = new Vector2(0f, 42f);
				GameObject val6 = new GameObject("Icon", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				val6.transform.SetParent((Transform)(object)Label, false);
				Icon = val6.GetComponent<Image>();
				((Graphic)Icon).raycastTarget = false;
				Icon.preserveAspect = true;
				((Graphic)Icon).rectTransform.sizeDelta = new Vector2(40f, 40f);
				((Graphic)Icon).rectTransform.anchoredPosition = new Vector2(-54f, 0f);
				GameObject val7 = new GameObject("Distance", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val7.transform.SetParent((Transform)(object)Label, false);
				Distance = val7.GetComponent<TextMeshProUGUI>();
				((TMP_Text)Distance).font = font;
				((TMP_Text)Distance).fontSize = 23f;
				((TMP_Text)Distance).alignment = (TextAlignmentOptions)4097;
				((Graphic)Distance).color = Color.white;
				((TMP_Text)Distance).outlineWidth = 0.25f;
				((TMP_Text)Distance).outlineColor = Color32.op_Implicit(Color.black);
				((Graphic)Distance).raycastTarget = false;
				((TMP_Text)Distance).textWrappingMode = (TextWrappingModes)0;
				((TMP_Text)Distance).rectTransform.sizeDelta = new Vector2(108f, 44f);
				((TMP_Text)Distance).rectTransform.anchoredPosition = new Vector2(24f, 0f);
				GameObject val8 = new GameObject("Age", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val8.transform.SetParent((Transform)(object)Label, false);
				Age = val8.GetComponent<TextMeshProUGUI>();
				((TMP_Text)Age).font = font;
				((TMP_Text)Age).fontSize = 16f;
				((TMP_Text)Age).enableAutoSizing = true;
				((TMP_Text)Age).fontSizeMin = 12f;
				((TMP_Text)Age).fontSizeMax = 16f;
				((TMP_Text)Age).alignment = (TextAlignmentOptions)514;
				((Graphic)Age).color = Color.white;
				((TMP_Text)Age).outlineWidth = 0.25f;
				((TMP_Text)Age).outlineColor = Color32.op_Implicit(Color.black);
				((Graphic)Age).raycastTarget = false;
				((TMP_Text)Age).textWrappingMode = (TextWrappingModes)0;
				((TMP_Text)Age).rectTransform.sizeDelta = new Vector2(80f, 24f);
				((TMP_Text)Age).rectTransform.anchoredPosition = new Vector2(-54f, -34f);
				val8.SetActive(false);
			}
		}

		private const float BeamHeight = 500f;

		private const float HudGap = 116f;

		private const float HudWidth = 192f;

		private const float HudMarginY = 80f;

		private static readonly List<SpearLocator.Target> Targets = new List<SpearLocator.Target>(10);

		private static readonly List<Marker> Markers = new List<Marker>(10);

		private static readonly List<Marker> HudMarkers = new List<Marker>(10);

		private static readonly List<Vector2> HudPositions = new List<Vector2>(10);

		private static readonly int[] HudOrder = new int[10];

		private static GameObject? _root;

		private static Canvas? _canvas;

		private static Material? _material;

		private static float _retryAt;

		internal static void Clear()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			if ((Object)(object)_canvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvas).gameObject);
			}
			if ((Object)(object)_material != (Object)null)
			{
				Object.Destroy((Object)(object)_material);
			}
			_root = null;
			_canvas = null;
			_material = null;
			Targets.Clear();
			Markers.Clear();
			HudMarkers.Clear();
			HudPositions.Clear();
			_retryAt = 0f;
		}

		private static void Hide()
		{
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
			if ((Object)(object)_canvas != (Object)null)
			{
				((Component)_canvas).gameObject.SetActive(false);
			}
		}

		internal static void Update()
		{
			//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_019e: 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_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: 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_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0509: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			//IL_052a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0712: Unknown result type (might be due to invalid IL or missing references)
			//IL_0717: Unknown result type (might be due to invalid IL or missing references)
			//IL_0719: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0563: Unknown result type (might be due to invalid IL or missing references)
			//IL_057b: 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)_root != (Object)null || (Object)(object)_canvas != (Object)null || (Object)(object)_material != (Object)null)
				{
					Clear();
				}
				return;
			}
			Camera mainCamera = Utils.GetMainCamera();
			if (FearNoSpearPlugin.IsShuttingDown || (FearNoSpearPlugin.Cfg.SpearIndicatorStyle.Value == FearNoSpearPlugin.IndicatorStyle.Off && FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value == FearNoSpearPlugin.IndicatorStyle.Off) || (Object)(object)mainCamera == (Object)null || mainCamera.pixelHeight <= 0 || (Object)(object)Hud.instance == (Object)null || !Hud.instance.IsVisible() || Hud.IsUserHidden() || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || localPlayer.IsSleeping() || Minimap.IsOpen() || InventoryGui.IsVisible() || Menu.IsVisible())
			{
				Hide();
				return;
			}
			SpearLocator.GetTargets(localPlayer, Targets, FearNoSpearPlugin.Cfg.MaxDisplayedSpears.Value);
			if (Targets.Count == 0)
			{
				Hide();
			}
			else
			{
				if (((Object)(object)_root == (Object)null || (Object)(object)_canvas == (Object)null) && !Create())
				{
					return;
				}
				int num = 0;
				bool flag = false;
				foreach (SpearLocator.Target target in Targets)
				{
					FearNoSpearPlugin.IndicatorStyle style = GetStyle(target.Kind);
					flag |= HasBeam(style);
					if (HasHud(style))
					{
						num++;
					}
				}
				_root.SetActive(flag);
				((Component)_canvas).gameObject.SetActive(num > 0);
				Rect pixelRect = mainCamera.pixelRect;
				Rect safeArea = Screen.safeArea;
				Rect safeViewport = Rect.MinMaxRect(Mathf.Max(((Rect)(ref pixelRect)).xMin, ((Rect)(ref safeArea)).xMin), Mathf.Max(((Rect)(ref pixelRect)).yMin, ((Rect)(ref safeArea)).yMin), Mathf.Min(((Rect)(ref pixelRect)).xMax, ((Rect)(ref safeArea)).xMax), Mathf.Min(((Rect)(ref pixelRect)).yMax, ((Rect)(ref safeArea)).yMax));
				if (((Rect)(ref safeViewport)).width <= 0f || ((Rect)(ref safeViewport)).height <= 0f)
				{
					safeViewport = pixelRect;
				}
				float hudScale = GetHudScale(safeViewport, num);
				HudPositions.Clear();
				HudMarkers.Clear();
				foreach (Marker marker2 in Markers)
				{
					bool flag2 = false;
					foreach (SpearLocator.Target target2 in Targets)
					{
						if (target2.Key == marker2.Key)
						{
							flag2 = true;
							break;
						}
					}
					if (!flag2)
					{
						((Component)marker2.Beam).gameObject.SetActive(false);
						((Component)marker2.Label).gameObject.SetActive(false);
					}
				}
				foreach (SpearLocator.Target target3 in Targets)
				{
					Marker marker = GetMarker(target3.Key);
					FearNoSpearPlugin.IndicatorStyle style2 = GetStyle(target3.Kind);
					bool flag3 = HasBeam(style2);
					bool flag4 = HasHud(style2);
					float num2 = Vector3.Distance(((Component)localPlayer).transform.position, target3.Position);
					((Component)marker.Beam).gameObject.SetActive(flag3);
					((Component)marker.Label).gameObject.SetActive(flag4);
					if (flag3)
					{
						if (marker.BeamKind != target3.Kind)
						{
							marker.BeamKind = target3.Kind;
							Color beamColor = GetBeamColor(target3.Kind);
							marker.Beam.startColor = beamColor;
							beamColor.a = 0.15f;
							marker.Beam.endColor = beamColor;
						}
						if (marker.BeamPositionSet)
						{
							Vector3 val = target3.Position - marker.BeamPosition;
							if (!(((Vector3)(ref val)).sqrMagnitude > 1E-06f))
							{
								goto IL_0451;
							}
						}
						marker.BeamPositionSet = true;
						marker.BeamPosition = target3.Position;
						marker.Beam.SetPosition(0, target3.Position);
						marker.Beam.SetPosition(1, target3.Position + Vector3.up * 500f);
						goto IL_0451;
					}
					goto IL_0492;
					IL_0451:
					float beamWidth = GetBeamWidth(num2);
					if (!Mathf.Approximately(beamWidth, marker.BeamWidth))
					{
						marker.BeamWidth = beamWidth;
						LineRenderer beam = marker.Beam;
						float startWidth = (marker.Beam.endWidth = beamWidth);
						beam.startWidth = startWidth;
					}
					goto IL_0492;
					IL_0492:
					if (flag4)
					{
						Vector3 val2 = ((Component)mainCamera).transform.InverseTransformPoint(target3.Position);
						Vector2 position;
						Vector2 direction;
						bool hudPlacement = GetHudPlacement((Vector3)((val2.z > 0f) ? mainCamera.WorldToViewportPoint(target3.Position) : new Vector3(0.5f, 0.5f, val2.z)), new Vector2(val2.x, val2.y), pixelRect, safeViewport, hudScale, out position, out direction);
						HudPositions.Add(position);
						HudMarkers.Add(marker);
						((Transform)marker.Label).localScale = Vector3.one * hudScale;
						((Component)marker.Pointer).gameObject.SetActive(hudPlacement);
						if (hudPlacement)
						{
							((Transform)marker.Pointer).localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(direction.y, direction.x) * 57.29578f - 90f);
						}
						marker.Icon.sprite = target3.Icon;
						((Behaviour)marker.Icon).enabled = (Object)(object)target3.Icon != (Object)null;
						if (Time.unscaledTime >= marker.NextTextAt)
						{
							((TMP_Text)marker.Distance).text = ((num2 < 1000f) ? $"{num2:0} m" : $"{num2 / 1000f:0.0} km");
							marker.NextTextAt = Time.unscaledTime + 0.1f;
						}
						bool flag5 = target3.Kind == LocationKind.Tombstone;
						((Component)marker.Age).gameObject.SetActive(flag5);
						if (flag5 && Time.unscaledTime >= marker.NextAgeAt)
						{
							((TMP_Text)marker.Age).text = (((Object)(object)ZNet.instance != (Object)null && (Object)(object)EnvMan.instance != (Object)null) ? FormatAge(target3.CreatedTicks, ZNet.instance.GetTime().Ticks, EnvMan.instance.m_dayLengthSec) : string.Empty);
							marker.NextAgeAt = Time.unscaledTime + 1f;
						}
					}
				}
				if (num > 0)
				{
					ArrangeHud(safeViewport, hudScale, HudPositions);
					Vector2 val3 = new Vector2((float)Screen.width, (float)Screen.height) * 0.5f;
					for (int i = 0; i < HudMarkers.Count; i++)
					{
						HudMarkers[i].Label.anchoredPosition = HudPositions[i] - val3;
					}
				}
			}
		}

		private static FearNoSpearPlugin.IndicatorStyle GetStyle(LocationKind kind)
		{
			if (kind != LocationKind.Tombstone)
			{
				return FearNoSpearPlugin.Cfg.SpearIndicatorStyle.Value;
			}
			return FearNoSpearPlugin.Cfg.TombstoneIndicatorStyle.Value;
		}

		private static bool HasBeam(FearNoSpearPlugin.IndicatorStyle style)
		{
			if (style != FearNoSpearPlugin.IndicatorStyle.BeamAndHud)
			{
				return style == FearNoSpearPlugin.IndicatorStyle.Beam;
			}
			return true;
		}

		private static bool HasHud(FearNoSpearPlugin.IndicatorStyle style)
		{
			if (style != FearNoSpearPlugin.IndicatorStyle.BeamAndHud)
			{
				return style == FearNoSpearPlugin.IndicatorStyle.Hud;
			}
			return true;
		}

		private static string FormatAge(long createdTicks, long nowTicks, double dayLengthSeconds)
		{
			if (createdTicks > 0)
			{
				DateTime maxValue = DateTime.MaxValue;
				if (createdTicks <= maxValue.Ticks && !(dayLengthSeconds <= 0.0) && !double.IsNaN(dayLengthSeconds) && !double.IsInfinity(dayLengthSeconds))
				{
					double num = Math.Max(0.0, ((double)nowTicks - (double)createdTicks) / 10000000.0 / dayLengthSeconds);
					long num2 = (long)Math.Min(9999.0, Math.Floor(num));
					if (num >= 10000.0)
					{
						return "9999d+";
					}
					int num3 = (int)Math.Floor((num - (double)num2) * 24.0);
					return $"{num2}d {num3}h";
				}
			}
			return string.Empty;
		}

		private static Marker GetMarker(string key)
		{
			foreach (Marker marker2 in Markers)
			{
				if (marker2.Key == key)
				{
					return marker2;
				}
			}
			foreach (Marker marker3 in Markers)
			{
				bool flag = false;
				foreach (SpearLocator.Target target in Targets)
				{
					if (target.Key == marker3.Key)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					marker3.Key = key;
					marker3.NextTextAt = 0f;
					marker3.NextAgeAt = 0f;
					return marker3;
				}
			}
			Marker marker = new Marker(_root.transform, ((Component)_canvas).transform, _material, Hud.instance.m_healthText.font)
			{
				Key = key
			};
			Markers.Add(marker);
			return marker;
		}

		private static float GetBeamWidth(float distance)
		{
			return Mathf.Clamp(distance * 0.0014f, 0.1f, 1f);
		}

		private static Color GetBeamColor(LocationKind kind)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (kind != LocationKind.Tombstone)
			{
				return new Color(1f, 0.85f, 0.3f, 1f);
			}
			return new Color(0.8f, 0.6f, 1f, 1f);
		}

		private static float GetHudScale(Rect safeViewport, int count)
		{
			return Mathf.Min(1f, Mathf.Min(((Rect)(ref safeViewport)).width / 320f, ((Rect)(ref safeViewport)).height / ((float)Mathf.Clamp(count, 1, 10) * 116f + 160f)));
		}

		private static bool GetHudPlacement(Vector3 projected, Vector2 behindDirection, Rect viewport, Rect safeViewport, float scale, out Vector2 position, out Vector2 direction)
		{
			//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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(((Rect)(ref safeViewport)).width * 0.5f - 100f * scale, ((Rect)(ref safeViewport)).height * 0.5f - 80f * scale);
			Vector2 center = ((Rect)(ref safeViewport)).center;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor((projected.x - 0.5f) * ((Rect)(ref viewport)).width, (projected.y - 0.5f) * ((Rect)(ref viewport)).height);
			bool flag = projected.z <= 0f;
			if (!flag && !(projected.x < 0f) && !(projected.x > 1f) && !(projected.y < 0f) && !(projected.y > 1f))
			{
				Vector2 val3 = ((Rect)(ref viewport)).center + val2 + Vector2.up * (32f * scale);
				position = new Vector2(Mathf.Clamp(val3.x, center.x - val.x, center.x + val.x), Mathf.Clamp(val3.y, center.y - val.y, center.y + val.y));
				direction = Vector2.zero;
				return false;
			}
			if (flag)
			{
				val2 = behindDirection;
			}
			direction = ((((Vector2)(ref val2)).sqrMagnitude < 0.0001f) ? Vector2.down : ((Vector2)(ref val2)).normalized);
			float num = ((Mathf.Abs(direction.x) > 0.0001f) ? (val.x / Mathf.Abs(direction.x)) : float.PositiveInfinity);
			float num2 = ((Mathf.Abs(direction.y) > 0.0001f) ? (val.y / Mathf.Abs(direction.y)) : float.PositiveInfinity);
			position = center + direction * Mathf.Min(num, num2);
			return true;
		}

		private static void ArrangeHud(Rect safeViewport, float scale, List<Vector2> positions)
		{
			//IL_002a: 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_006e: 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_0077: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < positions.Count; i++)
			{
				int num = i;
				while (num > 0 && positions[HudOrder[num - 1]].y > positions[i].y)
				{
					HudOrder[num] = HudOrder[num - 1];
					num--;
				}
				HudOrder[num] = i;
			}
			float num2 = 116f * scale;
			for (int j = 0; j < positions.Count; j++)
			{
				Vector2 val = positions[HudOrder[j]];
				val.y = Mathf.Max(val.y, ((Rect)(ref safeViewport)).yMin + 80f * scale);
				for (int k = 0; k < j; k++)
				{
					Vector2 val2 = positions[HudOrder[k]];
					if (Mathf.Abs(val.x - val2.x) < 192f * scale)
					{
						val.y = Mathf.Max(val.y, val2.y + num2);
					}
				}
				positions[HudOrder[j]] = val;
			}
			for (int num3 = positions.Count - 1; num3 >= 0; num3--)
			{
				Vector2 val3 = positions[HudOrder[num3]];
				val3.y = Mathf.Min(val3.y, ((Rect)(ref safeViewport)).yMax - 80f * scale);
				for (int l = num3 + 1; l < positions.Count; l++)
				{
					Vector2 val4 = positions[HudOrder[l]];
					if (Mathf.Abs(val3.x - val4.x) < 192f * scale)
					{
						val3.y = Mathf.Min(val3.y, val4.y - num2);
					}
				}
				positions[HudOrder[num3]] = val3;
			}
		}

		private static bool Create()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_00d9: 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)
			if (Time.unscaledTime < _retryAt)
			{
				return false;
			}
			_retryAt = Time.unscaledTime + 5f;
			Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default");
			TMP_FontAsset val2 = (((Object)(object)Hud.instance.m_healthText != (Object)null) ? Hud.instance.m_healthText.font : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return false;
			}
			_root = new GameObject("FearNoSpear Beams");
			_root.SetActive(false);
			_material = new Material(val)
			{
				color = new Color(1f, 1f, 1f, 0.95f)
			};
			GameObject val3 = new GameObject("FearNoSpear Indicator UI", new Type[2]
			{
				typeof(RectTransform),
				typeof(Canvas)
			});
			val3.SetActive(false);
			_canvas = val3.GetComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 50;
			return true;
		}
	}
	internal sealed class SpearEdgeArrow : MaskableGraphic
	{
		protected override void OnPopulateMesh(VertexHelper vertices)
		{
			//IL_0011: 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)
			//IL_0020: 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_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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_0063: 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_006d: 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_0077: 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_00b5: 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)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			vertices.Clear();
			vertices.AddVert(new Vector3(0f, 14f), Color32.op_Implicit(Color.black), Vector4.op_Implicit(Vector2.zero));
			vertices.AddVert(new Vector3(-11f, -11f), Color32.op_Implicit(Color.black), Vector4.op_Implicit(Vector2.zero));
			vertices.AddVert(new Vector3(11f, -11f), Color32.op_Implicit(Color.black), Vector4.op_Implicit(Vector2.zero));
			vertices.AddTriangle(0, 1, 2);
			Color val = default(Color);
			((Color)(ref val))..ctor(1f, 0.85f, 0.3f, 1f);
			vertices.AddVert(new Vector3(0f, 10f), Color32.op_Implicit(val), Vector4.op_Implicit(Vector2.zero));
			vertices.AddVert(new Vector3(-7f, -8f), Color32.op_Implicit(val), Vector4.op_Implicit(Vector2.zero));
			vertices.AddVert(new Vector3(7f, -8f), Color32.op_Implicit(val), Vector4.op_Implicit(Vector2.zero));
			vertices.AddTriangle(3, 4, 5);
		}
	}
	internal enum LocationKind
	{
		Weapon,
		Tombstone
	}
	internal sealed class SpearLocationRecord
	{
		internal string Key = string.Empty;

		internal Vector3 Position;

		internal int PrefabHash;

		internal int Variant;

		internal LocationKind Kind;

		internal long CreatedTicks;
	}
	internal static class SpearNetwork
	{
		private const int ProtocolVersion = 7;

		private const string RequestRpcName = "FearNoSpear_SpearLocationRequest";

		private const string ResponseRpcName = "FearNoSpear_SpearLocationResponse";

		private static ZRoutedRpc? _registeredRpc;

		private static bool _warnedProtocolMismatch;

		internal static void ClearSession()
		{
			SpearServerRegistry.Clear();
			_warnedProtocolMismatch = false;
		}

		internal static void RegisterRpcs()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && _registeredRpc != instance)
			{
				instance.Register<ZPackage>("FearNoSpear_SpearLocationRequest", (Action<long, ZPackage>)RPC_RequestSpearLocation);
				instance.Register<ZPackage>("FearNoSpear_SpearLocationResponse", (Action<long, ZPackage>)RPC_SpearLocationResponse);
				_registeredRpc = instance;
			}
		}

		internal static bool RequestServerSpearLocation(Player localPlayer, int requestId, bool weapons, bool tombstones)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			if (!TryGetServerPeerId(out var serverPeerId))
			{
				return false;
			}
			ZPackage val = new ZPackage();
			WriteHeader(val);
			val.Write(requestId);
			val.Write(((Component)localPlayer).transform.position);
			val.Write(weapons);
			val.Write(tombstones);
			ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerId, "FearNoSpear_SpearLocationRequest", new object[1] { val });
			return true;
		}

		private static bool TryGetServerPeerId(out long serverPeerId)
		{
			serverPeerId = 0L;
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
			{
				return false;
			}
			if (ZNet.instance.IsServer())
			{
				serverPeerId = ZNet.GetUID();
				return true;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null)
			{
				return false;
			}
			serverPeerId = serverPeer.m_uid;
			return serverPeerId != 0;
		}

		private static void RPC_RequestSpearLocation(long senderPeerId, ZPackage package)
		{
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			int requestId;
			Vector3 val;
			bool weapons;
			bool tombstones;
			try
			{
				if (!TryReadHeader(package, "FearNoSpear_SpearLocationRequest"))
				{
					return;
				}
				requestId = package.ReadInt();
				val = package.ReadVector3();
				weapons = package.ReadBool();
				tombstones = package.ReadBool();
				if (!IsFinite(val))
				{
					return;
				}
			}
			catch (Exception ex)
			{
				FearNoSpearPlugin.Log.LogWarning((object)("Failed to read spear location request RPC: " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (!TryResolvePeerPlayerId(senderPeerId, out var playerId))
			{
				SendSpearLocationResponse(senderPeerId, requestId, new List<SpearLocationRecord>());
				return;
			}
			List<SpearLocationRecord> records = SpearServerRegistry.SelectBest(playerId, val, weapons, tombstones);
			SendSpearLocationResponse(senderPeerId, requestId, records);
		}

		private static void RPC_SpearLocationResponse(long senderPeerId, ZPackage package)
		{
			if (!TryGetServerPeerId(out var serverPeerId) || senderPeerId != serverPeerId)
			{
				FearNoSpearPlugin.Log.LogWarning((object)$"Ignored spear location response from non-server peer {senderPeerId}.");
				return;
			}
			try
			{
				if (!TryReadHeader(package, "FearNoSpear_SpearLocationResponse"))
				{
					return;
				}
				int requestId = package.ReadInt();
				int num = package.ReadInt();
				if (num < 0 || num > 10)
				{
					FearNoSpearPlugin.Log.LogWarning((object)$"Ignored spear location response with invalid record count {num}.");
					return;
				}
				List<SpearLocationRecord> list = new List<SpearLocationRecord>(num);
				for (int i = 0; i < num; i++)
				{
					SpearLocationRecord item = ReadRecord(package);
					list.Add(item);
				}
				if (AreValidRecords(list))
				{
					SpearLocator.ReceiveServerRecords(requestId, list);
				}
			}
			catch (Exception ex)
			{
				FearNoSpearPlugin.Log.LogWarning((object)("Failed to read spear location response RPC: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void SendSpearLocationResponse(long targetPeerId, int requestId, List<SpearLocationRecord> records)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			if (ZRoutedRpc.instance != null)
			{
				int num = Mathf.Min(records.Count, 10);
				ZPackage val = new ZPackage();
				WriteHeader(val);
				val.Write(requestId);
				val.Write(num);
				for (int i = 0; i < num; i++)
				{
					WriteRecord(val, records[i]);
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "FearNoSpear_SpearLocationResponse", new object[1] { val });
			}
		}

		private static bool TryResolvePeerPlayerId(long senderPeerId, out long playerId)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			playerId = 0L;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && senderPeerId == ZNet.GetUID())
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					playerId = localPlayer.GetPlayerID();
					if (playerId != 0L)
					{
						return true;
					}
				}
			}
			if (senderPeerId == 0L)
			{
				return false;
			}
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(senderPeerId) : null);
			if (val != null && !((ZDOID)(ref val.m_characterID)).IsNone())
			{
				ZDO val2 = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val.m_characterID) : null);
				if (val2 != null && val2.IsValid())
				{
					playerId = val2.GetLong(ZDOVars.s_playerID, 0L);
					if (playerId != 0L)
					{
						return true;
					}
				}
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer == (Object)null)
				{
					continue;
				}
				ZNetView nView = ReflectionCache.GetNView((Character)(object)allPlayer);
				if ((Object)(object)nView == (Object)null || !nView.IsValid())
				{
					continue;
				}
				ZDO zDO = nView.GetZDO();
				if (zDO != null && zDO.IsValid() && zDO.GetOwner() == senderPeerId)
				{
					playerId = allPlayer.GetPlayerID();
					if (playerId != 0L)
					{
						return true;
					}
				}
			}
			playerId = 0L;
			return false;
		}

		private static void WriteHeader(ZPackage package)
		{
			package.Write(7);
		}

		private static bool TryReadHeader(ZPackage package, string rpcName)
		{
			int num = package.ReadInt();
			if (num == 7)
			{
				return true;
			}
			if (!_warnedProtocolMismatch)
			{
				_warnedProtocolMismatch = true;
				FearNoSpearPlugin.Log.LogWarning((object)$"Ignoring incompatible {rpcName} payload: protocol={num}; expected={7}. Make sure server and client use the same FearNoSpear build.");
			}
			return false;
		}

		private static bool IsFinite(Vector3 position)
		{
			if (!float.IsNaN(((Vector3)(ref position)).sqrMagnitude))
			{
				return !float.IsInfinity(((Vector3)(ref position)).sqrMagnitude);
			}
			return false;
		}

		private static void WriteRecord(ZPackage package, SpearLocationRecord record)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			package.Write(record.Key);
			package.Write(record.Position);
			package.Write(record.PrefabHash);
			package.Write(record.Variant);
			package.Write((int)record.Kind);
			package.Write(record.CreatedTicks);
		}

		private static SpearLocationRecord ReadRecord(ZPackage package)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new SpearLocationRecord
			{
				Key = package.ReadString(),
				Position = package.ReadVector3(),
				PrefabHash = package.ReadInt(),
				Variant = package.ReadInt(),
				Kind = (LocationKind)package.ReadInt(),
				CreatedTicks = package.ReadLong()
			};
		}

		private static bool AreValidRecords(List<SpearLocationRecord> records)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (SpearLocationRecord record in records)
			{
				if (string.IsNullOrEmpty(record.Key) || !hashSet.Add(record.Key) || !IsFinite(record.Position))
				{
					return false;
				}
				if (record.Kind == LocationKind.Weapon)
				{
					if (!record.Key.StartsWith("drop:", StringComparison.Ordinal) || record.CreatedTicks != 0L || ++num > 5)
					{
						return false;
					}
					continue;
				}
				if (record.Kind == LocationKind.Tombstone)
				{
					if (record.Key.StartsWith("tomb:", StringComparison.Ordinal) && record.CreatedTicks > 0)
					{
						long createdTicks = record.CreatedTicks;
						DateTime maxValue = DateTime.MaxValue;
						if (createdTicks <= maxValue.Ticks && ++num2 <= 5)
						{
							continue;
						}
					}
					return false;
				}
				return false;
			}
			return true;
		}
	}
	internal static class SpearServerRegistry
	{
		private const float CandidateCacheSeconds = 5f;

		private static readonly Dictionary<long, List<ZDOID>> WeaponIds = new Dictionary<long, List<ZDOID>>();

		private static readonly Dictionary<long, List<ZDOID>> TombstoneIds = new Dictionary<long, List<ZDOID>>();

		private static float _nextScanAt;

		private static float _nextTombstoneScanAt;

		internal static void Clear()
		{
			WeaponIds.Clear();
			_nextScanAt = 0f;
			TombstoneIds.Clear();
			_nextTombstoneScanAt = 0f;
		}

		internal static List<SpearLocationRecord> SelectBest(long playerId, Vector3 referencePosition, bool weapons, bool tombstones)
		{
			//IL_0007: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			List<SpearLocationRecord> list = new List<SpearLocationRecord>();
			if (playerId == 0L || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return list;
			}
			if (ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return list;
			}
			if (weapons && Time.unscaledTime >= _nextScanAt)
			{
				RefreshCandidates(WeaponIds, ZDOExtraData.GetAllZDOIDsWithHash((Type)4, SpearThrowerMetadata.ThrowerPlayerIdHash), SpearThrowerMetadata.ThrowerPlayerIdHash);
				_nextScanAt = Time.unscaledTime + 5f;
			}
			if (weapons && WeaponIds.TryGetValue(playerId, out List<ZDOID> value))
			{
				foreach (ZDOID item in value)
				{
					ZDO zDO = ZDOMan.instance.GetZDO(item);
					if (SpearThrowerMetadata.ReadFromZdo(zDO) == playerId)
					{
						GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab());
						if (!((Object)(object)(((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null) == (Object)null))
						{
							list.Add(new SpearLocationRecord
							{
								Key = SpearItemIdentity.BuildWorldDropRecordKey(item),
								Position = zDO.GetPosition(),
								PrefabHash = zDO.GetPrefab(),
								Variant = zDO.GetInt(ZDOVars.s_variant, 0)
							});
						}
					}
				}
			}
			list = list.OrderBy(delegate(SpearLocationRecord record)
			{
				//IL_0001: 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)
				Vector3 val = record.Position - referencePosition;
				return ((Vector3)(ref val)).sqrMagnitude;
			}).ThenBy<SpearLocationRecord, string>((SpearLocationRecord record) => record.Key, StringComparer.Ordinal).Take(5)
				.ToList();
			if (!tombstones)
			{
				return list;
			}
			if (Time.unscaledTime >= _