Decompiled source of Personnel v2.2.0

Personnel.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using DooDesch.AvatarKit;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.AvatarFramework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.UI;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Personnel;
using Personnel.Appearance;
using Personnel.Config;
using Personnel.Content;
using Personnel.Model;
using Personnel.Registration;
using Personnel.Spawn;
using Personnel.Util;
using S1API.Casino;
using S1API.Economy;
using S1API.Entities;
using S1API.Entities.Customer;
using S1API.Entities.Dealer;
using S1API.Entities.Relation;
using S1API.Entities.Schedule;
using S1API.Map;
using S1API.Rendering;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "Personnel", "2.2.0", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-Personnel")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("2.2.0.0")]
[assembly: AssemblyInformationalVersion("2.2.0+cf7fa11d9361c45be8f1d25e515b5cb36671b90b")]
[assembly: AssemblyProduct("Personnel")]
[assembly: AssemblyTitle("Personnel")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DooDesch
{
	internal static class ModVersion
	{
		internal const string Current = "2.2.0";
	}
}
namespace DooDesch.AvatarKit
{
	public static class AvatarDistortion
	{
		public static readonly string[] BoneKeys = new string[9] { "HeadBone", "HipBone", "LeftFootBone", "RightFootBone", "LeftShoulder", "RightShoulder", "MiddleSpine", "LowerSpine", "LowestSpine" };

		public const string FaceMeshKey = "FaceMesh";

		public const string BodyMeshKeyPrefix = "BodyMesh";

		private static Transform BoneByName(Avatar avatar, string key)
		{
			return (Transform)(key switch
			{
				"HeadBone" => avatar.HeadBone, 
				"HipBone" => avatar.HipBone, 
				"LeftFootBone" => avatar.LeftFootBone, 
				"RightFootBone" => avatar.RightFootBone, 
				"LeftShoulder" => avatar.LeftShoulder, 
				"RightShoulder" => avatar.RightShoulder, 
				"MiddleSpine" => avatar.MiddleSpine, 
				"LowerSpine" => avatar.LowerSpine, 
				"LowestSpine" => avatar.LowestSpine, 
				_ => null, 
			});
		}

		public static void Apply(Avatar avatar, IReadOnlyDictionary<string, (Vector3 scale, bool hide)> entries)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null)
			{
				return;
			}
			AvatarEffects component = ((Component)avatar).GetComponent<AvatarEffects>();
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = entries == null || entries.Count == 0;
			}
			string[] boneKeys = BoneKeys;
			foreach (string key in boneKeys)
			{
				Transform val = BoneByName(avatar, key);
				if (!((Object)(object)val == (Object)null))
				{
					if (entries != null && entries.TryGetValue(key, out (Vector3, bool) value))
					{
						val.localScale = (value.Item2 ? Vector3.zero : value.Item1);
					}
					else
					{
						val.localScale = Vector3.one;
					}
				}
			}
			if ((Object)(object)avatar.FaceMesh != (Object)null)
			{
				(Vector3, bool) value2;
				bool flag = entries != null && entries.TryGetValue("FaceMesh", out value2) && value2.Item2;
				((Renderer)avatar.FaceMesh).enabled = !flag;
			}
			Il2CppReferenceArray<SkinnedMeshRenderer> bodyMeshes = avatar.BodyMeshes;
			if (bodyMeshes == null)
			{
				return;
			}
			for (int j = 0; j < ((Il2CppArrayBase<SkinnedMeshRenderer>)(object)bodyMeshes).Length; j++)
			{
				if (!((Object)(object)((Il2CppArrayBase<SkinnedMeshRenderer>)(object)bodyMeshes)[j] == (Object)null))
				{
					(Vector3, bool) value3;
					bool flag2 = entries != null && entries.TryGetValue("BodyMesh" + j, out value3) && value3.Item2;
					((Renderer)((Il2CppArrayBase<SkinnedMeshRenderer>)(object)bodyMeshes)[j]).enabled = !flag2;
				}
			}
		}
	}
	public static class AvatarLayerSlots
	{
		public const int BodySlots = 8;

		public const int VanillaClearedSlots = 6;

		public const int FaceSlots = 6;

		public const int FixedFaceEntries = 2;

		public const int FreeFaceEntries = 3;

		public const string EmptyFacePath = "Avatar/Layers/None";

		private const string MouthPrefix = "Avatar/Layers/Face/Face_";

		private const string FacialHairPrefix = "Avatar/Layers/Face/FacialHair";

		public static bool IsMouthLayer(string path)
		{
			return path?.StartsWith("Avatar/Layers/Face/Face_", StringComparison.OrdinalIgnoreCase) ?? false;
		}

		public static bool IsFacialHairLayer(string path)
		{
			return path?.StartsWith("Avatar/Layers/Face/FacialHair", StringComparison.OrdinalIgnoreCase) ?? false;
		}

		public static void OrderFaceLayers(List<LayerSetting> list)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			if (list == null)
			{
				return;
			}
			List<(string, Color)> list2 = new List<(string, Color)>();
			List<(string, Color)> list3 = new List<(string, Color)>();
			List<(string, Color)> list4 = new List<(string, Color)>();
			for (int i = 0; i < list.Count; i++)
			{
				LayerSetting val = list[i];
				string layerPath = val.layerPath;
				if (!string.IsNullOrEmpty(layerPath) && !(layerPath == "Avatar/Layers/None"))
				{
					if (list2.Count == 0 && IsMouthLayer(layerPath))
					{
						list2.Add((layerPath, val.layerTint));
					}
					else if (list3.Count == 0 && IsFacialHairLayer(layerPath))
					{
						list3.Add((layerPath, val.layerTint));
					}
					else
					{
						list4.Add((layerPath, val.layerTint));
					}
				}
			}
			list.Clear();
			list.Add((list2.Count > 0) ? Setting(list2[0].Item1, list2[0].Item2) : Setting("Avatar/Layers/None", Color.white));
			list.Add((list3.Count > 0) ? Setting(list3[0].Item1, list3[0].Item2) : Setting("Avatar/Layers/None", Color.white));
			for (int j = 0; j < list4.Count; j++)
			{
				list.Add(Setting(list4[j].Item1, list4[j].Item2));
			}
		}

		public static List<LayerSetting> TrimFaceToBudget(List<LayerSetting> list, Func<string, int> rank)
		{
			List<LayerSetting> list2 = new List<LayerSetting>();
			if (list == null || rank == null || list.Count <= 5)
			{
				return list2;
			}
			for (int num = CountRenderable(list, 2) - 3; num > 0; num--)
			{
				int num2 = -1;
				int num3 = int.MaxValue;
				for (int i = 2; i < list.Count; i++)
				{
					if (Renders(list[i].layerPath))
					{
						int num4 = rank(list[i].layerPath);
						if (num4 < num3)
						{
							num3 = num4;
							num2 = i;
						}
					}
				}
				if (num2 < 0)
				{
					break;
				}
				list2.Add(list[num2]);
				list.RemoveAt(num2);
			}
			return list2;
		}

		private static LayerSetting Setting(string path, Color tint)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			return new LayerSetting
			{
				layerPath = path,
				layerTint = tint
			};
		}

		public static void LoadAndClean(Avatar avatar, AvatarSettings settings)
		{
			if (!((Object)(object)avatar == (Object)null) && !((Object)(object)settings == (Object)null))
			{
				avatar.LoadAvatarSettings(settings);
				List<LayerSetting> bodyLayerSettings = settings.BodyLayerSettings;
				int used = ((bodyLayerSettings != null && bodyLayerSettings.Count > 6) ? CountRenderable(bodyLayerSettings) : 6);
				ClearStaleBodySlots(avatar, used);
			}
		}

		public static int CountRenderable(List<LayerSetting> list, int startIndex = 0)
		{
			if (list == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = Math.Max(0, startIndex); i < list.Count; i++)
			{
				if (Renders(list[i].layerPath))
				{
					num++;
				}
			}
			return num;
		}

		public static void ClearStaleBodySlots(Avatar avatar, int used)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null || used >= 8)
			{
				return;
			}
			if (used < 0)
			{
				used = 0;
			}
			try
			{
				Il2CppReferenceArray<SkinnedMeshRenderer> bodyMeshes = avatar.BodyMeshes;
				for (int i = used + 1; i <= 8; i++)
				{
					avatar.SetBodyLayer(i, string.Empty, Color.white);
					if (bodyMeshes == null)
					{
						continue;
					}
					for (int j = 0; j < ((Il2CppArrayBase<SkinnedMeshRenderer>)(object)bodyMeshes).Length; j++)
					{
						SkinnedMeshRenderer val = ((Il2CppArrayBase<SkinnedMeshRenderer>)(object)bodyMeshes)[j];
						if ((Object)(object)val != (Object)null && (Object)(object)((Renderer)val).material != (Object)null)
						{
							((Renderer)val).material.SetTexture("_Layer_" + i + "_Normal", (Texture)null);
						}
					}
				}
			}
			catch (Exception)
			{
			}
		}

		public static List<LayerSetting> TrimToBudget(List<LayerSetting> list, Func<string, int> rank, int max = 8)
		{
			List<LayerSetting> list2 = new List<LayerSetting>();
			if (list == null || rank == null || list.Count <= max)
			{
				return list2;
			}
			for (int num = CountRenderable(list) - max; num > 0; num--)
			{
				int num2 = -1;
				int num3 = int.MaxValue;
				for (int i = 0; i < list.Count; i++)
				{
					if (Renders(list[i].layerPath))
					{
						int num4 = rank(list[i].layerPath);
						if (num4 < num3)
						{
							num3 = num4;
							num2 = i;
						}
					}
				}
				if (num2 < 0)
				{
					break;
				}
				list2.Add(list[num2]);
				list.RemoveAt(num2);
			}
			return list2;
		}

		public static bool AddOnce(List<LayerSetting> list, string path, Color tint)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (list == null || string.IsNullOrEmpty(path))
			{
				return false;
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].layerPath == path)
				{
					return false;
				}
			}
			LayerSetting val = new LayerSetting();
			val.layerPath = path;
			val.layerTint = tint;
			list.Add(val);
			return true;
		}

		private static bool Renders(string layerPath)
		{
			if (!string.IsNullOrEmpty(layerPath))
			{
				return Resources.Load(layerPath) != (Object)null;
			}
			return false;
		}
	}
}
namespace Personnel
{
	public static class API
	{
		private static MethodInfo _randomImpostor;

		private static bool _randomImpostorProbed;

		public static IReadOnlyList<NpcDef> All => NpcRegistry.AllDefs;

		public static event Action OnReloaded
		{
			add
			{
				NpcRegistry.OnReloaded += value;
			}
			remove
			{
				NpcRegistry.OnReloaded -= value;
			}
		}

		public static bool TryGet(string id, out NpcDef def)
		{
			return NpcRegistry.TryGet(id, out def);
		}

		public static bool Register(NpcDef def)
		{
			return NpcRegistry.Add(def);
		}

		public static AvatarSettings BuildAvatarSettings(NpcDef def)
		{
			return AvatarSettingsFactory.BuildAvatarSettings(def);
		}

		public static bool ApplyAppearance(Avatar avatar, NpcDef def)
		{
			if ((Object)(object)avatar == (Object)null || def == null)
			{
				return false;
			}
			AvatarSettings val = BuildAvatarSettings(def);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			AvatarLayerSlots.LoadAndClean(avatar, val);
			return true;
		}

