Decompiled source of Personnel v1.0.0

Personnel.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Il2CppScheduleOne.AvatarFramework;
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.Util;
using S1API.Entities;
using S1API.Rendering;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "Personnel", "1.0.0", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-Personnel")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: MelonOptionalDependencies(new string[] { "ModManager&PhoneApp" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Personnel")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+7b88287a9d7bee54a28488151a34a6d25416a250")]
[assembly: AssemblyProduct("Personnel")]
[assembly: AssemblyTitle("Personnel")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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 Personnel
{
	public static class API
	{
		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;
			}
			avatar.LoadAvatarSettings(val);
			return true;
		}

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

		public static void ConfigureFromDef(NPCPrefabBuilder builder, NpcDef def)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (builder != null && def != null)
			{
				SplitName(def.DisplayName, out var first, out var last);
				builder.WithIdentity(def.Id, first, last);
				builder.WithAppearanceDefaults((Action<AvatarDefaultsBuilder>)delegate(AvatarDefaultsBuilder ab)
				{
					AvatarSettingsFactory.ApplyToDefaults(ab, def);
				});
				NpcSpawn spawn = def.Spawn;
				if (spawn != null && spawn.Position.HasValue)
				{
					builder.WithSpawnPosition(def.Spawn.Position.Value, Quaternion.identity);
				}
			}
		}

		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 value = NpcRegistry.LoadPacks();
			Log.Msg($"Personnel {((MelonBase)this).Info.Version} - {value} NPC def(s) from packs ({NpcRegistry.AllDefs.Count} total).");
			Log.Msg("Drop packs in: " + PackLoader.PacksRoot);
		}
	}
	public abstract class PersonnelNpc : NPC
	{
		protected abstract string DefId { get; }

		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("[Personnel] PersonnelNpc: no definition '" + DefId + "' found - is the pack installed?");
			}
		}
	}
}
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;
		}
	}
}
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 NpcBehavior Behavior;

		public NpcSpawn Spawn;

		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 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 string Region = "";
	}
}
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}\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}\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 List<NpcEntry> npcs;
	}
	public sealed class NpcEntry
	{
		public string id;

		public string name;

		public AppearanceJson appearance;

		public BehaviorJson behavior;

		public SpawnJson spawn;

		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 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 string region;
	}
	internal static class PackLoader
	{
		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);
			foreach (string text in directories)
			{
				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;
					}
					int num = 0;
					List<string> list2 = new List<string>();
					foreach (NpcEntry npc in npcPackManifest.npcs)
					{
						NpcDef npcDef = ToDef(name, text, npc);
						if (npcDef != null)
						{
							list.Add(npcDef);
							num++;
							list2.Add(npcDef.Id);
						}
					}
					Instance log3 = Core.Log;
					if (log3 != null)
					{
						log3.Msg($"Pack '{name}' ({npcPackManifest.name ?? "unnamed"}): {num} NPC(s) [{string.Join(", ", list2)}].");
					}
				}
				catch (Exception ex2)
				{
					Instance log4 = Core.Log;
					if (log4 != null)
					{
						log4.Warning("Pack '" + name + "': failed to read manifest.json - " + ex2.Message);
					}
				}
			}
			return list;
		}

		private static NpcDef ToDef(string packName, string packDir, 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;
			}
			return new NpcDef
			{
				Id = Ids.Make(packName, text),
				DisplayName = text,
				Source = packName,
				PackDir = packDir,
				Appearance = BuildAppearance(e.appearance),
				Behavior = BuildBehavior(e.behavior),
				Spawn = BuildSpawn(e.spawn),
				Extensions = BuildExtensions(e.extensions)
			};
		}

		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)
			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.LeftEyeLidColor);
			npcAppearance.RightEyeLidColor = ColorParse.Parse(a.rightEyeLidColor, npcAppearance.RightEyeLidColor);
			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);
			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)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (s == null)
			{
				return null;
			}
			NpcSpawn npcSpawn = new NpcSpawn
			{
				Region = (s.region ?? "")
			};
			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 IReadOnlyDictionary<string, string> BuildExtensions(JObject ext)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (ext == null)
			{
				return dictionary;
			}
			foreach (JProperty item in ext.Properties())
			{
				try
				{
					dictionary[item.Name] = item.Value.ToString((Formatting)0);
				}
				catch
				{
				}
			}
			return dictionary;
		}

		private static void WriteReadmeIfMissing(string root)
		{
			string path = Path.Combine(root, "README.txt");
			if (!File.Exists(path))
			{
				File.WriteAllText(path, "Personnel - custom NPC packs\n============================\n\nDrop one folder per pack in this directory. Each pack needs a manifest.json and (optionally) the PNG files\nits custom layers reference. Build packs visually with the \"Personify\" editor (Side Hustle gamemode).\n\nFolder layout:\n  Packs/\n    MyPack/\n      manifest.json\n      grin.png            (optional custom layer texture)\n\nmanifest.json:\n{\n  \"name\": \"My Pack\",\n  \"author\": \"you\",\n  \"npcs\": [\n    {\n      \"id\": \"faceling_pale\",\n      \"name\": \"Pale Faceling\",\n      \"appearance\": {\n        \"gender\": 0.5, \"height\": 1.0, \"weight\": 0.4, \"skinColor\": \"#8899AA\",\n        \"hairPath\": \"\", \"hairColor\": \"#000000\",\n        \"eyeBallTint\": \"#FFFFFF\", \"pupilDilation\": 1,\n        \"faceLayers\": [ { \"file\": \"grin.png\", \"kind\": \"face\", \"tint\": \"#FFFFFF\" } ],\n        \"bodyLayers\": [ { \"path\": \"Avatar/Layers/Body/...\", \"tint\": \"#334455\" } ],\n        \"accessories\": [ ]\n      },\n      \"extensions\": {\n        \"backrooms\": { \"archetype\": \"faceling\", \"tierMin\": 1, \"tierMax\": 5,\n                         \"biomes\": [\"L0\",\"L1\"], \"weight\": 14, \"maxAlive\": 3, \"hostile\": false }\n      }\n    }\n  ]\n}\n\nLayers:  use \"path\" to reference an existing in-game layer, OR \"file\" (a PNG next to manifest.json)\n         with \"kind\": face|body for a custom layer. Custom PNGs must match the game's body/face UV.\nextensions: free-form per-consumer data. The Backrooms mod reads the \"backrooms\" block; other mods\n         ignore it.\n");
			}
		}
	}
}
namespace Personnel.Config
{
	internal static class Preferences
	{
		private const string CategoryId = "Personnel_01_Main";

		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _loadExamplePack;

