Decompiled source of Hearthfolk v0.1.3

plugins/Hearthfolk/Hearthfolk.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Hearthfolk.Core;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Hearthfolk
{
	internal static class CommandRegistrar
	{
		private sealed class HearthfolkCommand : ConsoleCommand
		{
			public override string Name => "hearthfolk";

			public override string Help => "hearthfolk list | inspect <name> | repair";

			public override bool IsCheat => true;

			public override void Run(string[] args)
			{
				if (args.Length == 0 || args[0].Equals("list", StringComparison.OrdinalIgnoreCase))
				{
					Console.instance.Print("Hearthfolk: " + VillageRegistry.AllTablets.Count() + " villages, " + VillageRegistry.AllVillagers.Count() + " loaded villagers, " + VillageRegistry.AllStations.Count() + " loaded stations.");
					{
						foreach (StoneTablet allTablet in VillageRegistry.AllTablets)
						{
							Console.instance.Print(allTablet.VillageId + " residents=" + VillageRegistry.Residents(allTablet.VillageId).Count() + " recruited=" + allTablet.RecruitmentCount);
						}
						return;
					}
				}
				if (args[0].Equals("inspect", StringComparison.OrdinalIgnoreCase) && args.Length > 1)
				{
					string name = string.Join(" ", args.Skip(1).ToArray());
					VillagerAgent villagerAgent = VillageRegistry.AllVillagers.FirstOrDefault((VillagerAgent x) => x.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase));
					Console.instance.Print(Object.op_Implicit((Object)(object)villagerAgent) ? (villagerAgent.DisplayName + " state=" + villagerAgent.State.ToString() + " hunger=" + villagerAgent.Hunger.ToString("0.0") + " fatigue=" + villagerAgent.Fatigue.ToString("0.0") + " job=" + villagerAgent.AssignedJob()) : "Villager not found.");
				}
				else if (args[0].Equals("repair", StringComparison.OrdinalIgnoreCase))
				{
					int num = 0;
					foreach (VillageWorkstation item in VillageRegistry.AllStations.Where((VillageWorkstation s) => !Object.op_Implicit((Object)(object)VillageRegistry.ByVillage(s.VillageId))))
					{
						item.ClearAssignment();
						item.SetBlocker("Village link is invalid; rebuild station inside a village.");
						num++;
					}
					Console.instance.Print("Repaired or marked " + num + " orphan stations.");
				}
				else
				{
					Console.instance.Print(((ConsoleCommand)this).Help);
				}
			}
		}

		internal static void Register()
		{
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new HearthfolkCommand());
		}
	}
	internal static class HearthfolkLocalization
	{
		internal static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "hf_hammer", "Hearthfolk Hammer" },
				{ "hf_hammer_desc", "Build and manage a Hearthfolk settlement." },
				{ "hf_tablet", "Hearthfolk Stone Tablet" },
				{ "hf_tablet_desc", "Calls new villagers and anchors a shared settlement." },
				{ "hf_pantry", "Village Pantry" },
				{ "hf_pantry_desc", "Villagers eat only from the container linked here." },
				{ "hf_woodcutting", "Woodcutting Workbench" },
				{ "hf_woodcutting_desc", "Assign a villager to fell nearby trees." },
				{ "hf_planting", "Tree-Planting Workbench" },
				{ "hf_planting_desc", "Assign a villager to plant supplied tree seeds." },
				{ "hf_fishing", "Fishing Workbench" },
				{ "hf_fishing_desc", "Assign a villager to fish from nearby water." },
				{ "hf_guard", "Guard Workbench" },
				{ "hf_guard_desc", "Assign a villager to patrol and defend the village." }
			});
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class TabletPlacementPatch
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)piece) || piece.m_name != "$hf_tablet")
			{
				return true;
			}
			if (VillageRegistry.CanPlaceTablet(((Component)piece).transform.position))
			{
				return true;
			}
			((Character)__instance).Message((MessageType)2, "Another Hearthfolk tablet is too close.", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
	internal static class TabletRemovalPatch
	{
		private static void Postfix(Piece __instance, ref bool __result)
		{
			StoneTablet stoneTablet = (Object.op_Implicit((Object)(object)__instance) ? ((Component)__instance).GetComponent<StoneTablet>() : null);
			if (Object.op_Implicit((Object)(object)stoneTablet) && stoneTablet.Zdo != null && stoneTablet.ResidentIds.Length != 0 && !HearthfolkPlugin.Settings.ForceDisband.Value)
			{
				__result = false;
			}
		}
	}
	[BepInPlugin("com.hearthfolk.valheim", "Hearthfolk", "0.1.3")]
	[BepInDependency("com.jotunn.jotunn", "2.30.0")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class HearthfolkPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.hearthfolk.valheim";

		public const string Name = "Hearthfolk";

		public const string Version = "0.1.3";

		internal const bool IsDebugBuild = true;

		private Harmony harmony;

		internal static HearthfolkPlugin Instance { get; private set; }

		internal static HearthfolkConfig Settings { get; private set; }

		internal static bool RuntimeReady { get; set; }

		private void Awake()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			Instance = this;
			Settings = new HearthfolkConfig(((BaseUnityPlugin)this).Config);
			harmony = new Harmony("com.hearthfolk.valheim");
			harmony.PatchAll(typeof(HearthfolkPlugin).Assembly);
			HearthfolkLocalization.Register();
			PrefabRegistrar.Initialize();
			CommandRegistrar.Register();
			RuntimeReady = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hearthfolk 0.1.3 initialized (DEBUG: recruitment costs disabled)");
		}

		private void OnGUI()
		{
			if (RuntimeReady && Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated())
			{
				VillageWindow.Draw();
				DebugOverlay.Draw();
			}
		}

		private void OnDestroy()
		{
			PrefabRegistrar.Shutdown();
			VillageWindow.Close();
			PortraitCache.Clear();
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		internal void LogError(string message)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)message);
		}
	}
	internal sealed class HearthfolkConfig
	{
		internal readonly ConfigEntry<string> SummonResource;

		internal readonly ConfigEntry<int> BaseRecruitCost;

		internal readonly ConfigEntry<int> RecruitGrowth;

		internal readonly ConfigEntry<int> RecruitCostCap;

		internal readonly ConfigEntry<int> RerollCost;

		internal readonly ConfigEntry<float> VillageRadius;

		internal readonly ConfigEntry<float> TabletSeparation;

		internal readonly ConfigEntry<int> VillagerCap;

		internal readonly ConfigEntry<float> HungerPerMinute;

		internal readonly ConfigEntry<float> FatiguePerMinute;

		internal readonly ConfigEntry<float> StarvationGraceMinutes;

		internal readonly ConfigEntry<float> CatchupMinutes;

		internal readonly ConfigEntry<float> JobRadius;

		internal readonly ConfigEntry<bool> DebugOverlay;

		internal readonly ConfigEntry<bool> ForceDisband;

		internal HearthfolkConfig(ConfigFile file)
		{
			SummonResource = file.Bind<string>("Recruitment", "Resource prefab", "SurtlingCore", "Prefab consumed by recruitment and rerolls.");
			BaseRecruitCost = file.Bind<int>("Recruitment", "Base cost", 1, "Cost of the first villager.");
			RecruitGrowth = file.Bind<int>("Recruitment", "Cost growth", 1, "Linear increase per successful recruitment.");
			RecruitCostCap = file.Bind<int>("Recruitment", "Cost cap", 0, "Maximum cost, or zero for no cap.");
			RerollCost = file.Bind<int>("Recruitment", "Reroll cost", 1, "Cost to replace both candidates.");
			VillageRadius = file.Bind<float>("Settlement", "Village radius", 80f, "Settlement boundary in metres.");
			TabletSeparation = file.Bind<float>("Settlement", "Minimum tablet separation", 160f, "Minimum distance between tablets.");
			VillagerCap = file.Bind<int>("Settlement", "Villager cap", 20, "Living villagers allowed per tablet.");
			HungerPerMinute = file.Bind<float>("Needs", "Hunger drain per minute", 0.35f, "Base hunger drain while time advances.");
			FatiguePerMinute = file.Bind<float>("Needs", "Fatigue drain per minute", 0.25f, "Base fatigue drain while time advances.");
			StarvationGraceMinutes = file.Bind<float>("Needs", "Starvation grace minutes", 15f, "Time at critical hunger before health damage.");
			CatchupMinutes = file.Bind<float>("Needs", "Maximum catchup minutes", 120f, "Maximum offline need decay applied at once.");
			JobRadius = file.Bind<float>("Jobs", "Default work radius", 30f, "Default workstation search radius.");
			DebugOverlay = file.Bind<bool>("Diagnostics", "Debug overlay", false, "Show settlement and NPC diagnostics.");
			ForceDisband = file.Bind<bool>("Administration", "Allow forced disband", false, "Allow a populated tablet to be removed.");
		}
	}
	internal static class HearthfolkKeys
	{
		internal const int Schema = 1;

		internal const string Prefix = "hf_";

		internal const string SchemaVersion = "hf_schema";

		internal const string VillageId = "hf_village";

		internal const string Founder = "hf_founder";

		internal const string Recruits = "hf_recruits";

		internal const string Residents = "hf_residents";

		internal const string Candidate0 = "hf_candidate0";

		internal const string Candidate1 = "hf_candidate1";

		internal const string CandidateGeneration = "hf_candidate_generation";

		internal const string VillagerId = "hf_villager";

		internal const string VillagerName = "hf_name";

		internal const string Traits = "hf_traits";

		internal const string Skills = "hf_skills";

		internal const string Hunger = "hf_hunger";

		internal const string Fatigue = "hf_fatigue";

		internal const string Starving = "hf_starving";

		internal const string LastNeedsTick = "hf_needs_tick";

		internal const string State = "hf_state";

		internal const string Job = "hf_job";

		internal const string Station = "hf_station";

		internal const string Bed = "hf_bed";

		internal const string Pantry = "hf_pantry";

		internal const string FoodHealth = "hf_food_health";

		internal const string FoodStamina = "hf_food_stamina";

		internal const string FoodUntil = "hf_food_until";

		internal const string AppearanceSeed = "hf_appearance";

		internal const string BaseHealth = "hf_base_health";

		internal const string BaseStamina = "hf_base_stamina";

		internal const string WorkEnabled = "hf_enabled";

		internal const string WorkRadius = "hf_radius";

		internal const string WorkType = "hf_work_type";

		internal const string Input = "hf_input";

		internal const string Output = "hf_output";

		internal const string Assigned = "hf_assigned";

		internal const string Blocker = "hf_blocker";
	}
	internal static class PrefabRegistrar
	{
		internal const string Hammer = "HearthfolkHammer";

		internal const string PieceTable = "_HearthfolkPieceTable";

		internal const string Tablet = "piece_hearthfolk_tablet";

		internal const string Pantry = "piece_hearthfolk_pantry";

		internal const string Woodcutting = "piece_hearthfolk_woodcutting";

		internal const string Planting = "piece_hearthfolk_planting";

		internal const string Fishing = "piece_hearthfolk_fishing";

		internal const string Guard = "piece_hearthfolk_guard";

		internal const string Villager = "HearthfolkVillager";

		private static bool registered;

		internal static void Initialize()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Register;
		}

		internal static void Shutdown()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			VillageRegistry.Clear();
		}

		private static void Register()
		{
			if (registered)
			{
				return;
			}
			registered = true;
			try
			{
				RegisterPieceTableAndHammer();
				RegisterTablet();
				RegisterStation("piece_hearthfolk_pantry", "piece_chest_wood", "$hf_pantry", "$hf_pantry_desc", JobType.None, typeof(PantryMarker));
				RegisterStation("piece_hearthfolk_woodcutting", "piece_workbench", "$hf_woodcutting", "$hf_woodcutting_desc", JobType.Woodcutting, null);
				RegisterStation("piece_hearthfolk_planting", "piece_cultivator", "$hf_planting", "$hf_planting_desc", JobType.TreePlanting, null);
				RegisterStation("piece_hearthfolk_fishing", "piece_cauldron", "$hf_fishing", "$hf_fishing_desc", JobType.Fishing, null);
				RegisterStation("piece_hearthfolk_guard", "piece_guardstone", "$hf_guard", "$hf_guard_desc", JobType.Guarding, null);
				RegisterVillager();
			}
			catch (Exception ex)
			{
				HearthfolkPlugin.RuntimeReady = false;
				HearthfolkPlugin.Instance.LogError("Prefab registration failed: " + ex);
			}
			finally
			{
				PrefabManager.OnVanillaPrefabsAvailable -= Register;
			}
		}

		private static void RegisterPieceTableAndHammer()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("_HearthfolkPieceTable", "_HammerPieceTable");
			PieceTableConfig val2 = new PieceTableConfig
			{
				UseCategories = false,
				UseCustomCategories = false,
				CanRemovePieces = true
			};
			PieceManager.Instance.AddPieceTable(new CustomPieceTable(val, val2));
			ItemConfig val3 = new ItemConfig
			{
				Name = "$hf_hammer",
				Description = "$hf_hammer_desc",
				PieceTable = "_HearthfolkPieceTable",
				CraftingStation = "piece_workbench",
				Amount = 1
			};
			val3.AddRequirement("Wood", 5, 0);
			val3.AddRequirement("Stone", 2, 0);
			ItemManager.Instance.AddItem(new CustomItem("HearthfolkHammer", "Hammer", val3));
		}

		private static void RegisterTablet()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			string text = Existing("piece_runestone", "piece_guardstone", "piece_workbench");
			PieceConfig val = Piece("piece_hearthfolk_tablet", "$hf_tablet", "$hf_tablet_desc", "Village");
			val.AddRequirement("Stone", 20, true);
			GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("piece_hearthfolk_tablet", text);
			CraftingStation component = val2.GetComponent<CraftingStation>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			val2.AddComponent<StoneTablet>();
			PieceManager.Instance.AddPiece(new CustomPiece(val2, false, val));
		}

		private static void RegisterStation(string name, string preferredSource, string displayName, string description, JobType job, Type extra)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			string text = Existing(preferredSource, "piece_workbench", "piece_chest_wood");
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(name, text);
			VillageWorkstation villageWorkstation = val.AddComponent<VillageWorkstation>();
			villageWorkstation.ConfiguredJob = job;
			if (extra != null)
			{
				val.AddComponent(extra);
			}
			PieceConfig val2 = Piece(name, displayName, description, job switch
			{
				JobType.None => "Village", 
				JobType.Guarding => "Defence", 
				_ => "Gathering", 
			});
			val2.AddRequirement("Wood", 10, true);
			PieceManager.Instance.AddPiece(new CustomPiece(val, false, val2));
		}

		private static PieceConfig Piece(string name, string display, string description, string category)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return new PieceConfig
			{
				Name = display,
				Description = description,
				PieceTable = "_HearthfolkPieceTable",
				Category = category,
				AllowedInDungeons = false
			};
		}

		private static string Existing(params string[] candidates)
		{
			foreach (string text in candidates)
			{
				if (Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(text)))
				{
					return text;
				}
			}
			throw new InvalidOperationException("No compatible vanilla prefab found: " + string.Join(", ", candidates));
		}

		private static void RegisterVillager()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0068: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			string text = Existing("Dverger", "Hildir", "Goblin");
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("HearthfolkVillager", text);
			val.AddComponent<VillagerAgent>();
			CreatureConfig val2 = new CreatureConfig
			{
				Name = "$hf_villager",
				Group = "Hearthfolk",
				Faction = (Faction)0
			};
			CreatureManager.Instance.AddCreature(new CustomCreature(val, false, val2));
		}
	}
	internal static class SaveMigration
	{
		internal static void Tablet(ZDO zdo)
		{
			if (zdo != null && zdo.GetInt("hf_schema", 0) < 1)
			{
				if (string.IsNullOrEmpty(zdo.GetString("hf_residents", string.Empty)))
				{
					zdo.Set("hf_residents", string.Empty);
				}
				zdo.Set("hf_schema", 1);
			}
		}

		internal static void Villager(ZDO zdo)
		{
			if (zdo != null)
			{
				int num = zdo.GetInt("hf_job", 0);
				if (!Enum.IsDefined(typeof(JobType), num))
				{
					zdo.Set("hf_job", 0);
				}
				int num2 = zdo.GetInt("hf_state", 0);
				if (!Enum.IsDefined(typeof(VillagerState), num2))
				{
					zdo.Set("hf_state", 0);
				}
				if (zdo.GetLong("hf_needs_tick", 0L) <= 0)
				{
					zdo.Set("hf_needs_tick", DateTime.UtcNow.Ticks);
				}
				zdo.Set("hf_schema", 1);
			}
		}

		internal static void Workstation(ZDO zdo, JobType configured)
		{
			if (zdo != null)
			{
				int num = zdo.GetInt("hf_work_type", (int)configured);
				if (!Enum.IsDefined(typeof(JobType), num))
				{
					zdo.Set("hf_work_type", 0);
				}
				zdo.Set("hf_schema", 1);
			}
		}
	}
	public sealed class StoneTablet : MonoBehaviour, Interactable, Hoverable
	{
		private ZNetView view;

		private Piece piece;

		private float nextEnsure;

		public string VillageId => (Object.op_Implicit((Object)(object)view) && view.IsValid()) ? view.GetZDO().GetString("hf_village", string.Empty) : string.Empty;

		public int RecruitmentCount => (Zdo != null) ? Zdo.GetInt("hf_recruits", 0) : 0;

		public string[] ResidentIds => (Zdo == null) ? new string[0] : Zdo.GetString("hf_residents", string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);

		internal ZDO Zdo => (Object.op_Implicit((Object)(object)view) && view.IsValid()) ? view.GetZDO() : null;

		public int CurrentCost => 0;

		public int CurrentRerollCost => 0;

		public string CostResource => HearthfolkPlugin.Settings.SummonResource.Value;

		private void Awake()
		{
			view = ((Component)this).GetComponent<ZNetView>();
			piece = ((Component)this).GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)view))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			view.Register<int, long>("HF_Recruit", (Action<long, int, long>)RpcRecruit);
			view.Register<long>("HF_Reroll", (Action<long, long>)RpcReroll);
			view.Register<string>("HF_ResidentDied", (Action<long, string>)RpcResidentDied);
			VillageRegistry.Add(this);
		}

		private void Start()
		{
			EnsureInitialized();
		}

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

		private void Update()
		{
			if (Time.time >= nextEnsure)
			{
				nextEnsure = Time.time + 5f;
				EnsureInitialized();
			}
		}

		private void EnsureInitialized()
		{
			ZDO zdo = Zdo;
			if (zdo != null && view.IsOwner())
			{
				SaveMigration.Tablet(zdo);
				if (string.IsNullOrEmpty(zdo.GetString("hf_village", string.Empty)))
				{
					zdo.Set("hf_schema", 1);
					zdo.Set("hf_village", Guid.NewGuid().ToString("N"));
					zdo.Set("hf_founder", Object.op_Implicit((Object)(object)piece) ? piece.GetCreator() : 0);
					zdo.Set("hf_recruits", 0);
					zdo.Set("hf_residents", string.Empty);
					zdo.Set("hf_candidate_generation", 0);
				}
				EnsureCandidates(zdo);
			}
		}

		private void EnsureCandidates(ZDO zdo)
		{
			if (!CandidateRecord.TryDecode(zdo.GetString("hf_candidate0", string.Empty), out var result) || !CandidateRecord.TryDecode(zdo.GetString("hf_candidate1", string.Empty), out result))
			{
				GenerateCandidates(zdo);
			}
		}

		private void GenerateCandidates(ZDO zdo)
		{
			int num = zdo.GetInt("hf_candidate_generation", 0) + 1;
			int num2 = (((object)Unsafe.As<ZDOID, ZDOID>(ref zdo.m_uid)/*cast due to .constrained prefix*/).GetHashCode() * 397) ^ num;
			HashSet<string> hashSet = new HashSet<string>(from v in VillageRegistry.Residents(VillageId)
				select v.DisplayName, StringComparer.OrdinalIgnoreCase);
			CandidateGenerator candidateGenerator = new CandidateGenerator();
			CandidateRecord candidateRecord = candidateGenerator.Generate(num2, hashSet);
			hashSet.Add(candidateRecord.Name);
			CandidateRecord candidateRecord2 = candidateGenerator.Generate(num2 ^ -1640531527, hashSet);
			zdo.Set("hf_candidate_generation", num);
			zdo.Set("hf_candidate0", candidateRecord.Encode());
			zdo.Set("hf_candidate1", candidateRecord2.Encode());
		}

		public CandidateRecord Candidate(int index)
		{
			CandidateRecord result;
			return (Zdo != null && CandidateRecord.TryDecode(Zdo.GetString((index == 0) ? "hf_candidate0" : "hf_candidate1", string.Empty), out result)) ? result : null;
		}

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

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public string GetHoverName()
		{
			return "Hearthfolk Stone Tablet";
		}

		public string GetHoverText()
		{
			return GetHoverName() + "\n[<color=yellow><b>$KEY_Use</b></color>] Manage village";
		}

		public float GetHoverOffset()
		{
			return 1.5f;
		}

		internal void RequestRecruit(int index)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)view))
			{
				view.InvokeRPC("HF_Recruit", new object[2]
				{
					index,
					Player.m_localPlayer.GetPlayerID()
				});
			}
		}

		internal void RequestReroll()
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)view))
			{
				view.InvokeRPC("HF_Reroll", new object[1] { Player.m_localPlayer.GetPlayerID() });
			}
		}

		private void RpcRecruit(long sender, int index, long claimedPlayerId)
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: 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_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			if (!view.IsOwner() || index < 0 || index > 1)
			{
				return;
			}
			Player player = VillagePermissions.ResolvePlayer(sender, claimedPlayerId);
			if (!ValidateManager(player, out var error))
			{
				Reply(player, error);
				return;
			}
			if (ResidentIds.Length >= HearthfolkPlugin.Settings.VillagerCap.Value)
			{
				Reply(player, "Village population cap reached.");
				return;
			}
			CandidateRecord candidateRecord = Candidate(index);
			if (candidateRecord == null)
			{
				EnsureCandidates(Zdo);
				Reply(player, "Candidates were repaired; try again.");
				return;
			}
			int currentCost = CurrentCost;
			if (!TakeResource(player, currentCost))
			{
				Reply(player, "Need " + currentCost + " " + CostResource + ".");
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab("HearthfolkVillager");
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				Refund(player, currentCost);
				Reply(player, "Villager prefab is unavailable.");
				return;
			}
			Vector3 val = ((Component)this).transform.position + ((Component)this).transform.forward * 3f + Vector3.up;
			GameObject val2 = Object.Instantiate<GameObject>(prefab, val, Quaternion.LookRotation(-((Component)this).transform.forward));
			VillagerAgent component = val2.GetComponent<VillagerAgent>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				Object.Destroy((Object)(object)val2);
				Refund(player, currentCost);
				Reply(player, "Villager initialization failed.");
			}
			else
			{
				component.Initialize(VillageId, candidateRecord);
				AddResident(candidateRecord.Id);
				Zdo.Set("hf_recruits", RecruitmentCount + 1);
				GenerateCandidates(Zdo);
				Reply(player, candidateRecord.Name + " joined " + GetHoverName() + ".");
			}
		}

		private void RpcReroll(long sender, long claimedPlayerId)
		{
			if (!view.IsOwner())
			{
				return;
			}
			Player player = VillagePermissions.ResolvePlayer(sender, claimedPlayerId);
			if (!ValidateManager(player, out var error))
			{
				Reply(player, error);
				return;
			}
			int currentRerollCost = CurrentRerollCost;
			if (!TakeResource(player, currentRerollCost))
			{
				Reply(player, "Need " + currentRerollCost + " " + CostResource + ".");
			}
			else
			{
				GenerateCandidates(Zdo);
				Reply(player, "Two new candidates answered the tablet.");
			}
		}

		private bool ValidateManager(Player player, out string error)
		{
			//IL_0027: 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)
			error = string.Empty;
			if (!Object.op_Implicit((Object)(object)player))
			{
				error = "Could not validate requesting player.";
				return false;
			}
			if (Vector3.Distance(((Component)player).transform.position, ((Component)this).transform.position) > 8f)
			{
				error = "Move closer to the tablet.";
				return false;
			}
			if (!VillagePermissions.CanManage(this, player))
			{
				error = "You do not have permission to manage this village.";
				return false;
			}
			return true;
		}

		private bool TakeResource(Player player, int amount)
		{
			if (amount <= 0)
			{
				return true;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			string prefab = CostResource;
			ItemData[] array = (from i in inventory.GetAllItems()
				where i != null && Object.op_Implicit((Object)(object)i.m_dropPrefab) && string.Equals(((Object)i.m_dropPrefab).name.Replace("(Clone)", string.Empty), prefab, StringComparison.OrdinalIgnoreCase)
				select i).ToArray();
			if (array.Sum((ItemData i) => i.m_stack) < amount)
			{
				return false;
			}
			int num = amount;
			ItemData[] array2 = array;
			foreach (ItemData val in array2)
			{
				int num3 = Math.Min(num, val.m_stack);
				if (!inventory.RemoveItem(val, num3))
				{
					return false;
				}
				num -= num3;
				if (num == 0)
				{
					return true;
				}
			}
			return false;
		}

		private void Refund(Player player, int amount)
		{
			GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(CostResource) : null);
			if (Object.op_Implicit((Object)(object)player) && Object.op_Implicit((Object)(object)val) && amount > 0)
			{
				((Humanoid)player).GetInventory().AddItem(val, amount);
			}
		}

		private static void Reply(Player player, string message)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		internal void AddResident(string id)
		{
			if (Zdo != null && !string.IsNullOrEmpty(id))
			{
				HashSet<string> hashSet = new HashSet<string>(ResidentIds, StringComparer.Ordinal);
				hashSet.Add(id);
				Zdo.Set("hf_residents", string.Join(",", hashSet.ToArray()));
			}
		}

		internal void RemoveResident(string id)
		{
			if (Zdo != null && !string.IsNullOrEmpty(id))
			{
				Zdo.Set("hf_residents", string.Join(",", ResidentIds.Where((string x) => x != id).ToArray()));
			}
		}

		internal void NotifyResidentDied(string id)
		{
			if (Object.op_Implicit((Object)(object)view) && !string.IsNullOrEmpty(id))
			{
				view.InvokeRPC("HF_ResidentDied", new object[1] { id });
			}
		}

		private void RpcResidentDied(long sender, string id)
		{
			if (view.IsOwner())
			{
				VillagerAgent villagerAgent = VillageRegistry.Villager(id);
				if ((Object)(object)villagerAgent == (Object)null || villagerAgent.IsDead)
				{
					RemoveResident(id);
				}
			}
		}
	}
	internal static class VillagePermissions
	{
		private static readonly MethodInfo IsPermitted = AccessTools.Method(typeof(PrivateArea), "IsPermitted", new Type[1] { typeof(long) }, (Type[])null);

		internal static Player ResolvePlayer(long sender, long claimedId)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (allPlayer.GetPlayerID() == claimedId)
				{
					if (sender == ZNet.GetUID())
					{
						return allPlayer;
					}
					ZNetPeer peer = ZNet.instance.GetPeer(sender);
					if (peer != null && peer.m_characterID == ((Character)allPlayer).GetZDOID())
					{
						return allPlayer;
					}
				}
			}
			return null;
		}

		internal static bool CanManage(StoneTablet tablet, Player player)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)tablet) || !Object.op_Implicit((Object)(object)player) || tablet.Zdo == null)
			{
				return false;
			}
			if (tablet.Zdo.GetLong("hf_founder", 0L) == player.GetPlayerID())
			{
				return true;
			}
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && ZNet.instance.LocalPlayerIsAdminOrHost())
			{
				return true;
			}
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && PrivateArea.CheckAccess(((Component)tablet).transform.position, 0f, false, false))
			{
				return true;
			}
			return false;
		}

		internal static bool WorldTargetAllowed(StoneTablet tablet, Vector3 position)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)tablet) || tablet.Zdo == null)
			{
				return false;
			}
			long num = tablet.Zdo.GetLong("hf_founder", 0L);
			PrivateArea[] array = Object.FindObjectsByType<PrivateArea>((FindObjectsSortMode)0);
			foreach (PrivateArea val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && !(Vector3.Distance(((Component)val).transform.position, position) > val.m_radius))
				{
					Piece component = ((Component)val).GetComponent<Piece>();
					if ((!Object.op_Implicit((Object)(object)component) || component.GetCreator() != num) && (!(IsPermitted != null) || !(bool)IsPermitted.Invoke(val, new object[1] { num })))
					{
						return false;
					}
				}
			}
			return true;
		}
	}
	public sealed class VillagerAgent : MonoBehaviour
	{
		private ZNetView view;

		private Character character;

		private Humanoid humanoid;

		private BaseAI ai;

		private float nextTick;

		private bool cleaned;

		public string VillagerId => (Zdo == null) ? string.Empty : Zdo.GetString("hf_villager", string.Empty);

		public string VillageId => (Zdo == null) ? string.Empty : Zdo.GetString("hf_village", string.Empty);

		public string DisplayName => (Zdo == null) ? "Villager" : Zdo.GetString("hf_name", "Villager");

		public float Hunger => (Zdo == null) ? 0f : Zdo.GetFloat("hf_hunger", 100f);

		public float Fatigue => (Zdo == null) ? 0f : Zdo.GetFloat("hf_fatigue", 100f);

		public VillagerState State => (Zdo != null) ? ((VillagerState)Zdo.GetInt("hf_state", 0)) : VillagerState.Idle;

		public bool IsDead => !Object.op_Implicit((Object)(object)character) || character.IsDead() || State == VillagerState.Dead;

		internal Character Character => character;

		internal Humanoid Humanoid => humanoid;

		internal Inventory Inventory => Object.op_Implicit((Object)(object)humanoid) ? humanoid.GetInventory() : null;

		internal ZDO Zdo => (Object.op_Implicit((Object)(object)view) && view.IsValid()) ? view.GetZDO() : null;

		private void Awake()
		{
			view = ((Component)this).GetComponent<ZNetView>();
			character = ((Component)this).GetComponent<Character>();
			humanoid = ((Component)this).GetComponent<Humanoid>();
			ai = ((Component)this).GetComponent<BaseAI>();
			VillageRegistry.Add(this);
		}

		private void Start()
		{
			if (Object.op_Implicit((Object)(object)view) && view.IsValid() && view.IsOwner())
			{
				SaveMigration.Villager(Zdo);
			}
			ApplySavedIdentity();
		}

		private void OnDestroy()
		{
			Cleanup();
			VillageRegistry.Remove(this);
		}

		internal void Initialize(string villageId, CandidateRecord candidate)
		{
			if (!Object.op_Implicit((Object)(object)view) || !view.IsValid())
			{
				return;
			}
			view.ClaimOwnership();
			ZDO zdo = Zdo;
			zdo.Set("hf_schema", 1);
			zdo.Set("hf_village", villageId);
			zdo.Set("hf_villager", candidate.Id);
			zdo.Set("hf_name", candidate.Name);
			zdo.Set("hf_appearance", candidate.AppearanceSeed);
			zdo.Set("hf_base_health", candidate.BaseHealth);
			zdo.Set("hf_base_stamina", candidate.BaseStamina);
			zdo.Set("hf_traits", string.Join(",", candidate.Traits.Select(delegate(TraitId x)
			{
				int num2 = (int)x;
				return num2.ToString(CultureInfo.InvariantCulture);
			}).ToArray()));
			zdo.Set("hf_skills", EncodeSkills(candidate.Skills));
			zdo.Set("hf_hunger", 100f);
			zdo.Set("hf_fatigue", 100f);
			zdo.Set("hf_needs_tick", DateTime.UtcNow.Ticks);
			zdo.Set("hf_state", 0);
			zdo.Set("hf_job", 0);
			if (Object.op_Implicit((Object)(object)character))
			{
				character.m_name = candidate.Name;
				float num = TraitCatalog.Product(candidate.Traits, (TraitDefinition t) => t.Health);
				character.SetMaxHealth(candidate.BaseHealth * num);
				character.SetHealth(character.GetMaxHealth());
				character.SetTamed(true);
			}
			ApplyAppearance(candidate.AppearanceSeed);
		}

		private void Update()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < nextTick)
			{
				return;
			}
			float delta = ((nextTick <= 0f) ? 5f : Mathf.Max(0.1f, Time.time - (nextTick - 5f)));
			nextTick = Time.time + 5f;
			if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || !view.IsOwner() || Zdo == null || string.IsNullOrEmpty(VillagerId))
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)character) && character.IsDead())
			{
				SetState(VillagerState.Dead);
				MarkDead();
				Cleanup();
				return;
			}
			AdvanceNeeds();
			UpdateFoodEffect();
			if (!Object.op_Implicit((Object)(object)ZoneSystem.instance) || ZoneSystem.instance.IsZoneLoaded(((Component)this).transform.position))
			{
				TickState(delta);
			}
		}

		private void AdvanceNeeds()
		{
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			long ticks = DateTime.UtcNow.Ticks;
			long num = Zdo.GetLong("hf_needs_tick", ticks);
			double elapsedSeconds = Math.Max(0.0, TimeSpan.FromTicks(Math.Max(0L, ticks - num)).TotalSeconds);
			Zdo.Set("hf_needs_tick", ticks);
			NeedSettings settings = new NeedSettings
			{
				HungerPerMinute = HearthfolkPlugin.Settings.HungerPerMinute.Value,
				FatiguePerMinute = HearthfolkPlugin.Settings.FatiguePerMinute.Value,
				StarvationThreshold = 10f,
				SleepThreshold = 20f,
				CatchupMinutes = HearthfolkPlugin.Settings.CatchupMinutes.Value
			};
			List<TraitId> traits = Traits();
			NeedSnapshot needSnapshot = NeedsEngine.Advance(new NeedSnapshot
			{
				Hunger = Hunger,
				Fatigue = Fatigue,
				StarvingSeconds = Zdo.GetFloat("hf_starving", 0f)
			}, elapsedSeconds, settings, TraitCatalog.Product(traits, (TraitDefinition t) => t.HungerUse), TraitCatalog.Product(traits, (TraitDefinition t) => t.FatigueUse));
			Zdo.Set("hf_hunger", needSnapshot.Hunger);
			Zdo.Set("hf_fatigue", needSnapshot.Fatigue);
			Zdo.Set("hf_starving", (float)needSnapshot.StarvingSeconds);
			if (needSnapshot.StarvingSeconds > (double)(HearthfolkPlugin.Settings.StarvationGraceMinutes.Value * 60f) && Object.op_Implicit((Object)(object)character) && !character.IsDead())
			{
				HitData val = new HitData();
				val.m_damage.m_damage = Mathf.Max(1f, character.GetMaxHealth() * 0.02f);
				val.m_point = ((Component)this).transform.position;
				character.Damage(val);
			}
		}

		private void TickState(float delta)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			Character val = FindDanger(18f);
			if (Object.op_Implicit((Object)(object)val) && AssignedJob() != JobType.Guarding)
			{
				SetState(VillagerState.Danger);
				MoveAway(((Component)val).transform.position);
				return;
			}
			if (Hunger <= 15f)
			{
				SetState(VillagerState.SeekingFood);
				if (!TryEat())
				{
				}
				return;
			}
			if (Fatigue <= 20f)
			{
				SetState(VillagerState.SeekingBed);
				Rest(delta);
				return;
			}
			VillageWorkstation villageWorkstation = AssignedStation();
			if (Object.op_Implicit((Object)(object)villageWorkstation) && villageWorkstation.WorkEnabled)
			{
				SetState(VillagerState.Working);
				villageWorkstation.Tick(this, delta);
				return;
			}
			SetState(VillagerState.Idle);
			if ((Object)(object)ai != (Object)null)
			{
				ai.MoveTowards(HomePosition(), false);
			}
		}

		private Character FindDanger(float radius)
		{
			//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)
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (Object.op_Implicit((Object)(object)allCharacter) && (Object)(object)allCharacter != (Object)(object)character && !allCharacter.IsDead() && Vector3.Distance(((Component)this).transform.position, ((Component)allCharacter).transform.position) <= radius && BaseAI.IsEnemy(character, allCharacter))
				{
					return allCharacter;
				}
			}
			return null;
		}

		private bool TryEat()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Container val = Pantry();
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			if (!MoveTo(((Component)val).transform.position, 2.2f))
			{
				return true;
			}
			Inventory inventory = val.GetInventory();
			ItemData val2 = (from i in inventory.GetAllItems()
				where i != null && i.m_shared != null && i.m_shared.m_food > 0f
				orderby i.m_shared.m_food + i.m_shared.m_foodStamina descending
				select i).FirstOrDefault();
			if (val2 == null)
			{
				return false;
			}
			float food = val2.m_shared.m_food;
			float foodStamina = val2.m_shared.m_foodStamina;
			float foodBurnTime = val2.m_shared.m_foodBurnTime;
			if (!inventory.RemoveOneItem(val2))
			{
				return false;
			}
			Zdo.Set("hf_hunger", Mathf.Clamp(Hunger + 45f, 0f, 100f));
			Zdo.Set("hf_starving", 0f);
			Zdo.Set("hf_food_health", food);
			Zdo.Set("hf_food_stamina", foodStamina);
			Zdo.Set("hf_food_until", DateTime.UtcNow.AddSeconds(foodBurnTime).Ticks);
			if (Object.op_Implicit((Object)(object)character))
			{
				character.SetMaxHealth(BaseMaximumHealth() + food);
				character.Heal(Mathf.Max(1f, food * 0.25f), true);
			}
			SetState(VillagerState.Eating);
			return true;
		}

		private void Rest(float delta)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			Bed val = FindBed();
			if (!Object.op_Implicit((Object)(object)val) || MoveTo(val.GetSpawnPoint(), 1.4f))
			{
				SetState(VillagerState.Sleeping);
				Zdo.Set("hf_fatigue", Mathf.Clamp(Fatigue + delta * 2f, 0f, 100f));
			}
		}

		private Bed FindBed()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			StoneTablet tablet = VillageRegistry.ByVillage(VillageId);
			if (!Object.op_Implicit((Object)(object)tablet))
			{
				return null;
			}
			ZDOID zDOID = Zdo.GetZDOID("hf_bed");
			if (!((ZDOID)(ref zDOID)).IsNone() && (Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject val = ZNetScene.instance.FindInstance(zDOID);
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.GetComponent<Bed>()))
				{
					return val.GetComponent<Bed>();
				}
			}
			HashSet<ZDOID> claimed = new HashSet<ZDOID>(from v in VillageRegistry.Residents(VillageId)
				where (Object)(object)v != (Object)(object)this && v.Zdo != null
				select v.Zdo.GetZDOID("hf_bed") into id
				where !((ZDOID)(ref id)).IsNone()
				select id);
			Bed val2 = (from b in (from b in Object.FindObjectsByType<Bed>((FindObjectsSortMode)0)
					where Object.op_Implicit((Object)(object)b) && Vector3.Distance(((Component)b).transform.position, ((Component)tablet).transform.position) <= HearthfolkPlugin.Settings.VillageRadius.Value
					select b).Where(delegate(Bed b)
				{
					//IL_0024: Unknown result type (might be due to invalid IL or missing references)
					ZNetView component = ((Component)b).GetComponent<ZNetView>();
					return Object.op_Implicit((Object)(object)component) && component.IsValid() && !claimed.Contains(component.GetZDO().m_uid);
				})
				orderby Vector3.Distance(((Component)this).transform.position, ((Component)b).transform.position)
				select b).FirstOrDefault();
			if (Object.op_Implicit((Object)(object)val2))
			{
				Zdo.Set("hf_bed", ((Component)val2).GetComponent<ZNetView>().GetZDO().m_uid);
			}
			return val2;
		}

		private Container Pantry()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			StoneTablet stoneTablet = VillageRegistry.ByVillage(VillageId);
			if (!Object.op_Implicit((Object)(object)stoneTablet) || stoneTablet.Zdo == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			ZDOID zDOID = stoneTablet.Zdo.GetZDOID("hf_pantry");
			if (((ZDOID)(ref zDOID)).IsNone())
			{
				return null;
			}
			GameObject val = ZNetScene.instance.FindInstance(zDOID);
			return Object.op_Implicit((Object)(object)val) ? val.GetComponent<Container>() : null;
		}

		internal void Assign(VillageWorkstation station)
		{
			//IL_006c: 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)
			if (Zdo != null)
			{
				view.ClaimOwnership();
				if (!Object.op_Implicit((Object)(object)station))
				{
					Zdo.Set("hf_station", ZDOID.None);
					Zdo.Set("hf_job", 0);
				}
				else
				{
					ZNetView component = ((Component)station).GetComponent<ZNetView>();
					Zdo.Set("hf_station", component.GetZDO().m_uid);
					Zdo.Set("hf_job", (int)station.Job);
				}
			}
		}

		internal VillageWorkstation AssignedStation()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (Zdo == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			ZDOID zDOID = Zdo.GetZDOID("hf_station");
			if (((ZDOID)(ref zDOID)).IsNone())
			{
				return null;
			}
			GameObject val = ZNetScene.instance.FindInstance(zDOID);
			return Object.op_Implicit((Object)(object)val) ? val.GetComponent<VillageWorkstation>() : null;
		}

		internal JobType AssignedJob()
		{
			return (Zdo != null) ? ((JobType)Zdo.GetInt("hf_job", 0)) : JobType.None;
		}

		internal bool MoveTo(Vector3 point, float distance)
		{
			//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_0036: Unknown result type (might be due to invalid IL or missing references)
			if (Vector3.Distance(((Component)this).transform.position, point) <= distance)
			{
				return true;
			}
			if ((Object)(object)ai != (Object)null)
			{
				ai.MoveTowards(point, false);
			}
			return false;
		}

		internal void Face(Vector3 point)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)character))
			{
				Character obj = character;
				Vector3 val = point - ((Component)this).transform.position;
				obj.SetLookDir(((Vector3)(ref val)).normalized, 0f);
			}
		}

		internal float Skill(VillagerSkill skill)
		{
			Dictionary<VillagerSkill, float> dictionary = DecodeSkills((Zdo == null) ? string.Empty : Zdo.GetString("hf_skills", string.Empty));
			float value;
			return dictionary.TryGetValue(skill, out value) ? value : 1f;
		}

		internal void Award(VillagerSkill skill, float amount)
		{
			Dictionary<VillagerSkill, float> dictionary = DecodeSkills(Zdo.GetString("hf_skills", string.Empty));
			dictionary.TryGetValue(skill, out var value);
			float num = (Traits().Contains(TraitId.QuickLearner) ? 1.25f : 1f);
			dictionary[skill] = Mathf.Clamp(value + amount * num, 0f, 100f);
			Zdo.Set("hf_skills", EncodeSkills(dictionary));
		}

		internal List<TraitId> Traits()
		{
			List<TraitId> list = new List<TraitId>();
			if (Zdo == null)
			{
				return list;
			}
			string[] array = Zdo.GetString("hf_traits", string.Empty).Split(',');
			foreach (string s in array)
			{
				if (int.TryParse(s, out var result) && Enum.IsDefined(typeof(TraitId), result))
				{
					list.Add((TraitId)result);
				}
			}
			return list;
		}

		private void SetState(VillagerState state)
		{
			if (Zdo != null && State != state)
			{
				Zdo.Set("hf_state", (int)state);
			}
		}

		private Vector3 HomePosition()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			StoneTablet stoneTablet = VillageRegistry.ByVillage(VillageId);
			return Object.op_Implicit((Object)(object)stoneTablet) ? ((Component)stoneTablet).transform.position : ((Component)this).transform.position;
		}

		private void MoveAway(Vector3 danger)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)this).transform.position;
			Vector3 val = ((Component)this).transform.position - danger;
			Vector3 point = position + ((Vector3)(ref val)).normalized * 12f;
			MoveTo(point, 2f);
		}

		private void ApplySavedIdentity()
		{
			if (Zdo != null && !string.IsNullOrEmpty(VillagerId))
			{
				if (Object.op_Implicit((Object)(object)character))
				{
					character.m_name = DisplayName;
					character.SetTamed(true);
				}
				ApplyAppearance(Zdo.GetInt("hf_appearance", 0));
			}
		}

		private float BaseMaximumHealth()
		{
			float num = ((Zdo == null) ? 100f : Zdo.GetFloat("hf_base_health", 100f));
			return num * TraitCatalog.Product(Traits(), (TraitDefinition t) => t.Health);
		}

		private void UpdateFoodEffect()
		{
			if (Object.op_Implicit((Object)(object)character) && Zdo.GetLong("hf_food_until", 0L) > 0 && DateTime.UtcNow.Ticks >= Zdo.GetLong("hf_food_until", 0L))
			{
				float healthPercentage = character.GetHealthPercentage();
				character.SetMaxHealth(BaseMaximumHealth());
				character.SetHealth(Mathf.Max(1f, character.GetMaxHealth() * healthPercentage));
				Zdo.Set("hf_food_health", 0f);
				Zdo.Set("hf_food_stamina", 0f);
				Zdo.Set("hf_food_until", 0L);
			}
		}

		private void ApplyAppearance(int seed)
		{
			if (Object.op_Implicit((Object)(object)humanoid))
			{
				string[] array = new string[8] { "Hair1", "Hair2", "Hair3", "Hair4", "Hair5", "Hair6", "Hair7", "Hair8" };
				string[] array2 = new string[6] { "", "Beard1", "Beard2", "Beard3", "Beard4", "Beard5" };
				Random random = new Random(seed);
				humanoid.SetHair(array[random.Next(array.Length)]);
				humanoid.SetBeard(array2[random.Next(array2.Length)]);
			}
		}

		private void Cleanup()
		{
			if (!cleaned)
			{
				cleaned = true;
				VillageWorkstation villageWorkstation = AssignedStation();
				if (Object.op_Implicit((Object)(object)villageWorkstation) && villageWorkstation.AssignedVillagerId == VillagerId)
				{
					villageWorkstation.ClearAssignment();
				}
			}
		}

		private void MarkDead()
		{
			StoneTablet stoneTablet = VillageRegistry.ByVillage(VillageId);
			if (Object.op_Implicit((Object)(object)stoneTablet))
			{
				stoneTablet.NotifyResidentDied(VillagerId);
			}
		}

		private static string EncodeSkills(Dictionary<VillagerSkill, float> values)
		{
			return string.Join(",", (from x in values
				orderby x.Key
				select (int)x.Key + ":" + x.Value.ToString("0.###", CultureInfo.InvariantCulture)).ToArray());
		}

		private static Dictionary<VillagerSkill, float> DecodeSkills(string encoded)
		{
			Dictionary<VillagerSkill, float> dictionary = new Dictionary<VillagerSkill, float>();
			string[] array = (encoded ?? string.Empty).Split(',');
			foreach (string text in array)
			{
				string[] array2 = text.Split(':');
				if (array2.Length == 2 && int.TryParse(array2[0], out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && Enum.IsDefined(typeof(VillagerSkill), result))
				{
					dictionary[(VillagerSkill)result] = result2;
				}
			}
			return dictionary;
		}
	}
	internal static class VillageRegistry
	{
		private static readonly HashSet<StoneTablet> Tablets = new HashSet<StoneTablet>();

		private static readonly HashSet<VillagerAgent> Villagers = new HashSet<VillagerAgent>();

		private static readonly HashSet<VillageWorkstation> Stations = new HashSet<VillageWorkstation>();

		internal static IEnumerable<StoneTablet> AllTablets => Tablets.Where((StoneTablet x) => Object.op_Implicit((Object)(object)x) && x.Zdo != null);

		internal static IEnumerable<VillagerAgent> AllVillagers => Villagers.Where((VillagerAgent x) => Object.op_Implicit((Object)(object)x) && x.Zdo != null);

		internal static IEnumerable<VillageWorkstation> AllStations => Stations.Where((VillageWorkstation x) => Object.op_Implicit((Object)(object)x) && !string.IsNullOrEmpty(x.VillageId));

		internal static void Add(StoneTablet value)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				Tablets.Add(value);
			}
		}

		internal static void Remove(StoneTablet value)
		{
			Tablets.Remove(value);
		}

		internal static void Add(VillagerAgent value)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				Villagers.Add(value);
			}
		}

		internal static void Remove(VillagerAgent value)
		{
			Villagers.Remove(value);
		}

		internal static void Add(VillageWorkstation value)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				Stations.Add(value);
			}
		}

		internal static void Remove(VillageWorkstation value)
		{
			Stations.Remove(value);
		}

		internal static StoneTablet ClosestTablet(Vector3 position, float maxDistance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return AllTablets.OrderBy((StoneTablet t) => Vector3.SqrMagnitude(((Component)t).transform.position - position)).FirstOrDefault((StoneTablet t) => Vector3.Distance(((Component)t).transform.position, position) <= maxDistance);
		}

		internal static StoneTablet ByVillage(string villageId)
		{
			return AllTablets.FirstOrDefault((StoneTablet t) => string.Equals(t.VillageId, villageId, StringComparison.Ordinal));
		}

		internal static IEnumerable<VillagerAgent> Residents(string villageId)
		{
			return AllVillagers.Where((VillagerAgent v) => string.Equals(v.VillageId, villageId, StringComparison.Ordinal));
		}

		internal static VillagerAgent Villager(string villagerId)
		{
			return AllVillagers.FirstOrDefault((VillagerAgent v) => string.Equals(v.VillagerId, villagerId, StringComparison.Ordinal));
		}

		internal static bool CanPlaceTablet(Vector3 position, StoneTablet except = null)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			float separation = HearthfolkPlugin.Settings.TabletSeparation.Value;
			return AllTablets.All((StoneTablet t) => (Object)(object)t == (Object)(object)except || Vector3.Distance(position, ((Component)t).transform.position) >= separation);
		}

		internal static void Clear()
		{
			Tablets.Clear();
			Villagers.Clear();
			Stations.Clear();
		}
	}
	internal interface IVillagerJob
	{
		JobType Type { get; }

		void Tick(VillageWorkstation station, VillagerAgent worker, float delta);

		void Cancel(VillageWorkstation station);
	}
	internal static class VillagerJobs
	{
		private static readonly Dictionary<JobType, IVillagerJob> Jobs = new Dictionary<JobType, IVillagerJob>
		{
			{
				JobType.Woodcutting,
				new WoodcuttingJob()
			},
			{
				JobType.TreePlanting,
				new TreePlantingJob()
			},
			{
				JobType.Fishing,
				new FishingJob()
			},
			{
				JobType.Guarding,
				new GuardJob()
			}
		};

		internal static void Tick(VillageWorkstation station, VillagerAgent worker, float delta)
		{
			if (Jobs.TryGetValue(station.Job, out var value))
			{
				value.Tick(station, worker, delta);
			}
		}
	}
	internal abstract class JobBase : IVillagerJob
	{
		protected readonly Dictionary<int, float> NextAction = new Dictionary<int, float>();

		public abstract JobType Type { get; }

		public abstract void Tick(VillageWorkstation station, VillagerAgent worker, float delta);

		public virtual void Cancel(VillageWorkstation station)
		{
			NextAction.Remove(((Object)station).GetInstanceID());
		}

		protected bool Ready(VillageWorkstation station, float interval)
		{
			int instanceID = ((Object)station).GetInstanceID();
			if (NextAction.TryGetValue(instanceID, out var value) && Time.time < value)
			{
				return false;
			}
			NextAction[instanceID] = Time.time + interval;
			return true;
		}

		protected ItemData FindItem(Container container, Func<ItemData, bool> predicate)
		{
			return Object.op_Implicit((Object)(object)container) ? container.GetInventory().GetAllItems().FirstOrDefault(predicate) : null;
		}

		protected bool Deposit(VillageWorkstation station, VillagerAgent worker)
		{
			Container val = station.Output();
			if (!Object.op_Implicit((Object)(object)val))
			{
				station.SetBlocker("Link an output container.");
				return false;
			}
			Inventory inventory = worker.Inventory;
			Inventory inventory2 = val.GetInventory();
			ItemData[] array = (from i in inventory.GetAllItems()
				where i != null && !i.m_equipped
				select i).ToArray();
			foreach (ItemData val2 in array)
			{
				if (!inventory2.CanAddItem(val2, val2.m_stack))
				{
					station.SetBlocker("Output container is full.");
					return false;
				}
				inventory2.MoveItemToThis(inventory, val2);
			}
			station.SetBlocker(string.Empty);
			return true;
		}

		protected ItemData EnsureTool(VillageWorkstation station, VillagerAgent worker, string token)
		{
			ItemData val = ((IEnumerable<ItemData>)worker.Inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => PrefabName(i).IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0 && i.m_durability > 0f));
			if (val != null)
			{
				worker.Humanoid.EquipItem(val, false);
				return val;
			}
			Container val2 = station.Input();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				station.SetBlocker("Link an input container.");
				return null;
			}
			ItemData val3 = FindItem(val2, (ItemData i) => PrefabName(i).IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0 && i.m_durability > 0f);
			if (val3 == null)
			{
				station.SetBlocker("Missing usable " + token.ToLowerInvariant() + ".");
				return null;
			}
			worker.Inventory.MoveItemToThis(val2.GetInventory(), val3);
			worker.Humanoid.EquipItem(val3, false);
			station.SetBlocker(string.Empty);
			return val3;
		}

		protected static string PrefabName(ItemData item)
		{
			return (item != null && Object.op_Implicit((Object)(object)item.m_dropPrefab)) ? ((Object)item.m_dropPrefab).name.Replace("(Clone)", string.Empty) : string.Empty;
		}
	}
	internal sealed class WoodcuttingJob : JobBase
	{
		private readonly Dictionary<int, TreeBase> targets = new Dictionary<int, TreeBase>();

		public override JobType Type => JobType.Woodcutting;

		public override void Tick(VillageWorkstation station, VillagerAgent worker, float delta)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			if (!DepositLooseWood(station, worker))
			{
				return;
			}
			ItemData val = EnsureTool(station, worker, "Axe");
			if (val == null)
			{
				return;
			}
			int instanceID = ((Object)station).GetInstanceID();
			targets.TryGetValue(instanceID, out var value);
			if (!Object.op_Implicit((Object)(object)value) || Vector3.Distance(((Component)value).transform.position, ((Component)station).transform.position) > station.Radius)
			{
				StoneTablet tablet = VillageRegistry.ByVillage(station.VillageId);
				value = (from t in Object.FindObjectsByType<TreeBase>((FindObjectsSortMode)0)
					where Object.op_Implicit((Object)(object)t) && Vector3.Distance(((Component)t).transform.position, ((Component)station).transform.position) <= station.Radius && VillagePermissions.WorldTargetAllowed(tablet, ((Component)t).transform.position)
					orderby Vector3.Distance(((Component)worker).transform.position, ((Component)t).transform.position)
					select t).FirstOrDefault();
				targets[instanceID] = value;
			}
			if (!Object.op_Implicit((Object)(object)value))
			{
				station.SetBlocker("No eligible tree in range.");
			}
			else if (worker.MoveTo(((Component)value).transform.position, 2.5f) && Ready(station, Mathf.Max(0.7f, 2.2f - worker.Skill(VillagerSkill.Woodcutting) * 0.012f)))
			{
				worker.Face(((Component)value).transform.position);
				((Character)worker.Humanoid).StartAttack((Character)null, false);
				HitData val2 = new HitData();
				val2.m_damage.m_chop = 10f + worker.Skill(VillagerSkill.Woodcutting) * 0.25f;
				val2.m_point = ((Component)value).transform.position;
				Vector3 val3 = ((Component)value).transform.position - ((Component)worker).transform.position;
				val2.m_dir = ((Vector3)(ref val3)).normalized;
				val2.SetAttacker(worker.Character);
				value.Damage(val2);
				val.m_durability = Mathf.Max(0f, val.m_durability - 1f);
				worker.Award(VillagerSkill.Woodcutting, 0.15f);
				station.SetBlocker(string.Empty);
				if (!Object.op_Implicit((Object)(object)value))
				{
					targets.Remove(instanceID);
				}
			}
		}

		private bool DepositLooseWood(VillageWorkstation station, VillagerAgent worker)
		{
			ItemDrop[] array = (from d in Object.FindObjectsByType<ItemDrop>((FindObjectsSortMode)0)
				where Object.op_Implicit((Object)(object)d) && Vector3.Distance(((Component)d).transform.position, ((Component)worker).transform.position) < 6f && IsWood(((Object)d).name)
				select d).Take(8).ToArray();
			foreach (ItemDrop val in array)
			{
				worker.Humanoid.Pickup(((Component)val).gameObject, false, false);
			}
			if (worker.Inventory.GetAllItems().Any((ItemData i) => IsWood(JobBase.PrefabName(i))))
			{
				return Deposit(station, worker);
			}
			return true;
		}

		private static bool IsWood(string name)
		{
			return name.IndexOf("Wood", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("RoundLog", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
	internal sealed class TreePlantingJob : JobBase
	{
		private static readonly Dictionary<string, string> Seeds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "BeechSeeds", "Beech_Sapling" },
			{ "FirCone", "FirTree_Sapling" },
			{ "PineCone", "PineTree_Sapling" }
		};

		public override JobType Type => JobType.TreePlanting;

		public override void Tick(VillageWorkstation station, VillagerAgent worker, float delta)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			Container val = station.Input();
			if (!Object.op_Implicit((Object)(object)val))
			{
				station.SetBlocker("Link an input container.");
				return;
			}
			ItemData val2 = ((IEnumerable<ItemData>)val.GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => Seeds.ContainsKey(JobBase.PrefabName(i))));
			if (val2 == null)
			{
				station.SetBlocker("Missing supported tree seeds.");
			}
			else
			{
				if (!Ready(station, Mathf.Max(5f, 15f - worker.Skill(VillagerSkill.Forestry) * 0.08f)))
				{
					return;
				}
				Random random = new Random((((Object)station).GetInstanceID() * 397) ^ (int)(Time.time * 10f));
				Vector2 val3 = Random.insideUnitCircle * station.Radius;
				Vector3 val4 = ((Component)station).transform.position + new Vector3(val3.x, 10f, val3.y);
				float y = default(float);
				if (!ZoneSystem.instance.FindFloor(val4, ref y))
				{
					station.SetBlocker("No valid ground in planting area.");
					return;
				}
				val4.y = y;
				if (Physics.CheckSphere(val4, 2.5f))
				{
					station.SetBlocker("Planting area needs more space.");
				}
				else if (worker.MoveTo(val4, 2f))
				{
					string text = Seeds[JobBase.PrefabName(val2)];
					GameObject prefab = ZNetScene.instance.GetPrefab(text);
					if (!Object.op_Implicit((Object)(object)prefab))
					{
						station.SetBlocker("Plant prefab " + text + " is unavailable.");
					}
					else if (val.GetInventory().RemoveOneItem(val2))
					{
						Object.Instantiate<GameObject>(prefab, val4, Quaternion.Euler(0f, (float)random.Next(360), 0f));
						worker.Award(VillagerSkill.Forestry, 0.2f);
						station.SetBlocker(string.Empty);
					}
				}
			}
		}
	}
	internal sealed class FishingJob : JobBase
	{
		private static readonly string[] Fish = new string[4] { "Fish1", "Fish2", "Fish3", "Fish4" };

		public override JobType Type => JobType.Fishing;

		public override void Tick(VillageWorkstation station, VillagerAgent worker, float delta)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = EnsureTool(station, worker, "FishingRod");
			if (val == null)
			{
				return;
			}
			Container val2 = station.Input();
			ItemData val3 = FindItem(val2, (ItemData i) => JobBase.PrefabName(i).IndexOf("FishingBait", StringComparison.OrdinalIgnoreCase) >= 0);
			if (val3 == null)
			{
				station.SetBlocker("Missing fishing bait.");
				return;
			}
			Vector3 position = ((Component)station).transform.position;
			position.y = ZoneSystem.instance.m_waterLevel;
			if (Mathf.Abs(((Component)station).transform.position.y - position.y) > 4f)
			{
				station.SetBlocker("Station must be placed near water.");
			}
			else if (worker.MoveTo(((Component)station).transform.position, 2f) && Ready(station, Mathf.Max(12f, 35f - worker.Skill(VillagerSkill.Fishing) * 0.18f)) && val2.GetInventory().RemoveOneItem(val3))
			{
				string text = Fish[Mathf.Abs(((Object)station).GetInstanceID() + (int)Time.time) % Fish.Length];
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
				if (Object.op_Implicit((Object)(object)itemPrefab) && worker.Inventory.CanAddItem(itemPrefab, 1))
				{
					worker.Inventory.AddItem(itemPrefab, 1);
					val.m_durability = Mathf.Max(0f, val.m_durability - 1f);
					worker.Award(VillagerSkill.Fishing, 0.25f);
					Deposit(station, worker);
				}
				else
				{
					station.SetBlocker("Catch could not be stored.");
				}
			}
		}
	}
	internal sealed class GuardJob : JobBase
	{
		public override JobType Type => JobType.Guarding;

		public override void Tick(VillageWorkstation station, VillagerAgent worker, float delta)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = EnsureWeapon(station, worker);
			if (val != null)
			{
				Character val2 = (from c in Character.GetAllCharacters()
					where Object.op_Implicit((Object)(object)c) && (Object)(object)c != (Object)(object)worker.Character && !c.IsDead() && Vector3.Distance(((Component)c).transform.position, ((Component)station).transform.position) <= station.Radius && BaseAI.IsEnemy(worker.Character, c)
					orderby Vector3.Distance(((Component)worker).transform.position, ((Component)c).transform.position)
					select c).FirstOrDefault();
				if (!Object.op_Implicit((Object)(object)val2))
				{
					worker.MoveTo(((Component)station).transform.position, 5f);
					station.SetBlocker(string.Empty);
				}
				else if (worker.MoveTo(((Component)val2).transform.position, 2.5f) && Ready(station, Mathf.Max(0.7f, 2f - worker.Skill(VillagerSkill.Combat) * 0.01f)))
				{
					worker.Face(((Component)val2).transform.position);
					((Character)worker.Humanoid).StartAttack(val2, false);
					worker.Award(VillagerSkill.Combat, 0.1f);
				}
			}
		}

		private ItemData EnsureWeapon(VillageWorkstation station, VillagerAgent worker)
		{
			ItemData val = ((IEnumerable<ItemData>)worker.Inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.IsWeapon() && i.m_durability > 0f));
			if (val != null)
			{
				worker.Humanoid.EquipItem(val, false);
				return val;
			}
			Container val2 = station.Input();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				station.SetBlocker("Link an input container.");
				return null;
			}
			val = ((IEnumerable<ItemData>)val2.GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.IsWeapon() && i.m_durability > 0f));
			if (val == null)
			{
				station.SetBlocker("Missing usable weapon.");
				return null;
			}
			worker.Inventory.MoveItemToThis(val2.GetInventory(), val);
			worker.Humanoid.EquipItem(val, false);
			station.SetBlocker(string.Empty);
			return val;
		}
	}
	internal static class VillageWindow
	{
		private static StoneTablet tablet;

		private static VillageWorkstation station;

		private static Rect window = new Rect(80f, 80f, 720f, 560f);

		private static Vector2 scroll;

		internal static void Open(StoneTablet value)
		{
			tablet = value;
			station = null;
		}

		internal static void Open(VillageWorkstation value)
		{
			station = value;
			tablet = VillageRegistry.ByVillage(value.VillageId);
		}

		internal static void Close()
		{
			tablet = null;
			station = null;
		}

		internal static void Draw()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)tablet) || Object.op_Implicit((Object)(object)station))
			{
				if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object.op_Implicit((Object)(object)tablet) && Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)tablet).transform.position) > 12f && (!Object.op_Implicit((Object)(object)station) || Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)station).transform.position) > 12f)))
				{
					Close();
					return;
				}
				GUI.skin.window.fontSize = 16;
				window = GUILayout.Window(18502, window, new WindowFunction(DrawWindow), Object.op_Implicit((Object)(object)station) ? station.GetHoverName() : "Hearthfolk Village", Array.Empty<GUILayoutOption>());
			}
		}

		private static void DrawWindow(int id)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Object.op_Implicit((Object)(object)tablet) ? ("Village " + Short(tablet.VillageId)) : "Unbound station", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			scroll = GUILayout.BeginScrollView(scroll, Array.Empty<GUILayoutOption>());
			if (Object.op_Implicit((Object)(object)station))
			{
				DrawStation();
			}
			else
			{
				DrawTablet();
			}
			GUILayout.EndScrollView();
			GUI.DragWindow(new Rect(0f, 0f, 650f, 30f));
		}

		private static void DrawTablet()
		{
			if (!Object.op_Implicit((Object)(object)tablet))
			{
				GUILayout.Label("Tablet is unavailable.", Array.Empty<GUILayoutOption>());
				return;
			}
			bool flag = VillagePermissions.CanManage(tablet, Player.m_localPlayer);
			GUILayout.Label("Candidates", Header(), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawCandidate(0, flag);
			DrawCandidate(1, flag);
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Recruitment cost: " + tablet.CurrentCost + " " + tablet.CostResource, Array.Empty<GUILayoutOption>());
			GUI.enabled = flag;
			if (GUILayout.Button("Reroll both (" + tablet.CurrentRerollCost + ")", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }))
			{
				tablet.RequestReroll();
			}
			GUI.enabled = true;
			GUILayout.EndHorizontal();
			GUILayout.Space(12f);
			GUILayout.Label("Residents", Header(), Array.Empty<GUILayoutOption>());
			VillagerAgent[] array = VillageRegistry.Residents(tablet.VillageId).ToArray();
			if (array.Length == 0)
			{
				GUILayout.Label("No villagers recruited yet.", Array.Empty<GUILayoutOption>());
			}
			VillagerAgent[] array2 = array;
			foreach (VillagerAgent villagerAgent in array2)
			{
				GUILayout.BeginVertical(GUI.skin.box, Array.Empty<GUILayoutOption>());
				GUILayout.Label(villagerAgent.DisplayName + " — " + villagerAgent.State.ToString() + (villagerAgent.IsDead ? " (dead)" : string.Empty), Array.Empty<GUILayoutOption>());
				GUILayout.Label("Health " + (Object.op_Implicit((Object)(object)villagerAgent.Character) ? (villagerAgent.Character.GetHealth().ToString("0") + "/" + villagerAgent.Character.GetMaxHealth().ToString("0")) : "—") + "   Hunger " + villagerAgent.Hunger.ToString("0") + "%   Fatigue " + villagerAgent.Fatigue.ToString("0") + "%", Array.Empty<GUILayoutOption>());
				GUILayout.Label("Traits: " + string.Join(", ", (from x in villagerAgent.Traits()
					select Split(x.ToString())).ToArray()), Array.Empty<GUILayoutOption>());
				VillageWorkstation villageWorkstation = villagerAgent.AssignedStation();
				GUILayout.Label("Assignment: " + (Object.op_Implicit((Object)(object)villageWorkstation) ? villageWorkstation.GetHoverName() : "Unassigned"), Array.Empty<GUILayoutOption>());
				GUILayout.EndVertical();
			}
		}

		private static void DrawCandidate(int index, bool manage)
		{
			CandidateRecord candidateRecord = tablet.Candidate(index);
			GUILayout.BeginVertical(GUI.skin.box, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(330f),
				GUILayout.MinHeight(170f)
			});
			if (candidateRecord == null)
			{
				GUILayout.Label("Candidate data is synchronizing…", Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				Texture2D val = PortraitCache.Get(candidateRecord);
				if (Object.op_Implicit((Object)(object)val))
				{
					GUILayout.Label((Texture)(object)val, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(96f),
						GUILayout.Height(96f)
					});
				}
				GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
				GUILayout.Label(candidateRecord.Name, Header(), Array.Empty<GUILayoutOption>());
				GUILayout.Label("Traits: " + string.Join(", ", candidateRecord.Traits.Select((TraitId t) => Split(t.ToString())).ToArray()), Array.Empty<GUILayoutOption>());
				GUILayout.Label("Health " + candidateRecord.BaseHealth.ToString("0") + "  Stamina " + candidateRecord.BaseStamina.ToString("0"), Array.Empty<GUILayoutOption>());
				GUILayout.EndVertical();
				GUILayout.EndHorizontal();
				VillagerSkill key = candidateRecord.Skills.OrderByDescending((KeyValuePair<VillagerSkill, float> s) => s.Value).First().Key;
				GUILayout.Label("Best skill: " + Split(key.ToString()) + " " + candidateRecord.Skills[key].ToString("0"), Array.Empty<GUILayoutOption>());
				GUI.enabled = manage;
				if (GUILayout.Button("Recruit for " + tablet.CurrentCost + " " + tablet.CostResource, Array.Empty<GUILayoutOption>()))
				{
					tablet.RequestRecruit(index);
				}
				GUI.enabled = true;
			}
			GUILayout.EndVertical();
		}

		private static void DrawStation()
		{
			if (!Object.op_Implicit((Object)(object)station) || !Object.op_Implicit((Object)(object)tablet))
			{
				GUILayout.Label("This station is not bound to a loaded village.", Array.Empty<GUILayoutOption>());
				return;
			}
			bool enabled = VillagePermissions.CanManage(tablet, Player.m_localPlayer);
			GUI.enabled = enabled;
			GUILayout.Label("Job: " + Split(station.Job.ToString()), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Radius: " + station.Radius.ToString("0") + " m", Array.Empty<GUILayoutOption>());
			if (!string.IsNullOrEmpty(station.Blocker))
			{
				GUILayout.Label("Blocked: " + station.Blocker, Array.Empty<GUILayoutOption>());
			}
			if (GUILayout.Button(station.WorkEnabled ? "Disable station" : "Enable station", Array.Empty<GUILayoutOption>()))
			{
				station.RequestToggle();
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Link nearest input chest", Array.Empty<GUILayoutOption>()))
			{
				station.RequestLink(input: true);
			}
			if (GUILayout.Button("Link nearest output chest", Array.Empty<GUILayoutOption>()))
			{
				station.RequestLink(input: false);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			GUILayout.Label("Assign worker", Header(), Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Clear assignment", Array.Empty<GUILayoutOption>()))
			{
				station.RequestClear();
			}
			foreach (VillagerAgent item in from v in VillageRegistry.Residents(tablet.VillageId)
				where !v.IsDead
				select v)
			{
				if (GUILayout.Button(item.DisplayName + ((station.AssignedVillagerId == item.VillagerId) ? "  ✓" : string.Empty), Array.Empty<GUILayoutOption>()))
				{
					station.RequestAssign(item);
				}
			}
			GUI.enabled = true;
		}

		private static GUIStyle Header()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.label);
			val.fontSize = 18;
			val.fontStyle = (FontStyle)1;
			return val;
		}

		private static string Short(string id)
		{
			return string.IsNullOrEmpty(id) ? "uninitialized" : id.Substring(0, Math.Min(8, id.Length));
		}

		private static string Split(string value)
		{
			return Regex.Replace(value ?? string.Empty, "([a-z])([A-Z])", "$1 $2");
		}
	}
	internal static class PortraitCache
	{
		private static readonly Dictionary<int, Texture2D> Cache = new Dictionary<int, Texture2D>();

		internal static Texture2D Get(CandidateRecord candidate)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(candidate.AppearanceSeed, out var value) && Object.op_Implicit((Object)(object)value))
			{
				return value;
			}
			value = new Texture2D(96, 96, (TextureFormat)4, false);
			Random random = new Random(candidate.AppearanceSeed);
			Color val = Color.Lerp(new Color(0.42f, 0.24f, 0.14f), new Color(0.96f, 0.73f, 0.52f), (float)random.NextDouble());
			Color val2 = Color.Lerp(new Color(0.08f, 0.04f, 0.02f), new Color(0.72f, 0.42f, 0.12f), (float)random.NextDouble());
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.13f, 0.18f, 0.18f, 1f);
			for (int i = 0; i < 96; i++)
			{
				for (int j = 0; j < 96; j++)
				{
					float num = (float)(j - 48) / 32f;
					float num2 = (float)(i - 48) / 39f;
					bool flag = num * num + num2 * num2 <= 1f;
					bool flag2 = flag && i > 64 + (int)(Math.Abs(num) * 8f);
					value.SetPixel(j, i, flag2 ? val2 : (flag ? val : val3));
				}
			}
			value.Apply();
			Cache[candidate.AppearanceSeed] = value;
			return value;
		}

		internal static void Clear()
		{
			foreach (Texture2D value in Cache.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					Object.Destroy((Object)(object)value);
				}
			}
			Cache.Clear();
		}
	}
	internal static class DebugOverlay
	{
		internal static void Draw()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (HearthfolkPlugin.Settings == null || !HearthfolkPlugin.Settings.DebugOverlay.Value)
			{
				return;
			}
			GUILayout.BeginArea(new Rect(10f, 10f, 380f, 240f), GUI.skin.box);
			GUILayout.Label("Hearthfolk diagnostics", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Villages: " + VillageRegistry.AllTablets.Count() + "  Villagers: " + VillageRegistry.AllVillagers.Count() + "  Stations: " + VillageRegistry.AllStations.Count(), Array.Empty<GUILayoutOption>());
			foreach (VillagerAgent item in VillageRegistry.AllVillagers.Take(8))
			{
				GUILayout.Label(item.DisplayName + ": " + item.State.ToString() + " H" + item.Hunger.ToString("0") + " F" + item.Fatigue.ToString("0"), Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndArea();
		}
	}
	public class VillageWorkstation : MonoBehaviour, Interactable, Hoverable
	{
		public JobType ConfiguredJob;

		protected ZNetView View;

		protected ZDO Zdo => (Object.op_Implicit((Object)(object)View) && View.IsValid()) ? View.GetZDO() : null;

		public string VillageId => (Zdo == null) ? string.Empty : Zdo.GetString("hf_village", string.Empty);

		public string AssignedVillagerId => (Zdo == null) ? string.Empty : Zdo.GetString("hf_assigned", string.Empty);

		public JobType Job => (Zdo == null) ? ConfiguredJob : ((JobType)Zdo.GetInt("hf_work_type", (int)ConfiguredJob));

		public float Radius => (Zdo == null) ? HearthfolkPlugin.Settings.JobRadius.Value : Zdo.GetFloat("hf_radius", HearthfolkPlugin.Settings.JobRadius.Value);

		public bool WorkEnabled => Zdo != null && Zdo.GetBool("hf_enabled", true);

		public string Blocker => (Zdo == null) ? string.Empty : Zdo.GetString("hf_blocker", string.Empty);

		protected virtual void Awake()
		{
			View = ((Component)this).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)View))
			{
				View.Register<int, string, long>("HF_Station", (Action<long, int, string, long>)RpcStation);
			}
			VillageRegistry.Add(this);
		}

		protected virtual void Start()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)View) || !View.IsValid() || !View.IsOwner())
			{
				return;
			}
			ZDO zdo = Zdo;
			SaveMigration.Workstation(zdo, ConfiguredJob);
			if (string.IsNullOrEmpty(zdo.GetString("hf_village", string.Empty)))
			{
				StoneTablet stoneTablet = VillageRegistry.ClosestTablet(((Component)this).transform.position, HearthfolkPlugin.Settings.VillageRadius.Value);
				if (Object.op_Implicit((Object)(object)stoneTablet))
				{
					zdo.Set("hf_village", stoneTablet.VillageId);
				}
				zdo.Set("hf_work_type", (int)ConfiguredJob);
				zdo.Set("hf_radius", HearthfolkPlugin.Settings.JobRadius.Value);
				zdo.Set("hf_enabled", true);
			}
		}

		protected virtual void OnDestroy()
		{
			VillageRegistry.Remove(this);
		}

		public string GetHoverName()
		{
			Piece component = ((Component)this).GetComponent<Piece>();
			return Object.op_Implicit((Object)(object)component) ? component.m_name.Replace("$hf_", "Hearthfolk ") : "Hearthfolk station";
		}

		public string GetHoverText()
		{
			return GetHoverName() + "\n[<color=yellow><b>$KEY_Use</b></color>] Configure" + (string.IsNullOrEmpty(Blocker) ? string.Empty : ("\n<color=orange>" + Blocker + "</color>"));
		}

		public float GetHoverOffset()
		{
			return 1.2f;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

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

		internal void Toggle()
		{
			if (Zdo != null)
			{
				View.ClaimOwnership();
				Zdo.Set("hf_enabled", !WorkEnabled);
			}
		}

		internal void RequestToggle()
		{
			Request(0, string.Empty);
		}

		internal void RequestClear()
		{
			Request(1, string.Empty);
		}

		internal void RequestLink(bool input)
		{
			Request(input ? 2 : 3, string.Empty);
		}

		internal void RequestAssign(VillagerAgent villager)
		{
			if (Object.op_Implicit((Object)(object)villager))
			{
				Request(4, villager.VillagerId);
			}
		}

		private void Request(int action, string argument)
		{
			if (Object.op_Implicit((Object)(object)View) && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				View.InvokeRPC("HF_Station", new object[3]
				{
					action,
					argument ?? string.Empty,
					Player.m_localPlayer.GetPlayerID()
				});
			}
		}

		private void RpcStation(long sender, int action, string argument, long claimedPlayerId)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!View.IsOwner())
			{
				return;
			}
			Player val = VillagePermissions.ResolvePlayer(sender, claimedPlayerId);
			StoneTablet stoneTablet = VillageRegistry.ByVillage(VillageId);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)stoneTablet) || Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) > 10f || !VillagePermissions.CanManage(stoneTablet, val))
			{
				return;
			}
			switch (action)
			{
			case 0:
				Toggle();
				break;
			case 1:
				ClearAssignment();
				break;
			case 2:
				LinkNearestContainer(input: true);
				break;
			case 3:
				LinkNearestContainer(input: false);
				break;
			case 4:
			{
				VillagerAgent villagerAgent = VillageRegistry.Villager(argument);
				if (Object.op_Implicit((Object)(object)villagerAgent))
				{
					Assign(villagerAgent);
				}
				break;
			}
			}
		}

		internal void Assign(VillagerAgent villager)
		{
			if (Zdo == null || !Object.op_Implicit((Object)(object)villager) || villager.VillageId != VillageId)
			{
				return;
			}
			View.ClaimOwnership();
			foreach (VillageWorkstation item in VillageRegistry.AllStations.Where((VillageWorkstation s) => (Object)(object)s != (Object)(object)this && s.AssignedVillagerId == villager.VillagerId))
			{
				item.ClearAssignment();
			}
			Zdo.Set("hf_assigned", villager.VillagerId);
			villager.Assign(this);
		}

		internal void ClearAssignment()
		{
			if (Zdo != null)
			{
				View.ClaimOwnership();
				VillagerAgent villagerAgent = VillageRegistry.Villager(AssignedVillagerId);
				Zdo.Set("hf_assigned", string.Empty);
				if (Object.op_Implicit((Object)(object)villagerAgent))
				{
					villagerAgent.Assign(null);
				}
			}
		}

		internal bool LinkNearestContainer(bool input)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			Container val = (from c in Object.FindObjectsByType<Container>((FindObjectsSortMode)0)
				where Object.op_Implicit((Object)(object)c) && Vector3.Distance(((Component)c).transform.position, ((Component)this).transform.position) <= 8f
				orderby Vector3.Distance(((Component)c).transform.position, ((Component)this).transform.position)
				select c).FirstOrDefault();
			if (!Object.op_Implicit((Object)(object)val))
			{
				SetBlocker("No container within 8 metres.");
				return false;
			}
			StoneTablet stoneTablet = VillageRegistry.ByVillage(VillageId);
			if (!Object.op_Implicit((Object)(object)stoneTablet) || Vector3.Distance(((Component)val).transform.position, ((Component)stoneTablet).transform.position) > HearthfolkPlugin.Settings.VillageRadius.Value)
			{
				SetBlocker("Container is outside the village.");
				return false;
			}
			ZNetView component = ((Component)val).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				SetBlocker("Container is not networked.");
				return false;
			}
			View.ClaimOwnership();
			Zdo.Set(input ? "hf_input" : "hf_output", component.GetZDO().m_uid);
			SetBlocker(string.Empty);
			return true;
		}

		internal Container Input()
		{
			return FindContainer("hf_input");
		}

		internal Container Output()
		{
			return FindContainer("hf_output");
		}

		private Container FindContainer(string key)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (Zdo == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			ZDOID zDOID = Zdo.GetZDOID(key);
			if (((ZDOID)(ref zDOID)).IsNone())
			{
				return null;
			}
			GameObject val = ZNetScene.instance.FindInstance(zDOID);
			return Object.op_Implicit((Object)(object)val) ? val.GetComponent<Container>() : null;
		}

		internal void SetBlocker(string value)
		{
			if (Zdo != null && View.IsOwner() && Blocker != value)
			{
				Zdo.Set("hf_blocker", value ?? string.Empty);
			}
		}

		internal void Tick(VillagerAgent agent, float delta)
		{
			VillagerJobs.Tick(this, agent, delta);
		}
	}
	public sealed class PantryMarker : MonoBehaviour
	{
		private void Start()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner())
			{
				StoneTablet stoneTablet = VillageRegistry.ClosestTablet(((Component)this).transform.position, HearthfolkPlugin.Settings.VillageRadius.Value);
				if (Object.op_Implicit((Object)(object)stoneTablet) && stoneTablet.Zdo != null)
				{
					stoneTablet.Zdo.Set("hf_pantry", component.GetZDO().m_uid);
				}
			}
		}
	}
}
namespace Hearthfolk.Core
{
	public enum TraitId
	{
		Lumberjack,
		GreenThumb,
		Angler,
		Vigilant,
		Strong,
		Tireless,
		Hardy,
		QuickLearner,
		HeartyAppetite,
		Sleepy,
		Clumsy,
		Frail
	}
	public enum VillagerSkill
	{
		Woodcutting,
		Forestry,
		Fishing,
		Combat,
		GeneralLabour
	}
	public enum VillagerState
	{
		Idle,
		Danger,
		SeekingFood,
		Eating,
		SeekingBed,
		Sleeping,
		Depositing,
		Working,
		Dead
	}
	public enum JobType
	{
		None,
		Woodcutting,
		TreePlanting,
		Fishing,
		Guarding
	}
	public sealed class TraitDefinition
	{
		public TraitId Id;