		public static void ApplyDistortion(Avatar avatar, NpcDef def)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null || def?.Appearance == null)
			{
				return;
			}
			Dictionary<string, (Vector3, bool)> dictionary = new Dictionary<string, (Vector3, bool)>();
			foreach (KeyValuePair<string, BoneDistortion> item in def.Appearance.Distortion)
			{
				if (item.Value != null)
				{
					dictionary[item.Key] = (item.Value.Scale, item.Value.Hide);
				}
			}
			AvatarDistortion.Apply(avatar, dictionary);
		}

		public static void Reload()
		{
			NpcRegistry.Reload();
		}

		public static void ConfigureFromDef(NPCPrefabBuilder builder, NpcDef def)
		{
			//IL_01a1: 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_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			if (builder == null || def == null)
			{
				return;
			}
			SplitName(def.DisplayName, out var first, out var last);
			builder.WithIdentity(def.SaveId ?? def.Id, first, last);
			builder.WithAppearanceDefaults((Action<AvatarDefaultsBuilder>)delegate(AvatarDefaultsBuilder ab)
			{
				AvatarSettingsFactory.ApplyToDefaults(ab, def);
				ApplyImpostorIfSupported(ab, def);
			});
			bool unlocked = def.Relationships?.Unlocked ?? def.Contact?.Visible ?? true;
			builder.WithRelationshipDefaults((Action<NPCRelationshipDataBuilder>)delegate(NPCRelationshipDataBuilder r)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				r.SetUnlocked(unlocked);
				NpcRelationships relationships = def.Relationships;
				if (relationships != null)
				{
					if (relationships.Delta.HasValue)
					{
						r.WithDelta(relationships.Delta.Value);
					}
					if (!string.IsNullOrWhiteSpace(relationships.UnlockType))
					{
						if (Parse.TryParseEnum<UnlockType>(relationships.UnlockType, out UnlockType result))
						{
							r.SetUnlockType(result);
						}
						else
						{
							Instance log = Core.Log;
							if (log != null)
							{
								log.Warning($"'{def.Id}': unknown relationships.unlockType '{relationships.UnlockType}' - ignored.");
							}
						}
					}
					if (relationships.Connections != null && relationships.Connections.Count > 0)
					{
						r.WithConnectionsById((IEnumerable<string>)relationships.Connections);
					}
				}
			});
			string text = ResolveRole(def);
			if (text == "dealer")
			{
				ApplyDealer(builder, def);
			}
			else if (text == "customer")
			{
				ApplyCustomer(builder, def);
			}
			ApplyInventory(builder, def);
			List<IScheduleActionSpec> list = ScheduleSpecFactory.Build(def);
			if (list.Count > 0)
			{
				builder.WithSchedule((IEnumerable<IScheduleActionSpec>)list);
			}
			NpcSpawn spawn = def.Spawn;
			if (spawn != null && spawn.Position.HasValue)
			{
				Quaternion val = (def.Spawn.RotationY.HasValue ? Quaternion.Euler(0f, def.Spawn.RotationY.Value, 0f) : Quaternion.identity);
				builder.WithSpawnPosition(def.Spawn.Position.Value, val);
			}
		}

		internal static string ResolveRole(NpcDef def)
		{
			if (def == null)
			{
				return null;
			}
			string a = def.Behavior?.Conversation;
			bool flag = string.Equals(a, "dealer", StringComparison.OrdinalIgnoreCase);
			bool flag2 = string.Equals(a, "customer", StringComparison.OrdinalIgnoreCase);
			if (def.Dealer != null)
			{
				if (def.Customer != null || flag2)
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning("'" + def.Id + "': has both dealer and customer data - dealer wins (an NPC can only be one).");
					}
				}
				return "dealer";
			}
			if (def.Customer != null)
			{
				if (flag)
				{
					Instance log2 = Core.Log;
					if (log2 != null)
					{
						log2.Warning("'" + def.Id + "': customer{} block contradicts behavior.conversation=\"dealer\" - customer wins.");
					}
				}
				return "customer";
			}
			if (flag)
			{
				return "dealer";
			}
			if (flag2)
			{
				return "customer";
			}
			return null;
		}

		private static void ApplyCustomer(NPCPrefabBuilder builder, NpcDef def)
		{
			builder.WithCustomerDefaults((Action<CustomerDataBuilder>)delegate(CustomerDataBuilder c)
			{
				NpcCustomer customer = def.Customer;
				if (customer != null)
				{
					if (customer.Spending != null)
					{
						c.WithSpending(customer.Spending.Min, customer.Spending.Max);
					}
					if (customer.OrdersPerWeek != null)
					{
						c.WithOrdersPerWeek((int)customer.OrdersPerWeek.Min, (int)customer.OrdersPerWeek.Max);
					}
					if (!string.IsNullOrWhiteSpace(customer.PreferredOrderDay))
					{
						c.WithPreferredOrderDay(customer.PreferredOrderDay);
					}
					if (customer.OrderTime.HasValue)
					{
						c.WithOrderTime(customer.OrderTime.Value);
					}
					if (!string.IsNullOrWhiteSpace(customer.Standards))
					{
						c.WithStandards(customer.Standards);
					}
					if (customer.AllowDirectApproach.HasValue)
					{
						c.AllowDirectApproach(customer.AllowDirectApproach.Value);
					}
					if (customer.GuaranteeFirstSample.HasValue)
					{
						c.GuaranteeFirstSample(customer.GuaranteeFirstSample.Value);
					}
					if (customer.MutualRelationRequirement != null)
					{
						c.WithMutualRelationRequirement(customer.MutualRelationRequirement.Min, customer.MutualRelationRequirement.Max);
					}
					if (customer.CallPoliceChance.HasValue)
					{
						c.WithCallPoliceChance(customer.CallPoliceChance.Value);
					}
					if (customer.DependenceBase.HasValue)
					{
						c.WithDependence(customer.DependenceBase.Value, customer.DependenceMultiplier ?? 1f);
					}
					if (customer.Affinities != null && customer.Affinities.Count > 0)
					{
						List<(string, float)> list = new List<(string, float)>();
						foreach (KeyValuePair<string, float> affinity in customer.Affinities)
						{
							list.Add((affinity.Key, affinity.Value));
						}
						c.WithAffinities((IEnumerable<ValueTuple<string, float>>)list);
					}
					if (customer.PreferredProperties != null && customer.PreferredProperties.Count > 0)
					{
						c.WithPreferredPropertiesById(customer.PreferredProperties.ToArray());
					}
				}
			});
		}

		private static void ApplyDealer(NPCPrefabBuilder builder, NpcDef def)
		{
			NpcDealer de = def.Dealer;
			if (de == null || (de.Type == null && !de.Cut.HasValue && !de.SigningFee.HasValue && de.Home == null && de.CompletedDealsVariable == null && !de.AllowInsufficientQuality.HasValue && !de.AllowExcessQuality.HasValue))
			{
				return;
			}
			builder.WithDealerDefaults((Action<DealerDataBuilder>)delegate(DealerDataBuilder d)
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				if (de != null)
				{
					if (!string.IsNullOrWhiteSpace(de.Type))
					{
						string text = de.Type;
						if (string.Equals(text, "player", StringComparison.OrdinalIgnoreCase))
						{
							text = "PlayerDealer";
						}
						else if (string.Equals(text, "cartel", StringComparison.OrdinalIgnoreCase))
						{
							text = "CartelDealer";
						}
						if (Parse.TryParseEnum<DealerType>(text, out DealerType result))
						{
							d.WithDealerType(result);
						}
						else
						{
							Instance log = Core.Log;
							if (log != null)
							{
								log.Warning($"'{def.Id}': unknown dealer.type '{de.Type}' - ignored.");
							}
						}
					}
					if (de.Cut.HasValue)
					{
						d.WithCut(de.Cut.Value);
					}
					if (de.SigningFee.HasValue)
					{
						d.WithSigningFee(de.SigningFee.Value);
					}
					if (!string.IsNullOrWhiteSpace(de.Home))
					{
						d.WithHomeName(de.Home);
					}
					if (!string.IsNullOrWhiteSpace(de.CompletedDealsVariable))
					{
						d.WithCompletedDealsVariable(de.CompletedDealsVariable);
					}
					if (de.AllowInsufficientQuality.HasValue)
					{
						d.AllowInsufficientQuality(de.AllowInsufficientQuality.Value);
					}
					if (de.AllowExcessQuality.HasValue)
					{
						d.AllowExcessQuality(de.AllowExcessQuality.Value);
					}
				}
			});
		}

		private static void ApplyImpostorIfSupported(AvatarDefaultsBuilder ab, NpcDef def)
		{
			try
			{
				if (!_randomImpostorProbed)
				{
					_randomImpostorProbed = true;
					_randomImpostor = typeof(AvatarDefaultsBuilder).GetMethod("WithRandomImpostor", new Type[2]
					{
						typeof(int),
						typeof(string[])
					});
				}
				_randomImpostor?.Invoke(ab, new object[2]
				{
					Parse.StableHash(def.Id),
					Array.Empty<string>()
				});
			}
			catch (Exception ex)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning($"'{def.Id}': setting an impostor failed ({ex.Message}) - distant billboard may be blank.");
				}
			}
		}

		private static void ApplyInventory(NPCPrefabBuilder builder, NpcDef def)
		{
			NpcInventory inv = def.Inventory;
			if (inv == null)
			{
				return;
			}
			builder.WithInventoryDefaults((Action<RandomInventoryItemsBuilder>)delegate(RandomInventoryItemsBuilder i)
			{
				if (inv.Cash != null)
				{
					i.WithRandomCash((int)inv.Cash.Min, (int)inv.Cash.Max);
				}
				if (inv.Items != null)
				{
					foreach (NpcInventoryItem item in inv.Items)
					{
						for (int j = 0; j < item.Quantity; j++)
						{
							i.WithStartupItem(item.Id);
						}
					}
				}
				if (inv.ClearEachNight.HasValue)
				{
					i.WithClearInventoryEachNight(inv.ClearEachNight.Value);
				}
			});
		}

		public static void AddMapMarker(GameObject npcRoot)
		{
			if ((Object)(object)npcRoot == (Object)null || !NetworkSingleton<NPCManager>.InstanceExists)
			{
				return;
			}
			NPCManager instance = NetworkSingleton<NPCManager>.Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.NPCPoIPrefab == (Object)null))
			{
				NPC component = npcRoot.GetComponent<NPC>();
				if (!((Object)(object)component == (Object)null))
				{
					NPCPoI obj = Object.Instantiate<NPCPoI>(instance.NPCPoIPrefab, npcRoot.transform);
					obj.SetNPC(component);
					((Behaviour)obj).enabled = true;
				}
			}
		}

		private static void SplitName(string display, out string first, out string last)
		{
			first = "NPC";
			last = string.Empty;
			if (!string.IsNullOrWhiteSpace(display))
			{
				display = display.Trim();
				int num = display.IndexOf(' ');
				if (num > 0)
				{
					first = display.Substring(0, num);
					last = display.Substring(num + 1).Trim();
				}
				else
				{
					first = display;
				}
			}
		}
	}
	public sealed class Core : MelonMod
	{
		public static Core Instance { get; private set; }

		public static Instance Log { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log = ((MelonBase)this).LoggerInstance;
			Preferences.Initialize();
			ExamplePack.ExtractIfEnabled();
			int packDefs = NpcRegistry.LoadPacks();
			if (Preferences.EnableAutoRegister)
			{
				DynamicNpcTypeFactory.EmitAutoRegisteredTypes(NpcRegistry.AllDefs);
			}
			try
			{
				((MelonBase)this).HarmonyInstance.PatchAll();
			}
			catch (Exception ex)
			{
				Log.Warning("Console commands unavailable: " + ex.Message);
			}
			LogRosterSummary(packDefs);
			Log.Msg("Drop packs in: " + PackLoader.PacksRoot);
			Log.Msg("Writing a schedule? Type 'personnel help' in the dev console to grab coordinates.");
		}

		private static void LogRosterSummary(int packDefs)
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			foreach (NpcDef allDef in NpcRegistry.AllDefs)
			{
				bool flag = allDef.Schedule != null && allDef.Schedule.Count > 0;
				if (allDef.Spawn?.Physical ?? flag)
				{
					num++;
				}
				else
				{
					num2++;
				}
				string text = API.ResolveRole(allDef);
				if (text == "customer")
				{
					num3++;
				}
				else if (text == "dealer")
				{
					num4++;
				}
				if (flag)
				{
					num5++;
				}
				NpcSpawn spawn = allDef.Spawn;
				if (spawn != null && spawn.Auto)
				{
					num6++;
				}
			}
			Log.Msg($"Personnel {((MelonBase)Instance).Info.Version} - {packDefs} NPC def(s) from packs ({NpcRegistry.AllDefs.Count} total): {num} physical / {num2} contact-only, {num3} customer(s), {num4} dealer(s), {num5} with schedules, {num6} auto-registered.");
		}
	}
	public abstract class PersonnelNpc : NPC
	{
		protected abstract string DefId { get; }

		public override bool IsDealer
		{
			get
			{
				try
				{
					NpcDef def;
					return API.TryGet(DefId, out def) && API.ResolveRole(def) == "dealer";
				}
				catch (Exception ex)
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning($"IsDealer lookup for '{((object)this).GetType().Name}' threw ({ex.Message}) - assuming civilian.");
					}
					return false;
				}
			}
		}

		public override bool IsPhysical
		{
			get
			{
				try
				{
					if (!API.TryGet(DefId, out var def) || def == null)
					{
						return false;
					}
					return def.Spawn?.Physical ?? (def.Schedule != null && def.Schedule.Count > 0);
				}
				catch
				{
					return false;
				}
			}
		}

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			if (API.TryGet(DefId, out var def) && def != null)
			{
				API.ConfigureFromDef(builder, def);
				return;
			}
			Instance log = Core.Log;
			if (log != null)
			{
				log.Warning("PersonnelNpc: no definition '" + DefId + "' found - is the pack installed?");
			}
		}

		protected override void OnCreated()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			((NPC)this).OnCreated();
			if (!API.TryGet(DefId, out var def) || def == null)
			{
				API.AddMapMarker(((NPC)this).gameObject);
				return;
			}
			Avatar componentInChildren = ((NPC)this).gameObject.GetComponentInChildren<Avatar>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				API.ApplyDistortion(componentInChildren, def);
			}
			if (!string.IsNullOrWhiteSpace(def.Spawn?.Region))
			{
				if (Parse.TryParseEnum<Region>(def.Spawn.Region, out Region result))
				{
					((NPC)this).Region = result;
				}
				else
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning($"'{def.Id}': unknown spawn.region '{def.Spawn.Region}' - ignored.");
					}
				}
			}
			if (def.Behavior != null)
			{
				try
				{
					((NPC)this).Aggressiveness = def.Behavior.Aggression;
					((NPC)this).MaxHealth = def.Behavior.MaxHealth;
					if (def.Behavior.Scale > 0f)
					{
						((NPC)this).Scale = def.Behavior.Scale;
					}
				}
				catch (Exception ex)
				{
					Instance log2 = Core.Log;
					if (log2 != null)
					{
						log2.Warning($"'{def.Id}': applying behavior stats failed ({ex.Message}).");
					}
				}
			}
			if (def.Schedule != null && def.Schedule.Count > 0)
			{
				try
				{
					((NPC)this).Schedule.Enable();
				}
				catch (Exception ex2)
				{
					Instance log3 = Core.Log;
					if (log3 != null)
					{
						log3.Warning($"'{def.Id}': Schedule.Enable failed ({ex2.Message}).");
					}
				}
			}
			NpcContact contact = def.Contact;
			if (contact == null || contact.MapMarker != false)
			{
				API.AddMapMarker(((NPC)this).gameObject);
			}
		}
	}
}
namespace Personnel.Util
{
	public static class ColorParse
	{
		public static bool TryParse(string s, out Color color)
		{
			//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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			color = Color.white;
			if (string.IsNullOrWhiteSpace(s))
			{
				return false;
			}
			s = s.Trim();
			if (s.StartsWith("#"))
			{
				s = s.Substring(1);
			}
			if (s.Length != 6 && s.Length != 8)
			{
				return false;
			}
			try
			{
				byte b = Convert.ToByte(s.Substring(0, 2), 16);
				byte b2 = Convert.ToByte(s.Substring(2, 2), 16);
				byte b3 = Convert.ToByte(s.Substring(4, 2), 16);
				byte b4 = ((s.Length == 8) ? Convert.ToByte(s.Substring(6, 2), 16) : byte.MaxValue);
				color = Color32.op_Implicit(new Color32(b, b2, b3, b4));
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static Color Parse(string s, Color fallback)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (!TryParse(s, out var color))
			{
				return fallback;
			}
			return color;
		}
	}
	public static class Ids
	{
		public static string Normalize(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			bool flag = false;
			string text = s.Trim().ToLowerInvariant();
			foreach (char c in text)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(c);
					flag = false;
				}
				else if (!flag)
				{
					stringBuilder.Append('_');
					flag = true;
				}
			}
			return stringBuilder.ToString().Trim('_');
		}

