Decompiled source of BaseDefenseWard v0.1.2

plugins/BaseDefenseWard.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SoftReferenceableAssets.SceneManagement;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BaseDefenseWard")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+81884e753045922b1f64b6ffa0b0268d3bd6b5ad")]
[assembly: AssemblyProduct("BaseDefenseWard")]
[assembly: AssemblyTitle("BaseDefenseWard")]
[assembly: AssemblyVersion("0.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BaseDefenseWard
{
	[HarmonyPatch(typeof(BossStone), "SetActivated")]
	internal static class BossStone_SetActivated
	{
		private static readonly Dictionary<string, bool> Last = new Dictionary<string, bool>();

		public static string KeyFor(BossStone stone)
		{
			string text = ((Object)((Component)stone).gameObject).name.Replace("(Clone)", "").Trim();
			if (text.StartsWith("BossStone_"))
			{
				text = text.Substring("BossStone_".Length);
			}
			return "bdw_hung_" + text.ToLowerInvariant();
		}

		private static void Postfix(BossStone __instance, bool active)
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)__instance.m_itemStand == (Object)null)
			{
				return;
			}
			string text = KeyFor(__instance);
			if (Last.TryGetValue(text, out var value) && value == active)
			{
				return;
			}
			Last[text] = active;
			if (active)
			{
				if (!ZoneSystem.instance.GetGlobalKey(text))
				{
					ZoneSystem.instance.SetGlobalKey(text);
				}
			}
			else if (ZoneSystem.instance.GetGlobalKey(text))
			{
				ZoneSystem.instance.RemoveGlobalKey(text);
			}
		}
	}
	public static class Cfg
	{
		public static ConfigEntry<float> CountdownMinutes;

		public static ConfigEntry<float> WaveTimeLimitMinutes;

		public static ConfigEntry<float> SpawnRadius;

		public static ConfigEntry<int> BaseMobCount;

		public static ConfigEntry<int> MobsPerCompletedChallenge;

		public static ConfigEntry<int> MaxMobCount;

		public static ConfigEntry<float> SpawnIntervalSeconds;

		public static ConfigEntry<float> RestMinMinutes;

		public static ConfigEntry<float> RestMaxMinutes;

		public static ConfigEntry<bool> PausableWards;

		public static ConfigEntry<KeyboardShortcut> PauseKey;

		public static ConfigEntry<bool> StartNow;

		public static ConfigEntry<float> PlayerHuntRange;

		public static ConfigEntry<float> RewardMultiplier;

		public static ConfigEntry<bool> DeleteWorldOnLoss;

		public static ConfigEntry<float> BuildDeadlineMinutes;

		public static ConfigEntry<string> BossKeys;

		public static ConfigEntry<string> ExtraMobPools;

		public static void Bind(ConfigFile c)
		{
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			CountdownMinutes = c.Bind<float>("Challenge", "CountdownMinutes", 10f, "Minutes between building the ward and the first wave.");
			WaveTimeLimitMinutes = c.Bind<float>("Challenge", "WaveTimeLimitMinutes", 5f, "If the ward still stands after this many minutes the challenge is won even if mobs remain.");
			SpawnRadius = c.Bind<float>("Challenge", "SpawnRadius", 55f, "Distance from the ward at which wave mobs spawn (the ward's protected radius is 32).");
			BaseMobCount = c.Bind<int>("Challenge", "BaseMobCount", 4, "Mobs in the first wave.");
			MobsPerCompletedChallenge = c.Bind<int>("Challenge", "MobsPerCompletedChallenge", 2, "Extra mobs per previously completed challenge on this world.");
			MaxMobCount = c.Bind<int>("Challenge", "MaxMobCount", 30, "Hard cap on mobs per wave.");
			SpawnIntervalSeconds = c.Bind<float>("Challenge", "SpawnIntervalSeconds", 1.5f, "Delay between individual spawns.");
			RestMinMinutes = c.Bind<float>("Challenge", "RestMinMinutes", 30f, "After a successful defense the ward rests for a random time between RestMinMinutes and RestMaxMinutes before the next countdown starts.");
			RestMaxMinutes = c.Bind<float>("Challenge", "RestMaxMinutes", 120f, "Upper bound of the rest between challenges, in minutes.");
			PausableWards = c.Bind<bool>("Challenge", "PausableWards", false, "Allow pausing a ward (its countdown and rest timers stop) by pressing PauseKey while standing within interact range of it. Not possible during a wave.");
			PauseKey = c.Bind<KeyboardShortcut>("Challenge", "PauseKey", new KeyboardShortcut((KeyCode)112, Array.Empty<KeyCode>()), "Key that pauses or resumes the ward you stand next to (only when PausableWards is on).");
			StartNow = c.Bind<bool>("Challenge", "StartNow", true, "Interacting with the ward (Use key) during the countdown starts the wave immediately instead of toggling the ward.");
			PlayerHuntRange = c.Bind<float>("Challenge", "PlayerHuntRange", 40f, "Mobs switch to hunting a player who is within this range of them and not sheltered (roofed and walled in). Otherwise they go for the ward.");
			RewardMultiplier = c.Bind<float>("Rewards", "RewardMultiplier", 1f, "Scales all reward amounts.");
			DeleteWorldOnLoss = c.Bind<bool>("Consequences", "DeleteWorldOnLoss", false, "If the ward is destroyed during a wave (or the build deadline is missed) the world save is deleted and the game exits. Dedicated servers exit so a wrapper script can restart them.");
			BuildDeadlineMinutes = c.Bind<float>("Consequences", "BuildDeadlineMinutes", 0f, "If > 0, a ward must be built within this many minutes of world creation or the challenge counts as lost. 0 = off.");
			BossKeys = c.Bind<string>("Progression", "BossKeys", "bdw_hung_eikthyr,bdw_hung_theelder,bdw_hung_bonemass,bdw_hung_dragonqueen,bdw_hung_yagluth,bdw_hung_thequeen,bdw_hung_fader", "Global keys in tier order; the highest tier whose key is set decides which mob pools are unlocked. The bdw_hung_* keys are set by this mod when a trophy is hung on the corresponding boss stone. To use boss kills instead: defeated_eikthyr,defeated_gdking,defeated_bonemass,defeated_dragon,defeated_goblinking,defeated_queen,defeated_fader");
			ExtraMobPools = c.Bind<string>("Progression", "ExtraMobPools", "", "Optional override of the mob pools, one tier per ';', prefabs per ',' e.g. 'Greyling,Neck;Greydwarf,Skeleton'. Empty = built-in pools.");
		}
	}
	public static class Deadline
	{
		private static float _timer;

		public static void Tick(float dt)
		{
			_timer -= dt;
			if (_timer > 0f)
			{
				return;
			}
			_timer = 1f;
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			string s = default(string);
			long result;
			if (!ZoneSystem.instance.GetGlobalKey("bdw_worldstart", ref s))
			{
				Progress.SetValue("bdw_worldstart", ((long)timeSeconds).ToString(CultureInfo.InvariantCulture));
			}
			else if (!(Cfg.BuildDeadlineMinutes.Value <= 0f) && !Progress.Has("bdw_built") && !Progress.Has("bdw_deadline_failed") && long.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result) && !(timeSeconds - (double)result < (double)Cfg.BuildDeadlineMinutes.Value * 60.0))
			{
				ZoneSystem.instance.SetGlobalKey("bdw_deadline_failed");
				Plugin.Log.LogWarning((object)"Build deadline missed: no Base Defense Ward was built in time");
				WardChallenge.Say("No ward was built in time. Challenge lost.");
				if (Cfg.DeleteWorldOnLoss.Value)
				{
					WorldReset.Schedule("build deadline missed");
				}
			}
		}

		public static string Status()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return "no znet";
			}
			string text = default(string);
			ZoneSystem.instance.GetGlobalKey("bdw_worldstart", ref text);
			return string.Format("deadlineMin={0} worldStart={1} now={2:0} built={3} failed={4}", Cfg.BuildDeadlineMinutes.Value, text, ZNet.instance.GetTimeSeconds(), Progress.Has("bdw_built"), Progress.Has("bdw_deadline_failed"));
		}
	}
	public static class Interaction
	{
		public static WardChallenge NearestInReach(Player p)
		{
			float reach = p.m_maxInteractDistance;
			return (from w in WardChallenge.All
				where (Object)(object)w != (Object)null && (Object)(object)w.View != (Object)null && w.View.IsValid()
				select (w: w, d: Vector3.Distance(((Component)w).transform.position, ((Component)p).transform.position)) into t
				where t.d <= reach
				orderby t.d
				select t.w).FirstOrDefault();
		}

		public static string TogglePause(Player p)
		{
			if (!Cfg.PausableWards.Value)
			{
				return "Pausing wards is disabled in the config.";
			}
			WardChallenge wardChallenge = NearestInReach(p);
			if ((Object)(object)wardChallenge == (Object)null)
			{
				return "No Base Defense Ward within reach.";
			}
			if (wardChallenge.State == ChallengeState.Active)
			{
				return "A wave is attacking. The ward cannot be paused now.";
			}
			wardChallenge.RequestTogglePause();
			return null;
		}

		public static string StartNow(WardChallenge w)
		{
			if (!Cfg.StartNow.Value)
			{
				return "Starting the wave early is disabled in the config.";
			}
			if (w.State != ChallengeState.Countdown)
			{
				return null;
			}
			w.RequestStartNow();
			return "The wave is called early!";
		}

		public static void Tick()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (!Cfg.PausableWards.Value || (Object)(object)Player.m_localPlayer == (Object)null || Console.IsVisible())
			{
				return;
			}
			Chat instance = Chat.instance;
			if (instance != null && instance.HasFocus())
			{
				return;
			}
			KeyboardShortcut value = Cfg.PauseKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				string text = TogglePause(Player.m_localPlayer);
				if (text != null)
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PrivateArea), "Interact")]
	internal static class PrivateArea_Interact
	{
		private static bool Prefix(PrivateArea __instance, Humanoid human, bool hold, ref bool __result)
		{
			if (hold || !Cfg.StartNow.Value)
			{
				return true;
			}
			WardChallenge component = ((Component)__instance).GetComponent<WardChallenge>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.View == (Object)null || !component.View.IsValid() || component.State != ChallengeState.Countdown)
			{
				return true;
			}
			string text = Interaction.StartNow(component);
			if (text != null)
			{
				Player val = (Player)(object)((human is Player) ? human : null);
				if (val != null)
				{
					((Character)val).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(PrivateArea), "GetHoverText")]
	internal static class PrivateArea_GetHoverText
	{
		private static void Postfix(PrivateArea __instance, ref string __result)
		{
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			WardChallenge component = ((Component)__instance).GetComponent<WardChallenge>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.View == (Object)null || !component.View.IsValid())
			{
				return;
			}
			List<string> list = new List<string>();
			switch (component.State)
			{
			case ChallengeState.Countdown:
				list.Add("Wave in " + Fmt(Cfg.CountdownMinutes.Value * 60f - component.Elapsed) + (component.Paused ? " (paused)" : ""));
				if (Cfg.StartNow.Value)
				{
					list.Add("[<color=yellow><b>$KEY_Use</b></color>] Start the wave now");
				}
				break;
			case ChallengeState.Active:
				list.Add("Wave! hold " + Fmt(Cfg.WaveTimeLimitMinutes.Value * 60f - component.WaveElapsed));
				break;
			case ChallengeState.Resting:
				list.Add("Defended. Next wave in " + Fmt(component.RestFor - component.RestElapsed) + (component.Paused ? " (paused)" : ""));
				break;
			}
			if (Cfg.PausableWards.Value && component.State != ChallengeState.Active)
			{
				KeyboardShortcut value = Cfg.PauseKey.Value;
				list.Add(string.Format("[<color=yellow><b>{0}</b></color>] {1} the ward", ((KeyboardShortcut)(ref value)).MainKey, component.Paused ? "Resume" : "Pause"));
			}
			if (list.Count > 0)
			{
				__result = Localization.instance.Localize(__result + "\n" + string.Join("\n", list));
			}
		}

		private static string Fmt(float s)
		{
			if (s < 0f)
			{
				s = 0f;
			}
			TimeSpan timeSpan = TimeSpan.FromSeconds(s);
			if (!(timeSpan.TotalHours >= 1.0))
			{
				return $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
			}
			return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
		}
	}
	public static class MobPool
	{
		private static readonly string[][] Builtin = new string[8][]
		{
			new string[3] { "Greyling", "Neck", "Boar" },
			new string[4] { "Greydwarf", "Greydwarf_Shaman", "Skeleton", "Greydwarf_Elite" },
			new string[5] { "Draugr", "Draugr_Ranged", "Blob", "Skeleton_Poison", "Troll" },
			new string[5] { "Wolf", "Draugr_Elite", "Fenring", "Ulv", "Wraith" },
			new string[4] { "Goblin", "GoblinArcher", "GoblinShaman", "GoblinBrute" },
			new string[4] { "Seeker", "SeekerBrute", "Tick", "Gjall" },
			new string[4] { "Charred_Melee", "Charred_Archer", "Charred_Mage", "Charred_Twitcher" },
			new string[4] { "Charred_Melee", "Charred_Archer", "Charred_Mage", "Morgen" }
		};

		private static readonly (string, int)[][] Rewards = new(string, int)[8][]
		{
			new(string, int)[3]
			{
				("Coins", 25),
				("Flint", 10),
				("LeatherScraps", 8)
			},
			new(string, int)[4]
			{
				("Coins", 50),
				("Copper", 6),
				("Tin", 4),
				("SurtlingCore", 2)
			},
			new(string, int)[4]
			{
				("Coins", 80),
				("Iron", 6),
				("Bronze", 4),
				("Chain", 1)
			},
			new(string, int)[4]
			{
				("Coins", 120),
				("Silver", 6),
				("WolfPelt", 3),
				("Obsidian", 6)
			},
			new(string, int)[4]
			{
				("Coins", 160),
				("BlackMetal", 6),
				("LinenThread", 6),
				("Needle", 2)
			},
			new(string, int)[4]
			{
				("Coins", 220),
				("BlackMarble", 12),
				("Eitr", 6),
				("Sap", 6)
			},
			new(string, int)[4]
			{
				("Coins", 300),
				("FlametalNew", 6),
				("Grausten", 12),
				("Blackwood", 12)
			},
			new(string, int)[4]
			{
				("Coins", 400),
				("FlametalNew", 10),
				("Grausten", 16),
				("CharredBone", 8)
			}
		};

		public static string[][] Pools()
		{
			string value = Cfg.ExtraMobPools.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				return Builtin;
			}
			return (from t in value.Split(';')
				select (from x in t.Split(',')
					select x.Trim() into x
					where x.Length > 0
					select x).ToArray()).ToArray();
		}

		public static List<string> Sample(int count, int bossTier, int completed, Random rng)
		{
			string[][] array = Pools();
			int num = Mathf.Clamp(bossTier, 0, array.Length - 1);
			float[] array2 = new float[num + 1];
			for (int i = 0; i <= num; i++)
			{
				float num2 = 1f + ((i == num) ? 2f : 0f) + (float)i * 0.5f + (float)completed * 0.25f * (float)i;
				array2[i] = num2;
			}
			List<string> list = new List<string>();
			for (int j = 0; j < count; j++)
			{
				int num3 = Weighted(array2, rng);
				string[] array3 = array[num3].Where(Exists).ToArray();
				if (array3.Length == 0)
				{
					array3 = array[0].Where(Exists).ToArray();
				}
				if (array3.Length == 0)
				{
					break;
				}
				list.Add(array3[rng.Next(array3.Length)]);
			}
			return list;
		}

		public static List<(string prefab, int amount)> RewardFor(int bossTier, int completed)
		{
			int num = Mathf.Clamp(bossTier, 0, Rewards.Length - 1);
			float scale = (1f + 0.25f * (float)completed) * Cfg.RewardMultiplier.Value;
			return (from r in Rewards[num]
				where Exists(r.Item1)
				select (r.Item1, Mathf.Max(1, Mathf.RoundToInt((float)r.Item2 * scale)))).ToList();
		}

		private static bool Exists(string prefab)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZNetScene.instance.GetPrefab(prefab) != (Object)null;
			}
			return false;
		}

		private static int Weighted(float[] w, Random rng)
		{
			float num = w.Sum();
			float num2 = (float)rng.NextDouble() * num;
			for (int i = 0; i < w.Length; i++)
			{
				num2 -= w[i];
				if (num2 <= 0f)
				{
					return i;
				}
			}
			return w.Length - 1;
		}
	}
	public static class OnePerPlayer
	{
		public static readonly int HState = StringExtensionMethods.GetStableHashCode("bdw_state");

		public static int BlockedCount;

		public static int RemovedCount;

		public static bool IsWard(Piece piece)
		{
			if ((Object)(object)piece != (Object)null)
			{
				return ((Object)((Component)piece).gameObject).name.StartsWith("BaseDefenseWard");
			}
			return false;
		}

		public static List<ZDO> WardsOf(long playerId)
		{
			List<ZDO> list = new List<ZDO>();
			int num = 0;
			while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("BaseDefenseWard", list, ref num))
			{
			}
			return list.FindAll((ZDO z) => z.GetLong(ZDOVars.s_creator, 0L) == playerId);
		}

		public static bool HasWaveInProgress(long playerId)
		{
			return WardsOf(playerId).Exists((ZDO z) => z.GetInt(HState, 0) == 2);
		}

		public static int RemoveOtherWards(long playerId, ZDOID keep)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (ZDO item in WardsOf(playerId))
			{
				if (!(item.m_uid == keep))
				{
					GameObject val = ZNetScene.instance.FindInstance(item.m_uid);
					if ((Object)(object)val != (Object)null)
					{
						ZNetView component = val.GetComponent<ZNetView>();
						component.ClaimOwnership();
						component.Destroy();
					}
					else
					{
						item.SetOwner(ZDOMan.GetSessionID());
						ZDOMan.instance.DestroyZDO(item);
					}
					num++;
				}
			}
			RemovedCount += num;
			return num;
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class Player_TryPlacePiece
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			if (!OnePerPlayer.IsWard(piece) || !OnePerPlayer.HasWaveInProgress(__instance.GetPlayerID()))
			{
				return true;
			}
			((Character)__instance).Message((MessageType)2, "A wave is attacking your ward. You cannot move it now.", 0, (Sprite)null, false);
			OnePerPlayer.BlockedCount++;
			Plugin.Log.LogInfo((object)"Ward placement blocked: wave in progress");
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "PlacePiece")]
	internal static class Player_PlacePiece
	{
		private static void Postfix(Player __instance, Piece piece, Vector3 pos)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (!OnePerPlayer.IsWard(piece))
			{
				return;
			}
			ZDOID keep = ZDOID.None;
			foreach (ZDO item in OnePerPlayer.WardsOf(__instance.GetPlayerID()))
			{
				if (Vector3.Distance(item.GetPosition(), pos) < 0.5f)
				{
					keep = item.m_uid;
				}
			}
			int num = OnePerPlayer.RemoveOtherWards(__instance.GetPlayerID(), keep);
			if (num > 0)
			{
				((Character)__instance).Message((MessageType)1, "Previous ward removed. The countdown restarts here.", 0, (Sprite)null, false);
				Plugin.Log.LogInfo((object)$"Removed {num} previous ward(s) for player {__instance.GetPlayerID()}");
			}
		}
	}
	[BepInPlugin("com.night.basedefenseward", "Base Defense Ward", "0.1.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "com.night.basedefenseward";

		public const string NAME = "Base Defense Ward";

		public const string VERSION = "0.1.2";

		public const string PrefabName = "BaseDefenseWard";

		public const string SourcePrefab = "guard_stone";

		public static readonly Color GlowColor = new Color(0.9f, 0.35f, 0.4f);

		public static ManualLogSource Log;

		public static GameObject WardPrefab;

		private static GameObject _holder;

		public static Plugin Instance;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			Log.LogInfo((object)"Base Defense Ward 0.1.2 loading");
			_holder = new GameObject("BaseDefenseWard_PrefabHolder");
			_holder.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_holder);
			new Harmony("com.night.basedefenseward").PatchAll(Assembly.GetExecutingAssembly());
		}

		private void Update()
		{
			Deadline.Tick(Time.deltaTime);
			WardBoard.Tick(Time.deltaTime);
			WardPins.Tick(Time.deltaTime);
			Interaction.Tick();
		}

		public static string DebugCommand(string line)
		{
			//IL_0882: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0819: 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_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_07d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_079e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Unknown result type (might be due to invalid IL or missing references)
			//IL_050b: Expected O, but got Unknown
			//IL_0b5c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b61: Unknown result type (might be due to invalid IL or missing references)
			string[] parts = line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			string text = ((parts.Length != 0) ? parts[0] : "");
			Player localPlayer = Player.m_localPlayer;
			switch (text)
			{
			case "status":
			{
				WardChallenge wardChallenge2 = (((Object)(object)localPlayer != (Object)null) ? WardChallenge.Nearest(((Component)localPlayer).transform.position) : WardChallenge.All.FirstOrDefault());
				return (((Object)(object)wardChallenge2 == (Object)null) ? "no ward nearby" : wardChallenge2.Status()) + "\n" + Progress.Summary() + "\n" + Deadline.Status();
			}
			case "skip":
			{
				WardChallenge wardChallenge6 = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if ((Object)(object)wardChallenge6 == (Object)null)
				{
					return "no ward nearby";
				}
				long num3 = long.Parse(parts[1]);
				ZDO zDO2 = ((Component)wardChallenge6).GetComponent<ZNetView>().GetZDO();
				int num4 = ((wardChallenge6.State == ChallengeState.Active) ? WardChallenge.HWaveElapsed : WardChallenge.HElapsed);
				zDO2.Set(num4, zDO2.GetFloat(num4, 0f) + (float)num3);
				return "skipped " + num3;
			}
			case "mobs":
			{
				WardChallenge w = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if ((Object)(object)w == (Object)null)
				{
					return "no ward nearby";
				}
				return string.Join("\n", w.AliveMobs().Select(delegate(Character c)
				{
					//IL_003d: Unknown result type (might be due to invalid IL or missing references)
					//IL_004d: Unknown result type (might be due to invalid IL or missing references)
					object[] obj2 = new object[5]
					{
						((Object)((Component)c).gameObject).name,
						c.GetHealth(),
						c.GetMaxHealth(),
						Vector3.Distance(((Component)c).transform.position, ((Component)w).transform.position),
						null
					};
					MonsterAI component2 = ((Component)c).GetComponent<MonsterAI>();
					obj2[4] = ((component2 != null) ? new bool?(((BaseAI)component2).HuntPlayer()) : ((bool?)null));
					return string.Format("{0}\t{1:0}/{2:0}\t{3:0}m hunt={4}", obj2);
				})) + $"\ncount={w.AliveMobs().Count}";
			}
			case "kill":
			{
				WardChallenge wardChallenge5 = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if ((Object)(object)wardChallenge5 == (Object)null)
				{
					return "no ward nearby";
				}
				int num2 = 0;
				foreach (Character item8 in wardChallenge5.AliveMobs())
				{
					HitData val = new HitData();
					val.m_damage.m_damage = 1E+10f;
					item8.Damage(val);
					num2++;
				}
				return "killed " + num2;
			}
			case "setcompleted":
				Progress.SetCompleted(int.Parse(parts[1]));
				return Progress.Summary();
			case "setkey":
				ZoneSystem.instance.SetGlobalKey(parts[1]);
				return Progress.Summary();
			case "removekey":
				ZoneSystem.instance.RemoveGlobalKey(parts[1]);
				return Progress.Summary();
			case "sample":
				return string.Join(",", MobPool.Sample(int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]), new Random()));
			case "reward":
				return string.Join(",", from tuple2 in MobPool.RewardFor(int.Parse(parts[1]), int.Parse(parts[2]))
					select tuple2.prefab + "x" + tuple2.amount);
			case "cfg":
				if (parts.Length == 3)
				{
					ConfigEntryBase val2 = (from kv in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)((BaseUnityPlugin)Instance).Config
						where kv.Key.Key == parts[1]
						select kv.Value).FirstOrDefault();
					if (val2 == null)
					{
						return "no such key";
					}
					val2.SetSerializedValue(parts[2]);
				}
				return string.Join("\n", ((IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)((BaseUnityPlugin)Instance).Config).Select((KeyValuePair<ConfigDefinition, ConfigEntryBase> kv) => kv.Key.Key + "=" + kv.Value.GetSerializedValue()));
			case "hud":
				if (!((Object)(object)WardHud.Instance == (Object)null))
				{
					return string.Format("visible={0} rows={1} tops={2} text={3}", WardHud.Instance.Visible, WardHud.Instance.RowCount, string.Join(",", WardHud.Instance.RowTops.Select((float t) => t.ToString("0"))), WardHud.Instance.CurrentText);
				}
				return "no hud";
			case "pins":
				return string.Join("\n", WardPins.Describe()) + $"\ncount={WardPins.Count}";
			case "board":
				return WardBoard.Describe();
			case "pause":
				return Interaction.TogglePause(localPlayer) ?? "toggled";
			case "startnow":
			{
				WardChallenge wardChallenge3 = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				object obj;
				if (!((Object)(object)wardChallenge3 == (Object)null))
				{
					obj = Interaction.StartNow(wardChallenge3);
					if (obj == null)
					{
						return "not in countdown";
					}
				}
				else
				{
					obj = "no ward nearby";
				}
				return (string)obj;
			}
			case "hover":
			{
				WardChallenge wardChallenge4 = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if (!((Object)(object)wardChallenge4 == (Object)null))
				{
					return ((Component)wardChallenge4).GetComponent<PrivateArea>().GetHoverText().Replace("\n", " / ");
				}
				return "no ward nearby";
			}
			case "interact":
			{
				WardChallenge wardChallenge7 = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if ((Object)(object)wardChallenge7 == (Object)null)
				{
					return "no ward nearby";
				}
				PrivateArea component = ((Component)wardChallenge7).GetComponent<PrivateArea>();
				return "result=" + component.Interact((Humanoid)(object)localPlayer, false, false) + " hover=" + component.GetHoverText().Replace("\n", " / ");
			}
			case "setcreator":
			{
				WardChallenge wardChallenge = WardChallenge.Nearest(((Component)localPlayer).transform.position);
				if ((Object)(object)wardChallenge == (Object)null)
				{
					return "no ward nearby";
				}
				ZDO zDO = ((Component)wardChallenge).GetComponent<ZNetView>().GetZDO();
				zDO.Set(ZDOVars.s_creator, long.Parse(parts[1]));
				zDO.Set(ZDOVars.s_creatorIndex, -1, false);
				return $"creator={zDO.GetLong(ZDOVars.s_creator, 0L)}";
			}
			case "hudrects":
			{
				Hud instance = Hud.instance;
				MessageHud instance2 = MessageHud.instance;
				List<(string, RectTransform)> list = new List<(string, RectTransform)>();
				Minimap instance3 = Minimap.instance;
				object item;
				if (instance3 == null)
				{
					item = null;
				}
				else
				{
					GameObject smallRoot = instance3.m_smallRoot;
					item = ((smallRoot != null) ? smallRoot.GetComponent<RectTransform>() : null);
				}
				list.Add(("minimap", (RectTransform)item));
				list.Add(("statusEffects", instance?.m_statusEffectListRoot));
				object item2;
				if (instance == null)
				{
					item2 = null;
				}
				else
				{
					GameObject eventBar = instance.m_eventBar;
					item2 = ((eventBar != null) ? eventBar.GetComponent<RectTransform>() : null);
				}
				list.Add(("eventBar", (RectTransform)item2));
				object item3;
				if (instance2 == null)
				{
					item3 = null;
				}
				else
				{
					TMP_Text messageText = instance2.m_messageText;
					item3 = ((messageText != null) ? messageText.rectTransform : null);
				}
				list.Add(("messageTopLeft", (RectTransform)item3));
				object item4;
				if (instance2 == null)
				{
					item4 = null;
				}
				else
				{
					TMP_Text messageCenterText = instance2.m_messageCenterText;
					item4 = ((messageCenterText != null) ? messageCenterText.rectTransform : null);
				}
				list.Add(("messageCenter", (RectTransform)item4));
				list.Add(("healthPanel", instance?.m_healthPanel));
				list.Add(("guardianPower", instance?.m_gpRoot));
				object item5;
				if (instance == null)
				{
					item5 = null;
				}
				else
				{
					GameObject buildHud = instance.m_buildHud;
					item5 = ((buildHud != null) ? buildHud.GetComponent<RectTransform>() : null);
				}
				list.Add(("buildHud", (RectTransform)item5));
				object item6;
				if (instance == null)
				{
					item6 = null;
				}
				else
				{
					GameObject saveIcon = instance.m_saveIcon;
					item6 = ((saveIcon != null) ? saveIcon.GetComponent<RectTransform>() : null);
				}
				list.Add(("saveIcon", (RectTransform)item6));
				object item7;
				if (instance == null)
				{
					item7 = null;
				}
				else
				{
					GameObject betaText = instance.m_betaText;
					item7 = ((betaText != null) ? betaText.GetComponent<RectTransform>() : null);
				}
				list.Add(("betaText", (RectTransform)item7));
				List<(string, RectTransform)> list2 = list;
				List<RectTransform> list3 = (((Object)(object)WardHud.Instance != (Object)null) ? WardHud.Instance.RowRects().ToList() : new List<RectTransform>());
				for (int num = 0; num < list3.Count; num++)
				{
					list2.Add(("row" + num, list3[num]));
				}
				StringBuilder stringBuilder = new StringBuilder();
				foreach (var item9 in list2)
				{
					var (text2, rt) = item9;
					if ((Object)(object)rt == (Object)null)
					{
						stringBuilder.AppendLine(text2 + "\tnull");
						continue;
					}
					Rect r = R(rt);
					IEnumerable<string> values = from row in list3
						where (Object)(object)row != (Object)(object)rt && ((Component)row).gameObject.activeInHierarchy && ((Rect)(ref r)).Overlaps(R(row))
						select ((Object)row).name;
					stringBuilder.AppendLine(string.Format("{0}\tactive={1}\tx={2:0}-{3:0}\ty={4:0}-{5:0}\toverlapsRows={6}", text2, ((Component)rt).gameObject.activeInHierarchy, ((Rect)(ref r)).xMin, ((Rect)(ref r)).xMax, ((Rect)(ref r)).yMin, ((Rect)(ref r)).yMax, string.Join(",", values)));
				}
				return stringBuilder.ToString().TrimEnd();
			}
			case "wards":
				return string.Join("\n", from z in OnePerPlayer.WardsOf(localPlayer.GetPlayerID())
					select string.Format("{0} state={1} pos={2}", z.m_uid, (ChallengeState)z.GetInt(StringExtensionMethods.GetStableHashCode("bdw_state"), 0), z.GetPosition())) + $"\ncount={OnePerPlayer.WardsOf(localPlayer.GetPlayerID()).Count} removed={OnePerPlayer.RemovedCount} blocked={OnePerPlayer.BlockedCount}";
			case "reset":
				WorldReset.Schedule("debug");
				return "scheduled";
			default:
				return "unknown: status skip <s> mobs setcompleted <n> setkey <k> removekey <k> sample <n> <tier> <completed> reward <tier> <completed> cfg [key value] reset";
			}
			static Rect R(RectTransform val3)
			{
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				Vector3[] array = (Vector3[])(object)new Vector3[4];
				val3.GetWorldCorners(array);
				return Rect.MinMaxRect(array[0].x, array[0].y, array[2].x, array[2].y);
			}
		}

		public static void TryRegister()
		{
			//IL_0132: 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_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: 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_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			ObjectDB instance2 = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || instance2.m_items == null || instance2.m_items.Count == 0)
			{
				return;
			}
			if ((Object)(object)WardPrefab == (Object)null || (Object)(object)instance.GetPrefab("BaseDefenseWard") == (Object)null)
			{
				GameObject prefab = instance.GetPrefab("guard_stone");
				if ((Object)(object)prefab == (Object)null)
				{
					Log.LogError((object)"Source prefab guard_stone not found");
					return;
				}
				WardPrefab = Object.Instantiate<GameObject>(prefab, _holder.transform);
				((Object)WardPrefab).name = "BaseDefenseWard";
				Piece component = WardPrefab.GetComponent<Piece>();
				component.m_name = "Base Defense Ward";
				component.m_description = "Build it and a wave comes for it. Defend it to earn rewards.";
				WardPrefab.AddComponent<WardChallenge>();
				StaticTarget obj = WardPrefab.AddComponent<StaticTarget>();
				obj.m_primaryTarget = true;
				obj.m_randomTarget = true;
				PrivateArea component2 = WardPrefab.GetComponent<PrivateArea>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.m_name = "Base Defense Ward";
					if ((Object)(object)component2.m_enabledEffect != (Object)null)
					{
						Light[] componentsInChildren = component2.m_enabledEffect.GetComponentsInChildren<Light>(true);
						for (int i = 0; i < componentsInChildren.Length; i++)
						{
							componentsInChildren[i].color = GlowColor;
						}
						ParticleSystem[] componentsInChildren2 = component2.m_enabledEffect.GetComponentsInChildren<ParticleSystem>(true);
						foreach (ParticleSystem obj2 in componentsInChildren2)
						{
							MainModule main = obj2.main;
							((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(GlowColor);
							ColorOverLifetimeModule colorOverLifetime = obj2.colorOverLifetime;
							if (((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled)
							{
								((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(GlowColor);
							}
						}
						Renderer[] componentsInChildren3 = component2.m_enabledEffect.GetComponentsInChildren<Renderer>(true);
						for (int i = 0; i < componentsInChildren3.Length; i++)
						{
							Material[] materials = componentsInChildren3[i].materials;
							foreach (Material val in materials)
							{
								if (!((Object)(object)val == (Object)null))
								{
									if (val.HasProperty("_Color"))
									{
										val.SetColor("_Color", GlowColor);
									}
									if (val.HasProperty("_EmissionColor"))
									{
										val.SetColor("_EmissionColor", GlowColor * 2f);
									}
								}
							}
						}
						GameObject val2 = new GameObject("BaseDefenseWardGlow");
						val2.transform.SetParent(component2.m_enabledEffect.transform, false);
						val2.transform.localPosition = new Vector3(0f, 1.5f, 0f);
						Light obj3 = val2.AddComponent<Light>();
						obj3.type = (LightType)2;
						obj3.color = GlowColor;
						obj3.intensity = 1.2f;
						obj3.range = 6f;
						obj3.shadows = (LightShadows)0;
					}
				}
				instance.m_prefabs.Add(WardPrefab);
				(AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs").GetValue(instance) as Dictionary<int, GameObject>)[StringExtensionMethods.GetStableHashCode("BaseDefenseWard")] = WardPrefab;
				Log.LogInfo((object)"Registered prefab BaseDefenseWard (clone of guard_stone)");
			}
			GameObject itemPrefab = instance2.GetItemPrefab("Hammer");
			PieceTable val3 = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces);
			if ((Object)(object)val3 == (Object)null)
			{
				Log.LogError((object)"Hammer piece table not found");
			}
			else if (!val3.m_pieces.Contains(WardPrefab))
			{
				val3.m_pieces.Add(WardPrefab);
				Log.LogInfo((object)"Added Base Defense Ward to Hammer piece table");
			}
		}
	}
	[HarmonyPatch(typeof(PrivateArea), "UpdateStatus")]
	internal static class PrivateArea_UpdateStatus
	{
		private static void Postfix(PrivateArea __instance)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)__instance).name.StartsWith("BaseDefenseWard") || (Object)(object)__instance.m_model == (Object)null)
			{
				return;
			}
			Material[] materials = ((Renderer)__instance.m_model).materials;
			foreach (Material val in materials)
			{
				if (val.HasProperty("_EmissionColor"))
				{
					val.SetColor("_EmissionColor", Plugin.GlowColor * 2f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetScene_Awake
	{
		private static void Postfix()
		{
			Plugin.TryRegister();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDB_Awake
	{
		private static void Postfix()
		{
			Plugin.TryRegister();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDB_CopyOtherDB
	{
		private static void Postfix()
		{
			Plugin.TryRegister();
		}
	}
	public static class Progress
	{
		public const string CompletedKey = "bdw_completed";

		public const string BuiltKey = "bdw_built";

		public const string WorldStartKey = "bdw_worldstart";

		public const string DeadlineFailedKey = "bdw_deadline_failed";

		public static int Completed()
		{
			string s = default(string);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey("bdw_completed", ref s) && int.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0;
		}

		public static void SetCompleted(int n)
		{
			SetValue("bdw_completed", n.ToString(CultureInfo.InvariantCulture));
		}

		public static void SetValue(string key, string value)
		{
			ZoneSystem.instance.RemoveGlobalKey(key);
			ZoneSystem.instance.SetGlobalKey(key + " " + value);
		}

		public static bool Has(string key)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(key);
			}
			return false;
		}

		public static string[] BossKeys()
		{
			return (from s in Cfg.BossKeys.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim()).ToArray();
		}

		public static int BossTier()
		{
			string[] array = BossKeys();
			int result = 0;
			for (int i = 0; i < array.Length; i++)
			{
				if (Has(array[i]))
				{
					result = i + 1;
				}
			}
			return result;
		}

		public static string Summary()
		{
			return string.Format("completed={0} bossTier={1} keys=[{2}]", Completed(), BossTier(), string.Join(",", BossKeys().Where(Has)));
		}
	}
	public static class WardBoard
	{
		public class Entry
		{
			public ZDOID Id;

			public long Creator;

			public string Name;

			public ChallengeState State;

			public bool Paused;

			public bool Running;

			public float Remaining;

			public Vector3 Pos;

			public float ReceivedAt;

			public float RemainingNow
			{
				get
				{
					if (!Running)
					{
						return Remaining;
					}
					return Mathf.Max(0f, Remaining - (Time.time - ReceivedAt));
				}
			}
		}

		public static readonly List<Entry> Entries = new List<Entry>();

		public static HashSet<long> Online = new HashSet<long>();

		public static float LastUpdate = -1f;

		private const string Rpc = "BDW_Wards";

		private static bool _registered;

		private static float _clockTimer;

		private static float _sendTimer;

		private static readonly int HState = StringExtensionMethods.GetStableHashCode("bdw_state");

		public static bool IsOnline(long playerId)
		{
			return Online.Contains(playerId);
		}

		public static void Tick(float dt)
		{
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				_registered = false;
				return;
			}
			if (!_registered)
			{
				ZRoutedRpc.instance.Register<ZPackage>("BDW_Wards", (Action<long, ZPackage>)RPC_Wards);
				_registered = true;
			}
			if (ZNet.instance.IsServer() && ZDOMan.instance != null)
			{
				_clockTimer += dt;
				_sendTimer += dt;
				if (_clockTimer >= 1f)
				{
					AdvanceUnloaded(_clockTimer);
					_clockTimer = 0f;
				}
				if (_sendTimer >= 2f)
				{
					Broadcast();
					_sendTimer = 0f;
				}
			}
		}

		private static void AdvanceUnloaded(float dt)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			HashSet<long> hashSet = OnlinePlayerIds();
			foreach (ZDO item in AllWardZdos())
			{
				ChallengeState challengeState = (ChallengeState)item.GetInt(HState, 0);
				if ((challengeState == ChallengeState.Countdown || challengeState == ChallengeState.Resting) && !item.GetBool(WardChallenge.HPaused, false) && hashSet.Contains(item.GetLong(ZDOVars.s_creator, 0L)) && !IsSimulated(item.GetPosition()))
				{
					int num = ((challengeState == ChallengeState.Countdown) ? WardChallenge.HElapsed : WardChallenge.HRestElapsed);
					item.Set(num, item.GetFloat(num, 0f) + dt);
				}
			}
		}

		private static bool IsSimulated(Vector3 pos)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNet.instance.IsDedicated() && ZNetScene.InActiveArea(pos, ZNet.instance.GetReferencePosition()))
			{
				return true;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.IsReady() && ZNetScene.InActiveArea(pos, peer.GetRefPos()))
				{
					return true;
				}
			}
			return false;
		}

		private static HashSet<long> OnlinePlayerIds()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			HashSet<long> hashSet = new HashSet<long>();
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				hashSet.Add(Player.m_localPlayer.GetPlayerID());
			}
			foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
			{
				ZDO zDO = ZDOMan.instance.GetZDO(player.m_characterID);
				long num = ((zDO != null) ? zDO.GetLong(ZDOVars.s_playerID, 0L) : 0);
				if (num != 0L)
				{
					hashSet.Add(num);
				}
			}
			return hashSet;
		}

		public static string NameOf(long playerId, ZDO ward)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == playerId)
			{
				return Player.m_localPlayer.GetPlayerName();
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerID() == playerId)
				{
					return allPlayer.GetPlayerName();
				}
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZDOMan.instance != null)
			{
				foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
				{
					ZDO zDO = ZDOMan.instance.GetZDO(player.m_characterID);
					if (zDO != null && zDO.GetLong(ZDOVars.s_playerID, 0L) == playerId && !string.IsNullOrEmpty(player.m_name))
					{
						return player.m_name;
					}
				}
			}
			int num = ((ward != null) ? ward.GetInt(ZDOVars.s_creatorIndex, -1) : (-1));
			List<CrossNetworkUserInfo> list = ZNet.World?.m_playerHistory;
			if (list != null && num >= 0 && num < list.Count && !string.IsNullOrEmpty(list[num].m_displayName))
			{
				return list[num].m_displayName;
			}
			return "Viking";
		}

		public static List<ZDO> AllWardZdos()
		{
			List<ZDO> list = new List<ZDO>();
			int num = 0;
			while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("BaseDefenseWard", list, ref num))
			{
			}
			return list;
		}

		private static void Broadcast()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			HashSet<long> hashSet = OnlinePlayerIds();
			ZPackage val = new ZPackage();
			val.Write(hashSet.Count);
			foreach (long item in hashSet)
			{
				val.Write(item);
			}
			List<ZDO> list = AllWardZdos();
			val.Write(list.Count);
			foreach (ZDO item2 in list)
			{
				ChallengeState challengeState = (ChallengeState)item2.GetInt(HState, 0);
				bool flag = item2.GetBool(WardChallenge.HPaused, false);
				long num = item2.GetLong(ZDOVars.s_creator, 0L);
				float num2 = 0f;
				bool flag2 = false;
				switch (challengeState)
				{
				case ChallengeState.Idle:
				case ChallengeState.Countdown:
					num2 = Cfg.CountdownMinutes.Value * 60f - item2.GetFloat(WardChallenge.HElapsed, 0f);
					flag2 = !flag && hashSet.Contains(num);
					break;
				case ChallengeState.Resting:
					num2 = item2.GetFloat(WardChallenge.HRestFor, 0f) - item2.GetFloat(WardChallenge.HRestElapsed, 0f);
					flag2 = !flag && hashSet.Contains(num);
					break;
				case ChallengeState.Active:
					num2 = Cfg.WaveTimeLimitMinutes.Value * 60f - item2.GetFloat(WardChallenge.HWaveElapsed, 0f);
					flag2 = IsSimulated(item2.GetPosition());
					break;
				}
				val.Write(item2.m_uid);
				val.Write(num);
				val.Write(NameOf(num, item2));
				val.Write((int)challengeState);
				val.Write(flag);
				val.Write(flag2);
				val.Write(Mathf.Max(0f, num2));
				val.Write(item2.GetPosition());
			}
			val.SetPos(0);
			RPC_Wards(0L, val);
			val.SetPos(0);
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "BDW_Wards", new object[1] { val });
		}

		private static void RPC_Wards(long sender, ZPackage pkg)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			HashSet<long> hashSet = new HashSet<long>();
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				hashSet.Add(pkg.ReadLong());
			}
			Online = hashSet;
			Entries.Clear();
			int num2 = pkg.ReadInt();
			for (int j = 0; j < num2; j++)
			{
				Entries.Add(new Entry
				{
					Id = pkg.ReadZDOID(),
					Creator = pkg.ReadLong(),
					Name = pkg.ReadString(),
					State = (ChallengeState)pkg.ReadInt(),
					Paused = pkg.ReadBool(),
					Running = pkg.ReadBool(),
					Remaining = pkg.ReadSingle(),
					Pos = pkg.ReadVector3(),
					ReceivedAt = Time.time
				});
			}
			LastUpdate = Time.time;
		}

		public static bool IsRunning(ChallengeState s)
		{
			if (s != ChallengeState.Idle && s != ChallengeState.Countdown && s != ChallengeState.Active)
			{
				return s == ChallengeState.Resting;
			}
			return true;
		}

		public static string Describe()
		{
			return string.Join("\n", Entries.Select((Entry e) => $"{e.Id} creator={e.Creator} name={e.Name} state={e.State} paused={e.Paused} running={e.Running} remaining={e.RemainingNow:0} pos={e.Pos}")) + $"\ncount={Entries.Count} age={((LastUpdate < 0f) ? (-1f) : (Time.time - LastUpdate)):0.0}";
		}
	}
	public enum ChallengeState
	{
		Idle,
		Countdown,
		Active,
		Won,
		Lost,
		Cancelled,
		Resting
	}
	public class WardChallenge : MonoBehaviour
	{
		private static readonly int HState = StringExtensionMethods.GetStableHashCode("bdw_state");

		public static readonly int HElapsed = StringExtensionMethods.GetStableHashCode("bdw_elapsed");

		public static readonly int HWaveElapsed = StringExtensionMethods.GetStableHashCode("bdw_waveelapsed");

		public static readonly int HRestElapsed = StringExtensionMethods.GetStableHashCode("bdw_restelapsed");

		public static readonly int HRestFor = StringExtensionMethods.GetStableHashCode("bdw_restfor");

		public static readonly int HPaused = StringExtensionMethods.GetStableHashCode("bdw_paused");

		private static readonly int HTier = StringExtensionMethods.GetStableHashCode("bdw_tier");

		private static readonly int HMobCount = StringExtensionMethods.GetStableHashCode("bdw_mobcount");

		public static readonly int HMobWard = StringExtensionMethods.GetStableHashCode("bdw_ward");

		private static readonly Random Rng = new Random();

		public static readonly List<WardChallenge> All = new List<WardChallenge>();

		private ZNetView _nview;

		private WearNTear _wnt;

		private readonly Queue<string> _toSpawn = new Queue<string>();

		private float _spawnTimer;

		private string _id;

		private double _lastTick = -1.0;

		public ChallengeState State => (ChallengeState)_nview.GetZDO().GetInt(HState, 0);

		private double Now => ZNet.instance.GetTimeSeconds();

		public int Tier => _nview.GetZDO().GetInt(HTier, 0);

		public float Elapsed => _nview.GetZDO().GetFloat(HElapsed, 0f);

		public float WaveElapsed => _nview.GetZDO().GetFloat(HWaveElapsed, 0f);

		public float RestElapsed => _nview.GetZDO().GetFloat(HRestElapsed, 0f);

		public float RestFor => _nview.GetZDO().GetFloat(HRestFor, 0f);

		public bool Paused => _nview.GetZDO().GetBool(HPaused, false);

		public ZNetView View => _nview;

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			if (!((Object)(object)_nview == (Object)null) && _nview.IsValid())
			{
				_wnt = ((Component)this).GetComponent<WearNTear>();
				if ((Object)(object)_wnt != (Object)null)
				{
					_wnt.m_onDestroyed = (Action)Delegate.Combine(_wnt.m_onDestroyed, new Action(OnDestroyed));
				}
				_id = ((object)Unsafe.As<ZDOID, ZDOID>(ref _nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString();
				_nview.Register("BDW_StartNow", (Action<long>)RPC_StartNow);
				_nview.Register("BDW_TogglePause", (Action<long>)RPC_TogglePause);
				All.Add(this);
				((MonoBehaviour)this).InvokeRepeating("Tick", 1f, 1f);
			}
		}

		private void OnDestroy()
		{
			All.Remove(this);
		}

		private void SetState(ChallengeState s)
		{
			_nview.GetZDO().Set(HState, (int)s, false);
		}

		public string Status()
		{
			ZDO zDO = _nview.GetZDO();
			return $"state={State} tier={Tier} owner={_nview.IsOwner()} paused={Paused} hp={(((Object)(object)_wnt != (Object)null) ? (_wnt.GetHealthPercentage() * 100f) : (-1f)):0}% elapsed={Elapsed:0}s " + $"waveElapsed={WaveElapsed:0}s rest={RestElapsed:0}/{RestFor:0}s alive={AliveMobs().Count} pending={_toSpawn.Count} planned={zDO.GetInt(HMobCount, 0)}";
		}

		private void RPC_StartNow(long sender)
		{
			if (_nview.IsOwner() && State == ChallengeState.Countdown)
			{
				Plugin.Log.LogInfo((object)("Wave started early by request: ward=" + _id));
				StartWave();
			}
		}

		private void RPC_TogglePause(long sender)
		{
			if (_nview.IsOwner() && State != ChallengeState.Active)
			{
				bool flag = !Paused;
				_nview.GetZDO().Set(HPaused, flag);
				Plugin.Log.LogInfo((object)("Ward " + (flag ? "paused" : "resumed") + ": ward=" + _id));
				Say(flag ? "Base Defense Ward paused." : "Base Defense Ward resumed.");
			}
		}

		public void RequestStartNow()
		{
			_nview.InvokeRPC("BDW_StartNow", Array.Empty<object>());
		}

		public void RequestTogglePause()
		{
			_nview.InvokeRPC("BDW_TogglePause", Array.Empty<object>());
		}

		private void Tick()
		{
			if (!_nview.IsValid() || !_nview.IsOwner() || (Object)(object)ZNet.instance == (Object)null)
			{
				_lastTick = -1.0;
				return;
			}
			ZDO zDO = _nview.GetZDO();
			float num = ((_lastTick < 0.0) ? 0f : Mathf.Clamp((float)(Now - _lastTick), 0f, 2f));
			_lastTick = Now;
			if (Paused && State != ChallengeState.Active)
			{
				num = 0f;
			}
			if ((State == ChallengeState.Countdown || State == ChallengeState.Resting) && !WardBoard.IsOnline(zDO.GetLong(ZDOVars.s_creator, 0L)))
			{
				num = 0f;
			}
			switch (State)
			{
			case ChallengeState.Won:
				StartRest();
				break;
			case ChallengeState.Resting:
				zDO.Set(HRestElapsed, RestElapsed + num);
				if (RestElapsed >= RestFor)
				{
					zDO.Set(HElapsed, 0f);
					SetState(ChallengeState.Countdown);
					Say($"The ward stirs. The next wave arrives in {Cfg.CountdownMinutes.Value:0.#} minutes.");
				}
				break;
			case ChallengeState.Idle:
				zDO.Set(HElapsed, 0f);
				SetState(ChallengeState.Countdown);
				if (!Progress.Has("bdw_built"))
				{
					ZoneSystem.instance.SetGlobalKey("bdw_built");
				}
				Say($"Base Defense Ward armed. The wave arrives in {Cfg.CountdownMinutes.Value:0.#} minutes.");
				break;
			case ChallengeState.Countdown:
				zDO.Set(HElapsed, Elapsed + num);
				if (Elapsed >= Cfg.CountdownMinutes.Value * 60f)
				{
					StartWave();
				}
				break;
			case ChallengeState.Active:
			{
				zDO.Set(HWaveElapsed, WaveElapsed + num);
				PumpSpawns();
				List<Character> list = AliveMobs();
				DirectMobs(list);
				bool flag = WaveElapsed >= Cfg.WaveTimeLimitMinutes.Value * 60f;
				if (_toSpawn.Count == 0 && list.Count == 0)
				{
					Win("Wave destroyed");
				}
				else if (flag)
				{
					Despawn(list);
					Win("Ward held until dawn");
				}
				break;
			}
			case ChallengeState.Lost:
			case ChallengeState.Cancelled:
				break;
			}
		}

		private void StartWave()
		{
			ZDO zDO = _nview.GetZDO();
			int num = Progress.BossTier();
			int num2 = Progress.Completed();
			int count = Mathf.Clamp(Cfg.BaseMobCount.Value + Cfg.MobsPerCompletedChallenge.Value * num2, 1, Cfg.MaxMobCount.Value);
			_toSpawn.Clear();
			foreach (string item in MobPool.Sample(count, num, num2, Rng))
			{
				_toSpawn.Enqueue(item);
			}
			zDO.Set(HTier, num, false);
			zDO.Set(HMobCount, _toSpawn.Count, false);
			zDO.Set(HWaveElapsed, 0f);
			SetState(ChallengeState.Active);
			Plugin.Log.LogInfo((object)string.Format("Wave start: ward={0} tier={1} completed={2} mobs={3}", _id, num, num2, string.Join(",", _toSpawn)));
			Say("The wave is here! Defend the ward!");
		}

		private void PumpSpawns()
		{
			if (_toSpawn.Count != 0)
			{
				_spawnTimer -= 1f;
				if (!(_spawnTimer > 0f))
				{
					_spawnTimer = Cfg.SpawnIntervalSeconds.Value;
					Spawn(_toSpawn.Dequeue());
				}
			}
		}

		private void Spawn(string prefabName)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: 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_00e7: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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)
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Missing mob prefab " + prefabName));
				return;
			}
			Vector3 val = ((Component)this).transform.position;
			float num2 = default(float);
			for (int i = 0; i < 10; i++)
			{
				float num = (float)(Rng.NextDouble() * Math.PI * 2.0);
				Vector3 val2 = ((Component)this).transform.position + new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * Cfg.SpawnRadius.Value;
				if (ZoneSystem.instance.GetGroundHeight(val2, ref num2) && num2 > ZoneSystem.instance.m_waterLevel)
				{
					val2.y = num2 + 0.3f;
					val = val2;
					break;
				}
			}
			Vector3 val3 = val;
			Vector3 val4 = ((Component)this).transform.position - val;
			GameObject obj = Object.Instantiate<GameObject>(prefab, val3, Quaternion.LookRotation(((Vector3)(ref val4)).normalized));
			ZNetView component = obj.GetComponent<ZNetView>();
			if (component != null)
			{
				ZDO zDO = component.GetZDO();
				if (zDO != null)
				{
					zDO.Set(HMobWard, _id);
				}
			}
			MonsterAI component2 = obj.GetComponent<MonsterAI>();
			if ((Object)(object)component2 != (Object)null)
			{
				((BaseAI)component2).SetHuntPlayer(true);
				((BaseAI)component2).Alert();
				if (component != null)
				{
					ZDO zDO2 = component.GetZDO();
					if (zDO2 != null)
					{
						zDO2.Set(ZDOVars.s_despawnInDay, false);
					}
				}
			}
			Plugin.Log.LogInfo((object)$"Spawned {prefabName} at {val}");
		}

		public List<Character> AliveMobs()
		{
			List<Character> list = new List<Character>();
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsDead() && !allCharacter.IsPlayer())
				{
					ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO().GetString(HMobWard, "") == _id)
					{
						list.Add(allCharacter);
					}
				}
			}
			return list;
		}

		private void DirectMobs(List<Character> mobs)
		{
			StaticTarget component = ((Component)this).GetComponent<StaticTarget>();
			List<Player> allPlayers = Player.GetAllPlayers();
			foreach (Character c in mobs)
			{
				MonsterAI component2 = ((Component)c).GetComponent<MonsterAI>();
				if ((Object)(object)component2 == (Object)null)
				{
					continue;
				}
				bool flag = allPlayers.Any((Player p) => !((Character)p).IsDead() && !p.InShelter() && Vector3.Distance(((Component)p).transform.position, ((Component)c).transform.position) <= Cfg.PlayerHuntRange.Value);
				((BaseAI)component2).SetHuntPlayer(flag);
				if (!flag && (Object)(object)component != (Object)null)
				{
					object? value = AccessTools.Field(typeof(MonsterAI), "m_targetStatic").GetValue(component2);
					object? obj = ((value is StaticTarget) ? value : null);
					object? value2 = AccessTools.Field(typeof(MonsterAI), "m_targetCreature").GetValue(component2);
					Character val = (Character)((value2 is Character) ? value2 : null);
					if ((Object)obj == (Object)null && (Object)(object)val == (Object)null)
					{
						AccessTools.Field(typeof(MonsterAI), "m_targetStatic").SetValue(component2, component);
					}
					if (!((BaseAI)component2).IsAlerted())
					{
						((BaseAI)component2).Alert();
					}
				}
			}
		}

		private void Despawn(List<Character> mobs)
		{
			foreach (Character mob in mobs)
			{
				ZNetView component = ((Component)mob).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					component.Destroy();
				}
			}
		}

		private void Win(string why)
		{
			int num = Progress.Completed();
			List<(string, int)> list = MobPool.RewardFor(Tier, num);
			Progress.SetCompleted(num + 1);
			foreach (var (prefab, amount) in list)
			{
				DropReward(prefab, amount);
			}
			Plugin.Log.LogInfo((object)string.Format("Challenge won ({0}): ward={1} completed->{2} reward={3}", why, _id, num + 1, string.Join(",", list.Select<(string, int), string>(((string prefab, int amount) r) => r.prefab + "x" + r.amount))));
			Say("Ward defended! " + why + ". Rewards have been dropped at the ward.");
			StartRest();
		}

		private void StartRest()
		{
			float num = Mathf.Max(0f, Cfg.RestMinMinutes.Value);
			float num2 = Mathf.Max(num, Cfg.RestMaxMinutes.Value);
			float num3 = (num + (float)Rng.NextDouble() * (num2 - num)) * 60f;
			ZDO zDO = _nview.GetZDO();
			zDO.Set(HRestFor, num3);
			zDO.Set(HRestElapsed, 0f);
			SetState(ChallengeState.Resting);
			Plugin.Log.LogInfo((object)$"Ward resting for {num3:0}s: ward={_id}");
		}

		private void DropReward(string prefab, int amount)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			ItemDrop val = ((prefab2 != null) ? prefab2.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val == (Object)null))
			{
				val.m_itemData.m_dropPrefab = prefab2;
				int num = Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize);
				Vector3 val2 = default(Vector3);
				while (amount > 0)
				{
					int num2 = Mathf.Min(amount, num);
					amount -= num2;
					((Vector3)(ref val2))..ctor((float)Rng.NextDouble() * 2f - 1f, 1.5f, (float)Rng.NextDouble() * 2f - 1f);
					ItemDrop.DropItem(val.m_itemData.Clone(), num2, ((Component)this).transform.position + val2, Quaternion.identity);
				}
			}
		}

		private void OnDestroyed()
		{
			if (!_nview.IsValid() || !_nview.IsOwner())
			{
				return;
			}
			switch (State)
			{
			case ChallengeState.Active:
				SetState(ChallengeState.Lost);
				Despawn(AliveMobs());
				Plugin.Log.LogInfo((object)("Challenge lost: ward=" + _id + " destroyed during wave"));
				Say("The ward has fallen. Challenge lost.");
				if (Cfg.DeleteWorldOnLoss.Value)
				{
					WorldReset.Schedule("ward destroyed");
				}
				break;
			case ChallengeState.Countdown:
				Plugin.Log.LogInfo((object)("Challenge cancelled: ward=" + _id + " removed before the wave"));
				break;
			}
		}

		public static void Say(string text)
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.MessageAll((MessageType)2, text);
			}
		}

		public static WardChallenge Nearest(Vector3 pos, float r = 500f)
		{
			//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)
			return (from w in All
				where (Object)(object)w != (Object)null && Vector3.Distance(((Component)w).transform.position, pos) <= r
				orderby (w.State != ChallengeState.Countdown && w.State != ChallengeState.Active && w.State != ChallengeState.Idle) ? 1 : 0, Vector3.Distance(((Component)w).transform.position, pos)
				select w).FirstOrDefault();
		}
	}
	public class WardHud : MonoBehaviour
	{
		private class Row
		{
			public GameObject Go;

			public RectTransform Rect;

			public Image Icon;

			public TMP_Text Text;
		}

		public static WardHud Instance;

		private const float RowWidth = 300f;

		private const float RowHeight = 36f;

		private const float RowGap = 4f;

		private RectTransform _holder;

		private readonly List<Row> _rows = new List<Row>();

		private readonly List<string> _lines = new List<string>();

		private Sprite _sprite;

		private Vector2 _anchor;

		private float _scanTimer;

		private TMP_Text _template;

		public string CurrentText => string.Join(" | ", _lines);

		public int RowCount => _lines.Count;

		public bool Visible => _lines.Count > 0;

		public IEnumerable<float> RowTops => from r in _rows.Take(_lines.Count)
			select r.Rect.anchoredPosition.y;

		public IEnumerable<RectTransform> RowRects()
		{
			return from r in _rows.Take(_lines.Count)
				select r.Rect;
		}

		public static void Create(Hud hud)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00c1: 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)
			if (!((Object)(object)Instance != (Object)null) && !((Object)(object)hud == (Object)null) && !((Object)(object)hud.m_rootObject == (Object)null))
			{
				GameObject val = new GameObject("BaseDefenseWardHud", new Type[1] { typeof(RectTransform) });
				Minimap instance = Minimap.instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					GameObject smallRoot = instance.m_smallRoot;
					obj = ((smallRoot != null) ? smallRoot.GetComponent<RectTransform>() : null);
				}
				RectTransform val2 = (RectTransform)obj;
				val.transform.SetParent(((Object)(object)val2 != (Object)null) ? ((Transform)val2).parent : hud.m_rootObject.transform, false);
				RectTransform component = val.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0f, 0f);
				component.anchorMax = new Vector2(1f, 1f);
				Vector2 offsetMin = (component.offsetMax = Vector2.zero);
				component.offsetMin = offsetMin;
				Instance = val.AddComponent<WardHud>();
				Instance._holder = component;
				Instance._template = hud.m_healthText;
				Instance.Reposition();
			}
		}

		private Row MakeRow()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0063: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Row" + _rows.Count, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 0f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.pivot = new Vector2(1f, 1f);
			component.sizeDelta = new Vector2(300f, 36f);
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.45f);
			GameObject val3 = new GameObject("Icon", new Type[1] { typeof(RectTransform) });
			val3.transform.SetParent((Transform)(object)component, false);
			RectTransform component2 = val3.GetComponent<RectTransform>();
			((Vector2)(ref val2))..ctor(0f, 0.5f);
			component2.anchorMax = val2;
			component2.anchorMin = val2;
			component2.pivot = new Vector2(0f, 0.5f);
			component2.sizeDelta = new Vector2(32f, 32f);
			component2.anchoredPosition = new Vector2(4f, 0f);
			Image val4 = val3.AddComponent<Image>();
			val4.preserveAspect = true;
			GameObject val5 = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val5.transform.SetParent((Transform)(object)component, false);
			RectTransform component3 = val5.GetComponent<RectTransform>();
			component3.anchorMin = new Vector2(0f, 0f);
			component3.anchorMax = new Vector2(1f, 1f);
			component3.offsetMin = new Vector2(42f, 2f);
			component3.offsetMax = new Vector2(-6f, -2f);
			TextMeshProUGUI val6 = val5.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)_template != (Object)null)
			{
				((TMP_Text)val6).font = _template.font;
				((TMP_Text)val6).fontSharedMaterial = _template.fontSharedMaterial;
			}
			((TMP_Text)val6).fontSize = 16f;
			((Graphic)val6).color = Color.white;
			((TMP_Text)val6).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)val6).enableWordWrapping = false;
			((TMP_Text)val6).overflowMode = (TextOverflowModes)1;
			Row row = new Row
			{
				Go = val,
				Rect = component,
				Icon = val4,
				Text = (TMP_Text)(object)val6
			};
			_rows.Add(row);
			return row;
		}

		private void Reposition()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject smallRoot = instance.m_smallRoot;
				obj = ((smallRoot != null) ? smallRoot.GetComponent<RectTransform>() : null);
			}
			RectTransform val = (RectTransform)obj;
			Rect rect;
			if ((Object)(object)val == (Object)null)
			{
				rect = _holder.rect;
				float num = ((Rect)(ref rect)).width - 20f;
				rect = _holder.rect;
				_anchor = new Vector2(num, ((Rect)(ref rect)).height - 160f);
			}
			else
			{
				Vector3[] array = (Vector3[])(object)new Vector3[4];
				val.GetWorldCorners(array);
				Vector3 val2 = ((Transform)_holder).InverseTransformPoint(array[3]);
				Vector2 val3 = new Vector2(val2.x, val2.y);
				rect = _holder.rect;
				Vector2 val4 = val3 + ((Rect)(ref rect)).size * _holder.pivot;
				_anchor = val4 + new Vector2(0f, -8f);
			}
		}

		private void Update()
		{
			//IL_00d6: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			_lines.Clear();
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ZNet.instance != (Object)null)
			{
				_scanTimer -= Time.deltaTime;
				if (_scanTimer <= 0f)
				{
					_scanTimer = 2f;
					Reposition();
					if ((Object)(object)_sprite == (Object)null)
					{
						GameObject wardPrefab = Plugin.WardPrefab;
						_sprite = ((wardPrefab == null) ? null : wardPrefab.GetComponent<Piece>()?.m_icon);
					}
				}
				BuildLines();
			}
			for (int i = 0; i < _lines.Count; i++)
			{
				Row row = ((i < _rows.Count) ? _rows[i] : MakeRow());
				if (!row.Go.activeSelf)
				{
					row.Go.SetActive(true);
				}
				row.Rect.anchoredPosition = _anchor - new Vector2(0f, (float)i * 40f);
				if ((Object)(object)row.Icon.sprite == (Object)null)
				{
					row.Icon.sprite = _sprite;
				}
				row.Text.text = _lines[i];
			}
			for (int j = _lines.Count; j < _rows.Count; j++)
			{
				if (_rows[j].Go.activeSelf)
				{
					_rows[j].Go.SetActive(false);
				}
			}
		}

		private void BuildLines()
		{
			long me = Player.m_localPlayer.GetPlayerID();
			WardBoard.Entry entry = (from e in WardBoard.Entries
				where e.Creator == me
				orderby (!WardBoard.IsRunning(e.State)) ? 1 : 0
				select e).FirstOrDefault();
			if (entry != null)
			{
				_lines.Add(Line(entry, "Your ward"));
			}
			foreach (WardBoard.Entry item in from e in WardBoard.Entries
				where e.Creator != me && WardBoard.IsRunning(e.State)
				orderby ((ZDOID)(ref e.Id)).ID
				select e)
			{
				_lines.Add(Line(item, item.Name));
			}
			string s = default(string);
			if (_lines.Count == 0 && Cfg.BuildDeadlineMinutes.Value > 0f && !Progress.Has("bdw_built") && !Progress.Has("bdw_deadline_failed") && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey("bdw_worldstart", ref s) && long.TryParse(s, out var result))
			{
				_lines.Add("Build ward in " + Fmt((double)Cfg.BuildDeadlineMinutes.Value * 60.0 - (ZNet.instance.GetTimeSeconds() - (double)result)));
			}
		}

		private static string Line(WardBoard.Entry e, string who)
		{
			string text = (e.Paused ? " (paused)" : "");
			switch (e.State)
			{
			case ChallengeState.Idle:
			case ChallengeState.Countdown:
				return who + ": wave in " + Fmt(e.RemainingNow) + text;
			case ChallengeState.Active:
				return who + ": hold " + Fmt(e.RemainingNow);
			case ChallengeState.Won:
			case ChallengeState.Resting:
				return who + ": defended, next in " + Fmt(e.RemainingNow) + text;
			case ChallengeState.Lost:
				return who + ": lost";
			default:
				return null;
			}
		}

		private static string Fmt(double seconds)
		{
			if (seconds < 0.0)
			{
				seconds = 0.0;
			}
			TimeSpan timeSpan = TimeSpan.FromSeconds(seconds);
			if (!(timeSpan.TotalHours >= 1.0))
			{
				return $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
			}
			return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
		}
	}
	public static class WardPins
	{
		private static readonly Dictionary<ZDOID, PinData> _pins = new Dictionary<ZDOID, PinData>();

		private static float _timer;

		public static int Count => _pins.Count;

		public static IEnumerable<string> Describe()
		{
			return _pins.Select((KeyValuePair<ZDOID, PinData> kv) => $"{kv.Key} {kv.Value.m_name} {kv.Value.m_pos}");
		}

		public static void Tick(float dt)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: 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_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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			_timer -= dt;
			if (_timer > 0f)
			{
				return;
			}
			_timer = 2f;
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				Clear();
				return;
			}
			HashSet<ZDOID> running = new HashSet<ZDOID>();
			foreach (WardBoard.Entry entry in WardBoard.Entries)
			{
				if (!WardBoard.IsRunning(entry.State))
				{
					continue;
				}
				running.Add(entry.Id);
				string text = entry.Name + "'s ward";
				if (_pins.TryGetValue(entry.Id, out var value) && value.m_name != text)
				{
					instance.RemovePin(value);
					_pins.Remove(entry.Id);
					value = null;
				}
				if (value == null)
				{
					value = instance.AddPin(entry.Pos, (PinType)3, text, false, false, 0L, default(PlatformUserID));
					GameObject wardPrefab = Plugin.WardPrefab;
					Sprite val = ((wardPrefab == null) ? null : wardPrefab.GetComponent<Piece>()?.m_icon);
					if ((Object)(object)val != (Object)null)
					{
						value.m_icon = val;
					}
					_pins[entry.Id] = value;
				}
				else
				{
					value.m_pos = entry.Pos;
				}
			}
			foreach (ZDOID item in _pins.Keys.Where((ZDOID id) => !running.Contains(id)).ToList())
			{
				instance.RemovePin(_pins[item]);
				_pins.Remove(item);
			}
		}

		public static void Clear()
		{
			if ((Object)(object)Minimap.instance != (Object)null)
			{
				foreach (PinData value in _pins.Values)
				{
					Minimap.instance.RemovePin(value);
				}
			}
			_pins.Clear();
		}
	}
	[HarmonyPatch(typeof(Hud), "Awake")]
	internal static class Hud_Awake
	{
		private static void Postfix(Hud __instance)
		{
			WardHud.Create(__instance);
		}
	}
	public static class WorldReset
	{
		public const float DelaySeconds = 10f;

		public static bool Pending { get; private set; }

		public static void Schedule(string reason)
		{
			if (!Pending && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				Pending = true;
				Plugin.Log.LogWarning((object)$"World reset scheduled in {10f}s: {reason}");
				WardChallenge.Say($"This world will be deleted in {10f:0} seconds.");
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Run());
			}
		}

		private static IEnumerator Run()
		{
			yield return (object)new WaitForSeconds(10f);
			World world = ZNet.World;
			string text = world?.m_name;
			FileSource val = (FileSource)((world == null) ? 2 : ((int)world.m_fileSource));
			bool dedicated = ZNet.instance.IsDedicated();
			Plugin.Log.LogWarning((object)$"Deleting world '{text}' ({val}) and shutting down");
			AccessTools.Method(typeof(Game), "Shutdown", (Type[])null, (Type[])null).Invoke(Game.instance, new object[1] { false });
			if (!string.IsNullOrEmpty(text))
			{
				World.RemoveWorld(text, val);
			}
			SaveSystem.InvalidateCache((SaveDataType)0);
			yield return null;
			if (dedicated)
			{
				Application.Quit();
			}
			else
			{
				SystemResourceManager.FastLoadScene(Game.instance.m_startScene, (LoadSceneMode)0);
			}
		}
	}
}