		public bool Positive;

		public float WorkSpeed = 1f;

		public float StaminaUse = 1f;

		public float HungerUse = 1f;

		public float FatigueUse = 1f;

		public float Health = 1f;

		public float YieldChance;

		public VillagerSkill? PreferredSkill;

		public TraitId[] Incompatible = new TraitId[0];
	}
	public static class TraitCatalog
	{
		private static readonly Dictionary<TraitId, TraitDefinition> All;

		public static IReadOnlyCollection<TraitDefinition> Values => All.Values;

		static TraitCatalog()
		{
			All = new Dictionary<TraitId, TraitDefinition>
			{
				{
					TraitId.Lumberjack,
					D(TraitId.Lumberjack, positive: true, VillagerSkill.Woodcutting, 1.2f, 0.95f, 1f, 1f, 1f, 0.08f)
				},
				{
					TraitId.GreenThumb,
					D(TraitId.GreenThumb, positive: true, VillagerSkill.Forestry, 1.15f, 1f, 1f, 1f, 1f, 0.1f)
				},
				{
					TraitId.Angler,
					D(TraitId.Angler, positive: true, VillagerSkill.Fishing, 1.18f, 0.95f, 1f, 1f, 1f, 0.1f)
				},
				{
					TraitId.Vigilant,
					D(TraitId.Vigilant, positive: true, VillagerSkill.Combat, 1.12f, 0.95f, 1f, 1f, 1f, 0f)
				},
				{
					TraitId.Strong,
					D(TraitId.Strong, positive: true, null, 1.1f, 0.85f, 1.05f, 1f, 1.1f, 0f)
				},
				{
					TraitId.Tireless,
					D(TraitId.Tireless, positive: true, null, 1.08f, 1f, 1f, 0.75f, 1f, 0f)
				},
				{
					TraitId.Hardy,
					D(TraitId.Hardy, positive: true, null, 1f, 1f, 1f, 1f, 1.25f, 0f)
				},
				{
					TraitId.QuickLearner,
					D(TraitId.QuickLearner, positive: true, VillagerSkill.GeneralLabour, 1.05f, 1f, 1f, 1f, 1f, 0f)
				},
				{
					TraitId.HeartyAppetite,
					D(TraitId.HeartyAppetite, positive: false, null, 1f, 1f, 1.35f, 1f, 1.05f, 0f)
				},
				{
					TraitId.Sleepy,
					D(TraitId.Sleepy, positive: false, null, 0.95f, 1f, 1f, 1.35f, 1f, 0f)
				},
				{
					TraitId.Clumsy,
					D(TraitId.Clumsy, positive: false, null, 0.85f, 1.15f, 1f, 1f, 1f, -0.05f)
				},
				{
					TraitId.Frail,
					D(TraitId.Frail, positive: false, null, 0.95f, 1f, 1f, 1f, 0.75f, 0f)
				}
			};
			All[TraitId.Lumberjack].Incompatible = new TraitId[1] { TraitId.Clumsy };
			All[TraitId.GreenThumb].Incompatible = new TraitId[1] { TraitId.Clumsy };
			All[TraitId.Angler].Incompatible = new TraitId[1] { TraitId.Clumsy };
			All[TraitId.Tireless].Incompatible = new TraitId[1] { TraitId.Sleepy };
			All[TraitId.Hardy].Incompatible = new TraitId[1] { TraitId.Frail };
		}

