Decompiled source of HearthAndVillage v0.12.6

TXC.Village.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using TXC.Village.Commands;
using TXC.Village.Core;
using TXC.Village.Expeditions;
using TXC.Village.Farming;
using TXC.Village.Fishing;
using TXC.Village.Food;
using TXC.Village.Forestry;
using TXC.Village.Gui;
using TXC.Village.Jobs;
using TXC.Village.Ranching;
using TXC.Village.Village;
using TXC.Village.Villagers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TXC.Village")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+e9e0d1290b81d1459f656b3b88273de415810c11")]
[assembly: AssemblyProduct("TXC.Village")]
[assembly: AssemblyTitle("TXC.Village")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TXC.Village
{
	internal static class Perf
	{
		private class Acc
		{
			public double Ms;

			public double Max;

			public long N;
		}

		private static readonly Dictionary<string, Acc> Accs = new Dictionary<string, Acc>();

		private static readonly double TickMs = 1000.0 / (double)Stopwatch.Frequency;

		private static Stopwatch _clock = Stopwatch.StartNew();

		private static long _frames;

		private static double _frameMs;

		private static double _frameMax;

		public static long Now => Stopwatch.GetTimestamp();

		public static void Add(string key, long start)
		{
			double num = (double)(Stopwatch.GetTimestamp() - start) * TickMs;
			if (!Accs.TryGetValue(key, out var value))
			{
				value = (Accs[key] = new Acc());
			}
			value.Ms += num;
			value.N++;
			if (num > value.Max)
			{
				value.Max = num;
			}
		}

		public static void Frame(float dt)
		{
			_frames++;
			double num = (double)dt * 1000.0;
			_frameMs += num;
			if (num > _frameMax)
			{
				_frameMax = num;
			}
		}

		public static void Reset()
		{
			Accs.Clear();
			_frames = 0L;
			_frameMs = 0.0;
			_frameMax = 0.0;
			_clock = Stopwatch.StartNew();
		}

		public static List<string> PerFrame(int top)
		{
			double num = Math.Max(0.001, _clock.Elapsed.TotalSeconds);
			long num2 = Math.Max(1L, _frames);
			List<string> list = new List<string>();
			double num3 = Accs.Where((KeyValuePair<string, Acc> kv) => !kv.Key.Contains("/")).Sum((KeyValuePair<string, Acc> kv) => kv.Value.Ms);
			list.Add($"The mod {num3 / (double)num2:0.000} ms per frame over {num:0} s ({_frames} frames).");
			foreach (KeyValuePair<string, Acc> item in Accs.OrderByDescending((KeyValuePair<string, Acc> k) => k.Value.Ms).Take(top))
			{
				list.Add(string.Format("    {0} {1:0.000} ms per frame, worst {2:0.0} ms", item.Key.Replace(":", ""), item.Value.Ms / (double)num2, item.Value.Max));
			}
			return list;
		}

		public static List<string> Report()
		{
			double num = Math.Max(0.001, _clock.Elapsed.TotalSeconds);
			List<string> list = new List<string>();
			double num2 = Accs.Where((KeyValuePair<string, Acc> kv) => !kv.Key.Contains("/")).Sum((KeyValuePair<string, Acc> kv) => kv.Value.Ms);
			list.Add($"Over {num:0} s, {_frames} frames. Average frame {((_frames > 0) ? (_frameMs / (double)_frames) : 0.0):0.0} ms ({((_frameMs > 0.0) ? ((double)_frames * 1000.0 / _frameMs) : 0.0):0} fps), worst {_frameMax:0} ms.");
			list.Add($"The mod: {num2 / num:0.00} ms per second ({num2 / Math.Max(1.0, _frameMs) * 100.0:0.0}% of frame time).");
			foreach (KeyValuePair<string, Acc> item in Accs.OrderByDescending((KeyValuePair<string, Acc> k) => k.Value.Ms))
			{
				list.Add($"  {item.Key}: {item.Value.Ms / num:0.000} ms/s, {(double)item.Value.N / num:0.0} calls/s, avg {item.Value.Ms / (double)Math.Max(1L, item.Value.N):0.000} ms, worst {item.Value.Max:0.00} ms");
			}
			return list;
		}
	}
	internal static class Fault
	{
		public const float Every = 60f;

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

		public static void Log(string where, Exception e, Object who = null)
		{
			try
			{
				string key = ((who != (Object)null) ? (where + "#" + who.GetInstanceID()) : where);
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!Next.TryGetValue(key, out var value) || !(realtimeSinceStartup < value))
				{
					if (Next.Count > 500)
					{
						Next.Clear();
					}
					Next[key] = realtimeSinceStartup + 60f;
					ManualLogSource log = VillagePlugin.Log;
					if (log != null)
					{
						log.LogError((object)string.Format("{0} failed{1} (logged once a minute): {2}", where, (who != (Object)null) ? (" for " + who.name) : "", e));
					}
				}
			}
			catch
			{
			}
		}
	}
	internal static class StaticSweep
	{
		[HarmonyPatch(typeof(ZNetScene), "OnDestroy")]
		private static class OnWorldUnload
		{
			private static void Postfix()
			{
				ResetStatics();
				VillagePlugin.Debug("Static caches emptied (world unloaded).");
			}
		}

		public const float Every = 60f;

		private static float _next;

		public static void Tick()
		{
			if (!(Time.time < _next))
			{
				_next = Time.time + 60f;
				Each(reset: false);
			}
		}

		public static void ResetStatics()
		{
			Each(reset: true);
		}

		private static void Each(bool reset)
		{
			Run("VillagerDoors", reset, VillagerDoors.Sweep);
			Run("CookJob", reset, CookJob.Sweep);
			Run("TenderJob", reset, TenderJob.Sweep);
			Run("Stations", reset, Stations.Sweep);
			Run("SorterJob", reset, SorterJob.Sweep);
			Run("KeeperJob", reset, KeeperJob.Sweep);
			Run("SoldierBrain", reset, SoldierBrain.Sweep);
			Run("SoldierFormation", reset, SoldierFormation.Sweep);
			Run("VillagerTargets", reset, VillagerTargets.Sweep);
			Run("VillagerHurtLog", reset, VillagerHurtLog.Sweep);
			Run("VillageHome", reset, VillageHome.Sweep);
			Run("LumberOffline", reset, LumberOffline.Sweep);
			Run("OfflineCatchUp", reset, OfflineCatchUp.Sweep);
		}

		private static void Run(string name, bool reset, Action<bool> sweep)
		{
			try
			{
				sweep(reset);
			}
			catch (Exception e)
			{
				Fault.Log("sweep " + name, e);
			}
		}

		public static void DropDead<K, V>(Dictionary<K, V> d) where K : Object
		{
			DropWhere(d, (K k, V v) => (Object)(object)k == (Object)null);
		}

		public static void DropWhere<K, V>(Dictionary<K, V> d, Func<K, V, bool> stale)
		{
			if (d == null || d.Count == 0)
			{
				return;
			}
			List<K> list = null;
			foreach (KeyValuePair<K, V> item in d)
			{
				if (stale(item.Key, item.Value))
				{
					(list ?? (list = new List<K>())).Add(item.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (K item2 in list)
			{
				d.Remove(item2);
			}
		}
	}
	[BepInPlugin("txc.village", "Hearth and Village", "0.12.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class VillagePlugin : BaseUnityPlugin
	{
		public const string ModGuid = "txc.village";

		public const string ModName = "Hearth and Village";

		public const string ModVersion = "0.12.6";

		private Harmony _harmony;

		private bool _consoleEnabled;

		internal static VillagePlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			VillageConfig.Bind(((BaseUnityPlugin)this).Config);
			BannerPrefab.Hook();
			VillageCamp.Hook();
			VillagerPrefab.Hook();
			ContractItem.Hook();
			ExpeditionBoardPrefab.Hook();
			FieldMarkerPrefab.Hook();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new VillageCommand());
			_harmony = new Harmony("txc.village");
			int num = 0;
			int num2 = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(VillagePlugin).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
						num++;
					}
					catch (Exception ex)
					{
						num2++;
						Log.LogError((object)("Patch " + type.FullName + " failed: " + ex.GetBaseException().Message));
					}
				}
			}
			Log.LogInfo((object)$"Patches: {num} applied, {num2} failed.");
			Log.LogInfo((object)"Hearth and Village 0.12.6 loaded.");
		}

		private void Update()
		{
			Perf.Frame(Time.unscaledDeltaTime);
			Tick("tick VillageNet.Tick", delegate
			{
				VillageNet.Tick(Time.deltaTime);
			});
			Tick("tick VillageMap.Tick", VillageMap.Tick);
			Tick("tick VillagePanel.Tick", VillagePanel.Tick);
			Tick("tick Gui.Book.Tick", Book.Tick);
			Tick("tick VillageLevels.Tick", VillageLevels.Tick);
			Tick("tick Villagers.VillagerFeelings.Tick", VillagerFeelings.Tick);
			Tick("tick Villagers.VillageSocial.FeastVisuals", VillageSocial.FeastVisuals);
			Tick("tick Expeditions.ExpeditionPanel.Tick", ExpeditionPanel.Tick);
			Tick("tick Villagers.VillagerPanel.Tick", VillagerPanel.Tick);
			Tick("tick Village.BedPanel.Tick", BedPanel.Tick);
			Tick("tick Jobs.StationPanel.Tick", StationPanel.Tick);
			Tick("tick Food.PantryPanel.Tick", PantryPanel.Tick);
			Tick("tick Villagers.SoldierFormation.Tick", SoldierFormation.Tick);
			Tick("tick Gui.FormationWheel.Tick", FormationWheel.Tick);
			Tick("tick Forestry.ForestPanel.Tick", ForestPanel.Tick);
			Tick("tick Ranching.PasturePanel.Tick", PasturePanel.Tick);
			Tick("tick StaticSweep.Tick", StaticSweep.Tick);
			Tick("tick VillageKeepAlive.Tick", VillageKeepAlive.Tick);
			Tick("tick Villagers.BodyIndex.Tick", BodyIndex.Tick);
			if (!_consoleEnabled && VillageConfig.EnableConsole.Value && (Object)(object)Console.instance != (Object)null)
			{
				Console.SetConsoleEnabledForThisSession();
				_consoleEnabled = true;
			}
		}

		private static void Tick(string name, Action tick)
		{
			long now = Perf.Now;
			try
			{
				tick();
			}
			catch (Exception e)
			{
				Fault.Log(name, e);
			}
			Perf.Add(name, now);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void Debug(string msg)
		{
			if (VillageConfig.DebugLogging != null && VillageConfig.DebugLogging.Value)
			{
				Log.LogInfo((object)msg);
			}
		}
	}
	internal static class VillageConfig
	{
		public static ConfigEntry<float> VillageRadius;

		public static ConfigEntry<float> MinDistanceBetweenVillages;

		public static ConfigEntry<bool> BedsNeedFire;

		public static ConfigEntry<bool> DebugLogging;

		public static ConfigEntry<string> HealingStaffs;

		public static ConfigEntry<KeyboardShortcut> FormationKey;

		public static ConfigEntry<KeyboardShortcut> HoldKey;

		public static ConfigEntry<int> RecruitBaseCost;

		public static ConfigEntry<float> WandererDays;

		public static ConfigEntry<int> WanderersAtStart;

		public static ConfigEntry<int> WanderersPerBoss;

		public static ConfigEntry<bool> Permadeath;

		public static ConfigEntry<float> InjuryDaysMultiplier;

		public static ConfigEntry<bool> Ageing;

		public static ConfigEntry<bool> OldAgeDeath;

		public static ConfigEntry<bool> MoodEffects;

		public static ConfigEntry<bool> EveningGathering;

		public static ConfigEntry<bool> Births;

		public static ConfigEntry<bool> ShowBubbles;

		public static ConfigEntry<bool> KeepAlive;

		public static ConfigEntry<float> KeepAliveCheckMinutes;

		public static ConfigEntry<float> KeepAliveMinFps;

		public static ConfigEntry<float> KeepAliveMaxDrop;

		public static ConfigEntry<float> KeepAliveRestMinutes;

		public static ConfigEntry<float> ServerStatsLogMinutes;

		public static ConfigEntry<bool> ShowMapPins;

		public static ConfigEntry<bool> ShowAreaWithHammer;

		public static ConfigEntry<bool> EnableConsole;

		private static int _order;

		public static void Bind(ConfigFile config)
		{
			//IL_0394: 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_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0473: Expected O, but got Unknown
			//IL_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Expected O, but got Unknown
			bool num = OldRecruitDefault(config);
			bool flag = OldDefault(config, "Run villages while players are away", "false");
			VillageRadius = Entry(config, "1 - Village", "Village base radius", 40f, "How far (metres, measured flat from the Village Banner) a hamlet reaches; a village adds 15 m, a town 35 m (40 / 55 / 75 by default). Only beds inside count as villager beds. Banners already built show the new size after the area is reloaded.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f));
			MinDistanceBetweenVillages = Entry(config, "1 - Village", "Minimum distance between village banners", 150f, "A new Village Banner can't be placed closer than this to another one (metres), so villages never overlap. 150 m leaves room for both to grow into towns (75 m each); closer ones can stay smaller.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 1000f));
			BedsNeedFire = Entry(config, "1 - Village", "Villager beds need a fire", value: false, "If on, a villager bed must also be warmed by a fire (like sleeping in a player bed). The roof rule always applies.");
			RecruitBaseCost = Entry(config, "2 - Villagers", "Recruit base cost", 20, "Coins for the first recruit at the banner. Later ones cost more (x1, x2, x3, x5, x7, x9, x12, x15, then +4 each).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			if (num && RecruitBaseCost.Value == 10)
			{
				RecruitBaseCost.Value = 20;
				ManualLogSource log = VillagePlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Recruit base cost was the old default 10. Set to the new default 20 (0.11.4 cost curve).");
				}
			}
			WandererDays = Entry(config, "2 - Villagers", "Wanderer every N days", 1.5f, "In-game days between wanderers who come by themselves to a village with a free villager bed. 0 = no wanderers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f));
			WanderersAtStart = Entry(config, "2 - Villagers", "Wanderers before any boss", 2, "How many wanderers come by themselves to each village before any boss is defeated. Recruiting and expedition finds don't count.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20));
			WanderersPerBoss = Entry(config, "2 - Villagers", "Extra wanderers per boss", 2, "Each boss defeated in the world lets this many more wanderers come to each village (Eikthyr, the Elder, Bonemass, Moder, Yagluth, the Queen, Fader).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20));
			Permadeath = Entry(config, "2 - Villagers", "Permadeath", value: false, "Off. A villager who falls is badly hurt and recovers in bed for 1-3 in-game days. On. A villager who falls is dead for good.");
			InjuryDaysMultiplier = Entry(config, "2 - Villagers", "Injury time multiplier", 1f, "Scales how long injured villagers stay in bed (1 = 1-3 in-game days).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f));
			Ageing = Entry(config, "2 - Villagers", "Ageing", value: true, "Villagers grow old. Elders (from 160 days) work a little slower, are frailer, leave heavy work and teach those under their roof. Off. Everyone stays as they are.");
			OldAgeDeath = Entry(config, "2 - Villagers", "Death of old age", value: true, "With ageing on, each villager dies peacefully in bed at their own age (200 to 260 days, at most one a night per village). Off. Elders live on.");
			MoodEffects = Entry(config, "2 - Villagers", "Mood changes work", value: true, "Mood speeds up or slows down work, below 10 villagers stop working, and an unhappy village draws no wanderers or children. Off. Mood is only shown.");
			EveningGathering = Entry(config, "2 - Villagers", "Evening by the fire", value: true, "In the evening villagers gather by a fire in the village before bed.");
			Births = Entry(config, "2 - Villagers", "Children", value: true, "Couples who share a house may have children.");
			HealingStaffs = Entry(config, "4 - Jobs", "Healing staffs", "BDS_DvergerStaffHeal", "Item names (comma separated) of staves that make a soldier a healer, besides any staff with \"Heal\" in its name (e.g. other mods' healing staves).");
			KeepAlive = Entry(config, "5 - Server", "Run villages while players are away", value: true, "Dedicated servers only. While anyone is online, the server runs villages that no player is near, one at a time, so their work goes on. Off. A village only lives while a player is near it.");
			if (flag && !KeepAlive.Value)
			{
				KeepAlive.Value = true;
				ManualLogSource log2 = VillagePlugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"Run villages while players are away was the old default (off). Turned on, the new default (0.12.1).");
				}
			}
			KeepAliveCheckMinutes = Entry(config, "5 - Server", "Check idle villages every", 15f, "Minutes between short visits to a village that had nothing to do, to see whether work has come up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 600f));
			KeepAliveMinFps = Entry(config, "5 - Server", "Lowest server FPS while running a village", 15f, "If the server's frame rate (15 s average) stays under this, or drops more than the share below, for 30 s while it runs a village, it leaves that village and lets it rest. If the server is already this slow with no village, it runs none. 0 = no limit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f));
			KeepAliveMaxDrop = Entry(config, "5 - Server", "Largest frame rate drop from a village (%)", 30f, "A running village may cost at most this share of the server's frame rate with no village (dedicated servers often run at about 30 FPS). 0 = only the lowest FPS counts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 90f));
			KeepAliveRestMinutes = Entry(config, "5 - Server", "Rest after a heavy village (minutes)", 30f, "How long a village the server found too heavy waits before the server runs it again. Too heavy again within 2 hours doubles the rest (at most 4 hours).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 240f));
			ServerStatsLogMinutes = Entry(config, "5 - Server", "Log server stats every (minutes)", 0f, "Writes the server's frame rate, the mod's time per frame, the objects loaded and what the keep-alive runs to the server log this often (the same as txc_village serverstat). 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1440f));
			FormationKey = Local<KeyboardShortcut>(config, "3 - This PC", "Soldier formation key", new KeyboardShortcut((KeyCode)107, Array.Empty<KeyCode>()), "Hold it to open the formation wheel (Free, Column, Shield wall, Ring, Hold / Follow me). Point at one and let go, or click it.");
			HoldKey = Local<KeyboardShortcut>(config, "3 - This PC", "Soldier hold key", new KeyboardShortcut((KeyCode)108, Array.Empty<KeyCode>()), "Soldiers out with you hold their position here (in their formation, facing your way) or follow you again.");
			ShowBubbles = Local(config, "3 - This PC", "Speech bubbles", value: true, "Villagers say what they need (and when they are unhappy) in a bubble over their heads.");
			ShowMapPins = Local(config, "3 - This PC", "Show villages on the map", value: true, "Shows every village as a pin with its name on the map and minimap.");
			ShowAreaWithHammer = Local(config, "3 - This PC", "Show village area with the hammer", value: true, "Draws the village circle on the ground while you hold the hammer near a Village Banner (and while placing one).");
			DebugLogging = Entry(config, "9 - Debug", "Debug logging", value: false, "Writes extra lines about villages and beds to the BepInEx log.");
			EnableConsole = config.Bind<bool>("9 - Debug", "Enable console (this PC only)", false, new ConfigDescription("Turns on the F5 console without the -console launch option. For testing; not synced.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				Order = (_order -= 1)
			} }));
			Ageing.SettingChanged += delegate
			{
				Apply();
			};
			OldAgeDeath.SettingChanged += delegate
			{
				Apply();
			};
			MoodEffects.SettingChanged += delegate
			{
				Apply();
			};
			Apply();
		}

		public static void Apply()
		{
			AgeRules.Enabled = Ageing.Value;
			AgeRules.DeathOn = OldAgeDeath.Value;
			MoodRules.Effects = MoodEffects.Value;
		}

		private static bool OldDefault(ConfigFile config, string key, string oldValue)
		{
			try
			{
				string configFilePath = config.ConfigFilePath;
				if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
				{
					return false;
				}
				string a = "";
				string[] array = File.ReadAllLines(configFilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.StartsWith("# Default value:"))
					{
						a = text.Substring("# Default value:".Length).Trim();
					}
					else if (text.StartsWith("["))
					{
						a = "";
					}
					else if (text.StartsWith(key) && text.Contains("="))
					{
						return string.Equals(a, oldValue, StringComparison.OrdinalIgnoreCase) && string.Equals(text.Substring(text.IndexOf('=') + 1).Trim(), oldValue, StringComparison.OrdinalIgnoreCase);
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool OldRecruitDefault(ConfigFile config)
		{
			try
			{
				string configFilePath = config.ConfigFilePath;
				if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
				{
					return false;
				}
				string text = "";
				string[] array = File.ReadAllLines(configFilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.StartsWith("# Default value:"))
					{
						text = text2.Substring("# Default value:".Length).Trim();
					}
					else if (text2.StartsWith("["))
					{
						text = "";
					}
					else if (text2.StartsWith("Recruit base cost") && text2.Contains("="))
					{
						return text == "10" && text2.Substring(text2.IndexOf('=') + 1).Trim() == "10";
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static ConfigEntry<T> Local<T>(ConfigFile config, string section, string key, T value, string description)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return config.Bind<T>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				Order = (_order -= 1)
			} }));
		}

		private static ConfigEntry<T> Entry<T>(ConfigFile config, string section, string key, T value, string description, AcceptableValueBase acceptable = null)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Order = (_order -= 1)
			};
			return config.Bind<T>(section, key, value, new ConfigDescription(description, acceptable, new object[1] { val }));
		}
	}
}
namespace TXC.Village.Village
{
	internal static class BannerPrefab
	{
		public const string Name = "TXC_VillageBanner";

		private static readonly string[] Candidates = new string[3] { "piece_maypole", "piece_banner01", "sign" };

		public static string BaseUsed { get; private set; }

		public static Sprite Icon { get; private set; }

		public static void Hook()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Build;
		}

		private static void Build()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//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_00c3: 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_00d7: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= Build;
			string text = null;
			string[] candidates = Candidates;
			foreach (string text2 in candidates)
			{
				if ((Object)(object)PrefabManager.Instance.GetPrefab(text2) != (Object)null)
				{
					text = text2;
					break;
				}
			}
			if (text == null)
			{
				VillagePlugin.Log.LogError((object)"No stand-in prefab found for the Village Banner; the piece is not available.");
				return;
			}
			BaseUsed = text;
			PieceConfig val = new PieceConfig
			{
				Name = "Village Banner",
				Description = "Founds a village. Villager beds inside its circle house villagers. Use it to name the village and pick its colours.",
				PieceTable = "Hammer",
				Category = "Misc",
				CraftingStation = "piece_workbench"
			};
			val.AddRequirement(new RequirementConfig
			{
				Item = "Wood",
				Amount = 10,
				Recover = true
			});
			val.AddRequirement(new RequirementConfig
			{
				Item = "LeatherScraps",
				Amount = 4,
				Recover = true
			});
			val.AddRequirement(new RequirementConfig
			{
				Item = "Resin",
				Amount = 2,
				Recover = true
			});
			CustomPiece val2 = new CustomPiece("TXC_VillageBanner", text, val);
			GameObject piecePrefab = val2.PiecePrefab;
			MonoBehaviour[] componentsInChildren = piecePrefab.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val3 in componentsInChildren)
			{
				if ((val3 is Hoverable || val3 is Interactable || val3 is TextReceiver) && !(val3 is VillageBanner))
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			Piece component = piecePrefab.GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_enabled = true;
				component.m_dlc = "";
				component.m_comfort = 1;
				component.m_comfortGroup = (ComfortGroup)3;
				Icon = component.m_icon;
			}
			piecePrefab.AddComponent<VillageBanner>();
			AddPlayerBaseArea(piecePrefab);
			AddAreaMarker(piecePrefab);
			PantryChest.AddTo(piecePrefab);
			PieceManager.Instance.AddPiece(val2);
			VillagePlugin.Log.LogInfo((object)("Village Banner built on the '" + text + "' stand-in."));
		}

		private static void AddPlayerBaseArea(GameObject prefab)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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)
			GameObject val = new GameObject("TXCV_PlayerBase");
			val.transform.SetParent(prefab.transform, false);
			val.layer = LayerMask.NameToLayer("character_trigger");
			SphereCollider obj = val.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 10f;
			val.AddComponent<EffectArea>().m_type = (Type)4;
		}

		private static void AddAreaMarker(GameObject prefab)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = PrefabManager.Instance.GetPrefab("guard_stone");
			PrivateArea val = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<PrivateArea>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.m_areaMarker == (Object)null)
			{
				VillagePlugin.Log.LogWarning((object)"Ward area marker not found; the village circle won't be drawn.");
				return;
			}
			GameObject obj = Object.Instantiate<GameObject>(((Component)val.m_areaMarker).gameObject, prefab.transform, false);
			((Object)obj).name = "TXCV_AreaMarker";
			obj.transform.localPosition = Vector3.zero;
			obj.SetActive(false);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
	internal static class BannerShield
	{
		private static bool Prefix(WearNTear __instance, HitData __1)
		{
			if ((Object)(object)((Component)__instance).GetComponent<VillageBanner>() == (Object)null)
			{
				return true;
			}
			if (__1 != null)
			{
				return __1.GetAttacker() is Player;
			}
			return false;
		}
	}
	internal static class BedPanel
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Give;

			public static UnityAction <1>__Close;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Predicate<VillagerRecord> <>9__14_0;

			public static Func<VillagerRecord, int> <>9__17_1;

			public static Func<VillagerRecord, string> <>9__17_2;

			public static UnityAction <>9__18_0;

			public static UnityAction <>9__18_1;

			public static Func<VillagerRecord, bool> <>9__21_0;

			internal bool <Open>b__14_0(VillagerRecord x)
			{
				return x.BedId == _bedId;
			}

			internal int <Candidates>b__17_1(VillagerRecord v)
			{
				if (!(v.BedId == _bedId))
				{
					if (v.BedId.Length != 0)
					{
						return 2;
					}
					return 1;
				}
				return 0;
			}

			internal string <Candidates>b__17_2(VillagerRecord v)
			{
				return v.Name;
			}

			internal void <Build>b__18_0()
			{
				Cycle(-1);
			}

			internal void <Build>b__18_1()
			{
				Cycle(1);
			}

			internal bool <Refresh>b__21_0(VillagerRecord x)
			{
				return x.BedId == _bedId;
			}
		}

		private const float W = 520f;

		private const float H = 300f;

		private static GameObject _root;

		private static Text _title;

		private static Text _who;

		private static Text _info;

		private static VillageBanner _village;

		private static string _bedId = "";

		private static int _index;

		private static float _refresh;

		public static Bed Current { get; private set; }

		public static void Open(Bed bed, VillageBanner village)
		{
			if (!((Object)(object)bed == (Object)null) && !((Object)(object)village == (Object)null) && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				if ((Object)(object)_root == (Object)null)
				{
					Build();
				}
				if ((Object)(object)Current == (Object)null)
				{
					GUIManager.BlockInput(true);
				}
				Current = bed;
				_village = village;
				_bedId = VillageHome.IdOf(bed);
				List<VillagerRecord> list = Candidates();
				_index = Math.Max(0, list.FindIndex((VillagerRecord x) => x.BedId == _bedId));
				_root.SetActive(true);
				Refresh();
			}
		}

		public static void Close()
		{
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
			if (!((Object)(object)Current == (Object)null))
			{
				Current = null;
				GUIManager.BlockInput(false);
			}
		}

		public static void Tick()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Current == (Object)null)
			{
				return;
			}
			if ((Object)(object)_root == (Object)null || (Object)(object)_village == (Object)null || Ui.ShouldClose(((Component)Current).transform.position))
			{
				Close();
				return;
			}
			_refresh -= Time.deltaTime;
			if (_refresh <= 0f)
			{
				_refresh = 0.5f;
				Refresh();
			}
		}

		internal static List<VillagerRecord> Candidates()
		{
			if ((Object)(object)_village == (Object)null)
			{
				return new List<VillagerRecord>();
			}
			double now = VillageHome.Now;
			double day = VillageHome.DayLength;
			return (from v in VillageHome.Peek(_village)
				where v.IsAlive && !FamilyRules.IsChild(v, now, day)
				orderby (!(v.BedId == _bedId)) ? ((v.BedId.Length == 0) ? 1 : 2) : 0, v.Name
				select v).ToList();
		}

		private static void Build()
		{
			//IL_003f: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_0115: 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_00ee: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Expected O, but got Unknown
			_root = Ui.Panel("TXCV_BedPanel", 520f, 300f);
			Transform transform = _root.transform;
			_title = Ui.Label(transform, "", 0f, 100f, 460f, 40f, 24, Ui.Orange, (TextAnchor)4);
			object obj = <>c.<>9__18_0;
			if (obj == null)
			{
				UnityAction val = delegate
				{
					Cycle(-1);
				};
				<>c.<>9__18_0 = val;
				obj = (object)val;
			}
			Ui.Button(transform, "Bed <", -190f, 40f, 44f, (UnityAction)obj, "<");
			_who = Ui.Label(transform, "", 0f, 40f, 300f, 30f, 20, Ui.Orange, (TextAnchor)4);
			object obj2 = <>c.<>9__18_1;
			if (obj2 == null)
			{
				UnityAction val2 = delegate
				{
					Cycle(1);
				};
				<>c.<>9__18_1 = val2;
				obj2 = (object)val2;
			}
			Ui.Button(transform, "Bed >", 190f, 40f, 44f, (UnityAction)obj2, ">");
			_info = Ui.Label(transform, "", 0f, -20f, 460f, 60f, 15, Ui.Soft, (TextAnchor)1);
			object obj3 = <>O.<0>__Give;
			if (obj3 == null)
			{
				UnityAction val3 = Give;
				<>O.<0>__Give = val3;
				obj3 = (object)val3;
			}
			Ui.Button(transform, "Bed give", -90f, -100f, 200f, (UnityAction)obj3, "Sleeps here");
			object obj4 = <>O.<1>__Close;
			if (obj4 == null)
			{
				UnityAction val4 = Close;
				<>O.<1>__Close = val4;
				obj4 = (object)val4;
			}
			Ui.Button(transform, "Close bed", 110f, -100f, 140f, (UnityAction)obj4, "Close");
			_root.SetActive(false);
		}

		private static void Cycle(int step)
		{
			int count = Candidates().Count;
			if (count != 0)
			{
				_index = ((_index + step) % count + count) % count;
				Refresh();
			}
		}

		private static void Give()
		{
			List<VillagerRecord> list = Candidates();
			if ((Object)(object)Current == (Object)null || list.Count == 0)
			{
				return;
			}
			VillagerRecord villagerRecord = list[Mathf.Clamp(_index, 0, list.Count - 1)];
			if (villagerRecord.BedId == _bedId)
			{
				Close();
				return;
			}
			_village.InvokeBannerRpc("TXCV_Bed", villagerRecord.Id, _bedId);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, villagerRecord.Name + " sleeps here now.", 0, (Sprite)null, false);
			}
			Close();
		}

		private static void Refresh()
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Current == (Object)null || (Object)(object)_village == (Object)null)
			{
				return;
			}
			List<VillagerRecord> list = Candidates();
			VillagerRecord villagerRecord = list.FirstOrDefault((VillagerRecord x) => x.BedId == _bedId);
			_title.text = ((villagerRecord != null) ? (villagerRecord.Name + "'s bed") : "Free villager bed");
			if (list.Count == 0)
			{
				_who.text = "No one";
				_info.text = "Nobody lives in this village yet.";
				return;
			}
			_index = Mathf.Clamp(_index, 0, list.Count - 1);
			VillagerRecord v = list[_index];
			_who.text = v.Name;
			List<VillagerRecord> source = VillageHome.Peek(_village);
			Vector3 pos;
			string text = ((v.BedId == _bedId) ? "Sleeps here." : ((v.BedId.Length == 0) ? "Has no bed." : (VillageHome.TryBedPos(v.BedId, out pos) ? $"Sleeps in a bed {VillageRules.DistanceXZ(((Component)Current).transform.position.x, ((Component)Current).transform.position.z, pos.x, pos.z):0} m away." : "Has a bed.")));
			VillagerRecord villagerRecord2 = ((v.PartnerState == 2) ? source.FirstOrDefault((VillagerRecord x) => x.Id == v.Partner && x.IsAlive) : null);
			string text2 = (v.Female ? "A woman" : "A man") + ((villagerRecord2 != null) ? (", married to " + villagerRecord2.Name.Split(new char[1] { ' ' })[0] + " (comes along if there is a free bed in this room)") : "") + ".";
			if (villagerRecord != null && villagerRecord != v)
			{
				text2 = text2 + " " + villagerRecord.Name.Split(new char[1] { ' ' })[0] + " " + ((v.BedId.Length > 0) ? ("takes " + v.Name.Split(new char[1] { ' ' })[0] + "'s bed") : "looks for another bed") + ".";
			}
			_info.text = $"{text} {text2}  ({_index + 1} of {list.Count})";
		}
	}
	internal static class OfflineCatchUp
	{
		private class Session
		{
			public float OwnedFor;

			public double PendingFrom = -1.0;

			public Dictionary<string, double> Meals = new Dictionary<string, double>();
		}

		public const string KeySimAt = "txcv_simat";

		private static readonly Dictionary<string, Session> Sessions = new Dictionary<string, Session>();

		public static double SimAt(VillageBanner b)
		{
			return (b?.Zdo != null) ? b.Zdo.GetLong("txcv_simat", 0L) : 0;
		}

		public static double PendingFrom(VillageBanner b)
		{
			if (!((Object)(object)b != (Object)null) || !Sessions.TryGetValue(b.Id, out var value))
			{
				return -1.0;
			}
			return value.PendingFrom;
		}

		public static bool Waiting(string villageId)
		{
			if (!string.IsNullOrEmpty(villageId) && Sessions.TryGetValue(villageId, out var value))
			{
				return value.PendingFrom >= 0.0;
			}
			return false;
		}

		public static Dictionary<string, double> NotedMeals(VillageBanner b)
		{
			if (!((Object)(object)b != (Object)null) || !Sessions.TryGetValue(b.Id, out var value))
			{
				return null;
			}
			return value.Meals;
		}

		public static void Forget(VillageBanner b)
		{
			if ((Object)(object)b != (Object)null && Sessions.TryGetValue(b.Id, out var value))
			{
				value.PendingFrom = -1.0;
				value.Meals.Clear();
			}
		}

		public static bool Tick(VillageBanner b, List<VillagerRecord> list, float ownedFor)
		{
			if (b?.Zdo == null || !b.Zdo.IsOwner())
			{
				return false;
			}
			LumberOffline.Tick(b);
			double now = VillageHome.Now;
			double num = SimAt(b);
			Session value;
			bool num2 = Sessions.TryGetValue(b.Id, out value);
			if (CatchUpRules.NewTurn(num2, num2 ? value.OwnedFor : 0f, ownedFor))
			{
				Dictionary<string, Session> sessions = Sessions;
				string id = b.Id;
				Session obj = new Session
				{
					PendingFrom = ((LumberRules.Gap(num, now) > 0.0) ? num : (-1.0))
				};
				value = obj;
				sessions[id] = obj;
				if (value.PendingFrom >= 0.0)
				{
					foreach (VillagerRecord item in list)
					{
						value.Meals[item.Id] = item.LastMeal;
					}
					VillagePlugin.Debug($"[catch-up] {b.VillageName} was not simulated for {LumberRules.GameHours(now - num, VillageHome.DayLength):0.#} game hours; catching up once it has loaded.");
				}
			}
			value.OwnedFor = ownedFor;
			if (value.PendingFrom >= 0.0)
			{
				if (!CatchUpRules.Ready(ownedFor, ownedFor >= 20f && LumberOffline.Loaded(b, list)))
				{
					return false;
				}
				double pendingFrom = value.PendingFrom;
				value.PendingFrom = -1.0;
				bool result = Run(b, list, pendingFrom, now, value.Meals);
				value.Meals = new Dictionary<string, double>();
				b.Zdo.Set("txcv_simat", (long)now);
				return result;
			}
			if (LumberRules.WriteSimAt(num, now))
			{
				b.Zdo.Set("txcv_simat", (long)now);
			}
			return false;
		}

		private static bool Run(VillageBanner b, List<VillagerRecord> list, double simAt, double now, Dictionary<string, double> noted)
		{
			bool flag = false;
			try
			{
				flag |= LumberOffline.Run(b, list, simAt, now, apply: true).Any((LumberOffline.Result r) => r.Changed);
			}
			catch (Exception arg)
			{
				VillagePlugin.Log.LogWarning((object)$"[lumberjack] {b.VillageName}: the catch-up failed. {arg}");
			}
			try
			{
				OfflineMealPlan offlineMealPlan = MealsOffline.Run(b, list, simAt, now, apply: true, noted);
				if (offlineMealPlan != null && offlineMealPlan.Served + offlineMealPlan.Missed > 0)
				{
					VillageHome.MarkStructural(b);
					flag = true;
				}
			}
			catch (Exception arg2)
			{
				VillagePlugin.Log.LogWarning((object)$"[meal] {b.VillageName}: the catch-up failed. {arg2}");
			}
			return flag;
		}

		internal static OfflineMealPlan Away(VillageBanner b, double gameHours, bool apply, out List<LumberOffline.Result> lumber)
		{
			lumber = new List<LumberOffline.Result>();
			double now = VillageHome.Now;
			double simAt = now - gameHours * LumberRules.HourSeconds(VillageHome.DayLength);
			List<VillagerRecord> list = ((apply || b.Zdo.IsOwner()) ? VillageHome.LoadWork(b) : VillageHome.Load(b));
			if (!apply)
			{
				return MealsOffline.Run(b, list, simAt, now, apply: false);
			}
			Forget(b);
			lumber = LumberOffline.Run(b, list, simAt, now, apply: true);
			OfflineMealPlan offlineMealPlan = MealsOffline.Run(b, list, simAt, now, apply: true);
			if (offlineMealPlan.Served + offlineMealPlan.Missed > 0)
			{
				VillageHome.Save(b, list);
			}
			else if (lumber.Any((LumberOffline.Result r) => r.Changed))
			{
				VillageHome.MarkMinor(b);
			}
			b.Zdo.Set("txcv_simat", (long)now);
			return offlineMealPlan;
		}

		public static void Sweep(bool reset)
		{
			if (reset)
			{
				Sessions.Clear();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class PlacementPatches
	{
		private static readonly FieldRef<Player, GameObject> Ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			//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_005a: 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_0162: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null || ((Object)((Component)piece).gameObject).name != "TXC_VillageBanner")
			{
				return true;
			}
			GameObject val = Ghost.Invoke(__instance);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			Vector3 position = val.transform.position;
			List<string> names;
			List<float[]> list = VillageNet.AllCentres(out names);
			float value = VillageConfig.MinDistanceBetweenVillages.Value;
			int num = VillageRules.TooCloseTo(position.x, position.z, list, value);
			if (num < 0)
			{
				foreach (VillageBanner item in VillageBanner.All)
				{
					if (!((Object)(object)item == (Object)null) && item.IsValid)
					{
						float num2 = VillageRules.DistanceXZ(position.x, position.z, item.Position.x, item.Position.z);
						float num3 = item.Radius + VillageConfig.VillageRadius.Value;
						if (!(num2 >= num3))
						{
							((Character)__instance).Message((MessageType)2, $"Too close to {item.VillageName} ({num2:0} m). A {item.Tier.Name.ToLowerInvariant()} needs {num3:0} m of room.", 0, (Sprite)null, false);
							__result = false;
							return false;
						}
					}
				}
				return true;
			}
			float num4 = VillageRules.DistanceXZ(position.x, position.z, list[num][0], list[num][1]);
			((Character)__instance).Message((MessageType)2, $"Too close to {names[num]} ({num4:0} m). Villages must be {value:0} m apart.", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	internal static class Ui
	{
		public static readonly Color Orange = new Color(1f, 0.63f, 0.24f);

		public static readonly Color Soft = new Color(0.85f, 0.85f, 0.85f);

		public static GameObject Panel(string name, float w, float h)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, w, h, false);
			((Object)obj).name = name;
			return obj;
		}

		public static Text Label(Transform parent, string text, float x, float y, float w, float h, int size, Color color, TextAnchor anchor)
		{
			//IL_0011: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			Text component = GUIManager.Instance.CreateText(text, parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(x, y), GUIManager.Instance.AveriaSerifBold, size, color, true, Color.black, w, h, false).GetComponent<Text>();
			component.alignment = anchor;
			component.supportRichText = true;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)0;
			return component;
		}

		public static GameObject Button(Transform parent, string name, float x, float y, float w, UnityAction onClick, string shown)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateButton(shown, parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(x, y), w, 34f);
			((Object)obj).name = "TXCV_" + name;
			((UnityEvent)obj.GetComponent<Button>().onClick).AddListener(onClick);
			return obj;
		}

		public static void SetText(GameObject button, string text)
		{
			Text val = (((Object)(object)button != (Object)null) ? button.GetComponentInChildren<Text>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.supportRichText = true;
				val.text = text;
			}
		}

		public static bool ShouldClose(Vector3 pos, float maxDist = 8f)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !(Vector3.Distance(((Component)localPlayer).transform.position, pos) > maxDist) && !ZInput.GetKeyDown((KeyCode)27, false))
			{
				return Menu.IsVisible();
			}
			return true;
		}
	}
	public class VillageBanner : MonoBehaviour, Hoverable, Interactable, TextReceiver
	{
		public const string MarkerName = "TXCV_AreaMarker";

		public const string KeyId = "txcv_id";

		public const string KeyName = "txcv_name";

		public const string KeyColor1 = "txcv_c1";

		public const string KeyColor2 = "txcv_c2";

		public const string KeyAlarm = "txcv_alarm";

		public const string KeyRally = "txcv_rally";

		private const string RpcRally = "TXCV_Rally";

		public const string KeyRaid = "txcv_raid";

		private const string RpcAlarm = "TXCV_Alarm";

		private const string RpcRename = "TXCV_Rename";

		private const string RpcColors = "TXCV_Colors";

		private const string RpcRecruit = "TXCV_Recruit";

		private const string RpcFell = "TXCV_Fell";

		private const string RpcAdmin = "TXCV_Admin";

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

		private ZNetView _nview;

		private GameObject _marker;

		private CircleProjector _projector;

		private float _lookTimer;

		private int _appliedColor = -1;

		private readonly List<Material> _tinted = new List<Material>();

		private bool _isGhost;

		public const string KeyLevel = "txcv_level";

		private float _ownedSince = -1f;

		private float _homeTimer;

		private float _markerRadius;

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_nview != (Object)null)
				{
					return _nview.IsValid();
				}
				return false;
			}
		}

		public ZDO Zdo
		{
			get
			{
				if (!IsValid)
				{
					return null;
				}
				return _nview.GetZDO();
			}
		}

		public string Id
		{
			get
			{
				ZDO zdo = Zdo;
				return ((zdo != null) ? zdo.GetString("txcv_id", "") : null) ?? "";
			}
		}

		public string VillageName
		{
			get
			{
				ZDO zdo = Zdo;
				string text = ((zdo != null) ? zdo.GetString("txcv_name", "") : null);
				if (text == null || text.Length <= 0)
				{
					return "Village";
				}
				return text;
			}
		}

		public int Color1
		{
			get
			{
				ZDO zdo = Zdo;
				return VillageRules.WrapColor((zdo != null) ? zdo.GetInt("txcv_c1", 0) : 0);
			}
		}

		public int Color2
		{
			get
			{
				ZDO zdo = Zdo;
				return VillageRules.WrapColor((zdo != null) ? zdo.GetInt("txcv_c2", 4) : 4);
			}
		}

		public Vector3 Position => ((Component)this).transform.position;

		public int Level
		{
			get
			{
				ZDO zdo = Zdo;
				return Mathf.Clamp((zdo == null) ? 1 : zdo.GetInt("txcv_level", 1), 1, VillageTiers.MaxLevel);
			}
		}

		public VillageTier Tier => VillageTiers.Of(Level);

		public float Radius => VillageTiers.Radius(_isGhost ? 1 : Level, VillageConfig.VillageRadius.Value);

		public bool Alarm
		{
			get
			{
				if (Zdo != null)
				{
					return Zdo.GetBool("txcv_alarm", false);
				}
				return false;
			}
		}

		public bool Raid
		{
			get
			{
				if (Alarm)
				{
					return Zdo.GetBool("txcv_raid", false);
				}
				return false;
			}
		}

		public Vector3 Rally
		{
			get
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Zdo != null) ? Zdo.GetVec3("txcv_rally", Vector3.zero) : Vector3.zero);
				if (!(val == Vector3.zero))
				{
					return val;
				}
				return Position;
			}
		}

		public bool HasRally
		{
			get
			{
				//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)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				if (Zdo != null)
				{
					return Zdo.GetVec3("txcv_rally", Vector3.zero) != Vector3.zero;
				}
				return false;
			}
		}

		public bool HasCook => VillageHome.Peek(this).Any((VillagerRecord v) => v.IsAlive && v.Role == "cook");

		public Pantry Pantry => ((Component)this).GetComponentInChildren<Pantry>();

		private void Awake()
		{
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			_nview = ((Component)this).GetComponent<ZNetView>();
			Transform val = ((Component)this).transform.Find("TXCV_AreaMarker");
			if ((Object)(object)val != (Object)null)
			{
				_marker = ((Component)val).gameObject;
				_projector = _marker.GetComponent<CircleProjector>();
				if ((Object)(object)_projector != (Object)null)
				{
					SizeMarker(VillageConfig.VillageRadius.Value);
				}
			}
			if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
			{
				_isGhost = true;
				return;
			}
			All.Add(this);
			_nview.Register<string>("TXCV_Rename", (Action<long, string>)RPC_Rename);
			Farm.Register(_nview, this);
			_nview.Register<int, int>("TXCV_Colors", (Action<long, int, int>)RPC_Colors);
			_nview.Register<string>("TXCV_Recruit", (Action<long, string>)delegate(long sender, string how)
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				if (_nview.IsOwner() && VillageHome.AddVillager(this, how) == null)
				{
					VillageNet.Notice("No one could come to " + VillageName + ". No free villager bed or no room.", Position);
				}
			});
			_nview.Register<string>("TXCV_Fell", (Action<long, string>)delegate(long sender, string vid)
			{
				if (_nview.IsOwner())
				{
					VillageHome.Fell(this, vid);
				}
			});
			_nview.Register<Vector3>("TXCV_Rally", (Action<long, Vector3>)delegate(long sender, Vector3 at)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				if (_nview.IsOwner())
				{
					Zdo.Set("txcv_rally", at);
				}
			});
			_nview.Register<bool>("TXCV_Alarm", (Action<long, bool>)delegate(long sender, bool on)
			{
				if (_nview.IsOwner())
				{
					VillageDefense.SetByHand(this, on);
				}
			});
			ExpeditionHome.Register(_nview, this);
			VillageLevels.Register(_nview, this);
			_nview.Register<string, string>("TXCV_VName", (Action<long, string, string>)delegate(long sender, string vid, string name)
			{
				if (_nview.IsOwner())
				{
					VillageHome.SetName(this, vid, name);
				}
			});
			_nview.Register<string, string>("TXCV_Bed", (Action<long, string, string>)delegate(long sender, string vid, string bed)
			{
				if (_nview.IsOwner())
				{
					VillageHome.ChooseBed(this, vid, bed);
				}
			});
			_nview.Register<string, string>("TXCV_Role", (Action<long, string, string>)delegate(long sender, string vid, string role)
			{
				if (_nview.IsOwner())
				{
					VillageHome.SetRole(this, vid, role);
				}
			});
			_nview.Register<string, string>("TXCV_Admin", (Action<long, string, string>)delegate(long sender, string cmd, string arg)
			{
				if (_nview.IsOwner())
				{
					string text2 = VillageHome.Admin(this, cmd, arg);
					VillagePlugin.Log.LogInfo((object)("[admin] " + VillageName + ": " + cmd + " " + arg + " -> " + text2));
				}
			});
			if (_nview.IsOwner() && string.IsNullOrEmpty(Zdo.GetString("txcv_id", "")) && VillageCamp.Adopt(this))
			{
				VillageNet.RequestNow();
			}
			else if (_nview.IsOwner() && string.IsNullOrEmpty(Zdo.GetString("txcv_id", "")))
			{
				string text = Guid.NewGuid().ToString("N");
				int hashCode = text.GetHashCode();
				Zdo.Set("txcv_id", text);
				Zdo.Set("txcv_name", VillageRules.DefaultName(hashCode));
				int num = VillageRules.WrapColor(hashCode >> 4);
				Zdo.Set("txcv_c1", num);
				Zdo.Set("txcv_c2", VillageRules.SecondColor(num, hashCode >> 8));
				VillagePlugin.Debug($"New village '{VillageName}' ({text}) at {Position}.");
				VillageNet.RequestNow();
			}
		}

		private void OnDestroy()
		{
			if (!_isGhost)
			{
				VillageHome.Flush(this);
			}
			All.Remove(this);
			foreach (Material item in _tinted)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			_tinted.Clear();
		}

		private void Update()
		{
			long now = Perf.Now;
			try
			{
				UpdateBanner();
			}
			finally
			{
				Perf.Add("banner update", now);
			}
		}

		private void UpdateBanner()
		{
			long now = Perf.Now;
			UpdateMarker();
			Perf.Add("banner/marker", now);
			if (_isGhost || !IsValid)
			{
				return;
			}
			if (_nview.IsOwner())
			{
				if (_ownedSince < 0f)
				{
					_ownedSince = Time.time;
				}
				_homeTimer -= Time.deltaTime;
				if (_homeTimer <= 0f)
				{
					_homeTimer = 2f;
					long now2 = Perf.Now;
					try
					{
						VillageHome.Tick(this, Time.time - _ownedSince);
					}
					catch (Exception arg)
					{
						VillagePlugin.Log.LogError((object)$"Village tick ({VillageName}): {arg}");
					}
					Perf.Add("banner/village tick (2 s)", now2);
				}
			}
			else
			{
				_ownedSince = -1f;
			}
			_lookTimer -= Time.deltaTime;
			if (!(_lookTimer > 0f))
			{
				_lookTimer = 1f;
				if (_appliedColor != Color1)
				{
					ApplyLook();
				}
			}
		}

		private void SizeMarker(float r)
		{
			if (!((Object)(object)_projector == (Object)null) && !(Mathf.Abs(_markerRadius - r) < 0.01f))
			{
				_markerRadius = r;
				_projector.m_radius = r;
				_projector.m_nrOfSegments = Mathf.Clamp(Mathf.RoundToInt(r * 2.4f), 24, 200);
			}
		}

		private void UpdateMarker()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (VillageKeepAlive.Headless)
			{
				return;
			}
			if (!_isGhost && IsValid)
			{
				SizeMarker(Radius);
			}
			if ((Object)(object)_marker == (Object)null)
			{
				return;
			}
			bool flag = false;
			if (_isGhost)
			{
				flag = true;
			}
			else if (VillageConfig.ShowAreaWithHammer.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).InPlaceMode() && Vector3.Distance(((Component)localPlayer).transform.position, Position) < Radius + 40f)
				{
					flag = true;
				}
				if ((Object)(object)VillagePanel.Current == (Object)(object)this)
				{
					flag = true;
				}
			}
			if (_marker.activeSelf != flag)
			{
				_marker.SetActive(flag);
			}
		}

		private void ApplyLook()
		{
			//IL_0061: 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_016c: 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_0175: Expected O, but got Unknown
			_appliedColor = Color1;
			if (VillageKeepAlive.Headless)
			{
				return;
			}
			PaletteColor paletteColor = VillageRules.Palette[Color1];
			Color color = default(Color);
			((Color)(ref color))..ctor(paletteColor.R, paletteColor.G, paletteColor.B);
			foreach (Material item in _tinted)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.color = color;
				}
			}
			if (_tinted.Count > 0)
			{
				return;
			}
			List<string> list = new List<string>();
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if ((Object)(object)_marker != (Object)null && ((Component)val).transform.IsChildOf(_marker.transform))
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if (!((Object)(object)sharedMaterials[j] == (Object)null))
					{
						string text = ((Object)sharedMaterials[j]).name.ToLowerInvariant();
						list.Add(((Object)sharedMaterials[j]).name);
						if (text.Contains("cloth") || text.Contains("banner") || text.Contains("flag") || text.Contains("ribbon") || text.Contains("fabric"))
						{
							Material val2 = new Material(sharedMaterials[j])
							{
								color = color
							};
							_tinted.Add(val2);
							sharedMaterials[j] = val2;
							flag = true;
						}
					}
				}
				if (flag)
				{
					val.sharedMaterials = sharedMaterials;
				}
			}
			if (_tinted.Count == 0)
			{
				VillagePlugin.Debug("Banner stand-in has no cloth material to tint (materials: " + string.Join(", ", list) + ").");
			}
		}

		public void RequestRename(string name)
		{
			string text = VillageRules.SanitizeName(name);
			if (text != null && IsValid)
			{
				_nview.InvokeRPC("TXCV_Rename", new object[1] { text });
				VillageNet.RequestNow();
			}
		}

		public void RequestColors(int c1, int c2)
		{
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Colors", new object[2]
				{
					VillageRules.WrapColor(c1),
					VillageRules.WrapColor(c2)
				});
				VillageNet.RequestNow();
			}
		}

		private void RPC_Rename(long sender, string name)
		{
			if (_nview.IsOwner())
			{
				string text = VillageRules.SanitizeName(name);
				if (text != null)
				{
					VillagePlugin.Debug("Village '" + VillageName + "' renamed to '" + text + "'.");
					Zdo.Set("txcv_name", text);
				}
			}
		}

		private void RPC_Colors(long sender, int c1, int c2)
		{
			if (_nview.IsOwner())
			{
				Zdo.Set("txcv_c1", VillageRules.WrapColor(c1));
				Zdo.Set("txcv_c2", VillageRules.WrapColor(c2));
			}
		}

		public void RequestRecruit(string how)
		{
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Recruit", new object[1] { how });
			}
		}

		public void RequestRally(Vector3 at)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Rally", new object[1] { at });
			}
		}

		public void RequestAlarm(bool on)
		{
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Alarm", new object[1] { on });
			}
		}

		public void ReportFell(string villagerId)
		{
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Fell", new object[1] { villagerId });
			}
		}

		public void InvokeBannerRpc(string name, params object[] args)
		{
			if (IsValid)
			{
				_nview.InvokeRPC(name, args);
			}
		}

		public void RequestAdmin(string cmd, string arg)
		{
			if (IsValid)
			{
				_nview.InvokeRPC("TXCV_Admin", new object[2]
				{
					cmd,
					arg ?? ""
				});
			}
		}

		public static VillageBanner ById(string id)
		{
			foreach (VillageBanner item in All)
			{
				if (item.Id == id)
				{
					return item;
				}
			}
			return null;
		}

		public string GetHoverText()
		{
			if (!IsValid)
			{
				return "";
			}
			VillageBeds.Summary summary = VillageBeds.Summarize(this);
			int num = VillageHome.Alive(VillageHome.Peek(this));
			string text = num switch
			{
				1 => "1 villager", 
				0 => "nobody lives here yet", 
				_ => $"{num} villagers", 
			};
			string text2 = ((summary.VillagerBeds == 0) ? ("\nMark a bed in a closed house with Shift + E." + (VillageConfig.ShowAreaWithHammer.Value ? "\nHold the hammer to see the village circle." : "")) : "");
			string text3 = "<b>" + VillageName + "</b> · " + text + "\n" + VillageRules.BedSummary(summary.Ready, summary.VillagerBeds) + text2 + "\n[<color=yellow><b>$KEY_Use</b></color>] Open village" + (((Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory().HaveItem("Recruitment contract", true)) ? "\n[<color=yellow><b>1-8</b></color>] Use a recruitment contract" : "");
			return Localization.instance.Localize(text3);
		}

		public string GetHoverName()
		{
			return VillageName;
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold || !IsValid || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!Pages.Open(this, "village"))
			{
				VillagePanel.Open(this);
			}
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			if (!IsValid || (Object)(object)item?.m_dropPrefab == (Object)null || ((Object)item.m_dropPrefab).name != "TXC_RecruitContract")
			{
				return false;
			}
			List<VillagerRecord> list = VillageHome.Load(this);
			if (VillageHome.Alive(list) >= Tier.MaxVillagers)
			{
				((Character)user).Message((MessageType)2, VillageName + " is full. Upgrade the village first.", 0, (Sprite)null, false);
				return true;
			}
			if (VillageHome.FreeBeds(this, list).Count == 0)
			{
				((Character)user).Message((MessageType)2, "No free villager bed. Mark a bed in a closed house with Shift + E.", 0, (Sprite)null, false);
				return true;
			}
			user.GetInventory().RemoveItem(item, 1);
			RequestRecruit("contract");
			return true;
		}

		public string GetText()
		{
			return VillageName;
		}

		public void SetText(string text)
		{
			RequestRename(text);
		}

		public static VillageBanner FindAt(Vector3 pos)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			VillageBanner result = null;
			float num = float.MaxValue;
			foreach (VillageBanner item in All)
			{
				if (!((Object)(object)item == (Object)null) && item.IsValid)
				{
					float num2 = VillageRules.DistanceXZ(pos.x, pos.z, item.Position.x, item.Position.z);
					if (num2 <= item.Radius && num2 < num)
					{
						num = num2;
						result = item;
					}
				}
			}
			return result;
		}
	}
	internal static class VillageBeds
	{
		public struct Summary
		{
			public int Ready;

			public int VillagerBeds;

			public int PlayerBeds;

			public int OtherBeds;
		}

		public struct BedInfo
		{
			public Bed Bed;

			public BedStatus Status;

			public float Distance;

			public string Direction;

			public string OwnerName;
		}

		private struct Cached
		{
			public float Time;

			public bool UnderRoof;

			public float Cover;

			public bool Fire;
		}

		private class SummaryEntry
		{
			public float Time;

			public Summary Sum;
		}

		[HarmonyPatch(typeof(Bed), "Awake")]
		private static class BedAwake
		{
			private static void Postfix(Bed __instance)
			{
				ZNetView nview = ((Component)__instance).GetComponent<ZNetView>();
				if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null)
				{
					SummaryCache.Clear();
					Bed bed = __instance;
					nview.Register<bool>("TXCV_MarkBed", (Action<long, bool>)delegate(long sender, bool on)
					{
						RPC_Mark(bed, nview, on);
					});
				}
			}
		}

		[HarmonyPatch(typeof(Bed), "GetHoverText")]
		private static class BedHover
		{
			private static void Postfix(Bed __instance, ref string __result)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				if (ZdoOf(__instance) == null)
				{
					return;
				}
				VillageBanner villageBanner = VillageBanner.FindAt(((Component)__instance).transform.position);
				if ((Object)(object)villageBanner == (Object)null)
				{
					return;
				}
				BedStatus bedStatus = StatusOf(__instance);
				if (IsMarked(__instance) && PlayerOwner(__instance) == 0L)
				{
					string value = Localization.instance.Localize("$piece_bed_claim");
					string text = Localization.instance.Localize("$piece_bed_unclaimed");
					string id = VillageHome.IdOf(__instance);
					VillagerRecord villagerRecord = VillageHome.Peek(villageBanner).FirstOrDefault((VillagerRecord v) => v.IsAlive && v.BedId == id);
					string newValue = ((villagerRecord != null) ? (villagerRecord.Name + "'s bed") : "Free villager bed");
					List<string> list = new List<string>();
					string[] array = __result.Split(new char[1] { '\n' });
					foreach (string text2 in array)
					{
						if (!text2.Contains(value))
						{
							list.Add((text.Length > 0 && text2.Contains(text)) ? text2.Replace(text, newValue) : text2);
						}
					}
					__result = string.Join("\n", list);
				}
				object obj;
				switch (bedStatus)
				{
				default:
					obj = "#ffb060";
					break;
				case BedStatus.NotVillagerBed:
				case BedStatus.PlayerOwned:
					obj = "#c0c0c0";
					break;
				case BedStatus.Ready:
					obj = "#9fdf7f";
					break;
				}
				string text3 = (string)obj;
				string text4 = ((bedStatus == BedStatus.Ready && IsMarked(__instance) && VillageHome.Peek(villageBanner).Any((VillagerRecord v) => v.IsAlive && v.BedId == VillageHome.IdOf(__instance))) ? ("\n<color=" + text3 + ">" + villageBanner.VillageName + "</color>") : ("\n<color=" + text3 + ">" + villageBanner.VillageName + ". " + Cap(VillageRules.Describe(bedStatus)) + ".</color>"));
				if (bedStatus != BedStatus.PlayerOwned && IsMarked(__instance))
				{
					text4 += "\n[<color=yellow><b>$KEY_Use</b></color>] Choose who sleeps here";
				}
				if (bedStatus != BedStatus.PlayerOwned)
				{
					text4 += (IsMarked(__instance) ? "\n[<color=yellow><b>$KEY_AltPlace + $KEY_Use</b></color>] Unmark villager bed" : "\n[<color=yellow><b>$KEY_AltPlace + $KEY_Use</b></color>] Mark for a villager");
				}
				__result += Localization.instance.Localize(text4);
			}
		}

		[HarmonyPatch(typeof(Bed), "Interact")]
		private static class BedInteract
		{
			private static bool Prefix(Bed __instance, Humanoid human, bool repeat, bool alt, ref bool __result)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				if (ZdoOf(__instance) == null)
				{
					return true;
				}
				VillageBanner villageBanner = VillageBanner.FindAt(((Component)__instance).transform.position);
				bool flag = IsMarked(__instance);
				VillagePlugin.Debug(string.Format("[beds] Use on a bed: alt={0} repeat={1} marked={2} village={3}.", alt, repeat, flag, ((Object)(object)villageBanner != (Object)null) ? villageBanner.VillageName : "none"));
				if (alt && (Object)(object)villageBanner != (Object)null)
				{
					__result = false;
					if (repeat)
					{
						return false;
					}
					if (PlayerOwner(__instance) != 0L)
					{
						((Character)human).Message((MessageType)2, PlayerOwnerName(__instance) + " sleeps here. A villager needs a free bed.", 0, (Sprite)null, false);
						return false;
					}
					RequestMark(__instance, !flag);
					((Character)human).Message((MessageType)2, flag ? "No longer a villager bed" : ("Villager bed for " + villageBanner.VillageName), 0, (Sprite)null, false);
					__result = true;
					return false;
				}
				if (!alt && flag && PlayerOwner(__instance) == 0L)
				{
					__result = false;
					if (repeat)
					{
						return false;
					}
					if ((Object)(object)villageBanner != (Object)null && (Object)(object)human == (Object)(object)Player.m_localPlayer)
					{
						BedPanel.Open(__instance, villageBanner);
						__result = true;
					}
					else
					{
						((Character)human).Message((MessageType)2, "This is a villager bed. Unmark it first (Shift + E).", 0, (Sprite)null, false);
					}
					return false;
				}
				return true;
			}
		}

		public const string KeyVillagerBed = "txcv_vbed";

		private const string RpcMark = "TXCV_MarkBed";

		private static readonly Dictionary<int, Cached> Cache = new Dictionary<int, Cached>();

		public static bool TestIgnoreRoof;

		private static readonly Dictionary<VillageBanner, SummaryEntry> SummaryCache = new Dictionary<VillageBanner, SummaryEntry>();

		private static string Cap(string s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				return char.ToUpperInvariant(s[0]) + s.Substring(1).TrimEnd(new char[1] { '.' });
			}
			return "";
		}

		public static ZDO ZdoOf(Bed bed)
		{
			ZNetView val = (((Object)(object)bed != (Object)null) ? ((Component)bed).GetComponent<ZNetView>() : null);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return null;
			}
			return val.GetZDO();
		}

		public static bool IsMarked(Bed bed)
		{
			ZDO obj = ZdoOf(bed);
			if (obj == null)
			{
				return false;
			}
			return obj.GetBool("txcv_vbed", false);
		}

		public static long PlayerOwner(Bed bed)
		{
			ZDO obj = ZdoOf(bed);
			if (obj == null)
			{
				return 0L;
			}
			return obj.GetLong(ZDOVars.s_owner, 0L);
		}

		public static string PlayerOwnerName(Bed bed)
		{
			ZDO obj = ZdoOf(bed);
			return ((obj != null) ? obj.GetString(ZDOVars.s_ownerName, "") : null) ?? "";
		}

		public static BedStatus StatusOf(Bed bed)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			bool flag = IsMarked(bed);
			long num = PlayerOwner(bed);
			if (num != 0L || !flag)
			{
				return VillageRules.ClassifyBed(flag, num, underRoof: true, 1f, needFire: false, hasFire: true);
			}
			int instanceID = ((Object)bed).GetInstanceID();
			if (!Cache.TryGetValue(instanceID, out var value) || Time.time - value.Time > 20f || Time.time < value.Time)
			{
				float cover = default(float);
				bool underRoof = default(bool);
				Cover.GetCoverForPoint(bed.GetSpawnPoint(), ref cover, ref underRoof, 0.5f);
				bool fire = (Object)(object)EffectArea.IsPointInsideArea(((Component)bed).transform.position, (Type)1, 0f) != (Object)null;
				value = new Cached
				{
					Time = Time.time,
					UnderRoof = underRoof,
					Cover = cover,
					Fire = fire
				};
				Cache[instanceID] = value;
			}
			if (TestIgnoreRoof)
			{
				return VillageRules.ClassifyBed(markedForVillagers: true, 0L, underRoof: true, 1f, needFire: false, hasFire: true);
			}
			return VillageRules.ClassifyBed(markedForVillagers: true, 0L, value.UnderRoof, value.Cover, VillageConfig.BedsNeedFire.Value, value.Fire);
		}

		public static List<BedInfo> BedsIn(VillageBanner village)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			List<BedInfo> list = new List<BedInfo>();
			if ((Object)(object)village == (Object)null)
			{
				return list;
			}
			Vector3 position = village.Position;
			Bed[] array = SceneCache.All<Bed>(2f);
			foreach (Bed val in array)
			{
				if (!((Object)(object)val == (Object)null) && ZdoOf(val) != null)
				{
					Vector3 position2 = ((Component)val).transform.position;
					if (!((Object)(object)VillageBanner.FindAt(position2) != (Object)(object)village))
					{
						list.Add(new BedInfo
						{
							Bed = val,
							Status = StatusOf(val),
							Distance = VillageRules.DistanceXZ(position.x, position.z, position2.x, position2.z),
							Direction = VillageRules.Compass(position.x, position.z, position2.x, position2.z),
							OwnerName = PlayerOwnerName(val)
						});
					}
				}
			}
			list.Sort((BedInfo a, BedInfo b) => a.Distance.CompareTo(b.Distance));
			return list;
		}

		public static Summary Summarize(VillageBanner village, bool fresh = false)
		{
			if (!fresh && SummaryCache.TryGetValue(village, out var value) && Time.time - value.Time < 1f)
			{
				return value.Sum;
			}
			Summary summary = default(Summary);
			using (List<BedInfo>.Enumerator enumerator = BedsIn(village).GetEnumerator())
			{
				while (enumerator.MoveNext())
				{
					switch (enumerator.Current.Status)
					{
					case BedStatus.Ready:
						summary.Ready++;
						summary.VillagerBeds++;
						break;
					case BedStatus.PlayerOwned:
						summary.PlayerBeds++;
						break;
					case BedStatus.NotVillagerBed:
						summary.OtherBeds++;
						break;
					default:
						summary.VillagerBeds++;
						break;
					}
				}
			}
			if (SummaryCache.Count > 32)
			{
				SummaryCache.Clear();
			}
			SummaryCache[village] = new SummaryEntry
			{
				Time = Time.time,
				Sum = summary
			};
			return summary;
		}

		public static void RequestMark(Bed bed, bool on)
		{
			Cache.Remove(((Object)bed).GetInstanceID());
			ZNetView component = ((Component)bed).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				component.InvokeRPC("TXCV_MarkBed", new object[1] { on });
				Cache.Remove(((Object)bed).GetInstanceID());
				SummaryCache.Clear();
			}
		}

		private static void RPC_Mark(Bed bed, ZNetView nview, bool on)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (nview.IsOwner() && (!on || nview.GetZDO().GetLong(ZDOVars.s_owner, 0L) == 0L))
			{
				nview.GetZDO().Set("txcv_vbed", on);
				VillagePlugin.Debug(string.Format("Bed at {0} {1} for villagers.", ((Component)bed).transform.position, on ? "marked" : "unmarked"));
			}
		}
	}
	internal static class VillageCamp
	{
		public const string PrefabName = "TXC_VillageCamp";

		public const float AdoptRange = 150f;

		private static readonly string[] Strings = new string[8] { "txcv_id", "txcv_name", "txcv_villagers", "txcv_trips", "txcv_deliveries", "txcv_reports", "txcv_foods", "txcv_orders" };

		private static readonly string[] Ints = new string[6] { "txcv_c1", "txcv_c2", "txcv_level", "txcv_wanderers", "txcv_fishmode", "txcv_fishkeep" };

		private static readonly string[] Longs = new string[1] { "txcv_lastarrival" };

		private static GameObject _prefab;

		private const string NoString = "\u0002txcv_unset";

		private const int NoInt = int.MinValue;

		private const long NoLong = long.MinValue;

		private static float _waitingAt = -99f;

		private static readonly Dictionary<string, bool> _waitingCache = new Dictionary<string, bool>();

		public static void Hook()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Build;
		}

		private static void Build()
		{
			//IL_0052: 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_00d5: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= Build;
			_prefab = PrefabManager.Instance.CreateEmptyPrefab("TXC_VillageCamp", true);
			ZNetView component = _prefab.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_persistent = true;
				component.m_type = (ObjectType)0;
			}
			Collider[] componentsInChildren = _prefab.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			Renderer[] componentsInChildren2 = _prefab.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			MeshFilter[] componentsInChildren3 = _prefab.GetComponentsInChildren<MeshFilter>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
			}
			PrefabManager.Instance.AddPrefab(new CustomPrefab(_prefab, false));
		}

		private static void Copy(ZDO from, ZDO to)
		{
			string[] strings = Strings;
			foreach (string text in strings)
			{
				string text2 = from.GetString(text, "\u0002txcv_unset");
				if (text2 != "\u0002txcv_unset")
				{
					to.Set(text, text2);
				}
			}
			strings = Ints;
			foreach (string text3 in strings)
			{
				int num = from.GetInt(text3, int.MinValue);
				if (num != int.MinValue)
				{
					to.Set(text3, num);
				}
			}
			strings = Longs;
			foreach (string text4 in strings)
			{
				long num2 = from.GetLong(text4, long.MinValue);
				if (num2 != long.MinValue)
				{
					to.Set(text4, num2);
				}
			}
		}

		public static void Disband(VillageBanner b)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: 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)
			if ((Object)(object)b == (Object)null || b.Zdo == null || (Object)(object)_prefab == (Object)null)
			{
				return;
			}
			VillageHome.Flush(b);
			List<VillagerRecord> list = VillagerCodec.Decode(b.Zdo.GetString("txcv_villagers", ""));
			if (list.Count == 0)
			{
				return;
			}
			ZNetView component = Object.Instantiate<GameObject>(_prefab, b.Position, Quaternion.identity).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				VillagePlugin.Log.LogWarning((object)("[camp] couldn't make a camp for " + b.VillageName + "."));
				return;
			}
			Copy(b.Zdo, component.GetZDO());
			VillageNet.Notice(string.Format("{0}'s banner is down. {1} for a new banner (within {2:0} m).", b.VillageName, (list.Count == 1) ? "Its villager stops work and waits" : $"Its {list.Count} villagers stop work and wait", 150f), b.Position);
			VillagePlugin.Log.LogInfo((object)string.Format("[camp] {0} ({1}) waits at {2}: {3}.", b.VillageName, b.Id, b.Position, string.Join(", ", list.Select((VillagerRecord v) => v.Name).ToArray())));
		}

		public static bool Adopt(VillageBanner b)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)b == (Object)null || b.Zdo == null || ZDOMan.instance == null)
			{
				return false;
			}
			List<ZDO> list = new List<ZDO>();
			int num = 0;
			while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("TXC_VillageCamp", list, ref num))
			{
			}
			Vector3 at = ((Component)b).transform.position;
			ZDO val = ((IEnumerable<ZDO>)list.Where((ZDO z) => z != null && z.IsValid() && z.GetString("txcv_id", "").Length > 0).OrderBy(delegate(ZDO z)
			{
				//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 val2 = z.GetPosition() - at;
				return ((Vector3)(ref val2)).sqrMagnitude;
			})).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => VillageRules.DistanceXZ(z.GetPosition().x, z.GetPosition().z, at.x, at.z) <= 150f));
			if (val == null)
			{
				return false;
			}
			Copy(val, b.Zdo);
			val.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(val);
			List<VillagerRecord> list2 = VillagerCodec.Decode(b.Zdo.GetString("txcv_villagers", ""));
			VillageNet.Notice(b.VillageName + " has a banner again. " + ((list2.Count == 1) ? "Its villager goes" : $"Its {list2.Count} villagers go") + " back to work.", at);
			VillagePlugin.Log.LogInfo((object)$"[camp] {b.VillageName} ({b.Id}) taken back by the banner at {at}.");
			return true;
		}

		public static bool Waiting(string villageId)
		{
			if (string.IsNullOrEmpty(villageId) || ZDOMan.instance == null)
			{
				return false;
			}
			if (_waitingAt > Time.time - 5f && _waitingCache.TryGetValue(villageId, out var value))
			{
				return value;
			}
			if (_waitingAt <= Time.time - 5f)
			{
				_waitingCache.Clear();
				_waitingAt = Time.time;
			}
			List<ZDO> list = new List<ZDO>();
			int num = 0;
			while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("TXC_VillageCamp", list, ref num))
			{
			}
			value = list.Any((ZDO z) => z != null && z.GetString("txcv_id", "") == villageId);
			_waitingCache[villageId] = value;
			return value;
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class BannerDisband
	{
		private static void Prefix(WearNTear __instance)
		{
			VillageBanner component = ((Component)__instance).GetComponent<VillageBanner>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsValid() || !component2.IsOwner())
			{
				return;
			}
			try
			{
				VillageCamp.Disband(component);
			}
			catch (Exception arg)
			{
				VillagePlugin.Log.LogWarning((object)$"[camp] disband failed: {arg}");
			}
		}
	}
	internal static class VillageKeepAlive
	{
		[HarmonyPatch(typeof(Game), "FixedUpdate")]
		private static class HoldPosition
		{
			private static void Postfix()
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				if (_current != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)Player.m_localPlayer != (Object)null))
				{
					ZNet.instance.SetReferencePosition(_currentPos);
				}
			}
		}

		[HarmonyPatch(typeof(Game), "Start")]
		private static class Register
		{
			private static void Postfix()
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.Register("TXCV_KeepAliveAsk", (Action<long>)OnAsk);
				}
				ZRoutedRpc instance2 = ZRoutedRpc.instance;
				if (instance2 != null)
				{
					instance2.Register<string>("TXCV_KeepAliveSay", (Action<long, string>)OnSay);
				}
				ZRoutedRpc instance3 = ZRoutedRpc.instance;
				if (instance3 != null)
				{
					instance3.Register<string>("TXCV_KeepAliveAct", (Action<long, string>)OnAct);
				}
			}
		}

		private const string RpcAsk = "TXCV_KeepAliveAsk";

		private const string RpcSay = "TXCV_KeepAliveSay";

		private const string RpcAct = "TXCV_KeepAliveAct";

		private const float TickEvery = 2f;

		private const float RaidMargin = 30f;

		private static int _noGraphics = -1;

		private static ZNet _net;

		private static bool _haveOrigin;

		private static Vector3 _origin;

		private static string _current;

		private static double _visitStart;

		private static Vector3 _currentPos;

		private static float _timer;

		private static string _off = "";

		private static string _blocked = "";

		private static readonly ServerLoad Load = new ServerLoad();

		private static float _fakeRun;

		private static float _fakeIdle;

		private static float _statsAt = -1f;

		private static readonly Dictionary<string, KeepAliveVillage> Mem = new Dictionary<string, KeepAliveVillage>();

		private static readonly Dictionary<string, Vector3> Where = new Dictionary<string, Vector3>();

		private static readonly FieldRef<RandEventSystem, RandomEvent> ForcedEvent = ((AccessTools.Field(typeof(RandEventSystem), "m_forcedEvent") != null) ? AccessTools.FieldRefAccess<RandEventSystem, RandomEvent>("m_forcedEvent") : null);

		public static bool Headless
		{
			get
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Invalid comparison between Unknown and I4
				if (_noGraphics < 0)
				{
					_noGraphics = (((int)SystemInfo.graphicsDeviceType == 4) ? 1 : 0);
				}
				if (_noGraphics != 1)
				{
					if ((Object)(object)ZNet.instance != (Object)null)
					{
						return ZNet.instance.IsDedicated();
					}
					return false;
				}
				return true;
			}
		}

		public static bool NoPeerNear(Vector3 pos, float range)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer())
			{
				return false;
			}
			return NearestPeer(instance.GetConnectedPeers(), pos) > range;
		}

		private static float NearestPeer(List<ZNetPeer> peers, Vector3 pos)
		{
			//IL_0030: 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)
			float num = float.MaxValue;
			foreach (ZNetPeer peer in peers)
			{
				if (peer != null)
				{
					float num2 = VillageRules.DistanceXZ(peer.m_refPos.x, peer.m_refPos.z, pos.x, pos.z);
					if (num2 < num)
					{
						num = num2;
					}
				}
			}
			return num;
		}

		private static void Reset()
		{
			_haveOrigin = false;
			_current = null;
			_timer = 0f;
			_off = "";
			_blocked = "";
			_statsAt = -1f;
			Mem.Clear();
			Where.Clear();
		}

		public static void Tick()
		{
			//IL_00f7: 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_01cd: 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_01df: Unknown result type (might be due to invalid IL or missing references)
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				if ((Object)(object)_net != (Object)null)
				{
					_net = null;
					Reset();
				}
				return;
			}
			if ((Object)(object)instance != (Object)(object)_net)
			{
				_net = instance;
				Reset();
			}
			if (instance.IsServer())
			{
				Measure();
			}
			_timer -= Time.deltaTime;
			if (_timer > 0f)
			{
				return;
			}
			_timer = 2f;
			string text = ((!instance.IsServer()) ? "not the server" : (((Object)(object)Player.m_localPlayer != (Object)null) ? "the server has its own player" : ((!Headless) ? "not a dedicated server" : ((!VillageConfig.KeepAlive.Value) ? "off in the settings" : ""))));
			if (text.Length > 0)
			{
				_off = text;
				if (_current != null)
				{
					Leave(instance, "turned off", backToIdle: true);
				}
				return;
			}
			if (!_haveOrigin)
			{
				_origin = new Vector3(1000000f, 0f, 1000000f);
				_haveOrigin = true;
			}
			List<ZNetPeer> connectedPeers = instance.GetConnectedPeers();
			if (connectedPeers.Count == 0)
			{
				_off = "nobody is online";
				if (_current != null)
				{
					Leave(instance, "nobody is online", backToIdle: true);
				}
				return;
			}
			_off = "";
			double timeSeconds = instance.GetTimeSeconds();
			Refresh(connectedPeers, timeSeconds);
			double checkEvery = (double)VillageConfig.KeepAliveCheckMinutes.Value * 60.0;
			List<KeepAliveVillage> all = new List<KeepAliveVillage>(Mem.Values);
			string current = null;
			if (_current != null)
			{
				if (!Mem.TryGetValue(_current, out var value))
				{
					Leave(instance, "the village is gone", backToIdle: true);
				}
				else
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					KeepAliveRules.Verdict verdict = KeepAliveRules.Judge(value, _visitStart, timeSeconds, Load.Heavy(realtimeSinceStartup));
					if (verdict == KeepAliveRules.Verdict.Stay)
					{
						if (instance.GetReferencePosition() != _currentPos)
						{
							instance.SetReferencePosition(_currentPos);
						}
						return;
					}
					current = value.Id;
					switch (verdict)
					{
					case KeepAliveRules.Verdict.Heavy:
					{
						string why = $"too heavy for the server, {Load.RunAverage(realtimeSinceStartup):0} FPS, {IdleFps()} when idle";
						KeepAliveRules.RestHeavy(value, timeSeconds, (double)VillageConfig.KeepAliveRestMinutes.Value * 60.0);
						Leave(instance, why, backToIdle: true);
						VillagePlugin.Log.LogInfo((object)("[keepalive] " + value.Name + " rests for " + Minutes(value.RestFor) + "."));
						return;
					}
					case KeepAliveRules.Verdict.Idle:
						value.IdleWorkAt = value.WorkAt;
						value.IdleScoreAt = value.ScoreAt;
						break;
					}
					KeepAliveVillage keepAliveVillage = (Load.BaselineTooLow ? null : KeepAliveRules.Next(all, current, timeSeconds, checkEvery));
					Leave(instance, KeepAliveRules.Why(verdict), keepAliveVillage == null);
				}
			}
			if (Load.BaselineTooLow)
			{
				string text2 = $"the server runs at {Load.Baseline:0} FPS with no village, under the {VillageConfig.KeepAliveMinFps.Value:0} FPS limit";
				if (_blocked.Length == 0)
				{
					VillagePlugin.Log.LogInfo((object)("[keepalive] Running no village (" + text2 + ")."));
				}
				_blocked = text2;
			}
			else
			{
				_blocked = "";
				KeepAliveVillage keepAliveVillage2 = KeepAliveRules.Next(all, current, timeSeconds, checkEvery);
				if (keepAliveVillage2 != null)
				{
					Run(instance, keepAliveVillage2, timeSeconds);
				}
			}
		}

		private static string IdleFps()
		{
			if (!(Load.Baseline >= 0f))
			{
				return "not measured";
			}
			return $"{Load.Baseline:0}";
		}

		private static void Measure()
		{
			bool flag = _current != null;
			float num = (flag ? _fakeRun : _fakeIdle);
			float num2 = ((num > 0f) ? (1f / num) : Time.unscaledDeltaTime);
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Load.Limit = VillageConfig.KeepAliveMinFps.Value;
			Load.MaxDrop = VillageConfig.KeepAliveMaxDrop.Value / 100f;
			Load.Sample(realtimeSinceStartup, num2, flag);
			float num3 = VillageConfig.ServerStatsLogMinutes.Value * 60f;
			if (num3 <= 0f || !Headless)
			{
				_statsAt = -1f;
			}
			else if (_statsAt < 0f)
			{
				_statsAt = realtimeSinceStartup + num3;
			}
			else
			{
				if (realtimeSinceStartup < _statsAt)
				{
					return;
				}
				_statsAt = realtimeSinceStartup + num3;
				foreach (string item in ServerStat())
				{
					VillagePlugin.Log.LogInfo((object)("[serverstat] " + item));
				}
				Perf.Reset();
			}
		}

		private static void Refresh(List<ZNetPeer> peers, double now)
		{
			//IL_00d8: 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_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>();
			RandomEvent e = (((Object)(object)RandEventSystem.instance != (Object)null) ? RandEventSystem.instance.GetCurrentRandomEvent() : null);
			RandomEvent e2 = (((Object)(object)RandEventSystem.instance != (Object)null && ForcedEvent != null) ? ForcedEvent.Invoke(RandEventSystem.instance) : null);
			foreach (VillageInfo item in VillageNet.Known)
			{
				if (!string.IsNullOrEmpty(item.Id))
				{
					hashSet.Add(item.Id);
					if (!Mem.TryGetValue(item.Id, out var value))
					{
						Dictionary<string, KeepAliveVillage> mem = Mem;
						string id = item.Id;
						KeepAliveVillage obj = new KeepAliveVillage
						{
							Id = item.Id
						};
						value = obj;
						mem[id] = obj;
					}
					value.Name = item.Name;
					Where[item.Id] = item.Pos;
					value.NearestPeer = NearestPeer(peers, item.Pos);
					float reach = VillageTiers.Radius(item.Level, VillageConfig.VillageRadius.Value) + 30f;
					value.Raid = Near(e, item.Pos, reach) || Near(e2, item.Pos, reach);
					ZDO val = ((ZDOMan.instance != null && item.Zid != ZDOID.None) ? ZDOMan.instance.GetZDO(item.Zid) : null);
					if (val != null)
					{
						value.WorkAt = val.GetLong("txcv_workat", 0L);
						value.Score = val.GetInt("txcv_workscore", -1);
						value.ScoreAt = val.GetLong("txcv_workscoreat", 0L);
						value.ScoreWhy = val.GetString("txcv_workwhy", "");
					}
					if (value.Fake >= 0)
					{
						value.Score = value.Fake;
						value.ScoreAt = value.FakeAt;
						value.ScoreWhy = "set by an admin";
					}
					if (KeepAliveRules.Covered(value))
					{
						value.LastSim = now;
						value.IdleWorkAt = -1.0;
						value.IdleScoreAt = -1.0;
					}
					else if (value.Id == _current)
					{
						value.LastSim = now;
						KeepAliveRules.NoteScore(value, _visitStart, now);
					}
				}
			}
			if (!VillageNet.HaveList)
			{
				return;
			}
			foreach (string item2 in new List<string>(Mem.Keys))
			{
				if (!hashSet.Contains(item2))
				{
					Mem.Remove(item2);
					Where.Remove(item2);
				}
			}
		}

		private static bool Near(RandomEvent e, Vector3 pos, float reach)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (e != null)
			{
				return VillageRules.DistanceXZ(e.m_pos.x, e.m_pos.z, pos.x, pos.z) <= reach;
			}
			return false;
		}

		private static void Run(ZNet net, KeepAliveVillage v, double now)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if (Where.TryGetValue(v.Id, out var value))
			{
				_current = v.Id;
				_visitStart = now;
				_currentPos = value;
				string text = ((!KeepAliveRules.WantsWork(v)) ? "a check" : (KeepAliveRules.ScoreKnown(v) ? WorkScoreRules.Describe(v.Score, v.ScoreWhy) : "work to do"));
				v.IdleWorkAt = -1.0;
				v.IdleScoreAt = -1.0;
				v.ScoreBusyAt = -1.0;
				MoveTo(net, value);
				Load.NewRun(Time.realtimeSinceStartup);
				VillagePlugin.Log.LogInfo((object)("[keepalive] Running " + v.Name + " (no player near, " + text + ")."));
			}
		}

		private static void Leave(ZNet net, string why, bool backToIdle)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			string current = _current;
			KeepAliveVillage value;
			string text = ((current != null && Mem.TryGetValue(current, out value)) ? value.Name : "a village");
			try
			{
				foreach (VillagerBody item in VillagerBody.All)
				{
					if ((Object)(object)item != (Object)null && item.OwnedHere && item.VillageId == current)
					{
						item.FlushSkills();
					}
				}
				VillageHome.FlushAll();
			}
			catch (Exception ex)
			{
				VillagePlugin.Log.LogWarning((object)("[keepalive] Saving " + text + " before leaving failed. " + ex.Message));
			}
			if (current != null && Mem.TryGetValue(current, out var value2) && (Object)(object)net != (Object)null)
			{
				value2.LastSim = net.GetTimeSeconds();
			}
			_current = null;
			if (backToIdle && _haveOrigin && (Obj