		internal static bool LoadExamplePack => _loadExamplePack?.Value ?? false;

		internal static void Initialize()
		{
			if (_category == null)
			{
				_category = MelonPreferences.CreateCategory("Personnel_01_Main", "Personnel (Custom NPCs)");
				_loadExamplePack = _category.CreateEntry<bool>("LoadExamplePack", false, "Load example NPC pack", "OFF by default. When ON, Personnel drops a small example pack into UserData/Personnel/Packs/Examples on startup (if not already there) so you get a working manifest template to copy for your own pack. Requires a game restart.", false, false, (ValueValidator)null, (string)null);
			}
		}
	}
}
namespace Personnel.Appearance
{
	public static class AvatarSettingsFactory
	{
		public static AvatarSettings BuildAvatarSettings(NpcDef def)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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)
			if (def == null)
			{
				return null;
			}
			NpcAppearance npcAppearance = def.Appearance ?? new NpcAppearance();
			AvatarSettings obj = ScriptableObject.CreateInstance<AvatarSettings>();
			((Object)obj).hideFlags = (HideFlags)32;
			obj.Gender = npcAppearance.Gender;
			obj.Height = npcAppearance.Height;
			obj.Weight = npcAppearance.Weight;
			obj.SkinColor = npcAppearance.SkinColor;
			obj.HairPath = npcAppearance.HairPath ?? string.Empty;
			obj.HairColor = npcAppearance.HairColor;
			obj.EyebrowScale = npcAppearance.EyebrowScale;
			obj.EyebrowThickness = npcAppearance.EyebrowThickness;
			obj.EyebrowRestingHeight = npcAppearance.EyebrowRestingHeight;
			obj.EyebrowRestingAngle = npcAppearance.EyebrowRestingAngle;
			obj.LeftEyeLidColor = npcAppearance.LeftEyeLidColor;
			obj.RightEyeLidColor = npcAppearance.RightEyeLidColor;
			obj.LeftEyeRestingState = new EyeLidConfiguration
			{
				topLidOpen = npcAppearance.LeftEyeTop,
				bottomLidOpen = npcAppearance.LeftEyeBottom
			};
			obj.RightEyeRestingState = new EyeLidConfiguration
			{
				topLidOpen = npcAppearance.RightEyeTop,
				bottomLidOpen = npcAppearance.RightEyeBottom
			};
			obj.EyeballMaterialIdentifier = (string.IsNullOrEmpty(npcAppearance.EyeballMaterial) ? "Default" : npcAppearance.EyeballMaterial);
			obj.EyeBallTint = npcAppearance.EyeBallTint;
			obj.PupilDilation = npcAppearance.PupilDilation;
			List<LayerSetting> val = new List<LayerSetting>();
			List<LayerSetting> val2 = new List<LayerSetting>();
			List<AccessorySetting> val3 = new List<AccessorySetting>();
			AddLayers(def, npcAppearance.FaceLayers, val, face: true);
			AddLayers(def, npcAppearance.BodyLayers, val2, face: false);
			AddAccessories(npcAppearance.Accessories, val3);
			obj.FaceLayerSettings = val;
			obj.BodyLayerSettings = val2;
			obj.AccessorySettings = val3;
			return obj;
		}

		private static void AddLayers(NpcDef def, List<NpcLayer> layers, List<LayerSetting> dst, bool face)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (layers == null)
			{
				return;
			}
			foreach (NpcLayer layer in layers)
			{
				if (layer != null)
				{
					string text = ResolvePath(def, layer, face);
					if (!string.IsNullOrEmpty(text))
					{
						LayerSetting val = new LayerSetting();
						val.layerPath = text;
						val.layerTint = layer.Tint;
						dst.Add(val);
					}
				}
			}
		}

		private static void AddAccessories(List<NpcLayer> layers, List<AccessorySetting> dst)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (layers == null)
			{
				return;
			}
			foreach (NpcLayer layer in layers)
			{
				if (layer != null && !string.IsNullOrWhiteSpace(layer.Path))
				{
					AccessorySetting val = new AccessorySetting();
					val.path = layer.Path;
					val.color = layer.Tint;
					dst.Add(val);
				}
			}
		}

		public static void ApplyToDefaults(AvatarDefaultsBuilder ab, NpcDef def)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			if (ab == null || def == null)
			{
				return;
			}
			NpcAppearance npcAppearance = def.Appearance ?? new NpcAppearance();
			ab.Gender = npcAppearance.Gender;
			ab.Height = npcAppearance.Height;
			ab.Weight = npcAppearance.Weight;
			ab.SkinColor = Color32.op_Implicit(npcAppearance.SkinColor);
			ab.HairPath = npcAppearance.HairPath ?? string.Empty;
			ab.HairColor = npcAppearance.HairColor;
			ab.EyebrowScale = npcAppearance.EyebrowScale;
			ab.EyebrowThickness = npcAppearance.EyebrowThickness;
			ab.EyebrowRestingHeight = npcAppearance.EyebrowRestingHeight;
			ab.EyebrowRestingAngle = npcAppearance.EyebrowRestingAngle;
			ab.LeftEyeLidColor = npcAppearance.LeftEyeLidColor;
			ab.RightEyeLidColor = npcAppearance.RightEyeLidColor;
			ab.LeftEye = (npcAppearance.LeftEyeTop, npcAppearance.LeftEyeBottom);
			ab.RightEye = (npcAppearance.RightEyeTop, npcAppearance.RightEyeBottom);
			ab.EyeballMaterialIdentifier = (string.IsNullOrEmpty(npcAppearance.EyeballMaterial) ? "Default" : npcAppearance.EyeballMaterial);
			ab.EyeBallTint = npcAppearance.EyeBallTint;
			ab.PupilDilation = npcAppearance.PupilDilation;
			foreach (NpcLayer faceLayer in npcAppearance.FaceLayers)
			{
				string text = ResolvePath(def, faceLayer, face: true);
				if (!string.IsNullOrEmpty(text))
				{
					ab.WithFaceLayer(text, faceLayer.Tint);
				}
			}
			foreach (NpcLayer bodyLayer in npcAppearance.BodyLayers)
			{
				string text2 = ResolvePath(def, bodyLayer, face: false);
				if (!string.IsNullOrEmpty(text2))
				{
					ab.WithBodyLayer(text2, bodyLayer.Tint);
				}
			}
			foreach (NpcLayer accessory in npcAppearance.Accessories)
			{
				if (accessory != null && !string.IsNullOrWhiteSpace(accessory.Path))
				{
					ab.WithAccessoryLayer(accessory.Path, accessory.Tint);
				}
			}
		}

		internal static string ResolvePath(NpcDef def, NpcLayer l, bool face)
		{
			if ((Object)(object)l.Texture != (Object)null)
			{
				return CustomLayerRegistry.EnsureLayer(def.Source, def.Id, def.PackDir, null, l.Texture, face);
			}
			if (!string.IsNullOrWhiteSpace(l.File))
			{
				return CustomLayerRegistry.EnsureLayer(def.Source, def.Id, def.PackDir, l.File, null, face);
			}
			return l.Path;
		}
	}
}