		private static TraitDefinition D(TraitId id, bool positive, VillagerSkill? skill, float work, float stamina, float hunger, float fatigue, float health, float yield)
		{
			return new TraitDefinition
			{
				Id = id,
				Positive = positive,
				PreferredSkill = skill,
				WorkSpeed = work,
				StaminaUse = stamina,
				HungerUse = hunger,
				FatigueUse = fatigue,
				Health = health,
				YieldChance = yield
			};
		}

		public static TraitDefinition Get(TraitId id)
		{
			return All[id];
		}

		public static bool Compatible(TraitId a, TraitId b)
		{
			return a != b && !Get(a).Incompatible.Contains(b) && !Get(b).Incompatible.Contains(a);
		}

		public static float Product(IEnumerable<TraitId> traits, Func<TraitDefinition, float> selector)
		{
			float num = 1f;
			foreach (TraitId trait in traits)
			{
				num *= selector(Get(trait));
			}
			return num;
		}
	}
	public sealed class CandidateRecord
	{
		public string Id = Guid.NewGuid().ToString("N");

		public string Name = "Villager";

		public int AppearanceSeed;

		public float BaseHealth = 100f;

		public float BaseStamina = 100f;

		public List<TraitId> Traits = new List<TraitId>();

		public Dictionary<VillagerSkill, float> Skills = new Dictionary<VillagerSkill, float>();