		public static string Make(string pack, string name)
		{
			string text = Normalize(pack);
			string text2 = Normalize(name);
			if (string.IsNullOrEmpty(text))
			{
				return text2;
			}
			if (string.IsNullOrEmpty(text2))
			{
				return text;
			}
			return text + "_" + text2;
		}
	}
	internal static class Parse
	{
		public static bool TryParseEnum<T>(string value, out T result) where T : struct, Enum
		{
			result = default(T);
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string text = Fold(value);
			string[] names = Enum.GetNames(typeof(T));
			foreach (string text2 in names)
			{
				if (Fold(text2) == text)
				{
					result = (T)Enum.Parse(typeof(T), text2);
					return true;
				}
			}
			return false;
		}

		private static string Fold(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			foreach (char c in s)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		public static bool TryParseTime(string value, out int hhmm)
		{
			hhmm = 0;
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			value = value.Trim();
			int num = value.IndexOf(':');
			int result;
			int result2;
			if (num > 0)
			{
				if (!int.TryParse(value.Substring(0, num), out result))
				{
					return false;
				}
				if (!int.TryParse(value.Substring(num + 1), out result2))
				{
					return false;
				}
			}
			else
			{
				if (!int.TryParse(value, out var result3) || result3 < 0)
				{
					return false;
				}
				result = result3 / 100;
				result2 = result3 % 100;
			}
			if (result < 0 || result > 23 || result2 < 0 || result2 > 59)
			{
				return false;
			}
			hhmm = result * 100 + result2;
			return true;
		}

		public static bool TryParseVec3(float[] xyz, out Vector3 v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			v = default(Vector3);
			if (xyz == null || xyz.Length != 3)
			{
				return false;
			}
			if (float.IsNaN(xyz[0]) || float.IsNaN(xyz[1]) || float.IsNaN(xyz[2]))
			{
				return false;
			}
			v = new Vector3(xyz[0], xyz[1], xyz[2]);
			return true;
		}

		public static int StableHash(string s)
		{
			uint num = 2166136261u;
			if (s != null)
			{
				foreach (char c in s)
				{
					num ^= c;
					num *= 16777619;
				}
			}
			return (int)num;
		}
	}
}
namespace Personnel.Tools
{
	internal static class PersonnelConsole
	{
		private const string Prefix = "personnel";

		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		private static int _lastFrame = -1;

		private static string _lastSig = "";

		private static string RoutePath => Path.Combine(MelonEnvironment.UserDataDirectory, "Personnel", "route.json");

		internal static bool TryHandle(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			return Dispatch(raw.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries));
		}

		internal static bool TryHandle(List<string> args)
		{
			if (args == null || args.Count == 0)
			{
				return false;
			}
			string[] array = new string[args.Count];
			for (int i = 0; i < args.Count; i++)
			{
				array[i] = args[i];
			}
			return Dispatch(array);
		}

		private static bool Dispatch(string[] p)
		{
			if (p.Length == 0 || !p[0].Equals("personnel", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text = string.Join(" ", p);
			int frameCount = Time.frameCount;
			if (frameCount == _lastFrame && text == _lastSig)
			{
				return true;
			}
			_lastFrame = frameCount;
			_lastSig = text;
			string text2 = ((p.Length > 1) ? p[1].ToLowerInvariant() : "help");
			try
			{
				switch (text2)
				{
				case "help":
					Help();
					break;
				case "pos":
					Pos(Arg(p, 2));
					break;
				case "spawn":
					SpawnBlock();
					break;
				case "route":
					Route(Arg(p, 2));
					break;
				case "npcs":
					Npcs(Arg(p, 2));
					break;
				default:
					Log("unknown command '" + text2 + "'. Try: personnel help");
					break;
				}
			}
			catch (Exception ex)
			{
				Log("command failed: " + ex.Message);
			}
			return true;
		}

		private static void Help()
		{
			Log("commands (results are copied to your clipboard and written to this log):");
			Log("  personnel pos [HH:MM]   position you are standing on - with a time it becomes a walkTo action");
			Log("  personnel spawn         the same spot as a spawn block (x/y/z/rotationY/region)");
			Log("  personnel route HH:MM   append a walkTo step to " + RoutePath);
			Log("  personnel route show    print the collected steps");
			Log("  personnel route clear   start a new route");
			Log("  personnel npcs [filter] list loaded Personnel NPCs, and where the physical ones are right now");
			Notify("Personnel", "Command list written to the MelonLoader log.");
		}

		private static void Pos(string timeArg)
		{
			//IL_0014: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetPlayer(out var pos, out var yaw))
			{
				string time;
				if (string.IsNullOrEmpty(timeArg))
				{
					string text = Vec(pos);
					Log("position " + text + "  (facing " + Num(yaw) + " degrees)");
					Log("  paste into any action that takes a position: \"position\": " + text);
					Copy(text);
					Notify("Position copied", Plain(pos));
				}
				else if (!TryNormalizeTime(timeArg, out time))
				{
					Log("'" + timeArg + "' is not a time. Use HH:MM, e.g. personnel pos 07:30");
				}
				else
				{
					string text2 = WalkTo(time, pos);
					Log("walkTo step at " + time + ":");
					Log("  " + text2);
					Copy(text2);
					Notify("walkTo copied", time + "  " + Plain(pos));
				}
			}
		}

		private static void SpawnBlock()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetPlayer(out var pos, out var yaw))
			{
				string text = RegionAt(pos);
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("\"spawn\": { \"x\": ").Append(Num(pos.x)).Append(", \"y\": ")
					.Append(Num(pos.y))
					.Append(", \"z\": ")
					.Append(Num(pos.z))
					.Append(", \"rotationY\": ")
					.Append(Num(yaw));
				if (text != null)
				{
					stringBuilder.Append(", \"region\": \"").Append(text).Append('"');
				}
				stringBuilder.Append(", \"physical\": true }");
				string text2 = stringBuilder.ToString();
				Log("spawn block:");
				Log("  " + text2);
				Copy(text2);
				Notify("Spawn block copied", Plain(pos) + ((text != null) ? ("  " + text) : ""));
			}
		}

