Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Hearthfolk v0.1.3
plugins/Hearthfolk/Hearthfolk.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
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