Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CreatureManager v1.0.7
CreatureManager.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CreatureManager; using GUIFramework; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CreatureManager")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("CreatureManager")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.7")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.7.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 LocalizationManager { internal static class Localizer { private static BaseUnityPlugin? _plugin; private static readonly List<string> fileExtensions = new List<string>(2) { ".json", ".yml" }; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable<TypeInfo> source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } internal static void Load(Harmony harmony) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0089: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown _ = plugin; harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", new Type[1] { typeof(string) }, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "BeforeLanguageSetup", (Type[])null, (Type[])null), 800, (string[])null, (string[])null, (bool?)null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeLoadLocalization", (Type[])null, (Type[])null), 0, (string[])null, (string[])null, (bool?)null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null), 0, (string[])null, (string[])null, (bool?)null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } internal static void Unload() { _plugin = null; } private static void LoadLocalizationLater() { Localization instance = Localization.instance; if (instance != null) { SafeLoadLocalization(instance, instance.GetSelectedLanguage()); } } [HarmonyPriority(800)] private static void BeforeLanguageSetup(Localization __instance) { try { CreatureServerLocalization.BeforeLanguageSetup(__instance); } catch (Exception ex) { Debug.LogError((object)("Failed to prepare CreatureManager server localization before a language change. " + ex.Message)); } } [HarmonyPriority(0)] private static void SafeLoadLocalization(Localization __instance, string language) { try { LoadLocalization(__instance, language); } catch (Exception ex) { Debug.LogError((object)("Failed to load " + plugin.Info.Metadata.Name + " localization for '" + language + "'. Vanilla localization will remain active. " + ex.Message)); } try { CreatureServerLocalization.ApplyCurrentLocalization(__instance, language); } catch (Exception ex2) { Debug.LogError((object)("Failed to apply CreatureManager server localization for '" + language + "'. " + ex2.Message)); } } private static void LoadLocalization(Localization __instance, string language) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>()) { dictionary2[item2.Key] = item2.Value; } } foreach (KeyValuePair<string, string> item3 in dictionary2) { __instance.AddWord(item3.Key, item3.Value); } } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } } namespace CreatureManager { internal static class CreatureConsoleCommands { private enum SpawnAutocompleteField { None, Prefab, Modifier } [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__WriteReference; public static ConsoleOptionsFetcher <1>__GetReferenceDomainOptions; public static ConsoleEvent <2>__WriteFull; public static ConsoleOptionsFetcher <3>__GetFullDomainOptions; public static ConsoleEvent <4>__Spawn; public static ConsoleOptionsFetcher <5>__GetSpawnPrefabOptions; public static ConsoleEvent <6>__Karma; } private const string SpawnCommandName = "cm:spawn"; private const int MaximumCommandSpawnLevel = 100; private static readonly List<string> ReferenceDomainOptions = new List<string> { "creature", "ai", "attack", "loadout", "projectile", "texture", "levelvisual" }; private static readonly List<string> FullDomainOptions = new List<string> { "creature" }; private static readonly List<string> EmptyAutocompleteOptions = new List<string>(); private static readonly char[] ArgumentSeparators = new char[2] { ' ', '\t' }; private static ConsoleCommand? SpawnCommand; private static bool Registered; internal static void Register() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00d0: 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) //IL_00db: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_012d: Expected O, but got Unknown if (!Registered) { Registered = true; object obj = <>O.<0>__WriteReference; if (obj == null) { ConsoleEvent val = WriteReference; <>O.<0>__WriteReference = val; obj = (object)val; } object obj2 = <>O.<1>__GetReferenceDomainOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetReferenceDomainOptions; <>O.<1>__GetReferenceDomainOptions = val2; obj2 = (object)val2; } new ConsoleCommand("cm:reference", "Write generated CreatureManager reference files. Usage: cm:reference creature|ai|attack|loadout|projectile|texture|levelvisual", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); object obj3 = <>O.<2>__WriteFull; if (obj3 == null) { ConsoleEvent val3 = WriteFull; <>O.<2>__WriteFull = val3; obj3 = (object)val3; } object obj4 = <>O.<3>__GetFullDomainOptions; if (obj4 == null) { ConsoleOptionsFetcher val4 = GetFullDomainOptions; <>O.<3>__GetFullDomainOptions = val4; obj4 = (object)val4; } new ConsoleCommand("cm:full", "Write generated CreatureManager full scaffold YAML. Usage: cm:full creature", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, false); string text = $"Spawn a creature with optional modifiers and an exact trailing level from 1 to {100}. Usage: cm:spawn <prefab> [modifier1,modifier2,modifier3,modifier4] [level]"; object obj5 = <>O.<4>__Spawn; if (obj5 == null) { ConsoleEvent val5 = Spawn; <>O.<4>__Spawn = val5; obj5 = (object)val5; } object obj6 = <>O.<5>__GetSpawnPrefabOptions; if (obj6 == null) { ConsoleOptionsFetcher val6 = GetSpawnPrefabOptions; <>O.<5>__GetSpawnPrefabOptions = val6; obj6 = (object)val6; } SpawnCommand = new ConsoleCommand("cm:spawn", text, (ConsoleEvent)obj5, true, true, true, false, false, (ConsoleOptionsFetcher)obj6, false, true, true); object obj7 = <>O.<6>__Karma; if (obj7 == null) { ConsoleEvent val7 = Karma; <>O.<6>__Karma = val7; obj7 = (object)val7; } new ConsoleCommand("cm:karma", "Show or set current 3x3 zone-neighborhood Karma. Usage: cm:karma [value]", (ConsoleEvent)obj7, true, true, true, false, false, (ConsoleOptionsFetcher)null, false, true, true); } } private static List<string> GetReferenceDomainOptions() { return ReferenceDomainOptions; } private static List<string> GetFullDomainOptions() { return FullDomainOptions; } private static List<string> GetSpawnPrefabOptions() { List<GameObject> creaturePrefabs = CreaturePrefabRegistry.GetCreaturePrefabs(); List<string> list = new List<string>(creaturePrefabs.Count); foreach (GameObject item in creaturePrefabs) { list.Add(((Object)item).name); } return list; } internal static void InvalidateSpawnAutocompleteOptions() { if (SpawnCommand != null) { SpawnCommand.m_tabOptions = null; } } internal static void AdjustSpawnAutocomplete(Terminal terminal, ref string word, ref List<string> options) { if (!TryGetSpawnAutocompleteContext(terminal, out SpawnAutocompleteField field, out string currentToken)) { return; } switch (field) { case SpawnAutocompleteField.Prefab: word = currentToken; if (options == null || options.Count == 0) { options = GetSpawnPrefabOptions(); } break; case SpawnAutocompleteField.Modifier: options = GetModifierAutocompleteOptions(currentToken, out word); break; default: word = currentToken; options = EmptyAutocompleteOptions; break; } } private static bool TryGetSpawnAutocompleteContext(Terminal terminal, out SpawnAutocompleteField field, out string currentToken) { field = SpawnAutocompleteField.None; currentToken = ""; if ((Object)(object)terminal.m_input == (Object)null) { return false; } GuiInputField input = terminal.m_input; string text = ((TMP_InputField)input).text ?? ""; string text2 = text[..Mathf.Clamp(((TMP_InputField)input).caretPosition, 0, text.Length)].TrimStart(Array.Empty<char>()); if (terminal.m_tabPrefix != 0 && text2.Length > 0 && text2[0] == terminal.m_tabPrefix) { text2 = text2.Substring(1); } if (!text2.StartsWith("cm:spawn", StringComparison.OrdinalIgnoreCase) || text2.Length <= "cm:spawn".Length || !char.IsWhiteSpace(text2["cm:spawn".Length])) { return false; } string text3 = text2.Substring("cm:spawn".Length).TrimStart(ArgumentSeparators); if (text3.Length == 0) { field = SpawnAutocompleteField.Prefab; return true; } int num = IndexOfWhitespace(text3); if (num < 0) { field = SpawnAutocompleteField.Prefab; currentToken = text3; return true; } string text4 = text3.Substring(num).TrimStart(ArgumentSeparators); if (text4.Length == 0) { field = SpawnAutocompleteField.Modifier; return true; } int num2 = LastNonWhitespaceIndex(text4); if (num2 < text4.Length - 1) { if (text4[num2] == ',') { field = SpawnAutocompleteField.Modifier; currentToken = text4; } return true; } int num3 = num2; while (num3 >= 0 && !char.IsWhiteSpace(text4[num3])) { num3--; } string s = text4.Substring(num3 + 1); int result; if (num3 >= 0) { int num4 = LastNonWhitespaceIndex(text4, num3); if (num4 < 0 || text4[num4] != ',') { return true; } } else if (int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { return true; } field = SpawnAutocompleteField.Modifier; currentToken = text4; return true; } private static int IndexOfWhitespace(string value) { for (int i = 0; i < value.Length; i++) { if (char.IsWhiteSpace(value[i])) { return i; } } return -1; } private static int LastNonWhitespaceIndex(string value, int exclusiveEnd = -1) { int num = ((exclusiveEnd < 0) ? (value.Length - 1) : Mathf.Min(exclusiveEnd - 1, value.Length - 1)); while (num >= 0 && char.IsWhiteSpace(value[num])) { num--; } return num; } private static List<string> GetModifierAutocompleteOptions(string modifierToken, out string currentModifier) { string[] array = modifierToken.Split(new char[1] { ',' }, StringSplitOptions.None); currentModifier = array[^1].Trim(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < array.Length - 1; i++) { string text = array[i].Trim(); if (text.Length > 0) { hashSet.Add(text); } } if (hashSet.Count >= 4) { return EmptyAutocompleteOptions; } IReadOnlyList<string> knownModifierKeys = CreatureModifierManager.GetKnownModifierKeys(); List<string> list = new List<string>(knownModifierKeys.Count - hashSet.Count); foreach (string item in knownModifierKeys) { if (!hashSet.Contains(item)) { list.Add(item); } } return list; } private static void WriteReference(ConsoleEventArgs args) { CreatureAssetOwnerCatalog.RefreshMappings(); string scope = GetScope(args); if (!IsKnownScope(scope, args, "cm:reference", includeTexture: true)) { return; } if (scope switch { "ai" => CreatureDomainManager.TryWriteAiReferenceConfigurationFile(out string path, out string error), "attack" => CreatureDomainManager.TryWriteAttackReferenceConfigurationFile(out path, out error), "loadout" => CreatureDomainManager.TryWriteCreatureLoadoutReferenceConfigurationFile(out path, out error), "projectile" => CreatureDomainManager.TryWriteProjectileReferenceConfigurationFile(out path, out error), "texture" => CreatureDomainManager.TryWriteTextureReferenceConfigurationFile(out path, out error), "levelvisual" => CreatureDomainManager.TryWriteLevelVisualReferenceConfigurationFile(out path, out error), _ => CreatureDomainManager.TryWriteReferenceConfigurationFile(out path, out error), }) { Terminal context = args.Context; if (context != null) { context.AddString("Wrote " + NormalizeScope(scope) + " reference to " + path); } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } private static void WriteFull(ConsoleEventArgs args) { CreatureAssetOwnerCatalog.RefreshMappings(); string scope = GetScope(args); string path; string error; if (scope != "creature") { Terminal context = args.Context; if (context != null) { context.AddString("Syntax: cm:full creature"); } } else if (CreatureDomainManager.TryWriteFullScaffoldConfigurationFile(out path, out error)) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Wrote " + NormalizeScope(scope) + " full scaffold to " + path); } } else { Terminal context3 = args.Context; if (context3 != null) { context3.AddString(error); } } } private static void Karma(ConsoleEventArgs args) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!RequireAuthoritativeAdmin(args)) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Terminal context = args.Context; if (context != null) { context.AddString("No local player."); } return; } ExecuteKarma(args, ((Component)localPlayer).transform.position, delegate(string message) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(message); } }); } private static void ExecuteKarma(ConsoleEventArgs args, Vector3 playerPosition, Action<string> reply) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (args.Length == 1) { reply(CreatureKarmaManager.GetDebugLine(playerPosition)); return; } if (args.Length != 2 || !float.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result) || result < 0f) { reply("Syntax: cm:karma [non-negative value]"); return; } CreatureKarmaManager.SetDebugKarma(playerPosition, result); reply(CreatureKarmaManager.GetDebugLine(playerPosition)); } private static void Spawn(ConsoleEventArgs args) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!RequireAuthoritativeAdmin(args)) { return; } if (!CreatureLevelManager.IsLevelSystemEnabled()) { Terminal context = args.Context; if (context != null) { context.AddString("CreatureManager level system is disabled."); } return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString("No local player."); } return; } ExecuteSpawn(args, ((Component)localPlayer).transform.position, ((Component)localPlayer).transform.rotation, delegate(string message) { Terminal context3 = args.Context; if (context3 != null) { context3.AddString(message); } }); } private static void ExecuteSpawn(ConsoleEventArgs args, Vector3 playerPosition, Quaternion playerRotation, Action<string> reply) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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_00ad: Unknown result type (might be due to invalid IL or missing references) if (!TryParseSpawnArguments(args, out string prefabName, out int level, out List<string> modifiers, out string error)) { reply(error); return; } GameObject prefab = CreaturePrefabRegistry.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { reply("Creature prefab '" + prefabName + "' was not found."); return; } if ((Object)(object)prefab.GetComponent<Character>() == (Object)null || CreaturePrefabRegistry.IsPlayerPrefab(prefab)) { reply("Prefab '" + prefabName + "' is not a supported non-player creature."); return; } Vector3 commandSpawnPosition = GetCommandSpawnPosition(playerPosition, playerRotation * Vector3.forward); Quaternion val = Quaternion.Euler(0f, ((Quaternion)(ref playerRotation)).eulerAngles.y, 0f); GameObject val2 = null; CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.Managed); try { val2 = Object.Instantiate<GameObject>(prefab, commandSpawnPosition, val); } catch (Exception ex) { error = "Failed to spawn '" + prefabName + "': " + ex.Message; } finally { CreatureManagerSpawnLifecycle.EndSourceContext(); } if ((Object)(object)val2 == (Object)null) { reply((error.Length > 0) ? error : ("Failed to spawn '" + prefabName + "'.")); return; } Character component = val2.GetComponent<Character>(); if ((Object)(object)component == (Object)null || !CreatureModifierManager.TryApplyForcedModifiers(component, modifiers, out error) || !CreatureLevelManager.TryApplyForcedLevel(component, level, out error)) { CleanupFailedSpawn(val2); reply((error.Length > 0) ? error : ("Failed to initialize '" + prefabName + "'.")); } else { CreatureManagerCharacterLifecycle.ApplyLevelAndModifiers(component); string arg = ((modifiers.Count > 0) ? string.Join(", ", modifiers) : "none"); reply($"Spawned {((Object)prefab).name} at level {level} with modifiers: {arg}."); } } private static void CleanupFailedSpawn(GameObject spawned) { try { ZNetView component = spawned.GetComponent<ZNetView>(); if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { ZNetScene.instance.Destroy(spawned); return; } } catch { } Object.Destroy((Object)(object)spawned); } internal static bool TryHandleAuthenticatedRemoteAdminCommand(ZNet znet, ZRpc rpc, string command) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) string remoteCommandName = GetRemoteCommandName(command); if (!string.Equals(remoteCommandName, "cm:spawn", StringComparison.OrdinalIgnoreCase) && !string.Equals(remoteCommandName, "cm:karma", StringComparison.OrdinalIgnoreCase)) { return false; } if (!znet.IsServer() || rpc == null) { return false; } Action<string> action = delegate(string message) { try { znet.RemotePrint(rpc, message); } catch { } }; try { ZNetPeer peer = znet.GetPeer(rpc); if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { action("Could not resolve the remote admin's active player."); return true; } ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); GameObject val = ((zDO != null) ? ZNetScene.instance.GetPrefab(zDO.GetPrefab()) : null); if (zDO == null || zDO.GetOwner() != peer.m_uid || (Object)(object)val == (Object)null || (Object)(object)val.GetComponent<Player>() == (Object)null) { action("Could not validate the remote admin's active player."); return true; } Vector3 position = zDO.GetPosition(); Quaternion val2 = zDO.GetRotation(); if (!IsFinite(position)) { action("Could not validate the remote admin's player position."); return true; } if (!IsFinite(val2)) { val2 = Quaternion.identity; } ConsoleEventArgs args = new ConsoleEventArgs(command, (Terminal)(object)Console.instance); if (string.Equals(remoteCommandName, "cm:spawn", StringComparison.OrdinalIgnoreCase)) { if (!CreatureLevelManager.IsLevelSystemEnabled()) { action("CreatureManager level system is disabled."); return true; } ExecuteSpawn(args, position, val2, action); } else { ExecuteKarma(args, position, action); } ISocket socket = rpc.GetSocket(); string text = ((socket != null) ? socket.GetHostName() : null) ?? ""; CreatureManagerPlugin.Log.LogInfo((object)("Remote admin '" + text + "' executed CreatureManager command '" + remoteCommandName + "'.")); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to execute remote CreatureManager command '" + remoteCommandName + "': " + ex.Message)); action("CreatureManager could not execute the remote command. Check the server log."); } return true; } private static string GetRemoteCommandName(string command) { if (string.IsNullOrWhiteSpace(command)) { return ""; } int num = command.IndexOfAny(ArgumentSeparators); return ((num >= 0) ? command.Substring(0, num) : command).Trim(); } private static bool RequireAuthoritativeAdmin(ConsoleEventArgs args) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZNet.instance.LocalPlayerIsAdminOrHost()) { return true; } Terminal context = args.Context; if (context != null) { context.AddString("This command must be run locally by the server host."); } return false; } private static bool TryParseSpawnArguments(ConsoleEventArgs args, out string prefabName, out int level, out List<string> modifiers, out string error) { prefabName = ((args.Length >= 2) ? (args[1] ?? "").Trim() : ""); level = 1; modifiers = new List<string>(); error = ""; if (prefabName.Length == 0) { error = "Syntax: cm:spawn <prefab> [modifier1,modifier2,modifier3,modifier4] [level]"; return false; } int num = args.Length; if (args.Length >= 3 && int.TryParse(args[args.Length - 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { if (result < 1 || result > 100) { error = $"level must be an integer from 1 to {100}."; return false; } level = result; num--; } if (num > 2) { List<string> list = new List<string>(num - 2); for (int i = 2; i < num; i++) { list.Add(args[i] ?? ""); } string text = string.Join(" ", list).Trim(); string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (text.Length == 0 || array.Length == 0 || !CreatureModifierManager.TryNormalizeForcedModifierKeys(array, out modifiers, out error)) { error = ((error.Length > 0) ? error : "At least one modifier must be specified."); return false; } } return true; } private static Vector3 GetCommandSpawnPosition(Vector3 playerPosition, Vector3 playerForward) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0053: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) Vector3 val = playerForward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = playerPosition + val * 3f; if (val2.y >= 4000f) { return val2 + Vector3.up * 0.5f; } float num = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val2 + Vector3.up * 100f, ref num)) { val2.y = num + 0.5f; } else if (WorldGenerator.instance != null) { val2.y = WorldGenerator.instance.GetHeight(val2.x, val2.z) + 0.5f; } else { val2.y += 0.5f; } return val2; } private static bool IsFinite(Vector3 value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsFinite(Quaternion value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z)) { return IsFinite(value.w); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static string GetScope(ConsoleEventArgs args) { if (args.Length < 2) { return ""; } return (args[1] ?? "").Trim().ToLowerInvariant(); } private static bool IsKnownScope(string scope, ConsoleEventArgs args, string command, bool includeTexture) { bool flag; switch (scope) { case "creature": case "ai": case "attack": flag = true; break; default: flag = false; break; } if (flag) { return true; } flag = includeTexture; if (flag) { bool flag2; switch (scope) { case "loadout": case "projectile": case "texture": case "levelvisual": flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { return true; } Terminal context = args.Context; if (context != null) { context.AddString(includeTexture ? ("Syntax: " + command + " creature|ai|attack|loadout|projectile|texture|levelvisual") : ("Syntax: " + command + " creature|ai|attack")); } return false; } private static string NormalizeScope(string scope) { return scope switch { "ai" => "ai", "attack" => "attack", "loadout" => "loadout", "projectile" => "projectile", "texture" => "texture", "levelvisual" => "levelvisual", _ => "creature", }; } } internal static class CreatureCompendiumManager { private readonly struct CompendiumModifierEntry { internal readonly string ModifierKey; internal readonly string GroupHeading; internal readonly string Name; internal readonly string Description; internal readonly Sprite Sprite; internal CompendiumModifierEntry(string modifierKey, string groupHeading, string name, string description, Sprite sprite) { ModifierKey = modifierKey; GroupHeading = groupHeading; Name = name; Description = description; Sprite = sprite; } } private const string PageTopic = "CreatureManager"; private const string BodyIconPrefix = "CreatureManager_CompendiumModifierIcon_"; private const string IconLinkPrefix = "cm-modifier-icon-"; private const char IconPlaceholder = ''; private const float IconSize = 18f; private const float IconTextGap = 5f; internal static void AddModifierEntries(TextsDialog dialog) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown if ((Object)(object)dialog == (Object)null || dialog.m_texts == null) { return; } dialog.m_texts.RemoveAll((TextInfo text) => IsCreatureManagerPage(text?.m_topic)); ConfigEntry<CreatureManagerPlugin.Toggle> enableLevelSystem = CreatureManagerPlugin.EnableLevelSystem; if (enableLevelSystem != null && enableLevelSystem.Value == CreatureManagerPlugin.Toggle.Off) { return; } List<CompendiumModifierEntry> list = BuildEntries(); if (list.Count != 0) { TextInfo item = new TextInfo("CreatureManager", BuildPageText(list)); dialog.m_texts.Add(item); dialog.m_texts.Sort((TextInfo a, TextInfo b) => a.m_topic.CompareTo(b.m_topic)); } } internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dialog == (Object)null || info == null || (Object)(object)dialog.m_textArea == (Object)null) { return; } ClearPageContentIcons(dialog); if (!IsCreatureManagerPage(info.m_topic)) { return; } List<CompendiumModifierEntry> list = BuildEntries(); if (list.Count == 0) { return; } Dictionary<string, CompendiumModifierEntry> dictionary = new Dictionary<string, CompendiumModifierEntry>(StringComparer.OrdinalIgnoreCase); foreach (CompendiumModifierEntry item in list) { dictionary[item.ModifierKey] = item; } TMP_Text textArea = dialog.m_textArea; Transform parent = textArea.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null) { return; } textArea.ForceMeshUpdate(false, false); LayoutRebuilder.ForceRebuildLayoutImmediate(val); textArea.ForceMeshUpdate(false, false); TMP_TextInfo textInfo = textArea.textInfo; for (int i = 0; i < textInfo.linkCount; i++) { TMP_LinkInfo val2 = textInfo.linkInfo[i]; string linkID = ((TMP_LinkInfo)(ref val2)).GetLinkID(); if (linkID.StartsWith("cm-modifier-icon-", StringComparison.Ordinal) && dictionary.TryGetValue(linkID.Substring("cm-modifier-icon-".Length), out var value) && val2.linkTextfirstCharacterIndex >= 0 && val2.linkTextfirstCharacterIndex < textInfo.characterCount) { TMP_CharacterInfo marker = textInfo.characterInfo[val2.linkTextfirstCharacterIndex]; AttachBodyIcon(val, textArea.rectTransform, marker, value.Sprite, value.ModifierKey); } } } private static List<CompendiumModifierEntry> BuildEntries() { List<CompendiumModifierEntry> list = new List<CompendiumModifierEntry>(); Dictionary<string, ModifierDefinition> globalModifierDefinitions = CreatureLevelManager.GetGlobalModifierDefinitions(); if (globalModifierDefinitions.Count == 0) { return list; } foreach (string knownModifierKey in CreatureModifierManager.GetKnownModifierKeys()) { if (globalModifierDefinitions.TryGetValue(knownModifierKey, out var value) && CreatureModifierManager.TryGetModifierSprite(knownModifierKey, out Sprite sprite)) { list.Add(new CompendiumModifierEntry(knownModifierKey, CreatureModifierManager.GetModifierGroupHeading(knownModifierKey), CreatureModifierManager.GetModifierDisplayName(knownModifierKey), CreatureModifierManager.GetModifierCompendiumText(knownModifierKey, value), sprite)); } } return list; } private static string BuildPageText(List<CompendiumModifierEntry> entries) { StringBuilder stringBuilder = new StringBuilder(); string a = string.Empty; foreach (CompendiumModifierEntry entry in entries) { if (!string.Equals(a, entry.GroupHeading, StringComparison.Ordinal)) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append("<color=#FFD27A><b>").Append(entry.GroupHeading).Append("</b></color>\n\n"); a = entry.GroupHeading; } stringBuilder.Append("<link=\"").Append("cm-modifier-icon-").Append(entry.ModifierKey) .Append("\"><color=#00000000>") .Append('') .Append("</color></link> ") .Append(" ") .Append("<color=orange><b>") .Append(entry.Name) .Append("</b></color>") .Append('\n') .Append(" ") .Append(entry.Description) .Append("\n\n"); } return stringBuilder.ToString().TrimEnd(Array.Empty<char>()); } private static void AttachBodyIcon(RectTransform content, RectTransform textArea, TMP_CharacterInfo marker, Sprite sprite, string modifierKey) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_0112: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)content == (Object)null) && !((Object)(object)textArea == (Object)null) && !((Object)(object)sprite == (Object)null)) { GameObject val = new GameObject("CreatureManager_CompendiumModifierIcon_" + modifierKey, new Type[3] { typeof(RectTransform), typeof(Image), typeof(LayoutElement) }) { layer = ((Component)textArea).gameObject.layer }; RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)content, false); val2.anchorMin = content.pivot; val2.anchorMax = content.pivot; val2.pivot = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(18f, 18f); Vector3 val3 = (marker.bottomLeft + marker.topLeft) * 0.5f; val3.x += 14f; Vector3 val4 = ((Transform)textArea).TransformPoint(val3); Vector3 val5 = ((Transform)content).InverseTransformPoint(val4); val2.anchoredPosition = new Vector2(val5.x, val5.y); Image component = val.GetComponent<Image>(); component.sprite = sprite; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; val.GetComponent<LayoutElement>().ignoreLayout = true; } } private static void ClearPageContentIcons(TextsDialog dialog) { if (!((Object)(object)dialog?.m_textArea == (Object)null)) { ClearIconChildren(dialog.m_textArea.transform); if ((Object)(object)dialog.m_textArea.transform.parent != (Object)null) { ClearIconChildren(dialog.m_textArea.transform.parent); } } } private static void ClearIconChildren(Transform parent) { for (int num = parent.childCount - 1; num >= 0; num--) { Transform child = parent.GetChild(num); if (((Object)child).name.StartsWith("CreatureManager_CompendiumModifierIcon_", StringComparison.Ordinal)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } } private static bool IsCreatureManagerPage(string? topic) { return string.Equals(topic, "CreatureManager", StringComparison.Ordinal); } } internal sealed class CreatureDefinition { public string? Prefab { get; set; } public bool? Enabled { get; set; } public string? ClonedFrom { get; set; } public string? Ai { get; set; } public CharacterDefinition? Character { get; set; } public HumanoidDefinition? Humanoid { get; set; } public float? Scale { get; set; } public List<string>? Textures { get; set; } public AppearanceDefinition? Appearance { get; set; } public List<string>? AvailableAttackAnimations { get; set; } internal bool IsEnabled => Enabled != false; } internal sealed class AiDefinition { public string? Ai { get; set; } public bool? Enabled { get; set; } public string? CopyFrom { get; set; } public string? ClonedFrom { get; set; } public BaseAiDefinition? BaseAI { get; set; } public MonsterAiDefinition? MonsterAI { get; set; } internal bool IsEnabled => Enabled != false; } internal sealed class BaseAiDefinition { public List<string>? Senses { get; set; } public List<float>? IdleSound { get; set; } public List<string>? Movement { get; set; } public List<string>? Serpent { get; set; } public List<float>? RandomMove { get; set; } public List<string>? Flight { get; set; } public List<string>? Avoid { get; set; } public List<float>? Flee { get; set; } public List<string>? Aggressive { get; set; } public List<string>? Messages { get; set; } } internal sealed class MonsterAiDefinition { public float? AlertRange { get; set; } public List<string>? Hunt { get; set; } public List<float>? Chase { get; set; } public List<float>? Circle { get; set; } public List<string>? HurtFlee { get; set; } public List<string>? Charge { get; set; } public List<string>? Sleep { get; set; } public bool? AvoidLand { get; set; } } internal sealed class AttackDefinition { public string? Prefab { get; set; } public bool? Enabled { get; set; } public string? ClonedFrom { get; set; } public AttackDamageDefinition? Damage { get; set; } public List<string>? Attack { get; set; } public List<string>? StatusEffect { get; set; } public List<string>? Projectile { get; set; } public List<float>? Ai { get; set; } internal bool IsEnabled => Enabled != false; } internal sealed class ProjectileDefinition { public string? Prefab { get; set; } public bool? Enabled { get; set; } public string? ClonedFrom { get; set; } public List<string>? UsedByAttacks { get; set; } public ProjectileComponentDefinition? Projectile { get; set; } public SpawnAbilityDefinition? SpawnAbility { get; set; } internal bool IsEnabled => Enabled != false; } internal sealed class ProjectileComponentDefinition { private string? _spawnOnHit; [YamlIgnore] public string? SpawnOnHit { get { return _spawnOnHit; } set { _spawnOnHit = value; SpawnOnHitSpecified = true; } } [YamlIgnore] public bool SpawnOnHitSpecified { get; private set; } [YamlMember(Alias = "spawnOnHit")] public ExplicitNullableYamlString? SerializedSpawnOnHit { get { if (!SpawnOnHitSpecified) { return null; } return new ExplicitNullableYamlString(_spawnOnHit); } set { _spawnOnHit = value?.Value; SpawnOnHitSpecified = true; } } internal bool HasSpecifiedFields => SpawnOnHitSpecified; } internal sealed class SpawnAbilityDefinition { private List<string>? _spawnPrefabs; public List<string>? SpawnPrefabs { get { return _spawnPrefabs; } set { _spawnPrefabs = value; SpawnPrefabsSpecified = true; } } [YamlIgnore] public bool SpawnPrefabsSpecified { get; private set; } } internal sealed class ExplicitNullableYamlString : IYamlConvertible { internal string? Value { get; private set; } public ExplicitNullableYamlString() { } internal ExplicitNullableYamlString(string? value) { Value = value; } public void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Value = (string)nestedObjectDeserializer(typeof(string)); } public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { nestedObjectSerializer(Value, typeof(string)); } } internal sealed class FactionDefinition { public string? Faction { get; set; } public int? Id { get; set; } public List<string>? Friendly { get; set; } public List<string>? AggravatedFriendly { get; set; } public List<string>? AlertedFriendly { get; set; } } internal static class CreatureModifierCatalog { internal static readonly IReadOnlyList<string> Keys = Array.AsReadOnly(new string[32] { "enraged", "fire", "frost", "lightning", "spirit", "armorPiercing", "staggering", "undodgeable", "armored", "deathward", "regenerating", "reflection", "vortex", "adaptive", "unflinching", "chameleon", "exposed", "weakened", "withered", "crippling", "disruptive", "adrenalineDrain", "corrosive", "toxicDeath", "swift", "attackSpeed", "vampiric", "reaping", "blink", "omen", "juggernaut", "blamer" }); private static readonly HashSet<string> KeySet = new HashSet<string>(Keys, StringComparer.OrdinalIgnoreCase); internal static bool IsKnown(string modifier) { if (!string.IsNullOrWhiteSpace(modifier)) { return KeySet.Contains(modifier.Trim()); } return false; } } internal sealed class LevelDefinition { private Dictionary<string, ModifierDefinition>? _modifiers; public string? Target { get; set; } public string? Biome { get; set; } public List<string>? Prefabs { get; set; } public List<float>? Level { get; set; } public float? Damage { get; set; } public float? DamagePerLevel { get; set; } public float? Health { get; set; } public float? HealthPerLevel { get; set; } public float? ScalePerLevel { get; set; } public List<float>? DistanceScaling { get; set; } public List<float>? ModifierDistanceScaling { get; set; } public Dictionary<string, ModifierDefinition>? Modifiers { get { return _modifiers; } set { _modifiers = ((value == null) ? null : new Dictionary<string, ModifierDefinition>(value, StringComparer.OrdinalIgnoreCase)); } } public bool ModifiersCleared { get; set; } public bool IsPreset { get; set; } } internal sealed class ModifierDefinition { public float? Chance { get; set; } public float? Power { get; set; } public float? Cooldown { get; set; } public int? MaxActivations { get; set; } public float? MaxRange { get; set; } public string? StartEffect { get; set; } public float? ProcChance { get; set; } public float? Duration { get; set; } public float? SecondaryPower { get; set; } public float? Radius { get; set; } public string? TriggerEffect { get; set; } public float? MaxKarmaGain { get; set; } public float? FleeHealthRatio { get; set; } public int? ReapingHealMaxActivations { get; set; } public float? ReapingMaxHealthPerKill { get; set; } public float? ReapingMaxHealthCap { get; set; } public float? ReapingDamagePerKill { get; set; } public float? ReapingDamageCap { get; set; } public float? ReapingScalePerKill { get; set; } public float? ReapingScaleCap { get; set; } internal ModifierDefinition Clone() { ModifierDefinition modifierDefinition = new ModifierDefinition(); modifierDefinition.OverlayFrom(this); return modifierDefinition; } internal void OverlayFrom(ModifierDefinition source) { if (source.Chance.HasValue) { Chance = source.Chance; } if (source.Power.HasValue) { Power = source.Power; } if (source.Cooldown.HasValue) { Cooldown = source.Cooldown; } if (source.MaxActivations.HasValue) { MaxActivations = source.MaxActivations; } if (source.MaxRange.HasValue) { MaxRange = source.MaxRange; } if (source.StartEffect != null) { StartEffect = source.StartEffect; } if (source.ProcChance.HasValue) { ProcChance = source.ProcChance; } if (source.Duration.HasValue) { Duration = source.Duration; } if (source.SecondaryPower.HasValue) { SecondaryPower = source.SecondaryPower; } if (source.Radius.HasValue) { Radius = source.Radius; } if (source.TriggerEffect != null) { TriggerEffect = source.TriggerEffect; } if (source.MaxKarmaGain.HasValue) { MaxKarmaGain = source.MaxKarmaGain; } if (source.FleeHealthRatio.HasValue) { FleeHealthRatio = source.FleeHealthRatio; } if (source.ReapingHealMaxActivations.HasValue) { ReapingHealMaxActivations = source.ReapingHealMaxActivations; } if (source.ReapingMaxHealthPerKill.HasValue) { ReapingMaxHealthPerKill = source.ReapingMaxHealthPerKill; } if (source.ReapingMaxHealthCap.HasValue) { ReapingMaxHealthCap = source.ReapingMaxHealthCap; } if (source.ReapingDamagePerKill.HasValue) { ReapingDamagePerKill = source.ReapingDamagePerKill; } if (source.ReapingDamageCap.HasValue) { ReapingDamageCap = source.ReapingDamageCap; } if (source.ReapingScalePerKill.HasValue) { ReapingScalePerKill = source.ReapingScalePerKill; } if (source.ReapingScaleCap.HasValue) { ReapingScaleCap = source.ReapingScaleCap; } } } internal sealed class ModifierChanceDefinition { private readonly Dictionary<string, float> _values = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); internal float? Get(string modifier) { if (!_values.TryGetValue(modifier, out var value)) { return null; } return value; } internal void Set(string modifier, float chance) { _values[modifier] = chance; } } internal sealed class ModifierPowerDefinition { private readonly Dictionary<string, float> _values = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); public float? DeathwardCooldown { get; set; } public int? DeathwardMaxActivations { get; set; } public float? ReflectionProcChance { get; set; } public float? ExposedProcChance { get; set; } public float? ExposedDuration { get; set; } public float? WeakenedProcChance { get; set; } public float? WeakenedDuration { get; set; } public float? WitheredProcChance { get; set; } public float? WitheredDuration { get; set; } public float? CripplingJump { get; set; } public float? CripplingProcChance { get; set; } public float? CripplingDuration { get; set; } public float? DisruptiveEitr { get; set; } public float? DisruptiveProcChance { get; set; } public float? DisruptiveDuration { get; set; } public float? AdrenalineDrainGainReduction { get; set; } public float? AdrenalineDrainProcChance { get; set; } public float? AdrenalineDrainDuration { get; set; } public float? CorrosiveProcChance { get; set; } public float? CorrosiveDuration { get; set; } public float? ToxicDeathRadius { get; set; } public string? ToxicDeathTriggerEffect { get; set; } public int? ReapingHealMaxActivations { get; set; } public float? ReapingMaxHealthPerKill { get; set; } public float? ReapingMaxHealthCap { get; set; } public float? ReapingDamagePerKill { get; set; } public float? ReapingDamageCap { get; set; } public float? ReapingScalePerKill { get; set; } public float? ReapingScaleCap { get; set; } public float? BlinkCooldown { get; set; } public float? BlinkMaxRange { get; set; } public string? BlinkStartEffect { get; set; } public float? KnockbackCooldown { get; set; } public float? BlamerMaxKarmaGain { get; set; } public float? BlamerFleeHealthRatio { get; set; } internal float? Get(string modifier) { if (!_values.TryGetValue(modifier, out var value)) { return null; } return value; } internal void Set(string modifier, float power) { _values[modifier] = power; } } internal sealed class AttackDamageDefinition { public float? Damage { get; set; } public float? Blunt { get; set; } public float? Slash { get; set; } public float? Pierce { get; set; } public float? Chop { get; set; } public float? Pickaxe { get; set; } public float? Fire { get; set; } public float? Frost { get; set; } public float? Lightning { get; set; } public float? Poison { get; set; } public float? Spirit { get; set; } public float? AttackForce { get; set; } public int? ToolTier { get; set; } } internal sealed class CharacterDefinition { public string? Name { get; set; } public string? Faction { get; set; } public string? Boss { get; set; } public string? DefeatSetGlobalKey { get; set; } public string? Health { get; set; } public DamageModifiersDefinition? DamageModifiers { get; set; } public string? Speed { get; set; } public string? Jump { get; set; } public string? Swim { get; set; } public string? Flight { get; set; } } internal sealed class DamageModifiersDefinition { public string? Blunt { get; set; } public string? Slash { get; set; } public string? Pierce { get; set; } public string? Chop { get; set; } public string? Pickaxe { get; set; } public string? Fire { get; set; } public string? Frost { get; set; } public string? Lightning { get; set; } public string? Poison { get; set; } public string? Spirit { get; set; } } internal sealed class HumanoidDefinition { public List<string>? DefaultItems { get; set; } public List<string>? RandomWeapon { get; set; } public List<string>? RandomArmor { get; set; } public List<string>? RandomHair { get; set; } public List<string>? RandomShield { get; set; } public List<string>? RandomItems { get; set; } public List<string>? RandomSets { get; set; } } internal sealed class AppearanceDefinition { private string? _hair; private string? _beard; public string? Hair { get { return _hair; } set { _hair = ((value != null && string.IsNullOrWhiteSpace(value)) ? "" : value); } } public string? Beard { get { return _beard; } set { _beard = ((value != null && string.IsNullOrWhiteSpace(value)) ? "" : value); } } public string? HairColor { get; set; } public string? SkinColor { get; set; } public int? ModelIndex { get; set; } internal bool HasSpecifiedFields { get { if (Hair == null && Beard == null && HairColor == null && SkinColor == null) { return ModelIndex.HasValue; } return true; } } } internal static class CreatureDomainManager { private sealed class TextureMaterialOverride { internal Renderer Renderer; internal int MaterialIndex; internal Material Original; internal Material Generated; internal bool Active; } private sealed class RagdollScaleRuntimeState { internal readonly Vector3 FinalScale; internal readonly Vector3 EquipmentScale; internal RagdollScaleRuntimeState(Vector3 finalScale, Vector3 equipmentScale) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) FinalScale = finalScale; EquipmentScale = equipmentScale; } } private sealed class RagdollTextureRuntimeState { internal readonly string RendererName; internal readonly int MaterialIndex; internal readonly Texture Texture; internal RagdollTextureRuntimeState(string rendererName, int materialIndex, Texture texture) { RendererName = rendererName; MaterialIndex = materialIndex; Texture = texture; } } internal sealed class CreatureAppearanceRuntimeState { internal readonly string? Hair; internal readonly string? Beard; internal readonly Vector3? HairColor; internal readonly Vector3? SkinColor; internal readonly int? ModelIndex; internal bool HasSpecifiedFields { get { if (Hair == null && Beard == null && !HairColor.HasValue && !SkinColor.HasValue) { return ModelIndex.HasValue; } return true; } } internal CreatureAppearanceRuntimeState(string? hair, string? beard, Vector3? hairColor, Vector3? skinColor, int? modelIndex) { Hair = hair; Beard = beard; HairColor = hairColor; SkinColor = skinColor; ModelIndex = modelIndex; } } private sealed class DefinitionSnapshot { internal List<FactionDefinition> Factions = new List<FactionDefinition>(); internal List<LevelDefinition> Levels = new List<LevelDefinition>(); internal List<AiDefinition> Ai = new List<AiDefinition>(); internal List<AttackDefinition> Attacks = new List<AttackDefinition>(); internal List<ProjectileDefinition> Projectiles = new List<ProjectileDefinition>(); internal List<CreatureDefinition> Creatures = new List<CreatureDefinition>(); internal string KarmaYaml = ""; } private sealed class SyncedYamlBundleData { public int Version { get; set; } public List<FactionDefinition>? Factions { get; set; } public List<LevelDefinition>? Levels { get; set; } public List<AiDefinition>? Ai { get; set; } public List<AttackDefinition>? Attacks { get; set; } public List<ProjectileDefinition>? Projectiles { get; set; } public List<CreatureDefinition>? Creatures { get; set; } public string? Karma { get; set; } } private sealed class DefinitionApplyCheckpoint { internal readonly Dictionary<string, CreatureAppearanceRuntimeState> InheritedAppearances = new Dictionary<string, CreatureAppearanceRuntimeState>(InheritedAppearanceByClone, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, GameObject> RagdollSources = new Dictionary<string, GameObject>(RagdollCloneSources, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, Vector3> OriginalRagdollScaleValues = new Dictionary<string, Vector3>(OriginalRagdollScales, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, RagdollScaleRuntimeState> InheritedRagdollScaleValues = new Dictionary<string, RagdollScaleRuntimeState>(InheritedRagdollScales, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, RagdollTextureRuntimeState[]> InheritedRagdollTextureValues = new Dictionary<string, RagdollTextureRuntimeState[]>(InheritedRagdollTextures, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<(int RendererId, int MaterialIndex), TextureMaterialOverride> TextureOverrides = new Dictionary<(int, int), TextureMaterialOverride>(TextureMaterialOverrides); } private sealed class ReferenceFileSnapshot { internal readonly string CreatureYaml; internal readonly string AiYaml; internal readonly string AttackYaml; internal readonly string CreatureLoadout; internal readonly string ProjectileYaml; internal readonly string TextureReference; internal readonly string Signature; internal ReferenceFileSnapshot(string creatureYaml, string aiYaml, string attackYaml, string creatureLoadout, string projectileYaml, string textureReference, string signature) { CreatureYaml = creatureYaml; AiYaml = aiYaml; AttackYaml = attackYaml; CreatureLoadout = creatureLoadout; ProjectileYaml = projectileYaml; TextureReference = textureReference; Signature = signature; } } private sealed class CloneMaterializationRequest { internal readonly string Target; internal readonly string Source; internal readonly string Domain; internal CloneMaterializationRequest(string target, string source, string domain) { Target = target; Source = source; Domain = domain; } } private delegate bool TryReadYaml<T>(string yaml, string source, out List<T> definitions); private const long ReloadDebounceTicks = 2500000L; private const long SyncedApplyDebounceTicks = 1000000L; private const int SyncedYamlBundleVersion = 4; private const string ReferenceLogicVersion = "2026-07-21-ragdoll-visual-v2"; private const string MainTextureProperty = "_MainTex"; private const string RagdollCloneSuffix = "_CreatureManagerRagdoll"; private const string DefaultTextureResourcePrefix = "CreatureManager.defaults.textures."; private static readonly string[] DefaultTextureFileNames = new string[11] { "boar2.png", "DarkBrood.png", "DarkSpider.png", "DarkSpiderSmall.png", "goblin2.png", "PolarFenring.png", "PolarLox.png", "PolarWolf.png", "StormFenring.png", "SvartalfarMage.png", "troll2.png" }; private static readonly object Sync = new object(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull).Build(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static ConfigSync? ConfigSync; private static CustomSyncedValue<string>? SyncedYamlBundle; private static FileSystemWatcher? Watcher; private static DateTime PendingDiskReloadTime = DateTime.MaxValue; private static DateTime PendingSyncedApplyTime = DateTime.MaxValue; private static DateTime PendingTextureRefreshTime = DateTime.MaxValue; private static bool DiskReloadPending; private static bool SyncedApplyPending; private static bool TextureRefreshPending; private static bool GameDataRefreshPending; private static bool SuppressSyncedApply; private static bool RemoteBundleReady; private static bool WatcherResetPending; private static DefinitionSnapshot ActiveSnapshot = new DefinitionSnapshot(); private static readonly Dictionary<string, Vector3> EquipmentVisualScales = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, GameObject[]> RandomHairPrefabsByCreature = new Dictionary<string, GameObject[]>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, CreatureAppearanceRuntimeState> AppearanceByCreature = new Dictionary<string, CreatureAppearanceRuntimeState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, CreatureAppearanceRuntimeState> InheritedAppearanceByClone = new Dictionary<string, CreatureAppearanceRuntimeState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, CreatureAppearanceRuntimeState> AppearanceByRagdollPrefab = new Dictionary<string, CreatureAppearanceRuntimeState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, GameObject> ManagedRagdollPrefabs = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, GameObject> RagdollCloneSources = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Vector3> OriginalRagdollScales = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, RagdollScaleRuntimeState> RagdollScalesByPrefab = new Dictionary<string, RagdollScaleRuntimeState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, RagdollScaleRuntimeState> InheritedRagdollScales = new Dictionary<string, RagdollScaleRuntimeState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, RagdollTextureRuntimeState[]> RagdollTexturesByPrefab = new Dictionary<string, RagdollTextureRuntimeState[]>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, RagdollTextureRuntimeState[]> InheritedRagdollTextures = new Dictionary<string, RagdollTextureRuntimeState[]>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<int> AppliedRagdollTextureVisuals = new HashSet<int>(); private static readonly HashSet<string> SuccessfullyAppliedDefinitionTargets = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<(int RendererId, int MaterialIndex), TextureMaterialOverride> TextureMaterialOverrides = new Dictionary<(int, int), TextureMaterialOverride>(); private static readonly MaterialPropertyBlock RagdollTexturePropertyBlock = new MaterialPropertyBlock(); private static bool GameDataReady; private static bool ActiveDefinitionsApplied; private static bool LoadedHumanoidsRefreshedForCurrentGameData; private static bool ReferenceCaptureAttemptedForCurrentGameData; private static ReferenceFileSnapshot? CapturedReferenceFiles; private static int PendingGameDataRefreshFrame = int.MaxValue; internal static string ConfigDirectoryPath => Path.Combine(Paths.ConfigPath, "CreatureManager"); internal static string CacheDirectoryPath => Path.Combine(ConfigDirectoryPath, "cache"); internal static string TextureDirectoryPath => Path.Combine(ConfigDirectoryPath, "textures"); internal static string FactionConfigurationPath => Path.Combine(ConfigDirectoryPath, "factions.yml"); internal static string LevelConfigurationPath => Path.Combine(ConfigDirectoryPath, "levels.yml"); internal static string KarmaConfigurationPath => Path.Combine(ConfigDirectoryPath, "karma.yml"); internal static string AiConfigurationPath => Path.Combine(ConfigDirectoryPath, "ai.yml"); internal static string AiReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "ai.reference.yml"); internal static string AttackConfigurationPath => Path.Combine(ConfigDirectoryPath, "attacks.yml"); internal static string AttackSampleConfigurationPath => Path.Combine(ConfigDirectoryPath, "attacks.sample.yml"); internal static string AttackReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "attacks.reference.yml"); internal static string ProjectileConfigurationPath => Path.Combine(ConfigDirectoryPath, "projectile.yml"); internal static string ProjectileReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "projectile.reference.yml"); internal static string CreatureLoadoutReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "creatureLoadout.reference.txt"); internal static string TextureReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "textures.reference.txt"); internal static string LevelVisualReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "levelVisual.reference.yml"); internal static string CreatureConfigurationPath => Path.Combine(ConfigDirectoryPath, "creatures.yml"); internal static string CreatureSampleConfigurationPath => Path.Combine(ConfigDirectoryPath, "creatures.sample.yml"); internal static string ReferenceConfigurationPath => Path.Combine(ConfigDirectoryPath, "creatures.reference.yml"); internal static string FullScaffoldConfigurationPath => Path.Combine(ConfigDirectoryPath, "creatures.full.yml"); private static string ReferenceStatePath => Path.Combine(CacheDirectoryPath, ".reference-state.txt"); internal static void Initialize(ConfigSync configSync) { ConfigSync = configSync; ActiveDefinitionsApplied = false; RemoteBundleReady = false; LoadedHumanoidsRefreshedForCurrentGameData = false; SyncedYamlBundle = new CustomSyncedValue<string>(configSync, "YamlBundle", "", 100); SyncedYamlBundle.ValueChanged += RequestSyncedYamlApply; configSync.SourceOfTruthChanged += OnSourceOfTruthChanged; EnsureDirectoriesAndDefaultFiles(); if (configSync.IsSourceOfTruth) { LoadDefinitionsFromDisk(assignSyncedValue: true); } else { RequestSyncedYamlApply(); } SetupWatcher(); CreatureConsoleCommands.Register(); } internal static void Dispose() { NotifyGameDataUnavailable(); if (SyncedYamlBundle != null) { SyncedYamlBundle.ValueChanged -= RequestSyncedYamlApply; SyncedYamlBundle = null; } if (ConfigSync != null) { ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged; ConfigSync = null; } DiskReloadPending = false; SyncedApplyPending = false; TextureRefreshPending = false; GameDataRefreshPending = false; PendingDiskReloadTime = DateTime.MaxValue; PendingSyncedApplyTime = DateTime.MaxValue; PendingTextureRefreshTime = DateTime.MaxValue; PendingGameDataRefreshFrame = int.MaxValue; SuppressSyncedApply = false; RemoteBundleReady = false; WatcherResetPending = false; ReferenceCaptureAttemptedForCurrentGameData = false; CapturedReferenceFiles = null; Watcher?.Dispose(); Watcher = null; lock (Sync) { ActiveSnapshot = new DefinitionSnapshot(); } CreatureTextureRegistry.Dispose(); } internal static void RequestConfigurationReload() { ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { DiskReloadPending = true; PendingDiskReloadTime = DateTime.UtcNow.AddTicks(2500000L); } } internal static void Update() { if (!DiskReloadPending && !SyncedApplyPending && !TextureRefreshPending && !GameDataRefreshPending) { return; } if (GameDataRefreshPending && Time.frameCount >= PendingGameDataRefreshFrame) { CompletePendingGameDataRefresh(); } DateTime utcNow = DateTime.UtcNow; if (DiskReloadPending && utcNow >= PendingDiskReloadTime) { DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { LoadDefinitionsFromDisk(assignSyncedValue: true); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } } if (SyncedApplyPending && utcNow >= PendingSyncedApplyTime) { SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ApplySyncedYaml(); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } if (TextureRefreshPending && !DiskReloadPending && !SyncedApplyPending && utcNow >= PendingTextureRefreshTime) { TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ApplyDefinitionsToGameData(); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } } internal static void NotifyGameDataAvailable(bool objectDbFinalized = false) { if (HasGameDataInstances()) { GameDataReady = true; CreatureConsoleCommands.InvalidateSpawnAutocompleteOptions(); CreatureAssetOwnerCatalog.InvalidateMappings(); GameDataRefreshPending = true; PendingGameDataRefreshFrame = (objectDbFinalized ? Time.frameCount : Math.Min(PendingGameDataRefreshFrame, Time.frameCount + 1)); if (objectDbFinalized) { CompletePendingGameDataRefresh(); } } } private static void CompletePendingGameDataRefresh() { if (GameDataRefreshPending && HasGameDataInstances()) { GameDataRefreshPending = false; PendingGameDataRefreshFrame = int.MaxValue; CaptureReferenceConfigurationFilesIfNeeded(); WriteCapturedReferenceConfigurationFilesIfNeeded(); ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth && !RemoteBundleReady) { RequestSyncedYamlApply(); } ApplyDefinitionsToGameData(); } } internal static void NotifyGameDataUnavailable() { CreaturePrefabBaseline.RestoreAllAndClear(); DisposeTextureOverrides(); EquipmentVisualScales.Clear(); RandomHairPrefabsByCreature.Clear(); AppearanceByCreature.Clear(); InheritedAppearanceByClone.Clear(); AppearanceByRagdollPrefab.Clear(); ManagedRagdollPrefabs.Clear(); RagdollCloneSources.Clear(); OriginalRagdollScales.Clear(); RagdollScalesByPrefab.Clear(); InheritedRagdollScales.Clear(); RagdollTexturesByPrefab.Clear(); InheritedRagdollTextures.Clear(); AppliedRagdollTextureVisuals.Clear(); SuccessfullyAppliedDefinitionTargets.Clear(); ActiveDefinitionsApplied = false; CreaturePrefabRegistry.ResetOwnedClones(); CreatureConsoleCommands.InvalidateSpawnAutocompleteOptions(); CreatureAssetOwnerCatalog.InvalidateMappings(); GameDataReady = false; LoadedHumanoidsRefreshedForCurrentGameData = false; ReferenceCaptureAttemptedForCurrentGameData = false; CapturedReferenceFiles = null; RemoteBundleReady = false; SyncedApplyPending = false; GameDataRefreshPending = false; PendingSyncedApplyTime = DateTime.MaxValue; PendingGameDataRefreshFrame = int.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; } internal static void LoadDefinitionsFromDisk(bool assignSyncedValue) { if (GameDataRefreshPending) { RequestConfigurationReload(); return; } EnsureDirectoriesAndDefaultFiles(); if (!TryBuildDiskSnapshot(out DefinitionSnapshot snapshot) || !CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, KarmaConfigurationPath, out CreatureKarmaManager.ParsedConfiguration parsed)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because at least one local YAML domain is invalid."); return; } string serialized = null; if (assignSyncedValue) { if (!TrySerializeAndVerifyBundle(snapshot, out serialized, out DefinitionSnapshot verified)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because the synchronized YAML bundle did not round-trip."); return; } snapshot = verified; if (!CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, "local synchronized YAML bundle", out parsed)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because Karma did not survive the synchronized YAML bundle round-trip."); return; } } if (!TryBeginDefinitionClonePreparation(snapshot, "local YAML bundle", out var preparationStarted)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because its clones could not be prepared."); return; } bool keepPreparedClones = false; string text = null; bool flag = false; try { if (serialized != null) { SuppressSyncedApply = true; try { if (SyncedYamlBundle == null) { CreatureManagerPlugin.Log.LogError((object)"Cannot publish the synchronized YAML bundle because ServerSync is not initialized."); return; } text = SyncedYamlBundle.Value; SyncedYamlBundle.AssignLocalValue(serialized); flag = true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to publish the synchronized YAML bundle; keeping the complete last-known-good configuration: " + ex.Message)); return; } finally { SuppressSyncedApply = false; } SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; } if (!TrySetActiveDefinitions(snapshot, "local YAML bundle", preparationStarted)) { if (!flag || SyncedYamlBundle == null || text == null) { return; } SuppressSyncedApply = true; try { SyncedYamlBundle.AssignLocalValue(text); return; } catch (Exception ex2) { CreatureManagerPlugin.Log.LogError((object)("Failed to restore the previous synchronized YAML bundle after the local definition transaction was rejected: " + ex2.Message)); return; } finally { SuppressSyncedApply = false; } } CreatureKarmaManager.CommitParsedConfiguration(parsed); keepPreparedClones = true; } finally { EndDefinitionClonePreparation(preparationStarted, keepPreparedClones); } } internal static bool TryWriteReferenceConfigurationFile(out string path, out string error) { path = ReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildReferenceYaml, "creature reference YAML", out error); } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { path = FullScaffoldConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildFullScaffoldYaml, "creature full scaffold YAML", out error); } internal static bool TryWriteAttackReferenceConfigurationFile(out string path, out string error) { path = AttackReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildAttackReferenceYaml, "attack reference YAML", out error); } internal static bool TryWriteAiReferenceConfigurationFile(out string path, out string error) { path = AiReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildAiReferenceYaml, "AI reference YAML", out error); } internal static bool TryWriteTextureReferenceConfigurationFile(out string path, out string error) { path = TextureReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureTextureRegistry.BuildTextureReferenceText, "texture reference", out error); } internal static bool TryWriteLevelVisualReferenceConfigurationFile(out string path, out string error) { path = LevelVisualReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildLevelVisualReferenceYaml, "level visual reference YAML", out error); } internal static bool TryWriteCreatureLoadoutReferenceConfigurationFile(out string path, out string error) { path = CreatureLoadoutReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildCreatureLoadoutReferenceText, "creature loadout reference", out error); } internal static bool TryWriteProjectileReferenceConfigurationFile(out string path, out string error) { path = ProjectileReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildProjectileReferenceYaml, "projectile reference YAML", out error); } private static bool TryWriteGeneratedFile(string path, Func<string> buildContent, string label, out string error) { error = ""; if (!IsGameDataReady()) { error = "Creature game data is not ready yet."; return false; } try { File.WriteAllText(path, buildContent()); CreatureManagerPlugin.Log.LogInfo((object)("Wrote " + label + " to " + path + ".")); return true; } catch (Exception ex) { error = "Failed to write " + label + ": " + ex.Message; CreatureManagerPlugin.Log.LogError((object)(error + " Path: " + path)); return false; } } private static void CaptureReferenceConfigurationFilesIfNeeded() { if (ReferenceCaptureAttemptedForCurrentGameData || !IsGameDataReady()) { return; } ReferenceCaptureAttemptedForCurrentGameData = true; ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { return; } try { CreatureAssetOwnerCatalog.RefreshMappings(); string text = CreatureReferenceWriter.BuildReferenceYaml(); string text2 = CreatureReferenceWriter.BuildAiReferenceYaml(); string text3 = CreatureReferenceWriter.BuildAttackReferenceYaml(); string text4 = CreatureReferenceWriter.BuildCreatureLoadoutReferenceText(); string text5 = CreatureReferenceWriter.BuildProjectileReferenceYaml(); string text6 = CreatureTextureRegistry.BuildTextureReferenceText(); if (!(text == "[]\n") || !(text2 == "[]\n") || !(text3 == "[]\n") || !(text4 == "[]\n") || !(text5 == "[]\n") || !(text6 == "[]\n")) { string signature = ComputeStableSignature("2026-07-21-ragdoll-visual-v2\n" + text + "\n---ai---\n" + text2 + "\n---attacks---\n" + text3 + "\n---loadout---\n" + text4 + "\n---projectile---\n" + text5 + "\n---textures---\n" + text6); CapturedReferenceFiles = new ReferenceFileSnapshot(text, text2, text3, text4, text5, text6, signature); } } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to capture pristine CreatureManager reference data; automatic reference generation is disabled until game data reloads: " + ex.Message)); } } private static void WriteCapturedReferenceConfigurationFilesIfNeeded() { ReferenceFileSnapshot capturedReferenceFiles = CapturedReferenceFiles; if (capturedReferenceFiles == null) { return; } ConfigSync? configSync = ConfigSync; if ((configSync != null && !configSync.IsSourceOfTruth) || ReferenceFilesAreCurrent(capturedReferenceFiles.Signature)) { return; } try { File.WriteAllText(ReferenceConfigurationPath, capturedReferenceFiles.CreatureYaml); File.WriteAllText(AiReferenceConfigurationPath, capturedReferenceFiles.AiYaml); File.WriteAllText(AttackReferenceConfigurationPath, capturedReferenceFiles.AttackYaml); File.WriteAllText(CreatureLoadoutReferenceConfigurationPath, capturedReferenceFiles.CreatureLoadout); File.WriteAllText(ProjectileReferenceConfigurationPath, capturedReferenceFiles.ProjectileYaml); File.WriteAllText(TextureReferenceConfigurationPath, capturedReferenceFiles.TextureReference); RecordReferenceSignature(capturedReferenceFiles.Signature); CreatureManagerPlugin.Log.LogInfo((object)("Updated reference files at " + ReferenceConfigurationPath + ", " + AiReferenceConfigurationPath + ", " + AttackReferenceConfigurationPath + ", " + CreatureLoadoutReferenceConfigurationPath + ", " + ProjectileReferenceConfigurationPath + ", and " + TextureReferenceConfigurationPath + ".")); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to update CreatureManager reference files: " + ex.Message)); } } internal static bool IsGameDataReady() { if (!GameDataReady) { return HasGameDataInstances(); } return true; } internal static bool IsSynchronizedConfigurationReady() { if (ActiveDefinitionsApplied && !GameDataRefreshPending) { ConfigSync? configSync = ConfigSync; if (configSync == null || !configSync.IsSourceOfTruth || DiskReloadPending) { ConfigSync? configSync2 = ConfigSync; if (configSync2 != null && !configSync2.IsSourceOfTruth && RemoteBundleReady) { return !SyncedApplyPending; } return false; } return true; } return false; } private static bool HasGameDataInstances() { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ObjectDB.instance != (Object)null; } return false; } private static bool ReferenceFilesAreCurrent(string signature) { if (!File.Exists(ReferenceConfigurationPath) || !File.Exists(AiReferenceConfigurationPath) || !File.Exists(AttackReferenceConfigurationPath) || !File.Exists(CreatureLoadoutReferenceConfigurationPath) || !File.Exists(ProjectileReferenceConfigurationPath) || !File.Exists(TextureReferenceConfigurationPath) || !File.Exists(ReferenceStatePath)) { return false; } try { string[] array = File.ReadAllLines(ReferenceStatePath); return array.Length >= 2 && string.Equals(array[0], "2026-07-21-ragdoll-visual-v2", StringComparison.Ordinal) && string.Equals(array[1], signature, StringComparison.Ordinal); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to read reference state '" + ReferenceStatePath + "': " + ex.Message)); return false; } } private static void RecordReferenceSignature(string signature) { try { Directory.CreateDirectory(CacheDirectoryPath); File.WriteAllLines(ReferenceStatePath, new string[2] { "2026-07-21-ragdoll-visual-v2", signature }); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to write reference state '" + ReferenceStatePath + "': " + ex.Message)); } } private static string ComputeStableSignature(string value) { ulong num = 14695981039346656037uL; foreach (char c in value) { num ^= c; num *= 1099511628211L; } return num.ToString("X16", CultureInfo.InvariantCulture); } private static void OnSourceOfTruthChanged(bool isSourceOfTruth) { if (isSourceOfTruth) { RemoteBundleReady = false; SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; WriteCapturedReferenceConfigurationFilesIfNeeded(); LoadDefinitionsFromDisk(assignSyncedValue: true); return; } DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; RemoteBundleReady = false; if (WatcherResetPending) { TextureRefreshPending = true; PendingTextureRefreshTime = DateTime.UtcNow; } RequestSyncedYamlApply(); } private static void ApplySyncedYaml() { if (SyncedYamlBundle == null) { return; } if (GameDataRefreshPending) { RequestSyncedYamlApply(); return; } ConfigSync? configSync = ConfigSync; string text = ((configSync != null && configSync.IsSourceOfTruth) ? "local synchronized YAML bundle" : "server synchronized YAML bundle"); if (!TryDeserializeBundle(SyncedYamlBundle.Value, text, out DefinitionSnapshot snapshot) || !CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, text, out CreatureKarmaManager.ParsedConfiguration parsed)) { CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because " + text + " is invalid.")); return; } if (!TryBeginDefinitionClonePreparation(snapshot, text, out var preparationStarted)) { CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because the clones from " + text + " could not be prepared.")); return; } bool remoteBundleReady = RemoteBundleReady; ConfigSync? configSync2 = ConfigSync; RemoteBundleReady = configSync2 != null && !configSync2.IsSourceOfTruth; bool keepPreparedClones = false; try { if (!TrySetActiveDefinitions(snapshot, text, preparationStarted)) { RemoteBundleReady = remoteBundleReady; return; } CreatureKarmaManager.CommitParsedConfiguration(parsed); keepPreparedClones = true; } finally { EndDefinitionClonePreparation(preparationStarted, keepPreparedClones); } } private static bool TryBuildDiskSnapshot(out DefinitionSnapshot snapshot) { snapshot = new DefinitionSnapshot(); bool num = TryLoadOverrideFiles("factions", (TryReadYaml<FactionDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Factions); bool flag = TryLoadLevelDefinitions(out snapshot.Levels); bool flag2 = TryLoadOverrideFiles("ai", (TryReadYaml<AiDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Ai); bool flag3 = TryLoadOverrideFiles("attacks", (TryReadYaml<AttackDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Attacks); bool flag4 = TryLoadOverrideFiles("projectile", (TryReadYaml<ProjectileDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Projectiles); RemoveProjectileReferenceMetadata(snapshot.Projectiles); bool flag5 = TryLoadOverrideFiles("creatures", (TryReadYaml<CreatureDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Creatures); bool flag6 = TryReadTextFile(KarmaConfigurationPath, "Karma", out snapshot.KarmaYaml); return num && flag && flag2 && flag3 && flag4 && flag5 && flag6; } private static bool TryReadTextFile(string path, string domain, out string text) { text = ""; try { if (!File.Exists(path)) { CreatureManagerPlugin.Log.LogError((object)(domain + " configuration was not found at " + path + ".")); return false; } text = File.ReadAllText(path); return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + domain + " configuration from " + path + ": " + ex.Message)); return false; } } private static bool TrySerializeAndVerifyBundle(DefinitionSnapshot snapshot, out string serialized, out DefinitionSnapshot verified) { serialized = ""; verified = new DefinitionSnapshot(); try { SyncedYamlBundleData graph = new SyncedYamlBundleData { Version = 4, Factions = snapshot.Factions, Levels = snapshot.Levels, Ai = snapshot.Ai, Attacks = snapshot.Attacks, Projectiles = snapshot.Projectiles, Creatures = snapshot.Creatures, Karma = snapshot.KarmaYaml }; serialized = Serializer.Serialize(graph); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to serialize synchronized YAML bundle: " + ex.Message)); return false; } return TryDeserializeBundle(serialized, "local synchronized YAML bundle round-trip", out verified); } private static bool TryDeserializeBundle(string yaml, string source, out DefinitionSnapshot snapshot) { snapshot = new DefinitionSnapshot(); if (string.IsNullOrWhiteSpace(yaml)) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": the bundle is empty.")); return false; } try { CreatureYaml.ValidateUniqueMappingKeysInDocument(yaml, source); SyncedYamlBundleData syncedYamlBundleData = Deserializer.Deserialize<SyncedYamlBundleData>(yaml); if (syncedYamlBundleData == null || syncedYamlBundleData.Version != 4) { string arg = ((syncedYamlBundleData == null) ? "missing" : syncedYamlBundleData.Version.ToString(CultureInfo.InvariantCulture)); CreatureManagerPlugin.Log.LogError((object)$"Failed to read {source}: expected bundle version {4} but got {arg}."); return false; } if (syncedYamlBundleData.Factions == null || syncedYamlBundleData.Levels == null || syncedYamlBundleData.Ai == null || syncedYamlBundleData.Attacks == null || syncedYamlBundleData.Projectiles == null || syncedYamlBundleData.Creatures == null || syncedYamlBundleData.Karma == null) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": all seven configuration domains must be present.")); return false; } if (!CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Factions, source + ".factions") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Levels, source + ".levels") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Ai, source + ".ai") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Attacks, source + ".attacks") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Projectiles, source + ".projectile") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Creatures, source + ".creatures")) { return false; } snapshot.Factions = syncedYamlBundleData.Factions; snapshot.Levels = syncedYamlBundleData.Levels; snapshot.Ai = syncedYamlBundleData.Ai; snapshot.Attacks = syncedYamlBundleData.Attacks; RemoveProjectileReferenceMetadata(syncedYamlBundleData.Projectiles); snapshot.Projectiles = syncedYamlBundleData.Projecti