		private static void Route(string arg)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			string time;
			Vector3 pos;
			float yaw;
			if (string.Equals(arg, "show", StringComparison.OrdinalIgnoreCase))
			{
				RouteShow();
			}
			else if (string.Equals(arg, "clear", StringComparison.OrdinalIgnoreCase))
			{
				RouteClear();
			}
			else if (string.IsNullOrEmpty(arg))
			{
				Log("personnel route HH:MM  (or: personnel route show | personnel route clear)");
			}
			else if (!TryNormalizeTime(arg, out time))
			{
				Log("'" + arg + "' is not a time. Use HH:MM, e.g. personnel route 07:30");
			}
			else if (TryGetPlayer(out pos, out yaw))
			{
				string text = WalkTo(time, pos);
				try
				{
					Directory.CreateDirectory(Path.GetDirectoryName(RoutePath));
					File.AppendAllText(RoutePath, text + "," + Environment.NewLine);
				}
				catch (Exception ex)
				{
					Log("could not write " + RoutePath + ": " + ex.Message);
					return;
				}
				int value = CountRouteSteps();
				Log($"route step {value} at {time}: {text}");
				Log("  file: " + RoutePath);
				Notify("Route step " + value, time + "  " + Plain(pos));
			}
		}

		private static void RouteShow()
		{
			string[] array = ReadRoute();
			if (array.Length == 0)
			{
				Log("no route steps yet - walk somewhere and run: personnel route 07:30");
				Notify("Personnel", "Route is empty.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < array.Length; i++)
			{
				stringBuilder.Append(array[i].TrimEnd());
				if (i < array.Length - 1)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(Environment.NewLine);
			}
			string text = stringBuilder.ToString();
			Log($"route ({array.Length} step(s)) - paste into \"schedule\": [ ... ]:");
			string[] array2 = text.Split('\n');
			foreach (string text2 in array2)
			{
				Log("  " + text2.TrimEnd());
			}
			Copy(text);
			Notify("Route copied", array.Length + " step(s)");
		}

		private static void RouteClear()
		{
			try
			{
				if (File.Exists(RoutePath))
				{
					File.Delete(RoutePath);
				}
			}
			catch (Exception ex)
			{
				Log("could not clear " + RoutePath + ": " + ex.Message);
				return;
			}
			Log("route cleared.");
			Notify("Personnel", "Route cleared.");
		}

		private static string[] ReadRoute()
		{
			try
			{
				if (!File.Exists(RoutePath))
				{
					return Array.Empty<string>();
				}
				List<string> list = new List<string>();
				string[] array = File.ReadAllLines(RoutePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim().TrimEnd(',');
					if (text.Length > 0)
					{
						list.Add(text);
					}
				}
				return list.ToArray();
			}
			catch
			{
				return Array.Empty<string>();
			}
		}

		private static int CountRouteSteps()
		{
			return ReadRoute().Length;
		}

		private static void Npcs(string filter)
		{
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<NpcDef> allDefs = NpcRegistry.AllDefs;
			if (allDefs == null || allDefs.Count == 0)
			{
				Log("no NPC definitions loaded. Packs live in " + PackLoader.PacksRoot);
				Notify("Personnel", "No NPC definitions loaded.");
				return;
			}
			Dictionary<string, Vector3> dictionary = LivePositions();
			int num = 0;
			Log($"{allDefs.Count} definition(s) loaded:");
			foreach (NpcDef item in allDefs)
			{
				if (item != null && (string.IsNullOrEmpty(filter) || (item.Id != null && item.Id.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)))
				{
					bool flag = item.Schedule != null && item.Schedule.Count > 0;
					bool flag2 = item.Spawn?.Physical ?? flag;
					Vector3 value2;
					string value = ((flag2 && dictionary.TryGetValue(item.Id ?? "", out value2)) ? ("  at " + Plain(value2)) : "");
					Log($"  {item.Id} ({item.Source}) {(flag2 ? "physical" : "contact-only")}{(flag ? (", " + item.Schedule.Count + " schedule step(s)") : "")}{value}");
					num++;
				}
			}
			if (num == 0)
			{
				Log("  (nothing matched '" + filter + "')");
			}
			Notify("Personnel", num + " of " + allDefs.Count + " definition(s) listed.");
		}

		private static Dictionary<string, Vector3> LivePositions()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, Vector3> dictionary = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
			try
			{
				List<NPC> all = NPC.All;
				if (all == null)
				{
					return dictionary;
				}
				foreach (NPC item in all)
				{
					if (item == null)
					{
						continue;
					}
					try
					{
						string iD = item.ID;
						if (!string.IsNullOrEmpty(iD))
						{
							dictionary[iD] = item.Position;
						}
					}
					catch
					{
					}
				}
			}
			catch
			{
			}
			return dictionary;
		}

		private static bool TryGetPlayer(out Vector3 pos, out float yaw)
		{
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			pos = Vector3.zero;
			yaw = 0f;
			Player val = null;
			try
			{
				val = Player.Local;
			}
			catch
			{
			}
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform == (Object)null)
			{
				Log("no local player - load into a save first.");
				return false;
			}
			pos = ((Component)val).transform.position;
			yaw = ((Component)val).transform.eulerAngles.y;
			return true;
		}

		private static string RegionAt(Vector3 pos)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Map instance = Singleton<Map>.Instance;
				return ((Object)(object)instance == (Object)null) ? null : ((object)instance.GetRegionFromPosition(pos)/*cast due to .constrained prefix*/).ToString();
			}
			catch
			{
				return null;
			}
		}

		private static string WalkTo(string time, Vector3 pos)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return "{ \"type\": \"walkTo\", \"time\": \"" + time + "\", \"position\": " + Vec(pos) + ", \"warpIfSkipped\": true }";
		}

		private static bool TryNormalizeTime(string raw, out string time)
		{
			time = null;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string text = raw.Trim();
			int num = text.IndexOf(':');
			int result;
			int result2;
			if (num > 0)
			{
				if (!int.TryParse(text.Substring(0, num), NumberStyles.Integer, Inv, out result))
				{
					return false;
				}
				if (!int.TryParse(text.Substring(num + 1), NumberStyles.Integer, Inv, out result2))
				{
					return false;
				}
			}
			else
			{
				if (!int.TryParse(text, NumberStyles.Integer, Inv, out var result3))
				{
					return false;
				}
				result = result3 / 100;
				result2 = result3 % 100;
			}
			if (result < 0 || result > 23 || result2 < 0 || result2 > 59)
			{
				return false;
			}
			time = result.ToString("00", Inv) + ":" + result2.ToString("00", Inv);
			return true;
		}

		private static string Num(float f)
		{
			return f.ToString("0.##", Inv);
		}

		private static string Vec(Vector3 v)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			return "[" + Num(v.x) + ", " + Num(v.y) + ", " + Num(v.z) + "]";
		}

		private static string Plain(Vector3 v)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			return Num(v.x) + ", " + Num(v.y) + ", " + Num(v.z);
		}

		private static string Arg(string[] p, int i)
		{
			if (p.Length <= i)
			{
				return null;
			}
			return p[i];
		}

		private static void Copy(string text)
		{
			try
			{
				GUIUtility.systemCopyBuffer = text;
			}
			catch (Exception ex)
			{
				Log("clipboard unavailable (" + ex.Message + ") - copy it from this log instead.");
			}
		}

		private static void Notify(string title, string subtitle)
		{
			try
			{
				NotificationsManager instance = Singleton<NotificationsManager>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.SendNotification(title, subtitle, (Sprite)null, 5f, false);
				}
			}
			catch
			{
			}
		}

		private static void Log(string msg)
		{
			Instance log = Core.Log;
			if (log != null)
			{
				log.Msg(msg);
			}
		}
	}
	[HarmonyPatch(typeof(Console), "SubmitCommand", new Type[] { typeof(string) })]
	internal static class Personnel_Console_SubmitCommand_String_Patch
	{
		private static bool Prefix(string args)
		{
			try
			{
				return !PersonnelConsole.TryHandle(args);
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Console), "SubmitCommand", new Type[] { typeof(List<string>) })]
	internal static class Personnel_Console_SubmitCommand_List_Patch
	{
		private static bool Prefix(List<string> args)
		{
			try
			{
				return !PersonnelConsole.TryHandle(args);
			}
			catch
			{
				return true;
			}
		}
	}
}
namespace Personnel.Spawn
{
	internal static class DynamicNpcTypeFactory
	{
		private const string AssemblyName = "Personnel.Generated";

		private static ModuleBuilder _module;