		public string Encode()
		{
			string text = string.Join(",", Traits.Select(delegate(TraitId t)
			{
				int num = (int)t;
				return num.ToString(CultureInfo.InvariantCulture);
			}).ToArray());
			string text2 = string.Join(",", (from k in Skills
				orderby k.Key
				select (int)k.Key + ":" + k.Value.ToString("0.###", CultureInfo.InvariantCulture)).ToArray());
			return string.Join("|", E(Id), E(Name), AppearanceSeed.ToString(CultureInfo.InvariantCulture), BaseHealth.ToString("0.###", CultureInfo.InvariantCulture), BaseStamina.ToString("0.###", CultureInfo.InvariantCulture), text, text2);
		}

		public static bool TryDecode(string value, out CandidateRecord result)
		{
			result = null;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string[] array = value.Split('|');
			if (array.Length != 7 || !int.TryParse(array[2], out var result2) || !float.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || !float.TryParse(array[4], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
			{
				return false;
			}
			CandidateRecord candidateRecord = new CandidateRecord
			{
				Id = U(array[0]),
				Name = U(array[1]),
				AppearanceSeed = result2,
				BaseHealth = result3,
				BaseStamina = result4
			};
			string[] array2 = array[5].Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			int result5;
			foreach (string s in array2)
			{
				if (int.TryParse(s, out result5) && Enum.IsDefined(typeof(TraitId), result5))
				{
					candidateRecord.Traits.Add((TraitId)result5);
				}
			}
			string[] array3 = array[6].Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array3)
			{
				string[] array4 = text.Split(':');
				if (array4.Length == 2 && int.TryParse(array4[0], out result5) && float.TryParse(array4[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6) && Enum.IsDefined(typeof(VillagerSkill), result5))
				{
					candidateRecord.Skills[(VillagerSkill)result5] = result6;
				}
			}
			if (candidateRecord.Tra