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.3.11
CreatureManager.dll
Decompiled a day 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.Cryptography; 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.3.11")] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.11.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; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace LocalizationManager { internal static class Localizer { private const string ModName = "CreatureManager"; private const string LocalizationExtension = ".yml"; private static readonly Assembly ModAssembly = typeof(CreatureManagerPlugin).Assembly; private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build(); internal static void Load(Harmony harmony) { //IL_004f: 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_0083: Expected O, but got Unknown //IL_0083: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown 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); } 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 CreatureManager 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>(); string directoryName = Path.GetDirectoryName(ModAssembly.Location); IEnumerable<string> source; if (directoryName == null || !Directory.Exists(directoryName)) { IEnumerable<string> enumerable = Enumerable.Empty<string>(); source = enumerable; } else { source = Directory.EnumerateFiles(directoryName, "CreatureManager.*", SearchOption.TopDirectoryOnly); } foreach (string item in source.Where((string file) => Path.GetExtension(file).Equals(".yml", StringComparison.OrdinalIgnoreCase))) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); if (fileNameWithoutExtension.Length > "CreatureManager".Length + 1 && fileNameWithoutExtension["CreatureManager".Length] == '.') { string text = fileNameWithoutExtension.Substring("CreatureManager".Length + 1); if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for CreatureManager. The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array = LoadTranslationFromAssembly("English"); if (array == null) { throw new Exception("Found no English localizations in mod CreatureManager. Expected an embedded resource translations/English.yml."); } Dictionary<string, string> dictionary2 = Deserializer.Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array)); if (dictionary2 == null) { throw new Exception("Localization for mod CreatureManager failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array2 = LoadTranslationFromAssembly(language); if (array2 != null) { text2 = Encoding.UTF8.GetString(array2); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair<string, string> item2 in Deserializer.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) { return ReadEmbeddedFileBytes("translations." + language + ".yml"); } private static byte[]? ReadEmbeddedFileBytes(string resourceFileName) { using MemoryStream memoryStream = new MemoryStream(); string text = ModAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { ModAssembly.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 const string ReferenceScopeSyntax = "creature|ai|attack|loadout|projectile|texture|levelvisual"; 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 ConsoleCommand? KarmaCommand; 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //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_0082: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: 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, 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, 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, false, (ConsoleOptionsFetcher)obj6, false, true, true); object obj7 = <>O.<6>__Karma; if (obj7 == null) { ConsoleEvent val7 = Karma; <>O.<6>__Karma = val7; obj7 = (object)val7; } KarmaCommand = new ConsoleCommand("cm:karma", "Show or set the current outdoor or dungeon 3x3 zone-neighborhood Karma. Usage: cm:karma [value]", (ConsoleEvent)obj7, true, true, true, false, 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")) { 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 " + 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 " + 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_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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: 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, string.Equals(remoteCommandName, "cm:spawn", StringComparison.OrdinalIgnoreCase) ? SpawnCommand : KarmaCommand); 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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) 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 (Character.InInterior(val2)) { 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 flag; switch (scope) { case "loadout": case "texture": case "creature": case "ai": case "attack": case "projectile": case "levelvisual": flag = true; break; default: flag = false; break; } if (flag) { return true; } Terminal context = args.Context; if (context != null) { context.AddString("Syntax: " + command + " creature|ai|attack|loadout|projectile|texture|levelvisual"); } return false; } } 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 CompendiumModifierEntry(string modifierKey, string groupHeading, string name, string description) { ModifierKey = modifierKey; GroupHeading = groupHeading; Name = name; Description = description; } } private const string PageTopic = "$cm_compendium_tab"; 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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)); if (!CreatureLevelManager.IsLevelSystemEnabled()) { return; } List<CompendiumModifierEntry> list = BuildEntries(); if (list.Count != 0) { TextInfo item = new TextInfo("$cm_compendium_tab", BuildPageText(list)); dialog.m_texts.Add(item); dialog.m_texts.Sort((TextInfo a, TextInfo b) => string.Compare(CreatureLocalization.LocalizeText(a.m_topic), CreatureLocalization.LocalizeText(b.m_topic), StringComparison.CurrentCultureIgnoreCase)); } } internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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_014c: 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) if ((Object)(object)dialog == (Object)null || info == null || (Object)(object)dialog.m_textArea == (Object)null) { return; } ClearPageContentIcons(dialog); if (!IsCreatureManagerPage(info.m_topic)) { return; } Dictionary<string, ModifierDefinition> globalModifierDefinitions = CreatureLevelManager.GetGlobalModifierDefinitions(); if (globalModifierDefinitions.Count == 0) { return; } IReadOnlyList<string> knownModifierKeys = CreatureModifierManager.GetKnownModifierKeys(); 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) || val2.linkTextfirstCharacterIndex < 0 || val2.linkTextfirstCharacterIndex >= textInfo.characterCount) { continue; } string b = linkID.Substring("cm-modifier-icon-".Length); string text = null; for (int j = 0; j < knownModifierKeys.Count; j++) { if (string.Equals(knownModifierKeys[j], b, StringComparison.OrdinalIgnoreCase)) { text = knownModifierKeys[j]; break; } } if (text != null && globalModifierDefinitions.ContainsKey(text) && CreatureModifierManager.TryGetModifierSprite(text, out Sprite sprite)) { TMP_CharacterInfo marker = textInfo.characterInfo[val2.linkTextfirstCharacterIndex]; AttachBodyIcon(val, textArea.rectTransform, marker, sprite, text); } } } 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 _)) { list.Add(new CompendiumModifierEntry(knownModifierKey, CreatureModifierManager.GetModifierGroupHeading(knownModifierKey), CreatureModifierManager.GetModifierDisplayName(knownModifierKey), CreatureModifierManager.GetModifierCompendiumText(knownModifierKey, value))); } } return list; } private static string BuildPageText(List<CompendiumModifierEntry> entries) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(CreatureLocalization.Localize("cm_compendium_resistance_hint", "Sneak while looking at a creature to view its weaknesses, resistances, and immunities beneath its nameplate.")).Append("\n\n"); 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, "$cm_compendium_tab", 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; private List<string>? _randomSpawnOnHit; private int? _randomSpawnOnHitCount; [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; } } public List<string>? RandomSpawnOnHit { get { return _randomSpawnOnHit; } set { _randomSpawnOnHit = value; RandomSpawnOnHitSpecified = true; } } [YamlIgnore] public bool RandomSpawnOnHitSpecified { get; private set; } public int? RandomSpawnOnHitCount { get { return _randomSpawnOnHitCount; } set { _randomSpawnOnHitCount = value; RandomSpawnOnHitCountSpecified = true; } } [YamlIgnore] public bool RandomSpawnOnHitCountSpecified { get; private set; } internal bool HasSpecifiedFields { get { if (!SpawnOnHitSpecified && !RandomSpawnOnHitSpecified) { return RandomSpawnOnHitCountSpecified; } return true; } } } 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", "toxicAura", "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? Speed { get; set; } public float? SpeedPerLevel { 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 float? MaxKarmaGain { get; set; } public float? FleeHealthRatio { get; set; } public float? RegeneratingHealthPerSecondCap { 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.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.MaxKarmaGain.HasValue) { MaxKarmaGain = source.MaxKarmaGain; } if (source.FleeHealthRatio.HasValue) { FleeHealthRatio = source.FleeHealthRatio; } if (source.RegeneratingHealthPerSecondCap.HasValue) { RegeneratingHealthPerSecondCap = source.RegeneratingHealthPerSecondCap; } 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? RegeneratingHealthPerSecondCap { 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 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 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 = ""; internal CreatureTextureSync.ManifestData TextureManifest = CreatureTextureSync.CreateEmptyManifest(); internal CreatureTextureSync.ServerSnapshot? ServerTextures; } 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; } public CreatureTextureSync.ManifestData? Textures { 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 = 5; private const string ReferenceLogicVersion = "2026-07-21-ragdoll-visual-v2"; private const string MainTextureProperty = "_MainTex"; private const string RagdollCloneSuffix = "_CreatureManagerRagdoll"; 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 CreatureTextureSync.ManifestData? ApplyingRemoteTextureManifest; 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; CreatureTextureSync.ClientManifestReady += OnClientTextureManifestReady; EnsureDirectoriesAndDefaultFiles(); if (configSync.IsSourceOfTruth) { LoadDefinitionsFromDisk(assignSyncedValue: true); } else { CreatureTextureSync.BeginRemoteSession(); 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; } CreatureTextureSync.ClientManifestReady -= OnClientTextureManifestReady; CreatureTextureSync.ResetRuntimeState(); 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() { CreatureTextureSync.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; CreatureKarmaManager.InvalidateBossBlockerDiscovery(); 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(); ClearActiveDefinitionSideState(); InheritedAppearanceByClone.Clear(); RagdollCloneSources.Clear(); OriginalRagdollScales.Clear(); InheritedRagdollScales.Clear(); InheritedRagdollTextures.Clear(); SuccessfullyAppliedDefinitionTargets.Clear(); ActiveDefinitionsApplied = false; CreaturePrefabRegistry.ResetOwnedClones(); CreatureConsoleCommands.InvalidateSpawnAutocompleteOptions(); CreatureAssetOwnerCatalog.InvalidateMappings(); ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { CreatureTextureSync.BeginRemoteSession(); } else { CreatureTextureSync.ResetClientState(); } CreatureTextureRegistry.PruneInactiveSynchronizedTextures(); CreatureTextureRegistry.InvalidateResourceTextures(); 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; bool flag = false; string text = null; bool flag2 = false; CreatureTextureSync.ServerSnapshot snapshot2 = null; bool flag3 = false; try { if (snapshot.ServerTextures == null) { CreatureManagerPlugin.Log.LogError((object)"Cannot publish the synchronized YAML bundle because its server texture snapshot is missing."); return; } snapshot2 = CreatureTextureSync.InstallServerSnapshot(snapshot.ServerTextures); flag3 = true; 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; if (!string.Equals(text, serialized, StringComparison.Ordinal)) { SyncedYamlBundle.AssignLocalValue(serialized); flag2 = 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 (flag3) { CreatureTextureSync.RestoreServerSnapshot(snapshot2); flag3 = false; } if (!flag2 || 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; flag = true; } finally { if (flag3 && !flag) { CreatureTextureSync.RestoreServerSnapshot(snapshot2); } 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) { CreatureTextureSync.ResetClientState(); RemoteBundleReady = false; SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; WriteCapturedReferenceConfigurationFilesIfNeeded(); LoadDefinitionsFromDisk(assignSyncedValue: true); } else { DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; RemoteBundleReady = false; CreatureTextureSync.BeginRemoteSession(); RequestSyncedYamlApply(); } } private static void OnClientTextureManifestReady(string rootHash) { ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { DefinitionSnapshot activeSnapshot; lock (Sync) { activeSnapshot = ActiveSnapshot; } if (activeSnapshot.TextureManifest.RootHash.Equals(rootHash, StringComparison.Ordinal)) { TextureRefreshPending = true; PendingTextureRefreshTime = DateTime.UtcNow; } } } 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); ConfigSync? configSync3 = ConfigSync; if (configSync3 != null && !configSync3.IsSourceOfTruth && !CreatureTextureSync.AcceptServerManifest(snapshot.TextureManifest, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Kept the last-known-good synchronized textures because the new server manifest could not be staged: " + error)); } 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); bool flag5 = TryLoadOverrideFiles("creatures", (TryReadYaml<CreatureDefinition>)CreatureYaml.TryReadDefinitions, out snapshot.Creatures); StripReferenceMetadata(snapshot); bool flag6 = flag5 && TryBuildServerTextureSnapshot(snapshot); bool flag7 = TryReadTextFile(KarmaConfigurationPath, "Karma", out snapshot.KarmaYaml); return num && flag && flag2 && flag3 && flag4 && flag5 && flag6 && flag7; } private static bool TryBuildServerTextureSnapshot(DefinitionSnapshot snapshot) { List<string> list = new List<string>(); foreach (CreatureDefinition item in snapshot.Creatures.Where((CreatureDefinition definition) => definition.IsEnabled)) { IEnumerable<string> textures = item.Textures; foreach (string item2 in textures ?? Enumerable.Empty<string>()) { if (!TryParseTextureOverride(item2, out string _, out int _, out string textureName, out string error)) { CreatureManagerPlugin.Log.LogError((object)("Failed to collect synchronized textures from creature '" + item.Prefab + "': " + error)); return false; } list.Add(textureName); } } if (!CreatureTextureSync.TryBuildServerSnapshot(list, out CreatureTextureSync.ServerSnapshot snapshot2, out string error2)) { CreatureManagerPlugin.Log.LogError((object)error2); return false; } snapshot.ServerTextures = snapshot2; snapshot.TextureManifest = snapshot2.GetManifestCopy(); return true; } private static bool TryR