		private static readonly HashSet<string> _emittedIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static int EmitAutoRegisteredTypes(IReadOnlyList<NpcDef> allDefs)
		{
			if (allDefs == null)
			{
				return 0;
			}
			List<NpcDef> list = new List<NpcDef>();
			foreach (NpcDef allDef in allDefs)
			{
				if (allDef?.Spawn != null && allDef.Spawn.Auto && !_emittedIds.Contains(allDef.Id))
				{
					list.Add(allDef);
				}
			}
			if (list.Count == 0)
			{
				return 0;
			}
			list.Sort((NpcDef a, NpcDef b) => string.CompareOrdinal(a.Id, b.Id));
			CollectExistingConsumers(out var takenDefIds, out var takenTypeNames);
			int num = 0;
			foreach (NpcDef item in list)
			{
				if (takenDefIds.Contains(item.Id))
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning("auto-register: a compiled mod already provides an NPC class for '" + item.Id + "' - skipping the generated one (compiled wins). If you migrated this pack to auto-registration, remove the old consumer DLL.");
					}
					continue;
				}
				string text = "Personnel_" + item.Id;
				if (takenTypeNames.Contains(text))
				{
					Instance log2 = Core.Log;
					if (log2 != null)
					{
						log2.Error($"auto-register: type name '{text}' collides with an existing NPC class - '{item.Id}' is NOT registered. Rename the NPC or its pack.");
					}
					continue;
				}
				try
				{
					if (SelfTest(EmitOne(text, item.Id), item.Id))
					{
						_emittedIds.Add(item.Id);
						takenTypeNames.Add(text);
						num++;
					}
				}
				catch (Exception ex)
				{
					Instance log3 = Core.Log;
					if (log3 != null)
					{
						log3.Warning($"auto-register: emitting '{item.Id}' failed ({ex.Message}) - skipped.");
					}
				}
			}
			if (num > 0)
			{
				Instance log4 = Core.Log;
				if (log4 != null)
				{
					log4.Msg($"auto-registered {num} pack NPC(s) as world NPCs (assembly '{"Personnel.Generated"}').");
				}
			}
			return num;
		}

		private static Type EmitOne(string typeName, string defId)
		{
			if (_module == null)
			{
				_module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Personnel.Generated"), AssemblyBuilderAccess.Run).DefineDynamicModule("Personnel.Generated");
			}
			TypeBuilder typeBuilder = _module.DefineType("Personnel.Generated." + typeName, TypeAttributes.Public | TypeAttributes.Sealed, typeof(PersonnelNpc));
			MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_DefId", MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(string), Type.EmptyTypes);
			ILGenerator iLGenerator = methodBuilder.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldstr, defId);
			iLGenerator.Emit(OpCodes.Ret);
			typeBuilder.DefineProperty("DefId", PropertyAttributes.None, typeof(string), null).SetGetMethod(methodBuilder);
			typeBuilder.DefineMethodOverride(methodBuilder, BaseDefIdGetter());
			typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
			return typeBuilder.CreateTypeInfo();
		}

		private static bool SelfTest(Type t, string defId)
		{
			if (t == null || t.IsAbstract || !typeof(PersonnelNpc).IsAssignableFrom(t))
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Error("auto-register: emitted type for '" + defId + "' is malformed - skipped.");
				}
				return false;
			}
			object uninitializedObject = FormatterServices.GetUninitializedObject(t);
			string text = BaseDefIdGetter().Invoke(uninitializedObject, null) as string;
			if (!string.Equals(text, defId, StringComparison.Ordinal))
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Error($"auto-register: emitted type for '{defId}' returned DefId '{text}' - skipped.");
				}
				return false;
			}
			return true;
		}

		private static MethodInfo BaseDefIdGetter()
		{
			return typeof(PersonnelNpc).GetProperty("DefId", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(nonPublic: true);
		}

		private static void CollectExistingConsumers(out HashSet<string> takenDefIds, out HashSet<string> takenTypeNames)
		{
			takenDefIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			takenTypeNames = new HashSet<string>(StringComparer.Ordinal);
			MethodInfo methodInfo = BaseDefIdGetter();
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.IsDynamic && assembly.GetName().Name == "Personnel.Generated")
				{
					continue;
				}
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type == null || type.IsAbstract)
					{
						continue;
					}
					try
					{
						if (!typeof(NPC).IsAssignableFrom(type))
						{
							continue;
						}
						takenTypeNames.Add(type.Name);
						if (typeof(PersonnelNpc).IsAssignableFrom(type))
						{
							object uninitializedObject = FormatterServices.GetUninitializedObject(type);
							if (methodInfo.Invoke(uninitializedObject, null) is string text && !string.IsNullOrWhiteSpace(text))
							{
								takenDefIds.Add(text);
							}
						}
					}
					catch
					{
					}
				}
			}
		}
	}
	internal static class ScheduleSpecFactory
	{
		public static List<IScheduleActionSpec> Build(NpcDef def)
		{
			List<IScheduleActionSpec> list = new List<IScheduleActionSpec>();
			if (def?.Schedule == null)
			{
				return list;
			}
			foreach (NpcScheduleAction item in def.Schedule)
			{
				if (item == null)
				{
					continue;
				}
				try
				{
					IScheduleActionSpec val = BuildOne(def, item);
					if (val != null)
					{
						list.Add(val);
					}
				}
				catch (Exception ex)
				{
					Warn(def, item, "failed to build (" + ex.Message + ")");
				}
			}
			return list;
		}

		private static IScheduleActionSpec BuildOne(NpcDef def, NpcScheduleAction a)
		{
			//IL_0996: Unknown result type (might be due to invalid IL or missing references)
			//IL_099c: Expected O, but got Unknown
			//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: Expected O, but got Unknown
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Expected O, but got Unknown
			//IL_0620: Unknown result type (might be due to invalid IL or missing references)
			//IL_0625: Unknown result type (might be due to invalid IL or missing references)
			//IL_062c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0636: Unknown result type (might be due to invalid IL or missing references)
			//IL_0642: Unknown result type (might be due to invalid IL or missing references)
			//IL_0650: Expected O, but got Unknown
			//IL_043e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0443: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Unknown result type (might be due to invalid IL or missing references)
			//IL_045b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Expected O, but got Unknown
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Expected O, but got Unknown
			//IL_08bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_08cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e6: Expected O, but got Unknown
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Expected O, but got Unknown
			//IL_0498: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Expected O, but got Unknown
			//IL_071e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0723: Unknown result type (might be due to invalid IL or missing references)
			//IL_072a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0734: Unknown result type (might be due to invalid IL or missing references)
			//IL_0740: Unknown result type (might be due to invalid IL or missing references)
			//IL_074e: Expected O, but got Unknown
			//IL_092f: Unknown result type (might be due to invalid IL or missing references)
			//IL_095d: Unknown result type (might be due to invalid IL or missing references)
			//IL_081c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0822: Invalid comparison between Unknown and I4
			//IL_07e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_085e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0599: Unknown result type (might be due to invalid IL or missing references)
			switch (Fold(a.Type))
			{
			case "walkto":
			{
				if (!a.Position.HasValue)
				{
					return Fail(def, a, "needs 'position'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				WalkToSpec val5 = new WalkToSpec
				{
					Destination = a.Position.Value,
					StartTime = a.Time,
					Name = a.Name
				};
				if (a.FaceDestination.HasValue)
				{
					val5.FaceDestinationDirection = a.FaceDestination.Value;
				}
				if (a.Within.HasValue)
				{
					val5.Within = a.Within.Value;
				}
				if (a.WarpIfSkipped.HasValue)
				{
					val5.WarpIfSkipped = a.WarpIfSkipped.Value;
				}
				return (IScheduleActionSpec)(object)val5;
			}
			case "stayinbuilding":
			{
				if (string.IsNullOrWhiteSpace(a.Building))
				{
					return Fail(def, a, "needs 'building'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				StayInBuildingSpec val7 = new StayInBuildingSpec
				{
					BuildingName = a.Building,
					StartTime = a.Time,
					Name = a.Name
				};
				if (a.DurationMinutes.HasValue)
				{
					val7.DurationMinutes = a.DurationMinutes.Value;
				}
				if (a.DoorIndex.HasValue)
				{
					val7.DoorIndex = a.DoorIndex.Value;
				}
				return (IScheduleActionSpec)(object)val7;
			}
			case "sit":
			{
				if (string.IsNullOrWhiteSpace(a.SeatSet) && string.IsNullOrWhiteSpace(a.SeatSetPath))
				{
					return Fail(def, a, "needs 'seatSet' or 'seatSetPath'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				SitSpec val3 = new SitSpec
				{
					StartTime = a.Time,
					Name = a.Name
				};
				if (!string.IsNullOrWhiteSpace(a.SeatSet))
				{
					val3.SeatSetName = a.SeatSet;
				}
				if (!string.IsNullOrWhiteSpace(a.SeatSetPath))
				{
					val3.SeatSetPath = a.SeatSetPath;
				}
				if (a.DurationMinutes.HasValue)
				{
					val3.DurationMinutes = a.DurationMinutes.Value;
				}
				if (a.WarpIfSkipped.HasValue)
				{
					val3.WarpIfSkipped = a.WarpIfSkipped.Value;
				}
				if (a.IncludeInactive.HasValue)
				{
					val3.IncludeInactiveSearch = a.IncludeInactive.Value;
				}
				return (IScheduleActionSpec)(object)val3;
			}
			case "usevendingmachine":
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				return (IScheduleActionSpec)new UseVendingMachineSpec
				{
					StartTime = a.Time,
					MachineGUID = a.MachineGuid,
					Name = a.Name
				};
			case "useatm":
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				return (IScheduleActionSpec)new UseATMSpec
				{
					StartTime = a.Time,
					ATMGUID = a.AtmGuid,
					Name = a.Name
				};
			case "useslotmachine":
			{
				if (!a.Position.HasValue)
				{
					return Fail(def, a, "needs 'position'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				UseSlotMachineSpec val6 = new UseSlotMachineSpec
				{
					MachinePosition = a.Position.Value,
					StartTime = a.Time,
					Name = a.Name
				};
				if (a.Bet.HasValue)
				{
					val6.BetAmount = a.Bet.Value;
				}
				if (a.Spins.HasValue)
				{
					val6.SpinCount = a.Spins.Value;
				}
				if (a.EndTime >= 0)
				{
					val6.EndTime = a.EndTime;
				}
				if (a.TimeBetweenSpins.HasValue)
				{
					val6.TimeBetweenSpins = a.TimeBetweenSpins.Value;
				}
				if (a.MaxSearchDistance.HasValue)
				{
					val6.MaxSearchDistance = a.MaxSearchDistance.Value;
				}
				if (!string.IsNullOrWhiteSpace(a.Mode))
				{
					if (Parse.TryParseEnum<GamblingSessionMode>((Fold(a.Mode) == "single") ? "SingleSpin" : a.Mode, out GamblingSessionMode result3))
					{
						val6.SessionMode = result3;
					}
					else
					{
						Warn(def, a, $"unknown mode '{a.Mode}' - using {val6.SessionMode}");
					}
				}
				return (IScheduleActionSpec)(object)val6;
			}
			case "locationdialogue":
			{
				if (!a.Position.HasValue)
				{
					return Fail(def, a, "needs 'position'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				LocationDialogueSpec val2 = new LocationDialogueSpec
				{
					Destination = a.Position.Value,
					StartTime = a.Time,
					Name = a.Name
				};
				if (a.FaceDestination.HasValue)
				{
					val2.FaceDestinationDirection = a.FaceDestination.Value;
				}
				if (a.Within.HasValue)
				{
					val2.Within = a.Within.Value;
				}
				if (a.WarpIfSkipped.HasValue)
				{
					val2.WarpIfSkipped = a.WarpIfSkipped.Value;
				}
				if (a.GreetingOverride.HasValue)
				{
					val2.GreetingOverrideToEnable = a.GreetingOverride.Value;
				}
				if (a.Choice.HasValue)
				{
					val2.ChoiceToEnable = a.Choice.Value;
				}
				return (IScheduleActionSpec)(object)val2;
			}
			case "locationaction":
			{
				if (!a.Position.HasValue)
				{
					return Fail(def, a, "needs 'position'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				LocationBasedActionSpec val4 = new LocationBasedActionSpec
				{
					Destination = a.Position.Value,
					StartTime = a.Time,
					Name = a.Name
				};
				if (a.DurationMinutes.HasValue)
				{
					val4.DurationMinutes = a.DurationMinutes.Value;
				}
				if (a.FaceDestination.HasValue)
				{
					val4.FaceDestinationDirection = a.FaceDestination.Value;
				}
				if (a.Within.HasValue)
				{
					val4.Within = a.Within.Value;
				}
				if (a.WarpIfSkipped.HasValue)
				{
					val4.WarpIfSkipped = a.WarpIfSkipped.Value;
				}
				if (!string.IsNullOrWhiteSpace(a.Action))
				{
					if (Parse.TryParseEnum<LocationArriveBehaviour>(a.Action, out LocationArriveBehaviour result))
					{
						val4.ArriveBehaviour = result;
					}
					else
					{
						Warn(def, a, "unknown action '" + a.Action + "' - using None");
					}
				}
				if (!string.IsNullOrWhiteSpace(a.EquippablePath))
				{
					if ((int)val4.ArriveBehaviour == 3)
					{
						val4.DrinkEquippablePath = a.EquippablePath;
					}
					else
					{
						val4.EquippableAssetPath = a.EquippablePath;
					}
				}
				if (!string.IsNullOrWhiteSpace(a.GraffitiRegion))
				{
					if (Parse.TryParseEnum<Region>(a.GraffitiRegion, out Region result2))
					{
						val4.GraffitiRegion = result2;
					}
					else
					{
						Warn(def, a, "unknown graffitiRegion '" + a.GraffitiRegion + "' - ignored");
					}
				}
				return (IScheduleActionSpec)(object)val4;
			}
			case "drivetocarpark":
			{
				if (string.IsNullOrWhiteSpace(a.ParkingLot))
				{
					return Fail(def, a, "needs 'parkingLot'");
				}
				if (a.Time < 0)
				{
					return Fail(def, a, "needs 'time'");
				}
				DriveToCarParkSpec val = new DriveToCarParkSpec
				{
					ParkingLotName = a.ParkingLot,
					StartTime = a.Time,
					Name = a.Name
				};
				if (!string.IsNullOrWhiteSpace(a.Vehicle))
				{
					val.VehicleName = a.Vehicle;
				}
				if (!string.IsNullOrWhiteSpace(a.CreateVehicleCode))
				{
					val.VehicleCode = a.CreateVehicleCode;
					if (a.CreateVehiclePosition.HasValue)
					{
						val.VehicleSpawnPosition = a.CreateVehiclePosition.Value;
					}
					if (a.CreateVehicleRotationY.HasValue)
					{
						val.VehicleSpawnRotation = Quaternion.Euler(0f, a.CreateVehicleRotationY.Value, 0f);
					}
				}
				if (string.IsNullOrWhiteSpace(a.Vehicle) && string.IsNullOrWhiteSpace(a.CreateVehicleCode))
				{
					return Fail(def, a, "needs 'vehicle' or 'createVehicle'");
				}
				return (IScheduleActionSpec)(object)val;
			}
			case "dealsignal":
				return (IScheduleActionSpec)new EnsureDealSignalSpec();
			case "handledeal":
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Msg("'" + def.Id + "': schedule action 'handleDeal' is obsolete (deals are automatic) - ignored.");
				}
				return null;
			}
			default:
				return Fail(def, a, "unknown action type");
			}
		}

		private static IScheduleActionSpec Fail(NpcDef def, NpcScheduleAction a, string reason)
		{
			Warn(def, a, reason);
			return null;
		}

		private static void Warn(NpcDef def, NpcScheduleAction a, string message)
		{
			Instance log = Core.Log;
			if (log != null)
			{
				log.Warning($"'{def.Id}' schedule action '{a.Type}': {message} - skipped/ignored.");
			}
		}

		private static string Fold(string s)
		{
			if (s == null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			foreach (char c in s)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}
	}
}
namespace Personnel.Registration
{
	internal static class CustomLayerRegistry
	{
		private static readonly Dictionary<string, string> _resolved = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static string SourceLayer(bool face)
		{
			if (!face)
			{
				return "Avatar/Layers/Tattoos/chest/Chest_Bird";
			}
			return "Avatar/Layers/Tattoos/face/Face_Teardrop";
		}

		public static string EnsureLayer(string npcSource, string npcId, string packDir, string file, Texture2D tex, bool face)
		{
			string text = ((!string.IsNullOrEmpty(file)) ? Path.GetFileNameWithoutExtension(file) : "tex");
			string text2 = (face ? "Face" : "body");
			string text3 = "Avatar/Layers/Tattoos/personnel/" + text2 + "/" + Sanitize(npcSource) + "_" + Sanitize(npcId) + "_" + Sanitize(text);
			if (_resolved.TryGetValue(text3, out var value))
			{
				return value;
			}
			try
			{
				if ((Object)(object)tex == (Object)null)
				{
					if (string.IsNullOrWhiteSpace(file))
					{
						Instance log = Core.Log;
						if (log != null)
						{
							log.Warning($"Custom layer for '{npcSource}/{npcId}': no texture or file.");
						}
						return null;
					}
					string text4 = (Path.IsPathRooted(file) ? file : Path.Combine(packDir ?? "", file));
					if (!File.Exists(text4))
					{
						Instance log2 = Core.Log;
						if (log2 != null)
						{
							log2.Warning($"Custom layer for '{npcSource}/{npcId}': PNG not found at '{text4}'.");
						}
						return null;
					}
					tex = TextureUtils.LoadTextureFromFile(text4, (FilterMode)1, (TextureWrapMode)1);
					if ((Object)(object)tex == (Object)null)
					{
						Instance log3 = Core.Log;
						if (log3 != null)
						{
							log3.Warning($"Custom layer for '{npcSource}/{npcId}': failed to load '{text4}'.");
						}
						return null;
					}
				}
				if (!AvatarLayerFactory.CreateAndRegisterAvatarLayer(SourceLayer(face), text3, npcId ?? text, tex))
				{
					Instance log4 = Core.Log;
					if (log4 != null)
					{
						log4.Warning($"Custom layer for '{npcSource}/{npcId}': CreateAndRegisterAvatarLayer failed.");
					}
					return null;
				}
				_resolved[text3] = text3;
				Instance log5 = Core.Log;
				if (log5 != null)
				{
					log5.Msg($"Registered custom layer '{npcSource}/{npcId}' ({text}) -> {text3}");
				}
				return text3;
			}
			catch (Exception ex)
			{
				Instance log6 = Core.Log;
				if (log6 != null)
				{
					log6.Warning($"Custom layer for '{npcSource}/{npcId}': registration error - {ex.Message}");
				}
				return null;
			}
		}

		private static string Sanitize(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "x";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			foreach (char c in s)
			{
				stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-' || c == '_') ? c : '_');
			}
			return stringBuilder.ToString();
		}
	}
	internal static class NpcRegistry
	{
		private static readonly List<NpcDef> _all = new List<NpcDef>();

		private static readonly HashSet<string> _keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static IReadOnlyList<NpcDef> AllDefs => _all;

		public static event Action OnReloaded;

		public static bool Add(NpcDef def)
		{
			if (def == null || string.IsNullOrWhiteSpace(def.Id))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(def.Source))
			{
				def.Source = "API";
			}
			if (!_keys.Add(def.Key))
			{
				return false;
			}
			_all.Add(def);
			return true;
		}

		public static int AddRange(IEnumerable<NpcDef> defs)
		{
			int num = 0;
			if (defs == null)
			{
				return 0;
			}
			foreach (NpcDef def in defs)
			{
				if (Add(def))
				{
					num++;
				}
			}
			return num;
		}

		public static bool TryGet(string id, out NpcDef def)
		{
			def = null;
			if (string.IsNullOrWhiteSpace(id))
			{
				return false;
			}
			foreach (NpcDef item in _all)
			{
				if (string.Equals(item.Id, id, StringComparison.OrdinalIgnoreCase))
				{
					def = item;
					return true;
				}
			}
			return false;
		}

		public static int LoadPacks()
		{
			return AddRange(PackLoader.LoadAll());
		}

		public static void Reload()
		{
			for (int num = _all.Count - 1; num >= 0; num--)
			{
				if (_all[num].PackDir != null)
				{
					_keys.Remove(_all[num].Key);
					_all.RemoveAt(num);
				}
			}
			LoadPacks();
			try
			{
				NpcRegistry.OnReloaded?.Invoke();
			}
			catch (Exception ex)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("OnReloaded handler threw: " + ex.Message);
				}
			}
		}
	}
}
namespace Personnel.Model
{
	public sealed class NpcDef
	{
		public string Id;

		public string DisplayName;

		public string Source;

		public string PackDir;

		public NpcAppearance Appearance = new NpcAppearance();

		public string SaveId;

		public NpcBehavior Behavior;

		public NpcSpawn Spawn;

		public NpcContact Contact;

		public NpcRelationships Relationships;

		public NpcCustomer Customer;

		public NpcDealer Dealer;

		public NpcInventory Inventory;

		public List<NpcScheduleAction> Schedule;

		public IReadOnlyDictionary<string, string> Extensions = new Dictionary<string, string>();

		public string Key => (Source ?? "?") + "/" + (Id ?? "?");
	}
	public sealed class NpcAppearance
	{
		public float Gender;

		public float Height = 0.98f;

		public float Weight = 0.4f;

		public Color SkinColor = Color32.op_Implicit(new Color32((byte)150, (byte)120, (byte)95, byte.MaxValue));

		public string HairPath = "";

		public Color HairColor = Color.black;

		public float EyebrowScale = 1f;

		public float EyebrowThickness = 1f;

		public float EyebrowRestingHeight;

		public float EyebrowRestingAngle;

		public Color LeftEyeLidColor = Color32.op_Implicit(new Color32((byte)150, (byte)120, (byte)95, byte.MaxValue));

		public Color RightEyeLidColor = Color32.op_Implicit(new Color32((byte)150, (byte)120, (byte)95, byte.MaxValue));

		public float LeftEyeTop = 0.5f;

		public float LeftEyeBottom = 0.5f;

		public float RightEyeTop = 0.5f;

		public float RightEyeBottom = 0.5f;

		public string EyeballMaterial = "Default";

		public Color EyeBallTint = Color.white;

		public float PupilDilation = 1f;

		public List<NpcLayer> FaceLayers = new List<NpcLayer>();

		public List<NpcLayer> BodyLayers = new List<NpcLayer>();

		public List<NpcLayer> Accessories = new List<NpcLayer>();

		public Dictionary<string, BoneDistortion> Distortion = new Dictionary<string, BoneDistortion>();
	}
	public sealed class BoneDistortion
	{
		public Vector3 Scale = Vector3.one;

		public bool Hide;
	}
	public sealed class NpcLayer
	{
		public string Path;

		public string File;

		public Texture2D Texture;

		public Color Tint = Color.white;
	}
	public sealed class NpcBehavior
	{
		public float Aggression;

		public float MaxHealth = 100f;

		public float Scale = 1f;

		public string Conversation = "none";
	}
	public sealed class NpcSpawn
	{
		public Vector3? Position;

		public float? RotationY;

		public string Region = "";

		public bool? Physical;

		public bool Auto;
	}
	public sealed class NpcContact
	{
		public bool? Visible;

		public bool? MapMarker;
	}
	public sealed class NpcRelationships
	{
		public float? Delta;

		public bool? Unlocked;

		public string UnlockType;

		public List<string> Connections;
	}
	public sealed class NpcRange
	{
		public float Min;

		public float Max;
	}
	public sealed class NpcCustomer
	{
		public NpcRange Spending;

		public NpcRange OrdersPerWeek;

		public string PreferredOrderDay;

		public int? OrderTime;

		public string Standards;

		public bool? AllowDirectApproach;

		public bool? GuaranteeFirstSample;

		public NpcRange MutualRelationRequirement;

		public float? CallPoliceChance;

		public float? DependenceBase;

		public float? DependenceMultiplier;

		public Dictionary<string, float> Affinities;

		public List<string> PreferredProperties;
	}
	public sealed class NpcDealer
	{
		public string Type;

		public float? Cut;

		public float? SigningFee;

		public string Home;

		public string CompletedDealsVariable;

		public bool? AllowInsufficientQuality;

		public bool? AllowExcessQuality;
	}
	public sealed class NpcInventoryItem
	{
		public string Id;

		public int Quantity = 1;
	}
	public sealed class NpcInventory
	{
		public NpcRange Cash;

		public List<NpcInventoryItem> Items;

		public bool? ClearEachNight;
	}
	public sealed class NpcScheduleAction
	{
		public string Type;

		public int Time = -1;

		public string Name;

		public Vector3? Position;

		public bool? FaceDestination;

		public float? Within;

		public bool? WarpIfSkipped;

		public int? DurationMinutes;

		public string Building;

		public int? DoorIndex;

		public string SeatSet;

		public string SeatSetPath;

		public bool? IncludeInactive;

		public string MachineGuid;

		public string AtmGuid;

		public int? Bet;

		public string Mode;

		public int EndTime = -1;

		public int? Spins;

		public float? TimeBetweenSpins;

		public float? MaxSearchDistance;

		public int? GreetingOverride;

		public int? Choice;

		public string Action;

		public string EquippablePath;

		public string GraffitiRegion;

		public string ParkingLot;

		public string Vehicle;

		public string CreateVehicleCode;

		public Vector3? CreateVehiclePosition;

		public float? CreateVehicleRotationY;
	}
}
namespace Personnel.Content
{
	internal static class ExamplePack
	{
		private const string ExampleManifestJson = "{\n  \"name\": \"Personnel Examples\",\n  \"author\": \"DooDesch\",\n  \"npcs\": [\n    {\n      \"name\": \"Pale\",\n      \"appearance\": {\n        \"gender\": 0.5,\n        \"height\": 1.0,\n        \"weight\": 0.4,\n        \"skinColor\": \"#8899AA\",\n        \"hairPath\": \"\",\n        \"hairColor\": \"#101014\",\n        \"eyeBallTint\": \"#FFFFFF\",\n        \"pupilDilation\": 0.8\n      },\n      \"extensions\": {\n        \"backrooms\": {\n          \"archetype\": \"faceling\",\n          \"tierMin\": 1, \"tierMax\": 5,\n          \"biomes\": [\"L0\", \"L1\"],\n          \"weight\": 14, \"maxAlive\": 3, \"hostile\": false\n        }\n      }\n    },\n    {\n      \"name\": \"Ashen\",\n      \"appearance\": {\n        \"gender\": 0.2,\n        \"height\": 1.1,\n        \"weight\": 0.6,\n        \"skinColor\": \"#4A4A50\",\n        \"hairColor\": \"#000000\",\n        \"eyeBallTint\": \"#FFCC66\"\n      },\n      \"extensions\": {\n        \"backrooms\": {\n          \"archetype\": \"wanderer_hollow\",\n          \"tierMin\": 3, \"tierMax\": 5,\n          \"weight\": 10, \"maxAlive\": 1, \"hostile\": true\n        }\n      }\n    },\n    {\n      \"name\": \"Errand Eddie\",\n      \"appearance\": {\n        \"gender\": 0.0,\n        \"height\": 1.0,\n        \"weight\": 0.5,\n        \"skinColor\": \"#C09070\",\n        \"hairColor\": \"#3A2A1A\"\n      },\n      \"spawn\": {\n        \"x\": -66.4, \"y\": -2.9, \"z\": 86.1,\n        \"rotationY\": 145.0,\n        \"region\": \"Westville\",\n        \"physical\": true,\n        \"auto\": false\n      },\n      \"contact\": { \"visible\": true, \"mapMarker\": true },\n      \"relationships\": { \"delta\": 1.5, \"unlockType\": \"Recommendation\" },\n      \"customer\": {\n        \"spending\": { \"min\": 300, \"max\": 900 },\n        \"ordersPerWeek\": { \"min\": 1, \"max\": 3 },\n        \"preferredOrderDay\": \"Friday\",\n        \"orderTime\": \"19:30\",\n        \"standards\": \"Moderate\",\n        \"affinities\": { \"marijuana\": 0.6 }\n      },\n      \"inventory\": { \"cash\": { \"min\": 20, \"max\": 120 }, \"clearEachNight\": true },\n      \"schedule\": [\n        { \"type\": \"walkTo\", \"time\": \"07:30\", \"position\": [-70.1, -2.9, 80.0] },\n        { \"type\": \"stayInBuilding\", \"time\": \"09:00\", \"duration\": 240, \"building\": \"Thrifty Threads\" },\n        { \"type\": \"walkTo\", \"time\": \"14:00\", \"position\": [-66.4, -2.9, 86.1] }\n      ]\n    }\n  ]\n}\n";

		public static void ExtractIfEnabled()
		{
			if (!Preferences.LoadExamplePack)
			{
				return;
			}
			string text = Path.Combine(PackLoader.PacksRoot, "Examples");
			try
			{
				string path = Path.Combine(text, "manifest.json");
				if (File.Exists(path))
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Msg("Example pack already present - leaving it untouched.");
					}
					return;
				}
				Directory.CreateDirectory(text);
				File.WriteAllText(path, "{\n  \"name\": \"Personnel Examples\",\n  \"author\": \"DooDesch\",\n  \"npcs\": [\n    {\n      \"name\": \"Pale\",\n      \"appearance\": {\n        \"gender\": 0.5,\n        \"height\": 1.0,\n        \"weight\": 0.4,\n        \"skinColor\": \"#8899AA\",\n        \"hairPath\": \"\",\n        \"hairColor\": \"#101014\",\n        \"eyeBallTint\": \"#FFFFFF\",\n        \"pupilDilation\": 0.8\n      },\n      \"extensions\": {\n        \"backrooms\": {\n          \"archetype\": \"faceling\",\n          \"tierMin\": 1, \"tierMax\": 5,\n          \"biomes\": [\"L0\", \"L1\"],\n          \"weight\": 14, \"maxAlive\": 3, \"hostile\": false\n        }\n      }\n    },\n    {\n      \"name\": \"Ashen\",\n      \"appearance\": {\n        \"gender\": 0.2,\n        \"height\": 1.1,\n        \"weight\": 0.6,\n        \"skinColor\": \"#4A4A50\",\n        \"hairColor\": \"#000000\",\n        \"eyeBallTint\": \"#FFCC66\"\n      },\n      \"extensions\": {\n        \"backrooms\": {\n          \"archetype\": \"wanderer_hollow\",\n          \"tierMin\": 3, \"tierMax\": 5,\n          \"weight\": 10, \"maxAlive\": 1, \"hostile\": true\n        }\n      }\n    },\n    {\n      \"name\": \"Errand Eddie\",\n      \"appearance\": {\n        \"gender\": 0.0,\n        \"height\": 1.0,\n        \"weight\": 0.5,\n        \"skinColor\": \"#C09070\",\n        \"hairColor\": \"#3A2A1A\"\n      },\n      \"spawn\": {\n        \"x\": -66.4, \"y\": -2.9, \"z\": 86.1,\n        \"rotationY\": 145.0,\n        \"region\": \"Westville\",\n        \"physical\": true,\n        \"auto\": false\n      },\n      \"contact\": { \"visible\": true, \"mapMarker\": true },\n      \"relationships\": { \"delta\": 1.5, \"unlockType\": \"Recommendation\" },\n      \"customer\": {\n        \"spending\": { \"min\": 300, \"max\": 900 },\n        \"ordersPerWeek\": { \"min\": 1, \"max\": 3 },\n        \"preferredOrderDay\": \"Friday\",\n        \"orderTime\": \"19:30\",\n        \"standards\": \"Moderate\",\n        \"affinities\": { \"marijuana\": 0.6 }\n      },\n      \"inventory\": { \"cash\": { \"min\": 20, \"max\": 120 }, \"clearEachNight\": true },\n      \"schedule\": [\n        { \"type\": \"walkTo\", \"time\": \"07:30\", \"position\": [-70.1, -2.9, 80.0] },\n        { \"type\": \"stayInBuilding\", \"time\": \"09:00\", \"duration\": 240, \"building\": \"Thrifty Threads\" },\n        { \"type\": \"walkTo\", \"time\": \"14:00\", \"position\": [-66.4, -2.9, 86.1] }\n      ]\n    }\n  ]\n}\n");
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Msg("Wrote example NPC pack -> " + text);
				}
			}
			catch (Exception ex)
			{
				Instance log3 = Core.Log;
				if (log3 != null)
				{
					log3.Warning("Example pack write failed: " + ex.Message);
				}
			}
		}
	}
	public sealed class NpcPackManifest
	{
		public string name;

		public string author;

		public int? schemaVersion;

		public string packId;

		public bool? autoRegister;

		public List<NpcEntry> npcs;
	}
	public sealed class NpcEntry
	{
		public string id;

		public string name;

		public string saveId;

		public AppearanceJson appearance;

		public BehaviorJson behavior;

		public SpawnJson spawn;

		public ContactJson contact;

		public RelationshipsJson relationships;

		public CustomerJson customer;

		public DealerJson dealer;

		public InventoryJson inventory;

		public List<ScheduleActionJson> schedule;

		public JObject extensions;
	}
	public sealed class AppearanceJson
	{
		public float? gender;

		public float? height;

		public float? weight;

		public string skinColor;

		public string hairPath;

		public string hairColor;

		public float? eyebrowScale;

		public float? eyebrowThickness;

		public float? eyebrowRestingHeight;

		public float? eyebrowRestingAngle;

		public string leftEyeLidColor;

		public string rightEyeLidColor;

		public EyeJson leftEye;

		public EyeJson rightEye;

		public string eyeballMaterial;

		public string eyeBallTint;

		public float? pupilDilation;

		public List<LayerJson> faceLayers;

		public List<LayerJson> bodyLayers;

		public List<LayerJson> accessories;

		public Dictionary<string, BoneDistortionJson> distortion;
	}
	public sealed class BoneDistortionJson
	{
		public float? scaleX;

		public float? scaleY;

		public float? scaleZ;

		public bool? hide;
	}
	public sealed class EyeJson
	{
		public float? top;

		public float? bottom;
	}
	public sealed class LayerJson
	{
		public string path;

		public string file;

		public string kind;

		public string tint;

		public string color;
	}
	public sealed class BehaviorJson
	{
		public float? aggression;

		public float? maxHealth;

		public float? scale;

		public string conversation;
	}
	public sealed class SpawnJson
	{
		public float? x;

		public float? y;

		public float? z;

		public float? rotationY;

		public string region;

		public bool? physical;

		public bool? auto;
	}
	public sealed class ContactJson
	{
		public bool? visible;

		public bool? mapMarker;
	}
	public sealed class RelationshipsJson
	{
		public float? delta;

		public bool? unlocked;

		public string unlockType;

		public List<string> connections;
	}
	public sealed class MinMaxJson
	{
		public float? min;

		public float? max;
	}
	public sealed class CustomerJson
	{
		public MinMaxJson spending;

		public MinMaxJson ordersPerWeek;

		public string preferredOrderDay;

		public string orderTime;

		public string standards;

		public bool? allowDirectApproach;

		public bool? guaranteeFirstSample;

		public MinMaxJson mutualRelationRequirement;

		public float? callPoliceChance;

		public DependenceJson dependence;

		public Dictionary<string, float> affinities;

		public List<string> preferredProperties;
	}
	public sealed class DependenceJson
	{
		public float? @base;

		public float? multiplier;
	}
	public sealed class DealerJson
	{
		public string type;

		public float? cut;

		public float? signingFee;

		public string home;

		public string completedDealsVariable;

		public bool? allowInsufficientQuality;

		public bool? allowExcessQuality;
	}
	public sealed class InventoryJson
	{
		public MinMaxJson cash;

		public List<JToken> items;

		public bool? clearEachNight;
	}
	public sealed class ScheduleActionJson
	{
		public string type;

		public string time;

		public string name;

		public float[] position;

		public bool? faceDestination;

		public float? within;

		public bool? warpIfSkipped;

		public int? duration;

		public string building;

		public int? doorIndex;

		public string seatSet;

		public string seatSetPath;

		public bool? includeInactive;

		public string machineGuid;

		public string atmGuid;

		public int? bet;

		public string mode;

		public string endTime;

		public int? spins;

		public float? timeBetweenSpins;

		public float? maxSearchDistance;

		public int? greetingOverride;

		public int? choice;

		public string action;

		public string equippablePath;

		public string graffitiRegion;

		public string parkingLot;

		public string vehicle;

		public CreateVehicleJson createVehicle;
	}
	public sealed class CreateVehicleJson
	{
		public string code;

		public float[] position;

		public float? rotationY;
	}
	internal static class PackLoader
	{
		public const int SchemaVersion = 2;

		public static string PacksRoot => Path.Combine(MelonEnvironment.UserDataDirectory, "Personnel", "Packs");

		public static List<NpcDef> LoadAll()
		{
			List<NpcDef> list = new List<NpcDef>();
			string packsRoot = PacksRoot;
			try
			{
				Directory.CreateDirectory(packsRoot);
				WriteReadmeIfMissing(packsRoot);
			}
			catch (Exception ex)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("Could not prepare packs folder '" + packsRoot + "': " + ex.Message);
				}
				return list;
			}
			string[] directories = Directory.GetDirectories(packsRoot);
			Array.Sort(directories, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			string[] array = directories;
			foreach (string text in array)
			{
				string path = Path.Combine(text, "manifest.json");
				if (!File.Exists(path))
				{
					continue;
				}
				string name = new DirectoryInfo(text).Name;
				try
				{
					NpcPackManifest npcPackManifest = JsonConvert.DeserializeObject<NpcPackManifest>(File.ReadAllText(path));
					if (npcPackManifest?.npcs == null)
					{
						Instance log2 = Core.Log;
						if (log2 != null)
						{
							log2.Warning("Pack '" + name + "': manifest has no 'npcs' array - skipped.");
						}
						continue;
					}
					if (npcPackManifest.schemaVersion.HasValue && npcPackManifest.schemaVersion.Value > 2)
					{
						Instance log3 = Core.Log;
						if (log3 != null)
						{
							log3.Warning($"Pack '{name}': schemaVersion {npcPackManifest.schemaVersion} is newer than this Personnel understands ({2}) - unknown fields will be ignored. " + "Update Personnel.");
						}
					}
					int num = 0;
					List<string> list2 = new List<string>();
					foreach (NpcEntry npc in npcPackManifest.npcs)
					{
						NpcDef npcDef = ToDef(name, text, npcPackManifest, npc);
						if (npcDef != null)
						{
							list.Add(npcDef);
							num++;
							list2.Add(npcDef.Id);
						}
					}
					Instance log4 = Core.Log;
					if (log4 != null)
					{
						log4.Msg($"Pack '{name}' ({npcPackManifest.name ?? "unnamed"}): {num} NPC(s) [{string.Join(", ", list2)}].");
					}
				}
				catch (Exception ex2)
				{
					Instance log5 = Core.Log;
					if (log5 != null)
					{
						log5.Warning("Pack '" + name + "': failed to read manifest.json - " + ex2.Message);
					}
				}
			}
			return list;
		}

		private static NpcDef ToDef(string packName, string packDir, NpcPackManifest manifest, NpcEntry e)
		{
			if (e == null)
			{
				return null;
			}
			string text = ((!string.IsNullOrWhiteSpace(e.name)) ? e.name : e.id);
			if (string.IsNullOrWhiteSpace(text))
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("Pack '" + packName + "': an NPC entry has no 'name' (or 'id') - skipped.");
				}
				return null;
			}
			string text2 = Ids.Make((!string.IsNullOrWhiteSpace(manifest?.packId)) ? manifest.packId : packName, text);
			string text3 = Ids.Normalize(e.id);
			string id = ((!string.IsNullOrEmpty(text3)) ? text3 : text2);
			if (!string.IsNullOrEmpty(text3) && !string.Equals(text3, text2, StringComparison.Ordinal))
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Msg($"Pack '{packName}': NPC '{text}' uses its authored id '{text3}' (derived would be '{text2}').");
				}
			}
			NpcDef npcDef = new NpcDef
			{
				Id = id,
				SaveId = (string.IsNullOrWhiteSpace(e.saveId) ? null : Ids.Normalize(e.saveId)),
				DisplayName = text,
				Source = packName,
				PackDir = packDir,
				Appearance = BuildAppearance(e.appearance),
				Behavior = BuildBehavior(e.behavior),
				Spawn = BuildSpawn(e.spawn, manifest, e),
				Contact = BuildContact(e.contact),
				Relationships = BuildRelationships(e.relationships),
				Customer = BuildCustomer(packName, text, e.customer),
				Dealer = BuildDealer(e.dealer),
				Inventory = BuildInventory(packName, text, e.inventory),
				Schedule = BuildSchedule(packName, text, e.schedule),
				Extensions = BuildExtensions(e.extensions)
			};
			if (npcDef.Spawn == null && (e.spawn?.auto ?? (manifest?.autoRegister == true)))
			{
				npcDef.Spawn = new NpcSpawn
				{
					Auto = true
				};
			}
			return npcDef;
		}

		private static NpcAppearance BuildAppearance(AppearanceJson a)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			NpcAppearance npcAppearance = new NpcAppearance();
			if (a == null)
			{
				return npcAppearance;
			}
			if (a.gender.HasValue)
			{
				npcAppearance.Gender = a.gender.Value;
			}
			if (a.height.HasValue)
			{
				npcAppearance.Height = a.height.Value;
			}
			if (a.weight.HasValue)
			{
				npcAppearance.Weight = a.weight.Value;
			}
			npcAppearance.SkinColor = ColorParse.Parse(a.skinColor, npcAppearance.SkinColor);
			if (a.hairPath != null)
			{
				npcAppearance.HairPath = a.hairPath;
			}
			npcAppearance.HairColor = ColorParse.Parse(a.hairColor, npcAppearance.HairColor);
			if (a.eyebrowScale.HasValue)
			{
				npcAppearance.EyebrowScale = a.eyebrowScale.Value;
			}
			if (a.eyebrowThickness.HasValue)
			{
				npcAppearance.EyebrowThickness = a.eyebrowThickness.Value;
			}
			if (a.eyebrowRestingHeight.HasValue)
			{
				npcAppearance.EyebrowRestingHeight = a.eyebrowRestingHeight.Value;
			}
			if (a.eyebrowRestingAngle.HasValue)
			{
				npcAppearance.EyebrowRestingAngle = a.eyebrowRestingAngle.Value;
			}
			npcAppearance.LeftEyeLidColor = ColorParse.Parse(a.leftEyeLidColor, npcAppearance.SkinColor);
			npcAppearance.RightEyeLidColor = ColorParse.Parse(a.rightEyeLidColor, npcAppearance.SkinColor);
			if (a.leftEye != null)
			{
				if (a.leftEye.top.HasValue)
				{
					npcAppearance.LeftEyeTop = a.leftEye.top.Value;
				}
				if (a.leftEye.bottom.HasValue)
				{
					npcAppearance.LeftEyeBottom = a.leftEye.bottom.Value;
				}
			}
			if (a.rightEye != null)
			{
				if (a.rightEye.top.HasValue)
				{
					npcAppearance.RightEyeTop = a.rightEye.top.Value;
				}
				if (a.rightEye.bottom.HasValue)
				{
					npcAppearance.RightEyeBottom = a.rightEye.bottom.Value;
				}
			}
			if (!string.IsNullOrWhiteSpace(a.eyeballMaterial))
			{
				npcAppearance.EyeballMaterial = a.eyeballMaterial;
			}
			npcAppearance.EyeBallTint = ColorParse.Parse(a.eyeBallTint, npcAppearance.EyeBallTint);
			if (a.pupilDilation.HasValue)
			{
				npcAppearance.PupilDilation = a.pupilDilation.Value;
			}
			AppendLayers(a.faceLayers, npcAppearance.FaceLayers);
			AppendLayers(a.bodyLayers, npcAppearance.BodyLayers);
			AppendLayers(a.accessories, npcAppearance.Accessories);
			if (a.distortion != null)
			{
				foreach (KeyValuePair<string, BoneDistortionJson> item in a.distortion)
				{
					if (item.Value != null)
					{
						npcAppearance.Distortion[item.Key] = new BoneDistortion
						{
							Scale = new Vector3(item.Value.scaleX ?? 1f, item.Value.scaleY ?? 1f, item.Value.scaleZ ?? 1f),
							Hide = (item.Value.hide == true)
						};
					}
				}
			}
			return npcAppearance;
		}

		private static void AppendLayers(List<LayerJson> src, List<NpcLayer> dst)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (src == null)
			{
				return;
			}
			foreach (LayerJson item in src)
			{
				if (item != null && (!string.IsNullOrWhiteSpace(item.path) || !string.IsNullOrWhiteSpace(item.file)))
				{
					dst.Add(new NpcLayer
					{
						Path = item.path,
						File = item.file,
						Tint = ColorParse.Parse(item.tint ?? item.color, Color.white)
					});
				}
			}
		}

		private static NpcBehavior BuildBehavior(BehaviorJson b)
		{
			if (b == null)
			{
				return null;
			}
			NpcBehavior npcBehavior = new NpcBehavior();
			if (b.aggression.HasValue)
			{
				npcBehavior.Aggression = b.aggression.Value;
			}
			if (b.maxHealth.HasValue)
			{
				npcBehavior.MaxHealth = b.maxHealth.Value;
			}
			if (b.scale.HasValue)
			{
				npcBehavior.Scale = b.scale.Value;
			}
			if (!string.IsNullOrWhiteSpace(b.conversation))
			{
				npcBehavior.Conversation = b.conversation;
			}
			return npcBehavior;
		}

		private static NpcSpawn BuildSpawn(SpawnJson s, NpcPackManifest manifest, NpcEntry e)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (s == null)
			{
				return null;
			}
			NpcSpawn npcSpawn = new NpcSpawn
			{
				Region = (s.region ?? ""),
				RotationY = s.rotationY,
				Physical = s.physical,
				Auto = (s.auto ?? (manifest?.autoRegister == true))
			};
			if (s.x.HasValue && s.y.HasValue && s.z.HasValue)
			{
				npcSpawn.Position = new Vector3(s.x.Value, s.y.Value, s.z.Value);
			}
			return npcSpawn;
		}

		private static NpcContact BuildContact(ContactJson c)
		{
			if (c == null)
			{
				return null;
			}
			return new NpcContact
			{
				Visible = c.visible,
				MapMarker = c.mapMarker
			};
		}

		private static NpcRelationships BuildRelationships(RelationshipsJson r)
		{
			if (r == null)
			{
				return null;
			}
			NpcRelationships npcRelationships = new NpcRelationships
			{
				Delta = r.delta,
				Unlocked = r.unlocked,
				UnlockType = (string.IsNullOrWhiteSpace(r.unlockType) ? null : r.unlockType)
			};
			if (r.connections != null)
			{
				npcRelationships.Connections = new List<string>();
				foreach (string connection in r.connections)
				{
					if (!string.IsNullOrWhiteSpace(connection))
					{
						npcRelationships.Connections.Add(connection.Trim());
					}
				}
			}
			return npcRelationships;
		}

		private static NpcRange BuildRange(MinMaxJson m)
		{
			if (m == null || (!m.min.HasValue && !m.max.HasValue))
			{
				return null;
			}
			float num = m.min ?? m.max.GetValueOrDefault();
			float num2 = m.max ?? m.min.GetValueOrDefault();
			if (num2 < num)
			{
				float num3 = num2;
				num2 = num;
				num = num3;
			}
			return new NpcRange
			{
				Min = num,
				Max = num2
			};
		}

		private static NpcCustomer BuildCustomer(string packName, string npcName, CustomerJson c)
		{
			if (c == null)
			{
				return null;
			}
			NpcCustomer npcCustomer = new NpcCustomer
			{
				Spending = BuildRange(c.spending),
				OrdersPerWeek = BuildRange(c.ordersPerWeek),
				PreferredOrderDay = c.preferredOrderDay,
				Standards = c.standards,
				AllowDirectApproach = c.allowDirectApproach,
				GuaranteeFirstSample = c.guaranteeFirstSample,
				MutualRelationRequirement = BuildRange(c.mutualRelationRequirement),
				CallPoliceChance = c.callPoliceChance,
				DependenceBase = c.dependence?.@base,
				DependenceMultiplier = c.dependence?.multiplier
			};
			if (!string.IsNullOrWhiteSpace(c.orderTime))
			{
				if (Parse.TryParseTime(c.orderTime, out var hhmm))
				{
					npcCustomer.OrderTime = hhmm;
				}
				else
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning($"Pack '{packName}': NPC '{npcName}' customer.orderTime '{c.orderTime}' is not a valid time - ignored.");
					}
				}
			}
			if (c.affinities != null && c.affinities.Count > 0)
			{
				npcCustomer.Affinities = new Dictionary<string, float>(c.affinities);
			}
			if (c.preferredProperties != null)
			{
				npcCustomer.PreferredProperties = new List<string>();
				foreach (string preferredProperty in c.preferredProperties)
				{
					if (!string.IsNullOrWhiteSpace(preferredProperty))
					{
						npcCustomer.PreferredProperties.Add(preferredProperty.Trim());
					}
				}
			}
			return npcCustomer;
		}

		private static NpcDealer BuildDealer(DealerJson d)
		{
			if (d == null)
			{
				return null;
			}
			return new NpcDealer
			{
				Type = d.type,
				Cut = d.cut,
				SigningFee = d.signingFee,
				Home = d.home,
				CompletedDealsVariable = d.completedDealsVariable,
				AllowInsufficientQuality = d.allowInsufficientQuality,
				AllowExcessQuality = d.allowExcessQuality
			};
		}

		private static NpcInventory BuildInventory(string packName, string npcName, InventoryJson inv)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Invalid comparison between Unknown and I4
			if (inv == null)
			{
				return null;
			}
			NpcInventory npcInventory = new NpcInventory
			{
				Cash = BuildRange(inv.cash),
				ClearEachNight = inv.clearEachNight
			};
			if (inv.items != null)
			{
				npcInventory.Items = new List<NpcInventoryItem>();
				foreach (JToken item in inv.items)
				{
					if (item == null)
					{
						continue;
					}
					try
					{
						if ((int)item.Type == 8)
						{
							string text = Extensions.Value<string>((IEnumerable<JToken>)item);
							if (!string.IsNullOrWhiteSpace(text))
							{
								npcInventory.Items.Add(new NpcInventoryItem
								{
									Id = text.Trim()
								});