using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CreatureManager; using GUIFramework; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CreatureManager")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("CreatureManager")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.7")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.7.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LocalizationManager { internal static class Localizer { private static BaseUnityPlugin? _plugin; private static readonly List fileExtensions = new List(2) { ".json", ".yml" }; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } internal static void Load(Harmony harmony) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0089: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown _ = plugin; harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", new Type[1] { typeof(string) }, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "BeforeLanguageSetup", (Type[])null, (Type[])null), 800, (string[])null, (string[])null, (bool?)null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeLoadLocalization", (Type[])null, (Type[])null), 0, (string[])null, (string[])null, (bool?)null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null), 0, (string[])null, (string[])null, (bool?)null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } internal static void Unload() { _plugin = null; } private static void LoadLocalizationLater() { Localization instance = Localization.instance; if (instance != null) { SafeLoadLocalization(instance, instance.GetSelectedLanguage()); } } [HarmonyPriority(800)] private static void BeforeLanguageSetup(Localization __instance) { try { CreatureServerLocalization.BeforeLanguageSetup(__instance); } catch (Exception ex) { Debug.LogError((object)("Failed to prepare CreatureManager server localization before a language change. " + ex.Message)); } } [HarmonyPriority(0)] private static void SafeLoadLocalization(Localization __instance, string language) { try { LoadLocalization(__instance, language); } catch (Exception ex) { Debug.LogError((object)("Failed to load " + plugin.Info.Metadata.Name + " localization for '" + language + "'. Vanilla localization will remain active. " + ex.Message)); } try { CreatureServerLocalization.ApplyCurrentLocalization(__instance, language); } catch (Exception ex2) { Debug.LogError((object)("Failed to apply CreatureManager server localization for '" + language + "'. " + ex2.Message)); } } private static void LoadLocalization(Localization __instance, string language) { Dictionary dictionary = new Dictionary(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize>(text2) ?? new Dictionary()) { dictionary2[item2.Key] = item2.Value; } } foreach (KeyValuePair item3 in dictionary2) { __instance.AddWord(item3.Key, item3.Value); } } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } } namespace CreatureManager { internal static class CreatureConsoleCommands { private enum SpawnAutocompleteField { None, Prefab, Modifier } [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__WriteReference; public static ConsoleOptionsFetcher <1>__GetReferenceDomainOptions; public static ConsoleEvent <2>__WriteFull; public static ConsoleOptionsFetcher <3>__GetFullDomainOptions; public static ConsoleEvent <4>__Spawn; public static ConsoleOptionsFetcher <5>__GetSpawnPrefabOptions; public static ConsoleEvent <6>__Karma; } private const string SpawnCommandName = "cm:spawn"; private const int MaximumCommandSpawnLevel = 100; private static readonly List ReferenceDomainOptions = new List { "creature", "ai", "attack", "loadout", "projectile", "texture", "levelvisual" }; private static readonly List FullDomainOptions = new List { "creature" }; private static readonly List EmptyAutocompleteOptions = new List(); private static readonly char[] ArgumentSeparators = new char[2] { ' ', '\t' }; private static ConsoleCommand? SpawnCommand; private static bool Registered; internal static void Register() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown if (!Registered) { Registered = true; object obj = <>O.<0>__WriteReference; if (obj == null) { ConsoleEvent val = WriteReference; <>O.<0>__WriteReference = val; obj = (object)val; } object obj2 = <>O.<1>__GetReferenceDomainOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetReferenceDomainOptions; <>O.<1>__GetReferenceDomainOptions = val2; obj2 = (object)val2; } new ConsoleCommand("cm:reference", "Write generated CreatureManager reference files. Usage: cm:reference creature|ai|attack|loadout|projectile|texture|levelvisual", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); object obj3 = <>O.<2>__WriteFull; if (obj3 == null) { ConsoleEvent val3 = WriteFull; <>O.<2>__WriteFull = val3; obj3 = (object)val3; } object obj4 = <>O.<3>__GetFullDomainOptions; if (obj4 == null) { ConsoleOptionsFetcher val4 = GetFullDomainOptions; <>O.<3>__GetFullDomainOptions = val4; obj4 = (object)val4; } new ConsoleCommand("cm:full", "Write generated CreatureManager full scaffold YAML. Usage: cm:full creature", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, false); string text = $"Spawn a creature with optional modifiers and an exact trailing level from 1 to {100}. Usage: cm:spawn [modifier1,modifier2,modifier3,modifier4] [level]"; object obj5 = <>O.<4>__Spawn; if (obj5 == null) { ConsoleEvent val5 = Spawn; <>O.<4>__Spawn = val5; obj5 = (object)val5; } object obj6 = <>O.<5>__GetSpawnPrefabOptions; if (obj6 == null) { ConsoleOptionsFetcher val6 = GetSpawnPrefabOptions; <>O.<5>__GetSpawnPrefabOptions = val6; obj6 = (object)val6; } SpawnCommand = new ConsoleCommand("cm:spawn", text, (ConsoleEvent)obj5, true, true, true, false, false, (ConsoleOptionsFetcher)obj6, false, true, true); object obj7 = <>O.<6>__Karma; if (obj7 == null) { ConsoleEvent val7 = Karma; <>O.<6>__Karma = val7; obj7 = (object)val7; } new ConsoleCommand("cm:karma", "Show or set current 3x3 zone-neighborhood Karma. Usage: cm:karma [value]", (ConsoleEvent)obj7, true, true, true, false, false, (ConsoleOptionsFetcher)null, false, true, true); } } private static List GetReferenceDomainOptions() { return ReferenceDomainOptions; } private static List GetFullDomainOptions() { return FullDomainOptions; } private static List GetSpawnPrefabOptions() { List creaturePrefabs = CreaturePrefabRegistry.GetCreaturePrefabs(); List list = new List(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 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()); 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 GetModifierAutocompleteOptions(string modifierToken, out string currentModifier) { string[] array = modifierToken.Split(new char[1] { ',' }, StringSplitOptions.None); currentModifier = array[^1].Trim(); HashSet hashSet = new HashSet(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 knownModifierKeys = CreatureModifierManager.GetKnownModifierKeys(); List list = new List(knownModifierKeys.Count - hashSet.Count); foreach (string item in knownModifierKeys) { if (!hashSet.Contains(item)) { list.Add(item); } } return list; } private static void WriteReference(ConsoleEventArgs args) { CreatureAssetOwnerCatalog.RefreshMappings(); string scope = GetScope(args); if (!IsKnownScope(scope, args, "cm:reference", includeTexture: true)) { return; } if (scope switch { "ai" => CreatureDomainManager.TryWriteAiReferenceConfigurationFile(out string path, out string error), "attack" => CreatureDomainManager.TryWriteAttackReferenceConfigurationFile(out path, out error), "loadout" => CreatureDomainManager.TryWriteCreatureLoadoutReferenceConfigurationFile(out path, out error), "projectile" => CreatureDomainManager.TryWriteProjectileReferenceConfigurationFile(out path, out error), "texture" => CreatureDomainManager.TryWriteTextureReferenceConfigurationFile(out path, out error), "levelvisual" => CreatureDomainManager.TryWriteLevelVisualReferenceConfigurationFile(out path, out error), _ => CreatureDomainManager.TryWriteReferenceConfigurationFile(out path, out error), }) { Terminal context = args.Context; if (context != null) { context.AddString("Wrote " + NormalizeScope(scope) + " reference to " + path); } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } private static void WriteFull(ConsoleEventArgs args) { CreatureAssetOwnerCatalog.RefreshMappings(); string scope = GetScope(args); string path; string error; if (scope != "creature") { Terminal context = args.Context; if (context != null) { context.AddString("Syntax: cm:full creature"); } } else if (CreatureDomainManager.TryWriteFullScaffoldConfigurationFile(out path, out error)) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Wrote " + NormalizeScope(scope) + " full scaffold to " + path); } } else { Terminal context3 = args.Context; if (context3 != null) { context3.AddString(error); } } } private static void Karma(ConsoleEventArgs args) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!RequireAuthoritativeAdmin(args)) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Terminal context = args.Context; if (context != null) { context.AddString("No local player."); } return; } ExecuteKarma(args, ((Component)localPlayer).transform.position, delegate(string message) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(message); } }); } private static void ExecuteKarma(ConsoleEventArgs args, Vector3 playerPosition, Action 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 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 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() == (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(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(); 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(); if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { ZNetScene.instance.Destroy(spawned); return; } } catch { } Object.Destroy((Object)(object)spawned); } internal static bool TryHandleAuthenticatedRemoteAdminCommand(ZNet znet, ZRpc rpc, string command) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) string remoteCommandName = GetRemoteCommandName(command); if (!string.Equals(remoteCommandName, "cm:spawn", StringComparison.OrdinalIgnoreCase) && !string.Equals(remoteCommandName, "cm:karma", StringComparison.OrdinalIgnoreCase)) { return false; } if (!znet.IsServer() || rpc == null) { return false; } Action 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() == (Object)null) { action("Could not validate the remote admin's active player."); return true; } Vector3 position = zDO.GetPosition(); Quaternion val2 = zDO.GetRotation(); if (!IsFinite(position)) { action("Could not validate the remote admin's player position."); return true; } if (!IsFinite(val2)) { val2 = Quaternion.identity; } ConsoleEventArgs args = new ConsoleEventArgs(command, (Terminal)(object)Console.instance); if (string.Equals(remoteCommandName, "cm:spawn", StringComparison.OrdinalIgnoreCase)) { if (!CreatureLevelManager.IsLevelSystemEnabled()) { action("CreatureManager level system is disabled."); return true; } ExecuteSpawn(args, position, val2, action); } else { ExecuteKarma(args, position, action); } ISocket socket = rpc.GetSocket(); string text = ((socket != null) ? socket.GetHostName() : null) ?? ""; CreatureManagerPlugin.Log.LogInfo((object)("Remote admin '" + text + "' executed CreatureManager command '" + remoteCommandName + "'.")); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to execute remote CreatureManager command '" + remoteCommandName + "': " + ex.Message)); action("CreatureManager could not execute the remote command. Check the server log."); } return true; } private static string GetRemoteCommandName(string command) { if (string.IsNullOrWhiteSpace(command)) { return ""; } int num = command.IndexOfAny(ArgumentSeparators); return ((num >= 0) ? command.Substring(0, num) : command).Trim(); } private static bool RequireAuthoritativeAdmin(ConsoleEventArgs args) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZNet.instance.LocalPlayerIsAdminOrHost()) { return true; } Terminal context = args.Context; if (context != null) { context.AddString("This command must be run locally by the server host."); } return false; } private static bool TryParseSpawnArguments(ConsoleEventArgs args, out string prefabName, out int level, out List modifiers, out string error) { prefabName = ((args.Length >= 2) ? (args[1] ?? "").Trim() : ""); level = 1; modifiers = new List(); error = ""; if (prefabName.Length == 0) { error = "Syntax: cm:spawn [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 list = new List(num - 2); for (int i = 2; i < num; i++) { list.Add(args[i] ?? ""); } string text = string.Join(" ", list).Trim(); string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (text.Length == 0 || array.Length == 0 || !CreatureModifierManager.TryNormalizeForcedModifierKeys(array, out modifiers, out error)) { error = ((error.Length > 0) ? error : "At least one modifier must be specified."); return false; } } return true; } private static Vector3 GetCommandSpawnPosition(Vector3 playerPosition, Vector3 playerForward) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) Vector3 val = playerForward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = playerPosition + val * 3f; if (val2.y >= 4000f) { return val2 + Vector3.up * 0.5f; } float num = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val2 + Vector3.up * 100f, ref num)) { val2.y = num + 0.5f; } else if (WorldGenerator.instance != null) { val2.y = WorldGenerator.instance.GetHeight(val2.x, val2.z) + 0.5f; } else { val2.y += 0.5f; } return val2; } private static bool IsFinite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsFinite(Quaternion value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z)) { return IsFinite(value.w); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static string GetScope(ConsoleEventArgs args) { if (args.Length < 2) { return ""; } return (args[1] ?? "").Trim().ToLowerInvariant(); } private static bool IsKnownScope(string scope, ConsoleEventArgs args, string command, bool includeTexture) { bool flag; switch (scope) { case "creature": case "ai": case "attack": flag = true; break; default: flag = false; break; } if (flag) { return true; } flag = includeTexture; if (flag) { bool flag2; switch (scope) { case "loadout": case "projectile": case "texture": case "levelvisual": flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { return true; } Terminal context = args.Context; if (context != null) { context.AddString(includeTexture ? ("Syntax: " + command + " creature|ai|attack|loadout|projectile|texture|levelvisual") : ("Syntax: " + command + " creature|ai|attack")); } return false; } private static string NormalizeScope(string scope) { return scope switch { "ai" => "ai", "attack" => "attack", "loadout" => "loadout", "projectile" => "projectile", "texture" => "texture", "levelvisual" => "levelvisual", _ => "creature", }; } } internal static class CreatureCompendiumManager { private readonly struct CompendiumModifierEntry { internal readonly string ModifierKey; internal readonly string GroupHeading; internal readonly string Name; internal readonly string Description; internal readonly Sprite Sprite; internal CompendiumModifierEntry(string modifierKey, string groupHeading, string name, string description, Sprite sprite) { ModifierKey = modifierKey; GroupHeading = groupHeading; Name = name; Description = description; Sprite = sprite; } } private const string PageTopic = "CreatureManager"; private const string BodyIconPrefix = "CreatureManager_CompendiumModifierIcon_"; private const string IconLinkPrefix = "cm-modifier-icon-"; private const char IconPlaceholder = ''; private const float IconSize = 18f; private const float IconTextGap = 5f; internal static void AddModifierEntries(TextsDialog dialog) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown if ((Object)(object)dialog == (Object)null || dialog.m_texts == null) { return; } dialog.m_texts.RemoveAll((TextInfo text) => IsCreatureManagerPage(text?.m_topic)); ConfigEntry enableLevelSystem = CreatureManagerPlugin.EnableLevelSystem; if (enableLevelSystem != null && enableLevelSystem.Value == CreatureManagerPlugin.Toggle.Off) { return; } List list = BuildEntries(); if (list.Count != 0) { TextInfo item = new TextInfo("CreatureManager", BuildPageText(list)); dialog.m_texts.Add(item); dialog.m_texts.Sort((TextInfo a, TextInfo b) => a.m_topic.CompareTo(b.m_topic)); } } internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dialog == (Object)null || info == null || (Object)(object)dialog.m_textArea == (Object)null) { return; } ClearPageContentIcons(dialog); if (!IsCreatureManagerPage(info.m_topic)) { return; } List list = BuildEntries(); if (list.Count == 0) { return; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (CompendiumModifierEntry item in list) { dictionary[item.ModifierKey] = item; } TMP_Text textArea = dialog.m_textArea; Transform parent = textArea.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null) { return; } textArea.ForceMeshUpdate(false, false); LayoutRebuilder.ForceRebuildLayoutImmediate(val); textArea.ForceMeshUpdate(false, false); TMP_TextInfo textInfo = textArea.textInfo; for (int i = 0; i < textInfo.linkCount; i++) { TMP_LinkInfo val2 = textInfo.linkInfo[i]; string linkID = ((TMP_LinkInfo)(ref val2)).GetLinkID(); if (linkID.StartsWith("cm-modifier-icon-", StringComparison.Ordinal) && dictionary.TryGetValue(linkID.Substring("cm-modifier-icon-".Length), out var value) && val2.linkTextfirstCharacterIndex >= 0 && val2.linkTextfirstCharacterIndex < textInfo.characterCount) { TMP_CharacterInfo marker = textInfo.characterInfo[val2.linkTextfirstCharacterIndex]; AttachBodyIcon(val, textArea.rectTransform, marker, value.Sprite, value.ModifierKey); } } } private static List BuildEntries() { List list = new List(); Dictionary globalModifierDefinitions = CreatureLevelManager.GetGlobalModifierDefinitions(); if (globalModifierDefinitions.Count == 0) { return list; } foreach (string knownModifierKey in CreatureModifierManager.GetKnownModifierKeys()) { if (globalModifierDefinitions.TryGetValue(knownModifierKey, out var value) && CreatureModifierManager.TryGetModifierSprite(knownModifierKey, out Sprite sprite)) { list.Add(new CompendiumModifierEntry(knownModifierKey, CreatureModifierManager.GetModifierGroupHeading(knownModifierKey), CreatureModifierManager.GetModifierDisplayName(knownModifierKey), CreatureModifierManager.GetModifierCompendiumText(knownModifierKey, value), sprite)); } } return list; } private static string BuildPageText(List entries) { StringBuilder stringBuilder = new StringBuilder(); string a = string.Empty; foreach (CompendiumModifierEntry entry in entries) { if (!string.Equals(a, entry.GroupHeading, StringComparison.Ordinal)) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append("").Append(entry.GroupHeading).Append("\n\n"); a = entry.GroupHeading; } stringBuilder.Append("") .Append('') .Append(" ") .Append(" ") .Append("") .Append(entry.Name) .Append("") .Append('\n') .Append(" ") .Append(entry.Description) .Append("\n\n"); } return stringBuilder.ToString().TrimEnd(Array.Empty()); } 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(); component.sprite = sprite; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; val.GetComponent().ignoreLayout = true; } } private static void ClearPageContentIcons(TextsDialog dialog) { if (!((Object)(object)dialog?.m_textArea == (Object)null)) { ClearIconChildren(dialog.m_textArea.transform); if ((Object)(object)dialog.m_textArea.transform.parent != (Object)null) { ClearIconChildren(dialog.m_textArea.transform.parent); } } } private static void ClearIconChildren(Transform parent) { for (int num = parent.childCount - 1; num >= 0; num--) { Transform child = parent.GetChild(num); if (((Object)child).name.StartsWith("CreatureManager_CompendiumModifierIcon_", StringComparison.Ordinal)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } } private static bool IsCreatureManagerPage(string? topic) { return string.Equals(topic, "CreatureManager", StringComparison.Ordinal); } } internal sealed class CreatureDefinition { public string? Prefab { get; set; } public bool? Enabled { get; set; } public string? ClonedFrom { get; set; } public string? Ai { get; set; } public CharacterDefinition? Character { get; set; } public HumanoidDefinition? Humanoid { get; set; } public float? Scale { get; set; } public List? Textures { get; set; } public AppearanceDefinition? Appearance { get; set; } public List? 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? Senses { get; set; } public List? IdleSound { get; set; } public List? Movement { get; set; } public List? Serpent { get; set; } public List? RandomMove { get; set; } public List? Flight { get; set; } public List? Avoid { get; set; } public List? Flee { get; set; } public List? Aggressive { get; set; } public List? Messages { get; set; } } internal sealed class MonsterAiDefinition { public float? AlertRange { get; set; } public List? Hunt { get; set; } public List? Chase { get; set; } public List? Circle { get; set; } public List? HurtFlee { get; set; } public List? Charge { get; set; } public List? 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? Attack { get; set; } public List? StatusEffect { get; set; } public List? Projectile { get; set; } public List? 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? UsedByAttacks { get; set; } public ProjectileComponentDefinition? Projectile { get; set; } public SpawnAbilityDefinition? SpawnAbility { get; set; } internal bool IsEnabled => Enabled != false; } internal sealed class ProjectileComponentDefinition { private string? _spawnOnHit; [YamlIgnore] public string? SpawnOnHit { get { return _spawnOnHit; } set { _spawnOnHit = value; SpawnOnHitSpecified = true; } } [YamlIgnore] public bool SpawnOnHitSpecified { get; private set; } [YamlMember(Alias = "spawnOnHit")] public ExplicitNullableYamlString? SerializedSpawnOnHit { get { if (!SpawnOnHitSpecified) { return null; } return new ExplicitNullableYamlString(_spawnOnHit); } set { _spawnOnHit = value?.Value; SpawnOnHitSpecified = true; } } internal bool HasSpecifiedFields => SpawnOnHitSpecified; } internal sealed class SpawnAbilityDefinition { private List? _spawnPrefabs; public List? 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? Friendly { get; set; } public List? AggravatedFriendly { get; set; } public List? AlertedFriendly { get; set; } } internal static class CreatureModifierCatalog { internal static readonly IReadOnlyList Keys = Array.AsReadOnly(new string[32] { "enraged", "fire", "frost", "lightning", "spirit", "armorPiercing", "staggering", "undodgeable", "armored", "deathward", "regenerating", "reflection", "vortex", "adaptive", "unflinching", "chameleon", "exposed", "weakened", "withered", "crippling", "disruptive", "adrenalineDrain", "corrosive", "toxicDeath", "swift", "attackSpeed", "vampiric", "reaping", "blink", "omen", "juggernaut", "blamer" }); private static readonly HashSet KeySet = new HashSet(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? _modifiers; public string? Target { get; set; } public string? Biome { get; set; } public List? Prefabs { get; set; } public List? Level { get; set; } public float? Damage { get; set; } public float? DamagePerLevel { get; set; } public float? Health { get; set; } public float? HealthPerLevel { get; set; } public float? ScalePerLevel { get; set; } public List? DistanceScaling { get; set; } public List? ModifierDistanceScaling { get; set; } public Dictionary? Modifiers { get { return _modifiers; } set { _modifiers = ((value == null) ? null : new Dictionary(value, StringComparer.OrdinalIgnoreCase)); } } public bool ModifiersCleared { get; set; } public bool IsPreset { get; set; } } internal sealed class ModifierDefinition { public float? Chance { get; set; } public float? Power { get; set; } public float? Cooldown { get; set; } public int? MaxActivations { get; set; } public float? MaxRange { get; set; } public string? StartEffect { get; set; } public float? ProcChance { get; set; } public float? Duration { get; set; } public float? SecondaryPower { get; set; } public float? Radius { get; set; } public string? TriggerEffect { get; set; } public float? MaxKarmaGain { get; set; } public float? FleeHealthRatio { get; set; } public int? ReapingHealMaxActivations { get; set; } public float? ReapingMaxHealthPerKill { get; set; } public float? ReapingMaxHealthCap { get; set; } public float? ReapingDamagePerKill { get; set; } public float? ReapingDamageCap { get; set; } public float? ReapingScalePerKill { get; set; } public float? ReapingScaleCap { get; set; } internal ModifierDefinition Clone() { ModifierDefinition modifierDefinition = new ModifierDefinition(); modifierDefinition.OverlayFrom(this); return modifierDefinition; } internal void OverlayFrom(ModifierDefinition source) { if (source.Chance.HasValue) { Chance = source.Chance; } if (source.Power.HasValue) { Power = source.Power; } if (source.Cooldown.HasValue) { Cooldown = source.Cooldown; } if (source.MaxActivations.HasValue) { MaxActivations = source.MaxActivations; } if (source.MaxRange.HasValue) { MaxRange = source.MaxRange; } if (source.StartEffect != null) { StartEffect = source.StartEffect; } if (source.ProcChance.HasValue) { ProcChance = source.ProcChance; } if (source.Duration.HasValue) { Duration = source.Duration; } if (source.SecondaryPower.HasValue) { SecondaryPower = source.SecondaryPower; } if (source.Radius.HasValue) { Radius = source.Radius; } if (source.TriggerEffect != null) { TriggerEffect = source.TriggerEffect; } if (source.MaxKarmaGain.HasValue) { MaxKarmaGain = source.MaxKarmaGain; } if (source.FleeHealthRatio.HasValue) { FleeHealthRatio = source.FleeHealthRatio; } if (source.ReapingHealMaxActivations.HasValue) { ReapingHealMaxActivations = source.ReapingHealMaxActivations; } if (source.ReapingMaxHealthPerKill.HasValue) { ReapingMaxHealthPerKill = source.ReapingMaxHealthPerKill; } if (source.ReapingMaxHealthCap.HasValue) { ReapingMaxHealthCap = source.ReapingMaxHealthCap; } if (source.ReapingDamagePerKill.HasValue) { ReapingDamagePerKill = source.ReapingDamagePerKill; } if (source.ReapingDamageCap.HasValue) { ReapingDamageCap = source.ReapingDamageCap; } if (source.ReapingScalePerKill.HasValue) { ReapingScalePerKill = source.ReapingScalePerKill; } if (source.ReapingScaleCap.HasValue) { ReapingScaleCap = source.ReapingScaleCap; } } } internal sealed class ModifierChanceDefinition { private readonly Dictionary _values = new Dictionary(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 _values = new Dictionary(StringComparer.OrdinalIgnoreCase); public float? DeathwardCooldown { get; set; } public int? DeathwardMaxActivations { get; set; } public float? ReflectionProcChance { get; set; } public float? ExposedProcChance { get; set; } public float? ExposedDuration { get; set; } public float? WeakenedProcChance { get; set; } public float? WeakenedDuration { get; set; } public float? WitheredProcChance { get; set; } public float? WitheredDuration { get; set; } public float? CripplingJump { get; set; } public float? CripplingProcChance { get; set; } public float? CripplingDuration { get; set; } public float? DisruptiveEitr { get; set; } public float? DisruptiveProcChance { get; set; } public float? DisruptiveDuration { get; set; } public float? AdrenalineDrainGainReduction { get; set; } public float? AdrenalineDrainProcChance { get; set; } public float? AdrenalineDrainDuration { get; set; } public float? CorrosiveProcChance { get; set; } public float? CorrosiveDuration { get; set; } public float? ToxicDeathRadius { get; set; } public string? ToxicDeathTriggerEffect { get; set; } public int? ReapingHealMaxActivations { get; set; } public float? ReapingMaxHealthPerKill { get; set; } public float? ReapingMaxHealthCap { get; set; } public float? ReapingDamagePerKill { get; set; } public float? ReapingDamageCap { get; set; } public float? ReapingScalePerKill { get; set; } public float? ReapingScaleCap { get; set; } public float? BlinkCooldown { get; set; } public float? BlinkMaxRange { get; set; } public string? BlinkStartEffect { get; set; } public float? KnockbackCooldown { get; set; } public float? BlamerMaxKarmaGain { get; set; } public float? BlamerFleeHealthRatio { get; set; } internal float? Get(string modifier) { if (!_values.TryGetValue(modifier, out var value)) { return null; } return value; } internal void Set(string modifier, float power) { _values[modifier] = power; } } internal sealed class AttackDamageDefinition { public float? Damage { get; set; } public float? Blunt { get; set; } public float? Slash { get; set; } public float? Pierce { get; set; } public float? Chop { get; set; } public float? Pickaxe { get; set; } public float? Fire { get; set; } public float? Frost { get; set; } public float? Lightning { get; set; } public float? Poison { get; set; } public float? Spirit { get; set; } public float? AttackForce { get; set; } public int? ToolTier { get; set; } } internal sealed class CharacterDefinition { public string? Name { get; set; } public string? Faction { get; set; } public string? Boss { get; set; } public string? DefeatSetGlobalKey { get; set; } public string? Health { get; set; } public DamageModifiersDefinition? DamageModifiers { get; set; } public string? Speed { get; set; } public string? Jump { get; set; } public string? Swim { get; set; } public string? Flight { get; set; } } internal sealed class DamageModifiersDefinition { public string? Blunt { get; set; } public string? Slash { get; set; } public string? Pierce { get; set; } public string? Chop { get; set; } public string? Pickaxe { get; set; } public string? Fire { get; set; } public string? Frost { get; set; } public string? Lightning { get; set; } public string? Poison { get; set; } public string? Spirit { get; set; } } internal sealed class HumanoidDefinition { public List? DefaultItems { get; set; } public List? RandomWeapon { get; set; } public List? RandomArmor { get; set; } public List? RandomHair { get; set; } public List? RandomShield { get; set; } public List? RandomItems { get; set; } public List? 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 Factions = new List(); internal List Levels = new List(); internal List Ai = new List(); internal List Attacks = new List(); internal List Projectiles = new List(); internal List Creatures = new List(); internal string KarmaYaml = ""; } private sealed class SyncedYamlBundleData { public int Version { get; set; } public List? Factions { get; set; } public List? Levels { get; set; } public List? Ai { get; set; } public List? Attacks { get; set; } public List? Projectiles { get; set; } public List? Creatures { get; set; } public string? Karma { get; set; } } private sealed class DefinitionApplyCheckpoint { internal readonly Dictionary InheritedAppearances = new Dictionary(InheritedAppearanceByClone, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary RagdollSources = new Dictionary(RagdollCloneSources, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary OriginalRagdollScaleValues = new Dictionary(OriginalRagdollScales, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary InheritedRagdollScaleValues = new Dictionary(InheritedRagdollScales, StringComparer.OrdinalIgnoreCase); internal readonly Dictionary InheritedRagdollTextureValues = new Dictionary(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(string yaml, string source, out List definitions); private const long ReloadDebounceTicks = 2500000L; private const long SyncedApplyDebounceTicks = 1000000L; private const int SyncedYamlBundleVersion = 4; private const string ReferenceLogicVersion = "2026-07-21-ragdoll-visual-v2"; private const string MainTextureProperty = "_MainTex"; private const string RagdollCloneSuffix = "_CreatureManagerRagdoll"; private const string DefaultTextureResourcePrefix = "CreatureManager.defaults.textures."; private static readonly string[] DefaultTextureFileNames = new string[11] { "boar2.png", "DarkBrood.png", "DarkSpider.png", "DarkSpiderSmall.png", "goblin2.png", "PolarFenring.png", "PolarLox.png", "PolarWolf.png", "StormFenring.png", "SvartalfarMage.png", "troll2.png" }; private static readonly object Sync = new object(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull).Build(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static ConfigSync? ConfigSync; private static CustomSyncedValue? SyncedYamlBundle; private static FileSystemWatcher? Watcher; private static DateTime PendingDiskReloadTime = DateTime.MaxValue; private static DateTime PendingSyncedApplyTime = DateTime.MaxValue; private static DateTime PendingTextureRefreshTime = DateTime.MaxValue; private static bool DiskReloadPending; private static bool SyncedApplyPending; private static bool TextureRefreshPending; private static bool GameDataRefreshPending; private static bool SuppressSyncedApply; private static bool RemoteBundleReady; private static bool WatcherResetPending; private static DefinitionSnapshot ActiveSnapshot = new DefinitionSnapshot(); private static readonly Dictionary EquipmentVisualScales = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RandomHairPrefabsByCreature = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary AppearanceByCreature = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary InheritedAppearanceByClone = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary AppearanceByRagdollPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ManagedRagdollPrefabs = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RagdollCloneSources = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary OriginalRagdollScales = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RagdollScalesByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary InheritedRagdollScales = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RagdollTexturesByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary InheritedRagdollTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet AppliedRagdollTextureVisuals = new HashSet(); private static readonly HashSet SuccessfullyAppliedDefinitionTargets = new HashSet(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(configSync, "YamlBundle", "", 100); SyncedYamlBundle.ValueChanged += RequestSyncedYamlApply; configSync.SourceOfTruthChanged += OnSourceOfTruthChanged; EnsureDirectoriesAndDefaultFiles(); if (configSync.IsSourceOfTruth) { LoadDefinitionsFromDisk(assignSyncedValue: true); } else { RequestSyncedYamlApply(); } SetupWatcher(); CreatureConsoleCommands.Register(); } internal static void Dispose() { NotifyGameDataUnavailable(); if (SyncedYamlBundle != null) { SyncedYamlBundle.ValueChanged -= RequestSyncedYamlApply; SyncedYamlBundle = null; } if (ConfigSync != null) { ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged; ConfigSync = null; } DiskReloadPending = false; SyncedApplyPending = false; TextureRefreshPending = false; GameDataRefreshPending = false; PendingDiskReloadTime = DateTime.MaxValue; PendingSyncedApplyTime = DateTime.MaxValue; PendingTextureRefreshTime = DateTime.MaxValue; PendingGameDataRefreshFrame = int.MaxValue; SuppressSyncedApply = false; RemoteBundleReady = false; WatcherResetPending = false; ReferenceCaptureAttemptedForCurrentGameData = false; CapturedReferenceFiles = null; Watcher?.Dispose(); Watcher = null; lock (Sync) { ActiveSnapshot = new DefinitionSnapshot(); } CreatureTextureRegistry.Dispose(); } internal static void RequestConfigurationReload() { ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { DiskReloadPending = true; PendingDiskReloadTime = DateTime.UtcNow.AddTicks(2500000L); } } internal static void Update() { if (!DiskReloadPending && !SyncedApplyPending && !TextureRefreshPending && !GameDataRefreshPending) { return; } if (GameDataRefreshPending && Time.frameCount >= PendingGameDataRefreshFrame) { CompletePendingGameDataRefresh(); } DateTime utcNow = DateTime.UtcNow; if (DiskReloadPending && utcNow >= PendingDiskReloadTime) { DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { LoadDefinitionsFromDisk(assignSyncedValue: true); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } } if (SyncedApplyPending && utcNow >= PendingSyncedApplyTime) { SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ApplySyncedYaml(); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } if (TextureRefreshPending && !DiskReloadPending && !SyncedApplyPending && utcNow >= PendingTextureRefreshTime) { TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; ApplyDefinitionsToGameData(); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } } internal static void NotifyGameDataAvailable(bool objectDbFinalized = false) { if (HasGameDataInstances()) { GameDataReady = true; CreatureConsoleCommands.InvalidateSpawnAutocompleteOptions(); CreatureAssetOwnerCatalog.InvalidateMappings(); GameDataRefreshPending = true; PendingGameDataRefreshFrame = (objectDbFinalized ? Time.frameCount : Math.Min(PendingGameDataRefreshFrame, Time.frameCount + 1)); if (objectDbFinalized) { CompletePendingGameDataRefresh(); } } } private static void CompletePendingGameDataRefresh() { if (GameDataRefreshPending && HasGameDataInstances()) { GameDataRefreshPending = false; PendingGameDataRefreshFrame = int.MaxValue; CaptureReferenceConfigurationFilesIfNeeded(); WriteCapturedReferenceConfigurationFilesIfNeeded(); ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth && !RemoteBundleReady) { RequestSyncedYamlApply(); } ApplyDefinitionsToGameData(); } } internal static void NotifyGameDataUnavailable() { CreaturePrefabBaseline.RestoreAllAndClear(); DisposeTextureOverrides(); EquipmentVisualScales.Clear(); RandomHairPrefabsByCreature.Clear(); AppearanceByCreature.Clear(); InheritedAppearanceByClone.Clear(); AppearanceByRagdollPrefab.Clear(); ManagedRagdollPrefabs.Clear(); RagdollCloneSources.Clear(); OriginalRagdollScales.Clear(); RagdollScalesByPrefab.Clear(); InheritedRagdollScales.Clear(); RagdollTexturesByPrefab.Clear(); InheritedRagdollTextures.Clear(); AppliedRagdollTextureVisuals.Clear(); SuccessfullyAppliedDefinitionTargets.Clear(); ActiveDefinitionsApplied = false; CreaturePrefabRegistry.ResetOwnedClones(); CreatureConsoleCommands.InvalidateSpawnAutocompleteOptions(); CreatureAssetOwnerCatalog.InvalidateMappings(); GameDataReady = false; LoadedHumanoidsRefreshedForCurrentGameData = false; ReferenceCaptureAttemptedForCurrentGameData = false; CapturedReferenceFiles = null; RemoteBundleReady = false; SyncedApplyPending = false; GameDataRefreshPending = false; PendingSyncedApplyTime = DateTime.MaxValue; PendingGameDataRefreshFrame = int.MaxValue; TextureRefreshPending = false; PendingTextureRefreshTime = DateTime.MaxValue; } internal static void LoadDefinitionsFromDisk(bool assignSyncedValue) { if (GameDataRefreshPending) { RequestConfigurationReload(); return; } EnsureDirectoriesAndDefaultFiles(); if (!TryBuildDiskSnapshot(out DefinitionSnapshot snapshot) || !CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, KarmaConfigurationPath, out CreatureKarmaManager.ParsedConfiguration parsed)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because at least one local YAML domain is invalid."); return; } string serialized = null; if (assignSyncedValue) { if (!TrySerializeAndVerifyBundle(snapshot, out serialized, out DefinitionSnapshot verified)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because the synchronized YAML bundle did not round-trip."); return; } snapshot = verified; if (!CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, "local synchronized YAML bundle", out parsed)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because Karma did not survive the synchronized YAML bundle round-trip."); return; } } if (!TryBeginDefinitionClonePreparation(snapshot, "local YAML bundle", out var preparationStarted)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the complete last-known-good CreatureManager configuration because its clones could not be prepared."); return; } bool keepPreparedClones = false; string text = null; bool flag = false; try { if (serialized != null) { SuppressSyncedApply = true; try { if (SyncedYamlBundle == null) { CreatureManagerPlugin.Log.LogError((object)"Cannot publish the synchronized YAML bundle because ServerSync is not initialized."); return; } text = SyncedYamlBundle.Value; SyncedYamlBundle.AssignLocalValue(serialized); flag = true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to publish the synchronized YAML bundle; keeping the complete last-known-good configuration: " + ex.Message)); return; } finally { SuppressSyncedApply = false; } SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; } if (!TrySetActiveDefinitions(snapshot, "local YAML bundle", preparationStarted)) { if (!flag || SyncedYamlBundle == null || text == null) { return; } SuppressSyncedApply = true; try { SyncedYamlBundle.AssignLocalValue(text); return; } catch (Exception ex2) { CreatureManagerPlugin.Log.LogError((object)("Failed to restore the previous synchronized YAML bundle after the local definition transaction was rejected: " + ex2.Message)); return; } finally { SuppressSyncedApply = false; } } CreatureKarmaManager.CommitParsedConfiguration(parsed); keepPreparedClones = true; } finally { EndDefinitionClonePreparation(preparationStarted, keepPreparedClones); } } internal static bool TryWriteReferenceConfigurationFile(out string path, out string error) { path = ReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildReferenceYaml, "creature reference YAML", out error); } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { path = FullScaffoldConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildFullScaffoldYaml, "creature full scaffold YAML", out error); } internal static bool TryWriteAttackReferenceConfigurationFile(out string path, out string error) { path = AttackReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildAttackReferenceYaml, "attack reference YAML", out error); } internal static bool TryWriteAiReferenceConfigurationFile(out string path, out string error) { path = AiReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildAiReferenceYaml, "AI reference YAML", out error); } internal static bool TryWriteTextureReferenceConfigurationFile(out string path, out string error) { path = TextureReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureTextureRegistry.BuildTextureReferenceText, "texture reference", out error); } internal static bool TryWriteLevelVisualReferenceConfigurationFile(out string path, out string error) { path = LevelVisualReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildLevelVisualReferenceYaml, "level visual reference YAML", out error); } internal static bool TryWriteCreatureLoadoutReferenceConfigurationFile(out string path, out string error) { path = CreatureLoadoutReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildCreatureLoadoutReferenceText, "creature loadout reference", out error); } internal static bool TryWriteProjectileReferenceConfigurationFile(out string path, out string error) { path = ProjectileReferenceConfigurationPath; return TryWriteGeneratedFile(path, CreatureReferenceWriter.BuildProjectileReferenceYaml, "projectile reference YAML", out error); } private static bool TryWriteGeneratedFile(string path, Func buildContent, string label, out string error) { error = ""; if (!IsGameDataReady()) { error = "Creature game data is not ready yet."; return false; } try { File.WriteAllText(path, buildContent()); CreatureManagerPlugin.Log.LogInfo((object)("Wrote " + label + " to " + path + ".")); return true; } catch (Exception ex) { error = "Failed to write " + label + ": " + ex.Message; CreatureManagerPlugin.Log.LogError((object)(error + " Path: " + path)); return false; } } private static void CaptureReferenceConfigurationFilesIfNeeded() { if (ReferenceCaptureAttemptedForCurrentGameData || !IsGameDataReady()) { return; } ReferenceCaptureAttemptedForCurrentGameData = true; ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { return; } try { CreatureAssetOwnerCatalog.RefreshMappings(); string text = CreatureReferenceWriter.BuildReferenceYaml(); string text2 = CreatureReferenceWriter.BuildAiReferenceYaml(); string text3 = CreatureReferenceWriter.BuildAttackReferenceYaml(); string text4 = CreatureReferenceWriter.BuildCreatureLoadoutReferenceText(); string text5 = CreatureReferenceWriter.BuildProjectileReferenceYaml(); string text6 = CreatureTextureRegistry.BuildTextureReferenceText(); if (!(text == "[]\n") || !(text2 == "[]\n") || !(text3 == "[]\n") || !(text4 == "[]\n") || !(text5 == "[]\n") || !(text6 == "[]\n")) { string signature = ComputeStableSignature("2026-07-21-ragdoll-visual-v2\n" + text + "\n---ai---\n" + text2 + "\n---attacks---\n" + text3 + "\n---loadout---\n" + text4 + "\n---projectile---\n" + text5 + "\n---textures---\n" + text6); CapturedReferenceFiles = new ReferenceFileSnapshot(text, text2, text3, text4, text5, text6, signature); } } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to capture pristine CreatureManager reference data; automatic reference generation is disabled until game data reloads: " + ex.Message)); } } private static void WriteCapturedReferenceConfigurationFilesIfNeeded() { ReferenceFileSnapshot capturedReferenceFiles = CapturedReferenceFiles; if (capturedReferenceFiles == null) { return; } ConfigSync? configSync = ConfigSync; if ((configSync != null && !configSync.IsSourceOfTruth) || ReferenceFilesAreCurrent(capturedReferenceFiles.Signature)) { return; } try { File.WriteAllText(ReferenceConfigurationPath, capturedReferenceFiles.CreatureYaml); File.WriteAllText(AiReferenceConfigurationPath, capturedReferenceFiles.AiYaml); File.WriteAllText(AttackReferenceConfigurationPath, capturedReferenceFiles.AttackYaml); File.WriteAllText(CreatureLoadoutReferenceConfigurationPath, capturedReferenceFiles.CreatureLoadout); File.WriteAllText(ProjectileReferenceConfigurationPath, capturedReferenceFiles.ProjectileYaml); File.WriteAllText(TextureReferenceConfigurationPath, capturedReferenceFiles.TextureReference); RecordReferenceSignature(capturedReferenceFiles.Signature); CreatureManagerPlugin.Log.LogInfo((object)("Updated reference files at " + ReferenceConfigurationPath + ", " + AiReferenceConfigurationPath + ", " + AttackReferenceConfigurationPath + ", " + CreatureLoadoutReferenceConfigurationPath + ", " + ProjectileReferenceConfigurationPath + ", and " + TextureReferenceConfigurationPath + ".")); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to update CreatureManager reference files: " + ex.Message)); } } internal static bool IsGameDataReady() { if (!GameDataReady) { return HasGameDataInstances(); } return true; } internal static bool IsSynchronizedConfigurationReady() { if (ActiveDefinitionsApplied && !GameDataRefreshPending) { ConfigSync? configSync = ConfigSync; if (configSync == null || !configSync.IsSourceOfTruth || DiskReloadPending) { ConfigSync? configSync2 = ConfigSync; if (configSync2 != null && !configSync2.IsSourceOfTruth && RemoteBundleReady) { return !SyncedApplyPending; } return false; } return true; } return false; } private static bool HasGameDataInstances() { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ObjectDB.instance != (Object)null; } return false; } private static bool ReferenceFilesAreCurrent(string signature) { if (!File.Exists(ReferenceConfigurationPath) || !File.Exists(AiReferenceConfigurationPath) || !File.Exists(AttackReferenceConfigurationPath) || !File.Exists(CreatureLoadoutReferenceConfigurationPath) || !File.Exists(ProjectileReferenceConfigurationPath) || !File.Exists(TextureReferenceConfigurationPath) || !File.Exists(ReferenceStatePath)) { return false; } try { string[] array = File.ReadAllLines(ReferenceStatePath); return array.Length >= 2 && string.Equals(array[0], "2026-07-21-ragdoll-visual-v2", StringComparison.Ordinal) && string.Equals(array[1], signature, StringComparison.Ordinal); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to read reference state '" + ReferenceStatePath + "': " + ex.Message)); return false; } } private static void RecordReferenceSignature(string signature) { try { Directory.CreateDirectory(CacheDirectoryPath); File.WriteAllLines(ReferenceStatePath, new string[2] { "2026-07-21-ragdoll-visual-v2", signature }); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to write reference state '" + ReferenceStatePath + "': " + ex.Message)); } } private static string ComputeStableSignature(string value) { ulong num = 14695981039346656037uL; foreach (char c in value) { num ^= c; num *= 1099511628211L; } return num.ToString("X16", CultureInfo.InvariantCulture); } private static void OnSourceOfTruthChanged(bool isSourceOfTruth) { if (isSourceOfTruth) { RemoteBundleReady = false; SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; WriteCapturedReferenceConfigurationFilesIfNeeded(); LoadDefinitionsFromDisk(assignSyncedValue: true); return; } DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; RemoteBundleReady = false; if (WatcherResetPending) { TextureRefreshPending = true; PendingTextureRefreshTime = DateTime.UtcNow; } RequestSyncedYamlApply(); } private static void ApplySyncedYaml() { if (SyncedYamlBundle == null) { return; } if (GameDataRefreshPending) { RequestSyncedYamlApply(); return; } ConfigSync? configSync = ConfigSync; string text = ((configSync != null && configSync.IsSourceOfTruth) ? "local synchronized YAML bundle" : "server synchronized YAML bundle"); if (!TryDeserializeBundle(SyncedYamlBundle.Value, text, out DefinitionSnapshot snapshot) || !CreatureKarmaManager.TryParseYaml(snapshot.KarmaYaml, text, out CreatureKarmaManager.ParsedConfiguration parsed)) { CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because " + text + " is invalid.")); return; } if (!TryBeginDefinitionClonePreparation(snapshot, text, out var preparationStarted)) { CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because the clones from " + text + " could not be prepared.")); return; } bool remoteBundleReady = RemoteBundleReady; ConfigSync? configSync2 = ConfigSync; RemoteBundleReady = configSync2 != null && !configSync2.IsSourceOfTruth; bool keepPreparedClones = false; try { if (!TrySetActiveDefinitions(snapshot, text, preparationStarted)) { RemoteBundleReady = remoteBundleReady; return; } CreatureKarmaManager.CommitParsedConfiguration(parsed); keepPreparedClones = true; } finally { EndDefinitionClonePreparation(preparationStarted, keepPreparedClones); } } private static bool TryBuildDiskSnapshot(out DefinitionSnapshot snapshot) { snapshot = new DefinitionSnapshot(); bool num = TryLoadOverrideFiles("factions", (TryReadYaml)CreatureYaml.TryReadDefinitions, out snapshot.Factions); bool flag = TryLoadLevelDefinitions(out snapshot.Levels); bool flag2 = TryLoadOverrideFiles("ai", (TryReadYaml)CreatureYaml.TryReadDefinitions, out snapshot.Ai); bool flag3 = TryLoadOverrideFiles("attacks", (TryReadYaml)CreatureYaml.TryReadDefinitions, out snapshot.Attacks); bool flag4 = TryLoadOverrideFiles("projectile", (TryReadYaml)CreatureYaml.TryReadDefinitions, out snapshot.Projectiles); RemoveProjectileReferenceMetadata(snapshot.Projectiles); bool flag5 = TryLoadOverrideFiles("creatures", (TryReadYaml)CreatureYaml.TryReadDefinitions, out snapshot.Creatures); bool flag6 = TryReadTextFile(KarmaConfigurationPath, "Karma", out snapshot.KarmaYaml); return num && flag && flag2 && flag3 && flag4 && flag5 && flag6; } private static bool TryReadTextFile(string path, string domain, out string text) { text = ""; try { if (!File.Exists(path)) { CreatureManagerPlugin.Log.LogError((object)(domain + " configuration was not found at " + path + ".")); return false; } text = File.ReadAllText(path); return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + domain + " configuration from " + path + ": " + ex.Message)); return false; } } private static bool TrySerializeAndVerifyBundle(DefinitionSnapshot snapshot, out string serialized, out DefinitionSnapshot verified) { serialized = ""; verified = new DefinitionSnapshot(); try { SyncedYamlBundleData graph = new SyncedYamlBundleData { Version = 4, Factions = snapshot.Factions, Levels = snapshot.Levels, Ai = snapshot.Ai, Attacks = snapshot.Attacks, Projectiles = snapshot.Projectiles, Creatures = snapshot.Creatures, Karma = snapshot.KarmaYaml }; serialized = Serializer.Serialize(graph); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to serialize synchronized YAML bundle: " + ex.Message)); return false; } return TryDeserializeBundle(serialized, "local synchronized YAML bundle round-trip", out verified); } private static bool TryDeserializeBundle(string yaml, string source, out DefinitionSnapshot snapshot) { snapshot = new DefinitionSnapshot(); if (string.IsNullOrWhiteSpace(yaml)) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": the bundle is empty.")); return false; } try { CreatureYaml.ValidateUniqueMappingKeysInDocument(yaml, source); SyncedYamlBundleData syncedYamlBundleData = Deserializer.Deserialize(yaml); if (syncedYamlBundleData == null || syncedYamlBundleData.Version != 4) { string arg = ((syncedYamlBundleData == null) ? "missing" : syncedYamlBundleData.Version.ToString(CultureInfo.InvariantCulture)); CreatureManagerPlugin.Log.LogError((object)$"Failed to read {source}: expected bundle version {4} but got {arg}."); return false; } if (syncedYamlBundleData.Factions == null || syncedYamlBundleData.Levels == null || syncedYamlBundleData.Ai == null || syncedYamlBundleData.Attacks == null || syncedYamlBundleData.Projectiles == null || syncedYamlBundleData.Creatures == null || syncedYamlBundleData.Karma == null) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": all seven configuration domains must be present.")); return false; } if (!CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Factions, source + ".factions") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Levels, source + ".levels") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Ai, source + ".ai") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Attacks, source + ".attacks") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Projectiles, source + ".projectile") || !CreatureYaml.ValidateDefinitions(syncedYamlBundleData.Creatures, source + ".creatures")) { return false; } snapshot.Factions = syncedYamlBundleData.Factions; snapshot.Levels = syncedYamlBundleData.Levels; snapshot.Ai = syncedYamlBundleData.Ai; snapshot.Attacks = syncedYamlBundleData.Attacks; RemoveProjectileReferenceMetadata(syncedYamlBundleData.Projectiles); snapshot.Projectiles = syncedYamlBundleData.Projectiles; snapshot.Creatures = syncedYamlBundleData.Creatures; snapshot.KarmaYaml = syncedYamlBundleData.Karma; return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": " + ex.Message)); return false; } } private static void RequestSyncedYamlApply() { if (!SuppressSyncedApply) { SyncedApplyPending = true; PendingSyncedApplyTime = DateTime.UtcNow.AddTicks(1000000L); } } private static void RemoveProjectileReferenceMetadata(IEnumerable definitions) { foreach (ProjectileDefinition definition in definitions) { definition.UsedByAttacks = null; } } private static bool TrySetActiveDefinitions(DefinitionSnapshot snapshot, string source, bool clonePreparationStarted) { DefinitionSnapshot activeSnapshot; lock (Sync) { activeSnapshot = ActiveSnapshot; } if (!CreatureFactionManager.Load(snapshot.Factions)) { CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because the faction rules from " + source + " were rejected.")); return false; } if (!TryPreflightCreatureFactionReferences(snapshot, source)) { CreatureFactionManager.Load(activeSnapshot.Factions); return false; } bool flag = CanApplyDefinitionsToGameData(); if (flag && !ApplyDefinitionsToGameData(snapshot, clonePreparationStarted)) { bool flag2 = CreatureFactionManager.Load(activeSnapshot.Factions); bool flag3 = ApplyDefinitionsToGameData(activeSnapshot, cloneTransactionPrepared: false); ActiveDefinitionsApplied = flag2 && flag3; if (!ActiveDefinitionsApplied) { SuccessfullyAppliedDefinitionTargets.Clear(); } if (!flag2 || !flag3) { CreatureManagerPlugin.Log.LogError((object)"CreatureManager could not fully restore the previous prefab/faction state after a definition apply failed. Restart the game before editing the YAML files again."); } CreatureManagerPlugin.Log.LogWarning((object)("Keeping the complete last-known-good CreatureManager configuration because applying " + source + " to the game prefabs failed.")); return false; } ActiveDefinitionsApplied = flag; lock (Sync) { ActiveSnapshot = snapshot; } CreatureLevelManager.Load(snapshot.Levels); CreatureManagerPlugin.Log.LogInfo((object)$"Loaded {snapshot.Factions.Count} faction definition(s), {snapshot.Levels.Count} level rule definition(s), {snapshot.Ai.Count} AI definition(s), {snapshot.Attacks.Count} attack definition(s), {snapshot.Projectiles.Count} projectile definition(s), and {snapshot.Creatures.Count} creature definition(s) from {source}."); return true; } private static void ApplyDefinitionsToGameData() { DefinitionSnapshot activeSnapshot; lock (Sync) { activeSnapshot = ActiveSnapshot; } if (!CanApplyDefinitionsToGameData()) { ActiveDefinitionsApplied = false; return; } bool flag = ApplyDefinitionsToGameData(activeSnapshot, cloneTransactionPrepared: false); if (!flag && CanApplyDefinitionsToGameData()) { CreatureManagerPlugin.Log.LogWarning((object)"CreatureManager will retry the active definition snapshot once after its first prefab apply failed."); flag = ApplyDefinitionsToGameData(activeSnapshot, cloneTransactionPrepared: false); } ActiveDefinitionsApplied = flag; if (!flag) { SuccessfullyAppliedDefinitionTargets.Clear(); CreatureManagerPlugin.Log.LogError((object)"CreatureManager could not apply the active definition snapshot. Supported prefab fields were restored to their pre-CreatureManager baselines, and level/modifier application is paused until the next successful reload."); } } private static bool ApplyDefinitionsToGameData(DefinitionSnapshot snapshot, bool cloneTransactionPrepared) { if (!CanApplyDefinitionsToGameData()) { return false; } List definitions = snapshot.Ai.ToList(); List list = snapshot.Attacks.ToList(); List list2 = snapshot.Projectiles.ToList(); List list3 = snapshot.Creatures.ToList(); if (!TryBuildCloneMaterializationOrder(list2, list, list3, out List ordered)) { CreatureManagerPlugin.Log.LogError((object)"CreatureManager definitions were not applied because their clonedFrom graph is invalid."); return false; } if (!cloneTransactionPrepared) { CreaturePrefabRegistry.BeginCloneApplyPass(); } bool flag = false; bool flag2 = false; DefinitionApplyCheckpoint checkpoint = null; try { if (!cloneTransactionPrepared && !TryMaterializeDefinitionClones(ordered)) { return false; } if (!cloneTransactionPrepared && !TryPreflightDefinitionApplication(snapshot, "active definition snapshot")) { return false; } checkpoint = new DefinitionApplyCheckpoint(); CreaturePrefabBaseline.BeginApplyPass(); ClearActiveDefinitionSideState(); BeginTextureOverrideApply(); flag = true; Dictionary aiDefinitionsByName = BuildAiDefinitionLookup(definitions); foreach (ProjectileDefinition item in list2) { if (item.IsEnabled) { try { ApplyProjectileDefinition(item); } catch (Exception arg) { CreatureManagerPlugin.Log.LogError((object)$"Failed to apply projectile definition for '{item.Prefab}': {arg}"); return false; } } } foreach (AttackDefinition item2 in list) { if (item2.IsEnabled) { try { ApplyAttackDefinition(item2); } catch (Exception arg2) { CreatureManagerPlugin.Log.LogError((object)$"Failed to apply attack definition for '{item2.Prefab}': {arg2}"); return false; } } } foreach (CreatureDefinition item3 in list3) { if (item3.IsEnabled) { try { ApplyDefinition(item3, aiDefinitionsByName); } catch (Exception arg3) { CreatureManagerPlugin.Log.LogError((object)$"Failed to apply creature definition for '{item3.Prefab}': {arg3}"); return false; } } } flag2 = true; UpdateSuccessfullyAppliedDefinitionTargets(snapshot); return true; } finally { if (flag) { CompleteTextureOverrideApply(); } if (!flag2) { if (flag) { RestoreAfterFailedTemplateApply(checkpoint); } CreaturePrefabRegistry.CancelCloneApplyPass(); } else { if (!cloneTransactionPrepared) { CreaturePrefabRegistry.CompleteCloneApplyPass(); } if (!LoadedHumanoidsRefreshedForCurrentGameData) { LoadedHumanoidsRefreshedForCurrentGameData = true; try { CreatureManagerRandomHairRuntime.RefreshLoadedHumanoids(); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to initialize random hair on loaded humanoids: " + ex.Message)); } } } } } private static void ClearActiveDefinitionSideState() { ManagedRagdollPrefabs.Clear(); EquipmentVisualScales.Clear(); RandomHairPrefabsByCreature.Clear(); AppearanceByCreature.Clear(); AppearanceByRagdollPrefab.Clear(); RagdollScalesByPrefab.Clear(); RagdollTexturesByPrefab.Clear(); AppliedRagdollTextureVisuals.Clear(); } private static void RestoreAfterFailedTemplateApply(DefinitionApplyCheckpoint checkpoint) { CreaturePrefabBaseline.BeginApplyPass(); ClearActiveDefinitionSideState(); BeginTextureOverrideApply(); CompleteTextureOverrideApply(); TextureMaterialOverride value; (int, int)[] array = TextureMaterialOverrides.Keys.Where(((int RendererId, int MaterialIndex) key2) => !checkpoint.TextureOverrides.TryGetValue(key2, out value) || TextureMaterialOverrides[key2] != value).ToArray(); for (int num = 0; num < array.Length; num++) { (int, int) tuple = array[num]; int item = tuple.Item1; int item2 = tuple.Item2; (int, int) key = (item, item2); TextureMaterialOverride textureMaterialOverride = TextureMaterialOverrides[key]; RestoreTextureOverrideSlot(textureMaterialOverride); if ((Object)(object)textureMaterialOverride.Generated != (Object)null) { Object.Destroy((Object)(object)textureMaterialOverride.Generated); } TextureMaterialOverrides.Remove(key); } RestoreDictionary(InheritedAppearanceByClone, checkpoint.InheritedAppearances); RestoreDictionary(RagdollCloneSources, checkpoint.RagdollSources); RestoreDictionary(OriginalRagdollScales, checkpoint.OriginalRagdollScaleValues); RestoreDictionary(InheritedRagdollScales, checkpoint.InheritedRagdollScaleValues); RestoreDictionary(InheritedRagdollTextures, checkpoint.InheritedRagdollTextureValues); } private static void RestoreDictionary(IDictionary target, IReadOnlyDictionary checkpoint) where TKey : notnull { target.Clear(); foreach (KeyValuePair item in checkpoint) { target[item.Key] = item.Value; } } private static void UpdateSuccessfullyAppliedDefinitionTargets(DefinitionSnapshot snapshot) { SuccessfullyAppliedDefinitionTargets.Clear(); foreach (ProjectileDefinition projectile in snapshot.Projectiles) { AddSuccessfullyAppliedTarget(projectile.IsEnabled, projectile.Prefab); } foreach (AttackDefinition attack in snapshot.Attacks) { AddSuccessfullyAppliedTarget(attack.IsEnabled, attack.Prefab); } foreach (CreatureDefinition creature in snapshot.Creatures) { AddSuccessfullyAppliedTarget(creature.IsEnabled, creature.Prefab); } } private static void AddSuccessfullyAppliedTarget(bool enabled, string? prefabName) { string text = (prefabName ?? "").Trim(); if (enabled && text.Length != 0) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(text); if ((Object)(object)prefab != (Object)null && (CreaturePrefabBaseline.HasAppliedGroups(prefab) || HasActiveTextureOverride(prefab))) { SuccessfullyAppliedDefinitionTargets.Add(text); } } } private static bool HasActiveTextureOverride(GameObject prefab) { Transform root = prefab.transform; return TextureMaterialOverrides.Values.Any((TextureMaterialOverride textureOverride) => textureOverride.Active && (Object)(object)textureOverride.Renderer != (Object)null && ((Component)textureOverride.Renderer).transform.IsChildOf(root)); } private static bool TryBeginDefinitionClonePreparation(DefinitionSnapshot snapshot, string source, out bool preparationStarted) { preparationStarted = false; if (!IsGameDataReady() || GameDataRefreshPending) { return true; } if (!TryBuildCloneMaterializationOrder(snapshot.Projectiles, snapshot.Attacks, snapshot.Creatures, out List ordered)) { return false; } CreaturePrefabRegistry.BeginCloneApplyPass(); if (TryMaterializeDefinitionClones(ordered) && TryPreflightDefinitionApplication(snapshot, source)) { preparationStarted = true; return true; } CreaturePrefabRegistry.CancelCloneApplyPass(); CreatureManagerPlugin.Log.LogError((object)("CreatureManager clone preparation failed for " + source + "; no configuration state was committed.")); return false; } private static void EndDefinitionClonePreparation(bool preparationStarted, bool keepPreparedClones) { if (preparationStarted) { if (keepPreparedClones) { CreaturePrefabRegistry.CompleteCloneApplyPass(); } else { CreaturePrefabRegistry.CancelCloneApplyPass(); } } } private static bool TryBuildCloneMaterializationOrder(IEnumerable projectileDefinitions, IEnumerable attackDefinitions, IEnumerable creatureDefinitions, out List ordered) { ordered = new List(); Dictionary requests = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (ProjectileDefinition projectileDefinition in projectileDefinitions) { if (projectileDefinition.IsEnabled && !AddRequest(projectileDefinition.Prefab, projectileDefinition.ClonedFrom, "projectile")) { return false; } } foreach (AttackDefinition attackDefinition in attackDefinitions) { if (attackDefinition.IsEnabled && !AddRequest(attackDefinition.Prefab, attackDefinition.ClonedFrom, "attack")) { return false; } } foreach (CreatureDefinition creatureDefinition in creatureDefinitions) { if (creatureDefinition.IsEnabled && !AddRequest(creatureDefinition.Prefab, creatureDefinition.ClonedFrom, "creature")) { return false; } } Dictionary dictionary = requests.Keys.ToDictionary((string key) => key, (string _) => 0, StringComparer.OrdinalIgnoreCase); Dictionary> dictionary2 = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (CloneMaterializationRequest value5 in requests.Values) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(value5.Target); if ((Object)(object)prefab != (Object)null && !CreaturePrefabRegistry.IsCreatureManagerClone(prefab)) { CreatureManagerPlugin.Log.LogError((object)("Clone '" + value5.Target + "' was not created because a prefab with that name is already registered.")); return false; } string sourceName; bool flag = CreaturePrefabRegistry.TryGetCloneSource(value5.Target, out sourceName); if (flag && !string.Equals(sourceName, value5.Source, StringComparison.OrdinalIgnoreCase)) { CreatureManagerPlugin.Log.LogError((object)("CreatureManager clone '" + value5.Target + "' is already based on '" + sourceName + "' and cannot be changed to '" + value5.Source + "' at runtime. Restart the game after changing clonedFrom.")); return false; } if (!flag && SuccessfullyAppliedDefinitionTargets.Contains(value5.Source)) { CreatureManagerPlugin.Log.LogError((object)("Cannot hot-add " + value5.Domain + " clone '" + value5.Target + "' from '" + value5.Source + "' because CreatureManager has already applied overrides to that source prefab. Creating it now would copy a different source state than a clean startup. Keep the YAML change and restart the game/server to create the clone deterministically.")); return false; } if (requests.ContainsKey(value5.Source)) { dictionary[value5.Target]++; if (!dictionary2.TryGetValue(value5.Source, out var value)) { value = new List(); dictionary2[value5.Source] = value; } value.Add(value5.Target); continue; } GameObject prefab2 = CreaturePrefabRegistry.GetPrefab(value5.Source); if ((Object)(object)prefab2 == (Object)null) { CreatureManagerPlugin.Log.LogError((object)(value5.Domain + " clone source '" + value5.Source + "' for '" + value5.Target + "' was not found.")); return false; } if (CreaturePrefabRegistry.IsCreatureManagerClone(prefab2)) { CreatureManagerPlugin.Log.LogError((object)("Clone '" + value5.Target + "' depends on stale CreatureManager clone '" + value5.Source + "', which is not defined in the current YAML bundle.")); return false; } if (!CreaturePrefabRegistry.IsPlayerPrefab(prefab2)) { continue; } CreatureManagerPlugin.Log.LogError((object)("Clone source '" + value5.Source + "' for '" + value5.Target + "' is a Player prefab and is not managed by CreatureManager.")); return false; } SortedSet sortedSet = new SortedSet(from pair in dictionary where pair.Value == 0 select pair.Key, StringComparer.OrdinalIgnoreCase); while (sortedSet.Count > 0) { string min = sortedSet.Min; sortedSet.Remove(min); ordered.Add(requests[min]); if (!dictionary2.TryGetValue(min, out var value2)) { continue; } foreach (string item in value2.OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase)) { if (--dictionary[item] == 0) { sortedSet.Add(item); } } } if (ordered.Count == requests.Count) { foreach (CloneMaterializationRequest item2 in ordered) { string source = item2.Source; CloneMaterializationRequest value3; while (requests.TryGetValue(source, out value3)) { source = value3.Source; } GameObject prefab3 = CreaturePrefabRegistry.GetPrefab(source); if ((Object)(object)prefab3 == (Object)null) { CreatureManagerPlugin.Log.LogError((object)("Clone source prototype '" + source + "' for '" + item2.Target + "' was not found.")); ordered.Clear(); return false; } if (!CreaturePrefabRegistry.TryValidateCloneRegistration(prefab3, item2.Target, out string error)) { CreatureManagerPlugin.Log.LogError((object)error); ordered.Clear(); return false; } } return true; } string text = string.Join(", ", (from pair in dictionary where pair.Value > 0 select pair.Key).OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase)); CreatureManagerPlugin.Log.LogError((object)("CreatureManager clonedFrom graph contains a cycle involving: " + text + ".")); ordered.Clear(); return false; bool AddRequest(string? targetValue, string? sourceValue, string domain) { string text2 = (targetValue ?? "").Trim(); string text3 = (sourceValue ?? "").Trim(); if (text2.Length == 0 || text3.Length == 0) { return true; } if (requests.TryGetValue(text2, out CloneMaterializationRequest value4)) { if (string.Equals(value4.Source, text3, StringComparison.OrdinalIgnoreCase)) { return true; } CreatureManagerPlugin.Log.LogError((object)("Clone '" + text2 + "' has conflicting clonedFrom sources '" + value4.Source + "' (" + value4.Domain + ") and '" + text3 + "' (" + domain + ").")); return false; } requests[text2] = new CloneMaterializationRequest(text2, text3, domain); return true; } } private static bool TryMaterializeDefinitionClones(IEnumerable ordered) { foreach (CloneMaterializationRequest item in ordered) { try { GameObject prefab = CreaturePrefabRegistry.GetPrefab(item.Source); if ((Object)(object)prefab == (Object)null || (Object)(object)CreaturePrefabRegistry.ClonePrefab(prefab, item.Target) == (Object)null) { CreatureManagerPlugin.Log.LogError((object)("Failed to materialize " + item.Domain + " clone '" + item.Target + "' from '" + item.Source + "'. No CreatureManager prefab overrides were applied.")); return false; } } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)$"Failed to materialize {item.Domain} clone '{item.Target}' from '{item.Source}': {ex}"); return false; } } return true; } private static bool TryPreflightDefinitionApplication(DefinitionSnapshot snapshot, string source) { List list = new List(); HashSet currentCloneTargets = BuildCurrentCloneTargetSet(snapshot); Dictionary aiDefinitions = snapshot.Ai.Where((AiDefinition definition) => definition.IsEnabled && !string.IsNullOrWhiteSpace(definition.Ai)).GroupBy((AiDefinition definition) => definition.Ai.Trim(), StringComparer.OrdinalIgnoreCase).ToDictionary, string, AiDefinition>((IGrouping group) => group.Key, (IGrouping group) => group.Last(), StringComparer.OrdinalIgnoreCase); foreach (ProjectileDefinition item2 in snapshot.Projectiles.Where((ProjectileDefinition definition) => definition.IsEnabled)) { string text = (item2.Prefab ?? "").Trim(); GameObject prefab = CreaturePrefabRegistry.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { list.Add("projectile '" + text + "' target prefab was not found"); continue; } if (IsStaleManagedClone(prefab, currentCloneTargets)) { list.Add("projectile '" + text + "' resolves to a stale CreatureManager clone that is not defined by the current YAML bundle"); continue; } ProjectileComponentDefinition? projectile = item2.Projectile; if (projectile != null && projectile.SpawnOnHitSpecified) { if ((Object)(object)prefab.GetComponent() == (Object)null) { list.Add("projectile '" + text + "' sets projectile.spawnOnHit but has no root Projectile component"); } string text2 = item2.Projectile.SpawnOnHit?.Trim(); if (text2 != null) { GameObject prefab2 = CreaturePrefabRegistry.GetPrefab(text2); if ((Object)(object)prefab2 == (Object)null) { list.Add("projectile '" + text + "' references missing projectile.spawnOnHit prefab '" + text2 + "'"); } else if (IsStaleManagedClone(prefab2, currentCloneTargets)) { list.Add("projectile '" + text + "' references stale projectile.spawnOnHit clone '" + text2 + "'"); } } } if (item2.SpawnAbility?.SpawnPrefabs == null) { continue; } if ((Object)(object)prefab.GetComponent() == (Object)null) { list.Add("projectile '" + text + "' sets spawnAbility.spawnPrefabs but has no root SpawnAbility component"); } if (!CreatureYaml.TryParseSpawnPrefabEntries(item2.SpawnAbility.SpawnPrefabs, out List<(string, int)> entries, out string error)) { list.Add("projectile '" + text + "' has invalid spawnAbility.spawnPrefabs: " + error); continue; } foreach (var item3 in entries) { string item = item3.Item1; GameObject prefab3 = CreaturePrefabRegistry.GetPrefab(item); if ((Object)(object)prefab3 == (Object)null) { list.Add("projectile '" + text + "' references missing spawnAbility prefab '" + item + "'"); } else if (IsStaleManagedClone(prefab3, currentCloneTargets)) { list.Add("projectile '" + text + "' references stale spawnAbility clone '" + item + "'"); } } } foreach (AttackDefinition item4 in snapshot.Attacks.Where((AttackDefinition definition) => definition.IsEnabled)) { string text3 = (item4.Prefab ?? "").Trim(); GameObject prefab4 = CreaturePrefabRegistry.GetPrefab(text3); ItemDrop val = (((Object)(object)prefab4 != (Object)null) ? prefab4.GetComponent() : null); if ((Object)(object)prefab4 == (Object)null) { list.Add("attack '" + text3 + "' target prefab was not found"); continue; } if (IsStaleManagedClone(prefab4, currentCloneTargets)) { list.Add("attack '" + text3 + "' resolves to a stale CreatureManager clone that is not defined by the current YAML bundle"); continue; } if ((Object)(object)val == (Object)null) { list.Add("attack '" + text3 + "' has no ItemDrop component"); continue; } if (val.m_itemData?.m_shared == null) { list.Add("attack '" + text3 + "' has no initialized ItemDrop shared data"); continue; } string[] array = ((item4.Projectile != null) ? CleanTuple(item4.Projectile) : Array.Empty()); if (array.Length != 0) { GameObject prefab5 = CreaturePrefabRegistry.GetPrefab(array[0]); if ((Object)(object)prefab5 == (Object)null) { list.Add("attack '" + text3 + "' references missing projectile prefab '" + array[0] + "'"); } else if (IsStaleManagedClone(prefab5, currentCloneTargets)) { list.Add("attack '" + text3 + "' references stale projectile clone '" + array[0] + "'"); } } string[] array2 = ((item4.StatusEffect != null) ? CleanTuple(item4.StatusEffect) : Array.Empty()); if (array2.Length != 0 && (Object)(object)ResolveAttackStatusEffect(array2[0]) == (Object)null) { list.Add("attack '" + text3 + "' references missing status effect '" + array2[0] + "'"); } } foreach (CreatureDefinition item5 in snapshot.Creatures.Where((CreatureDefinition definition) => definition.IsEnabled)) { string text4 = (item5.Prefab ?? "").Trim(); GameObject prefab6 = CreaturePrefabRegistry.GetPrefab(text4); if ((Object)(object)prefab6 == (Object)null) { list.Add("creature '" + text4 + "' target prefab was not found"); continue; } if (IsStaleManagedClone(prefab6, currentCloneTargets)) { list.Add("creature '" + text4 + "' resolves to a stale CreatureManager clone that is not defined by the current YAML bundle"); continue; } if (CreaturePrefabRegistry.IsPlayerPrefab(prefab6)) { list.Add("creature '" + text4 + "' resolves to the Player prefab, which CreatureManager does not manage"); continue; } if (item5.Character != null && (Object)(object)prefab6.GetComponent() == (Object)null) { list.Add("creature '" + text4 + "' has a character block but no Character component"); } if (item5.Humanoid != null) { if ((Object)(object)prefab6.GetComponent() == (Object)null) { list.Add("creature '" + text4 + "' has a humanoid block but no Humanoid component"); } else { PreflightHumanoidReferences(text4, item5.Humanoid, currentCloneTargets, list); } } AppearanceDefinition? appearance = item5.Appearance; if (appearance != null && appearance.HasSpecifiedFields) { if ((item5.Appearance.Hair != null || item5.Appearance.Beard != null) && (Object)(object)prefab6.GetComponent() == (Object)null) { list.Add("creature '" + text4 + "' sets appearance hair/beard but has no Humanoid component"); } if ((item5.Appearance.HairColor != null || item5.Appearance.SkinColor != null || item5.Appearance.ModelIndex.HasValue) && (Object)(object)prefab6.GetComponent() == (Object)null) { list.Add("creature '" + text4 + "' sets visual appearance fields but has no VisEquipment component"); } PreflightAppearanceItemReference(text4, item5.Appearance.Hair, "hair", currentCloneTargets, list); PreflightAppearanceItemReference(text4, item5.Appearance.Beard, "beard", currentCloneTargets, list); } string text5 = (item5.Ai ?? "").Trim(); if (text5.Length > 0) { PreflightCreatureAiReference(prefab6, text5, aiDefinitions, currentCloneTargets, list); } } if (list.Count == 0) { return true; } foreach (string item6 in list) { CreatureManagerPlugin.Log.LogError((object)("Definition preflight failed: " + item6 + ".")); } CreatureManagerPlugin.Log.LogWarning((object)$"CreatureManager rejected {source} before publishing prefab changes because {list.Count} required component/reference validation error(s) were found."); return false; } private static void PreflightAppearanceItemReference(string creatureName, string? itemName, string field, ISet currentCloneTargets, ICollection errors) { string text = (itemName ?? "").Trim(); if (text.Length != 0) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(text); if ((Object)(object)prefab != (Object)null && IsStaleManagedClone(prefab, currentCloneTargets)) { errors.Add("creature '" + creatureName + "' appearance." + field + " references stale item clone '" + text + "'"); } } } private static HashSet BuildCurrentCloneTargetSet(DefinitionSnapshot snapshot) { return (from entry in snapshot.Projectiles.Select((ProjectileDefinition definition) => (IsEnabled: definition.IsEnabled, Prefab: definition.Prefab, ClonedFrom: definition.ClonedFrom)).Concat(snapshot.Attacks.Select((AttackDefinition definition) => (IsEnabled: definition.IsEnabled, Prefab: definition.Prefab, ClonedFrom: definition.ClonedFrom))).Concat(snapshot.Creatures.Select((CreatureDefinition definition) => (IsEnabled: definition.IsEnabled, Prefab: definition.Prefab, ClonedFrom: definition.ClonedFrom))) where entry.IsEnabled && !string.IsNullOrWhiteSpace(entry.Prefab) && !string.IsNullOrWhiteSpace(entry.ClonedFrom) select entry.Prefab.Trim()).ToHashSet(StringComparer.OrdinalIgnoreCase); } private static bool IsStaleManagedClone(GameObject prefab, ISet currentCloneTargets) { if (CreaturePrefabRegistry.IsCreatureManagerClone(prefab)) { return !currentCloneTargets.Contains(((Object)prefab).name); } return false; } private static bool TryPreflightCreatureFactionReferences(DefinitionSnapshot snapshot, string source) { Faction faction; List list = (from definition in snapshot.Creatures where definition.IsEnabled && definition.Character?.Faction != null select new { Prefab = (definition.Prefab ?? "").Trim(), Faction = definition.Character.Faction } into reference where !CreatureFactionManager.TryGetFaction(reference.Faction, out faction) select "creature '" + reference.Prefab + "' references unknown faction '" + reference.Faction + "'").ToList(); if (list.Count == 0) { return true; } foreach (string item in list) { CreatureManagerPlugin.Log.LogError((object)("Definition preflight failed: " + item + ".")); } CreatureManagerPlugin.Log.LogWarning((object)$"CreatureManager rejected {source} because {list.Count} creature faction reference(s) could not be resolved."); return false; } private static void PreflightHumanoidReferences(string creatureName, HumanoidDefinition definition, ISet currentCloneTargets, ICollection errors) { ValidateSimpleItemList(definition.DefaultItems, "defaultItems"); ValidateSimpleItemList(definition.RandomWeapon, "randomWeapon"); ValidateSimpleItemList(definition.RandomArmor, "randomArmor"); ValidateSimpleItemList(definition.RandomHair, "randomHair"); ValidateSimpleItemList(definition.RandomShield, "randomShield"); if (definition.RandomItems != null) { foreach (string randomItem in definition.RandomItems) { if (!CreatureYaml.TryParseRandomItemTuple(randomItem, out string prefabName, out float _, out string error)) { errors.Add("creature '" + creatureName + "' humanoid.randomItems entry is invalid: " + error); } else { ValidateItem(prefabName, "randomItems"); } } } if (definition.RandomSets == null) { return; } foreach (string randomSet in definition.RandomSets) { if (!CreatureYaml.TryParseRandomSetTuple(randomSet, out string _, out string[] itemNames, out string error2)) { errors.Add("creature '" + creatureName + "' humanoid.randomSets entry is invalid: " + error2); continue; } string[] array = itemNames; for (int i = 0; i < array.Length; i++) { ValidateItem(array[i], "randomSets"); } } void ValidateItem(string itemName, string field) { string text = itemName.Trim(); if (text.Length != 0) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { errors.Add("creature '" + creatureName + "' humanoid." + field + " references missing item prefab '" + text + "'"); } else if (IsStaleManagedClone(prefab, currentCloneTargets)) { errors.Add("creature '" + creatureName + "' humanoid." + field + " references stale item clone '" + text + "'"); } else if ((Object)(object)prefab.GetComponent() == (Object)null) { errors.Add("creature '" + creatureName + "' humanoid." + field + " prefab '" + text + "' has no ItemDrop component"); } } } void ValidateSimpleItemList(IEnumerable? enumerable, string field) { if (enumerable == null) { return; } foreach (string item in enumerable) { ValidateItem(item, field); } } } private static void PreflightCreatureAiReference(GameObject target, string aiName, IReadOnlyDictionary aiDefinitions, ISet currentCloneTargets, ICollection errors) { bool flag = (Object)(object)target.GetComponent() != (Object)null; bool flag2 = (Object)(object)target.GetComponent() != (Object)null; if (!flag && !flag2) { errors.Add("creature '" + ((Object)target).name + "' references AI '" + aiName + "' but has no MonsterAI or AnimalAI component"); return; } if (!aiDefinitions.TryGetValue(aiName, out AiDefinition value)) { ValidateAiPrefab(aiName, "creature '" + ((Object)target).name + "' AI", flag, flag2); return; } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); while (value != null) { string text = (value.Ai ?? "").Trim(); if (!hashSet.Add(text)) { errors.Add("creature '" + ((Object)target).name + "' AI preset '" + aiName + "' has a copyFrom cycle at '" + text + "'"); break; } if (value.MonsterAI != null && !flag) { errors.Add("creature '" + ((Object)target).name + "' uses MonsterAI preset '" + text + "' but has no MonsterAI component"); break; } string text2 = (value.ClonedFrom ?? "").Trim(); if (text2.Length > 0) { ValidateAiPrefab(text2, "AI preset '" + text + "' clonedFrom", flag, flag2 && value.MonsterAI == null); } string text3 = (value.CopyFrom ?? "").Trim(); if (text3.Length == 0) { break; } if (!aiDefinitions.TryGetValue(text3, out AiDefinition value2)) { ValidateAiPrefab(text3, "AI preset '" + text + "' copyFrom", flag, flag2 && value.MonsterAI == null); break; } value = value2; } void ValidateAiPrefab(string prefabName, string context, bool requireMonster, bool requireAnimal) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)(context + " prefab '" + prefabName + "' was not found; omitted AI fields keep the target prefab's current values.")); } else if (IsStaleManagedClone(prefab, currentCloneTargets)) { errors.Add(context + " prefab '" + prefabName + "' is a stale CreatureManager clone not defined by the current YAML bundle"); } else if (requireMonster && (Object)(object)prefab.GetComponent() == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)(context + " prefab '" + prefabName + "' has no MonsterAI component; omitted MonsterAI fields keep the target prefab's current values.")); } else if (!requireMonster && requireAnimal && (Object)(object)prefab.GetComponent() == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)(context + " prefab '" + prefabName + "' has no AnimalAI component; omitted AnimalAI fields keep the target prefab's current values.")); } } } private static bool CanApplyDefinitionsToGameData() { if (GameDataRefreshPending || !IsGameDataReady() || (Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer()) { return true; } ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { return RemoteBundleReady; } return false; } private static Dictionary BuildAiDefinitionLookup(IEnumerable definitions) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (AiDefinition definition in definitions) { string text = (definition.Ai ?? "").Trim(); if (definition.IsEnabled && text.Length != 0) { if (!HasAnyAiPresetContent(definition)) { CreatureManagerPlugin.Log.LogWarning((object)("AI preset '" + text + "' has no baseAI, monsterAI, clonedFrom, or copyFrom fields. Check block names and indentation.")); } dictionary[text] = definition; } } return dictionary; } private static bool HasAnyAiPresetContent(AiDefinition definition) { if (definition.BaseAI == null && definition.MonsterAI == null && string.IsNullOrWhiteSpace(definition.ClonedFrom)) { return !string.IsNullOrWhiteSpace(definition.CopyFrom); } return true; } private static void ApplyDefinition(CreatureDefinition definition, Dictionary aiDefinitionsByName) { if (string.IsNullOrWhiteSpace(definition.Prefab)) { CreatureManagerPlugin.Log.LogWarning((object)"Skipping creature definition without prefab."); return; } GameObject val = ResolveTargetPrefab(definition); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Skipping creature '" + definition.Prefab + "': prefab not found.")); return; } if (CreaturePrefabRegistry.IsPlayerPrefab(val)) { CreatureManagerPlugin.Log.LogWarning((object)("Skipping creature '" + definition.Prefab + "': Player prefabs are not managed by CreatureManager.")); return; } ApplyCharacter(val, definition.Character); ApplyAi(val, definition.Ai, aiDefinitionsByName); ApplyHumanoid(val, definition.Humanoid); ApplyVisEquipment(val, definition.Appearance, definition.ClonedFrom); ApplyVisual(val, definition.Scale, definition.Textures); } private static void ApplyAi(GameObject prefab, string? aiName, Dictionary aiDefinitionsByName) { string text = (aiName ?? "").Trim(); if (text.Length == 0) { return; } if (!aiDefinitionsByName.TryGetValue(text, out AiDefinition value)) { ApplyAiFromPrefab(prefab, text); return; } if (value.MonsterAI != null) { MonsterAI component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' references MonsterAI preset '" + text + "' but has no MonsterAI component.")); } else { ApplyMonsterAiDefinition(prefab, component, value, aiDefinitionsByName, new HashSet(StringComparer.OrdinalIgnoreCase)); } return; } MonsterAI component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { ApplyMonsterAiDefinition(prefab, component2, value, aiDefinitionsByName, new HashSet(StringComparer.OrdinalIgnoreCase)); return; } AnimalAI component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null) { ApplyAnimalAiDefinition(prefab, component3, value, aiDefinitionsByName, new HashSet(StringComparer.OrdinalIgnoreCase)); return; } CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' references AI preset '" + text + "' but has no MonsterAI or AnimalAI component.")); } private static void ApplyAiFromPrefab(GameObject targetPrefab, string sourceName) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(sourceName); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' references unknown AI preset or prefab '" + sourceName + "'.")); return; } MonsterAI component = prefab.GetComponent(); AnimalAI component2 = prefab.GetComponent(); MonsterAI component3 = targetPrefab.GetComponent(); AnimalAI component4 = targetPrefab.GetComponent(); if ((Object)(object)component != (Object)null) { if ((Object)(object)component3 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' references AI prefab '" + sourceName + "' with MonsterAI but target has no MonsterAI component.")); } else { CopySupportedMonsterAiFields(component, component3); } } else if ((Object)(object)component2 != (Object)null) { if ((Object)(object)component4 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' references AI prefab '" + sourceName + "' with AnimalAI but target has no AnimalAI component.")); } else { CopySupportedAnimalAiFields(component2, component4); } } else { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' references AI prefab '" + sourceName + "' but source has no MonsterAI or AnimalAI component.")); } } private static void ApplyMonsterAiDefinition(GameObject targetPrefab, MonsterAI target, AiDefinition definition, Dictionary aiDefinitionsByName, HashSet visited) { string text = (definition.Ai ?? "").Trim(); if (text.Length == 0) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' has an AI preset without an ai name.")); return; } if (!visited.Add(text)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' has a copyFrom cycle.")); return; } string text2 = (definition.CopyFrom ?? "").Trim(); string text3 = (definition.ClonedFrom ?? "").Trim(); if (text2.Length > 0) { if (aiDefinitionsByName.TryGetValue(text2, out AiDefinition value)) { ApplyMonsterAiDefinition(targetPrefab, target, value, aiDefinitionsByName, visited); } else if (!TryCopyMonsterAiFromPrefab(target, text2)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' copyFrom preset or prefab '" + text2 + "' was not found.")); } } else if (text3.Length == 0) { TryCopyMonsterAiFromPrefab(target, text); } if (text3.Length > 0) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(text3); MonsterAI val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' clonedFrom '" + text3 + "' was not found or has no MonsterAI component.")); } else { CopySupportedMonsterAiFields(val, target); } } ApplyBaseAiDefinition(targetPrefab, (BaseAI)(object)target, definition.BaseAI); ApplyMonsterAiDefinition(targetPrefab, target, definition.MonsterAI); } private static void ApplyAnimalAiDefinition(GameObject targetPrefab, AnimalAI target, AiDefinition definition, Dictionary aiDefinitionsByName, HashSet visited) { string text = (definition.Ai ?? "").Trim(); if (text.Length == 0) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' has an AI preset without an ai name.")); return; } if (!visited.Add(text)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' has a copyFrom cycle.")); return; } if (definition.MonsterAI != null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' has a monsterAI block but is being applied as AnimalAI.")); return; } string text2 = (definition.CopyFrom ?? "").Trim(); string text3 = (definition.ClonedFrom ?? "").Trim(); if (text2.Length > 0) { if (aiDefinitionsByName.TryGetValue(text2, out AiDefinition value)) { ApplyAnimalAiDefinition(targetPrefab, target, value, aiDefinitionsByName, visited); } else if (!TryCopyAnimalAiFromPrefab(target, text2)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' copyFrom preset or prefab '" + text2 + "' was not found.")); } } else if (text3.Length == 0) { TryCopyAnimalAiFromPrefab(target, text); } if (text3.Length > 0) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(text3); AnimalAI val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)targetPrefab).name + "' AI preset '" + text + "' clonedFrom '" + text3 + "' was not found or has no AnimalAI component.")); } else { CopySupportedAnimalAiFields(val, target); } } ApplyBaseAiDefinition(targetPrefab, (BaseAI)(object)target, definition.BaseAI); } private static void CopySupportedMonsterAiFields(MonsterAI source, MonsterAI target) { CreaturePrefabBaseline.Capture(((Component)target).gameObject, CreaturePrefabBaselineGroup.BaseAiAll | CreaturePrefabBaselineGroup.MonsterAiAll); CopySupportedBaseAiFields((BaseAI)(object)source, (BaseAI)(object)target); target.m_alertRange = source.m_alertRange; target.m_fleeIfHurtWhenTargetCantBeReached = source.m_fleeIfHurtWhenTargetCantBeReached; target.m_fleeUnreachableSinceAttacking = source.m_fleeUnreachableSinceAttacking; target.m_fleeUnreachableSinceHurt = source.m_fleeUnreachableSinceHurt; target.m_fleeIfNotAlerted = source.m_fleeIfNotAlerted; target.m_fleeIfLowHealth = source.m_fleeIfLowHealth; target.m_fleeTimeSinceHurt = source.m_fleeTimeSinceHurt; target.m_fleeInLava = source.m_fleeInLava; target.m_circulateWhileCharging = source.m_circulateWhileCharging; target.m_circulateWhileChargingFlying = source.m_circulateWhileChargingFlying; target.m_enableHuntPlayer = source.m_enableHuntPlayer; target.m_attackPlayerObjects = source.m_attackPlayerObjects; target.m_privateAreaTriggerTreshold = source.m_privateAreaTriggerTreshold; target.m_interceptTimeMax = source.m_interceptTimeMax; target.m_interceptTimeMin = source.m_interceptTimeMin; target.m_maxChaseDistance = source.m_maxChaseDistance; target.m_minAttackInterval = source.m_minAttackInterval; target.m_circleTargetInterval = source.m_circleTargetInterval; target.m_circleTargetDuration = source.m_circleTargetDuration; target.m_circleTargetDistance = source.m_circleTargetDistance; target.m_sleeping = source.m_sleeping; target.m_wakeupRange = source.m_wakeupRange; target.m_noiseWakeup = source.m_noiseWakeup; target.m_maxNoiseWakeupRange = source.m_maxNoiseWakeupRange; target.m_wakeUpDelayMin = source.m_wakeUpDelayMin; target.m_wakeUpDelayMax = source.m_wakeUpDelayMax; target.m_fallAsleepDistance = source.m_fallAsleepDistance; target.m_avoidLand = source.m_avoidLand; } private static bool TryCopyMonsterAiFromPrefab(MonsterAI target, string sourceName) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(sourceName); MonsterAI val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { return false; } CopySupportedMonsterAiFields(val, target); return true; } private static bool TryCopyAnimalAiFromPrefab(AnimalAI target, string sourceName) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(sourceName); AnimalAI val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { return false; } CopySupportedAnimalAiFields(val, target); return true; } private static void CopySupportedAnimalAiFields(AnimalAI source, AnimalAI target) { CreaturePrefabBaseline.Capture(((Component)target).gameObject, CreaturePrefabBaselineGroup.BaseAiAll); CopySupportedBaseAiFields((BaseAI)(object)source, (BaseAI)(object)target); } private static void CopySupportedBaseAiFields(BaseAI sourceBase, BaseAI targetBase) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) targetBase.m_viewRange = sourceBase.m_viewRange; targetBase.m_viewAngle = sourceBase.m_viewAngle; targetBase.m_hearRange = sourceBase.m_hearRange; targetBase.m_mistVision = sourceBase.m_mistVision; targetBase.m_idleSoundInterval = sourceBase.m_idleSoundInterval; targetBase.m_idleSoundChance = sourceBase.m_idleSoundChance; targetBase.m_pathAgentType = sourceBase.m_pathAgentType; targetBase.m_moveMinAngle = sourceBase.m_moveMinAngle; targetBase.m_smoothMovement = sourceBase.m_smoothMovement; targetBase.m_serpentMovement = sourceBase.m_serpentMovement; targetBase.m_serpentTurnRadius = sourceBase.m_serpentTurnRadius; targetBase.m_jumpInterval = sourceBase.m_jumpInterval; targetBase.m_randomCircleInterval = sourceBase.m_randomCircleInterval; targetBase.m_randomMoveInterval = sourceBase.m_randomMoveInterval; targetBase.m_randomMoveRange = sourceBase.m_randomMoveRange; targetBase.m_randomFly = sourceBase.m_randomFly; targetBase.m_chanceToTakeoff = sourceBase.m_chanceToTakeoff; targetBase.m_chanceToLand = sourceBase.m_chanceToLand; targetBase.m_groundDuration = sourceBase.m_groundDuration; targetBase.m_airDuration = sourceBase.m_airDuration; targetBase.m_maxLandAltitude = sourceBase.m_maxLandAltitude; targetBase.m_takeoffTime = sourceBase.m_takeoffTime; targetBase.m_flyAltitudeMin = sourceBase.m_flyAltitudeMin; targetBase.m_flyAltitudeMax = sourceBase.m_flyAltitudeMax; targetBase.m_flyAbsMinAltitude = sourceBase.m_flyAbsMinAltitude; targetBase.m_avoidFire = sourceBase.m_avoidFire; targetBase.m_afraidOfFire = sourceBase.m_afraidOfFire; targetBase.m_avoidWater = sourceBase.m_avoidWater; targetBase.m_avoidLava = sourceBase.m_avoidLava; targetBase.m_skipLavaTargets = sourceBase.m_skipLavaTargets; targetBase.m_avoidLavaFlee = sourceBase.m_avoidLavaFlee; targetBase.m_aggravatable = sourceBase.m_aggravatable; targetBase.m_passiveAggresive = sourceBase.m_passiveAggresive; targetBase.m_spawnMessage = sourceBase.m_spawnMessage; targetBase.m_deathMessage = sourceBase.m_deathMessage; targetBase.m_alertedMessage = sourceBase.m_alertedMessage; targetBase.m_fleeRange = sourceBase.m_fleeRange; targetBase.m_fleeAngle = sourceBase.m_fleeAngle; targetBase.m_fleeInterval = sourceBase.m_fleeInterval; targetBase.m_patrol = sourceBase.m_patrol; } private static void ApplyBaseAiDefinition(GameObject prefab, BaseAI ai, BaseAiDefinition? definition) { if (definition == null) { return; } CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (definition.Senses != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiSenses; } if (definition.IdleSound != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiIdleSound; } if (definition.Movement != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiMovement; } if (definition.Serpent != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiSerpent; } if (definition.RandomMove != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiRandomMove; } if (definition.Flight != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiFlight; } if (definition.Avoid != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiAvoid; } if (definition.Flee != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiFlee; } if (definition.Aggressive != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiAggressive; } if (definition.Messages != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.BaseAiMessages; } CreaturePrefabBaseline.Capture(prefab, creaturePrefabBaselineGroup); if (TryGetStringTuple(definition.Senses, 4, ((Object)prefab).name, "base.senses", out string[] values)) { if (TryParseFloat(values[0], ((Object)prefab).name, "base.senses viewRange", out var value)) { ai.m_viewRange = value; } if (TryParseFloat(values[1], ((Object)prefab).name, "base.senses viewAngle", out var value2)) { ai.m_viewAngle = value2; } if (TryParseFloat(values[2], ((Object)prefab).name, "base.senses hearRange", out var value3)) { ai.m_hearRange = value3; } if (TryParseBool(values[3], ((Object)prefab).name, "base.senses mistVision", out var value4)) { ai.m_mistVision = value4; } } if (TryGetFloatTuple(definition.IdleSound, 2, ((Object)prefab).name, "base.idleSound", out float[] values2)) { ai.m_idleSoundInterval = values2[0]; ai.m_idleSoundChance = values2[1]; } if (TryGetStringTuple(definition.Movement, 4, ((Object)prefab).name, "base.movement", out string[] values3)) { if (TryParseBool(values3[0], ((Object)prefab).name, "base.movement patrol", out var value5)) { ai.m_patrol = value5; } if (!TrySetEnumField(ai, "m_pathAgentType", values3[1])) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' base.movement pathAgentType '" + values3[1] + "' is invalid.")); } if (TryParseFloat(values3[2], ((Object)prefab).name, "base.movement moveMinAngle", out var value6)) { ai.m_moveMinAngle = value6; } if (TryParseBool(values3[3], ((Object)prefab).name, "base.movement smoothMovement", out var value7)) { ai.m_smoothMovement = value7; } } if (TryGetStringTuple(definition.Serpent, 2, ((Object)prefab).name, "base.serpent", out string[] values4)) { if (TryParseBool(values4[0], ((Object)prefab).name, "base.serpent serpentMovement", out var value8)) { ai.m_serpentMovement = value8; } if (TryParseFloat(values4[1], ((Object)prefab).name, "base.serpent serpentTurnRadius", out var value9)) { ai.m_serpentTurnRadius = value9; } } if (TryGetFloatTuple(definition.RandomMove, 4, ((Object)prefab).name, "base.randomMove", out float[] values5)) { ai.m_jumpInterval = values5[0]; ai.m_randomCircleInterval = values5[1]; ai.m_randomMoveInterval = values5[2]; ai.m_randomMoveRange = values5[3]; } if (TryGetStringTuple(definition.Flight, 10, ((Object)prefab).name, "base.flight", out string[] values6)) { if (TryParseBool(values6[0], ((Object)prefab).name, "base.flight randomFly", out var value10)) { ai.m_randomFly = value10; } if (TryParseFloat(values6[1], ((Object)prefab).name, "base.flight chanceToTakeoff", out var value11)) { ai.m_chanceToTakeoff = value11; } if (TryParseFloat(values6[2], ((Object)prefab).name, "base.flight chanceToLand", out var value12)) { ai.m_chanceToLand = value12; } if (TryParseFloat(values6[3], ((Object)prefab).name, "base.flight groundDuration", out var value13)) { ai.m_groundDuration = value13; } if (TryParseFloat(values6[4], ((Object)prefab).name, "base.flight airDuration", out var value14)) { ai.m_airDuration = value14; } if (TryParseFloat(values6[5], ((Object)prefab).name, "base.flight maxLandAltitude", out var value15)) { ai.m_maxLandAltitude = value15; } if (TryParseFloat(values6[6], ((Object)prefab).name, "base.flight takeoffTime", out var value16)) { ai.m_takeoffTime = value16; } if (TryParseFloat(values6[7], ((Object)prefab).name, "base.flight flyAltitudeMin", out var value17)) { ai.m_flyAltitudeMin = value17; } if (TryParseFloat(values6[8], ((Object)prefab).name, "base.flight flyAltitudeMax", out var value18)) { ai.m_flyAltitudeMax = value18; } if (TryParseFloat(values6[9], ((Object)prefab).name, "base.flight flyAbsMinAltitude", out var value19)) { ai.m_flyAbsMinAltitude = value19; } } if (TryGetStringTuple(definition.Avoid, 6, ((Object)prefab).name, "base.avoid", out string[] values7)) { if (TryParseBool(values7[0], ((Object)prefab).name, "base.avoid avoidFire", out var value20)) { ai.m_avoidFire = value20; } if (TryParseBool(values7[1], ((Object)prefab).name, "base.avoid afraidOfFire", out var value21)) { ai.m_afraidOfFire = value21; } if (TryParseBool(values7[2], ((Object)prefab).name, "base.avoid avoidWater", out var value22)) { ai.m_avoidWater = value22; } if (TryParseBool(values7[3], ((Object)prefab).name, "base.avoid avoidLava", out var value23)) { ai.m_avoidLava = value23; } if (TryParseBool(values7[4], ((Object)prefab).name, "base.avoid skipLavaTargets", out var value24)) { ai.m_skipLavaTargets = value24; } if (TryParseBool(values7[5], ((Object)prefab).name, "base.avoid avoidLavaFlee", out var value25)) { ai.m_avoidLavaFlee = value25; } } if (TryGetFloatTuple(definition.Flee, 3, ((Object)prefab).name, "base.flee", out float[] values8)) { ai.m_fleeRange = values8[0]; ai.m_fleeAngle = values8[1]; ai.m_fleeInterval = values8[2]; } if (TryGetStringTuple(definition.Aggressive, 2, ((Object)prefab).name, "base.aggressive", out string[] values9)) { if (TryParseBool(values9[0], ((Object)prefab).name, "base.aggressive aggravatable", out var value26)) { ai.m_aggravatable = value26; } if (TryParseBool(values9[1], ((Object)prefab).name, "base.aggressive passiveAggressive", out var value27)) { ai.m_passiveAggresive = value27; } } if (TryGetStringTuple(definition.Messages, 3, ((Object)prefab).name, "base.messages", out string[] values10)) { ai.m_spawnMessage = values10[0]; ai.m_deathMessage = values10[1]; ai.m_alertedMessage = values10[2]; } } private static void ApplyMonsterAiDefinition(GameObject prefab, MonsterAI ai, MonsterAiDefinition? definition) { if (definition == null) { return; } CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (definition.AlertRange.HasValue) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiAlertRange; } if (definition.Hunt != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiHunt; } if (definition.Chase != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiChase; } if (definition.Circle != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiCircle; } if (definition.HurtFlee != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiHurtFlee; } if (definition.Charge != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiCharge; } if (definition.Sleep != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiSleep; } if (definition.AvoidLand.HasValue) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.MonsterAiAvoidLand; } CreaturePrefabBaseline.Capture(prefab, creaturePrefabBaselineGroup); if (definition.AlertRange.HasValue) { ai.m_alertRange = definition.AlertRange.Value; } if (TryGetStringTuple(definition.Hunt, 3, ((Object)prefab).name, "monster.hunt", out string[] values)) { if (TryParseBool(values[0], ((Object)prefab).name, "monster.hunt enableHuntPlayer", out var value)) { ai.m_enableHuntPlayer = value; } if (TryParseBool(values[1], ((Object)prefab).name, "monster.hunt attackPlayerObjects", out var value2)) { ai.m_attackPlayerObjects = value2; } if (TryParseInt(values[2], ((Object)prefab).name, "monster.hunt privateAreaTriggerThreshold", out var value3)) { ai.m_privateAreaTriggerTreshold = value3; } } if (TryGetFloatTuple(definition.Chase, 4, ((Object)prefab).name, "monster.chase", out float[] values2)) { ai.m_interceptTimeMin = values2[0]; ai.m_interceptTimeMax = values2[1]; ai.m_maxChaseDistance = values2[2]; ai.m_minAttackInterval = values2[3]; } if (TryGetFloatTuple(definition.Circle, 3, ((Object)prefab).name, "monster.circle", out float[] values3)) { ai.m_circleTargetInterval = values3[0]; ai.m_circleTargetDuration = values3[1]; ai.m_circleTargetDistance = values3[2]; } if (TryGetStringTuple(definition.HurtFlee, 7, ((Object)prefab).name, "monster.hurtFlee", out string[] values4)) { if (TryParseBool(values4[0], ((Object)prefab).name, "monster.hurtFlee fleeIfHurtWhenTargetCantBeReached", out var value4)) { ai.m_fleeIfHurtWhenTargetCantBeReached = value4; } if (TryParseFloat(values4[1], ((Object)prefab).name, "monster.hurtFlee fleeUnreachableSinceAttacking", out var value5)) { ai.m_fleeUnreachableSinceAttacking = value5; } if (TryParseFloat(values4[2], ((Object)prefab).name, "monster.hurtFlee fleeUnreachableSinceHurt", out var value6)) { ai.m_fleeUnreachableSinceHurt = value6; } if (TryParseBool(values4[3], ((Object)prefab).name, "monster.hurtFlee fleeIfNotAlerted", out var value7)) { ai.m_fleeIfNotAlerted = value7; } if (TryParseFloat(values4[4], ((Object)prefab).name, "monster.hurtFlee fleeIfLowHealth", out var value8)) { ai.m_fleeIfLowHealth = value8; } if (TryParseFloat(values4[5], ((Object)prefab).name, "monster.hurtFlee fleeTimeSinceHurt", out var value9)) { ai.m_fleeTimeSinceHurt = value9; } if (TryParseBool(values4[6], ((Object)prefab).name, "monster.hurtFlee fleeInLava", out var value10)) { ai.m_fleeInLava = value10; } } if (TryGetStringTuple(definition.Charge, 2, ((Object)prefab).name, "monster.charge", out string[] values5)) { if (TryParseBool(values5[0], ((Object)prefab).name, "monster.charge circulateWhileCharging", out var value11)) { ai.m_circulateWhileCharging = value11; } if (TryParseBool(values5[1], ((Object)prefab).name, "monster.charge circulateWhileChargingFlying", out var value12)) { ai.m_circulateWhileChargingFlying = value12; } } if (TryGetStringTuple(definition.Sleep, 7, ((Object)prefab).name, "monster.sleep", out string[] values6)) { if (TryParseBool(values6[0], ((Object)prefab).name, "monster.sleep sleeping", out var value13)) { ai.m_sleeping = value13; } if (TryParseFloat(values6[1], ((Object)prefab).name, "monster.sleep wakeupRange", out var value14)) { ai.m_wakeupRange = value14; } if (TryParseBool(values6[2], ((Object)prefab).name, "monster.sleep noiseWakeup", out var value15)) { ai.m_noiseWakeup = value15; } if (TryParseFloat(values6[3], ((Object)prefab).name, "monster.sleep maxNoiseWakeupRange", out var value16)) { ai.m_maxNoiseWakeupRange = value16; } if (TryParseFloat(values6[4], ((Object)prefab).name, "monster.sleep wakeUpDelayMin", out var value17)) { ai.m_wakeUpDelayMin = value17; } if (TryParseFloat(values6[5], ((Object)prefab).name, "monster.sleep wakeUpDelayMax", out var value18)) { ai.m_wakeUpDelayMax = value18; } if (TryParseFloat(values6[6], ((Object)prefab).name, "monster.sleep fallAsleepDistance", out var value19)) { ai.m_fallAsleepDistance = value19; } } if (definition.AvoidLand.HasValue) { ai.m_avoidLand = definition.AvoidLand.Value; } } private static bool TryGetStringTuple(List? tuple, int expectedCount, string prefabName, string label, out string[] values) { values = Array.Empty(); if (tuple == null) { return false; } string[] array = tuple.Select((string value) => (value ?? "").Trim()).ToArray(); if (array.Length != expectedCount) { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{prefabName}' {label} tuple expected {expectedCount} values but got {array.Length}."); return false; } values = array; return true; } private static bool TryGetFloatTuple(List? tuple, int expectedCount, string prefabName, string label, out float[] values) { values = Array.Empty(); if (tuple == null) { return false; } if (tuple.Count != expectedCount) { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{prefabName}' {label} tuple expected {expectedCount} values but got {tuple.Count}."); return false; } values = tuple.ToArray(); return true; } private static bool TryParseBool(string token, string prefabName, string label, out bool value) { if (bool.TryParse(token, out value)) { return true; } string text = (token ?? "").Trim().ToLowerInvariant(); if (text != null) { switch (text.Length) { case 1: { char c = text[0]; if ((uint)c <= 49u) { if (c != '0') { if (c != '1') { break; } goto IL_00cf; } } else if (c != 'n') { if (c != 'y') { break; } goto IL_00cf; } goto IL_00d4; } case 3: { char c = text[0]; if (c != 'o') { if (c != 'y' || !(text == "yes")) { break; } goto IL_00cf; } if (!(text == "off")) { break; } goto IL_00d4; } case 2: { char c = text[0]; if (c != 'n') { if (c != 'o' || !(text == "on")) { break; } goto IL_00cf; } if (!(text == "no")) { break; } goto IL_00d4; } IL_00d4: value = false; return true; IL_00cf: value = true; return true; } } value = false; CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + prefabName + "' " + label + " has invalid boolean '" + token + "'.")); return false; } private static bool TrySetEnumField(object target, string fieldName, string token) { FieldInfo field = target.GetType().GetField(fieldName); Type type = field?.FieldType; if (field == null || type == null || !type.IsEnum) { return false; } try { object value = Enum.Parse(type, token, ignoreCase: true); field.SetValue(target, value); return true; } catch { return false; } } private static void ApplyProjectileDefinition(ProjectileDefinition definition) { if (string.IsNullOrWhiteSpace(definition.Prefab)) { CreatureManagerPlugin.Log.LogWarning((object)"Skipping projectile definition without prefab."); return; } GameObject val = ResolveProjectilePrefab(definition); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Skipping projectile '" + definition.Prefab + "': prefab not found.")); return; } bool flag = definition.Projectile?.SpawnOnHitSpecified ?? false; Projectile val2 = null; GameObject val3 = null; if (flag) { val2 = val.GetComponent(); if ((Object)(object)val2 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile definition '" + ((Object)val).name + "' sets projectile.spawnOnHit, but the prefab has no root Projectile component.")); return; } string text = definition.Projectile.SpawnOnHit?.Trim(); if (text != null) { val3 = CreaturePrefabRegistry.GetPrefab(text); if ((Object)(object)val3 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile definition '" + ((Object)val).name + "' references missing projectile.spawnOnHit prefab '" + text + "'.")); return; } } } SpawnAbility val4 = null; GameObject[] array = null; if (definition.SpawnAbility?.SpawnPrefabs != null) { val4 = val.GetComponent(); if ((Object)(object)val4 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile definition '" + ((Object)val).name + "' sets spawnAbility.spawnPrefabs, but the prefab has no root SpawnAbility component.")); return; } if (!CreatureYaml.TryParseSpawnPrefabEntries(definition.SpawnAbility.SpawnPrefabs, out List<(string, int)> entries, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile definition '" + ((Object)val).name + "' has invalid spawnAbility.spawnPrefabs: " + error)); return; } array = (GameObject[])(object)new GameObject[entries.Sum<(string, int)>(((string PrefabName, int Weight) entry) => entry.Weight)]; int num = 0; foreach (var item3 in entries) { string item = item3.Item1; int item2 = item3.Item2; GameObject prefab = CreaturePrefabRegistry.GetPrefab(item); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile definition '" + ((Object)val).name + "' references missing spawnAbility.spawnPrefabs prefab '" + item + "'.")); return; } for (int num2 = 0; num2 < item2; num2++) { array[num++] = prefab; } } } CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (flag) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.ProjectileSpawnOnHit; } if (array != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.SpawnAbilitySpawnPrefabs; } CreaturePrefabBaseline.Capture(val, creaturePrefabBaselineGroup); if (flag) { val2.m_spawnOnHit = val3; } if (array != null) { val4.m_spawnPrefab = array; } } private static GameObject? ResolveProjectilePrefab(ProjectileDefinition definition) { string text = definition.Prefab.Trim(); string text2 = (definition.ClonedFrom ?? "").Trim(); if (string.IsNullOrWhiteSpace(text2)) { return CreaturePrefabRegistry.GetPrefab(text); } GameObject prefab = CreaturePrefabRegistry.GetPrefab(text2); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Projectile clone source '" + text2 + "' for '" + text + "' was not found.")); return null; } return CreaturePrefabRegistry.ClonePrefab(prefab, text); } private static void ApplyAttackDefinition(AttackDefinition definition) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(definition.Prefab)) { CreatureManagerPlugin.Log.LogWarning((object)"Skipping attack definition without prefab."); return; } GameObject val = ResolveAttackPrefab(definition); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Skipping attack '" + definition.Prefab + "': prefab not found.")); return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + ((Object)val).name + "' has no ItemDrop component.")); return; } SharedData shared = component.m_itemData.m_shared; CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (definition.Damage != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackDamage; } if (definition.Attack != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackTuple; } if (definition.StatusEffect != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackStatusEffect; } if (definition.Projectile != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackProjectile; } if (definition.Ai != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackAi; } if (shared.m_attack == null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.AttackTuple; } CreaturePrefabBaseline.Capture(val, creaturePrefabBaselineGroup); if (shared.m_attack == null) { shared.m_attack = new Attack(); } ApplyAttackDamage(shared, definition.Damage); ApplyAttackTuple(val, shared.m_attack, definition.Attack); ApplyAttackStatusEffectTuple(val, shared, definition.StatusEffect); ApplyProjectileTuple(val, shared.m_attack, definition.Projectile); ApplyAttackAiTuple(val, shared, definition.Ai); } private static GameObject? ResolveAttackPrefab(AttackDefinition definition) { string text = definition.Prefab.Trim(); string text2 = (definition.ClonedFrom ?? "").Trim(); if (string.IsNullOrWhiteSpace(text2)) { return CreaturePrefabRegistry.GetPrefab(text); } GameObject prefab = CreaturePrefabRegistry.GetPrefab(text2); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Attack clone source '" + text2 + "' for '" + text + "' was not found.")); return null; } return CreaturePrefabRegistry.ClonePrefab(prefab, text); } private static GameObject? ResolveTargetPrefab(CreatureDefinition definition) { string text = definition.Prefab.Trim(); string text2 = (definition.ClonedFrom ?? "").Trim(); if (string.Equals(text, "Player", StringComparison.OrdinalIgnoreCase)) { CreatureManagerPlugin.Log.LogWarning((object)"Creature prefab 'Player' is not managed by CreatureManager."); return null; } if (string.IsNullOrWhiteSpace(text2)) { return CreaturePrefabRegistry.GetPrefab(text); } GameObject prefab = CreaturePrefabRegistry.GetPrefab(text2); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Clone source '" + text2 + "' for '" + text + "' was not found.")); return null; } if (CreaturePrefabRegistry.IsPlayerPrefab(prefab)) { CreatureManagerPlugin.Log.LogWarning((object)("Clone source '" + text2 + "' for '" + text + "' is a Player prefab and is not managed by CreatureManager.")); return null; } return CreaturePrefabRegistry.ClonePrefab(prefab, text); } private static void ApplyAttackDamage(SharedData shared, AttackDamageDefinition? definition) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if (definition != null) { DamageTypes damages = shared.m_damages; ApplyDamageValue(ref damages.m_damage, definition.Damage); ApplyDamageValue(ref damages.m_blunt, definition.Blunt); ApplyDamageValue(ref damages.m_slash, definition.Slash); ApplyDamageValue(ref damages.m_pierce, definition.Pierce); ApplyDamageValue(ref damages.m_chop, definition.Chop); ApplyDamageValue(ref damages.m_pickaxe, definition.Pickaxe); ApplyDamageValue(ref damages.m_fire, definition.Fire); ApplyDamageValue(ref damages.m_frost, definition.Frost); ApplyDamageValue(ref damages.m_lightning, definition.Lightning); ApplyDamageValue(ref damages.m_poison, definition.Poison); ApplyDamageValue(ref damages.m_spirit, definition.Spirit); shared.m_damages = damages; if (definition.AttackForce.HasValue) { shared.m_attackForce = definition.AttackForce.Value; } if (definition.ToolTier.HasValue) { shared.m_toolTier = definition.ToolTier.Value; } } } private static void ApplyDamageValue(ref float target, float? value) { if (value.HasValue) { target = value.Value; } } private static void ApplyAttackTuple(GameObject prefab, Attack attack, List? tuple) { //IL_0042: 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) if (tuple == null) { return; } string[] array = CleanTuple(tuple); if (array.Length != 2) { CreatureManagerPlugin.Log.LogWarning((object)$"Attack '{((Object)prefab).name}' attack tuple expected '[type, animation]' but got {array.Length} values."); return; } if (Enum.TryParse(array[0], ignoreCase: true, out AttackType result)) { attack.m_attackType = result; } else { CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + ((Object)prefab).name + "' has unknown attack type '" + array[0] + "'.")); } attack.m_attackAnimation = array[1]; } private static void ApplyProjectileTuple(GameObject prefab, Attack attack, List? tuple) { if (tuple == null) { return; } string[] array = CleanTuple(tuple); if (array.Length == 0) { return; } int num = array.Length; if ((num < 1 || num > 4) ? true : false) { CreatureManagerPlugin.Log.LogWarning((object)$"Attack '{((Object)prefab).name}' projectile tuple expected '[prefab, velocity, accuracy, count]' with 1-4 values but got {array.Length}."); return; } GameObject prefab2 = CreaturePrefabRegistry.GetPrefab(array[0]); if ((Object)(object)prefab2 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + ((Object)prefab).name + "' projectile prefab '" + array[0] + "' was not found.")); return; } attack.m_attackProjectile = prefab2; if (array.Length >= 2 && TryParseFloat(array[1], ((Object)prefab).name, "projectile velocity", out var value)) { attack.m_projectileVel = value; } if (array.Length >= 3 && TryParseFloat(array[2], ((Object)prefab).name, "projectile accuracy", out var value2)) { attack.m_projectileAccuracy = value2; } if (array.Length >= 4 && TryParseInt(array[3], ((Object)prefab).name, "projectile count", out var value3)) { attack.m_projectiles = value3; } } private static void ApplyAttackStatusEffectTuple(GameObject prefab, SharedData shared, List? tuple) { if (tuple == null) { return; } string[] array = CleanTuple(tuple); if (array.Length != 2) { CreatureManagerPlugin.Log.LogWarning((object)$"Attack '{((Object)prefab).name}' statusEffect tuple expected '[effect, chance]' but got {array.Length} values."); } else { if (!TryParseFloat(array[1], ((Object)prefab).name, "status effect chance", out var value)) { return; } StatusEffect val = ResolveAttackStatusEffect(array[0]); if ((Object)(object)val == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + ((Object)prefab).name + "' status effect '" + array[0] + "' was not found in ObjectDB.")); return; } if ((value < 0f || value > 1f) ? true : false) { CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + ((Object)prefab).name + "' status effect chance " + value.ToString(CultureInfo.InvariantCulture) + " is outside 0-1 and will be clamped.")); } shared.m_attackStatusEffect = val; shared.m_attackStatusEffectChance = Mathf.Clamp01(value); } } private static StatusEffect? ResolveAttackStatusEffect(string effectName) { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || instance.m_StatusEffects == null) { return null; } return instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(effectName)) ?? ((IEnumerable)instance.m_StatusEffects).FirstOrDefault((Func)((StatusEffect candidate) => (Object)(object)candidate != (Object)null && string.Equals(((Object)candidate).name, effectName, StringComparison.OrdinalIgnoreCase))); } private static void ApplyAttackAiTuple(GameObject prefab, SharedData shared, List? tuple) { if (tuple != null) { if (tuple.Count != 4) { CreatureManagerPlugin.Log.LogWarning((object)$"Attack '{((Object)prefab).name}' ai tuple expected '[interval, minRange, maxRange, maxAngle]' but got {tuple.Count} values."); return; } shared.m_aiAttackInterval = tuple[0]; shared.m_aiAttackRangeMin = tuple[1]; shared.m_aiAttackRange = tuple[2]; shared.m_aiAttackMaxAngle = tuple[3]; } } private static string[] CleanTuple(IEnumerable tuple) { return (from value in tuple select value?.Trim() ?? "" into value where value.Length > 0 select value).ToArray(); } private static bool TryParseFloat(string token, string prefabName, string label, out float value) { if (float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { return true; } CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + prefabName + "' has invalid " + label + " '" + token + "'.")); return false; } private static bool TryParseInt(string token, string prefabName, string label, out int value) { if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) { return true; } CreatureManagerPlugin.Log.LogWarning((object)("Attack '" + prefabName + "' has invalid " + label + " '" + token + "'.")); return false; } private static void ApplyCharacter(GameObject prefab, CharacterDefinition? definition) { //IL_0123: 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) if (definition == null) { return; } Character component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has no Character component.")); return; } CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (definition.Name != null || definition.Faction != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterIdentity; } if (!string.IsNullOrWhiteSpace(definition.Boss)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterBoss; } if (definition.DefeatSetGlobalKey != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterGlobalKey; } if (!string.IsNullOrWhiteSpace(definition.Health)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterHealth; } if (definition.DamageModifiers != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterDamageModifiers; } if (!string.IsNullOrWhiteSpace(definition.Speed)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterSpeed; } if (!string.IsNullOrWhiteSpace(definition.Jump)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterJump; } if (!string.IsNullOrWhiteSpace(definition.Swim)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterSwim; } if (!string.IsNullOrWhiteSpace(definition.Flight)) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.CharacterFlight; } CreaturePrefabBaseline.Capture(prefab, creaturePrefabBaselineGroup); if (definition.Name != null) { component.m_name = definition.Name; } if (definition.Faction != null) { if (CreatureFactionManager.TryGetFaction(definition.Faction, out var faction)) { component.m_faction = faction; } else { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has unknown faction '" + definition.Faction + "'.")); } } ApplyBossTuple(component, definition.Boss); if (definition.DefeatSetGlobalKey != null) { component.m_defeatSetGlobalKey = definition.DefeatSetGlobalKey; } ApplyHealthTuple(component, definition.Health); ApplyDamageModifiers(component, definition.DamageModifiers); ApplySpeedTuple(component, definition.Speed); ApplyJumpTuple(component, definition.Jump); ApplySwimTuple(component, definition.Swim); ApplyFlightTuple(component, definition.Flight); } private static void ApplyBossTuple(Character character, string? tuple) { string text = tuple ?? ""; if (string.IsNullOrWhiteSpace(text)) { return; } if (!CreatureYaml.TryParseBossTuple(text, out bool boss, out bool dontHideBossHud, out string bossEvent, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' boss skipped: " + error)); return; } character.m_boss = boss; character.m_dontHideBossHud = dontHideBossHud; if (bossEvent != null) { character.m_bossEvent = bossEvent; } } private static void ApplyHealthTuple(Character character, string? tuple) { string text = tuple ?? ""; if (string.IsNullOrWhiteSpace(text)) { return; } if (!CreatureYaml.TryParseHealthTuple(text, out float health, out float? regenAllHPTime, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' health skipped: " + error)); return; } character.m_health = health; if (regenAllHPTime.HasValue) { character.m_regenAllHPTime = regenAllHPTime.Value; } } private static void ApplyDamageModifiers(Character character, DamageModifiersDefinition? definition) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //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) if (definition != null) { DamageModifiers damageModifiers = character.m_damageModifiers; ApplyDamageModifier(ref damageModifiers.m_blunt, definition.Blunt); ApplyDamageModifier(ref damageModifiers.m_slash, definition.Slash); ApplyDamageModifier(ref damageModifiers.m_pierce, definition.Pierce); ApplyDamageModifier(ref damageModifiers.m_chop, definition.Chop); ApplyDamageModifier(ref damageModifiers.m_pickaxe, definition.Pickaxe); ApplyDamageModifier(ref damageModifiers.m_fire, definition.Fire); ApplyDamageModifier(ref damageModifiers.m_frost, definition.Frost); ApplyDamageModifier(ref damageModifiers.m_lightning, definition.Lightning); ApplyDamageModifier(ref damageModifiers.m_poison, definition.Poison); ApplyDamageModifier(ref damageModifiers.m_spirit, definition.Spirit); character.m_damageModifiers = damageModifiers; } } private static void ApplyDamageModifier(ref DamageModifier target, string? value) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown if (value != null) { if (Enum.TryParse(value, ignoreCase: true, out DamageModifier result)) { target = (DamageModifier)(int)result; } else { CreatureManagerPlugin.Log.LogWarning((object)("Unknown damage modifier '" + value + "'.")); } } } private static void ApplySpeedTuple(Character character, string? tuple) { string text = tuple ?? ""; if (!string.IsNullOrWhiteSpace(text)) { if (!CreatureYaml.TryParseFloatTuple(text, 7, "speed", out float[] values, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' speed skipped: " + error)); return; } character.m_crouchSpeed = values[0]; character.m_walkSpeed = values[1]; character.m_speed = values[2]; character.m_turnSpeed = values[3]; character.m_runSpeed = values[4]; character.m_runTurnSpeed = values[5]; character.m_acceleration = values[6]; } } private static void ApplyJumpTuple(Character character, string? tuple) { string text = tuple ?? ""; if (!string.IsNullOrWhiteSpace(text)) { if (!CreatureYaml.TryParseFloatTuple(text, 5, "jump", out float[] values, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' jump skipped: " + error)); return; } character.m_jumpForce = values[0]; character.m_jumpForceForward = values[1]; character.m_jumpForceTiredFactor = values[2]; character.m_airControl = values[3]; character.m_jumpStaminaUsage = values[4]; } } private static void ApplySwimTuple(Character character, string? tuple) { string text = tuple ?? ""; if (!string.IsNullOrWhiteSpace(text)) { if (!CreatureYaml.TryParseBoolFloatTuple(text, 4, "swim", out bool boolValue, out float[] values, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' swim skipped: " + error)); return; } character.m_canSwim = boolValue; character.m_swimDepth = values[0]; character.m_swimSpeed = values[1]; character.m_swimTurnSpeed = values[2]; character.m_swimAcceleration = values[3]; } } private static void ApplyFlightTuple(Character character, string? tuple) { string text = tuple ?? ""; if (!string.IsNullOrWhiteSpace(text)) { if (!CreatureYaml.TryParseBoolFloatTuple(text, 3, "flight", out bool boolValue, out float[] values, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)character).name + "' flight skipped: " + error)); return; } character.m_flying = boolValue; character.m_flySlowSpeed = values[0]; character.m_flyFastSpeed = values[1]; character.m_flyTurnSpeed = values[2]; } } internal static bool TryGetRandomHairPrefabs(Humanoid humanoid, out IReadOnlyList prefabs) { if ((Object)(object)humanoid == (Object)null) { prefabs = Array.Empty(); return false; } return TryGetRandomHairPrefabs(((Component)humanoid).gameObject, out prefabs); } internal static bool TryGetRandomHairPrefabs(GameObject creature, out IReadOnlyList prefabs) { prefabs = Array.Empty(); if ((Object)(object)creature == (Object)null || !RandomHairPrefabsByCreature.TryGetValue(GetPrefabName(((Object)creature).name), out GameObject[] value)) { return false; } prefabs = value; return true; } internal static bool TryGetConfiguredAppearanceHairColor(Humanoid humanoid, out Vector3 color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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) color = Vector3.one; if (!TryGetConfiguredAppearance(humanoid, out CreatureAppearanceRuntimeState appearance) || !appearance.HairColor.HasValue) { return false; } color = appearance.HairColor.Value; return true; } internal static bool TryGetConfiguredAppearance(Humanoid humanoid, out CreatureAppearanceRuntimeState appearance) { appearance = null; if ((Object)(object)humanoid != (Object)null) { return AppearanceByCreature.TryGetValue(GetPrefabName(((Object)((Component)humanoid).gameObject).name), out appearance); } return false; } internal static bool TryGetConfiguredRagdollAppearance(VisEquipment visEquipment, out CreatureAppearanceRuntimeState appearance) { appearance = null; if ((Object)(object)visEquipment != (Object)null) { return AppearanceByRagdollPrefab.TryGetValue(GetPrefabName(((Object)((Component)visEquipment).gameObject).name), out appearance); } return false; } internal static Vector3 GetInheritedAppearanceHairColor(Humanoid humanoid) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)humanoid == (Object)null) { return Vector3.one; } GameObject prefab = CreaturePrefabRegistry.GetPrefab(GetPrefabName(((Object)((Component)humanoid).gameObject).name)); return (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null)?.m_hairColor ?? Vector3.one; } internal static bool IsConfirmedHairItemPrefab(GameObject itemPrefab) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 if ((Object)(object)itemPrefab == (Object)null) { return false; } SharedData val = itemPrefab.GetComponent()?.m_itemData?.m_shared; if (val != null && (int)val.m_itemType == 6 && !string.IsNullOrWhiteSpace(val.m_name) && val.m_name.StartsWith("$customization_hair", StringComparison.OrdinalIgnoreCase)) { return HasAttachableHeadVisual(itemPrefab); } return false; } internal static bool IsConfirmedHairOnly(IEnumerable? itemPrefabs) { if (itemPrefabs == null) { return false; } bool result = false; foreach (GameObject itemPrefab in itemPrefabs) { if ((Object)(object)itemPrefab == (Object)null || !IsConfirmedHairItemPrefab(itemPrefab)) { return false; } result = true; } return result; } private static void ApplyHumanoid(GameObject prefab, HumanoidDefinition? definition) { if (definition == null) { return; } Humanoid component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has no Humanoid component.")); return; } bool flag = definition.RandomHair != null && definition.RandomArmor == null && IsConfirmedHairOnly(component.m_randomArmor); CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = CreaturePrefabBaselineGroup.None; if (definition.DefaultItems != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidDefaultItems; } if (definition.RandomWeapon != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidRandomWeapon; } if (definition.RandomArmor != null || flag) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidRandomArmor; } if (definition.RandomShield != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidRandomShield; } if (definition.RandomItems != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidRandomItems; } if (definition.RandomSets != null) { creaturePrefabBaselineGroup |= CreaturePrefabBaselineGroup.HumanoidRandomSets; } CreaturePrefabBaseline.Capture(prefab, creaturePrefabBaselineGroup); if (definition.DefaultItems != null) { component.m_defaultItems = BuildDefaultItems(prefab, definition.DefaultItems).ToArray(); } if (definition.RandomWeapon != null) { component.m_randomWeapon = ResolveItemPrefabs(prefab, "randomWeapon", definition.RandomWeapon).ToArray(); } if (definition.RandomArmor != null) { component.m_randomArmor = ResolveItemPrefabs(prefab, "randomArmor", definition.RandomArmor).ToArray(); } else if (flag) { component.m_randomArmor = Array.Empty(); } if (definition.RandomHair != null) { RandomHairPrefabsByCreature[GetPrefabName(((Object)prefab).name)] = ResolveRandomHairPrefabs(prefab, definition.RandomHair).ToArray(); } if (definition.RandomShield != null) { component.m_randomShield = ResolveItemPrefabs(prefab, "randomShield", definition.RandomShield).ToArray(); } if (definition.RandomItems != null) { component.m_randomItems = BuildRandomItems(prefab, definition.RandomItems).ToArray(); } if (definition.RandomSets != null) { component.m_randomSets = BuildRandomSets(prefab, definition.RandomSets).ToArray(); } } private static List ResolveRandomHairPrefabs(GameObject creaturePrefab, IEnumerable itemNames) { List list = ResolveItemPrefabs(creaturePrefab, "randomHair", itemNames); for (int num = list.Count - 1; num >= 0; num--) { GameObject val = list[num]; ObjectDB instance = ObjectDB.instance; GameObject val2 = ((instance != null) ? instance.GetItemPrefab(((Object)val).name) : null); if ((Object)(object)val2 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid.randomHair item prefab '" + ((Object)val).name + "' is not registered in ObjectDB and was skipped.")); list.RemoveAt(num); } else if (HasAttachableHeadVisual(val2)) { list[num] = val2; } else { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid.randomHair item prefab '" + ((Object)val).name + "' has no direct attach or attach_skin visual and was skipped.")); list.RemoveAt(num); } } return list; } private static bool HasAttachableHeadVisual(GameObject itemPrefab) { if ((Object)(object)itemPrefab == (Object)null) { return false; } Transform transform = itemPrefab.transform; for (int i = 0; i < transform.childCount; i++) { string name = ((Object)transform.GetChild(i)).name; if (name.Equals("attach", StringComparison.Ordinal) || name.Equals("attach_skin", StringComparison.Ordinal)) { return true; } } return false; } private static List ResolveItemPrefabs(GameObject creaturePrefab, string fieldName, IEnumerable itemNames) { List list = new List(); foreach (string itemName in itemNames) { string text = itemName.Trim(); if (text.Length != 0) { GameObject val = ResolveItemPrefab(creaturePrefab, fieldName, text); if ((Object)(object)val != (Object)null) { list.Add(val); } } } return list; } private static GameObject? ResolveItemPrefab(GameObject creaturePrefab, string fieldName, string prefabName) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid." + fieldName + " item prefab '" + prefabName + "' was not found.")); return null; } if ((Object)(object)prefab.GetComponent() == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid." + fieldName + " item prefab '" + prefabName + "' has no ItemDrop component.")); return null; } return prefab; } private static List BuildRandomItems(GameObject creaturePrefab, IEnumerable lines) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown List list = new List(); foreach (string line in lines) { if (!CreatureYaml.TryParseRandomItemTuple(line, out string prefabName, out float chance, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid.randomItems entry skipped: " + error)); continue; } GameObject val = ResolveItemPrefab(creaturePrefab, "randomItems", prefabName); if (!((Object)(object)val == (Object)null)) { list.Add(new RandomItem { m_prefab = val, m_chance = chance }); } } return list; } private static List BuildRandomSets(GameObject creaturePrefab, IEnumerable lines) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown List list = new List(); foreach (string line in lines) { if (!CreatureYaml.TryParseRandomSetTuple(line, out string setName, out string[] itemNames, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid.randomSets entry skipped: " + error)); continue; } GameObject[] array = ResolveItemPrefabs(creaturePrefab, "randomSets", itemNames).ToArray(); if (array.Length == 0) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' humanoid.randomSets entry '" + setName + "' has no resolved item prefabs.")); } else { list.Add(new ItemSet { m_name = setName, m_items = array }); } } return list; } private static List BuildDefaultItems(GameObject creaturePrefab, List lines) { List list = new List(); List list2 = new List(); foreach (string line in lines) { string text = line.Trim(); if (text.Length == 0) { continue; } GameObject val = ResolveItemPrefab(creaturePrefab, "defaultItems", text); if (!((Object)(object)val == (Object)null)) { if (CreaturePrefabRegistry.IsAttackItem(val)) { list.Add(val); } else { list2.Add(val); } } } return AppendDistinctByName(list, list2); } private static void ApplyVisual(GameObject prefab, float? scale, List? textures) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) bool flag = textures != null && textures.Count > 0; AppearanceByCreature.TryGetValue(GetPrefabName(((Object)prefab).name), out CreatureAppearanceRuntimeState value); if (scale.HasValue) { CreaturePrefabBaseline.Capture(prefab, CreaturePrefabBaselineGroup.VisualScale); prefab.transform.localScale = Vector3.one * scale.Value; RegisterEquipmentVisualScale(prefab); } if (scale.HasValue || flag || value != null) { CreaturePrefabBaseline.Capture(prefab, CreaturePrefabBaselineGroup.RagdollReferences); ApplyRagdollVisuals(prefab, scale.HasValue, textures, value); } if (textures == null) { return; } foreach (string texture in textures) { ApplyTextureOverride(prefab, texture); } } private static RagdollTextureRuntimeState? ApplyTextureOverride(GameObject prefab, string definition) { //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Expected O, but got Unknown if (!TryParseTextureOverride(definition, out string rendererName, out int materialIndex, out string textureName, out string error)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' texture override '" + definition + "' is invalid: " + error)); return null; } if (ShouldSkipRendererTextureWork()) { return null; } Texture texture = CreatureTextureRegistry.GetTexture(textureName); if ((Object)(object)texture == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' texture '" + textureName + "' was not found.")); return null; } List list = FindRenderers(prefab, rendererName).ToList(); if (list.Count == 0) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has no renderer matching '" + rendererName + "'.")); return null; } bool flag = false; foreach (Renderer item in list) { Material[] sharedMaterials = item.sharedMaterials; if (materialIndex < 0 || materialIndex >= sharedMaterials.Length || (Object)(object)sharedMaterials[materialIndex] == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{((Object)prefab).name}' renderer '{((Object)item).name}' has no material index {materialIndex}."); continue; } (int, int) key = (((Object)item).GetInstanceID(), materialIndex); if (TextureMaterialOverrides.TryGetValue(key, out TextureMaterialOverride value)) { if ((Object)(object)value.Renderer == (Object)(object)item && (Object)(object)value.Generated != (Object)null) { value.Active = true; value.Generated.SetTexture("_MainTex", texture); if ((Object)(object)sharedMaterials[materialIndex] != (Object)(object)value.Generated) { sharedMaterials[materialIndex] = value.Generated; item.sharedMaterials = sharedMaterials; } flag = true; continue; } RestoreTextureOverrideSlot(value); if ((Object)(object)value.Generated != (Object)null) { Object.Destroy((Object)(object)value.Generated); } TextureMaterialOverrides.Remove(key); } Material val = sharedMaterials[materialIndex]; if (!val.HasProperty("_MainTex")) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' renderer '" + ((Object)item).name + "' material '" + ((Object)val).name + "' has no _MainTex texture property.")); } else { Material val2 = new Material(val); ((Object)val2).name = ((Object)val).name + "_CreatureManager"; val2.SetTexture("_MainTex", texture); sharedMaterials[materialIndex] = val2; item.sharedMaterials = sharedMaterials; TextureMaterialOverrides[key] = new TextureMaterialOverride { Renderer = item, MaterialIndex = materialIndex, Original = val, Generated = val2, Active = true }; flag = true; } } if (!flag) { return null; } return new RagdollTextureRuntimeState(rendererName, materialIndex, texture); } private static bool ShouldSkipRendererTextureWork() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if (!Application.isBatchMode) { return (int)SystemInfo.graphicsDeviceType == 4; } return true; } private static void BeginTextureOverrideApply() { foreach (TextureMaterialOverride value in TextureMaterialOverrides.Values) { value.Active = false; } } private static void CompleteTextureOverrideApply() { foreach (TextureMaterialOverride value in TextureMaterialOverrides.Values) { if (!value.Active) { RestoreTextureOverrideSlot(value); } } } private static void DisposeTextureOverrides() { foreach (TextureMaterialOverride value in TextureMaterialOverrides.Values) { RestoreTextureOverrideSlot(value); if ((Object)(object)value.Generated != (Object)null) { Object.Destroy((Object)(object)value.Generated); } } TextureMaterialOverrides.Clear(); } private static void RestoreTextureOverrideSlot(TextureMaterialOverride textureOverride) { Renderer renderer = textureOverride.Renderer; if (!((Object)(object)renderer == (Object)null)) { Material[] sharedMaterials = renderer.sharedMaterials; int materialIndex = textureOverride.MaterialIndex; if (materialIndex >= 0 && materialIndex < sharedMaterials.Length && (Object)(object)sharedMaterials[materialIndex] == (Object)(object)textureOverride.Generated) { sharedMaterials[materialIndex] = textureOverride.Original; renderer.sharedMaterials = sharedMaterials; } } } private static void ApplyRagdollVisuals(GameObject prefab, bool scaleSpecified, List? textures, CreatureAppearanceRuntimeState? appearance) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) EffectData[] array = prefab.GetComponent()?.m_deathEffects?.m_effectPrefabs; if (array == null || array.Length == 0) { return; } Vector3 localScale = prefab.transform.localScale; bool flag = textures != null && textures.Count > 0; EffectData[] array2 = array; foreach (EffectData val in array2) { if ((Object)(object)val?.m_prefab == (Object)null || (Object)(object)val.m_prefab.GetComponent() == (Object)null) { continue; } GameObject prefab2 = val.m_prefab; GameObject originalRagdollPrefab = GetOriginalRagdollPrefab(prefab2); bool flag2 = (Object)(object)prefab2 != (Object)(object)originalRagdollPrefab; if (!(flag || appearance != null || flag2) && (!scaleSpecified || (IsUnitScale(localScale) && !EffectMayModifyRagdollScale(val)))) { val.m_prefab = originalRagdollPrefab; continue; } string key = ((Object)prefab).name + "|" + ((Object)originalRagdollPrefab).name; if (!ManagedRagdollPrefabs.TryGetValue(key, out GameObject value) || (Object)(object)value == (Object)null) { string cloneName = ((Object)originalRagdollPrefab).name + "_" + ((Object)prefab).name + "_CreatureManagerRagdoll"; value = CreaturePrefabRegistry.ClonePrefab(prefab2, cloneName); if ((Object)(object)value == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' failed to clone ragdoll '" + ((Object)prefab2).name + "' for its configured visuals; the shared source ragdoll was left unchanged.")); continue; } ManagedRagdollPrefabs[key] = value; RagdollCloneSources[((Object)value).name] = originalRagdollPrefab; } RagdollScaleRuntimeState ragdollScaleRuntimeState = null; RagdollScaleRuntimeState scale; if (scaleSpecified) { CreaturePrefabBaseline.Capture(value, CreaturePrefabBaselineGroup.VisualScale); Vector3 val2 = Vector3.Scale(GetOriginalRagdollScale(originalRagdollPrefab), localScale); value.transform.localScale = val2; ragdollScaleRuntimeState = new RagdollScaleRuntimeState(val2, localScale); } else if (TryGetInheritedRagdollScale(prefab2, out scale)) { CreaturePrefabBaseline.Capture(value, CreaturePrefabBaselineGroup.VisualScale); value.transform.localScale = scale.FinalScale; ragdollScaleRuntimeState = scale; } string prefabName = GetPrefabName(((Object)value).name); if (ragdollScaleRuntimeState != null) { RagdollScalesByPrefab[prefabName] = ragdollScaleRuntimeState; InheritedRagdollScales[prefabName] = ragdollScaleRuntimeState; RegisterEquipmentVisualScale(prefabName, ragdollScaleRuntimeState.EquipmentScale); } else { InheritedRagdollScales.Remove(prefabName); } List list = new List(); if (TryGetInheritedRagdollTextures(prefab2, out IReadOnlyList textures2)) { list.AddRange(textures2); } if (flag) { foreach (string texture in textures) { RagdollTextureRuntimeState ragdollTextureRuntimeState = ApplyTextureOverride(value, texture); if (ragdollTextureRuntimeState != null) { AddOrReplaceRagdollTexture(list, ragdollTextureRuntimeState); } } } if (list.Count > 0) { RagdollTextureRuntimeState[] value2 = list.ToArray(); RagdollTexturesByPrefab[prefabName] = value2; InheritedRagdollTextures[prefabName] = value2; } else { InheritedRagdollTextures.Remove(prefabName); } if (appearance != null) { ApplyAppearanceToRagdollPrefab(prefab, value, appearance); } val.m_prefab = value; } } private static bool TryGetInheritedRagdollScale(GameObject ragdollPrefab, out RagdollScaleRuntimeState scale) { string prefabName = GetPrefabName(((Object)ragdollPrefab).name); if (!RagdollScalesByPrefab.TryGetValue(prefabName, out scale)) { return InheritedRagdollScales.TryGetValue(prefabName, out scale); } return true; } private static bool TryGetInheritedRagdollTextures(GameObject ragdollPrefab, out IReadOnlyList textures) { string prefabName = GetPrefabName(((Object)ragdollPrefab).name); if (RagdollTexturesByPrefab.TryGetValue(prefabName, out RagdollTextureRuntimeState[] value) || InheritedRagdollTextures.TryGetValue(prefabName, out value)) { textures = value; return true; } textures = Array.Empty(); return false; } private static void AddOrReplaceRagdollTexture(List textures, RagdollTextureRuntimeState texture) { int num = textures.FindIndex((RagdollTextureRuntimeState existing) => existing.MaterialIndex == texture.MaterialIndex && string.Equals(existing.RendererName, texture.RendererName, StringComparison.OrdinalIgnoreCase)); if (num >= 0) { textures[num] = texture; } else { textures.Add(texture); } } private static bool EffectMayModifyRagdollScale(EffectData effect) { if (!effect.m_scale && !effect.m_inheritParentScale) { return effect.m_multiplyParentVisualScale; } return true; } private static GameObject GetOriginalRagdollPrefab(GameObject ragdollPrefab) { GameObject val = ragdollPrefab; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); while ((Object)(object)val != (Object)null) { if (!hashSet.Add(((Object)val).name)) { CreatureManagerPlugin.Log.LogWarning((object)("Detected a CreatureManager ragdoll provenance cycle while resolving '" + ((Object)ragdollPrefab).name + "'.")); break; } if (!RagdollCloneSources.TryGetValue(((Object)val).name, out GameObject value) || (Object)(object)value == (Object)null || (Object)(object)value == (Object)(object)val) { break; } val = value; } if (!((Object)(object)val != (Object)null)) { return ragdollPrefab; } return val; } private static Vector3 GetOriginalRagdollScale(GameObject ragdollPrefab) { //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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (OriginalRagdollScales.TryGetValue(((Object)ragdollPrefab).name, out var value)) { return value; } value = ragdollPrefab.transform.localScale; OriginalRagdollScales[((Object)ragdollPrefab).name] = value; return value; } private static bool TryParseTextureOverride(string line, out string rendererName, out int materialIndex, out string textureName, out string error) { rendererName = ""; materialIndex = 0; textureName = ""; error = ""; if (string.IsNullOrWhiteSpace(line)) { error = "entry is empty."; return false; } string[] array = line.Split(new char[1] { ':' }); if (array.Length != 3) { error = "expected 'rendererName:materialIndex:textureName'."; return false; } rendererName = array[0].Trim(); string text = array[1].Trim(); textureName = array[2].Trim(); if (rendererName.Length == 0) { error = "rendererName is empty."; return false; } if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out materialIndex)) { error = "materialIndex '" + text + "' is not an integer."; return false; } if (textureName.Length == 0) { error = "textureName is empty."; return false; } return true; } private static IEnumerable FindRenderers(GameObject prefab, string? rendererName) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); if (string.IsNullOrWhiteSpace(rendererName)) { return componentsInChildren; } return componentsInChildren.Where((Renderer renderer) => string.Equals(((Object)renderer).name, rendererName, StringComparison.OrdinalIgnoreCase) || GetTransformPath(((Component)renderer).transform).EndsWith(rendererName, StringComparison.OrdinalIgnoreCase)); } internal static void ApplyConfiguredRagdollScale(Ragdoll ragdoll) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ragdoll == (Object)null) && RagdollScalesByPrefab.TryGetValue(GetPrefabName(((Object)((Component)ragdoll).gameObject).name), out RagdollScaleRuntimeState value)) { ZNetView component = ((Component)ragdoll).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { component.SetLocalScale(value.FinalScale); } else { ((Component)ragdoll).transform.localScale = value.FinalScale; } } } internal static void ApplyConfiguredRagdollTextures(VisEquipment visEquipment) { if ((Object)(object)visEquipment == (Object)null || ShouldSkipRendererTextureWork() || !RagdollTexturesByPrefab.TryGetValue(GetPrefabName(((Object)((Component)visEquipment).gameObject).name), out RagdollTextureRuntimeState[] value)) { return; } int instanceID = ((Object)visEquipment).GetInstanceID(); if (!AppliedRagdollTextureVisuals.Add(instanceID)) { return; } RagdollTextureRuntimeState[] array = value; foreach (RagdollTextureRuntimeState ragdollTextureRuntimeState in array) { foreach (Renderer item in FindRenderers(((Component)visEquipment).gameObject, ragdollTextureRuntimeState.RendererName)) { Material[] sharedMaterials = item.sharedMaterials; int materialIndex = ragdollTextureRuntimeState.MaterialIndex; if (materialIndex >= 0 && materialIndex < sharedMaterials.Length && !((Object)(object)sharedMaterials[materialIndex] == (Object)null) && sharedMaterials[materialIndex].HasProperty("_MainTex")) { RagdollTexturePropertyBlock.Clear(); item.GetPropertyBlock(RagdollTexturePropertyBlock, materialIndex); RagdollTexturePropertyBlock.SetTexture("_MainTex", ragdollTextureRuntimeState.Texture); item.SetPropertyBlock(RagdollTexturePropertyBlock, materialIndex); } } } } internal static void ForgetRagdollTextureVisual(VisEquipment visEquipment) { if ((Object)(object)visEquipment != (Object)null) { AppliedRagdollTextureVisuals.Remove(((Object)visEquipment).GetInstanceID()); } } internal static void ScaleAttachedEquipmentVisual(VisEquipment visEquipment, GameObject? item) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)item == (Object)null) && TryGetEquipmentVisualScale(visEquipment, out var scale)) { item.transform.localScale = Vector3.Scale(item.transform.localScale, scale); } } internal static void ScaleAttachedEquipmentVisuals(VisEquipment visEquipment, IEnumerable? items) { if (items == null) { return; } foreach (GameObject item in items) { ScaleAttachedEquipmentVisual(visEquipment, item); } } private static void RegisterEquipmentVisualScale(GameObject prefab) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) RegisterEquipmentVisualScale(GetPrefabName(((Object)prefab).name), prefab.transform.localScale); } private static void RegisterEquipmentVisualScale(string prefabName, Vector3 scale) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (IsUnitScale(scale)) { EquipmentVisualScales.Remove(prefabName); } else { EquipmentVisualScales[prefabName] = scale; } } private static bool TryGetEquipmentVisualScale(VisEquipment visEquipment, out Vector3 scale) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) string prefabName = GetPrefabName(((Object)((Component)visEquipment).gameObject).name); if (EquipmentVisualScales.TryGetValue(prefabName, out scale)) { return true; } scale = Vector3.one; return false; } private static bool IsUnitScale(Vector3 scale) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Approximately(scale.x, 1f) && Mathf.Approximately(scale.y, 1f)) { return Mathf.Approximately(scale.z, 1f); } return false; } private static string GetPrefabName(string objectName) { int num = objectName.IndexOf("(Clone)", StringComparison.Ordinal); if (num >= 0) { return objectName.Substring(0, num); } return objectName; } private static void ApplyVisEquipment(GameObject prefab, AppearanceDefinition? appearance, string? clonedFrom) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) Vector3? val = null; if (appearance != null && appearance.HairColor != null) { if (CreatureYaml.TryParseAppearanceColor(appearance.HairColor, out var color)) { val = color; } else { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' appearance.hairColor '" + appearance.HairColor + "' is invalid and was skipped.")); } } Vector3? val2 = null; if (appearance != null && appearance.SkinColor != null) { if (CreatureYaml.TryParseAppearanceColor(appearance.SkinColor, out var color2)) { val2 = color2; } else { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' appearance.skinColor '" + appearance.SkinColor + "' is invalid and was skipped.")); } } CreatureAppearanceRuntimeState value = null; string prefabName = GetPrefabName(((Object)prefab).name); AppearanceByCreature.TryGetValue(prefabName, out value); string text = (clonedFrom ?? "").Trim(); bool flag = CreaturePrefabRegistry.IsCreatureManagerClone(prefab); if (value == null && flag) { InheritedAppearanceByClone.TryGetValue(prefabName, out value); } if (value == null && text.Length > 0) { AppearanceByCreature.TryGetValue(GetPrefabName(text), out value); if (value != null && flag) { InheritedAppearanceByClone[prefabName] = value; } } CreatureAppearanceRuntimeState creatureAppearanceRuntimeState = new CreatureAppearanceRuntimeState(appearance?.Hair ?? value?.Hair, appearance?.Beard ?? value?.Beard, (appearance != null && appearance.HairColor != null) ? val : value?.HairColor, (appearance != null && appearance.SkinColor != null) ? val2 : value?.SkinColor, appearance?.ModelIndex ?? value?.ModelIndex); if (creatureAppearanceRuntimeState.HasSpecifiedFields) { AppearanceByCreature[prefabName] = creatureAppearanceRuntimeState; } if (appearance == null || !appearance.HasSpecifiedFields) { return; } CreaturePrefabBaseline.Capture(prefab, CreaturePrefabBaselineGroup.Appearance); Humanoid component = prefab.GetComponent(); VisEquipment component2 = prefab.GetComponent(); if ((Object)(object)component2 == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has no VisEquipment component.")); } else { int? modelIndex = appearance.ModelIndex; if (modelIndex.HasValue) { int valueOrDefault = modelIndex.GetValueOrDefault(); if (component2.m_models != null && component2.m_models.Length != 0) { if (valueOrDefault < 0 || valueOrDefault >= component2.m_models.Length) { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{((Object)prefab).name}' appearance.modelIndex {valueOrDefault} is outside 0..{component2.m_models.Length - 1}."); } else { component2.m_modelIndex = valueOrDefault; } } else if (valueOrDefault != 0) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' appearance.modelIndex ignored because the prefab has no VisEquipment models.")); } } if (val2.HasValue) { component2.m_skinColor = val2.Value; } if (val.HasValue) { component2.m_hairColor = val.Value; } if (appearance.Hair != null) { component2.m_hairItem = appearance.Hair; } if (appearance.Beard != null) { component2.m_beardItem = appearance.Beard; } } if ((Object)(object)component == (Object)null) { if (appearance.Hair != null || appearance.Beard != null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' has no Humanoid component for hair/beard appearance.")); } return; } if (appearance.Hair != null) { WarnIfAppearancePrefabMissing(prefab, "hair", appearance.Hair); component.m_hairItem = appearance.Hair; } if (appearance.Beard != null) { WarnIfAppearancePrefabMissing(prefab, "beard", appearance.Beard); component.m_beardItem = appearance.Beard; } } private static void ApplyAppearanceToRagdollPrefab(GameObject creaturePrefab, GameObject ragdollPrefab, CreatureAppearanceRuntimeState appearance) { //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) AppearanceByRagdollPrefab[GetPrefabName(((Object)ragdollPrefab).name)] = appearance; VisEquipment component = ragdollPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)creaturePrefab).name + "' has configured appearance fields, but ragdoll '" + ((Object)ragdollPrefab).name + "' has no VisEquipment component.")); return; } CreaturePrefabBaseline.Capture(ragdollPrefab, CreaturePrefabBaselineGroup.Appearance); int? modelIndex = appearance.ModelIndex; if (modelIndex.HasValue) { int valueOrDefault = modelIndex.GetValueOrDefault(); if (component.m_models != null && component.m_models.Length != 0) { if (valueOrDefault >= 0 && valueOrDefault < component.m_models.Length) { component.m_modelIndex = valueOrDefault; } else { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{((Object)creaturePrefab).name}' appearance.modelIndex {valueOrDefault} is outside ragdoll '{((Object)ragdollPrefab).name}' model range 0..{component.m_models.Length - 1}."); } } else if (valueOrDefault == 0) { component.m_modelIndex = 0; } else { CreatureManagerPlugin.Log.LogWarning((object)$"Creature '{((Object)creaturePrefab).name}' appearance.modelIndex {valueOrDefault} cannot be applied because ragdoll '{((Object)ragdollPrefab).name}' has no VisEquipment models."); } } if (appearance.SkinColor.HasValue) { component.m_skinColor = appearance.SkinColor.Value; } if (appearance.HairColor.HasValue) { component.m_hairColor = appearance.HairColor.Value; } if (appearance.Hair != null) { component.m_hairItem = appearance.Hair; } if (appearance.Beard != null) { component.m_beardItem = appearance.Beard; } } private static void WarnIfAppearancePrefabMissing(GameObject prefab, string label, string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName) && !((Object)(object)CreaturePrefabRegistry.GetPrefab(prefabName) != (Object)null)) { CreatureManagerPlugin.Log.LogWarning((object)("Creature '" + ((Object)prefab).name + "' appearance " + label + " prefab '" + prefabName + "' was not found.")); } } private static List AppendDistinctByName(IEnumerable firstItems, IEnumerable secondItems) { List list = new List(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (GameObject item in firstItems.Concat(secondItems)) { if (!((Object)(object)item == (Object)null) && hashSet.Add(((Object)item).name)) { list.Add(item); } } return list; } private static void SetupWatcher() { Watcher?.Dispose(); Watcher = null; WatcherResetPending = false; try { Watcher = new FileSystemWatcher(ConfigDirectoryPath) { IncludeSubdirectories = true, Filter = "*.*", SynchronizingObject = ThreadingHelper.SynchronizingObject }; Watcher.Changed += OnOverrideFileChanged; Watcher.Created += OnOverrideFileChanged; Watcher.Renamed += OnOverrideFileChanged; Watcher.Deleted += OnOverrideFileChanged; Watcher.Error += OnOverrideWatcherError; Watcher.EnableRaisingEvents = true; } catch (Exception ex) { Watcher?.Dispose(); Watcher = null; CreatureManagerPlugin.Log.LogError((object)("Failed to watch CreatureManager configuration files: " + ex.Message)); } } private static void OnOverrideFileChanged(object sender, FileSystemEventArgs e) { if (IsTextureFile(e.FullPath) || (e is RenamedEventArgs e2 && IsTextureFile(e2.OldFullPath))) { TextureRefreshPending = true; PendingTextureRefreshTime = DateTime.UtcNow.AddTicks(2500000L); } else if (IsOverrideFile(e.FullPath) || (e is RenamedEventArgs e3 && IsOverrideFile(e3.OldFullPath))) { ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { RequestConfigurationReload(); } } } private static void OnOverrideWatcherError(object sender, ErrorEventArgs args) { CreatureManagerPlugin.Log.LogWarning((object)("CreatureManager configuration watcher lost file events and will be rebuilt: " + args.GetException().Message)); WatcherResetPending = true; ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { TextureRefreshPending = true; PendingTextureRefreshTime = DateTime.UtcNow.AddTicks(2500000L); } else { RequestConfigurationReload(); } } private static bool IsTextureFile(string path) { if (!Path.GetExtension(path).Equals(".png", StringComparison.OrdinalIgnoreCase)) { return false; } return Path.GetDirectoryName(Path.GetFullPath(path))?.Equals(Path.GetFullPath(TextureDirectoryPath), StringComparison.OrdinalIgnoreCase) ?? false; } private static bool TryLoadOverrideFiles(string prefix, TryReadYaml reader, out List definitions) { definitions = new List(); try { foreach (string item in EnumerateOverrideFiles(prefix)) { string yaml = File.ReadAllText(item); if (!reader(yaml, item, out var definitions2)) { definitions.Clear(); return false; } definitions.AddRange(definitions2); } return true; } catch (Exception ex) { definitions.Clear(); CreatureManagerPlugin.Log.LogError((object)("Failed to load " + prefix + " YAML files: " + ex.Message)); return false; } } private static bool TryLoadLevelDefinitions(out List definitions) { if (!TryLoadOverrideFiles("levels", (TryReadYaml)CreatureYaml.TryReadLevelDefinitions, out List definitions2)) { definitions = new List(); return false; } definitions = LoadLevelPresetDefinitions(); definitions.AddRange(definitions2); return true; } private static List LoadLevelPresetDefinitions() { return BuildLevelPresetDefinitions(GetLevelPresetWeights(CreatureManagerPlugin.BiomeLevelPreset?.Value ?? CreatureManagerPlugin.LevelBiomePreset.Easy)); } private static List BuildLevelPresetDefinitions(IEnumerable> presets) { return presets.Select, LevelDefinition>((KeyValuePair entry) => new LevelDefinition { Target = entry.Key, Level = entry.Value.ToList(), IsPreset = true }).ToList(); } private static IReadOnlyList> GetLevelPresetWeights(CreatureManagerPlugin.LevelBiomePreset preset) { return preset switch { CreatureManagerPlugin.LevelBiomePreset.Easy => new KeyValuePair[9] { LevelPreset("Meadows", 100f), LevelPreset("BlackForest", 85f, 15f), LevelPreset("Swamp", 70f, 25f, 5f), LevelPreset("Mountain", 55f, 30f, 12f, 3f), LevelPreset("Plains", 45f, 30f, 18f, 6f, 1f), LevelPreset("Mistlands", 35f, 30f, 22f, 10f, 3f), LevelPreset("AshLands", 25f, 30f, 25f, 14f, 5f, 1f), LevelPreset("Ocean", 70f, 25f, 5f), LevelPreset("DeepNorth", 25f, 30f, 25f, 14f, 5f, 1f) }, CreatureManagerPlugin.LevelBiomePreset.Normal => new KeyValuePair[9] { LevelPreset("Meadows", 95f, 5f), LevelPreset("BlackForest", 75f, 20f, 5f), LevelPreset("Swamp", 55f, 30f, 12f, 3f), LevelPreset("Mountain", 45f, 30f, 18f, 6f, 1f), LevelPreset("Plains", 35f, 30f, 22f, 10f, 3f), LevelPreset("Mistlands", 25f, 30f, 25f, 14f, 5f, 1f), LevelPreset("AshLands", 15f, 25f, 25f, 20f, 10f, 5f), LevelPreset("Ocean", 55f, 30f, 12f, 3f), LevelPreset("DeepNorth", 15f, 25f, 25f, 20f, 10f, 5f) }, CreatureManagerPlugin.LevelBiomePreset.Hard => new KeyValuePair[9] { LevelPreset("Meadows", 90f, 10f), LevelPreset("BlackForest", 68f, 25f, 7f), LevelPreset("Swamp", 48f, 34f, 14f, 4f), LevelPreset("Mountain", 38f, 32f, 20f, 8f, 2f), LevelPreset("Plains", 30f, 30f, 23f, 12f, 4f, 1f), LevelPreset("Mistlands", 22f, 28f, 26f, 16f, 6f, 2f), LevelPreset("AshLands", 12f, 22f, 25f, 21f, 13f, 7f), LevelPreset("Ocean", 48f, 34f, 14f, 4f), LevelPreset("DeepNorth", 12f, 22f, 25f, 21f, 13f, 7f) }, CreatureManagerPlugin.LevelBiomePreset.VeryHard => new KeyValuePair[9] { LevelPreset("Meadows", 85f, 15f), LevelPreset("BlackForest", 60f, 30f, 10f), LevelPreset("Swamp", 40f, 35f, 18f, 7f), LevelPreset("Mountain", 30f, 35f, 22f, 10f, 3f), LevelPreset("Plains", 25f, 30f, 25f, 14f, 5f, 1f), LevelPreset("Mistlands", 18f, 27f, 27f, 18f, 8f, 2f), LevelPreset("AshLands", 10f, 20f, 25f, 22f, 15f, 8f), LevelPreset("Ocean", 40f, 35f, 18f, 7f), LevelPreset("DeepNorth", 10f, 20f, 25f, 22f, 15f, 8f) }, _ => GetLevelPresetWeights(CreatureManagerPlugin.LevelBiomePreset.Easy), }; } private static KeyValuePair LevelPreset(string biome, params float[] weights) { return new KeyValuePair(biome, weights); } private static IEnumerable EnumerateOverrideFiles(string prefix) { return (from path in Directory.EnumerateFiles(ConfigDirectoryPath, "*.yml").Concat(Directory.EnumerateFiles(ConfigDirectoryPath, "*.yaml")) where IsConfigurationFile(path, prefix) select path).OrderBy((string path) => path, StringComparer.OrdinalIgnoreCase); } private static bool IsOverrideFile(string path) { if (!IsConfigurationFile(path, "creatures") && !IsConfigurationFile(path, "ai") && !IsConfigurationFile(path, "attacks") && !IsConfigurationFile(path, "projectile") && !IsConfigurationFile(path, "levels") && !IsConfigurationFile(path, "karma")) { return IsConfigurationFile(path, "factions"); } return true; } private static bool IsConfigurationFile(string path, string prefix) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); if (prefix.Equals("factions", StringComparison.OrdinalIgnoreCase) || prefix.Equals("karma", StringComparison.OrdinalIgnoreCase)) { if (fileNameWithoutExtension.Equals(prefix, StringComparison.OrdinalIgnoreCase)) { return Path.GetExtension(path).Equals(".yml", StringComparison.OrdinalIgnoreCase); } return false; } if (!fileNameWithoutExtension.Equals(prefix, StringComparison.OrdinalIgnoreCase)) { return fileNameWithoutExtension.StartsWith(prefix + "_", StringComparison.OrdinalIgnoreCase); } return true; } private static void EnsureDirectoriesAndDefaultFiles() { Directory.CreateDirectory(ConfigDirectoryPath); Directory.CreateDirectory(CacheDirectoryPath); EnsureDefaultTextures(); if (!File.Exists(FactionConfigurationPath)) { File.WriteAllText(FactionConfigurationPath, CreatureFactionManager.BuildDefaultOverrideYaml()); } if (!File.Exists(LevelConfigurationPath)) { File.WriteAllText(LevelConfigurationPath, BuildDefaultLevelOverrideYaml()); } if (!File.Exists(KarmaConfigurationPath)) { File.WriteAllText(KarmaConfigurationPath, CreatureKarmaManager.BuildDefaultYaml()); } if (!File.Exists(AttackConfigurationPath)) { File.WriteAllText(AttackConfigurationPath, BuildDefaultAttackOverrideYaml()); } if (!File.Exists(ProjectileConfigurationPath)) { File.WriteAllText(ProjectileConfigurationPath, BuildDefaultProjectileOverrideYaml()); } if (!File.Exists(AiConfigurationPath)) { File.WriteAllText(AiConfigurationPath, BuildDefaultAiOverrideYaml()); } if (!File.Exists(CreatureConfigurationPath)) { File.WriteAllText(CreatureConfigurationPath, BuildDefaultOverrideYaml()); } WriteEmbeddedDefaultIfMissing(CreatureSampleConfigurationPath, "CreatureManager.defaults.creatures.sample.yml"); WriteEmbeddedDefaultIfMissing(AttackSampleConfigurationPath, "CreatureManager.defaults.attacks.sample.yml"); } internal static void EnsureDefaultTextures() { Directory.CreateDirectory(TextureDirectoryPath); ConfigEntry generateSampleTextures = CreatureManagerPlugin.GenerateSampleTextures; if (generateSampleTextures == null || generateSampleTextures.Value != CreatureManagerPlugin.Toggle.Off) { string[] defaultTextureFileNames = DefaultTextureFileNames; foreach (string text in defaultTextureFileNames) { WriteEmbeddedDefaultIfMissing(Path.Combine(TextureDirectoryPath, text), "CreatureManager.defaults.textures." + text); } } } private static void WriteEmbeddedDefaultIfMissing(string path, string resourceName) { if (File.Exists(path)) { return; } using Stream stream = typeof(CreatureDomainManager).Assembly.GetManifestResourceStream(resourceName); if (stream == null) { CreatureManagerPlugin.Log.LogError((object)("Embedded default resource '" + resourceName + "' was not found.")); return; } try { using FileStream destination = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.Read); stream.CopyTo(destination); } catch (IOException) when (File.Exists(path)) { } } private static void AppendIndented(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static string FormatLevelFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string BuildDefaultLevelOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "CreatureManager level configuration."); AppendTemplateComment(stringBuilder, "Loaded files: levels.yml, levels.yaml, levels_*.yml, levels_*.yaml."); AppendTemplateComment(stringBuilder, "A YAML reload does not reroll completed creatures: stored levels, health/damage multipliers, and modifier state remain unchanged."); AppendTemplateComment(stringBuilder, "Visual scale is resolved again when visuals rebuild, and an external SetLevel recalculates health, damage, and visual scale from the current rules."); AppendTemplateComment(stringBuilder, "BepInEx 'Enable Level System' is the master switch. BepInEx 'Biome Level Preset' adds built-in biome rules."); AppendTemplateComment(stringBuilder, "BepInEx 'Global Modifiers', 'Boss Modifiers', and 'Enforcer Modifiers' independently gate modifier rolls/effects for those creature classes."); AppendTemplateComment(stringBuilder, "Top-level targets: Global, Boss, biome names, group names, or prefab names."); AppendTemplateComment(stringBuilder, "Non-boss and Enforcer specificity: prefab > group > biome > Global. Omitted fields fall back independently."); AppendTemplateComment(stringBuilder, "Regular boss specificity: prefab > group > Boss. Missing fields never fall back to Global; level may use the opt-in biome preset."); AppendTemplateComment(stringBuilder, "For any modifiers field, omission or {} keeps lower-priority fallback; a mapping overrides only listed values."); AppendTemplateComment(stringBuilder, "Use modifiers: [] as a terminal clear that blocks every lower-priority modifier source for that target."); AppendTemplateComment(stringBuilder, "Enforcer summons ignore Boss and continue to use prefab/group, biome, and Global rules even when their source prefab is a boss."); AppendTemplateComment(stringBuilder, "Nested biome rules under a prefab/group beat the same target without a biome. User biome targets do not affect regular bosses; built-in preset biome levels can be opted in by config."); AppendTemplateComment(stringBuilder, "Rules are selected by the current network owner from the server-synchronized definitions when a creature is processed."); AppendTemplateComment(stringBuilder, "Modifiers are rolled at most once per group. Group totals below 100 leave the remainder as no modifier; totals above 100 are normalized as weights."); AppendTemplateComment(stringBuilder, "Reaping healing, max health, and damage work in dungeons, but new Reaping scale gains are disabled there."); AppendTemplateComment(stringBuilder, "CreatureManager does not manage loot/drop from this domain."); AppendTemplateBlankLine(stringBuilder); stringBuilder.AppendLine("Global:"); AppendIndented(stringBuilder, 1, "level: [80, 20] # Fallback level weights. [80, 20] = level 1 weight 80, level 2 weight 20."); AppendIndented(stringBuilder, 1, "scalePerLevel: 0.1 # Visual LevelEffects scale per level above 1. Always skipped in dungeons; saddle-able creatures are controlled by config."); AppendIndented(stringBuilder, 1, "damage: 1 # Outgoing damage multiplier. Omit or keep 1 to keep baseline."); AppendIndented(stringBuilder, 1, "damagePerLevel: 0.25 # Extra outgoing damage per level above 1: damage * (1 + (level - 1) * value)."); AppendIndented(stringBuilder, 1, "health: 1 # Base max-health multiplier. Omit or keep 1 to keep level 1 baseline."); AppendIndented(stringBuilder, 1, "healthPerLevel: 1 # Max-health growth per level above 1, based on level 1 health. 1 keeps vanilla level growth."); AppendTemplateLine(stringBuilder, 1, "distanceScaling: [0.03, 0.08, 1000, 5] # damagePerStep, healthPerStep, interval, maxSteps. maxSteps 0 = no cap."); AppendTemplateLine(stringBuilder, 1, "modifierDistanceScaling: [0.03, 1000, 8] # Each chance * (1 + 0.03 * steps), 1 step per 1000 distance, max 8; e.g. 20% becomes 20.6% at 1000 and 24.8% at 8000+."); AppendDefaultLevelModifiers(stringBuilder, bossDefaults: false); stringBuilder.AppendLine(); stringBuilder.AppendLine("Boss:"); AppendIndented(stringBuilder, 1, "level: [100] # Boss fallback level weights. Keeps bosses at level 1 unless overridden or Karma adds bonus levels."); AppendIndented(stringBuilder, 1, "scalePerLevel: 0.1 # Boss level scale per level above 1. Enforcer boss summons use Global.scalePerLevel instead."); AppendIndented(stringBuilder, 1, "damage: 1 # Boss outgoing damage multiplier. Omit or keep 1 to keep baseline."); AppendIndented(stringBuilder, 1, "damagePerLevel: 0.1 # Boss outgoing damage bonus per level above 1."); AppendIndented(stringBuilder, 1, "health: 1 # Boss base max-health multiplier. Keep 1 to avoid changing level 1 bosses."); AppendIndented(stringBuilder, 1, "healthPerLevel: 0.5 # Boss max-health growth per level above 1, based on level 1 health."); AppendTemplateLine(stringBuilder, 1, "distanceScaling: [0.03, 0.08, 1000, 5] # Boss damage/health distance scaling tuple."); AppendTemplateLine(stringBuilder, 1, "modifierDistanceScaling: [0.02, 1000, 6] # Optional Boss scaling. Omit to leave boss modifier chances unscaled by distance."); AppendDefaultLevelModifiers(stringBuilder, bossDefaults: true); stringBuilder.AppendLine(); stringBuilder.AppendLine("TentaRoot:"); AppendIndented(stringBuilder, 1, "modifiers: [] # Terminal clear: disable modifier rolls and block lower-specificity fallback for this prefab."); stringBuilder.AppendLine(); AppendTemplateComment(stringBuilder, "Optional examples. Uncomment or copy only the blocks you want to use."); AppendTemplateLine(stringBuilder, 0, "groups:"); AppendTemplateLine(stringBuilder, 1, "ForestBrutes:"); AppendTemplateLine(stringBuilder, 2, "- Troll"); AppendTemplateLine(stringBuilder, 2, "- Greydwarf_Elite"); AppendTemplateLine(stringBuilder, 2, "- Greydwarf_Shaman"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "Meadows:"); AppendTemplateLine(stringBuilder, 1, "level: [100] # Biome rule for non-boss creatures in Meadows."); AppendTemplateLine(stringBuilder, 1, "health: 2 # Level 1 max health becomes 2x baseline."); AppendTemplateLine(stringBuilder, 1, "healthPerLevel: 0.5 # Health = level1Health * 2 * (1 + (level - 1) * 0.5): level 1=2x, level 2=3x, level 3=4x."); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "ForestBrutes:"); AppendTemplateLine(stringBuilder, 1, "level: [50, 30, 20] # Group rule. Applies to prefabs listed in groups.ForestBrutes."); AppendTemplateLine(stringBuilder, 1, "damage: 1.05"); AppendTemplateLine(stringBuilder, 1, "health: 1.1"); AppendTemplateLine(stringBuilder, 1, "distanceScaling: [0.05, 0.1, 1000] # maxSteps omitted or 0 = no cap."); AppendTemplateLine(stringBuilder, 1, "modifierDistanceScaling: [0.02, 1000, 6]"); AppendTemplateLine(stringBuilder, 1, "modifiers:"); AppendIndented(stringBuilder, 2, "# Offense: Enraged to Undodgeable"); AppendTemplateLine(stringBuilder, 2, "enraged: 10"); AppendTemplateLine(stringBuilder, 2, "spirit: 5, 0.25"); AppendIndented(stringBuilder, 2, "# Defense: Armored to Chameleon"); AppendTemplateLine(stringBuilder, 2, "armored: 20, 0.4"); AppendTemplateLine(stringBuilder, 2, "deathward: 5, 0.25, 60, 3"); AppendTemplateLine(stringBuilder, 2, "vortex: 5, 0.5"); AppendTemplateLine(stringBuilder, 2, "unflinching: 5"); AppendTemplateLine(stringBuilder, 2, "chameleon: 5, 10"); AppendIndented(stringBuilder, 2, "# Affliction: Exposed to ToxicDeath"); AppendTemplateLine(stringBuilder, 2, "withered: 10, 0.5, 0.5, 5"); AppendIndented(stringBuilder, 2, "# Special: Swift to Blamer"); AppendTemplateLine(stringBuilder, 2, "reaping: 5, 0.15, 20, 0.1, 2, 0.05, 1, 0.02, 0.4"); AppendTemplateLine(stringBuilder, 2, "blink: 5, 1, 16, fx_Adrenaline1"); AppendTemplateLine(stringBuilder, 2, "omen: 5, 0.25"); AppendTemplateLine(stringBuilder, 2, "juggernaut: 5, 80, 5"); AppendTemplateLine(stringBuilder, 2, "blamer: 5, 1, 60, 0.75"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "Troll:"); AppendTemplateLine(stringBuilder, 1, "level: [0, 0, 10, 5] # Prefab rule. 0 weights skip lower levels."); AppendTemplateLine(stringBuilder, 1, "damage: 1.15"); AppendTemplateLine(stringBuilder, 1, "damagePerLevel: 0.5"); AppendTemplateLine(stringBuilder, 1, "health: 1.25"); AppendTemplateLine(stringBuilder, 1, "healthPerLevel: 0.75"); AppendTemplateLine(stringBuilder, 1, "modifierDistanceScaling: [0, 1000, 0] # Disable inherited modifier chance distance scaling for this prefab."); AppendTemplateLine(stringBuilder, 1, "modifiers:"); AppendIndented(stringBuilder, 2, "# Offense: Enraged to Undodgeable"); AppendTemplateLine(stringBuilder, 2, "enraged: 25, 0.6"); AppendIndented(stringBuilder, 2, "# Defense: Armored to Chameleon"); AppendTemplateLine(stringBuilder, 2, "armored: 50, 0.5"); AppendTemplateLine(stringBuilder, 2, "reflection: 10, 0.2, 0.5"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "VC_Vaettr:"); AppendTemplateLine(stringBuilder, 1, "Meadows:"); AppendTemplateLine(stringBuilder, 2, "level: [100] # Nested biome rule: only this prefab/group in Meadows."); AppendTemplateLine(stringBuilder, 2, "damage: 0.4"); AppendTemplateBlankLine(stringBuilder); AppendLevelPresetTemplateComments(stringBuilder); return stringBuilder.ToString(); } private static void AppendDefaultLevelModifiers(StringBuilder builder, bool bossDefaults) { string text = (bossDefaults ? "10" : "5"); string text2 = (bossDefaults ? "0.002" : "0.01"); string text3 = (bossDefaults ? "0" : text); string text4 = (bossDefaults ? "1" : "0.5"); string text5 = (bossDefaults ? "60" : "45"); AppendIndented(builder, 1, "modifiers: # At most one modifier per group. Special tuples are documented inline."); AppendIndented(builder, 2, "# Offense: Enraged to Undodgeable"); AppendIndented(builder, 2, "enraged: " + text + ", 0.15 # chance%, outgoingDamageBonus."); AppendIndented(builder, 2, "fire: " + text + ", 0.2 # chance%, addedFireDamage."); AppendIndented(builder, 2, "frost: " + text + ", 0.1 # chance%, addedFrostDamage."); AppendIndented(builder, 2, "lightning: " + text + ", 0.1 # chance%, addedLightningDamage."); AppendIndented(builder, 2, "spirit: " + text + ", 0.05 # chance%, addedSpiritDoT."); AppendIndented(builder, 2, "armorPiercing: " + text + ", 0.3 # chance%, ignoredPlayerArmor."); AppendIndented(builder, 2, "staggering: " + text + ", 0.6 # chance%, staggerBonus."); AppendIndented(builder, 2, "undodgeable: " + text + ", 0.25 # chance%, damageReduction; attacks against players ignore dodge invulnerability."); AppendIndented(builder, 2, "# Defense: Armored to Chameleon"); AppendIndented(builder, 2, "armored: " + text + ", 0.3 # chance%, damageReduction."); AppendIndented(builder, 2, "deathward: " + text + ", 0.2, 10, 3 # chance%, restoredHealth, cooldownSeconds, maxActivations."); AppendIndented(builder, 2, "regenerating: " + text + ", " + text2 + " # chance%, maxHealthRegenPerSecond."); AppendIndented(builder, 2, "reflection: " + text + ", 0.1, 0.5 # chance%, actualMeleeDamageReflected, procChance."); AppendIndented(builder, 2, "vortex: " + text + ", 0.5 # chance%, projectileIgnoreProc."); AppendIndented(builder, 2, "adaptive: " + text + ", 0.5 # chance%, rememberedTypeDamageReduction."); AppendIndented(builder, 2, "unflinching: " + text + " # chance%; prevents normal-hit and perfect-parry stagger."); AppendIndented(builder, 2, "chameleon: " + text + ", 10 # chance%, immunitySwitchSeconds."); AppendIndented(builder, 2, "# Affliction: Exposed to ToxicDeath"); AppendIndented(builder, 2, "exposed: " + text + ", 0.2, 0.5, 5 # chance%, damageTaken, proc, duration."); AppendIndented(builder, 2, "weakened: " + text + ", 0.2, 0.5, 5 # chance%, outgoingDamageReduction, proc, duration."); AppendIndented(builder, 2, "withered: " + text + ", 0.5, 0.5, 5 # chance%, healingReduction, proc, duration."); AppendIndented(builder, 2, "crippling: " + text + ", 0.5, 0.5, 0.5, 5 # chance%, moveReduction, jumpReduction, proc, duration."); AppendIndented(builder, 2, "disruptive: " + text + ", 0.5, 0.5, 0.5, 5 # chance%, staminaRegenReduction, eitrRegenReduction, proc, duration."); AppendIndented(builder, 2, "adrenalineDrain: " + text + ", 0.5, 0.5, 0.5, 5 # chance%, currentAdrenalineRemoved, adrenalineGainReduction, procChance, duration."); AppendIndented(builder, 2, "corrosive: " + text + ", 0.5, 0.5, 5 # chance%, durabilityLossBonus, procChance, duration. Equipped armor, weapons, and shield only."); AppendIndented(builder, 2, "toxicDeath: 10, 0.3, 4, blob_aoe # chance%, maxHealthDamage, radius, triggerEffect."); AppendIndented(builder, 2, "# Special: Swift to Blamer"); AppendIndented(builder, 2, "swift: " + text + ", 0.4 # chance%, movementSpeedBonus."); AppendIndented(builder, 2, "attackSpeed: " + text + ", 0.3 # chance%, attackSpeedBonus."); AppendIndented(builder, 2, "vampiric: " + text + ", 0.3 # chance%, actualDirectDamageHealing."); AppendIndented(builder, 2, "reaping: " + text + ", 0.05, 20, 0.1, 2, 0.01, 0.2, 0.05, 0.5 # chance%, heal/base, healMaxActivations, maxHealth/base, maxHealthCap, damagePerKill, damageCap, scalePerKill, scaleCap. New scale gains are disabled in dungeons."); AppendIndented(builder, 2, "blink: " + text + ", 6, 16, fx_Adrenaline1 # chance%, cooldown, maxRange, startEffect."); AppendIndented(builder, 2, "omen: " + text + ", 0.5 # chance%, forcedEnforcerChance."); AppendIndented(builder, 2, "juggernaut: " + text + ", 150, 5 # chance%, minimumPushForce, cooldownSeconds."); AppendIndented(builder, 2, "blamer: " + text3 + ", " + text4 + ", " + text5 + ", 0.75 # chance%, karmaPerSecond, maxKarmaGain, fleeHealthRatio. 0 cap is unlimited."); } private static void AppendLevelPresetTemplateComments(StringBuilder builder) { AppendTemplateComment(builder, "Built-in biome level distributions used by the BepInEx 'Biome Level Preset' option."); AppendTemplateComment(builder, "The selected preset supplies these biome defaults. To customize, uncomment or copy only the biome blocks you want to override."); AppendTemplateComment(builder, "All other biomes keep following the selected preset."); CreatureManagerPlugin.LevelBiomePreset[] array = new CreatureManagerPlugin.LevelBiomePreset[4] { CreatureManagerPlugin.LevelBiomePreset.Easy, CreatureManagerPlugin.LevelBiomePreset.Normal, CreatureManagerPlugin.LevelBiomePreset.Hard, CreatureManagerPlugin.LevelBiomePreset.VeryHard }; foreach (CreatureManagerPlugin.LevelBiomePreset levelBiomePreset in array) { AppendTemplateBlankLine(builder); AppendTemplateComment(builder, $"{levelBiomePreset} preset:"); foreach (KeyValuePair levelPresetWeight in GetLevelPresetWeights(levelBiomePreset)) { AppendTemplateLine(builder, 0, levelPresetWeight.Key + ":"); AppendTemplateLine(builder, 1, "level: [" + string.Join(", ", levelPresetWeight.Value.Select(FormatLevelFloat)) + "]"); } } } private static string BuildDefaultOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "CreatureManager creature configuration."); AppendTemplateComment(stringBuilder, "Copy entries from creatures.reference.yml, or run cm:full creature to generate creatures.full.yml for exhaustive field examples."); AppendTemplateComment(stringBuilder, "Loaded files: creatures.yml, creatures.yaml, creatures_*.yml, creatures_*.yaml."); AppendTemplateComment(stringBuilder, "Hot reload updates templates used by later creature instances; already loaded creature GameObjects are not rebuilt."); AppendTemplateComment(stringBuilder, "A persistent creature instantiated again after zone unload uses the current template. Changing clonedFrom for an existing clone requires a restart."); AppendTemplateComment(stringBuilder, "Omitted fields keep the current creature value. The schema below is commented out and safe to leave in the file."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Schema:"); AppendTemplateLine(stringBuilder, 0, "- prefab: EikthyrClone # target creature prefab id; use an existing prefab or a new clone name."); AppendTemplateLine(stringBuilder, 1, "enabled: true # false skips this creature entry."); AppendTemplateLine(stringBuilder, 1, "clonedFrom: Eikthyr # optional source creature prefab used to create prefab."); AppendTemplateLine(stringBuilder, 1, "ai: eikthyr_like # optional AI preset from ai.yml, or an existing creature prefab name."); AppendTemplateLine(stringBuilder, 1, "character:"); AppendTemplateLine(stringBuilder, 2, "name: $enemy_eikthyr # existing token, localization/.yml token, or literal display name."); AppendTemplateLine(stringBuilder, 2, "faction: Boss # Vanilla Character.Faction or a custom name from factions.yml."); AppendTemplateLine(stringBuilder, 2, "boss: true, false, event_eikthyr # boss, dontHideBossHud, bossEvent."); AppendTemplateLine(stringBuilder, 2, "defeatSetGlobalKey: defeated_eikthyr # global key set when defeated."); AppendTemplateLine(stringBuilder, 2, "health: 500, 3600 # health, regenAllHPTime."); AppendTemplateLine(stringBuilder, 2, "damageModifiers:"); AppendTemplateLine(stringBuilder, 3, "blunt: Normal # Normal, Resistant, VeryResistant, Weak, VeryWeak, Immune, Ignore, SlightlyResistant."); AppendTemplateLine(stringBuilder, 3, "slash: Normal"); AppendTemplateLine(stringBuilder, 3, "pierce: Normal"); AppendTemplateLine(stringBuilder, 3, "chop: Ignore"); AppendTemplateLine(stringBuilder, 3, "pickaxe: Ignore"); AppendTemplateLine(stringBuilder, 3, "fire: Weak"); AppendTemplateLine(stringBuilder, 3, "frost: Normal"); AppendTemplateLine(stringBuilder, 3, "lightning: Normal"); AppendTemplateLine(stringBuilder, 3, "poison: Normal"); AppendTemplateLine(stringBuilder, 3, "spirit: Normal"); AppendTemplateLine(stringBuilder, 2, "speed: 2, 5, 10, 300, 20, 300, 1 # crouchSpeed, walkSpeed, speed, turnSpeed, runSpeed, runTurnSpeed, acceleration."); AppendTemplateLine(stringBuilder, 2, "jump: 10, 0, 0.7, 0.1, 10 # jumpForce, jumpForceForward, jumpForceTiredFactor, airControl, jumpStaminaUsage."); AppendTemplateLine(stringBuilder, 2, "swim: true, 2, 2, 100, 0.05 # canSwim, swimDepth, swimSpeed, swimTurnSpeed, swimAcceleration."); AppendTemplateLine(stringBuilder, 2, "flight: false, 5, 12, 12 # flying, flySlowSpeed, flyFastSpeed, flyTurnSpeed."); AppendTemplateLine(stringBuilder, 1, "humanoid:"); AppendTemplateLine(stringBuilder, 2, "defaultItems: # attack prefabs first, then loadout/appearance item prefabs."); AppendTemplateLine(stringBuilder, 3, "- attack_eikthyr_stomp"); AppendTemplateLine(stringBuilder, 3, "- attack_bow_alt1"); AppendTemplateLine(stringBuilder, 3, "- NPC_SwordIron_Right"); AppendTemplateLine(stringBuilder, 2, "randomWeapon: [NPC_SwordIron_Right] # Humanoid random weapon prefab list."); AppendTemplateLine(stringBuilder, 2, "randomArmor: [] # Humanoid random armor prefab list."); AppendTemplateLine(stringBuilder, 2, "randomHair: [] # Visual-only attach/attach_skin hair; does not use the helmet slot or grant item armor/effects."); AppendTemplateLine(stringBuilder, 2, "randomShield: [] # Humanoid random shield prefab list."); AppendTemplateLine(stringBuilder, 2, "randomItems: # prefab[, chance]."); AppendTemplateLine(stringBuilder, 3, "- NPC_HelmetIron_Worn0, 0.5"); AppendTemplateLine(stringBuilder, 2, "randomSets: # setName, itemPrefab..."); AppendTemplateLine(stringBuilder, 3, "- warrior, NPC_SwordIron_Right, NPC_ShieldIron_Worn"); AppendTemplateLine(stringBuilder, 1, "scale: 1.1 # Uniform creature scale; equipment visuals and a target-specific death ragdoll follow it."); AppendTemplateLine(stringBuilder, 1, "textures: # rendererName:materialIndex:textureName; matching death-ragdoll renderers receive the same override."); AppendTemplateLine(stringBuilder, 2, "- 'Body:0:eikthyr_clone.png'"); AppendTemplateLine(stringBuilder, 1, "appearance: # Optional mapping; omitted fields inherit the source prefab and specified fields follow its death ragdoll."); AppendTemplateLine(stringBuilder, 2, "hair: NPC_Black_Hair5 # Item prefab name; use '' to clear ordinary hair."); AppendTemplateLine(stringBuilder, 2, "beard: Beard1 # Item prefab name; use '' to clear ordinary beard."); AppendTemplateLine(stringBuilder, 2, "hairColor: '#2A1B12' # #RRGGBB; also tints humanoid.randomHair and its ragdoll."); AppendTemplateLine(stringBuilder, 2, "skinColor: '#AD7A55' # #RRGGBB."); AppendTemplateLine(stringBuilder, 2, "modelIndex: 0 # Zero-based VisEquipment model index."); AppendTemplateLine(stringBuilder, 1, "availableAttackAnimations: [] # reference-only attackAnimation names generated in full scaffold; ignored in config."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Examples:"); AppendTemplateLine(stringBuilder, 0, "- prefab: EikthyrClone"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: Eikthyr # Creates a new creature prefab before applying the fields below."); AppendTemplateLine(stringBuilder, 1, "character:"); AppendTemplateLine(stringBuilder, 2, "health: 1200, 3600"); AppendTemplateLine(stringBuilder, 1, "scale: 1.2"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- prefab: Troll"); AppendTemplateLine(stringBuilder, 1, "enabled: false # Keeps this configuration entry loaded in the file but prevents it from applying."); AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static string BuildDefaultAiOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "CreatureManager AI configuration."); AppendTemplateComment(stringBuilder, "In creatures.yml, 'ai: existingPrefabName' can copy AI directly from that creature prefab."); AppendTemplateComment(stringBuilder, "In ai.yml, a preset named like an existing prefab uses that prefab as its baseline unless copyFrom or clonedFrom is set."); AppendTemplateComment(stringBuilder, "Copy entries from ai.reference.yml into ai.yml only when you want to create or edit a reusable preset."); AppendTemplateComment(stringBuilder, "Loaded files: ai.yml, ai.yaml, ai_*.yml, ai_*.yaml."); AppendTemplateComment(stringBuilder, "Hot reload updates AI templates used by later creature instances; loaded BaseAI and MonsterAI components keep their current values."); AppendTemplateComment(stringBuilder, "Omitted fields keep the baseline AI value. If no baseline is found, they keep the current target AI value."); AppendTemplateComment(stringBuilder, "Effect lists are intentionally not managed here."); AppendTemplateComment(stringBuilder, "Use baseAI for shared BaseAI fields. AnimalAI creatures use baseAI only; monsterAI adds MonsterAI-only fields."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Schema:"); AppendTemplateLine(stringBuilder, 0, "- ai: draugr_melee # reusable AI preset name."); AppendTemplateLine(stringBuilder, 1, "enabled: true # false skips this AI preset."); AppendTemplateLine(stringBuilder, 1, "copyFrom: '' # optional parent AI preset or creature prefab applied before this entry."); AppendTemplateLine(stringBuilder, 1, "clonedFrom: Draugr # optional AI prefab baseline copied before overrides."); AppendTemplateLine(stringBuilder, 1, "baseAI:"); AppendTemplateLine(stringBuilder, 2, "senses: [30, 90, 999, false] # viewRange, viewAngle, hearRange, mistVision."); AppendTemplateLine(stringBuilder, 2, "idleSound: [5, 0.5] # idleSoundInterval, idleSoundChance."); AppendTemplateLine(stringBuilder, 2, "movement: [false, Humanoid, 5, true] # patrol, pathAgentType, moveMinAngle, smoothMovement."); AppendTemplateLine(stringBuilder, 2, "serpent: [false, 20] # serpentMovement, serpentTurnRadius."); AppendTemplateLine(stringBuilder, 2, "randomMove: [0, 5, 10, 20] # jumpInterval, randomCircleInterval, randomMoveInterval, randomMoveRange."); AppendTemplateLine(stringBuilder, 2, "flight: [false, 0, 0, 5, 5, 2, 1, 2, 5, 0] # randomFly, chanceToTakeoff, chanceToLand, groundDuration, airDuration, maxLandAltitude, takeoffTime, flyAltitudeMin, flyAltitudeMax, flyAbsMinAltitude."); AppendTemplateLine(stringBuilder, 2, "avoid: [false, false, false, false, false, false] # avoidFire, afraidOfFire, avoidWater, avoidLava, skipLavaTargets, avoidLavaFlee."); AppendTemplateLine(stringBuilder, 2, "flee: [0, 90, 2] # fleeRange, fleeAngle, fleeInterval."); AppendTemplateLine(stringBuilder, 2, "aggressive: [true, false] # aggravatable, passiveAggressive."); AppendTemplateLine(stringBuilder, 2, "messages: ['', '', ''] # spawnMessage, deathMessage, alertedMessage."); AppendTemplateLine(stringBuilder, 1, "monsterAI:"); AppendTemplateLine(stringBuilder, 2, "alertRange: 20"); AppendTemplateLine(stringBuilder, 2, "hunt: [true, true, 3] # enableHuntPlayer, attackPlayerObjects, privateAreaTriggerThreshold."); AppendTemplateLine(stringBuilder, 2, "chase: [0, 999, 200, 2] # interceptTimeMin, interceptTimeMax, maxChaseDistance, minAttackInterval."); AppendTemplateLine(stringBuilder, 2, "circle: [5, 2, 6] # circleTargetInterval, circleTargetDuration, circleTargetDistance."); AppendTemplateLine(stringBuilder, 2, "hurtFlee: [false, 0, 0, false, 0, 0, false] # fleeIfHurtWhenTargetCantBeReached, fleeUnreachableSinceAttacking, fleeUnreachableSinceHurt, fleeIfNotAlerted, fleeIfLowHealth, fleeTimeSinceHurt, fleeInLava."); AppendTemplateLine(stringBuilder, 2, "charge: [false, false] # circulateWhileCharging, circulateWhileChargingFlying."); AppendTemplateLine(stringBuilder, 2, "sleep: [false, 20, true, 50, 0, 0, 999] # sleeping, wakeupRange, noiseWakeup, maxNoiseWakeupRange, wakeUpDelayMin, wakeUpDelayMax, fallAsleepDistance."); AppendTemplateLine(stringBuilder, 2, "avoidLand: false"); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Examples:"); AppendTemplateLine(stringBuilder, 0, "- ai: Draugr # same as an existing prefab, so Draugr is the implicit baseline."); AppendTemplateLine(stringBuilder, 1, "baseAI:"); AppendTemplateLine(stringBuilder, 2, "senses: [40, 120, 999, false]"); AppendTemplateLine(stringBuilder, 1, "monsterAI:"); AppendTemplateLine(stringBuilder, 2, "chase: [0, 999, 400, 1]"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- ai: vincent_archer # custom preset name, so copyFrom chooses the baseline."); AppendTemplateLine(stringBuilder, 1, "copyFrom: GoblinArcher"); AppendTemplateLine(stringBuilder, 1, "monsterAI:"); AppendTemplateLine(stringBuilder, 2, "circle: [8, 6, 8]"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- ai: Boar # AnimalAI uses baseAI only."); AppendTemplateLine(stringBuilder, 1, "baseAI:"); AppendTemplateLine(stringBuilder, 2, "senses: [20, 90, 999, false]"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- ai: disabled_example"); AppendTemplateLine(stringBuilder, 1, "enabled: false"); AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static string BuildDefaultAttackOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "CreatureManager attack configuration."); AppendTemplateComment(stringBuilder, "Copy entries from attacks.reference.yml and use the schema below for optional fields."); AppendTemplateComment(stringBuilder, "Loaded files: attacks.yml, attacks.yaml, attacks_*.yml, attacks_*.yaml."); AppendTemplateComment(stringBuilder, "Hot reload updates attack item templates used by later creature instances; loaded creatures keep their equipped attack data."); AppendTemplateComment(stringBuilder, "Omitted fields keep the current attack prefab value. The schema below is commented out and safe to leave in the file."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Schema:"); AppendTemplateLine(stringBuilder, 0, "- prefab: attack_bow_alt1 # target attack item prefab id; use an existing prefab or a new clone name."); AppendTemplateLine(stringBuilder, 1, "enabled: true # false skips this attack entry."); AppendTemplateLine(stringBuilder, 1, "clonedFrom: attack_bow # optional source attack item prefab used to create prefab."); AppendTemplateLine(stringBuilder, 1, "damage: { pierce: 50, poison: 50, attackForce: 15, toolTier: 0 } # damage keys plus attackForce and toolTier."); AppendTemplateLine(stringBuilder, 1, "attack: [Projectile, bow_fire] # Attack.AttackType, animation name."); AppendTemplateLine(stringBuilder, 1, "statusEffect: [Puke, 0.4] # ObjectDB StatusEffect name, application chance from 0 to 1."); AppendTemplateLine(stringBuilder, 1, "projectile: [BlobTar_projectile, 30, 2, 1] # projectile prefab, velocity, accuracy, projectile count."); AppendTemplateLine(stringBuilder, 1, "ai: [30, 2, 20, 25] # aiAttackInterval, aiAttackRangeMin, aiAttackRange, aiAttackMaxAngle."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Examples:"); AppendTemplateLine(stringBuilder, 0, "- prefab: attack_bow_alt1"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: attack_bow # Creates a new attack prefab before applying the fields below."); AppendTemplateLine(stringBuilder, 1, "damage: { pierce: 50, poison: 50, attackForce: 15, toolTier: 0 }"); AppendTemplateLine(stringBuilder, 1, "attack: [Projectile, bow_fire]"); AppendTemplateLine(stringBuilder, 1, "statusEffect: [Puke, 0.4]"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- prefab: StaffLightning"); AppendTemplateLine(stringBuilder, 1, "enabled: false # Keeps this configuration entry loaded in the file but prevents it from applying."); AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static string BuildDefaultProjectileOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "CreatureManager projectile configuration."); AppendTemplateComment(stringBuilder, "Copy entries from projectile.reference.yml and use only the fields that should change."); AppendTemplateComment(stringBuilder, "Loaded files: projectile.yml, projectile.yaml, projectile_*.yml, projectile_*.yaml."); AppendTemplateComment(stringBuilder, "Already flying projectiles keep their values. Any projectile instantiated after a hot reload uses the current template, even when fired by an existing creature."); AppendTemplateComment(stringBuilder, "Omitted fields inherit the source prefab. Clones retain every unexposed component and field unchanged."); AppendTemplateComment(stringBuilder, "usedByAttacks is generated reference metadata. It is accepted in this file but never changes runtime behavior."); AppendTemplateComment(stringBuilder, "There is no type field; projectile and spawnAbility blocks already identify the supported component."); AppendTemplateComment(stringBuilder, "spawnAbility.spawnPrefabs uses Prefab[:weight]. Weight defaults to 1, is limited to 1000, and expanded lists are limited to 4096 slots."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Schema:"); AppendTemplateLine(stringBuilder, 0, "- prefab: CM_BombBlob_Tar_projectile # existing target prefab id or a new clone name."); AppendTemplateLine(stringBuilder, 1, "enabled: true"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: BombBlob_Tar_projectile # optional source used to clone the entire prefab."); AppendTemplateLine(stringBuilder, 1, "usedByAttacks: [BombBlob_Tar] # informational only; ignored when applying this entry."); AppendTemplateLine(stringBuilder, 1, "projectile:"); AppendTemplateLine(stringBuilder, 2, "spawnOnHit: BlobTar # replace with a prefab name, or use null to clear it explicitly."); AppendTemplateLine(stringBuilder, 1, "spawnAbility:"); AppendTemplateLine(stringBuilder, 2, "spawnPrefabs: [Mistile] # whole-list replacement using Prefab[:weight]; omitted weight is 1."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Examples:"); AppendTemplateLine(stringBuilder, 0, "- prefab: CM_DvergerMistile_spawn"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: DvergerMistile_spawn"); AppendTemplateLine(stringBuilder, 1, "spawnAbility:"); AppendTemplateLine(stringBuilder, 2, "spawnPrefabs: [Mistile]"); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- prefab: CM_bonemass_spawn"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: bonemass_spawn"); AppendTemplateLine(stringBuilder, 1, "spawnAbility:"); AppendTemplateLine(stringBuilder, 2, "spawnPrefabs: [Skeleton:10, Blob] # 10:1 selection weight; each spawn is rolled independently."); AppendTemplateBlankLine(stringBuilder); AppendTemplateLine(stringBuilder, 0, "- prefab: CM_BombBlob_Tar_projectile"); AppendTemplateLine(stringBuilder, 1, "clonedFrom: BombBlob_Tar_projectile"); AppendTemplateLine(stringBuilder, 1, "projectile:"); AppendTemplateLine(stringBuilder, 2, "spawnOnHit: null # explicit clear; omitting projectile keeps the inherited value."); AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static void AppendTemplateComment(StringBuilder builder, string text) { builder.Append("# "); builder.AppendLine(text); } private static void AppendTemplateLine(StringBuilder builder, int indent, string text) { builder.Append("# "); builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendTemplateBlankLine(StringBuilder builder) { builder.AppendLine("#"); } private static string GetTransformPath(Transform transform) { List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } } internal static class CreatureFactionManager { private sealed class FactionData { public HashSet Friendly { get; } public HashSet? AggravatedFriendly { get; } public HashSet? AlertedFriendly { get; } public FactionData(HashSet friendly, HashSet? aggravatedFriendly, HashSet? alertedFriendly) { Friendly = friendly; AggravatedFriendly = aggravatedFriendly; AlertedFriendly = alertedFriendly; } } private sealed class FactionSnapshot { public Dictionary NameToFaction { get; } public Dictionary FactionToName { get; } public Dictionary FactionDataByFaction { get; } public HashSet Aggravatable { get; } public FactionSnapshot(Dictionary nameToFaction, Dictionary factionToName, Dictionary factionDataByFaction, HashSet aggravatable) { NameToFaction = nameToFaction; FactionToName = factionToName; FactionDataByFaction = factionDataByFaction; Aggravatable = aggravatable; } } private const int CustomFactionStartId = 100; private static readonly int FactionHash; private static readonly object Sync; private static Dictionary NameToFaction; private static Dictionary FactionToName; private static Dictionary FactionDataByFaction; private static HashSet Aggravatable; static CreatureFactionManager() { FactionHash = StringExtensionMethods.GetStableHashCode("faction"); Sync = new object(); NameToFaction = new Dictionary(StringComparer.OrdinalIgnoreCase); FactionToName = new Dictionary(); FactionDataByFaction = new Dictionary(); Aggravatable = new HashSet(); Load(DefaultFactionDefinitions()); } internal static bool Load(List definitions) { if (definitions.Count == 0) { definitions = DefaultFactionDefinitions(); } if (!TryBuildSnapshot(definitions, out FactionSnapshot snapshot, out List errors)) { foreach (string item in errors) { CreatureManagerPlugin.Log.LogError((object)("Faction configuration rejected: " + item)); } CreatureManagerPlugin.Log.LogWarning((object)$"Faction configuration was not applied because {errors.Count} validation error(s) were found; the previously published faction rules remain active."); return false; } lock (Sync) { NameToFaction = snapshot.NameToFaction; FactionToName = snapshot.FactionToName; FactionDataByFaction = snapshot.FactionDataByFaction; Aggravatable = snapshot.Aggravatable; } RefreshLiveBaseAis(); return true; } private static bool TryBuildSnapshot(IReadOnlyList definitions, out FactionSnapshot snapshot, out List errors) { //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected I4, but got Unknown //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) errors = new List(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary2 = new Dictionary(); Dictionary dictionary3 = ((IEnumerable)Enum.GetNames(typeof(Faction))).ToDictionary((Func)((string name) => name), (Func)((string name) => (Faction)Enum.Parse(typeof(Faction), name)), (IEqualityComparer?)StringComparer.OrdinalIgnoreCase); HashSet hashSet = dictionary3.Values.Select((Faction faction) => (int)faction).ToHashSet(); HashSet hashSet2 = new HashSet(hashSet); for (int num = 0; num < definitions.Count; num++) { FactionDefinition factionDefinition = definitions[num]; if (factionDefinition == null) { errors.Add($"entry {num + 1} is null; each faction entry must be a mapping."); } else if (factionDefinition.Id.HasValue) { hashSet2.Add(factionDefinition.Id.Value); } } int nextCustomId = 100; for (int num2 = 0; num2 < definitions.Count; num2++) { FactionDefinition factionDefinition2 = definitions[num2]; if (factionDefinition2 == null) { continue; } string text = NormalizeName(factionDefinition2.Faction); if (text == null) { errors.Add($"entry {num2 + 1} has an empty faction name."); continue; } if (int.TryParse(text, out var _)) { errors.Add($"entry {num2 + 1} faction name '{text}' is numeric; use a non-numeric name and the id field instead."); continue; } if (dictionary.ContainsKey(text)) { errors.Add($"entry {num2 + 1} duplicates faction name '{text}' (names are case-insensitive)."); continue; } int num3; if (dictionary3.TryGetValue(text, out var value)) { num3 = (int)value; if (factionDefinition2.Id.HasValue && factionDefinition2.Id.Value != num3) { errors.Add($"entry {num2 + 1} faction '{text}' is a vanilla faction and must use id {num3}, not {factionDefinition2.Id.Value}."); continue; } } else if (factionDefinition2.Id.HasValue) { num3 = factionDefinition2.Id.Value; if (hashSet.Contains(num3)) { string vanillaNameForId = GetVanillaNameForId(dictionary3, num3); errors.Add($"entry {num2 + 1} custom faction '{text}' cannot alias vanilla faction id {num3} ({vanillaNameForId})."); continue; } } else { num3 = GetNextCustomId(hashSet2, ref nextCustomId); } Faction val = (Faction)num3; if (dictionary2.TryGetValue(val, out var value2)) { errors.Add($"entry {num2 + 1} faction '{text}' duplicates id {num3}, which is already assigned to '{value2}'."); } else { dictionary[text] = val; dictionary2[val] = text; hashSet2.Add(num3); } } Dictionary dictionary4 = new Dictionary(); HashSet hashSet3 = new HashSet(); HashSet allFactions = dictionary2.Keys.ToHashSet(); for (int num4 = 0; num4 < definitions.Count; num4++) { FactionDefinition factionDefinition3 = definitions[num4]; if (factionDefinition3 == null) { continue; } string text2 = NormalizeName(factionDefinition3.Faction); if (text2 == null || !dictionary.TryGetValue(text2, out var value3)) { continue; } HashSet factions; bool flag = TryResolveFactionSet(factionDefinition3.Friendly, dictionary, allFactions, text2, "friendly", errors, out factions); HashSet factions2; bool flag2 = TryResolveOptionalFactionSet(factionDefinition3.AggravatedFriendly, dictionary, allFactions, text2, "aggravatedFriendly", errors, out factions2); HashSet factions3; bool flag3 = TryResolveOptionalFactionSet(factionDefinition3.AlertedFriendly, dictionary, allFactions, text2, "alertedFriendly", errors, out factions3); if (flag && flag2 && flag3) { FactionData factionData = (dictionary4[value3] = new FactionData(factions, factions2, factions3)); if (factionData.AggravatedFriendly != null) { hashSet3.Add(value3); } } } snapshot = new FactionSnapshot(dictionary, dictionary2, dictionary4, hashSet3); return errors.Count == 0; } internal static bool TryGetFaction(string? name, out Faction faction) { faction = (Faction)0; string text = NormalizeName(name); if (text == null) { return false; } lock (Sync) { if (NameToFaction.TryGetValue(text, out faction)) { return true; } } if (int.TryParse(text, out var result)) { lock (Sync) { if (FactionToName.ContainsKey((Faction)result)) { faction = (Faction)result; return true; } } if (Enum.IsDefined(typeof(Faction), result)) { faction = (Faction)result; return true; } return false; } if (Enum.TryParse(text, ignoreCase: true, out faction)) { return Enum.IsDefined(typeof(Faction), faction); } return false; } internal static bool TryGetRegisteredFaction(string? name, out Faction faction, out string canonicalName) { faction = (Faction)0; canonicalName = ""; string text = NormalizeName(name); if (text == null) { return false; } lock (Sync) { if (NameToFaction.TryGetValue(text, out faction)) { canonicalName = FactionToName[faction]; return true; } if (int.TryParse(text, out var result) && FactionToName.TryGetValue((Faction)result, out canonicalName)) { faction = (Faction)result; return true; } } return false; } internal static string[] GetRegisteredFactionNames() { lock (Sync) { return (from entry in FactionToName orderby (int)entry.Key select entry.Value).ToArray(); } } internal unsafe static string GetDisplayName(Faction faction) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { string value; return FactionToName.TryGetValue(faction, out value) ? value : ((object)(*(Faction*)(&faction))/*cast due to .constrained prefix*/).ToString(); } } internal static void ApplyFactionToZdo(Character character, string configuredFaction) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected I4, but got Unknown ZNetView component = ((Component)character).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { val.Set(FactionHash, configuredFaction); val.Set(FactionHash, (int)character.m_faction, false); } } internal static void SetupBaseAi(BaseAI baseAi) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) ZNetView nview = baseAi.m_nview; ZDO val = ((nview != null) ? nview.GetZDO() : null); if (val != null) { string text = val.GetString(FactionHash, ""); if (!string.IsNullOrWhiteSpace(text) && TryGetFaction(text, out var faction)) { baseAi.m_character.m_faction = faction; } else { int num = val.GetInt(FactionHash, 0); if (num != 0) { baseAi.m_character.m_faction = (Faction)num; } } } lock (Sync) { baseAi.m_aggravatable = Aggravatable.Contains(baseAi.m_character.m_faction); } } internal static bool TryIsEnemy(Character a, Character b, out bool isEnemy) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0014: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) isEnemy = false; if ((Object)a == (Object)b) { return true; } if (!Object.op_Implicit((Object)(object)a) || !Object.op_Implicit((Object)(object)b)) { return true; } FactionData value; lock (Sync) { FactionDataByFaction.TryGetValue(a.GetFaction(), out value); } if (value == null) { return false; } bool flag = a.IsTamed(); bool flag2 = b.IsTamed(); if (flag && flag2) { isEnemy = false; return true; } if (flag) { isEnemy = BaseAI.IsEnemy(b, a); return true; } BaseAI baseAI = b.GetBaseAI(); if (a.IsPlayer() && !flag2 && Object.op_Implicit((Object)(object)baseAI) && baseAI.IsAggravatable()) { isEnemy = BaseAI.IsEnemy(b, a); return true; } string text = a.GetGroup(); if (text.Length > 0 && text == b.GetGroup()) { isEnemy = false; return true; } Faction item = b.GetFaction(); if (flag2) { item = (Faction)0; } BaseAI baseAI2 = a.GetBaseAI(); if (value.AlertedFriendly != null && Object.op_Implicit((Object)(object)baseAI2) && baseAI2.IsAlerted()) { isEnemy = !value.AlertedFriendly.Contains(item); } else if (value.AggravatedFriendly != null && Object.op_Implicit((Object)(object)baseAI2) && baseAI2.IsAggravated()) { isEnemy = !value.AggravatedFriendly.Contains(item); } else { isEnemy = !value.Friendly.Contains(item); } return true; } internal static string BuildDefaultOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# CreatureManager faction configuration."); stringBuilder.AppendLine("# Vanilla faction relationships are listed below as comments, then repeated as active YAML."); stringBuilder.AppendLine("# Edit these entries or append custom factions. Custom ids should normally start at 100."); stringBuilder.AppendLine("# friendly/aggravatedFriendly/alertedFriendly accept faction names or [All]."); stringBuilder.AppendLine("# Faction relationships are global live rules and immediately affect loaded creatures."); stringBuilder.AppendLine("# Give custom factions explicit stable ids; do not change an id while persisted creatures use it."); stringBuilder.AppendLine("#"); foreach (FactionDefinition item in DefaultFactionDefinitions()) { AppendCommentedFaction(stringBuilder, item); } stringBuilder.AppendLine(); foreach (FactionDefinition item2 in DefaultFactionDefinitions()) { AppendFaction(stringBuilder, item2, commented: false); } stringBuilder.AppendLine(); stringBuilder.AppendLine("# Custom example:"); stringBuilder.AppendLine("# - faction: MyRaiders"); stringBuilder.AppendLine("# id: 100"); stringBuilder.AppendLine("# friendly: [MyRaiders, PlainsMonsters]"); stringBuilder.AppendLine("# aggravatedFriendly: [MyRaiders]"); stringBuilder.AppendLine("# alertedFriendly: [MyRaiders]"); return stringBuilder.ToString(); } private static void AppendCommentedFaction(StringBuilder builder, FactionDefinition definition) { AppendFaction(builder, definition, commented: true); } private static void AppendFaction(StringBuilder builder, FactionDefinition definition, bool commented) { string value = (commented ? "# " : ""); builder.Append(value).Append("- faction: ").AppendLine(definition.Faction); builder.Append(value).Append(" id: ").AppendLine(definition.Id?.ToString() ?? "null"); builder.Append(value).Append(" friendly: ").AppendLine(FormatInlineList(definition.Friendly)); if (definition.AggravatedFriendly != null) { builder.Append(value).Append(" aggravatedFriendly: ").AppendLine(FormatInlineList(definition.AggravatedFriendly)); } if (definition.AlertedFriendly != null) { builder.Append(value).Append(" alertedFriendly: ").AppendLine(FormatInlineList(definition.AlertedFriendly)); } } private static string FormatInlineList(List? values) { if (values != null && values.Count != 0) { return "[" + string.Join(", ", values) + "]"; } return "[]"; } private static bool TryResolveOptionalFactionSet(List? values, Dictionary names, HashSet allFactions, string owner, string field, List errors, out HashSet? factions) { if (values == null) { factions = null; return true; } HashSet factions2; bool result = TryResolveFactionSet(values, names, allFactions, owner, field, errors, out factions2); factions = factions2; return result; } private static bool TryResolveFactionSet(List? values, Dictionary names, HashSet allFactions, string owner, string field, List errors, out HashSet factions) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) factions = new HashSet(); if (values == null) { return true; } bool result = true; for (int i = 0; i < values.Count; i++) { string text = NormalizeName(values[i]); if (text == null) { errors.Add($"faction '{owner}' field '{field}' contains an empty target at position {i + 1}."); result = false; continue; } if (string.Equals(text, "All", StringComparison.OrdinalIgnoreCase)) { factions.UnionWith(allFactions); continue; } if (names.TryGetValue(text, out var value)) { factions.Add(value); continue; } errors.Add("faction '" + owner + "' field '" + field + "' references unknown faction '" + text + "'."); result = false; } return result; } private static string GetVanillaNameForId(IReadOnlyDictionary vanillaFactions, int id) { return (from entry in vanillaFactions where (int)entry.Value == id select entry.Key).FirstOrDefault() ?? "unknown"; } private static int GetNextCustomId(HashSet reservedIds, ref int nextCustomId) { while (reservedIds.Contains(nextCustomId)) { nextCustomId++; } return nextCustomId++; } private static string? NormalizeName(string? value) { string text = (value ?? "").Trim(); if (text.Length != 0) { return text; } return null; } private static void RefreshLiveBaseAis() { try { foreach (BaseAI allInstance in BaseAI.GetAllInstances()) { if (!((Object)(object)allInstance == (Object)null)) { try { SetupBaseAi(allInstance); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to refresh live faction state for '" + ((Object)allInstance).name + "': " + ex.Message)); } } } } catch (Exception ex2) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to enumerate live creatures while refreshing faction state: " + ex2.Message)); } } private static List DefaultFactionDefinitions() { List friendly = new List { "AnimalsVeg", "ForestMonsters", "Undead", "Demon", "MountainMonsters", "SeaMonsters", "PlainsMonsters", "Boss", "MistlandsMonsters", "Dverger", "TrainingDummy" }; List friendly2 = new List { "AnimalsVeg", "ForestMonsters", "Undead", "Demon", "MountainMonsters", "SeaMonsters", "PlainsMonsters", "Boss", "MistlandsMonsters", "Dverger", "PlayerSpawned", "TrainingDummy" }; List list = new List(); list.Add(Create("Players", 0, "Players", "Dverger")); list.Add(Create("AnimalsVeg", 1, "AnimalsVeg")); list.Add(Create("ForestMonsters", 2, "ForestMonsters", "AnimalsVeg", "Boss")); list.Add(Create("Undead", 3, "Undead", "Demon", "Boss")); list.Add(Create("Demon", 4, "Demon", "Undead", "Boss")); list.Add(Create("MountainMonsters", 5, "MountainMonsters", "Boss")); list.Add(Create("SeaMonsters", 6, "SeaMonsters", "Boss")); list.Add(Create("PlainsMonsters", 7, "PlainsMonsters", "Boss")); list.Add(new FactionDefinition { Faction = "Boss", Id = 8, Friendly = friendly }); list.Add(Create("MistlandsMonsters", 9, "MistlandsMonsters", "AnimalsVeg", "Boss")); list.Add(new FactionDefinition { Faction = "Dverger", Id = 10, Friendly = new List { "Dverger", "AnimalsVeg", "Boss", "Players" }, AggravatedFriendly = new List { "Dverger", "AnimalsVeg", "Boss" } }); list.Add(Create("PlayerSpawned", 11, "PlayerSpawned")); list.Add(new FactionDefinition { Faction = "TrainingDummy", Id = 12, Friendly = friendly2 }); return list; } private static FactionDefinition Create(string name, int id, params string[] friendly) { return new FactionDefinition { Faction = name, Id = id, Friendly = friendly.ToList() }; } } public static class CreatureManagerFactionApi { public static bool TryResolve(string? name, out Faction faction) { string canonicalName; return CreatureFactionManager.TryGetRegisteredFaction(name, out faction, out canonicalName); } public static bool TryApply(Character? character, string? name) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || !CreatureFactionManager.TryGetRegisteredFaction(name, out Faction faction, out string canonicalName)) { return false; } character.m_faction = faction; CreatureFactionManager.ApplyFactionToZdo(character, canonicalName); BaseAI baseAI = character.GetBaseAI(); if ((Object)(object)baseAI != (Object)null) { CreatureFactionManager.SetupBaseAi(baseAI); } return true; } public static string[] GetNames() { return CreatureFactionManager.GetRegisteredFactionNames(); } } internal static class CreatureHarmonyTargetResolver { internal static MethodInfo? FindDeclared(Type declaringType, string methodName, string feature, Type[]? argumentTypes = null, bool warnIfMissing = true) { if (argumentTypes == null) { return FindUniqueDeclared(declaringType, methodName, feature, warnIfMissing); } MethodInfo methodInfo = AccessTools.DeclaredMethod(declaringType, methodName, argumentTypes, (Type[])null); if (methodInfo == null && warnIfMissing) { string text = "(" + string.Join(", ", argumentTypes.Select((Type type) => type.Name)) + ")"; CreatureManagerPlugin.Log.LogWarning((object)("Could not find optional Harmony target " + declaringType.FullName + "." + methodName + text + "; " + feature + " may be incomplete for this game version.")); } return methodInfo; } internal static MethodInfo? FindUniqueDeclared(Type declaringType, string methodName, string feature, bool warnIfMissing = true) { MethodInfo[] array = (from method in declaringType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == methodName select method).ToArray(); if (array.Length == 1) { return array[0]; } if (array.Length == 0) { if (warnIfMissing) { CreatureManagerPlugin.Log.LogWarning((object)("Could not find optional Harmony target " + declaringType.FullName + "." + methodName + "; " + feature + " may be incomplete for this game version.")); } return null; } string text = string.Join(", ", array.Select((MethodInfo method) => "(" + string.Join(", ", from parameter in method.GetParameters() select parameter.ParameterType.Name) + ")")); CreatureManagerPlugin.Log.LogWarning((object)($"Found {array.Length} optional Harmony targets named {declaringType.FullName}.{methodName} " + "with signatures " + text + "; refusing to select an overload implicitly, so " + feature + " is disabled.")); return null; } } [HarmonyPatch(typeof(FejdStartup), "Awake")] internal static class CreatureManagerFejdStartupAwakePatch { private static void Postfix(FejdStartup __instance) { CreaturePrefabRegistry.CacheFejdStartup(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class CreatureManagerZNetSceneAwakePatch { private static void Prefix(ZNetScene __instance) { CreaturePrefabRegistry.RegisterPendingPrefabs(__instance); } [HarmonyPriority(0)] private static void Postfix(ZNetScene __instance) { CreaturePrefabRegistry.RegisterPendingPrefabs(__instance); CreatureDomainManager.NotifyGameDataAvailable(); } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class CreatureManagerZNetAwakePatch { private static void Postfix() { CreatureLevelManager.RegisterRpcs(); CreatureKarmaManager.RegisterRpcs(); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class CreatureManagerObjectDbAwakePatch { private static void Prefix(ObjectDB __instance) { CreaturePrefabRegistry.RegisterPendingPrefabs(__instance); } [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { CreaturePrefabRegistry.RegisterPendingPrefabs(__instance); CreatureModifierManager.RegisterStatusEffects(__instance); CreatureDomainManager.NotifyGameDataAvailable((Object)(object)((Component)__instance).GetComponent() == (Object)null && __instance.m_items.Count > 0); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class CreatureManagerObjectDbCopyOtherDbPatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { CreaturePrefabRegistry.RegisterPendingPrefabs(__instance); CreatureModifierManager.RegisterStatusEffects(__instance); CreatureDomainManager.NotifyGameDataAvailable(objectDbFinalized: true); } } internal static class CreatureGameSettings { internal static void ApplyAll() { ApplyDifficulty(Game.instance); ApplyNameplateRange(EnemyHud.instance); } internal static void ApplyDifficulty(Game? game) { if (!((Object)(object)game == (Object)null)) { game.m_healthScalePerPlayer = Mathf.Clamp(CreatureManagerPlugin.MultiplayerHealthIncreasePerPlayer.Value, 0f, 200f) / 100f; game.m_damageScalePerPlayer = Mathf.Clamp(CreatureManagerPlugin.MultiplayerDamageIncreasePerPlayer.Value, 0f, 200f) / 100f; game.m_difficultyScaleMaxPlayers = Mathf.Clamp(CreatureManagerPlugin.MultiplayerMaximumPlayerCount.Value, 1, 25); } } internal static void ApplyNameplateRange(EnemyHud? enemyHud) { if (!((Object)(object)enemyHud == (Object)null)) { enemyHud.m_maxShowDistance = Mathf.Clamp(CreatureManagerPlugin.NormalCreatureNameplateRange.Value, 10f, 50f); } } } [HarmonyPatch(typeof(Game), "Awake")] internal static class CreatureManagerGameAwakePatch { private static void Postfix(Game __instance) { CreatureGameSettings.ApplyDifficulty(__instance); } } [HarmonyPatch(typeof(EnemyHud), "Awake")] internal static class CreatureManagerEnemyHudAwakePatch { private static void Postfix(EnemyHud __instance) { CreatureGameSettings.ApplyNameplateRange(__instance); } } [HarmonyPatch(typeof(Minimap), "Awake")] internal static class CreatureManagerMinimapAwakePatch { private static void Postfix(Minimap __instance) { CreatureKarmaMinimapHud.Create(__instance); } } [HarmonyPatch(typeof(Minimap), "Update")] internal static class CreatureManagerMinimapUpdatePatch { private static void Postfix() { CreatureKarmaMinimapHud.Update(); } } [HarmonyPatch(typeof(Minimap), "OnDestroy")] internal static class CreatureManagerMinimapOnDestroyPatch { private static void Postfix() { CreatureKarmaMinimapHud.Clear(); } } [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] internal static class CreatureManagerTextsDialogUpdateTextsListPatch { private static void Postfix(TextsDialog __instance) { CreatureCompendiumManager.AddModifierEntries(__instance); } } [HarmonyPatch(typeof(TextsDialog), "ShowText", new Type[] { typeof(TextInfo) })] internal static class CreatureManagerTextsDialogShowTextPatch { private static void Postfix(TextsDialog __instance, TextInfo text) { CreatureCompendiumManager.RefreshPageContentIcons(__instance, text); } } internal static class CreatureKarmaMinimapHud { private const float RefreshInterval = 1f; private static Text? KarmaText; private static float NextRefreshTime; internal static void Create(Minimap minimap) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //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_00fe: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)minimap == (Object)null || (Object)(object)KarmaText != (Object)null) { return; } Transform val = null; if ((Object)(object)minimap.m_smallRoot != (Object)null) { val = minimap.m_smallRoot.transform; } else if ((Object)(object)minimap.m_mapSmall != (Object)null && (Object)(object)minimap.m_mapSmall.transform.parent != (Object)null) { val = minimap.m_mapSmall.transform.parent; } else if ((Object)(object)minimap.m_mapImageSmall != (Object)null) { val = ((Component)minimap.m_mapImageSmall).transform; } if ((Object)(object)val == (Object)null) { return; } try { GameObject val2 = new GameObject("CreatureManager_KarmaMinimapText", new Type[3] { typeof(RectTransform), typeof(Text), typeof(Outline) }) { layer = LayerMask.NameToLayer("UI") }; val2.transform.SetParent(val, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = new Vector2(1f, 0f); val3.anchorMax = new Vector2(1f, 0f); val3.pivot = new Vector2(1f, 0f); val3.sizeDelta = new Vector2(220f, 36f); val3.anchoredPosition = new Vector2(-8f, 8f); ((Transform)val3).localRotation = Quaternion.identity; ((Transform)val3).localScale = Vector3.one; ((Transform)val3).SetAsLastSibling(); Text component = val2.GetComponent(); component.font = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font font) => (Object)(object)font != (Object)null && ((Object)font).name == "AveriaSerifLibre-Bold")) ?? Font.CreateDynamicFontFromOSFont("Arial", 14); component.fontSize = 14; component.alignment = (TextAnchor)8; ((Graphic)component).color = new Color(1f, 0.78f, 0.24f, 1f); ((Graphic)component).raycastTarget = false; component.text = ""; Outline component2 = val2.GetComponent(); ((Shadow)component2).effectColor = Color.black; ((Shadow)component2).effectDistance = new Vector2(1f, -1f); val2.SetActive(false); KarmaText = component; NextRefreshTime = 0f; } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to create Karma minimap HUD: " + ex.Message)); } } internal static void Update() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)KarmaText == (Object)null || Time.time < NextRefreshTime) { return; } NextRefreshTime = Time.time + 1f; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { SetVisible(visible: false); return; } string minimapStatus = CreatureKarmaManager.GetMinimapStatus(((Component)localPlayer).transform.position); if (minimapStatus.Length == 0) { SetVisible(visible: false); return; } KarmaText.text = minimapStatus; SetVisible(visible: true); } internal static void Clear() { if ((Object)(object)KarmaText != (Object)null) { Object.Destroy((Object)(object)((Component)KarmaText).gameObject); } KarmaText = null; NextRefreshTime = 0f; } private static void SetVisible(bool visible) { if ((Object)(object)KarmaText != (Object)null && ((Component)KarmaText).gameObject.activeSelf != visible) { ((Component)KarmaText).gameObject.SetActive(visible); } } } internal static class CreatureBossHudOnlyScope { internal static bool Begin(Character? character) { if ((Object)(object)character == (Object)null || character.m_boss || !CreatureKarmaManager.IsBossHudOnly(character)) { return false; } character.m_boss = true; return true; } internal static void End(Character? character, bool changed) { if (changed && (Object)(object)character != (Object)null) { character.m_boss = false; } } internal static List? Begin(EnemyHud? enemyHud) { if ((Object)(object)enemyHud == (Object)null) { return null; } List list = null; foreach (Character key in enemyHud.m_huds.Keys) { if (Begin(key)) { (list ?? (list = new List())).Add(key); } } return list; } internal static void End(List? changed) { if (changed == null) { return; } foreach (Character item in changed) { End(item, changed: true); } } } [HarmonyPatch(typeof(VisEquipment), "AttachItem")] internal static class CreatureManagerVisEquipmentAttachItemPatch { private static void Postfix(VisEquipment __instance, GameObject __result) { CreatureDomainManager.ScaleAttachedEquipmentVisual(__instance, __result); } } [HarmonyPatch(typeof(VisEquipment), "AttachArmor")] internal static class CreatureManagerVisEquipmentAttachArmorPatch { private static void Postfix(VisEquipment __instance, List __result) { CreatureDomainManager.ScaleAttachedEquipmentVisuals(__instance, __result); } } [HarmonyPatch] internal static class CreatureManagerVisEquipmentUpdateEquipmentVisualsPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(VisEquipment), "UpdateEquipmentVisuals", "ragdoll appearance and random hair visual attachment", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPriority(0)] private static void Postfix(VisEquipment __instance) { CreatureManagerRandomHairRuntime.UpdateConfiguredRagdollAppearance(__instance); CreatureDomainManager.ApplyConfiguredRagdollTextures(__instance); CreatureManagerRandomHairRuntime.RestoreEligibility(__instance); CreatureManagerRandomHairRuntime.UpdateVisual(__instance); } } [HarmonyPatch] internal static class CreatureManagerVisEquipmentGetHairItemPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(VisEquipment), "GetHairItem", "random hair helmet-visibility override", new Type[3] { typeof(HelmetHairType), typeof(int), typeof(AccessoryType) }); if (methodInfo != null) { yield return methodInfo; } } private static bool Prefix(VisEquipment __instance, int itemHash, AccessoryType accessory, ref int __result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) if (!CreatureManagerRandomHairRuntime.TryBypassHelmetHairFilter(__instance, itemHash, accessory, out var result)) { return true; } __result = result; return false; } } [HarmonyPatch(typeof(VisEquipment), "OnEnable")] internal static class CreatureManagerVisEquipmentOnEnableRandomHairPatch { private static void Postfix(VisEquipment __instance) { CreatureManagerRandomHairRuntime.UpdateConfiguredRagdollAppearance(__instance); CreatureDomainManager.ApplyConfiguredRagdollTextures(__instance); CreatureManagerRandomHairRuntime.RestoreEligibility(__instance); } } [HarmonyPatch(typeof(VisEquipment), "OnDisable")] internal static class CreatureManagerVisEquipmentOnDisableRandomHairPatch { private static void Prefix(VisEquipment __instance) { CreatureDomainManager.ForgetRagdollTextureVisual(__instance); CreatureManagerRandomHairRuntime.Forget(__instance); } } [HarmonyPatch(typeof(BaseAI), "Awake")] internal static class CreatureManagerBaseAiAwakePatch { private static void Postfix(BaseAI __instance) { CreatureFactionManager.SetupBaseAi(__instance); } } internal static class CreatureManagerCharacterLifecycle { internal static void ApplyLevelAndModifiers(Character character) { CreatureKarmaManager.ObservePotentialBlocker(character); CreatureLevelManager.TryApplyLevel(character); CreatureLevelManager.ApplyRuntimeVisuals(character); CreatureModifierManager.TryRollModifiers(character); } } [HarmonyPatch(typeof(SpawnSystem), "Spawn")] internal static class CreatureManagerSpawnSystemSpawnPatch { private static void Prefix() { CreatureManagerSpawnLifecycle.BeginSpawnContext(); } private static Exception? Finalizer(Exception? __exception) { CreatureManagerSpawnLifecycle.EndSpawnContext(); return __exception; } } [HarmonyPatch(typeof(CreatureSpawner), "Spawn")] internal static class CreatureManagerCreatureSpawnerSpawnPatch { private static void Prefix() { CreatureManagerSpawnLifecycle.BeginSpawnContext(); } private static Exception? Finalizer(Exception? __exception) { CreatureManagerSpawnLifecycle.EndSpawnContext(); return __exception; } } [HarmonyPatch(typeof(SpawnArea), "SpawnOne")] internal static class CreatureManagerSpawnAreaSpawnOnePatch { private static void Prefix() { CreatureManagerSpawnLifecycle.BeginSpawnContext(); } private static Exception? Finalizer(Exception? __exception) { CreatureManagerSpawnLifecycle.EndSpawnContext(); return __exception; } } internal enum CreatureSpawnSourceKind { Unknown, Managed, Command, PlayerSummon, Breeding, Egg, Growup, TamedRestore } [HarmonyPatch(typeof(Terminal), "TryRunCommand")] internal static class CreatureManagerTerminalTryRunCommandPatch { private static void Prefix(string text, out bool __state) { __state = CreatureManagerSpawnLifecycle.ShouldTrackCommandSpawn(text); if (__state) { CreatureManagerSpawnLifecycle.BeginCommandSpawnContext(); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { CreatureManagerSpawnLifecycle.EndCommandSpawnContext(); } return __exception; } } [HarmonyPatch(typeof(Terminal), "tabCycle")] internal static class CreatureManagerTerminalTabCyclePatch { private static void Prefix(Terminal __instance, ref string word, ref List options) { CreatureConsoleCommands.AdjustSpawnAutocomplete(__instance, ref word, ref options); } } [HarmonyPatch(typeof(Terminal), "updateSearch")] internal static class CreatureManagerTerminalUpdateSearchPatch { private static void Prefix(Terminal __instance, ref string word, ref List options) { CreatureConsoleCommands.AdjustSpawnAutocomplete(__instance, ref word, ref options); } } [HarmonyPatch(typeof(ZNet), "InternalCommand")] internal static class CreatureManagerZNetInternalCommandPatch { private static bool Prefix(ZNet __instance, ZRpc rpc, string command) { return !CreatureConsoleCommands.TryHandleAuthenticatedRemoteAdminCommand(__instance, rpc, command); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class CreatureManagerCharacterAwakePatch { private static void Postfix(Character __instance) { CreatureModifierManager.RegisterCharacterRpcs(__instance); CreatureModifierManager.RefreshStoredReapingScale(__instance); CreatureManagerSpawnLifecycle.RecordAwake(__instance); } } [HarmonyPatch(typeof(SpawnAbility), "Spawn")] internal static class CreatureManagerSpawnAbilitySpawnPatch { private static void Postfix(SpawnAbility __instance, ref IEnumerator __result) { if (__result != null && CreatureManagerSpawnLifecycle.IsBloodMagicItem(__instance.m_weapon)) { __result = CreatureManagerSpawnLifecycle.WrapSourceContext(__result, CreatureSpawnSourceKind.PlayerSummon); } } } [HarmonyPatch(typeof(Projectile), "SpawnOnHit")] internal static class CreatureManagerProjectileSpawnOnHitPatch { private static void Prefix(Projectile __instance, out bool __state) { __state = CreatureManagerSpawnLifecycle.IsBloodMagicItem(__instance.m_weapon); if (__state) { CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.PlayerSummon); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { CreatureManagerSpawnLifecycle.EndSourceContext(); } return __exception; } } internal static class CreatureManagerFeedLikeGrandmaPokeballReleasePatch { private static bool _patched; private static bool _lateAttempted; internal static void ApplyIfAvailable(Harmony harmony, bool lateAttempt = false) { //IL_0060: 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_0082: Expected O, but got Unknown //IL_0082: Expected O, but got Unknown if (_patched || (lateAttempt && _lateAttempted)) { return; } Type type = FindType("FeedLikeGrandma.PokeballSystem"); MethodInfo methodInfo = ((type == null) ? null : CreatureHarmonyTargetResolver.FindUniqueDeclared(type, "CompleteRelease", "Feed Like Grandma restored-creature tracking", warnIfMissing: false)); if (methodInfo == null) { if (lateAttempt) { _lateAttempted = true; } } else { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CreatureManagerFeedLikeGrandmaPokeballReleasePatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(CreatureManagerFeedLikeGrandmaPokeballReleasePatch), "Finalizer", (Type[])null), (HarmonyMethod)null); _patched = true; } } internal static void Reset() { _patched = false; _lateAttempted = false; } private static Type? FindType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(fullName, throwOnError: false); if (type != null) { return type; } } return null; } private static void Prefix() { CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.TamedRestore); } private static Exception? Finalizer(Exception? __exception) { CreatureManagerSpawnLifecycle.EndSourceContext(); return __exception; } } internal static class CreatureManagerSpawnLifecycle { private const string SpawnSourceKey = "CreatureManager_SpawnSource"; private static readonly List SpawnedCharacters = new List(); private static readonly HashSet ManagedSpawnCharacterIds = new HashSet(); private static readonly HashSet CommandSpawnCharacterIds = new HashSet(); private static int SpawnContextDepth; private static readonly Stack SourceContexts = new Stack(); internal static bool ShouldTrackCommandSpawn(string text) { if (string.IsNullOrWhiteSpace(text)) { return false; } return text.TrimStart(Array.Empty()).Split(new char[1] { ' ' }, StringSplitOptions.None)[0].ToLowerInvariant().StartsWith("spawn", StringComparison.Ordinal); } internal static void BeginSpawnContext() { if (SpawnContextDepth == 0) { SpawnedCharacters.Clear(); } SpawnContextDepth++; } internal static void BeginCommandSpawnContext() { BeginSourceContext(CreatureSpawnSourceKind.Command); } internal static void EndCommandSpawnContext() { EndSourceContext(); } internal static void BeginSourceContext(CreatureSpawnSourceKind source) { SourceContexts.Push(source); } internal static void EndSourceContext() { if (SourceContexts.Count > 0) { SourceContexts.Pop(); } } internal static void EndSpawnContext() { if (SpawnContextDepth <= 0) { return; } SpawnContextDepth--; if (SpawnContextDepth > 0) { return; } try { foreach (Character item in SpawnedCharacters.Where((Character character) => (Object)(object)character != (Object)null).Distinct()) { CreatureManagerCharacterLifecycle.ApplyLevelAndModifiers(item); } } finally { SpawnedCharacters.Clear(); } } internal static void RecordAwake(Character character) { if (!((Object)(object)character == (Object)null)) { if (SourceContexts.Count > 0) { MarkSpawnSource(character, SourceContexts.Peek()); } else if (SpawnContextDepth > 0) { SpawnedCharacters.Add(character); ManagedSpawnCharacterIds.Add(((Object)character).GetInstanceID()); } } } internal static bool IsManagedSpawn(Character character) { if ((Object)(object)character != (Object)null) { return ManagedSpawnCharacterIds.Contains(((Object)character).GetInstanceID()); } return false; } internal static bool IsCommandSpawn(Character character) { return GetSpawnSource(character) == CreatureSpawnSourceKind.Command; } internal static CreatureSpawnSourceKind GetSpawnSource(Character character) { if ((Object)(object)character == (Object)null) { return CreatureSpawnSourceKind.Unknown; } if (CommandSpawnCharacterIds.Contains(((Object)character).GetInstanceID())) { return CreatureSpawnSourceKind.Command; } ZNetView nview = character.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { CreatureSpawnSourceKind spawnSource = GetSpawnSource(zDO); if (spawnSource != CreatureSpawnSourceKind.Unknown) { return spawnSource; } } } if (IsManagedSpawn(character)) { return CreatureSpawnSourceKind.Managed; } if (!character.IsTamed()) { return CreatureSpawnSourceKind.Unknown; } return CreatureSpawnSourceKind.TamedRestore; } internal static CreatureSpawnSourceKind GetSpawnSource(ZDO zdo) { if (zdo == null) { return CreatureSpawnSourceKind.Unknown; } int num = zdo.GetInt("CreatureManager_SpawnSource", 0); if (num == 0 || !Enum.IsDefined(typeof(CreatureSpawnSourceKind), num)) { return CreatureSpawnSourceKind.Unknown; } return (CreatureSpawnSourceKind)num; } internal static IEnumerator WrapSourceContext(IEnumerator inner, CreatureSpawnSourceKind source) { while (true) { BeginSourceContext(source); object current; try { if (!inner.MoveNext()) { break; } current = inner.Current; } finally { EndSourceContext(); } yield return current; } } internal static bool IsBloodMagicItem(ItemData? item) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 try { return item?.m_shared != null && (int)item.m_shared.m_skillType == 10; } catch { return false; } } internal static void ForgetCharacter(Character character) { if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); ManagedSpawnCharacterIds.Remove(instanceID); CommandSpawnCharacterIds.Remove(instanceID); SpawnedCharacters.RemoveAll((Character candidate) => (Object)(object)candidate == (Object)null || (Object)(object)candidate == (Object)(object)character); } } internal static void ResetRuntimeState() { SpawnedCharacters.Clear(); ManagedSpawnCharacterIds.Clear(); CommandSpawnCharacterIds.Clear(); SourceContexts.Clear(); SpawnContextDepth = 0; } private static void MarkSpawnSource(Character character, CreatureSpawnSourceKind source) { if (source == CreatureSpawnSourceKind.Command) { CommandSpawnCharacterIds.Add(((Object)character).GetInstanceID()); } ZNetView nview = character.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { zDO.Set("CreatureManager_SpawnSource", (int)source); } } } } [HarmonyPatch(typeof(Character), "OnDestroy")] internal static class CreatureManagerCharacterOnDestroyPatch { private static void Prefix(Character __instance) { CreatureManagerSpawnLifecycle.ForgetCharacter(__instance); CreatureLevelManager.ForgetCharacter(__instance); CreatureKarmaManager.ForgetCharacter(__instance); CreatureModifierManager.ForgetCharacter(__instance); } } [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] internal static class CreatureManagerZNetSceneOnDestroyPatch { private static void Prefix() { CreatureDomainManager.NotifyGameDataUnavailable(); CreatureManagerRandomHairRuntime.Reset(); CreatureManagerSpawnLifecycle.ResetRuntimeState(); CreatureKarmaManager.ResetRuntimeState(); CreatureModifierManager.ResetRuntimeState(); CreatureLevelManager.ResetRuntimeState(); } } [HarmonyPatch(typeof(Character), "Start")] internal static class CreatureManagerCharacterStartPatch { private static void Postfix(Character __instance) { CreatureKarmaManager.RefreshStoredEnforcerLoot(__instance); CreatureManagerCharacterLifecycle.ApplyLevelAndModifiers(__instance); } } [HarmonyPatch(typeof(Humanoid), "Start")] internal static class CreatureManagerHumanoidStartPatch { private static void Postfix(Humanoid __instance) { CreatureManagerRandomHairRuntime.Initialize(__instance); CreatureManagerCharacterLifecycle.ApplyLevelAndModifiers((Character)(object)__instance); } } [HarmonyPatch(typeof(Humanoid), "OnRagdollCreated")] internal static class CreatureManagerHumanoidOnRagdollCreatedRandomHairPatch { [HarmonyPriority(0)] private static void Postfix(Humanoid __instance, Ragdoll ragdoll) { CreatureManagerRandomHairRuntime.ApplyToRagdoll(__instance, ragdoll); } } [HarmonyPatch(typeof(Ragdoll), "Setup")] internal static class CreatureManagerRagdollSetupVisualScalePatch { [HarmonyPriority(0)] private static void Postfix(Ragdoll __instance) { CreatureDomainManager.ApplyConfiguredRagdollScale(__instance); } } [HarmonyPatch(typeof(Growup), "GrowUpdate")] internal static class CreatureManagerGrowupGrowUpdatePatch { private static void Prefix(Growup __instance, out bool __state) { __state = ShouldTrackExplicitLevel(__instance); if (__state) { CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.Growup); CreatureLevelManager.BeginExplicitExternalLevelContext("growup", ((Component)__instance).GetComponent()); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { CreatureLevelManager.EndExplicitExternalLevelContext(); CreatureManagerSpawnLifecycle.EndSourceContext(); } return __exception; } private static bool ShouldTrackExplicitLevel(Growup growup) { if ((Object)(object)growup == (Object)null || (Object)(object)growup.m_nview == (Object)null || !growup.m_nview.IsValid() || !growup.m_nview.IsOwner()) { return false; } if ((Object)(object)growup.m_baseAI != (Object)null) { return growup.m_baseAI.GetTimeSinceSpawned().TotalSeconds > (double)growup.m_growTime; } return false; } } [HarmonyPatch(typeof(EggGrow), "GrowUpdate")] internal static class CreatureManagerEggGrowUpdatePatch { private static void Prefix(EggGrow __instance, out bool __state) { __state = ShouldTrackExplicitLevel(__instance); if (__state) { CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.Egg); CreatureLevelManager.BeginExplicitExternalLevelContext("egggrow"); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { CreatureLevelManager.EndExplicitExternalLevelContext(); CreatureManagerSpawnLifecycle.EndSourceContext(); } return __exception; } private static bool ShouldTrackExplicitLevel(EggGrow eggGrow) { if ((Object)(object)eggGrow == (Object)null || (Object)(object)eggGrow.m_nview == (Object)null || !eggGrow.m_nview.IsValid() || !eggGrow.m_nview.IsOwner()) { return false; } if ((Object)(object)eggGrow.m_item == (Object)null || eggGrow.m_item.m_itemData.m_stack > 1 || (Object)(object)ZNet.instance == (Object)null) { return false; } float num = eggGrow.m_nview.GetZDO().GetFloat(ZDOVars.s_growStart, 0f); if (num > 0f) { return ZNet.instance.GetTimeSeconds() > (double)(num + eggGrow.m_growTime); } return false; } } [HarmonyPatch(typeof(Procreation), "Procreate")] internal static class CreatureManagerProcreationProcreatePatch { private static void Prefix(Procreation __instance, out bool __state) { __state = ShouldTrackExplicitLevel(__instance); if (__state) { CreatureManagerSpawnLifecycle.BeginSourceContext(CreatureSpawnSourceKind.Breeding); CreatureLevelManager.BeginExplicitExternalLevelContext("procreation"); } } private static Exception? Finalizer(Exception? __exception, bool __state) { if (__state) { CreatureLevelManager.EndExplicitExternalLevelContext(); CreatureManagerSpawnLifecycle.EndSourceContext(); } return __exception; } private static bool ShouldTrackExplicitLevel(Procreation procreation) { if ((Object)(object)procreation == (Object)null || (Object)(object)procreation.m_nview == (Object)null || !procreation.m_nview.IsValid() || !procreation.m_nview.IsOwner()) { return false; } if ((Object)(object)procreation.m_tameable != (Object)null && procreation.m_tameable.IsTamed() && procreation.IsPregnant()) { return procreation.IsDue(); } return false; } } [HarmonyPatch(typeof(Character), "SetLevel")] internal static class CreatureManagerCharacterSetLevelPatch { private static void Prefix(Character __instance, out float __state) { __state = CreatureLevelManager.CaptureStoredHealthDeficit(__instance); } [HarmonyPriority(0)] private static void Postfix(Character __instance, int level, float __state) { if (!CreatureManagerSpawnLifecycle.IsCommandSpawn(__instance)) { if (!CreatureLevelManager.TryAdoptContextualExternalLevel(__instance, level)) { CreatureLevelManager.RestoreConfiguredLevel(__instance, level); } CreatureLevelManager.ApplyRuntimeVisuals(__instance); if (CreatureLevelManager.HasManagedLevel(__instance)) { CreatureModifierManager.TryRollModifiers(__instance); } CreatureLevelManager.RestoreStoredHealthDeficit(__instance, __state); CreatureModifierManager.RefreshStoredReapingScale(__instance); } } } [HarmonyPatch(typeof(LevelEffects), "SetupLevelVisualization")] internal static class CreatureManagerLevelEffectsSetupLevelVisualizationPatch { private static bool Prefix(LevelEffects __instance, int level) { return !CreatureLevelManager.TryApplyRotatedLevelEffects(__instance, level); } } [HarmonyPatch] internal static class CreatureManagerUndodgeableAttackScopePatch { private static IEnumerable TargetMethods() { string[] array = new string[3] { "DoMeleeAttack", "DoAreaAttack", "ProjectileAttackTriggered" }; foreach (string methodName in array) { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Attack), methodName, "Undodgeable attack handling", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } } [HarmonyPriority(0)] private static void Prefix(Attack __instance, MethodBase __originalMethod, out CreatureModifierManager.UndodgeableScopeState __state) { string name = __originalMethod.Name; CreatureModifierManager.UndodgeableSourcePath undodgeableSourcePath = ((name == "DoMeleeAttack") ? CreatureModifierManager.UndodgeableSourcePath.Melee : ((name == "DoAreaAttack") ? CreatureModifierManager.UndodgeableSourcePath.Area : CreatureModifierManager.UndodgeableSourcePath.None)); CreatureModifierManager.UndodgeableSourcePath sourcePath = undodgeableSourcePath; __state = CreatureModifierManager.BeginUndodgeableAttackScope(__instance, sourcePath); } [HarmonyPriority(800)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.UndodgeableScopeState __state) { CreatureModifierManager.EndUndodgeableScope(ref __state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerUndodgeableProjectileScopePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Projectile), "IsValidTarget", "Undodgeable projectile handling", new Type[1] { typeof(IDestructible) }); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPriority(0)] private static void Prefix(Projectile __instance, IDestructible destr, out CreatureModifierManager.UndodgeableScopeState __state) { __state = CreatureModifierManager.BeginUndodgeableProjectileScope(__instance, destr); } [HarmonyPriority(800)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.UndodgeableScopeState __state) { CreatureModifierManager.EndUndodgeableScope(ref __state); return __exception; } } [HarmonyPatch(typeof(Projectile), "OnHit", new Type[] { typeof(Collider), typeof(Vector3), typeof(bool), typeof(Vector3) })] internal static class CreatureManagerVortexProjectileImpactScopePatch { [HarmonyPriority(800)] private static void Prefix(Projectile __instance, Collider collider, bool water, out CreatureModifierManager.VortexProjectileImpactScopeState __state) { __state = CreatureModifierManager.BeginVortexProjectileImpact(__instance, collider, water); } [HarmonyPriority(0)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.VortexProjectileImpactScopeState __state) { CreatureModifierManager.EndVortexProjectileImpact(ref __state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerUndodgeableAoeScopePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Aoe), "ShouldHit", "Undodgeable area-attack handling", new Type[1] { typeof(Collider) }); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPriority(0)] private static void Prefix(Aoe __instance, Collider collider, out CreatureModifierManager.UndodgeableScopeState __state) { __state = CreatureModifierManager.BeginUndodgeableAoeScope(__instance, collider); } [HarmonyPriority(800)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.UndodgeableScopeState __state) { CreatureModifierManager.EndUndodgeableScope(ref __state); return __exception; } } [HarmonyPatch(typeof(Player), "IsDodgeInvincible")] internal static class CreatureManagerUndodgeableDodgeQueryPatch { [HarmonyPriority(0)] private static void Postfix(Player __instance, ref bool __result) { CreatureModifierManager.ApplyUndodgeableDodgeOverride(__instance, ref __result); } } [HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })] internal static class CreatureManagerUndodgeableDamageScopePatch { [HarmonyPriority(800)] private static void Prefix(out CreatureModifierManager.UndodgeableDamageScopeState __state) { __state = CreatureModifierManager.BeginUndodgeableDamageScope(); } [HarmonyPriority(0)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.UndodgeableDamageScopeState __state) { CreatureModifierManager.EndUndodgeableDamageScope(ref __state); return __exception; } } [HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })] internal static class CreatureManagerVortexDirectProjectileDamagePatch { [HarmonyPriority(0)] private static void Prefix(Character __instance, HitData hit, out CreatureModifierManager.VortexDirectProjectileDamageState __state) { __state = CreatureModifierManager.PrepareVortexDirectProjectileDamage(__instance, hit); } [HarmonyPriority(800)] private static Exception? Finalizer(Exception? __exception, ref CreatureModifierManager.VortexDirectProjectileDamageState __state) { CreatureModifierManager.RestoreVortexDirectProjectileDamage(ref __state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerUndodgeableDamageDispatchPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Character), "Damage", "Undodgeable damage dispatch", new Type[1] { typeof(HitData) }); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPriority(0)] private static void Prefix(Character __instance, HitData hit) { CreatureModifierManager.ApplyUndodgeableBeforeDamage(__instance, hit); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CreatureManagerVortexRpcDamageMarkerPatch { [HarmonyPriority(800)] private static void Prefix(HitData hit) { CreatureModifierManager.CaptureVortexProjectileDecision(hit); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CreatureManagerCharacterRpcDamagePatch { private static bool Prefix(Character __instance, ref HitData hit, out CreatureModifierManager.RpcDamageScopeState __state) { __state = CreatureModifierManager.BeginRpcDamageScope(__instance, hit); return CreatureModifierManager.ApplyDamageModifiers(__instance, hit); } [HarmonyPriority(0)] private static IEnumerable Transpiler(IEnumerable instructions) { //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown List list = instructions.ToList(); FieldInfo fieldInfo = AccessTools.Field(typeof(HitData), "m_damage"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(DamageTypes), "m_poison"); MethodInfo methodInfo = AccessTools.Method(typeof(CreatureModifierManager), "CapturePostMitigationDelayedDamage", (Type[])null, (Type[])null); if (fieldInfo == null || fieldInfo2 == null || methodInfo == null) { CreatureManagerPlugin.Log.LogError((object)"Failed to resolve delayed-damage capture members; pure poison/fire/spirit modifier support is disabled."); return list; } List list2 = new List(); for (int i = 0; i <= list.Count - 4; i++) { if (list[i].opcode == OpCodes.Ldarg_2 && list[i + 1].opcode == OpCodes.Ldflda && object.Equals(list[i + 1].operand, fieldInfo) && list[i + 2].opcode == OpCodes.Ldc_R4 && list[i + 2].operand is float num && num == 0f && list[i + 3].opcode == OpCodes.Stfld && object.Equals(list[i + 3].operand, fieldInfo2)) { list2.Add(i); } } if (list2.Count != 1) { CreatureManagerPlugin.Log.LogError((object)$"Expected one delayed-damage extraction point in Character.RPC_Damage, found {list2.Count}; pure poison/fire/spirit modifier support is disabled."); return list; } int index = list2[0]; CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null); val.labels.AddRange(list[index].labels); val.blocks.AddRange(list[index].blocks); list[index].labels.Clear(); list[index].blocks.Clear(); list.InsertRange(index, (IEnumerable)(object)new CodeInstruction[3] { val, new CodeInstruction(OpCodes.Ldarg_2, (object)null), new CodeInstruction(OpCodes.Call, (object)methodInfo) }); return list; } private static Exception? Finalizer(HitData hit, CreatureModifierManager.RpcDamageScopeState __state, Exception? __exception) { try { CreatureModifierManager.EndRpcDamageScope(__state); return __exception; } finally { CreatureModifierManager.ClearTransientDamageContext(hit); } } } [HarmonyPatch] internal static class CreatureManagerCharacterApplyPushbackPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Character), "ApplyPushback", "Juggernaut cooldown confirmation", new Type[1] { typeof(HitData) }); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(Character __instance, HitData hit) { CreatureModifierManager.ConfirmKnockbackPush(__instance, hit); } } [HarmonyPatch] internal static class CreatureManagerHumanoidBlockStaggerScopePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Humanoid), "BlockAttack", "Staggering block handling", new Type[2] { typeof(HitData), typeof(Character) }); if (methodInfo != null) { yield return methodInfo; } } private static void Prefix(Character attacker, out CreatureModifierManager.BlockAttackModifierState __state) { __state = CreatureModifierManager.BeginBlockAttackModifierScope(attacker); } private static Exception? Finalizer(Exception? __exception, CreatureModifierManager.BlockAttackModifierState __state) { CreatureModifierManager.EndBlockAttackModifierScope(__state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerCharacterBlockStaggerDamagePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Character), "AddStaggerDamage", "Staggering block buildup", new Type[3] { typeof(float), typeof(Vector3), typeof(HitData) }); if (methodInfo != null) { yield return methodInfo; } } private static void Prefix(ref float damage) { CreatureModifierManager.ApplyBlockStaggerBonus(ref damage); } } [HarmonyPatch(typeof(Character), "ApplyDamage", new Type[] { typeof(HitData), typeof(bool), typeof(bool), typeof(DamageModifier) })] internal static class CreatureManagerCharacterApplyDamagePatch { private static void Prefix(Character __instance, HitData hit, out CreatureModifierManager.ApplyDamageState __state) { __state = CreatureModifierManager.BeginApplyDamage(__instance, hit); } [HarmonyPriority(0)] private static IEnumerable Transpiler(IEnumerable instructions) { //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown List list = instructions.ToList(); MethodInfo methodInfo = AccessTools.Method(typeof(HitData), "GetTotalDamage", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(CreatureModifierManager), "ResolveFinalDeathwardDamage", new Type[3] { typeof(Character), typeof(HitData), typeof(float) }, (Type[])null); if (methodInfo == null || methodInfo2 == null) { CreatureManagerPlugin.Log.LogError((object)"Failed to resolve final-damage Deathward members; Deathward lethal prevention is disabled."); return list; } List list2 = new List(); for (int i = 0; i <= list.Count - 5; i++) { if (list[i].opcode == OpCodes.Ldarg_1 && CodeInstructionExtensions.Calls(list[i + 1], methodInfo) && list[i + 2].opcode == OpCodes.Stloc_1 && list[i + 3].opcode == OpCodes.Ldloc_1 && list[i + 4].opcode == OpCodes.Ldc_R4 && list[i + 4].operand is float num && num == 0.1f) { list2.Add(i + 3); } } if (list2.Count != 1) { CreatureManagerPlugin.Log.LogError((object)$"Expected one final-damage checkpoint in Character.ApplyDamage, found {list2.Count}; Deathward lethal prevention is disabled."); return list; } int index = list2[0]; CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null); val.labels.AddRange(list[index].labels); val.blocks.AddRange(list[index].blocks); list[index].labels.Clear(); list[index].blocks.Clear(); list.InsertRange(index, (IEnumerable)(object)new CodeInstruction[5] { val, new CodeInstruction(OpCodes.Ldarg_1, (object)null), new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Call, (object)methodInfo2), new CodeInstruction(OpCodes.Stloc_1, (object)null) }); return list; } private static void Postfix(Character __instance, HitData hit, CreatureModifierManager.ApplyDamageState __state) { CreatureModifierManager.CompleteApplyDamage(__instance, hit, __state); } private static Exception? Finalizer(HitData hit, Exception? __exception) { if (__exception != null) { CreatureModifierManager.ClearTransientDamageContext(hit); } return __exception; } } [HarmonyPatch(typeof(SE_Poison), "AddDamage", new Type[] { typeof(float) })] internal static class CreatureManagerPoisonSourcePatch { private static void Prefix(SE_Poison __instance, float damage, out bool __state) { __state = damage >= __instance.m_damageLeft; } private static void Postfix(SE_Poison __instance, bool __state) { CreatureModifierManager.RecordPoisonDamageSource(__instance, __state); } } [HarmonyPatch(typeof(SE_Burning), "AddFireDamage", new Type[] { typeof(float) })] internal static class CreatureManagerFireSourcePatch { private static void Prefix(SE_Burning __instance, out bool __state) { __state = __instance.m_fireDamageLeft <= 0f; } private static void Postfix(SE_Burning __instance, bool __state, bool __result) { CreatureModifierManager.RecordFireDamageSource(__instance, __state, __result); } } [HarmonyPatch(typeof(SE_Burning), "AddSpiritDamage", new Type[] { typeof(float) })] internal static class CreatureManagerSpiritSourcePatch { private static void Prefix(SE_Burning __instance, out bool __state) { __state = __instance.m_spiritDamageLeft <= 0f; } private static void Postfix(SE_Burning __instance, bool __state, bool __result) { CreatureModifierManager.RecordSpiritDamageSource(__instance, __state, __result); } } [HarmonyPatch(typeof(SE_Poison), "UpdateStatusEffect", new Type[] { typeof(float) })] internal static class CreatureManagerPoisonTickScopePatch { private static void Prefix(SE_Poison __instance, out CreatureModifierManager.DelayedDamageTickScopeState __state) { __state = CreatureModifierManager.BeginPoisonDamageTick(__instance); } private static Exception? Finalizer(Exception? __exception, CreatureModifierManager.DelayedDamageTickScopeState __state) { CreatureModifierManager.EndDelayedDamageTick(__state); return __exception; } } [HarmonyPatch(typeof(SE_Burning), "UpdateStatusEffect", new Type[] { typeof(float) })] internal static class CreatureManagerBurningTickScopePatch { private static void Prefix(SE_Burning __instance, out CreatureModifierManager.DelayedDamageTickScopeState __state) { __state = CreatureModifierManager.BeginBurningDamageTick(__instance); } private static Exception? Finalizer(Exception? __exception, CreatureModifierManager.DelayedDamageTickScopeState __state) { CreatureModifierManager.EndDelayedDamageTick(__state); return __exception; } } [HarmonyPatch(typeof(Character), "Heal")] internal static class CreatureManagerCharacterHealPatch { private static void Prefix(Character __instance, ref float __0) { CreatureModifierManager.ApplyPlayerHealingDebuff(__instance, ref __0); } private static void Postfix(Character __instance) { CreatureModifierManager.ClearRecoveredDelayedDamageDeathCredit(__instance); } } [HarmonyPatch(typeof(Character), "SetHealth", new Type[] { typeof(float) })] internal static class CreatureManagerCharacterSetHealthPatch { private static void Postfix(Character __instance) { CreatureModifierManager.ClearRecoveredDelayedDamageDeathCredit(__instance); } } [HarmonyPatch] internal static class CreatureManagerPlayerControlDebuffUpdatePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Player), "Update", "player control debuffs", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(Player __instance) { if (CreatureModifierManager.ShouldUpdatePlayerControlDebuffs(__instance)) { CreatureModifierManager.UpdatePlayerControlDebuffs(__instance); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class CreatureManagerCharacterOnDeathPatch { private static void Prefix(Character __instance) { CreatureModifierManager.FinalDeathAttribution attribution = CreatureModifierManager.CaptureFinalDeathAttribution(__instance); CreatureModifierManager.HandleDeath(__instance, attribution); if (!__instance.IsPlayer()) { CreatureKarmaManager.RecordDeath(__instance, attribution); } } } [HarmonyPatch] internal static class CreatureManagerCharacterModifierUpdatePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Character), "CustomFixedUpdate", "passive modifier updates", new Type[1] { typeof(float) }, warnIfMissing: false); if (methodInfo != null) { yield return methodInfo; yield break; } MethodInfo methodInfo2 = CreatureHarmonyTargetResolver.FindDeclared(typeof(Character), "FixedUpdate", "passive modifier update fallback", Type.EmptyTypes, warnIfMissing: false); if (methodInfo2 != null) { yield return methodInfo2; } else { CreatureManagerPlugin.Log.LogWarning((object)"Could not find optional Harmony targets Character.CustomFixedUpdate(float) or Character.FixedUpdate(); passive modifier updates are disabled for this game version."); } } private static void Postfix(Character __instance) { if (CreatureModifierManager.NeedsPassiveModifierUpdate(__instance)) { CreatureModifierManager.UpdatePassiveModifiers(__instance); } } } [HarmonyPatch] internal static class CreatureManagerPlayerGetBodyArmorPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Player), "GetBodyArmor", "Armor Piercing", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(ref float __result) { CreatureModifierManager.ApplyArmorPiercing(ref __result); } } [HarmonyPatch] internal static class CreatureManagerHumanoidOnStopMovingCompatibilityPatch { private static bool _warningLogged; private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Humanoid), "OnStopMoving", "Humanoid.OnStopMoving current-attack null-condition compatibility fix", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPriority(0)] private static IEnumerable Transpiler(IEnumerable instructions) { List list = instructions.ToList(); FieldInfo fieldInfo = AccessTools.Field(typeof(Humanoid), "m_currentAttack"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(Attack), "m_speedFactor"); FieldInfo fieldInfo3 = AccessTools.Field(typeof(Attack), "m_speedFactorRotation"); MethodInfo methodInfo = AccessTools.Method(typeof(Character), "InAttack", Type.EmptyTypes, (Type[])null); if (fieldInfo == null || fieldInfo2 == null || fieldInfo3 == null || methodInfo == null) { WarnOnce("Could not resolve Humanoid.OnStopMoving compatibility members; the vanilla null-condition fix was not applied."); return list; } List list2 = FindKnownGuardMatches(list, fieldInfo, fieldInfo2, fieldInfo3, methodInfo, branchWhenNull: true); List list3 = FindKnownGuardMatches(list, fieldInfo, fieldInfo2, fieldInfo3, methodInfo, branchWhenNull: false); if (list2.Count == 1 && list3.Count == 0) { int index = list2[0]; list[index].opcode = ((list[index].opcode == OpCodes.Brfalse_S) ? OpCodes.Brtrue_S : OpCodes.Brtrue); return list; } if (list2.Count == 0 && list3.Count == 1) { return list; } WarnOnce("Expected one known Humanoid.OnStopMoving current-attack guard, found " + $"{list2.Count} defective and {list3.Count} corrected matches; " + "the vanilla null-condition compatibility fix was not applied."); return list; } private static List FindKnownGuardMatches(IReadOnlyList codes, FieldInfo currentAttackField, FieldInfo speedFactorField, FieldInfo rotationSpeedFactorField, MethodInfo inAttackMethod, bool branchWhenNull) { List list = new List(); for (int i = 0; i <= codes.Count - 6; i++) { OpCode opcode = codes[i + 2].opcode; bool num; if (!branchWhenNull) { if (!(opcode == OpCodes.Brtrue)) { num = opcode == OpCodes.Brtrue_S; goto IL_0058; } } else if (!(opcode == OpCodes.Brfalse)) { num = opcode == OpCodes.Brfalse_S; goto IL_0058; } goto IL_005d; IL_0058: if (!num) { continue; } goto IL_005d; IL_005d: if (!(codes[i].opcode != OpCodes.Ldarg_0) && !(codes[i + 1].opcode != OpCodes.Ldfld) && object.Equals(codes[i + 1].operand, currentAttackField) && !(codes[i + 3].opcode != OpCodes.Ret) && !(codes[i + 4].opcode != OpCodes.Ldarg_0) && CodeInstructionExtensions.Calls(codes[i + 5], inAttackMethod) && BranchesTo(codes[i + 2], codes[i + 4]) && CountCurrentAttackZeroStores(codes, i + 4, currentAttackField, speedFactorField) == 1 && CountCurrentAttackZeroStores(codes, i + 4, currentAttackField, rotationSpeedFactorField) == 1) { list.Add(i + 2); } } return list; } private static int CountCurrentAttackZeroStores(IReadOnlyList codes, int startIndex, FieldInfo currentAttackField, FieldInfo destinationField) { int num = 0; for (int i = startIndex; i <= codes.Count - 4; i++) { if (codes[i].opcode == OpCodes.Ldarg_0 && codes[i + 1].opcode == OpCodes.Ldfld && object.Equals(codes[i + 1].operand, currentAttackField) && codes[i + 2].opcode == OpCodes.Ldc_R4 && codes[i + 2].operand is float num2 && num2 == 0f && codes[i + 3].opcode == OpCodes.Stfld && object.Equals(codes[i + 3].operand, destinationField)) { num++; } } return num; } private static bool BranchesTo(CodeInstruction branch, CodeInstruction target) { object operand = branch.operand; if (!(operand is Label item)) { CodeInstruction val = (CodeInstruction)((operand is CodeInstruction) ? operand : null); if (val != null) { return val == target; } return false; } return target.labels.Contains(item); } private static void WarnOnce(string message) { if (!_warningLogged) { _warningLogged = true; CreatureManagerPlugin.Log.LogWarning((object)message); } } } [HarmonyPatch] internal static class CreatureManagerCharacterAnimEventSpeedPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(CharacterAnimEvent), "CustomFixedUpdate", "Attack Speed animation updates", new Type[1] { typeof(float) }); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(CharacterAnimEvent __instance) { CreatureModifierManager.ApplyAttackAnimationSpeed(__instance); } } [HarmonyPatch(typeof(Humanoid), "GetTimeSinceLastAttack")] internal static class CreatureManagerHumanoidGetTimeSinceLastAttackPatch { private static void Postfix(Humanoid __instance, ref float __result) { CreatureModifierManager.ApplyMinimumAttackIntervalSpeed(__instance, ref __result); } } [HarmonyPatch] internal static class CreatureManagerMonsterAiBlamerFleePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(MonsterAI), "UpdateAI", "Blamer flee behavior", new Type[1] { typeof(float) }); if (methodInfo != null) { yield return methodInfo; } } private static void Prefix(MonsterAI __instance, out CreatureModifierManager.BlamerFleeOverrideState __state) { __state = CreatureModifierManager.BeginBlamerFleeOverride(__instance); } private static Exception? Finalizer(Exception? __exception, CreatureModifierManager.BlamerFleeOverrideState __state) { CreatureModifierManager.EndBlamerFleeOverride(__state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerMonsterAiBlinkAlertPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(MonsterAI), "SetAlerted", "Blink alert grace-period tracking", new Type[1] { typeof(bool) }); if (methodInfo != null) { yield return methodInfo; } } private static void Prefix(MonsterAI __instance, out bool __state) { __state = ((BaseAI)__instance).IsAlerted(); } private static void Postfix(MonsterAI __instance, bool __state) { CreatureModifierManager.HandleBlinkAlertStateChanged(__instance, __state); } } [HarmonyPatch] internal static class CreatureManagerMonsterAiBlinkRangePatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(MonsterAI), "UpdateAI", "Blink attack range handling", new Type[1] { typeof(float) }); if (methodInfo != null) { yield return methodInfo; } } private static void Prefix(MonsterAI __instance, out CreatureModifierManager.BlinkAttackAiOverrideState? __state) { __state = CreatureModifierManager.BeginBlinkAttackAiOverride(__instance); } private static Exception? Finalizer(Exception? __exception, CreatureModifierManager.BlinkAttackAiOverrideState? __state) { CreatureModifierManager.EndBlinkAttackAiOverride(__state); return __exception; } } [HarmonyPatch] internal static class CreatureManagerAttackStartSpeedPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Attack), "Start", "Blink and Attack Speed attack-start handling", new Type[9] { typeof(Humanoid), typeof(Rigidbody), typeof(ZSyncAnimation), typeof(CharacterAnimEvent), typeof(VisEquipment), typeof(ItemData), typeof(Attack), typeof(float), typeof(float) }); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(Humanoid character, ItemData weapon, bool __result) { CreatureModifierManager.TryBlinkOnAttackStart(character, weapon, __result); CreatureModifierManager.ApplyAttackIntervalSpeed(character, weapon, __result); } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class CreatureManagerPlayerOnDeathPatch { private static void Prefix(Player __instance) { CreatureModifierManager.HandlePlayerDeath(__instance); } private static void Postfix(Player __instance) { CreatureKarmaManager.RecordDeath((Character)(object)__instance); } } [HarmonyPatch] internal static class CreatureManagerPlayerOnRespawnPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = CreatureHarmonyTargetResolver.FindDeclared(typeof(Player), "OnRespawn", "Reaping replay reset after player respawn", Type.EmptyTypes); if (methodInfo != null) { yield return methodInfo; } } private static void Postfix(Player __instance) { CreatureModifierManager.NotifyPlayerRespawn(__instance); } } [HarmonyPatch(typeof(EnemyHud), "ShowHud")] internal static class CreatureManagerEnemyHudShowHudPatch { private static void Prefix(Character c, out bool __state) { __state = CreatureBossHudOnlyScope.Begin(c); } private static Exception? Finalizer(Exception? __exception, Character c, bool __state) { CreatureBossHudOnlyScope.End(c, __state); return __exception; } } [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] internal static class CreatureManagerEnemyHudUpdateHudsPatch { private static void Prefix(EnemyHud __instance, out List? __state) { __state = CreatureBossHudOnlyScope.Begin(__instance); } private static void Postfix(EnemyHud __instance, List? __state) { CreatureModifierManager.UpdateEnemyHuds(__instance); } private static Exception? Finalizer(Exception? __exception, List? __state) { CreatureBossHudOnlyScope.End(__state); return __exception; } } [HarmonyPatch(typeof(EnemyHud), "TestShow")] internal static class CreatureManagerEnemyHudTestShowPatch { private static void Prefix(Character c, out bool __state) { __state = CreatureBossHudOnlyScope.Begin(c); } private static Exception? Finalizer(Exception? __exception, Character c, bool __state) { CreatureBossHudOnlyScope.End(c, __state); return __exception; } } [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] internal static class CreatureManagerBaseAiIsEnemyPatch { private static bool Prefix(Character a, Character b, ref bool __result) { if (!CreatureFactionManager.TryIsEnemy(a, b, out var isEnemy)) { return true; } __result = isEnemy; return false; } } internal static class CreatureManagerRandomHairRuntime { private delegate bool SetHairEquippedDelegate(VisEquipment instance, int hash); private delegate bool SetBeardEquippedDelegate(VisEquipment instance, int hash); private delegate int GetHairItemDelegate(VisEquipment instance, HelmetHairType type, int itemHash, AccessoryType accessory); private delegate void UpdateLodgroupDelegate(VisEquipment instance); private delegate void UpdateBaseModelDelegate(VisEquipment instance); private const string SetHairEquippedMethodName = "SetHairEquipped"; private const string SetBeardEquippedMethodName = "SetBeardEquipped"; private const string GetHairItemMethodName = "GetHairItem"; private const string UpdateLodgroupMethodName = "UpdateLodgroup"; private const string UpdateBaseModelMethodName = "UpdateBaseModel"; private static readonly int RandomHairHashKey = StringExtensionMethods.GetStableHashCode("CreatureManager.RandomHair"); private static readonly int RandomHairColorManagedKey = StringExtensionMethods.GetStableHashCode("CreatureManager.RandomHairColorManaged"); private static readonly HashSet EligibleVisuals = new HashSet(); private static readonly HashSet AppliedVisuals = new HashSet(); private static readonly HashSet FailedVisuals = new HashSet(); private static readonly Dictionary ConfiguredHairColors = new Dictionary(); private static readonly Dictionary AppliedHairColors = new Dictionary(); private static readonly HashSet FailedRagdollAccessoryVisuals = new HashSet(); private static readonly HashSet FailedRagdollColorVisuals = new HashSet(); private static readonly HashSet FailedRagdollModelVisuals = new HashSet(); private static bool _delegatesResolved; private static bool _visualRuntimeFailed; private static bool _ragdollAppearanceDelegateWarningLogged; private static SetHairEquippedDelegate? _setHairEquipped; private static SetBeardEquippedDelegate? _setBeardEquipped; private static GetHairItemDelegate? _getHairItem; private static UpdateLodgroupDelegate? _updateLodgroup; private static UpdateBaseModelDelegate? _updateBaseModel; internal static void Initialize(Humanoid humanoid) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)humanoid == (Object)null) { return; } VisEquipment component = ((Component)humanoid).GetComponent(); IReadOnlyList prefabs; List list = (CreatureDomainManager.TryGetRandomHairPrefabs(humanoid, out prefabs) ? prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null).ToList() : new List()); Vector3 color; bool hasConfiguredColor = CreatureDomainManager.TryGetConfiguredAppearanceHairColor(humanoid, out color); if ((Object)(object)component != (Object)null) { int instanceID = ((Object)component).GetInstanceID(); FailedVisuals.Remove(instanceID); if (list.Count > 0) { EligibleVisuals.Add(instanceID); } else { EligibleVisuals.Remove(instanceID); ClearAppliedVisual(component); } ConfigureAppearanceHairColor(humanoid, component, list.Count > 0, hasConfiguredColor, color); } ZNetView nview = ((Character)humanoid).m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { if (list.Count > 0 && (Object)(object)component != (Object)null) { UpdateVisual(component); } return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (list.Count == 0) { if (zDO.GetInt(RandomHairHashKey, 0) != 0) { zDO.Set(RandomHairHashKey, 0, false); RestoreStandardHair(humanoid, component); } return; } int storedHash = zDO.GetInt(RandomHairHashKey, 0); GameObject val = ((IEnumerable)list).FirstOrDefault((Func)((GameObject prefab) => StringExtensionMethods.GetStableHashCode(((Object)prefab).name) == storedHash)); if ((Object)(object)val == (Object)null) { int seed = zDO.GetInt(ZDOVars.s_seed, 0); val = list[SelectDeterministicIndex(seed, list.Count)]; storedHash = StringExtensionMethods.GetStableHashCode(((Object)val).name); zDO.Set(RandomHairHashKey, storedHash, false); } if ((Object)(object)component != (Object)null && component.m_isPlayer) { component.SetHairItem(((Object)val).name); } if ((Object)(object)component != (Object)null) { UpdateVisual(component); } } internal static void RefreshLoadedHumanoids() { Character[] array = Character.GetAllCharacters().ToArray(); foreach (Character obj in array) { Humanoid val = (Humanoid)(object)((obj is Humanoid) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { try { Initialize(val); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to initialize random hair on loaded humanoid '" + ((Object)((Component)val).gameObject).name + "': " + ex.Message)); } } } } private static void ConfigureAppearanceHairColor(Humanoid humanoid, VisEquipment visEquipment, bool hasRandomHair, bool hasConfiguredColor, Vector3 configuredColor) { //IL_001e: 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_007d: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)visEquipment).GetInstanceID(); bool flag = ConfiguredHairColors.ContainsKey(instanceID); if (hasRandomHair && hasConfiguredColor) { ConfiguredHairColors[instanceID] = configuredColor; } else { ConfiguredHairColors.Remove(instanceID); } ZNetView nview = ((Character)humanoid).m_nview; ZDO val = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); bool flag2 = (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner() && val != null; if (hasConfiguredColor) { visEquipment.m_hairColor = configuredColor; if (flag2) { val.Set(ZDOVars.s_hairColor, configuredColor); val.Set(RandomHairColorManagedKey, 1, false); } return; } bool flag3 = flag2 && val.GetInt(RandomHairColorManagedKey, 0) != 0; if (flag || AppliedHairColors.ContainsKey(instanceID) || flag3) { Vector3 val2 = (visEquipment.m_hairColor = CreatureDomainManager.GetInheritedAppearanceHairColor(humanoid)); if (flag3) { val.Set(ZDOVars.s_hairColor, val2); val.Set(RandomHairColorManagedKey, 0, false); } ApplyHairMaterialColor(visEquipment, val2, force: true); AppliedHairColors.Remove(instanceID); } } internal static void UpdateVisual(VisEquipment visEquipment) { if ((Object)(object)visEquipment == (Object)null || _visualRuntimeFailed) { return; } int instanceID = ((Object)visEquipment).GetInstanceID(); if (!FailedVisuals.Contains(instanceID) && (EligibleVisuals.Contains(instanceID) || AppliedVisuals.Contains(instanceID))) { int storedHairHash = GetStoredHairHash(visEquipment); if (storedHairHash == 0) { ClearAppliedVisual(visEquipment); } else if (ApplyHairVisual(visEquipment, storedHairHash)) { AppliedVisuals.Add(instanceID); } } } internal static bool TryBypassHelmetHairFilter(VisEquipment visEquipment, int itemHash, AccessoryType accessory, out int result) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) result = 0; int item = (((Object)(object)visEquipment != (Object)null) ? ((Object)visEquipment).GetInstanceID() : 0); if ((Object)(object)visEquipment == (Object)null || (int)accessory != 0 || itemHash == 0 || FailedVisuals.Contains(item) || !EligibleVisuals.Contains(item) || GetStoredHairHash(visEquipment) != itemHash) { return false; } result = itemHash; return true; } internal static void ApplyToRagdoll(Humanoid source, Ragdoll ragdoll) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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) if ((Object)(object)source == (Object)null || (Object)(object)ragdoll == (Object)null) { return; } VisEquipment component = ((Component)ragdoll).GetComponent(); if ((Object)(object)component == (Object)null) { return; } if (!CreatureDomainManager.TryGetConfiguredRagdollAppearance(component, out CreatureDomainManager.CreatureAppearanceRuntimeState appearance)) { CreatureDomainManager.TryGetConfiguredAppearance(source, out appearance); } if (appearance != null) { ApplyConfiguredAppearanceToRagdoll(component, appearance); } CreatureDomainManager.ApplyConfiguredRagdollTextures(component); ZNetView nview = ((Character)source).m_nview; ZDO obj = ((nview != null) ? nview.GetZDO() : null); int num = ((obj != null) ? obj.GetInt(RandomHairHashKey, 0) : 0); if (num == 0) { return; } int instanceID = ((Object)component).GetInstanceID(); EligibleVisuals.Add(instanceID); Vector3 color; bool flag = CreatureDomainManager.TryGetConfiguredAppearanceHairColor(source, out color); if (flag) { ConfiguredHairColors[instanceID] = color; component.m_hairColor = color; } else { ConfiguredHairColors.Remove(instanceID); } ZNetView component2 = ((Component)ragdoll).GetComponent(); ZDO val = ((component2 != null) ? component2.GetZDO() : null); if ((Object)(object)component2 != (Object)null && component2.IsValid() && component2.IsOwner() && val != null) { val.Set(RandomHairHashKey, num, false); if (component.m_isPlayer) { val.Set(ZDOVars.s_hairItem, num, false); } if (flag) { val.Set(ZDOVars.s_hairColor, color); val.Set(RandomHairColorManagedKey, 1, false); } } if (ApplyHairVisual(component, num)) { AppliedVisuals.Add(instanceID); } } private static void ApplyConfiguredAppearanceToRagdoll(VisEquipment visEquipment, CreatureDomainManager.CreatureAppearanceRuntimeState appearance) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0125: Unknown result type (might be due to invalid IL or missing references) ZNetView nview = visEquipment.m_nview; ZDO val = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); bool flag = (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner() && val != null; if (appearance.Hair != null) { visEquipment.m_hairItem = appearance.Hair; if (flag) { val.Set(ZDOVars.s_hairItem, (appearance.Hair.Length > 0) ? StringExtensionMethods.GetStableHashCode(appearance.Hair) : 0, false); } } if (appearance.Beard != null) { visEquipment.m_beardItem = appearance.Beard; if (flag) { val.Set(ZDOVars.s_beardItem, (appearance.Beard.Length > 0) ? StringExtensionMethods.GetStableHashCode(appearance.Beard) : 0, false); } } if (appearance.HairColor.HasValue) { visEquipment.m_hairColor = appearance.HairColor.Value; if (flag) { val.Set(ZDOVars.s_hairColor, appearance.HairColor.Value); } } if (appearance.SkinColor.HasValue) { visEquipment.m_skinColor = appearance.SkinColor.Value; if (flag) { val.Set(ZDOVars.s_skinColor, appearance.SkinColor.Value); } } int? modelIndex = appearance.ModelIndex; if (modelIndex.HasValue) { int valueOrDefault = modelIndex.GetValueOrDefault(); if (IsSupportedModelIndex(visEquipment, valueOrDefault)) { visEquipment.m_modelIndex = valueOrDefault; if (flag) { val.Set(ZDOVars.s_modelIndex, valueOrDefault, false); } } } ApplyConfiguredRagdollAppearanceVisual(visEquipment, appearance); } internal static void UpdateConfiguredRagdollAppearance(VisEquipment visEquipment) { if (!((Object)(object)visEquipment == (Object)null) && CreatureDomainManager.TryGetConfiguredRagdollAppearance(visEquipment, out CreatureDomainManager.CreatureAppearanceRuntimeState appearance)) { ApplyConfiguredRagdollAppearanceVisual(visEquipment, appearance); } } private static void ApplyConfiguredRagdollAppearanceVisual(VisEquipment visEquipment, CreatureDomainManager.CreatureAppearanceRuntimeState appearance) { if (!visEquipment.m_isPlayer) { EnsureDelegates(); ApplyConfiguredRagdollModel(visEquipment, appearance); ApplyConfiguredRagdollAccessories(visEquipment, appearance); ApplyConfiguredRagdollColors(visEquipment, appearance); } } private static void ApplyConfiguredRagdollModel(VisEquipment visEquipment, CreatureDomainManager.CreatureAppearanceRuntimeState appearance) { int? modelIndex = appearance.ModelIndex; if (!modelIndex.HasValue) { return; } int valueOrDefault = modelIndex.GetValueOrDefault(); if (!IsSupportedModelIndex(visEquipment, valueOrDefault) || visEquipment.m_models == null || visEquipment.m_models.Length == 0 || (Object)(object)visEquipment.m_bodyModel == (Object)null) { return; } visEquipment.m_modelIndex = valueOrDefault; int instanceID = ((Object)visEquipment).GetInstanceID(); if (FailedRagdollModelVisuals.Contains(instanceID)) { return; } if (_updateBaseModel == null) { LogRagdollAppearanceDelegateWarning(); return; } try { _updateBaseModel(visEquipment); } catch (Exception ex) { FailedRagdollModelVisuals.Add(instanceID); CreatureManagerPlugin.Log.LogWarning((object)("Ragdoll model appearance was disabled for '" + ((Object)visEquipment).name + "' after the supported VisEquipment model update failed: " + ex.Message)); } } private static void ApplyConfiguredRagdollAccessories(VisEquipment visEquipment, CreatureDomainManager.CreatureAppearanceRuntimeState appearance) { //IL_0070: 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) if (appearance.Hair == null && appearance.Beard == null) { return; } int instanceID = ((Object)visEquipment).GetInstanceID(); if (FailedRagdollAccessoryVisuals.Contains(instanceID)) { return; } if (_setHairEquipped == null || _setBeardEquipped == null || _getHairItem == null) { LogRagdollAppearanceDelegateWarning(); return; } try { bool flag = false; if (appearance.Beard != null) { int itemHash = ((appearance.Beard.Length > 0) ? StringExtensionMethods.GetStableHashCode(appearance.Beard) : 0); itemHash = _getHairItem(visEquipment, visEquipment.m_helmetHideBeard, itemHash, (AccessoryType)1); flag |= _setBeardEquipped(visEquipment, itemHash); } if (appearance.Hair != null && GetStoredHairHash(visEquipment) == 0) { int itemHash2 = ((appearance.Hair.Length > 0) ? StringExtensionMethods.GetStableHashCode(appearance.Hair) : 0); itemHash2 = _getHairItem(visEquipment, visEquipment.m_helmetHideHair, itemHash2, (AccessoryType)0); flag |= _setHairEquipped(visEquipment, itemHash2); } if (flag) { _updateLodgroup?.Invoke(visEquipment); } } catch (Exception ex) { FailedRagdollAccessoryVisuals.Add(instanceID); CreatureManagerPlugin.Log.LogWarning((object)("Ragdoll hair/beard appearance was disabled for '" + ((Object)visEquipment).name + "' after attachment failed: " + ex.Message)); } } private static void ApplyConfiguredRagdollColors(VisEquipment visEquipment, CreatureDomainManager.CreatureAppearanceRuntimeState appearance) { //IL_00ab: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!appearance.SkinColor.HasValue && !appearance.HairColor.HasValue) { return; } int instanceID = ((Object)visEquipment).GetInstanceID(); if (FailedRagdollColorVisuals.Contains(instanceID)) { return; } try { Renderer bodyModel = (Renderer)(object)visEquipment.m_bodyModel; if ((Object)(object)bodyModel != (Object)null) { Material[] materials = bodyModel.materials; if (appearance.SkinColor.HasValue && materials.Length != 0) { SetSupportedAppearanceColor(materials[0], appearance.SkinColor.Value); } if (appearance.HairColor.HasValue && materials.Length > 1) { SetSupportedAppearanceColor(materials[1], appearance.HairColor.Value); } } if (appearance.HairColor.HasValue) { ApplySupportedAppearanceColor(visEquipment.m_hairItemInstance, appearance.HairColor.Value); ApplySupportedAppearanceColor(visEquipment.m_beardItemInstance, appearance.HairColor.Value); } } catch (Exception ex) { FailedRagdollColorVisuals.Add(instanceID); CreatureManagerPlugin.Log.LogWarning((object)("Ragdoll color appearance was disabled for '" + ((Object)visEquipment).name + "' after material property application failed: " + ex.Message)); } } private static void ApplySupportedAppearanceColor(GameObject? itemInstance, Vector3 color) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemInstance == (Object)null) { return; } Renderer[] componentsInChildren = itemInstance.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] materials = componentsInChildren[i].materials; for (int j = 0; j < materials.Length; j++) { SetSupportedAppearanceColor(materials[j], color); } } } private static void SetSupportedAppearanceColor(Material? material, Vector3 color) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null)) { Color val = Utils.Vec3ToColor(color); if (material.HasProperty("_SkinColor")) { material.SetColor("_SkinColor", val); } else if (material.HasProperty("_Color")) { material.SetColor("_Color", val); } } } private static bool IsSupportedModelIndex(VisEquipment visEquipment, int modelIndex) { if (modelIndex < 0) { return false; } PlayerModel[] models = visEquipment.m_models; if (models == null || models.Length <= 0) { return modelIndex == 0; } return modelIndex < visEquipment.m_models.Length; } private static void LogRagdollAppearanceDelegateWarning() { if (!_ragdollAppearanceDelegateWarningLogged) { _ragdollAppearanceDelegateWarningLogged = true; CreatureManagerPlugin.Log.LogWarning((object)"Some non-player ragdoll appearance fields cannot be rendered because this Valheim version does not expose the expected guarded VisEquipment update methods."); } } internal static void RestoreEligibility(VisEquipment visEquipment) { //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_009f: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)visEquipment == (Object)null || GetStoredHairHash(visEquipment) == 0) { return; } Humanoid component = ((Component)visEquipment).GetComponent(); if ((Object)(object)component != (Object)null) { if (!CreatureDomainManager.TryGetRandomHairPrefabs(component, out IReadOnlyList prefabs) || !prefabs.Any((GameObject prefab) => (Object)(object)prefab != (Object)null)) { return; } } else if ((Object)(object)((Component)visEquipment).GetComponent() == (Object)null) { return; } int instanceID = ((Object)visEquipment).GetInstanceID(); ZNetView nview = visEquipment.m_nview; ZDO val = ((nview != null) ? nview.GetZDO() : null); if (val != null && val.GetInt(RandomHairColorManagedKey, 0) != 0) { Vector3 vec = val.GetVec3(ZDOVars.s_hairColor, visEquipment.m_hairColor); ConfiguredHairColors[instanceID] = vec; visEquipment.m_hairColor = vec; } EligibleVisuals.Add(instanceID); } internal static void Forget(VisEquipment visEquipment) { if (!((Object)(object)visEquipment == (Object)null)) { int instanceID = ((Object)visEquipment).GetInstanceID(); EligibleVisuals.Remove(instanceID); FailedVisuals.Remove(instanceID); ClearAppliedVisual(visEquipment); ConfiguredHairColors.Remove(instanceID); AppliedHairColors.Remove(instanceID); FailedRagdollAccessoryVisuals.Remove(instanceID); FailedRagdollColorVisuals.Remove(instanceID); FailedRagdollModelVisuals.Remove(instanceID); } } internal static void Reset() { EligibleVisuals.Clear(); AppliedVisuals.Clear(); FailedVisuals.Clear(); ConfiguredHairColors.Clear(); AppliedHairColors.Clear(); FailedRagdollAccessoryVisuals.Clear(); FailedRagdollColorVisuals.Clear(); FailedRagdollModelVisuals.Clear(); } private static int GetStoredHairHash(VisEquipment visEquipment) { ZNetView nview = visEquipment.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return 0; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return 0; } return zDO.GetInt(RandomHairHashKey, 0); } private static void ClearAppliedVisual(VisEquipment visEquipment) { int instanceID = ((Object)visEquipment).GetInstanceID(); AppliedHairColors.Remove(instanceID); if (AppliedVisuals.Remove(instanceID)) { ApplyHairVisual(visEquipment, 0); } } private static bool ApplyHairVisual(VisEquipment visEquipment, int hairHash) { EnsureDelegates(); if (_setHairEquipped == null || _visualRuntimeFailed) { return false; } try { bool flag = _setHairEquipped(visEquipment, hairHash); if (flag) { _updateLodgroup?.Invoke(visEquipment); } ApplyConfiguredHairMaterialColor(visEquipment, flag); return true; } catch (Exception ex) { int instanceID = ((Object)visEquipment).GetInstanceID(); FailedVisuals.Add(instanceID); EligibleVisuals.Remove(instanceID); AppliedVisuals.Remove(instanceID); AppliedHairColors.Remove(instanceID); try { _setHairEquipped(visEquipment, 0); _updateLodgroup?.Invoke(visEquipment); } catch { } CreatureManagerPlugin.Log.LogWarning((object)$"Random hair visual '{hairHash}' was disabled for '{((Object)visEquipment).name}' after attachment failed: {ex.Message}"); return false; } } private static void ApplyConfiguredHairMaterialColor(VisEquipment visEquipment, bool force) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (ConfiguredHairColors.TryGetValue(((Object)visEquipment).GetInstanceID(), out var value)) { ApplyHairMaterialColor(visEquipment, value, force); } } private static void ApplyHairMaterialColor(VisEquipment visEquipment, Vector3 color, bool force) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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) int instanceID = ((Object)visEquipment).GetInstanceID(); if (!force && AppliedHairColors.TryGetValue(instanceID, out var value)) { Vector3 val = value - color; if (((Vector3)(ref val)).sqrMagnitude <= 1E-06f) { return; } } GameObject hairItemInstance = visEquipment.m_hairItemInstance; if ((Object)(object)hairItemInstance == (Object)null) { AppliedHairColors.Remove(instanceID); return; } Color val2 = Utils.Vec3ToColor(color); Renderer[] componentsInChildren = hairItemInstance.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] materials = componentsInChildren[i].materials; foreach (Material val3 in materials) { if (!((Object)(object)val3 == (Object)null)) { if (val3.HasProperty("_SkinColor")) { val3.SetColor("_SkinColor", val2); } else if (val3.HasProperty("_Color")) { val3.SetColor("_Color", val2); } } } } AppliedHairColors[instanceID] = color; } private static void EnsureDelegates() { if (_delegatesResolved) { return; } _delegatesResolved = true; try { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(VisEquipment), "SetHairEquipped", new Type[1] { typeof(int) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(VisEquipment), "SetBeardEquipped", new Type[1] { typeof(int) }, (Type[])null); MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(VisEquipment), "GetHairItem", new Type[3] { typeof(HelmetHairType), typeof(int), typeof(AccessoryType) }, (Type[])null); MethodInfo methodInfo4 = AccessTools.DeclaredMethod(typeof(VisEquipment), "UpdateLodgroup", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo5 = AccessTools.DeclaredMethod(typeof(VisEquipment), "UpdateBaseModel", Type.EmptyTypes, (Type[])null); if (methodInfo != null) { _setHairEquipped = (SetHairEquippedDelegate)Delegate.CreateDelegate(typeof(SetHairEquippedDelegate), methodInfo); } if (methodInfo2 != null) { _setBeardEquipped = (SetBeardEquippedDelegate)Delegate.CreateDelegate(typeof(SetBeardEquippedDelegate), methodInfo2); } if (methodInfo3 != null) { _getHairItem = (GetHairItemDelegate)Delegate.CreateDelegate(typeof(GetHairItemDelegate), methodInfo3); } if (methodInfo4 != null) { _updateLodgroup = (UpdateLodgroupDelegate)Delegate.CreateDelegate(typeof(UpdateLodgroupDelegate), methodInfo4); } if (methodInfo5 != null) { _updateBaseModel = (UpdateBaseModelDelegate)Delegate.CreateDelegate(typeof(UpdateBaseModelDelegate), methodInfo5); } if (_setHairEquipped == null || _updateLodgroup == null) { _visualRuntimeFailed = true; CreatureManagerPlugin.Log.LogWarning((object)"Random hair visuals are unavailable because this Valheim version does not expose the expected VisEquipment.SetHairEquipped(int) and UpdateLodgroup() methods."); } } catch (Exception ex) { _visualRuntimeFailed = true; CreatureManagerPlugin.Log.LogWarning((object)("Random hair visuals are unavailable because VisEquipment delegates could not be created: " + ex.Message)); } } private static int SelectDeterministicIndex(int seed, int count) { int num = seed + -1640531527; int num2 = (num ^ (num >>> 16)) * 2146121005; int num3 = (num2 ^ (num2 >>> 15)) * -2073254261; return (int)((uint)(num3 ^ (num3 >>> 16)) % (uint)count); } private static void RestoreStandardHair(Humanoid humanoid, VisEquipment? visEquipment) { if ((Object)(object)visEquipment != (Object)null && visEquipment.m_isPlayer) { visEquipment.SetHairItem(humanoid.m_hairItem ?? ""); } } } internal static class CreatureKarmaManager { internal enum KarmaAddResult { Added, Saturated, Unavailable } private sealed class CreatureDeathContext { internal ZDOID DeadId; internal string Prefab = ""; internal Vector3 Position; internal int Level; internal bool Boss; internal bool Enforcer; internal bool KarmaSummoned; internal bool PlayerAttributedKill; internal ZDOID PlayerKillerId; internal CreatureModifierManager.DeathAttributionKind AttributionKind; internal float OmenChance; internal uint Epoch; } private enum EnforcerSummonFailure { None, FeatureDisabled, ServerUnavailable, KillerUnavailable, BiomeNotConfigured, BiomeDisabled, NoCandidates, ActiveEnforcerCap, ActiveBoss, Cooldown, ChanceRollFailed, NoEligibleCandidate, InvalidCandidate, NoSpawnPosition, SectorStateCapacity, SpawnFailed } private sealed class SummonCheckWindow { internal readonly Vector2i CenterZone; internal readonly Vector3 CenterPosition; internal readonly float Karma; internal readonly List EligiblePlayers; internal readonly HashSet ZoneKeys; internal SummonCheckWindow(Vector2i centerZone, Vector3 centerPosition, float karma, List eligiblePlayers, HashSet zoneKeys) { //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) CenterZone = centerZone; CenterPosition = centerPosition; Karma = karma; EligiblePlayers = eligiblePlayers; ZoneKeys = zoneKeys; } } private sealed class ConnectedPlayerContext { internal readonly long PeerUid; internal readonly ZDOID CharacterId; internal readonly Vector3 Position; internal ConnectedPlayerContext(long peerUid, ZDOID characterId, Vector3 position) { //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) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) PeerUid = peerUid; CharacterId = characterId; Position = position; } } private sealed class SectorState { internal float Karma; internal float LastKarmaTime; internal float LastEnforcerTime = -999999f; internal SectorState Clone() { return new SectorState { Karma = Karma, LastKarmaTime = LastKarmaTime, LastEnforcerTime = LastEnforcerTime }; } } internal sealed class ParsedConfiguration { private readonly Action _commit; internal ParsedConfiguration(Action commit) { _commit = commit; } internal void Commit() { _commit(); } } private sealed class KarmaSettings { internal KarmaGainSettings Karma = new KarmaGainSettings(); internal EnforcerSettings Enforcer = new EnforcerSettings(); internal static KarmaSettings Default() { return new KarmaSettings(); } } private sealed class KarmaGainSettings { internal List Thresholds = new List { 60f, 120f, 180f }; internal float DecayAfterMinutes = 15f; internal float DecayPerMinute = 30f; internal float PlayerDeathClearKarma = 100f; internal float Kill = 1f; internal float BossKill = 25f; internal float KarmaScaling = 0.3f; internal float BossKarmaScaling = 0.15f; internal float DungeonMultiplier = 4f; internal Dictionary Prefabs = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class EnforcerSettings { internal float RequiredKarma = 40f; internal float ConsumeKarma = 30f; internal float Chance = 50f; internal float Cooldown = 1200f; internal float CheckInterval = 60f; internal float SpawnRadiusMin = 24f; internal float SpawnRadiusMax = 48f; internal float DungeonSpawnerSearchRadius = 32f; internal int LevelBonus = 2; internal Dictionary Modifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); internal bool ModifiersCleared; internal Dictionary Biomes = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class ResolvedEnforcerSettings { internal float RequiredKarma; internal float ConsumeKarma; internal float Chance; internal float Cooldown; internal bool IsBoss; internal bool BossHud; internal float SpawnRadiusMin; internal float SpawnRadiusMax; internal int LevelBonus; internal Dictionary Modifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); internal bool ModifiersCleared; internal static ResolvedEnforcerSettings FromGlobal(EnforcerSettings settings) { return new ResolvedEnforcerSettings { RequiredKarma = Mathf.Max(0f, settings.RequiredKarma), ConsumeKarma = Mathf.Max(0f, settings.ConsumeKarma), Chance = Mathf.Clamp(settings.Chance, 0f, 100f), Cooldown = Mathf.Max(0f, settings.Cooldown), IsBoss = false, BossHud = true, SpawnRadiusMin = Mathf.Max(0f, settings.SpawnRadiusMin), SpawnRadiusMax = Mathf.Max(settings.SpawnRadiusMin, settings.SpawnRadiusMax), LevelBonus = Mathf.Max(0, settings.LevelBonus), Modifiers = CloneModifiers(settings.Modifiers), ModifiersCleared = settings.ModifiersCleared }; } internal ResolvedEnforcerSettings Clone() { return new ResolvedEnforcerSettings { RequiredKarma = RequiredKarma, ConsumeKarma = ConsumeKarma, Chance = Chance, Cooldown = Cooldown, IsBoss = IsBoss, BossHud = BossHud, SpawnRadiusMin = SpawnRadiusMin, SpawnRadiusMax = SpawnRadiusMax, LevelBonus = LevelBonus, Modifiers = CloneModifiers(Modifiers), ModifiersCleared = ModifiersCleared }; } } private sealed class EnforcerOverrideSettings { internal float? RequiredKarma; internal float? ConsumeKarma; internal int? LevelBonus; internal Dictionary? Modifiers; internal bool ModifiersCleared; } private sealed class EnforcerBiomeDefinition { internal bool Enabled = true; internal List Outdoor = new List(); internal List Dungeon = new List(); internal Dictionary> DungeonByLocation = new Dictionary>(StringComparer.OrdinalIgnoreCase); internal bool HasContent { get { if (Enabled && Outdoor.Count <= 0 && Dungeon.Count <= 0) { return DungeonByLocation.Count > 0; } return true; } } internal List GetCandidates(bool dungeonSummon, string dungeonLocation) { if (!dungeonSummon) { return Outdoor; } string text = (dungeonLocation ?? "").Trim(); if (text.Length > 0) { if (DungeonByLocation.TryGetValue(text, out List value) && value.Count > 0) { return value; } string expandWorldDataBaseLocationName = GetExpandWorldDataBaseLocationName(text); if (!string.Equals(expandWorldDataBaseLocationName, text, StringComparison.OrdinalIgnoreCase) && DungeonByLocation.TryGetValue(expandWorldDataBaseLocationName, out List value2) && value2.Count > 0) { return value2; } } if (Dungeon.Count <= 0) { return Outdoor; } return Dungeon; } } private sealed class EnforcerCandidateDefinition { internal EnforcerSummonSet Summon = new EnforcerSummonSet(); internal float Weight = 1f; internal List Loot = new List(); internal string Location = ""; internal EnforcerOverrideSettings Override = new EnforcerOverrideSettings(); } private sealed class EnforcerLootDefinition { internal string Prefab = ""; internal int Amount; } private sealed class EnforcerSummonSet { internal string Boss = ""; internal List Minions = new List(); } private sealed class EnforcerMinionDefinition { internal string Prefab = ""; internal int Count = 1; } private const string CountedDeathKey = "CreatureManager_KarmaDeathCounted"; private const string EnforcerKey = "CreatureManager_KarmaEnforcer"; private const string EnforcerSummonedKey = "CreatureManager_KarmaEnforcerSummoned"; private const string EnforcerNameKey = "CreatureManager_KarmaEnforcerName"; private const string EnforcerLevelBonusKey = "CreatureManager_KarmaEnforcerLevelBonus"; private const string EnforcerIsBossKey = "CreatureManager_KarmaEnforcerIsBoss"; private const string EnforcerBossHudKey = "CreatureManager_KarmaEnforcerBossHud"; private const string EnforcerLootKey = "CreatureManager_KarmaEnforcerLoot"; private const string PlayerDeathRpc = "CreatureManager_KarmaPlayerDeath"; private const string CreatureDeathRpc = "CreatureManager_KarmaCreatureDeath"; private const string BlockerObservationRpc = "CreatureManager_KarmaBlockerObservation"; private const string CenterQuoteRpc = "CreatureManager_KarmaCenterQuote"; private const string KarmaStatusRequestRpc = "CreatureManager_KarmaStatusRequest"; private const string KarmaStatusResponseRpc = "CreatureManager_KarmaStatusResponse"; private const float KarmaStatusRequestInterval = 1f; private const float CreatureDeathSyncTimeout = 2f; private const float CreatureDeathPositionTolerance = 8f; private const float ProcessedCreatureDeathRetention = 300f; private const int MaximumPendingCreatureDeaths = 512; private const int MaximumPendingCreatureDeathsPerPeer = 64; private const int MaximumEnforcerMinionEntries = 16; private const int MaximumEnforcerMinionsPerEntry = 16; private const int MaximumEnforcerMinionsPerCandidate = 16; private const int MaximumEnforcerLootEntries = 32; private const int MaximumEnforcerLootAmountPerEntry = 64; private const int MaximumEnforcerLootAmountPerCandidate = 64; private const int MaximumEnforcerPrefabNameLength = 128; private const int MaximumSerializedEnforcerLootLength = 8192; private const float SectorPruneInterval = 1f; private const int MaximumSectorScansPerPass = 256; private const int MaximumSectorStates = 32768; private const float SectorCapacityWarningInterval = 30f; private const string EnforcerNameSuffix = "$cm_suffix_enforcer"; private const string EnforcerMinionSuffix = "$cm_suffix_minion"; private const int ZoneRadius = 1; private static readonly HashSet KarmaFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "thresholds", "decay", "gain", "prefabs" }; private static readonly HashSet EnforcerFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "settings", "checks", "modifiers" }; private static readonly HashSet EnforcerBiomeFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "enabled", "enforcers", "dungeonEnforcers" }; private static readonly HashSet EnforcerCandidateFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "summon", "settings", "weight", "loot", "modifiers", "location" }; private static readonly string[] KarmaLevelQuotes = new string[5] { "$cm_message_karma_level_01", "$cm_message_karma_level_02", "$cm_message_karma_level_03", "$cm_message_karma_level_04", "$cm_message_karma_level_05" }; private static readonly string[] EnforcerSpawnQuotes = new string[5] { "$cm_message_enforcer_spawn_01", "$cm_message_enforcer_spawn_02", "$cm_message_enforcer_spawn_03", "$cm_message_enforcer_spawn_04", "$cm_message_enforcer_spawn_05" }; private static readonly string[] EnforcerDeathQuotes = new string[5] { "$cm_message_enforcer_death_01", "$cm_message_enforcer_death_02", "$cm_message_enforcer_death_03", "$cm_message_enforcer_death_04", "$cm_message_enforcer_death_05" }; private static readonly object Sync = new object(); private static readonly Dictionary Sectors = new Dictionary(StringComparer.Ordinal); private static readonly Queue SectorPruneQueue = new Queue(); private static readonly SectorState EmptySectorState = new SectorState(); private static readonly HashSet RuntimeSummonedCreatureIds = new HashSet(); private static readonly Dictionary RuntimeEnforcerSettings = new Dictionary(); private static readonly Dictionary> RuntimeEnforcerLoot = new Dictionary>(); private static readonly HashSet AppliedEnforcerLootIds = new HashSet(); private static readonly HashSet TrackedEnforcerZdoIds = new HashSet(); private static readonly HashSet TrackedBossZdoIds = new HashSet(); private static readonly HashSet ReportedBlockerZdoIds = new HashSet(); private static readonly Dictionary ObservedPlayerDeathStates = new Dictionary(); private static readonly HashSet ServerPendingCreatureDeaths = new HashSet(); private static readonly Dictionary ServerPendingCreatureDeathCounts = new Dictionary(); private static readonly Dictionary ServerProcessedCreatureDeaths = new Dictionary(); private static readonly List StaleProcessedCreatureDeaths = new List(); private static uint CreatureDeathEpoch; private static KarmaSettings Settings = KarmaSettings.Default(); private static ZRoutedRpc? RegisteredRoutedRpc; private static FieldInfo? ExpandWorldDataCurrentLocationField; private static bool ExpandWorldDataCurrentLocationFieldResolved; private static MethodInfo? ExpandWorldDataTryGetBiomeMethod; private static MethodInfo? ExpandWorldDataTryGetBiomeDisplayNameMethod; private static bool ExpandWorldDataBiomeMethodsResolved; private static float NextSummonCheckTime; private static float NextSectorPruneTime; private static float NextSectorCapacityWarningTime; private static readonly Dictionary> DungeonComponentPositionCache = new Dictionary>(StringComparer.Ordinal); private static float NextKarmaStatusRequestTime; private static int NextKarmaStatusRequestId; private static int LastKarmaStatusResponseId = -1; private static float ClientKarmaStatusValue; private static int ClientKarmaStatusLevel; private static bool ClientKarmaStatusValid; private static bool IsKarmaSystemEnabled() { return GetKarmaSystemMode() != CreatureManagerPlugin.KarmaSystemMode.Off; } private static bool IsKarmaLevelEnabled() { CreatureManagerPlugin.KarmaSystemMode karmaSystemMode = GetKarmaSystemMode(); if ((uint)(karmaSystemMode - 1) <= 1u) { return true; } return false; } private static bool IsEnforcerEnabled() { CreatureManagerPlugin.KarmaSystemMode karmaSystemMode = GetKarmaSystemMode(); if (karmaSystemMode == CreatureManagerPlugin.KarmaSystemMode.KarmaLevelAndEnforcer || karmaSystemMode == CreatureManagerPlugin.KarmaSystemMode.EnforcerOnly) { return true; } return false; } private static CreatureManagerPlugin.KarmaSystemMode GetKarmaSystemMode() { return CreatureManagerPlugin.KarmaMode?.Value ?? CreatureManagerPlugin.KarmaSystemMode.KarmaLevelAndEnforcer; } internal static bool RequiresAuthoritativeLevelBonus(ZDO zdo, bool isBoss) { if (zdo != null && IsKarmaSystemEnabled() && zdo.GetBool("CreatureManager_KarmaEnforcer", false)) { return true; } if (!IsKarmaLevelEnabled() || Settings.Karma.Thresholds.Count <= 0) { if (isBoss) { return IsKarmaSystemEnabled(); } return false; } return true; } private static int GetMaximumEnforcersPerSector() { return Mathf.Max(1, CreatureManagerPlugin.MaximumEnforcersPerSector?.Value ?? 1); } private static bool ShouldBlockEnforcerWhileBossActive() { ConfigEntry blockEnforcerWhileBossActive = CreatureManagerPlugin.BlockEnforcerWhileBossActive; if (blockEnforcerWhileBossActive == null) { return true; } return blockEnforcerWhileBossActive.Value != CreatureManagerPlugin.Toggle.Off; } private static bool ShouldBlockKarmaGainWhileBossActive() { ConfigEntry blockKarmaGainWhileBossActive = CreatureManagerPlugin.BlockKarmaGainWhileBossActive; if (blockKarmaGainWhileBossActive == null) { return false; } return blockKarmaGainWhileBossActive.Value == CreatureManagerPlugin.Toggle.On; } private static bool ShouldBlockKarmaGain(Vector3 position, ZDOID excludedCharacterId) { //IL_0023: 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) bool flag = ShouldBlockKarmaGainWhileBossActive(); ConfigEntry blockKarmaGainWhileEnforcerActive = CreatureManagerPlugin.BlockKarmaGainWhileEnforcerActive; bool flag2 = blockKarmaGainWhileEnforcerActive != null && blockKarmaGainWhileEnforcerActive.Value == CreatureManagerPlugin.Toggle.On; if (!flag && !flag2) { return false; } GetEnforcerBlockerState(position, out var activeEnforcers, out var hasNonEnforcerBoss, null, null, excludedCharacterId); if (!(flag && hasNonEnforcerBoss)) { if (flag2) { return activeEnforcers > 0; } return false; } return true; } internal static void RegisterRpcs() { if (ZRoutedRpc.instance != null && RegisteredRoutedRpc != ZRoutedRpc.instance) { ZRoutedRpc.instance.Register("CreatureManager_KarmaPlayerDeath", (Action)RPC_PlayerDeath); ZRoutedRpc.instance.Register("CreatureManager_KarmaCreatureDeath", (Action)RPC_CreatureDeath); ZRoutedRpc.instance.Register("CreatureManager_KarmaBlockerObservation", (Action)RPC_BlockerObservation); ZRoutedRpc.instance.Register("CreatureManager_KarmaCenterQuote", (Action)RPC_CenterQuote); ZRoutedRpc.instance.Register("CreatureManager_KarmaStatusRequest", (Action)RPC_KarmaStatusRequest); ZRoutedRpc.instance.Register("CreatureManager_KarmaStatusResponse", (Action)RPC_KarmaStatusResponse); RegisteredRoutedRpc = ZRoutedRpc.instance; } } internal static void ForgetCharacter(Character character) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); RuntimeSummonedCreatureIds.Remove(instanceID); RuntimeEnforcerSettings.Remove(instanceID); RuntimeEnforcerLoot.Remove(instanceID); AppliedEnforcerLootIds.Remove(instanceID); ZDOID zDOID = character.GetZDOID(); if (!((ZDOID)(ref zDOID)).IsNone()) { ReportedBlockerZdoIds.Remove(zDOID); } } } internal static void ResetRuntimeState() { CreatureDeathEpoch++; lock (Sync) { Sectors.Clear(); SectorPruneQueue.Clear(); ObservedPlayerDeathStates.Clear(); } RuntimeSummonedCreatureIds.Clear(); RuntimeEnforcerSettings.Clear(); RuntimeEnforcerLoot.Clear(); AppliedEnforcerLootIds.Clear(); TrackedEnforcerZdoIds.Clear(); TrackedBossZdoIds.Clear(); ReportedBlockerZdoIds.Clear(); ServerPendingCreatureDeaths.Clear(); ServerPendingCreatureDeathCounts.Clear(); ServerProcessedCreatureDeaths.Clear(); StaleProcessedCreatureDeaths.Clear(); DungeonComponentPositionCache.Clear(); NextSummonCheckTime = 0f; NextSectorPruneTime = 0f; NextSectorCapacityWarningTime = 0f; NextKarmaStatusRequestTime = 0f; NextKarmaStatusRequestId = 0; LastKarmaStatusResponseId = -1; ClientKarmaStatusValue = 0f; ClientKarmaStatusLevel = 0; ClientKarmaStatusValid = false; } internal static string BuildDefaultYaml() { return "# CreatureManager Karma configuration.\r\n# Karma uses a sliding 3x3 vanilla zone neighborhood. Kill creatures in a neighborhood to raise its Karma.\r\n# Higher Karma can add level bonuses to future spawns in that neighborhood.\r\n# Enforcer summons a boss-style non-boss creature with minions in a high-Karma neighborhood.\r\n# Overlapping player-centered 3x3 windows join transitively into one connected check region.\r\n# Each connected region rolls once; its highest-Karma eligible window supplies the table and spawn location.\r\n# Blockers and cooldowns scan the full region, while Karma consumption and cooldown writes use that anchor window.\n# Feature mode, Enforcer cap, summon blocking, and Karma-gain blocking are configured in BepInEx section '3 - Karma'.\n# Karma gain, decay, thresholds, and Enforcer checks are global live rules; reload does not clear stored regional Karma.\n# Spawn-time Karma level bonuses, modifiers, loot, and Enforcer creature definitions affect later spawns, not existing Enforcers.\n# In any modifiers field, omission or {} keeps normal inheritance/fallback; [] is a terminal clear that blocks every lower modifier source.\n# A mapping overrides listed values only. Candidates inherit Enforcer.modifiers, and Enforcer.modifiers inherits omitted values from levels.yml.\n# Safety limits per candidate: at most 16 minion entries/16 total minions and 32 loot entries/64 total loot items.\n\r\nkarma:\n thresholds: [60, 120, 180] # Karma values reached for +1, +2, +3 level bonus; append more to extend levels. Positive gain stops at the highest value; [] leaves gain uncapped.\n decay: [15, 30, 100] # [afterMinutes, karmaPerMinute, playerDeathClearKarma].\r\n gain: [1, 25, 0.3, 0.15, 4] # [kill, bossKill, karmaScaling, bossKarmaScaling, dungeonMultiplier].\r\n prefabs: # Per-prefab Karma gain overrides.\r\n Troll: 5 # This prefab grants this exact Karma amount before dungeonMultiplier.\r\n Abomination: 5\r\n StoneGolem: 5\r\n GoblinBrute: 3\r\n Lox: 5\r\n Gjall: 5\r\n SeekerBrute: 5\r\n Morgen: 5\r\n Morgen_NonSleeping: 5\r\n FallenValkyrie: 8\r\n\r\nEnforcer:\r\n settings: [40, 30, 2] # [requiredKarma, consumeKarma, levelBonus].\r\n checks: [50, 1200, 60, 24~48] # [chance% per connected-region check, cooldownSeconds, checkIntervalSeconds, outdoorSpawnRadius].\r\n modifiers: # Partial Enforcer table. Omitted values inherit levels.yml; [] blocks that fallback.\r\n # Offense: Enraged to Undodgeable\r\n enraged: 10, 0.15 # chance%, outgoingDamageBonus.\r\n fire: 10, 0.2 # chance%, addedFireDamage.\r\n frost: 10, 0.1 # chance%, addedFrostDamage.\r\n lightning: 10, 0.1 # chance%, addedLightningDamage.\r\n spirit: 10, 0.05 # chance%, addedSpiritDoT.\r\n armorPiercing: 10, 0.3 # chance%, ignoredPlayerArmor.\r\n staggering: 10, 0.6 # chance%, staggerBonus.\r\n undodgeable: 10, 0.25 # chance%, damageReduction; attacks against players ignore dodge invulnerability.\r\n # Defense: Armored to Chameleon\r\n armored: 10, 0.3 # chance%, damageReduction.\r\n deathward: 10, 0.2, 10, 3 # chance%, restoredHealth, cooldownSeconds, maxActivations.\r\n regenerating: 10, 0.005 # chance%, maxHealthRegenPerSecond.\r\n reflection: 10, 0.1, 0.5 # chance%, actualMeleeDamageReflected, procChance.\r\n vortex: 10, 0.5 # chance%, projectileIgnoreProc.\r\n adaptive: 10, 0.5 # chance%, rememberedTypeDamageReduction.\r\n unflinching: 10 # chance%; prevents normal-hit and perfect-parry stagger.\r\n chameleon: 10, 10 # chance%, immunitySwitchSeconds.\r\n # Affliction: Exposed to ToxicDeath\r\n exposed: 10, 0.2, 0.5, 5 # chance%, damageTaken, proc, duration.\r\n weakened: 10, 0.2, 0.5, 5 # chance%, outgoingDamageReduction, proc, duration.\r\n withered: 10, 0.5, 0.5, 5 # chance%, healingReduction, proc, duration.\r\n crippling: 10, 0.5, 0.5, 0.5, 5 # chance%, moveReduction, jumpReduction, proc, duration.\r\n disruptive: 10, 0.5, 0.5, 0.5, 5 # chance%, staminaRegenReduction, eitrRegenReduction, proc, duration.\r\n adrenalineDrain: 10, 0.5, 0.5, 0.5, 5 # chance%, currentAdrenalineRemoved, adrenalineGainReduction, procChance, duration.\r\n corrosive: 10, 0.5, 0.5, 5 # chance%, durabilityLossBonus, procChance, duration. Equipped armor, weapons, and shield only.\r\n toxicDeath: 10, 0.3, 4, blob_aoe # chance%, maxHealthDamage, radius, triggerEffect.\r\n # Special: Swift to Blamer\r\n swift: 10, 0.4 # chance%, movementSpeedBonus.\r\n attackSpeed: 10, 0.3 # chance%, attackSpeedBonus.\r\n vampiric: 10, 0.3 # chance%, actualDirectDamageHealing.\r\n reaping: 10, 0.05, 20, 0.1, 2, 0.01, 0.2, 0.05, 0.5 # chance%, heal/base, healMaxActivations, maxHealth/base, maxHealthCap, damagePerKill, damageCap, scalePerKill, scaleCap. New scale gains are disabled in dungeons.\r\n blink: 10, 6, 16, fx_Adrenaline1 # chance%, cooldown, maxRange, startEffect.\n omen: 10, 0.5 # chance%, forcedEnforcerChance.\r\n juggernaut: 10, 150, 5 # chance%, minimumPushForce, cooldownSeconds.\r\n blamer: 0, 1, 60, 0.75 # chance%, karmaPerSecond, maxKarmaGain, fleeHealthRatio. 0 cap is unlimited.\r\n\r\n# Biome-specific Enforcer tables. Global can be used as fallback.\r\n# Enforcer and minion AI always hunts the player.\r\n# Non-boss Enforcers use a boss-style HUD; original boss prefabs keep their boss gameplay flag.\r\nBlackForest:\r\n enabled: true # If false, disables Enforcer summons for this biome.\r\n enforcers: # Candidate entries may use summon, settings, weight, loot, and modifiers.\r\n - summon: [Greydwarf_Elite, Greydwarf:2, Greydwarf_Shaman] # [enforcerPrefab, minionPrefab[:count], ...]\r\n settings: [40, 30, 1] # Optional [requiredKarma, consumeKarma, levelBonus].\r\n weight: 1 # Optional weighted random chance among eligible candidates.\r\n loot: [TrophyGreydwarfBrute:1, Amber:3] # Optional guaranteed bonus drops as itemPrefab:amount; original creature drops are retained.\r\n modifiers: # Optional partial map; omitted entries inherit Enforcer.modifiers. {} is the same as omitting this field.\r\n staggering: 30, 0.6 # chance%, staggerBonus.\r\n deathward: 30, 0.2, 10, 3 # chance%, restored max-health ratio, cooldown seconds, max activations.\r\n toxicDeath: 30, 0.3, 4, blob_aoe\r\n juggernaut: 30, 150, 5\r\n - summon: [Bjorn]\r\n settings: [50, 40, 1]\r\n weight: 3\r\n loot: [TrophyBjorn:1, Amber:3]\r\n modifiers: # Optional partial map; omitted entries inherit Enforcer.modifiers. {} is the same as omitting this field.\r\n lightning: 10, 0.1\r\n deathward: 20, 0.2, 10, 3\r\n disruptive: 10, 0.5, 0.5, 0.5, 5\r\n blink: 10, 6, 16, fx_Adrenaline1\n dungeonEnforcers:\r\n - summon: [Skeleton_Poison, Skeleton:2]\r\n weight: 2\r\n loot: [TrophySkeletonPoison:1, TrophySkeleton:1, Amber:3]\r\n - summon: [Ghost, Skeleton]\r\n loot: [TrophyGhost:1, Amber:3]\r\n\r\nSwamp:\r\n enabled: true\r\n enforcers:\r\n - summon: [Wraith, Ghost:2]\r\n loot: [TrophyWraith:1, AmberPearl:2]\r\n dungeonEnforcers:\r\n - summon: [Draugr_Elite, Draugr, Draugr_Ranged]\r\n loot: [TrophyDraugrElite:1, AmberPearl:2]\r\n\r\nMountain:\r\n enabled: true\r\n enforcers:\r\n - summon: [Fenring, Wolf:2]\r\n weight: 2\r\n loot: [TrophyFenring:1, AmberPearl:2]\r\n - summon: [Hatchling, Hatchling:2]\r\n loot: [TrophyHatchling:1, AmberPearl:2]\r\n dungeonEnforcers:\r\n - summon: [Fenring_Cultist, Ulv:2]\r\n loot: [TrophyCultist:1, AmberPearl:2]\r\n\r\nPlains:\r\n enabled: true\r\n enforcers:\r\n - summon: [GoblinBrute, GoblinShaman, GoblinArcher, Goblin:2]\r\n loot: [TrophyGoblinBrute:1, Ruby:2]\r\n - summon: [Deathsquito, Deathsquito:4]\r\n loot: [TrophyDeathsquito:1, Ruby:2]\r\n\r\nMistlands:\r\n enabled: true\r\n enforcers:\r\n - summon: [Gjall, Tick:4]\r\n loot: [TrophyGjall:1, Ruby:2]\r\n dungeonEnforcers:\r\n - summon: [SeekerBrute, Seeker:2]\r\n loot: [TrophySeekerBrute:1, Ruby:2]\r\n\r\nAshLands:\r\n enabled: true\r\n enforcers:\r\n - summon: [Charred_Mage, Charred_Archer, Charred_Melee]\r\n weight: 3\r\n loot: [TrophyCharredMelee:1, TrophyCharredMage:1, TrophyCharredArcher:1, SilverNecklace:2]\r\n - summon: [FallenValkyrie, Volture:2]\r\n loot: [TrophyFallenValkyrie:1, SilverNecklace:2]\r\n - summon: [Morgen_NonSleeping, Charred_Twitcher:2]\r\n loot: [TrophyMorgen:1, SilverNecklace:2]\r\n\r\n# Location-specific dungeon Enforcer entries.\r\n# Use location: LocationPrefab to restrict a dungeon entry to that location.\r\n# Quote Expand World Data clone names because the colon is part of the value.\r\n#Mountain:\r\n# dungeonEnforcers:\r\n# - summon: [Fenring_Cultist, Ulv:2]\r\n# location: MountainCave02\r\n# - summon: [AlphaWolf]\r\n# location: SomeModLocation\r\n# - summon: [AlphaWolf]\r\n# location: \"MountainCave02:cloned\""; } internal static bool TryParseYaml(string yaml, string source, out ParsedConfiguration parsed) { parsed = null; try { KarmaSettings loaded = (string.IsNullOrWhiteSpace(yaml) ? KarmaSettings.Default() : ReadSettings(yaml, source)); parsed = new ParsedConfiguration(delegate { Settings = loaded; }); return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to load Karma configuration from " + source + "; existing Karma settings were kept: " + ex.Message)); return false; } } internal static void CommitParsedConfiguration(ParsedConfiguration parsed) { if (parsed == null) { throw new ArgumentNullException("parsed"); } parsed.Commit(); } internal static void RecordDeath(Character character) { if (IsKarmaSystemEnabled() && !((Object)(object)character == (Object)null) && character.IsPlayer()) { RecordPlayerDeath(character); } } internal static void RecordDeath(Character character, CreatureModifierManager.FinalDeathAttribution attribution) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer() || character.IsTamed() || !attribution.HasSource) { return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null || zDO.GetBool("CreatureManager_KarmaDeathCounted", false)) { return; } Vector3 position = ((Component)character).transform.position; if (!IsFinite(position)) { return; } if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { if (TryBuildCreatureDeathContext(zDO, character, attribution.Source, attribution.Kind, position, out CreatureDeathContext context)) { ProcessAuthorizedCreatureDeath(context, zDO, validatedDestroyedZdo: false); } } else if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(zDO.m_uid); val.Write(attribution.Source); val.Write((int)attribution.Kind); val.Write(position); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_KarmaCreatureDeath", new object[1] { val }); } } private static void RecordPlayerDeath(Character player) { Player val = (Player)(object)((player is Player) ? player : null); if (val == null) { return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { ZNetView nview = ((Character)val).m_nview; ZDO val2 = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); if (val2 != null && IsPlayerCharacterZdo(val2)) { ObservePlayerDeathState(val2); } } else { SendPlayerDeathKarmaClear(); } } private static void SendPlayerDeathKarmaClear() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_KarmaPlayerDeath", new object[1] { (object)new ZPackage() }); } } private static void RPC_PlayerDeath(long sender, ZPackage package) { if (!IsKarmaSystemEnabled() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { if (TryGetPeerPlayerZdo(sender, out ZNetPeer _, out ZDO zdo)) { ObservePlayerDeathState(zdo); } } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to process Karma player death RPC: " + ex.Message)); } } private static void RPC_CreatureDeath(long sender, ZPackage package) { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) //IL_0066: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return; } ZDOID val = ZDOID.None; try { ZDOID val2 = package.ReadZDOID(); ZDOID val3 = package.ReadZDOID(); int num = package.ReadInt(); Vector3 val4 = package.ReadVector3(); if (val2 == ZDOID.None || val3 == ZDOID.None || val3 == val2 || !Enum.IsDefined(typeof(CreatureModifierManager.DeathAttributionKind), num) || num == 0 || !IsFinite(val4)) { return; } ZDO zDO = ZDOMan.instance.GetZDO(val2); if (zDO == null || zDO.GetOwner() != sender || zDO.GetBool("CreatureManager_KarmaDeathCounted", false) || !IsFinite(zDO.GetPosition())) { return; } Vector3 val5 = zDO.GetPosition() - val4; if (!(((Vector3)(ref val5)).sqrMagnitude > 64f) && !ServerProcessedCreatureDeaths.ContainsKey(val2) && !ServerPendingCreatureDeaths.Contains(val2) && ServerPendingCreatureDeaths.Count < 512 && (!ServerPendingCreatureDeathCounts.TryGetValue(sender, out var value) || value < 64)) { ServerPendingCreatureDeaths.Add(val2); ServerPendingCreatureDeathCounts[sender] = value + 1; val = val2; Character deadCharacter = TryFindCharacter(val2); if (!TryBuildCreatureDeathContext(zDO, deadCharacter, val3, (CreatureModifierManager.DeathAttributionKind)num, val4, out CreatureDeathContext context)) { ReleasePendingCreatureDeath(val2, sender); val = ZDOID.None; } else { ZDOMan instance = ZDOMan.instance; ((MonoBehaviour)ZNet.instance).StartCoroutine(AuthorizeCreatureDeathAfterSync(sender, val4, context, instance)); val = ZDOID.None; } } } catch (Exception ex) { if (val != ZDOID.None) { ReleasePendingCreatureDeath(val, sender); } CreatureManagerPlugin.Log.LogDebug((object)("Failed to process Karma creature death RPC: " + ex.Message)); } } private static IEnumerator AuthorizeCreatureDeathAfterSync(long sender, Vector3 reportedPosition, CreatureDeathContext context, ZDOMan authority) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) float deadline = Time.realtimeSinceStartup + 2f; try { while (Time.realtimeSinceStartup <= deadline && ZDOMan.instance == authority && context.Epoch == CreatureDeathEpoch) { ZDO zDO = authority.GetZDO(context.DeadId); if (zDO == null) { ProcessAuthorizedCreatureDeath(context, null, validatedDestroyedZdo: true); break; } if (zDO.GetOwner() == sender && !zDO.GetBool("CreatureManager_KarmaDeathCounted", false) && IsFinite(zDO.GetPosition())) { Vector3 val = zDO.GetPosition() - reportedPosition; if (((Vector3)(ref val)).sqrMagnitude > 64f) { break; } bool flag = zDO.GetBool(ZDOVars.s_dead, false) || zDO.GetFloat(ZDOVars.s_health, float.PositiveInfinity) <= 0f; Character val2 = TryFindCharacter(context.DeadId); if (!flag && (Object)(object)val2 != (Object)null) { flag = val2.IsDead() || val2.GetHealth() <= 0f; } if (flag) { context.Position = zDO.GetPosition(); ProcessAuthorizedCreatureDeath(context, zDO, validatedDestroyedZdo: false); break; } yield return null; continue; } break; } } finally { ReleasePendingCreatureDeath(context.DeadId, sender); } } private static bool TryBuildCreatureDeathContext(ZDO deadZdo, Character? deadCharacter, ZDOID sourceId, CreatureModifierManager.DeathAttributionKind attributionKind, Vector3 reportedPosition, out CreatureDeathContext context) { //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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) context = null; if (deadZdo == null || sourceId == ZDOID.None || sourceId == deadZdo.m_uid || attributionKind == CreatureModifierManager.DeathAttributionKind.None || !IsFinite(reportedPosition) || deadZdo.GetBool(ZDOVars.s_tamed, (Object)(object)deadCharacter != (Object)null && deadCharacter.IsTamed()) || (Object)(object)ZNetScene.instance == (Object)null || !TryValidatePlayerSideDeathSource(sourceId, out var sourceIsPlayer)) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab(deadZdo.GetPrefab()); Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val == (Object)null || val.IsPlayer()) { return false; } bool flag = (((Object)(object)deadCharacter != (Object)null) ? deadCharacter.IsBoss() : val.IsBoss()); bool flag2 = deadZdo.GetBool("CreatureManager_KarmaEnforcer", false); bool flag3 = sourceIsPlayer; if (flag3) { bool flag4 = (uint)(attributionKind - 1) <= 1u; flag3 = flag4; } bool flag5 = flag3; float chance = 0f; if (flag5 && CreatureLevelManager.AllowsModifierEffects(deadZdo, flag, flag2)) { CreatureModifierManager.TryGetOmenEnforcerChance(deadZdo, out chance); } context = new CreatureDeathContext { DeadId = deadZdo.m_uid, Prefab = Utils.GetPrefabName(prefab), Position = deadZdo.GetPosition(), Level = Mathf.Max(1, deadZdo.GetInt(ZDOVars.s_level, (deadCharacter == null) ? 1 : deadCharacter.GetLevel())), Boss = flag, Enforcer = flag2, KarmaSummoned = (deadZdo.GetBool("CreatureManager_KarmaEnforcerSummoned", false) || ((Object)(object)deadCharacter != (Object)null && IsRuntimeSummonedCreature(deadCharacter))), PlayerAttributedKill = flag5, PlayerKillerId = (flag5 ? sourceId : ZDOID.None), AttributionKind = attributionKind, OmenChance = chance, Epoch = CreatureDeathEpoch }; if (context.DeadId != ZDOID.None) { return IsFinite(context.Position); } return false; } private static void ProcessAuthorizedCreatureDeath(CreatureDeathContext context, ZDO? deadZdo, bool validatedDestroyedZdo) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || context == null || context.DeadId == ZDOID.None || context.Epoch != CreatureDeathEpoch || (deadZdo == null && !validatedDestroyedZdo) || (deadZdo != null && deadZdo.m_uid != context.DeadId)) { return; } PruneProcessedCreatureDeaths(); if ((deadZdo != null && deadZdo.GetBool("CreatureManager_KarmaDeathCounted", false)) || ServerProcessedCreatureDeaths.ContainsKey(context.DeadId)) { return; } ServerProcessedCreatureDeaths[context.DeadId] = Time.realtimeSinceStartup + 300f; if (deadZdo != null) { deadZdo.Set("CreatureManager_KarmaDeathCounted", true); } if (context.Enforcer) { BroadcastCenterQuote(EnforcerDeathQuotes); } if (context.KarmaSummoned) { return; } if (!ShouldBlockKarmaGain(context.Position, context.DeadId)) { float killKarma = GetKillKarma(context.Prefab, context.Boss, context.Level, IsLikelyDungeonPosition(context.Position)); if (killKarma > 0f) { AddKarma(context.Position, killKarma); } } if (context.PlayerAttributedKill && IsEnforcerEnabled() && context.OmenChance > 0f && Random.Range(0f, 1f) < context.OmenChance) { EnforcerSummonFailure failure; bool flag = TryForceEnforcerSummonNear(context.PlayerKillerId, context.DeadId, out failure); string arg = (flag ? "" : $" reason={failure}"); CreatureManagerPlugin.Log.LogInfo((object)($"Karma Omen triggered by {context.Prefab}: attribution={context.AttributionKind} " + $"chance={context.OmenChance:P0} summoned={flag}{arg}")); } } private static bool TryValidatePlayerSideDeathSource(ZDOID sourceId, out bool sourceIsPlayer) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Invalid comparison between Unknown and I4 sourceIsPlayer = false; if (sourceId == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null) { return false; } if ((Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).GetZDOID() == sourceId) { sourceIsPlayer = true; return true; } if ((Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.IsReady() && peer.m_characterID == sourceId) { sourceIsPlayer = true; return true; } } } Character val = TryFindCharacter(sourceId); if ((Object)(object)val != (Object)null) { if (val.IsPlayer()) { return false; } if (!val.IsTamed()) { return (int)val.GetFaction() == 11; } return true; } if (ZDOMan.instance == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(sourceId); if (zDO == null) { return false; } if (zDO.GetBool(ZDOVars.s_tamed, false)) { return true; } GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab()); Character val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val2 != (Object)null && !val2.IsPlayer()) { return (int)val2.GetFaction() == 11; } return false; } private static Character? TryFindCharacter(ZDOID id) { //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_0021: Unknown result type (might be due to invalid IL or missing references) if (id == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject val = ZNetScene.instance.FindInstance(id); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent(); } private static void PruneProcessedCreatureDeaths() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; StaleProcessedCreatureDeaths.Clear(); foreach (KeyValuePair serverProcessedCreatureDeath in ServerProcessedCreatureDeaths) { if (serverProcessedCreatureDeath.Value <= realtimeSinceStartup) { StaleProcessedCreatureDeaths.Add(serverProcessedCreatureDeath.Key); } } foreach (ZDOID staleProcessedCreatureDeath in StaleProcessedCreatureDeaths) { ServerProcessedCreatureDeaths.Remove(staleProcessedCreatureDeath); } StaleProcessedCreatureDeaths.Clear(); } private static void ReleasePendingCreatureDeath(ZDOID deadId, long sender) { //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_0012: Unknown result type (might be due to invalid IL or missing references) if (!(deadId == ZDOID.None) && ServerPendingCreatureDeaths.Remove(deadId)) { if (!ServerPendingCreatureDeathCounts.TryGetValue(sender, out var value) || value <= 1) { ServerPendingCreatureDeathCounts.Remove(sender); } else { ServerPendingCreatureDeathCounts[sender] = value - 1; } } } 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) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } private static void RequestKarmaStatus() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && !(Time.time < NextKarmaStatusRequestTime)) { NextKarmaStatusRequestTime = Time.time + 1f; int num = ++NextKarmaStatusRequestId; ZPackage val = new ZPackage(); val.Write(num); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_KarmaStatusRequest", new object[1] { val }); } } private static void RPC_KarmaStatusRequest(long sender, ZPackage package) { //IL_003c: 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_0049: Expected O, but got Unknown if (!IsKarmaSystemEnabled() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } try { int num = package.ReadInt(); if (TryGetPeerReferencePosition(sender, out var position)) { float karma = GetKarma(position); ZPackage val = new ZPackage(); val.Write(num); val.Write(karma); val.Write(IsKarmaLevelEnabled() ? GetSectorLevelBonus(karma) : 0); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "CreatureManager_KarmaStatusResponse", new object[1] { val }); } } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to process Karma status request: " + ex.Message)); } } private static void RPC_KarmaStatusResponse(long sender, ZPackage package) { if (ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID()) { return; } try { int num = package.ReadInt(); float num2 = package.ReadSingle(); int num3 = package.ReadInt(); if (num >= LastKarmaStatusResponseId) { LastKarmaStatusResponseId = num; ClientKarmaStatusValue = Mathf.Max(0f, num2); ClientKarmaStatusLevel = Mathf.Max(0, num3); ClientKarmaStatusValid = true; } } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to process Karma status response: " + ex.Message)); } } private static bool TryGetPeerReferencePosition(long sender, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if ((Object)(object)ZNet.instance == (Object)null) { return false; } ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null) { return false; } position = peer.m_refPos; return true; } private static bool TryGetPeerPlayerZdo(long sender, out ZNetPeer peer, out ZDO zdo) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) peer = null; zdo = null; if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null) { return false; } peer = ZNet.instance.GetPeer(sender); if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone()) { peer = null; return false; } zdo = ZDOMan.instance.GetZDO(peer.m_characterID); if (zdo == null || zdo.GetOwner() != peer.m_uid || !IsPlayerCharacterZdo(zdo)) { peer = null; zdo = null; return false; } return true; } private static bool IsPlayerCharacterZdo(ZDO zdo) { if (zdo == null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); if ((Object)(object)prefab != (Object)null) { return (Object)(object)prefab.GetComponent() != (Object)null; } return false; } private static void ObservePlayerDeathState(ZDO zdo) { //IL_006d: 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_0085: 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) float num = zdo.GetFloat(ZDOVars.s_health, float.PositiveInfinity); bool flag = zdo.GetBool(ZDOVars.s_dead, false) && !float.IsNaN(num) && !float.IsInfinity(num) && num <= 0f; bool flag2 = false; lock (Sync) { if (!flag) { ObservedPlayerDeathStates[zdo.m_uid] = false; return; } if (!ObservedPlayerDeathStates.TryGetValue(zdo.m_uid, out var value) || !value) { ObservedPlayerDeathStates[zdo.m_uid] = true; flag2 = true; } } if (flag2 && IsKarmaSystemEnabled() && Settings.Karma.PlayerDeathClearKarma > 0f) { ApplyPlayerDeathKarmaClear(zdo.GetPosition()); } } private static void RefreshObservedPlayerDeathTransitions() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return; } HashSet activeCharacterIds = new HashSet(); ZDOID localPlayerCharacterID = ZNet.instance.LocalPlayerCharacterID; if (!((ZDOID)(ref localPlayerCharacterID)).IsNone()) { activeCharacterIds.Add(localPlayerCharacterID); ZDO zDO = ZDOMan.instance.GetZDO(localPlayerCharacterID); if (zDO != null && IsPlayerCharacterZdo(zDO)) { ObservePlayerDeathState(zDO); } } foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers()) { if (connectedPeer != null && connectedPeer.IsReady() && !((ZDOID)(ref connectedPeer.m_characterID)).IsNone()) { activeCharacterIds.Add(connectedPeer.m_characterID); if (TryGetPeerPlayerZdo(connectedPeer.m_uid, out ZNetPeer _, out ZDO zdo)) { ObservePlayerDeathState(zdo); } } } lock (Sync) { foreach (ZDOID item in ObservedPlayerDeathStates.Keys.Where((ZDOID characterId) => !activeCharacterIds.Contains(characterId)).ToList()) { ObservedPlayerDeathStates.Remove(item); } } } private static void ApplyPlayerDeathKarmaClear(Vector3 position) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { ReduceKarmaUnsafe(position, Settings.Karma.PlayerDeathClearKarma, Time.time); } } internal static void UpdateSummons() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RefreshObservedPlayerDeathTransitions(); PruneSectorStates(Time.time); } if (!IsEnforcerEnabled() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null) { return; } float time = Time.time; if (time < NextSummonCheckTime) { return; } NextSummonCheckTime = time + Mathf.Max(1f, Settings.Enforcer.CheckInterval); DungeonComponentPositionCache.Clear(); foreach (var (connectedPlayerContext, value, regionZoneKeys) in BuildSummonCheckRegions(GetConnectedAlivePlayerContexts())) { if (!TrySummonForPlayer(connectedPlayerContext, time, out var failure, ignoreCooldown: false, ignoreChance: false, ignoreRequiredKarma: false, value, regionZoneKeys)) { CreatureManagerPlugin.Log.LogDebug((object)($"Karma Enforcer periodic check skipped for player {connectedPlayerContext.CharacterId} " + $"(peer {connectedPlayerContext.PeerUid}): reason={failure}.")); } } } private static List GetConnectedAlivePlayerContexts() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return list; } HashSet hashSet = new HashSet(); foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers()) { if (TryCreateConnectedPlayerContext(connectedPeer, out ConnectedPlayerContext player) && hashSet.Add(player.CharacterId)) { list.Add(player); } } Player localPlayer = Player.m_localPlayer; ZNetView val = (((Object)(object)localPlayer != (Object)null) ? ((Character)localPlayer).m_nview : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && val2 != null && IsPlayerCharacterZdo(val2) && TryCreateConnectedPlayerContext(val2.GetOwner(), val2, out ConnectedPlayerContext player2) && hashSet.Add(player2.CharacterId)) { list.Add(player2); } return list; } private static bool TryCreateConnectedPlayerContext(ZNetPeer peer, out ConnectedPlayerContext player) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) player = null; if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone() || ZDOMan.instance == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO != null && zDO.GetOwner() == peer.m_uid && IsPlayerCharacterZdo(zDO)) { return TryCreateConnectedPlayerContext(peer.m_uid, zDO, out player); } return false; } private static bool TryCreateConnectedPlayerContext(long peerUid, ZDO playerZdo, out ConnectedPlayerContext player) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) player = null; if (playerZdo == null || ((ZDOID)(ref playerZdo.m_uid)).IsNone() || !IsPlayerCharacterZdo(playerZdo)) { return false; } Vector3 position = playerZdo.GetPosition(); float num = playerZdo.GetFloat(ZDOVars.s_health, float.PositiveInfinity); if (playerZdo.GetBool(ZDOVars.s_dead, false) || (!float.IsNaN(num) && !float.IsInfinity(num) && num <= 0f) || !IsFinite(position) || float.IsNaN(num)) { return false; } player = new ConnectedPlayerContext(peerUid, playerZdo.m_uid, position); return true; } private static List<(ConnectedPlayerContext Representative, Vector3 CenterPosition, HashSet RegionZoneKeys)> BuildSummonCheckRegions(IReadOnlyList players) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00ee: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) Dictionary<(int, int), List> dictionary = new Dictionary<(int, int), List>(); foreach (ConnectedPlayerContext player in players) { Vector2i zone = ZoneSystem.GetZone(player.Position); (int, int) key = (zone.x, zone.y); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List()); } value.Add(player); } List list2 = new List(); foreach (KeyValuePair<(int, int), List> item in dictionary) { Vector2i centerZone = new Vector2i(item.Key.Item1, item.Key.Item2); Vector3 zonePos = ZoneSystem.GetZonePos(centerZone); zonePos.y = item.Value[0].Position.y; float karma = GetKarma(zonePos); List eligiblePlayers = (from player in players where IsInKarmaNeighborhood(player.Position, centerZone) where HasEligibleEnforcerCandidate(player, karma) select player).ToList(); list2.Add(new SummonCheckWindow(centerZone, zonePos, karma, eligiblePlayers, new HashSet(GetSectorKeys(zonePos), StringComparer.Ordinal))); } bool[] array = new bool[list2.Count]; List<(ConnectedPlayerContext, Vector3, HashSet)> list3 = new List<(ConnectedPlayerContext, Vector3, HashSet)>(); for (int num = 0; num < list2.Count; num++) { if (array[num]) { continue; } List list4 = new List(); Stack stack = new Stack(); stack.Push(num); array[num] = true; while (stack.Count > 0) { SummonCheckWindow summonCheckWindow = list2[stack.Pop()]; list4.Add(summonCheckWindow); for (int num2 = 0; num2 < list2.Count; num2++) { if (!array[num2] && summonCheckWindow.ZoneKeys.Overlaps(list2[num2].ZoneKeys)) { array[num2] = true; stack.Push(num2); } } } SummonCheckWindow summonCheckWindow2 = (from window in list4 where window.EligiblePlayers.Count > 0 orderby window.Karma descending, window.EligiblePlayers.Count descending, window.CenterZone.x, window.CenterZone.y select window).FirstOrDefault(); if (summonCheckWindow2 == null) { continue; } ConnectedPlayerContext connectedPlayerContext = summonCheckWindow2.EligiblePlayers[Random.Range(0, summonCheckWindow2.EligiblePlayers.Count)]; Vector3 centerPosition = summonCheckWindow2.CenterPosition; centerPosition.y = connectedPlayerContext.Position.y; HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (SummonCheckWindow item2 in list4) { hashSet.UnionWith(item2.ZoneKeys); } list3.Add((connectedPlayerContext, centerPosition, hashSet)); } return list3; } private static bool HasEligibleEnforcerCandidate(ConnectedPlayerContext player, float karma) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_000d: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 position = player.Position; Biome biome = GetBiome(position); bool flag = IsLikelyDungeonPosition(position); if (!TryGetEnforcerBiomeDefinition(biome, out EnforcerBiomeDefinition summon) || !summon.Enabled) { return false; } string locationPrefab; string dungeonLocation = ((flag && TryGetDungeonLocationPrefabName(position, out locationPrefab)) ? locationPrefab : ""); ResolvedEnforcerSettings baseline = ResolvedEnforcerSettings.FromGlobal(Settings.Enforcer); foreach (EnforcerCandidateDefinition candidate in summon.GetCandidates(flag, dungeonLocation)) { if (!(candidate.Weight <= 0f) && candidate.Summon.Boss.Length != 0) { ResolvedEnforcerSettings resolvedEnforcerSettings = ResolveEnforcerSettings(candidate.Override, baseline); if (karma >= resolvedEnforcerSettings.RequiredKarma) { return true; } } } return false; } private static bool TryForceEnforcerSummonNear(ZDOID killerId, ZDOID excludedDeadId, out EnforcerSummonFailure failure) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) failure = EnforcerSummonFailure.None; if (!IsEnforcerEnabled()) { failure = EnforcerSummonFailure.FeatureDisabled; return false; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null) { failure = EnforcerSummonFailure.ServerUnavailable; return false; } if (!TryFindConnectedAlivePlayer(killerId, out ConnectedPlayerContext player)) { failure = EnforcerSummonFailure.KillerUnavailable; return false; } ConfigEntry blockOmenEnforcerDuringCooldown = CreatureManagerPlugin.BlockOmenEnforcerDuringCooldown; bool ignoreCooldown = blockOmenEnforcerDuringCooldown != null && blockOmenEnforcerDuringCooldown.Value == CreatureManagerPlugin.Toggle.Off; return TrySummonForPlayer(player, Time.time, out failure, ignoreCooldown, ignoreChance: true, ignoreRequiredKarma: true, null, null, excludedDeadId); } private static bool TryFindConnectedAlivePlayer(ZDOID playerId, out ConnectedPlayerContext player) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //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) player = null; if (playerId == ZDOID.None) { return false; } foreach (ConnectedPlayerContext connectedAlivePlayerContext in GetConnectedAlivePlayerContexts()) { if (connectedAlivePlayerContext.CharacterId == playerId) { player = connectedAlivePlayerContext; return true; } } return false; } internal static int GetLevelBonus(Character character) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return 0; } if (TryGetZdo(character, out ZDO zdo)) { TrackPotentialBlockerZdo(zdo, character.IsBoss()); } float karma = GetKarma(((Component)character).transform.position); int num = (IsKarmaLevelEnabled() ? GetSectorLevelBonus(karma) : 0); if (IsEnforcer(character)) { num += GetStoredEnforcerLevelBonus(character); } return Mathf.Max(0, num); } internal static void ObservePotentialBlocker(Character character) { //IL_0083: 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_00a4: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer() || !TryGetZdo(character, out ZDO zdo)) { return; } bool num = zdo.GetBool("CreatureManager_KarmaEnforcer", false); bool flag = character.IsBoss(); if (!num && !flag) { return; } if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { TrackPotentialBlockerZdo(zdo, flag); return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || ReportedBlockerZdoIds.Contains(zdo.m_uid)) { return; } RegisterRpcs(); if (ZRoutedRpc.instance == null) { return; } try { ZPackage val = new ZPackage(); val.Write(zdo.m_uid); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_KarmaBlockerObservation", new object[1] { val }); ReportedBlockerZdoIds.Add(zdo.m_uid); } catch { } } private static void RPC_BlockerObservation(long sender, ZPackage package) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !IsKarmaSystemEnabled() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return; } try { ZDOID val = package.ReadZDOID(); ZNetPeer peer = ZNet.instance.GetPeer(sender); ZDO val2 = ((val != ZDOID.None) ? ZDOMan.instance.GetZDO(val) : null); GameObject val3 = ((val2 != null) ? ZNetScene.instance.GetPrefab(val2.GetPrefab()) : null); Character val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if (peer != null && peer.IsReady() && val2 != null && val2.GetOwner() == sender && !((Object)(object)val4 == (Object)null) && !val4.IsPlayer()) { bool num = val2.GetBool("CreatureManager_KarmaEnforcer", false); bool flag = val4.IsBoss(); if (num || flag) { TrackPotentialBlockerZdo(val2, flag); } } } catch { } } internal static void TrackPotentialBlockerZdo(ZDO zdo, bool isBoss) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (zdo != null && !((ZDOID)(ref zdo.m_uid)).IsNone()) { if (zdo.GetBool("CreatureManager_KarmaEnforcer", false)) { TrackedEnforcerZdoIds.Add(zdo.m_uid); TrackedBossZdoIds.Remove(zdo.m_uid); } else if (isBoss) { TrackedBossZdoIds.Add(zdo.m_uid); } } } internal static int GetAuthoritativeLevelBonus(ZDO zdo) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || zdo == null) { return 0; } float karma = GetKarma(zdo.GetPosition()); int num = (IsKarmaLevelEnabled() ? GetSectorLevelBonus(karma) : 0); if (zdo.GetBool("CreatureManager_KarmaEnforcer", false)) { num += Mathf.Max(0, zdo.GetInt("CreatureManager_KarmaEnforcerLevelBonus", Settings.Enforcer.LevelBonus)); } return Mathf.Max(0, num); } private static int GetStoredEnforcerLevelBonus(Character character) { if (TryGetRuntimeEnforcerSettings(character, out ResolvedEnforcerSettings settings)) { return Mathf.Max(0, settings.LevelBonus); } if (TryGetZdo(character, out ZDO zdo)) { return Mathf.Max(0, zdo.GetInt("CreatureManager_KarmaEnforcerLevelBonus", Settings.Enforcer.LevelBonus)); } return Mathf.Max(0, Settings.Enforcer.LevelBonus); } internal static bool TryGetEnforcerModifierDefinitions(Character character, out Dictionary modifiers, out bool fallbackBlocked) { modifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); fallbackBlocked = false; if (!IsKarmaSystemEnabled() || !IsEnforcer(character)) { return false; } if (TryGetRuntimeEnforcerSettings(character, out ResolvedEnforcerSettings settings)) { modifiers = settings.Modifiers; fallbackBlocked = settings.ModifiersCleared; return true; } modifiers = Settings.Enforcer.Modifiers; fallbackBlocked = Settings.Enforcer.ModifiersCleared; return true; } internal static bool TryGetDisplayName(Character character, out string displayName) { displayName = ""; if ((Object)(object)character == (Object)null) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); displayName = CreatureLocalization.LocalizeText(((zDO != null) ? zDO.GetString("CreatureManager_KarmaEnforcerName", "") : null) ?? ""); return displayName.Length > 0; } internal static string GetDebugLine(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) string key; SectorState bestState = GetBestState(position, out key); int num = (IsKarmaLevelEnabled() ? GetSectorLevelBonus(bestState.Karma) : 0); return $"Karma zone={key} neighborhood=3x3 karma={bestState.Karma:0.#} bonus={num} activeEnforcers={GetActiveEnforcerCountInSector(position)}/{GetMaximumEnforcersPerSector()} enforcerCooldown={GetRemainingEnforcerCooldown(position):0}s"; } internal static string GetMinimapStatus(Vector3 position) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled()) { return ""; } bool flag = IsKarmaLevelEnabled(); ConfigEntry showKarmaValueOnMinimap = CreatureManagerPlugin.ShowKarmaValueOnMinimap; bool flag2 = showKarmaValueOnMinimap != null && showKarmaValueOnMinimap.Value == CreatureManagerPlugin.Toggle.On; if (!flag && !flag2) { return ""; } float num; int num2; if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { RequestKarmaStatus(); if (!ClientKarmaStatusValid) { return ""; } num = ClientKarmaStatusValue; num2 = ClientKarmaStatusLevel; } else { num = GetKarma(position); num2 = GetSectorLevelBonus(num); } int num3 = Mathf.FloorToInt(Mathf.Max(0f, num)); if (!flag) { return CreatureLocalization.Format("cm_karma_value", $"Karma ({num3})", ("karma", num3.ToString(CultureInfo.InvariantCulture))); } if (flag2) { return CreatureLocalization.Format("cm_karma_level_value", $"Karma Lv. {num2} ({num3})", ("level", num2.ToString(CultureInfo.InvariantCulture)), ("karma", num3.ToString(CultureInfo.InvariantCulture))); } return CreatureLocalization.Format("cm_karma_level", $"Karma Lv. {num2}", ("level", num2.ToString(CultureInfo.InvariantCulture))); } internal static void SetDebugKarma(Vector3 position, float value) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; lock (Sync) { string[] array = GetSectorKeys(position).ToArray(); if (TryEnsureSectorStatesUnsafe((IReadOnlyCollection)(object)array)) { string[] array2 = array; foreach (string key in array2) { SectorState sectorState = Sectors[key]; sectorState.Karma = Mathf.Max(0f, value); sectorState.LastKarmaTime = time; } } } } internal static bool IsEnforcer(Character character) { if (TryGetRuntimeEnforcerSettings(character, out ResolvedEnforcerSettings _)) { return true; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return false; } return zDO.GetBool("CreatureManager_KarmaEnforcer", false); } internal static bool IsKarmaSummonedCreature(Character character) { if ((Object)(object)character == (Object)null) { return false; } if (!TryGetZdo(character, out ZDO zdo) || !zdo.GetBool("CreatureManager_KarmaEnforcerSummoned", false)) { return IsRuntimeSummonedCreature(character); } return true; } internal static KarmaAddResult TryAddBlamerKarma(Vector3 position, float amount) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!IsKarmaSystemEnabled() || amount <= 0f || float.IsNaN(amount) || float.IsInfinity(amount)) { return KarmaAddResult.Unavailable; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { return KarmaAddResult.Unavailable; } return AddKarma(position, amount); } internal static void RefreshStoredEnforcerLoot(Character character) { if (!((Object)(object)character == (Object)null) && !AppliedEnforcerLootIds.Contains(((Object)character).GetInstanceID()) && TryGetZdo(character, out ZDO zdo) && zdo.GetBool("CreatureManager_KarmaEnforcer", false)) { ApplyEnforcerLoot(character, DeserializeEnforcerLoot(zdo.GetString("CreatureManager_KarmaEnforcerLoot", ""))); } } internal static bool IsBossHudOnly(Character character) { if ((Object)(object)character == (Object)null) { return false; } if (TryGetRuntimeEnforcerSettings(character, out ResolvedEnforcerSettings settings)) { if (settings.BossHud) { return !settings.IsBoss; } return false; } if (TryGetZdo(character, out ZDO zdo) && zdo.GetBool("CreatureManager_KarmaEnforcerBossHud", false)) { return !zdo.GetBool("CreatureManager_KarmaEnforcerIsBoss", character.m_boss); } return false; } private static bool IsRuntimeSummonedCreature(Character character) { if ((Object)(object)character == (Object)null || !RuntimeSummonedCreatureIds.Contains(((Object)character).GetInstanceID())) { return false; } TryStoreRuntimeSummonedZdo(character); return true; } private static bool TryGetRuntimeEnforcerSettings(Character character, out ResolvedEnforcerSettings settings) { settings = null; if ((Object)(object)character == (Object)null || !RuntimeEnforcerSettings.TryGetValue(((Object)character).GetInstanceID(), out settings)) { return false; } TryStoreRuntimeEnforcerZdo(character, settings); return true; } private static void MarkRuntimeSummonedCreature(Character character) { if (!((Object)(object)character == (Object)null)) { RuntimeSummonedCreatureIds.Add(((Object)character).GetInstanceID()); TryStoreRuntimeSummonedZdo(character); } } private static void MarkRuntimeEnforcer(Character character, ResolvedEnforcerSettings settings, IReadOnlyList? loot) { if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); RuntimeEnforcerSettings[instanceID] = settings.Clone(); RuntimeEnforcerLoot[instanceID] = loot?.Select(CloneEnforcerLoot).ToList() ?? new List(); TryStoreRuntimeEnforcerZdo(character, settings); } } private static void TryStoreRuntimeSummonedZdo(Character character) { if (TryGetZdo(character, out ZDO zdo)) { zdo.Set("CreatureManager_KarmaEnforcerSummoned", true); } } private static void TryStoreRuntimeEnforcerZdo(Character character, ResolvedEnforcerSettings settings) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (TryGetZdo(character, out ZDO zdo)) { TrackedEnforcerZdoIds.Add(zdo.m_uid); TrackedBossZdoIds.Remove(zdo.m_uid); zdo.Set("CreatureManager_KarmaEnforcer", true); zdo.Set("CreatureManager_KarmaEnforcerSummoned", true); zdo.Set("CreatureManager_KarmaEnforcerLevelBonus", Mathf.Max(0, settings.LevelBonus)); zdo.Set("CreatureManager_KarmaEnforcerIsBoss", settings.IsBoss); zdo.Set("CreatureManager_KarmaEnforcerBossHud", settings.BossHud); if (RuntimeEnforcerLoot.TryGetValue(((Object)character).GetInstanceID(), out List value) && value.Count > 0 && string.IsNullOrEmpty(zdo.GetString("CreatureManager_KarmaEnforcerLoot", ""))) { StoreEnforcerLoot(zdo, value); } if (!string.IsNullOrWhiteSpace(character.m_name) && string.IsNullOrWhiteSpace(zdo.GetString("CreatureManager_KarmaEnforcerName", ""))) { zdo.Set("CreatureManager_KarmaEnforcerName", character.m_name); } } } private static bool TryGetZdo(Character character, out ZDO zdo) { zdo = null; ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } zdo = nview.GetZDO(); return zdo != null; } private static bool TrySummonForPlayer(ConnectedPlayerContext player, float now, out EnforcerSummonFailure failure, bool ignoreCooldown = false, bool ignoreChance = false, bool ignoreRequiredKarma = false, Vector3? regionPosition = null, HashSet? regionZoneKeys = null, ZDOID excludedCharacterId = default(ZDOID)) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) failure = EnforcerSummonFailure.None; if (player != null) { ZDOID characterId = player.CharacterId; if (!((ZDOID)(ref characterId)).IsNone() && IsFinite(player.Position)) { Vector3 position = player.Position; Vector3 position2 = regionPosition ?? position; Biome biome = GetBiome(position); bool flag = IsLikelyDungeonPosition(position); if (!TryGetEnforcerBiomeDefinition(biome, out EnforcerBiomeDefinition summon)) { failure = EnforcerSummonFailure.BiomeNotConfigured; return false; } string locationPrefab; string dungeonLocation = ((flag && TryGetDungeonLocationPrefabName(position, out locationPrefab)) ? locationPrefab : ""); List candidates = summon.GetCandidates(flag, dungeonLocation); if (!summon.Enabled) { failure = EnforcerSummonFailure.BiomeDisabled; return false; } if (candidates.Count == 0) { failure = EnforcerSummonFailure.NoCandidates; return false; } ResolvedEnforcerSettings resolvedEnforcerSettings = ResolvedEnforcerSettings.FromGlobal(Settings.Enforcer); EnforcerSummonFailure enforcerBlockerFailure = GetEnforcerBlockerFailure(position2, regionZoneKeys, excludedCharacterId); if (enforcerBlockerFailure != EnforcerSummonFailure.None) { failure = enforcerBlockerFailure; return false; } float karma; string key; lock (Sync) { karma = GetBestStateUnsafe(position2, out key).Karma; if (!ignoreCooldown && GetRemainingEnforcerCooldownUnsafe(position2, now, resolvedEnforcerSettings, regionZoneKeys) > 0f) { failure = EnforcerSummonFailure.Cooldown; return false; } } if (!ignoreChance && Random.Range(0f, 100f) >= Mathf.Clamp(resolvedEnforcerSettings.Chance, 0f, 100f)) { failure = EnforcerSummonFailure.ChanceRollFailed; return false; } if (!TrySelectEnforcerCandidate(candidates, resolvedEnforcerSettings, karma, ignoreRequiredKarma, out EnforcerCandidateDefinition selected, out ResolvedEnforcerSettings resolvedSettings)) { failure = EnforcerSummonFailure.NoEligibleCandidate; return false; } EnforcerSummonSet summon2 = selected.Summon; if (summon2.Boss.Length == 0) { failure = EnforcerSummonFailure.InvalidCandidate; return false; } if (!TryFindSummonPosition(position, resolvedSettings, out var position3)) { failure = EnforcerSummonFailure.NoSpawnPosition; CreatureManagerPlugin.Log.LogDebug((object)$"Karma Enforcer summon skipped: no spawn position near {position}."); return false; } lock (Sync) { if (!TryEnsureSectorStatesUnsafe((IReadOnlyCollection)(object)GetSectorKeys(position2).ToArray())) { failure = EnforcerSummonFailure.SectorStateCapacity; return false; } } if (!TrySpawnCreature(summon2.Boss, position3, position, markEnforcer: true, "$cm_suffix_enforcer", resolvedSettings, selected.Loot, out Character character)) { failure = EnforcerSummonFailure.SpawnFailed; return false; } foreach (EnforcerMinionDefinition minion in summon2.Minions) { for (int i = 0; i < minion.Count; i++) { Vector2 minionOffset = GetMinionOffset(flag); Vector3 position4 = position3 + new Vector3(minionOffset.x, 0f, minionOffset.y); TrySpawnCreature(minion.Prefab, position4, position, markEnforcer: false, "$cm_suffix_minion", resolvedSettings, null, out Character _); } } float num; lock (Sync) { num = ApplyEnforcerCostUnsafe(position2, now, resolvedSettings); } CreatureManagerPlugin.Log.LogInfo((object)$"Karma Enforcer summoned: {GetPrefabName(character)} zone={key} karma={karma:0.#}->{num:0.#} forced={ignoreCooldown || ignoreChance || ignoreRequiredKarma}"); BroadcastCenterQuote(EnforcerSpawnQuotes); return true; } } failure = EnforcerSummonFailure.KillerUnavailable; return false; } private static Vector2 GetMinionOffset(bool dungeonSummon) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Vector2 val = Random.insideUnitCircle; if (((Vector2)(ref val)).sqrMagnitude < 0.01f) { val = Vector2.right; } ((Vector2)(ref val)).Normalize(); float num = (dungeonSummon ? Random.Range(1f, 2f) : Random.Range(0f, 3f)); return val * num; } private static ResolvedEnforcerSettings ResolveEnforcerSettings(EnforcerOverrideSettings? candidateOverride, ResolvedEnforcerSettings baseline) { ResolvedEnforcerSettings resolvedEnforcerSettings = baseline.Clone(); ApplyCandidateOverride(resolvedEnforcerSettings, candidateOverride); return resolvedEnforcerSettings; } private static void ApplyCandidateOverride(ResolvedEnforcerSettings settings, EnforcerOverrideSettings? overrides) { if (overrides != null) { if (overrides.RequiredKarma.HasValue) { settings.RequiredKarma = Mathf.Max(0f, overrides.RequiredKarma.Value); } if (overrides.ConsumeKarma.HasValue) { settings.ConsumeKarma = Mathf.Max(0f, overrides.ConsumeKarma.Value); } if (overrides.LevelBonus.HasValue) { settings.LevelBonus = Mathf.Max(0, overrides.LevelBonus.Value); } if (overrides.ModifiersCleared) { settings.Modifiers.Clear(); settings.ModifiersCleared = true; } if (overrides.Modifiers != null) { MergeModifierOverrides(settings.Modifiers, overrides.Modifiers); } } } private static bool TrySelectEnforcerCandidate(List candidates, ResolvedEnforcerSettings biomeSettings, float karma, bool ignoreRequiredKarma, out EnforcerCandidateDefinition selected, out ResolvedEnforcerSettings resolvedSettings) { selected = new EnforcerCandidateDefinition(); resolvedSettings = biomeSettings; List<(EnforcerCandidateDefinition, ResolvedEnforcerSettings)> list = new List<(EnforcerCandidateDefinition, ResolvedEnforcerSettings)>(); foreach (EnforcerCandidateDefinition candidate in candidates) { if (!(candidate.Weight <= 0f) && candidate.Summon.Boss.Length != 0) { ResolvedEnforcerSettings resolvedEnforcerSettings = ResolveEnforcerSettings(candidate.Override, biomeSettings); if (ignoreRequiredKarma || karma >= resolvedEnforcerSettings.RequiredKarma) { list.Add((candidate, resolvedEnforcerSettings)); } } } if (list.Count == 0) { return false; } float num = list.Sum<(EnforcerCandidateDefinition, ResolvedEnforcerSettings)>(((EnforcerCandidateDefinition Candidate, ResolvedEnforcerSettings Settings) entry) => Mathf.Max(0f, entry.Candidate.Weight)); if (num <= 0f) { return false; } float num2 = Random.Range(0f, num); foreach (var item3 in list) { EnforcerCandidateDefinition item = item3.Item1; ResolvedEnforcerSettings item2 = item3.Item2; num2 -= Mathf.Max(0f, item.Weight); if (num2 <= 0f) { selected = item; resolvedSettings = item2; return true; } } (selected, resolvedSettings) = list[list.Count - 1]; return true; } private static bool TryGetEnforcerBiomeDefinition(Biome biome, out EnforcerBiomeDefinition summon) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) foreach (string biomeLookupKey in GetBiomeLookupKeys(biome)) { if (Settings.Enforcer.Biomes.TryGetValue(biomeLookupKey, out summon)) { return true; } } foreach (KeyValuePair biome3 in Settings.Enforcer.Biomes) { if (!IsGlobalBiomeKey(biome3.Key) && TryResolveBiomeName(biome3.Key, out var biome2) && (biome & biome2) != 0) { summon = biome3.Value; return true; } } if (Settings.Enforcer.Biomes.TryGetValue("global", out summon)) { return true; } summon = new EnforcerBiomeDefinition(); return false; } private unsafe static IEnumerable GetBiomeLookupKeys(Biome biome) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) yield return NormalizeBiomeName(((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString()); yield return NormalizeBiomeName(((int)biome).ToString(CultureInfo.InvariantCulture)); if (TryGetBiomeDisplayName(biome, out string displayName)) { yield return NormalizeBiomeName(displayName); yield return displayName.Trim(); } } private static bool IsGlobalBiomeKey(string key) { return string.Equals(key, "global", StringComparison.OrdinalIgnoreCase); } private static int GetActiveEnforcerCountInSector(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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) GetEnforcerBlockerState(position, out var activeEnforcers, out var _); return activeEnforcers; } private static void GetEnforcerBlockerState(Vector3 position, out int activeEnforcers, out bool hasNonEnforcerBoss, Character? excludedCharacter = null, HashSet? regionZoneKeys = null, ZDOID excludedCharacterId = default(ZDOID)) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) activeEnforcers = 0; hasNonEnforcerBoss = false; Vector2i zone = ZoneSystem.GetZone(position); HashSet hashSet = new HashSet(); foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || allCharacter == excludedCharacter || (excludedCharacterId != ZDOID.None && allCharacter.GetZDOID() == excludedCharacterId) || allCharacter.IsDead()) { continue; } ZDOID zDOID = allCharacter.GetZDOID(); if (!((ZDOID)(ref zDOID)).IsNone()) { hashSet.Add(zDOID); } bool flag = IsEnforcer(allCharacter); bool flag2 = !flag && allCharacter.IsBoss(); if (flag && !((ZDOID)(ref zDOID)).IsNone()) { TrackedEnforcerZdoIds.Add(zDOID); TrackedBossZdoIds.Remove(zDOID); } else if (flag2 && !((ZDOID)(ref zDOID)).IsNone()) { TrackedBossZdoIds.Add(zDOID); } if (IsInEnforcerCheckRegion(((Component)allCharacter).transform.position, zone, regionZoneKeys)) { if (flag) { activeEnforcers++; } else if (flag2) { hasNonEnforcerBoss = true; } } } CountTrackedBlockerZdos(zone, regionZoneKeys, excludedCharacterId, hashSet, ref activeEnforcers, ref hasNonEnforcerBoss); } private static void CountTrackedBlockerZdos(Vector2i centerZone, HashSet? regionZoneKeys, ZDOID excludedCharacterId, HashSet observedCharacterIds, ref int activeEnforcers, ref bool hasNonEnforcerBoss) { //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) //IL_006a: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: 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_011e: 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) if (ZDOMan.instance == null) { return; } foreach (ZDOID item in TrackedEnforcerZdoIds.ToList()) { if (item == excludedCharacterId || observedCharacterIds.Contains(item)) { continue; } ZDO zDO = ZDOMan.instance.GetZDO(item); if (!IsTrackedCharacterZdoAlive(zDO) || !zDO.GetBool("CreatureManager_KarmaEnforcer", false)) { TrackedEnforcerZdoIds.Remove(item); continue; } Vector3 position = zDO.GetPosition(); if (IsFinite(position) && IsInEnforcerCheckRegion(position, centerZone, regionZoneKeys)) { activeEnforcers++; } } if ((!ShouldBlockEnforcerWhileBossActive() && !ShouldBlockKarmaGainWhileBossActive()) | hasNonEnforcerBoss) { return; } foreach (ZDOID item2 in TrackedBossZdoIds.ToList()) { if (item2 == excludedCharacterId || observedCharacterIds.Contains(item2)) { continue; } ZDO zDO2 = ZDOMan.instance.GetZDO(item2); if (!IsTrackedCharacterZdoAlive(zDO2)) { TrackedBossZdoIds.Remove(item2); continue; } Vector3 position2 = zDO2.GetPosition(); if (!IsFinite(position2) || !IsInEnforcerCheckRegion(position2, centerZone, regionZoneKeys)) { continue; } hasNonEnforcerBoss = true; break; } } private static bool IsTrackedCharacterZdoAlive(ZDO zdo) { if (zdo == null || zdo.GetBool(ZDOVars.s_dead, false)) { return false; } float num = zdo.GetFloat(ZDOVars.s_health, float.PositiveInfinity); if (!float.IsNaN(num)) { if (!float.IsInfinity(num)) { return num > 0f; } return true; } return false; } private static bool IsInKarmaNeighborhood(Vector3 position, Vector2i centerZone) { //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_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Vector2i zone = ZoneSystem.GetZone(position); if (Math.Abs(zone.x - centerZone.x) <= 1) { return Math.Abs(zone.y - centerZone.y) <= 1; } return false; } private static bool IsInEnforcerCheckRegion(Vector3 position, Vector2i centerZone, HashSet? regionZoneKeys) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return regionZoneKeys?.Contains(GetSectorKey(position)) ?? IsInKarmaNeighborhood(position, centerZone); } private static EnforcerSummonFailure GetEnforcerBlockerFailure(Vector3 position, HashSet? regionZoneKeys, ZDOID excludedCharacterId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) GetEnforcerBlockerState(position, out var activeEnforcers, out var hasNonEnforcerBoss, null, regionZoneKeys, excludedCharacterId); if (activeEnforcers >= GetMaximumEnforcersPerSector()) { return EnforcerSummonFailure.ActiveEnforcerCap; } if (!(ShouldBlockEnforcerWhileBossActive() && hasNonEnforcerBoss)) { return EnforcerSummonFailure.None; } return EnforcerSummonFailure.ActiveBoss; } private static bool TryFindSummonPosition(Vector3 playerPosition, ResolvedEnforcerSettings settings, out Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0121: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (IsLikelyDungeonPosition(playerPosition)) { if (TryFindNearestComponentPosition(playerPosition, Settings.Enforcer.DungeonSpawnerSearchRadius, out position)) { return true; } if (TryFindNearestComponentPosition(playerPosition, Settings.Enforcer.DungeonSpawnerSearchRadius, out position)) { return true; } Vector2 val = Random.insideUnitCircle; if (((Vector2)(ref val)).sqrMagnitude < 0.01f) { val = Vector2.right; } ((Vector2)(ref val)).Normalize(); val *= Random.Range(6f, 12f); position = playerPosition + new Vector3(val.x, 0f, val.y); return true; } float num = Mathf.Max(2f, settings.SpawnRadiusMin); float num2 = Mathf.Max(num, settings.SpawnRadiusMax); Vector2 val2 = Random.insideUnitCircle; if (((Vector2)(ref val2)).sqrMagnitude < 0.01f) { val2 = Vector2.right; } ((Vector2)(ref val2)).Normalize(); float num3 = Random.Range(num, num2); Vector3 val3 = playerPosition + new Vector3(val2.x * num3, 0f, val2.y * num3); float y = val3.y; if ((Object)(object)ZoneSystem.instance != (Object)null) { ZoneSystem.instance.GetGroundHeight(val3 + Vector3.up * 100f, ref y); val3.y = y + 0.5f; } else if (WorldGenerator.instance != null) { val3.y = WorldGenerator.instance.GetHeight(val3.x, val3.z) + 0.5f; } position = val3; return true; } private static bool TryFindNearestComponentPosition(Vector3 origin, float radius, out Vector3 position) where T : Component { //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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) List cachedComponentPositions = GetCachedComponentPositions(ZoneSystem.GetZone(origin)); float num = Mathf.Max(0f, radius); Vector3 val = origin; bool flag = false; foreach (Vector3 item in cachedComponentPositions) { float num2 = Utils.DistanceXZ(item, origin); if (!(num2 > num)) { num = num2; val = item; flag = true; } } if (!flag) { position = origin; return false; } position = val; return true; } private static List GetCachedComponentPositions(Vector2i zone) where T : Component { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) string key = $"{typeof(T).FullName}:{zone.x},{zone.y}"; if (DungeonComponentPositionCache.TryGetValue(key, out List value)) { return value; } value = new List(); T[] array = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); foreach (T val in array) { if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy && IsSameZone(((Component)val).transform.position, zone)) { value.Add(((Component)val).transform.position); } } DungeonComponentPositionCache[key] = value; return value; } private static bool IsSameZone(Vector3 position, Vector2i zone) { //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_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector2i zone2 = ZoneSystem.GetZone(position); if (zone2.x == zone.x) { return zone2.y == zone.y; } return false; } private static bool TrySpawnCreature(string prefabName, Vector3 position, Vector3 targetPosition, bool markEnforcer, string nameSuffix, ResolvedEnforcerSettings settings, IReadOnlyList? loot, out Character character) { //IL_0076: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) character = null; if ((Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer summon skipped: missing prefab '" + prefabName + "'.")); return false; } if ((Object)(object)prefab.GetComponent() == (Object)null || CreaturePrefabRegistry.IsPlayerPrefab(prefab)) { CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer summon skipped: prefab '" + prefabName + "' is not a supported non-player Character.")); return false; } Vector3 val = targetPosition - position; val.y = 0f; Quaternion val2 = ((((Vector3)(ref val)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(((Vector3)(ref val)).normalized) : Quaternion.identity); GameObject val3 = null; try { val3 = Object.Instantiate(prefab, position, val2); character = val3.GetComponent(); if ((Object)(object)character == (Object)null || character.IsPlayer()) { CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer summon failed: instantiated prefab '" + prefabName + "' is not a supported non-player Character.")); CleanupFailedSummon(val3, character); character = null; return false; } MarkRuntimeSummonedCreature(character); ZNetView nview = character.m_nview; ZDO zdo = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); if (markEnforcer) { MarkSummonedEnforcer(character, zdo, nameSuffix, settings, loot); ApplyEnforcerLoot(character, loot); } else { MarkSummonedCreatureName(character, zdo, nameSuffix); } ApplyHuntPlayer(character); CreatureManagerCharacterLifecycle.ApplyLevelAndModifiers(character); return true; } catch (Exception ex) { CleanupFailedSummon(val3, character); character = null; CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer summon failed for '" + prefabName + "': " + ex.Message)); return false; } } private static void CleanupFailedSummon(GameObject? spawned, Character? character) { if ((Object)(object)character != (Object)null) { ForgetCharacter(character); } if ((Object)(object)spawned == (Object)null) { return; } try { ZNetView component = spawned.GetComponent(); if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { ZNetScene.instance.Destroy(spawned); } else { Object.Destroy((Object)(object)spawned); } } catch { Object.Destroy((Object)(object)spawned); } } private static void ApplyEnforcerLoot(Character character, IReadOnlyList? loot) { //IL_006c: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown if (!AppliedEnforcerLootIds.Add(((Object)character).GetInstanceID()) || loot == null || loot.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null) { return; } CharacterDrop val = ((Component)character).GetComponent() ?? ((Component)character).gameObject.AddComponent(); List list = new List(); if (val.m_drops != null) { foreach (Drop drop in val.m_drops) { if (drop != null) { list.Add(new Drop { m_prefab = drop.m_prefab, m_amountMin = drop.m_amountMin, m_amountMax = drop.m_amountMax, m_chance = drop.m_chance, m_onePerPlayer = drop.m_onePerPlayer, m_levelMultiplier = drop.m_levelMultiplier, m_dontScale = drop.m_dontScale }); } } } foreach (EnforcerLootDefinition item in loot) { GameObject prefab = ZNetScene.instance.GetPrefab(item.Prefab); if ((Object)(object)prefab == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer loot skipped: missing prefab '" + item.Prefab + "'.")); continue; } if ((Object)(object)prefab.GetComponent() == (Object)null) { CreatureManagerPlugin.Log.LogWarning((object)("Karma Enforcer loot skipped: prefab '" + item.Prefab + "' is not an item prefab.")); continue; } int num = item.Amount; while (num > 0) { int num2 = Math.Min(100, num); list.Add(new Drop { m_prefab = prefab, m_amountMin = num2, m_amountMax = num2, m_chance = 1f, m_onePerPlayer = false, m_levelMultiplier = false, m_dontScale = true }); num -= num2; } } val.m_drops = list; } private static void StoreEnforcerLoot(ZDO zdo, IReadOnlyList? loot) { string text = ((loot == null || loot.Count == 0) ? "" : string.Join("\n", loot.Select((EnforcerLootDefinition entry) => entry.Prefab + ":" + entry.Amount.ToString(CultureInfo.InvariantCulture)))); zdo.Set("CreatureManager_KarmaEnforcerLoot", text); } private static List DeserializeEnforcerLoot(string value) { List list = new List(); if (string.IsNullOrEmpty(value)) { return list; } if (value.Length > 8192) { CreatureManagerPlugin.Log.LogWarning((object)$"Stored Enforcer loot was ignored because it exceeds the {8192}-character safety limit."); return list; } int num = 0; string[] array = value.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (list.Count >= 32 || num >= 64) { break; } int num2 = text.LastIndexOf(':'); if (num2 > 0 && num2 < text.Length - 1 && num2 <= 128 && int.TryParse(text.Substring(num2 + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result > 0) { result = Math.Min(Math.Min(result, 64), 64 - num); if (result <= 0) { break; } string text2 = text.Substring(0, num2).Trim(); if (text2.Length != 0 && text2.Length <= 128) { list.Add(new EnforcerLootDefinition { Prefab = text2, Amount = result }); num += result; } } } return list; } private static EnforcerLootDefinition CloneEnforcerLoot(EnforcerLootDefinition source) { return new EnforcerLootDefinition { Prefab = source.Prefab, Amount = source.Amount }; } private static void MarkSummonedEnforcer(Character character, ZDO? zdo, string nameSuffix, ResolvedEnforcerSettings settings, IReadOnlyList? loot) { ResolvedEnforcerSettings resolvedEnforcerSettings = settings.Clone(); resolvedEnforcerSettings.IsBoss = character.m_boss || settings.IsBoss; character.m_boss = resolvedEnforcerSettings.IsBoss; MarkRuntimeEnforcer(character, resolvedEnforcerSettings, loot); MarkSummonedCreatureName(character, zdo, nameSuffix); } private static void MarkSummonedCreatureName(Character character, ZDO? zdo, string nameSuffix) { string prefabName = GetPrefabName(character); string text = BuildSummonedName(character, prefabName, nameSuffix); if (text.Length != 0) { character.m_name = text; if (zdo != null) { zdo.Set("CreatureManager_KarmaEnforcerName", text); } } } private static string BuildSummonedName(Character character, string prefab, string nameSuffix) { string text = nameSuffix.Trim(); string text2 = character.m_name.Trim(); if (text2.Length == 0) { text2 = prefab; } if (text.Length != 0) { return text2 + " " + text; } return text2; } private static void ApplyHuntPlayer(Character character) { BaseAI baseAI = character.GetBaseAI(); if (!((Object)(object)baseAI == (Object)null)) { baseAI.SetHuntPlayer(true); baseAI.SetAlerted(true); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val != null) { val.m_enableHuntPlayer = true; } } } private static bool IsLikelyDungeonPosition(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return position.y >= 4500f; } private static bool TryGetDungeonLocationPrefabName(Vector3 position, out string locationPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (TryGetZoneLocationPrefabName(position, out locationPrefab)) { return true; } try { if (TryGetLocationPrefabName(Location.GetZoneLocation(position), out locationPrefab)) { return true; } if (TryGetLocationPrefabName(Location.GetLocation(position, true), out locationPrefab)) { return true; } } catch { locationPrefab = ""; } return false; } private static bool TryGetLocationPrefabName(Location? location, out string prefabName) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; if ((Object)(object)location == (Object)null) { return false; } if (TryGetZoneLocationPrefabName(((Component)location).transform.position, out prefabName)) { return true; } prefabName = TrimCloneSuffix(((Object)((Component)location).gameObject).name).Trim(); return prefabName.Length > 0; } private static bool TryGetZoneLocationPrefabName(Vector3 position, out string prefabName) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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) prefabName = ""; if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } Vector2i zone = ZoneSystem.GetZone(position); if (!ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value)) { return false; } float num = Mathf.Max(value.m_location.m_exteriorRadius, value.m_location.m_interiorRadius); if (num > 0f && Utils.DistanceXZ(value.m_position, position) > num) { return false; } string locationSpawnContextPrefabName = GetLocationSpawnContextPrefabName(value.m_location); if (locationSpawnContextPrefabName.Length == 0) { return false; } prefabName = locationSpawnContextPrefabName; return true; } private static string GetLocationSpawnContextPrefabName(ZoneLocation? location) { string zoneLocationPrefabName = GetZoneLocationPrefabName(location); if (!Enumerable.Contains(zoneLocationPrefabName, ':') && TryGetExpandWorldDataCurrentLocationPrefabName(out string locationPrefab) && Enumerable.Contains(locationPrefab, ':') && string.Equals(GetExpandWorldDataBaseLocationName(locationPrefab), zoneLocationPrefabName, StringComparison.OrdinalIgnoreCase)) { return locationPrefab; } return zoneLocationPrefabName; } private static string GetZoneLocationPrefabName(ZoneLocation? location) { return (location?.m_prefabName ?? "").Trim(); } private static bool TryGetExpandWorldDataCurrentLocationPrefabName(out string locationPrefab) { locationPrefab = ""; if (!ExpandWorldDataCurrentLocationFieldResolved) { ExpandWorldDataCurrentLocationField = FindLoadedType("ExpandWorldData.LocationSpawning", "ExpandWorldData")?.GetField("CurrentLocation", BindingFlags.Static | BindingFlags.Public); ExpandWorldDataCurrentLocationFieldResolved = true; } try { object? obj = ExpandWorldDataCurrentLocationField?.GetValue(null); ZoneLocation val = (ZoneLocation)((obj is ZoneLocation) ? obj : null); if (val == null) { return false; } locationPrefab = GetZoneLocationPrefabName(val); return locationPrefab.Length > 0; } catch { return false; } } private static Type? FindLoadedType(string typeName, string assemblyName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { AssemblyName name; try { name = assembly.GetName(); } catch { continue; } if (string.Equals(name.Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { Type type = assembly.GetType(typeName, throwOnError: false); if (type != null) { return type; } } } return null; } internal static bool TryResolveBiomeName(string key, out Biome biome) { string text = (key ?? "").Trim(); if (text.Length == 0) { biome = (Biome)0; return false; } if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { biome = (Biome)result; return result != 0; } if (Enum.TryParse(text, ignoreCase: true, out biome)) { return true; } if (TryResolveExpandWorldDataBiome(text, out biome)) { return true; } biome = (Biome)0; return false; } private static bool TryResolveExpandWorldDataBiome(string key, out Biome biome) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected I4, but got Unknown EnsureExpandWorldDataBiomeMethods(); if (ExpandWorldDataTryGetBiomeMethod == null) { biome = (Biome)0; return false; } try { object[] array = new object[2] { key, (object)(Biome)0 }; object obj = ExpandWorldDataTryGetBiomeMethod.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is Biome val) { biome = (Biome)(int)val; return true; } } catch { } biome = (Biome)0; return false; } internal static bool TryGetBiomeDisplayName(Biome biome, out string displayName) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) displayName = ""; EnsureExpandWorldDataBiomeMethods(); if (ExpandWorldDataTryGetBiomeDisplayNameMethod == null) { return false; } try { object[] array = new object[2] { biome, null }; object obj = ExpandWorldDataTryGetBiomeDisplayNameMethod.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is string text && !string.IsNullOrWhiteSpace(text)) { displayName = text; return true; } } catch { } return false; } private static void EnsureExpandWorldDataBiomeMethods() { if (!ExpandWorldDataBiomeMethodsResolved) { Type type = FindLoadedType("ExpandWorldData.BiomeManager", "ExpandWorldData"); ExpandWorldDataTryGetBiomeMethod = type?.GetMethod("TryGetBiome", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(Biome).MakeByRefType() }, null); ExpandWorldDataTryGetBiomeDisplayNameMethod = type?.GetMethod("TryGetDisplayName", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Biome), typeof(string).MakeByRefType() }, null); ExpandWorldDataBiomeMethodsResolved = true; } } private static string GetExpandWorldDataBaseLocationName(string? locationPrefab) { string text = (locationPrefab ?? "").Trim(); int num = text.IndexOf(':'); if (num <= 0) { return text; } return text.Substring(0, num).Trim(); } private static string TrimCloneSuffix(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } private static float GetRemainingEnforcerCooldown(Vector3 position) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { return GetRemainingEnforcerCooldownUnsafe(position, Time.time); } } private static float GetRemainingEnforcerCooldownUnsafe(Vector3 position, float now) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return GetRemainingEnforcerCooldownUnsafe(position, now, ResolvedEnforcerSettings.FromGlobal(Settings.Enforcer)); } private static float GetRemainingEnforcerCooldownUnsafe(Vector3 position, float now, ResolvedEnforcerSettings settings, HashSet? regionZoneKeys = null) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (settings.Cooldown <= 0f) { return 0f; } float num = 0f; object obj; if (regionZoneKeys == null) { obj = GetSectorKeys(position); } else { obj = regionZoneKeys; } foreach (string item in (IEnumerable)obj) { if (Sectors.TryGetValue(item, out SectorState value)) { num = Mathf.Max(num, GetRemainingEnforcerCooldown(value, now, settings)); } } return num; } private static float GetRemainingEnforcerCooldown(SectorState state, float now, ResolvedEnforcerSettings settings) { if (settings.Cooldown <= 0f || state.LastEnforcerTime <= 0f) { return 0f; } return Mathf.Max(0f, settings.Cooldown - (now - state.LastEnforcerTime)); } private static float ApplyEnforcerCostUnsafe(Vector3 position, float now, ResolvedEnforcerSettings settings) { //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) float result = ReduceKarmaUnsafe(position, settings.ConsumeKarma, now); RefreshEnforcerCooldownUnsafe(position, now); return result; } private static void RefreshEnforcerCooldownUnsafe(Vector3 position, float now) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) foreach (string sectorKey in GetSectorKeys(position)) { if (Sectors.TryGetValue(sectorKey, out SectorState value)) { value.LastEnforcerTime = now; } } } private static float GetKillKarma(string prefab, bool boss, int level, bool dungeon) { float num = (Settings.Karma.Prefabs.TryGetValue(prefab, out var value) ? value : (boss ? Settings.Karma.BossKill : Settings.Karma.Kill)); float num2 = (boss ? Settings.Karma.BossKarmaScaling : Settings.Karma.KarmaScaling); num *= Mathf.Max(0f, 1f + Mathf.Max(0f, num2) * (float)(Mathf.Max(1, level) - 1)); if (dungeon) { num *= Mathf.Max(0f, Settings.Karma.DungeonMultiplier); } return num; } private static KarmaAddResult AddKarma(Vector3 position, float amount) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; bool flag = false; bool flag2 = false; bool flag3 = false; lock (Sync) { string[] array = GetSectorKeys(position).ToArray(); if (!TryEnsureSectorStatesUnsafe((IReadOnlyCollection)(object)array)) { return KarmaAddResult.Unavailable; } List thresholds = Settings.Karma.Thresholds; bool flag4 = thresholds.Count > 0; float num = (flag4 ? thresholds[thresholds.Count - 1] : 0f); bool flag5 = flag4; string key; int num2 = (IsKarmaLevelEnabled() ? GetSectorLevelBonus(GetBestStateUnsafe(position, out key).Karma) : 0); float num3 = 0f; string[] array2 = array; foreach (string key2 in array2) { SectorState sectorState = Sectors[key2]; ApplyDecayUnsafe(sectorState, time); float num4 = Mathf.Max(0f, sectorState.Karma); if (!flag4 || num4 < num) { flag5 = false; sectorState.Karma = Mathf.Max(0f, num4 + amount); if (flag4) { sectorState.Karma = Mathf.Min(num, sectorState.Karma); } if (sectorState.Karma > num4) { sectorState.LastKarmaTime = time; flag2 = true; } } num3 = Mathf.Max(num3, sectorState.Karma); } flag = IsKarmaLevelEnabled() && GetSectorLevelBonus(num3) > num2; flag3 = flag5; } if (flag) { BroadcastCenterQuote(KarmaLevelQuotes); } if (flag2) { return KarmaAddResult.Added; } if (!flag3) { return KarmaAddResult.Unavailable; } return KarmaAddResult.Saturated; } private static float ReduceKarmaUnsafe(Vector3 position, float amount, float now) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) float num = 0f; foreach (string sectorKey in GetSectorKeys(position)) { if (Sectors.TryGetValue(sectorKey, out SectorState value)) { ApplyDecayUnsafe(value, now); if (amount > 0f) { value.Karma = Mathf.Max(0f, value.Karma - amount); } num = Mathf.Max(num, value.Karma); } } return num; } private static void PruneSectorStates(float now) { if (now < NextSectorPruneTime) { return; } NextSectorPruneTime = now + 1f; float num = Mathf.Max(0f, Settings.Enforcer.Cooldown); lock (Sync) { int num2 = Math.Min(256, SectorPruneQueue.Count); for (int i = 0; i < num2; i++) { string text = SectorPruneQueue.Dequeue(); if (Sectors.TryGetValue(text, out SectorState value)) { ApplyDecayUnsafe(value, now); bool flag = value.LastEnforcerTime <= 0f || num <= 0f || now - value.LastEnforcerTime >= num; if (value.Karma <= 0f && flag) { Sectors.Remove(text); } else { SectorPruneQueue.Enqueue(text); } } } } } private static float GetKarma(Vector3 position) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { string key; return GetBestStateUnsafe(position, out key).Karma; } } private static SectorState GetBestState(Vector3 position, out string key) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { return GetBestStateUnsafe(position, out key).Clone(); } } private static SectorState GetBestStateUnsafe(Vector3 position, out string key) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; key = ""; SectorState sectorState = null; bool flag = false; foreach (string sectorKey in GetSectorKeys(position)) { if (Sectors.TryGetValue(sectorKey, out SectorState value)) { ApplyDecayUnsafe(value, time); if (!flag || value.Karma > sectorState.Karma) { key = sectorKey; sectorState = value; flag = true; } } } if (flag) { return sectorState; } key = GetSectorKey(position); return EmptySectorState; } private static void ApplyDecayUnsafe(SectorState state, float now) { if (!IsKarmaSystemEnabled()) { return; } if (state.Karma <= 0f) { state.Karma = 0f; state.LastKarmaTime = now; return; } float decayPerMinute = Settings.Karma.DecayPerMinute; if (decayPerMinute <= 0f) { return; } if (state.LastKarmaTime <= 0f) { state.LastKarmaTime = now; return; } float num = Mathf.Max(0f, Settings.Karma.DecayAfterMinutes) * 60f; float num2 = Mathf.Max(0f, now - state.LastKarmaTime); if (!(num2 <= num)) { float num3 = (num2 - num) / 60f * decayPerMinute; state.Karma = Mathf.Max(0f, state.Karma - num3); state.LastKarmaTime = ((state.Karma <= 0f) ? now : (now - num)); } } private static bool TryEnsureSectorStatesUnsafe(IReadOnlyCollection keys) { string[] array = new HashSet(keys, StringComparer.Ordinal).Where((string key) => !Sectors.ContainsKey(key)).ToArray(); if (Sectors.Count + array.Length > 32768) { float unscaledTime = Time.unscaledTime; if (unscaledTime >= NextSectorCapacityWarningTime) { NextSectorCapacityWarningTime = unscaledTime + 30f; CreatureManagerPlugin.Log.LogWarning((object)$"Karma sector state limit ({32768}) reached; preserving active Karma/cooldowns and rejecting new regional state until the bounded background pruner reclaims capacity."); } return false; } string[] array2 = array; foreach (string text in array2) { SectorState value = new SectorState(); Sectors[text] = value; SectorPruneQueue.Enqueue(text); } return true; } private static int GetSectorLevelBonus(float karma) { List thresholds = Settings.Karma.Thresholds; int num = 0; for (int i = 0; i < thresholds.Count; i++) { if (karma >= thresholds[i]) { num++; } } return Mathf.Max(0, num); } private static string GetSectorKey(Vector3 position) { //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) return GetSectorKey(ZoneSystem.GetZone(position)); } private static string GetSectorKey(Vector2i zone) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) return $"{zone.x},{zone.y}"; } private static IEnumerable GetSectorKeys(Vector3 position) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Vector2i zone = ZoneSystem.GetZone(position); yield return GetSectorKey(zone); int radius = 1; for (int x = -radius; x <= radius; x++) { for (int y = -radius; y <= radius; y++) { if (x != 0 || y != 0) { yield return GetSectorKey(new Vector2i(zone.x + x, zone.y + y)); } } } } private static Biome GetBiome(Vector3 position) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (WorldGenerator.instance == null) { return (Biome)0; } try { return WorldGenerator.instance.GetBiome(position); } catch { return (Biome)0; } } private static string GetPrefabName(Character character) { return Utils.GetPrefabName(((Component)character).gameObject); } private static void BroadcastCenterQuote(IReadOnlyList quotes) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (quotes.Count == 0) { return; } string text = quotes[Random.Range(0, quotes.Count)]; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { try { ZPackage val = new ZPackage(); val.Write(text); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_KarmaCenterQuote", new object[1] { val }); return; } catch { } } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { ((Character)allPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } } private static void RPC_CenterQuote(long sender, ZPackage package) { if (ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID()) { return; } try { string text = package.ReadString(); if (text.Length > 0 && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } catch { } } private static KarmaSettings ReadSettings(string yaml, string source) { KarmaSettings karmaSettings = KarmaSettings.Default(); YamlStream yamlStream = new YamlStream(); using StringReader input = new StringReader(yaml); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return karmaSettings; } if (yamlStream.Documents.Count == 1 && yamlStream.Documents[0].RootNode is YamlSequenceNode yamlSequenceNode && yamlSequenceNode.Children.Count == 0) { return karmaSettings; } if (yamlStream.Documents.Count != 1 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { throw new FormatException("Karma YAML from " + source + " must contain one top-level mapping."); } CreatureYaml.ValidateUniqueMappingKeys(yamlMappingNode, source, "root"); if (TryGetNode(yamlMappingNode, "karma", out YamlNode value)) { if (!(value is YamlMappingNode yamlMappingNode2)) { throw new FormatException("Karma YAML from " + source + " karma must be a mapping."); } ValidateKnownFields(yamlMappingNode2, KarmaFields, source, "karma"); karmaSettings.Karma.Thresholds = (from result in ReadFloatSequence(yamlMappingNode2, "thresholds", karmaSettings.Karma.Thresholds, source, "karma.thresholds") where result >= 0f orderby result select result).ToList(); if (TryReadExactFloatTuple(yamlMappingNode2, "decay", 3, source, "karma.decay", out List values)) { karmaSettings.Karma.DecayAfterMinutes = Mathf.Max(0f, values[0]); karmaSettings.Karma.DecayPerMinute = Mathf.Max(0f, values[1]); karmaSettings.Karma.PlayerDeathClearKarma = Mathf.Max(0f, values[2]); } ApplyKarmaGainTuple(yamlMappingNode2, karmaSettings.Karma, source); if (TryGetNode(yamlMappingNode2, "prefabs", out YamlNode value2)) { if (!(value2 is YamlMappingNode map)) { throw new FormatException("Karma YAML from " + source + " karma.prefabs must be a mapping."); } karmaSettings.Karma.Prefabs = ReadFloatMap(map, source, "karma.prefabs"); } } YamlNode value4; if (TryGetExactNode(yamlMappingNode, "Enforcer", out YamlNode value3)) { YamlMappingNode obj = (value3 as YamlMappingNode) ?? throw new FormatException("Karma YAML from " + source + " Enforcer must be a mapping."); ValidateKnownFields(obj, EnforcerFields, source, "Enforcer"); ApplyEnforcerSettingsTuple(obj, karmaSettings.Enforcer, source); ApplyEnforcerChecksTuple(obj, karmaSettings.Enforcer, source, "Enforcer"); if (TryReadModifierBlock(obj, source, "Enforcer.modifiers", out Dictionary modifiers, out bool cleared)) { karmaSettings.Enforcer.Modifiers = modifiers ?? new Dictionary(StringComparer.OrdinalIgnoreCase); karmaSettings.Enforcer.ModifiersCleared = cleared; } } else if (TryGetNode(yamlMappingNode, "enforcer", out value4)) { throw new FormatException("Karma YAML from " + source + " uses unsupported top-level block 'enforcer'. Use 'Enforcer'."); } karmaSettings.Enforcer.Biomes = ReadEnforcerBiomes(yamlMappingNode, source); return karmaSettings; } private static void ApplyKarmaGainTuple(YamlMappingNode map, KarmaGainSettings settings, string source) { if (TryReadExactFloatTuple(map, "gain", 5, source, "karma.gain", out List values)) { settings.Kill = Mathf.Max(0f, values[0]); settings.BossKill = Mathf.Max(0f, values[1]); settings.KarmaScaling = Mathf.Max(0f, values[2]); settings.BossKarmaScaling = Mathf.Max(0f, values[3]); settings.DungeonMultiplier = Mathf.Max(0f, values[4]); } } private static bool TryGetExactNode(YamlMappingNode node, string field, out YamlNode value) { foreach (KeyValuePair child in node.Children) { if (string.Equals(GetScalar(child.Key), field, StringComparison.Ordinal)) { value = child.Value; return true; } } value = new YamlScalarNode(""); return false; } private static void ApplyEnforcerSettingsTuple(YamlMappingNode map, EnforcerSettings settings, string source) { if (TryReadExactFloatTuple(map, "settings", 3, source, "Enforcer.settings", out List values)) { settings.RequiredKarma = Mathf.Max(0f, values[0]); settings.ConsumeKarma = Mathf.Max(0f, values[1]); settings.LevelBonus = Mathf.Max(0, Mathf.RoundToInt(values[2])); } } private static void ApplyEnforcerChecksTuple(YamlMappingNode map, EnforcerSettings settings, string source, string label) { if (TryGetNode(map, "checks", out YamlNode value)) { if (!TryReadStringSequence(value, out List values)) { throw new FormatException("Karma YAML from " + source + " " + label + ".checks must be a YAML list of non-empty scalar values."); } if (values.Count != 4) { throw new FormatException("Karma YAML from " + source + " " + label + ".checks must contain exactly 4 values: [chance, cooldown, checkInterval, spawnRadius]."); } if (!TryParseFiniteFloat(values[0], out var value2) || !TryParseFiniteFloat(values[1], out var value3) || !TryParseFiniteFloat(values[2], out var value4)) { throw new FormatException("Karma YAML from " + source + " " + label + ".checks first three values must be finite numbers."); } if (TryParseFloatRange(values[3], source, label + ".checks[4]", out var min, out var max)) { settings.Chance = Mathf.Clamp(value2, 0f, 100f); settings.Cooldown = Mathf.Max(0f, value3); settings.CheckInterval = Mathf.Max(0f, value4); settings.SpawnRadiusMin = min; settings.SpawnRadiusMax = max; } } } private static EnforcerOverrideSettings ReadEnforcerSettingsOverride(YamlMappingNode map, string source, string label) { EnforcerOverrideSettings enforcerOverrideSettings = new EnforcerOverrideSettings(); if (!TryReadExactFloatTuple(map, "settings", 3, source, label + ".settings", out List values)) { return enforcerOverrideSettings; } enforcerOverrideSettings.RequiredKarma = Mathf.Max(0f, values[0]); enforcerOverrideSettings.ConsumeKarma = Mathf.Max(0f, values[1]); enforcerOverrideSettings.LevelBonus = Mathf.Max(0, Mathf.RoundToInt(values[2])); return enforcerOverrideSettings; } private static bool TryReadExactFloatTuple(YamlMappingNode map, string field, int expectedCount, string source, string label, out List values) { values = new List(); if (!TryGetNode(map, field, out YamlNode value)) { return false; } if (!TryReadStringSequence(value, out List values2)) { throw new FormatException("Karma YAML from " + source + " " + label + " must be a YAML list of non-empty scalar values."); } if (values2.Count != expectedCount) { throw new FormatException($"Karma YAML from {source} {label} must contain exactly {expectedCount} values."); } foreach (string item in values2) { if (!TryParseFiniteFloat(item, out var value2)) { throw new FormatException("Karma YAML from " + source + " " + label + " has invalid number '" + item + "'."); } values.Add(value2); } return true; } private static void ValidateKnownFields(YamlMappingNode map, HashSet allowedFields, string source, string label) { foreach (KeyValuePair child in map.Children) { string scalar = GetScalar(child.Key); if (!allowedFields.Contains(scalar)) { throw new FormatException("Karma YAML from " + source + " " + label + " has unknown field '" + scalar + "'."); } } } private static bool ReadBool(YamlMappingNode node, string field, bool fallback, string source, string label) { if (!TryGetNode(node, field, out YamlNode value)) { return fallback; } if (bool.TryParse(GetScalar(value), out var result)) { return result; } throw new FormatException("Karma YAML from " + source + " " + label + "." + field + " must be true or false."); } private static float ReadFloat(YamlMappingNode node, string field, float fallback) { if (!TryGetNode(node, field, out YamlNode value)) { return fallback; } if (!TryParseFiniteFloat(GetScalar(value), out var value2)) { throw new FormatException("Karma YAML field '" + field + "' must be a finite number."); } return value2; } private static bool TryParseFloatRange(string text, string source, string label, out float min, out float max) { min = 0f; max = 0f; string[] array = text.Split(new char[1] { '~' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length != 2 || !TryParseFiniteFloat(array[0].Trim(), out min) || !TryParseFiniteFloat(array[1].Trim(), out max)) { throw new FormatException("Karma YAML from " + source + " " + label + " must use finite 'min~max' values, for example '24~48'."); } min = Mathf.Max(0f, min); max = Mathf.Max(min, max); return true; } private static List ReadFloatSequence(YamlMappingNode node, string field, List fallback, string source, string label) { if (!TryGetNode(node, field, out YamlNode value)) { return fallback.ToList(); } if (!TryReadStringSequence(value, out List values)) { throw new FormatException("Karma YAML from " + source + " " + label + " must be a YAML list of non-empty scalar values."); } List list = new List(values.Count); foreach (string item in values) { if (!TryParseFiniteFloat(item, out var value2)) { throw new FormatException("Karma YAML from " + source + " " + label + " has invalid number '" + item + "'."); } list.Add(value2); } return list; } private static Dictionary ReadFloatMap(YamlMappingNode map, string source, string label) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair child in map.Children) { string scalar = GetScalar(child.Key); if (scalar.Length > 0 && TryParseFiniteFloat(GetScalar(child.Value), out var value)) { dictionary[scalar] = value; continue; } throw new FormatException("Karma YAML from " + source + " " + label + "." + scalar + " must have a non-empty key and finite number value."); } return dictionary; } private static bool TryReadModifierBlock(YamlMappingNode owner, string source, string label, out Dictionary? modifiers, out bool cleared) { modifiers = null; cleared = false; if (!TryGetNode(owner, "modifiers", out YamlNode value)) { return false; } if (!CreatureYaml.TryReadModifierBlock(value, source, label, CreatureYaml.ModifierYamlContext.Karma, out modifiers, out cleared)) { throw new FormatException("Karma YAML from " + source + " " + label + " is invalid."); } return true; } private static Dictionary CloneModifiers(Dictionary source) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in source) { dictionary[item.Key] = item.Value.Clone(); } return dictionary; } private static void MergeModifierOverrides(Dictionary target, Dictionary overrides) { foreach (KeyValuePair @override in overrides) { if (!target.TryGetValue(@override.Key, out ModifierDefinition value)) { target[@override.Key] = @override.Value.Clone(); } else { value.OverlayFrom(@override.Value); } } } private static Dictionary ReadEnforcerBiomes(YamlMappingNode root, string source) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair child in root.Children) { string scalar = GetScalar(child.Key); if (!string.Equals(scalar, "karma", StringComparison.OrdinalIgnoreCase) && !string.Equals(scalar, "enforcer", StringComparison.OrdinalIgnoreCase)) { if (scalar.Length == 0 || !(child.Value is YamlMappingNode map)) { throw new FormatException("Karma YAML from " + source + " top-level biome blocks must be named mappings."); } EnforcerBiomeDefinition enforcerBiomeDefinition = ReadEnforcerBiome(map, source, scalar); if (enforcerBiomeDefinition.HasContent) { RegisterEnforcerBiomeDefinition(dictionary, scalar, enforcerBiomeDefinition); } } } return dictionary; } private unsafe static void RegisterEnforcerBiomeDefinition(Dictionary biomes, string key, EnforcerBiomeDefinition definition) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected I4, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) string text = (key ?? "").Trim(); if (text.Length == 0) { return; } biomes[text] = definition; biomes[NormalizeBiomeName(text)] = definition; if (TryResolveBiomeName(text, out var biome)) { biomes[NormalizeBiomeName(((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString())] = definition; biomes[NormalizeBiomeName(((int)biome).ToString(CultureInfo.InvariantCulture))] = definition; if (TryGetBiomeDisplayName(biome, out string displayName)) { biomes[displayName.Trim()] = definition; biomes[NormalizeBiomeName(displayName)] = definition; } } } private static EnforcerBiomeDefinition ReadEnforcerBiome(YamlMappingNode map, string source, string biome) { EnforcerBiomeDefinition enforcerBiomeDefinition = new EnforcerBiomeDefinition(); ValidateKnownFields(map, EnforcerBiomeFields, source, biome); enforcerBiomeDefinition.Enabled = ReadBool(map, "enabled", fallback: true, source, biome); if (TryGetNode(map, "enforcers", out YamlNode value)) { enforcerBiomeDefinition.Outdoor = ReadEnforcerCandidates(value, source, biome + ".enforcers"); } if (TryGetNode(map, "dungeonEnforcers", out YamlNode value2)) { ReadDungeonEnforcerCandidates(enforcerBiomeDefinition, value2, source, biome + ".dungeonEnforcers"); } return enforcerBiomeDefinition; } private static void ReadDungeonEnforcerCandidates(EnforcerBiomeDefinition definition, YamlNode node, string source, string label) { if (!(node is YamlSequenceNode)) { throw new FormatException("Karma YAML from " + source + " " + label + " must be a list. Use location: LocationPrefab on individual entries for location-specific dungeon summons."); } AddDungeonEnforcerCandidates(definition, ReadEnforcerCandidates(node, source, label)); } private static void AddDungeonEnforcerCandidates(EnforcerBiomeDefinition definition, List candidates) { foreach (IGrouping item in candidates.GroupBy((EnforcerCandidateDefinition candidate) => (candidate.Location ?? "").Trim(), StringComparer.OrdinalIgnoreCase)) { List list = item.ToList(); foreach (EnforcerCandidateDefinition item2 in list) { item2.Location = ""; } if (item.Key.Length == 0) { definition.Dungeon.AddRange(list); } else { AddDungeonLocationCandidates(definition, item.Key, list); } } } private static void AddDungeonLocationCandidates(EnforcerBiomeDefinition definition, string location, List candidates) { if (!definition.DungeonByLocation.TryGetValue(location, out List value)) { definition.DungeonByLocation[location] = candidates; } else { value.AddRange(candidates); } } private static List ReadEnforcerCandidates(YamlNode node, string source, string label) { List list = new List(); if (!(node is YamlSequenceNode yamlSequenceNode)) { throw new FormatException("Karma YAML from " + source + " " + label + " must be a list."); } int num = 0; foreach (YamlNode child in yamlSequenceNode.Children) { num++; EnforcerCandidateDefinition enforcerCandidateDefinition = ReadEnforcerCandidate(child, source, $"{label}[{num}]"); if (enforcerCandidateDefinition.Summon.Boss.Length > 0) { list.Add(enforcerCandidateDefinition); } } return list; } private static EnforcerCandidateDefinition ReadEnforcerCandidate(YamlNode node, string source, string label) { if (!(node is YamlMappingNode yamlMappingNode)) { throw new FormatException("Karma YAML from " + source + " " + label + " must be a mapping with summon: [BossPrefab, MinionPrefab[:count], ...]."); } ValidateKnownFields(yamlMappingNode, EnforcerCandidateFields, source, label); if (!TryGetNode(yamlMappingNode, "summon", out YamlNode value)) { throw new FormatException("Karma YAML from " + source + " " + label + " must include summon: [BossPrefab, MinionPrefab[:count], ...]."); } if (!TryReadStringSequence(value, out List values)) { throw new FormatException("Karma YAML from " + source + " " + label + ".summon must be a YAML list of non-empty prefab values."); } if (values.Count == 0) { throw new FormatException("Karma YAML from " + source + " " + label + ".summon must include a boss prefab."); } string location = ""; if (TryGetNode(yamlMappingNode, "location", out YamlNode value2)) { if (!(value2 is YamlScalarNode)) { throw new FormatException("Karma YAML from " + source + " " + label + ".location must be a scalar prefab name."); } location = GetScalar(value2); } return new EnforcerCandidateDefinition { Summon = ReadSummonSet(values, source, label), Weight = Mathf.Max(0f, ReadFloat(yamlMappingNode, "weight", 1f)), Loot = ReadEnforcerLoot(yamlMappingNode, source, label), Location = location, Override = ReadEnforcerCandidateOverride(yamlMappingNode, source, label) }; } private static List ReadEnforcerLoot(YamlMappingNode map, string source, string label) { List list = new List(); if (!TryGetNode(map, "loot", out YamlNode value)) { return list; } if (!TryReadStringSequence(value, out List values)) { throw new FormatException("Karma YAML from " + source + " " + label + ".loot must be a YAML list of itemPrefab:amount values without a space after the colon."); } if (values.Count > 32) { throw new FormatException($"Karma YAML from {source} {label}.loot has {values.Count} entries; the maximum is {32}."); } long num = 0L; foreach (string item in values) { string text = item.Trim(); int num2 = text.LastIndexOf(':'); if (num2 <= 0 || num2 >= text.Length - 1 || !int.TryParse(text.Substring(num2 + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { throw new FormatException("Karma YAML from " + source + " " + label + ".loot has invalid value '" + item + "'. Use itemPrefab:amount with a positive integer amount."); } string text2 = text.Substring(0, num2).Trim(); if (text2.Length == 0 || text2.Length > 128) { throw new FormatException($"Karma YAML from {source} {label}.loot item prefab in '{item}' must contain from 1 to {128} characters."); } if (result > 64) { throw new FormatException($"Karma YAML from {source} {label}.loot value '{item}' exceeds the per-item maximum of {64}."); } num += result; if (num > 64) { throw new FormatException($"Karma YAML from {source} {label}.loot grants {num} total items; the maximum is {64}."); } list.Add(new EnforcerLootDefinition { Prefab = text2, Amount = result }); } return list; } private static EnforcerOverrideSettings ReadEnforcerCandidateOverride(YamlMappingNode map, string source, string label) { EnforcerOverrideSettings enforcerOverrideSettings = ReadEnforcerSettingsOverride(map, source, label); if (TryReadModifierBlock(map, source, label + ".modifiers", out Dictionary modifiers, out bool cleared)) { enforcerOverrideSettings.Modifiers = modifiers; enforcerOverrideSettings.ModifiersCleared = cleared; } return enforcerOverrideSettings; } private static EnforcerSummonSet ReadSummonSet(List values, string source, string label) { if (values.Count == 0) { return new EnforcerSummonSet(); } string text = values[0].Trim(); if (text.Length == 0 || text.Length > 128) { throw new FormatException($"Karma YAML from {source} {label}.summon boss prefab must contain from 1 to {128} characters."); } if (values.Count - 1 > 16) { throw new FormatException($"Karma YAML from {source} {label}.summon has {values.Count - 1} minion entries; the maximum is {16}."); } List list = new List(); int num = 0; foreach (string item in values.Skip(1)) { EnforcerMinionDefinition enforcerMinionDefinition = ParseMinionDefinition(item, source, label); if (enforcerMinionDefinition.Prefab.Length != 0 && enforcerMinionDefinition.Count > 0) { num += enforcerMinionDefinition.Count; if (num > 16) { throw new FormatException($"Karma YAML from {source} {label}.summon requests {num} total minions; the maximum is {16}."); } list.Add(enforcerMinionDefinition); } } return new EnforcerSummonSet { Boss = text, Minions = list }; } private static EnforcerMinionDefinition ParseMinionDefinition(string value, string source, string label) { string text = value.Trim(); if (text.Length > 144) { throw new FormatException("Karma YAML from " + source + " " + label + ".summon minion entry exceeds the supported prefab/count length."); } int count = 1; int num = text.LastIndexOf(':'); if (num > 0 && num < text.Length - 1 && int.TryParse(text.Substring(num + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { if (result < 1) { throw new FormatException("Karma YAML from " + source + " " + label + ".summon minion '" + value + "' must use a count of 1 or greater."); } if (result > 16) { throw new FormatException($"Karma YAML from {source} {label}.summon minion '{value}' exceeds the per-entry maximum of {16}."); } text = text.Substring(0, num).Trim(); count = result; } if (text.Length == 0 || text.Length > 128) { throw new FormatException($"Karma YAML from {source} {label}.summon minion prefab must contain from 1 to {128} characters."); } return new EnforcerMinionDefinition { Prefab = text, Count = count }; } private static bool TryReadStringSequence(YamlNode node, out List values) { values = new List(); if (!(node is YamlSequenceNode yamlSequenceNode)) { return false; } foreach (YamlNode child in yamlSequenceNode.Children) { if (!(child is YamlScalarNode)) { values.Clear(); return false; } string scalar = GetScalar(child); if (scalar.Length == 0) { values.Clear(); return false; } values.Add(scalar); } return true; } private static bool TryGetNode(YamlMappingNode node, string field, out YamlNode value) { foreach (KeyValuePair child in node.Children) { if (string.Equals(GetScalar(child.Key), field, StringComparison.OrdinalIgnoreCase)) { value = child.Value; return true; } } value = new YamlScalarNode(""); return false; } private static string GetScalar(YamlNode node) { if (!(node is YamlScalarNode yamlScalarNode)) { return ""; } return (yamlScalarNode.Value ?? "").Trim(); } private static bool TryParseFiniteFloat(string text, out float value) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static string NormalizeBiomeName(string value) { return new string((value ?? "").Where((char character) => !char.IsWhiteSpace(character) && character != '_' && character != '-').Select(char.ToLowerInvariant).ToArray()); } } internal static class CreatureLevelManager { private sealed class PendingKarmaBonusRequest { internal int RequestId; internal float StartedAt = -1f; internal float NextSendAt; internal int TimeoutCount; } private enum LevelRuleScope { Full, BaselineOnly } private enum ModifierApplicationMode { Block, Roll, Keep } private readonly struct SpawnPolicy { internal readonly bool RollLevel; internal readonly LevelRuleScope? GeneralScope; internal readonly LevelRuleScope? HealthScope; internal readonly bool AllowHealthDistanceScaling; internal readonly ModifierApplicationMode ModifierMode; internal readonly LevelRuleScope ModifierScope; internal readonly bool AllowModifierDistanceScaling; internal bool PreserveExistingRolls => ModifierMode == ModifierApplicationMode.Keep; internal SpawnPolicy(bool rollLevel, LevelRuleScope? generalScope, LevelRuleScope? healthScope, bool allowHealthDistanceScaling, ModifierApplicationMode modifierMode, LevelRuleScope modifierScope, bool allowModifierDistanceScaling) { RollLevel = rollLevel; GeneralScope = generalScope; HealthScope = healthScope; AllowHealthDistanceScaling = allowHealthDistanceScaling; ModifierMode = modifierMode; ModifierScope = modifierScope; AllowModifierDistanceScaling = allowModifierDistanceScaling; } } private readonly struct LevelRuleContext { internal string PrefabName { get; } internal Biome Biome { get; } internal float Distance { get; } internal bool IsBoss { get; } internal bool IsEnforcer { get; } private LevelRuleContext(string prefabName, Biome biome, float distance, bool isBoss, bool isEnforcer) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) PrefabName = prefabName; Biome = biome; Distance = distance; IsBoss = isBoss; IsEnforcer = isEnforcer; } internal static LevelRuleContext From(Character character) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) return new LevelRuleContext(GetPrefabName(((Component)character).gameObject), GetBiome(((Component)character).transform.position), GetHorizontalDistance(((Component)character).transform.position), character.IsBoss(), CreatureKarmaManager.IsEnforcer(character)); } } private readonly struct LevelRuleCandidate { internal LevelDefinition Definition { get; } private int Specificity { get; } private int Index { get; } internal LevelRuleCandidate(LevelDefinition definition, int specificity, int index) { Definition = definition; Specificity = specificity; Index = index; } internal int CompareTo(LevelRuleCandidate other) { int num = Specificity.CompareTo(other.Specificity); if (num == 0) { return Index.CompareTo(other.Index); } return num; } } private sealed class LevelRuleSearch { internal LevelRuleContext Context { get; } internal List Candidates { get; } internal LevelRuleSearch(LevelRuleContext context, List candidates) { Context = context; Candidates = candidates; } } private const string AppliedKey = "CreatureManager_LevelApplied"; private const string ProcessingCompleteKey = "CreatureManager_LevelProcessingComplete"; private const string DesiredLevelKey = "CreatureManager_DesiredLevel"; private const string DesiredLevelSourceKey = "CreatureManager_DesiredLevelSource"; private const string HealthAppliedKey = "CreatureManager_LevelHealthApplied"; private const string HealthMultiplierKey = "CreatureManager_LevelHealthMultiplier"; private const string DamageAppliedKey = "CreatureManager_LevelDamageApplied"; private const string DamageMultiplierKey = "CreatureManager_LevelDamageMultiplier"; private const string KarmaBonusRequestRpc = "CreatureManager_LevelKarmaBonusRequest"; private const string KarmaBonusResponseRpc = "CreatureManager_LevelKarmaBonusResponse"; private const float KarmaBonusRetryInterval = 0.5f; private const float KarmaBonusRequestTimeout = 10f; private const float KarmaBonusMaximumRetryInterval = 5f; private const string HueProperty = "_Hue"; private const string SaturationProperty = "_Saturation"; private const string ValueProperty = "_Value"; private const string EmissionColorProperty = "_EmissionColor"; private static readonly object Sync = new object(); private static LevelDefinition[] ActiveDefinitions = Array.Empty(); private static int ManagedSetLevelDepth; private static readonly Stack<(string Reason, Character? Source)> ExplicitLevelContexts = new Stack<(string, Character)>(); private static Character? CachedRuleSearchCharacter; private static LevelRuleScope CachedRuleSearchScope; private static int CachedRuleSearchFrame = -1; private static LevelRuleSearch? CachedRuleSearch; private static ZRoutedRpc? RegisteredRoutedRpc; private static int NextKarmaBonusRequestId; private static readonly Dictionary PendingLevelCharacters = new Dictionary(); private static readonly Dictionary PendingKarmaBonusRequests = new Dictionary(); private static readonly Dictionary ResolvedKarmaBonuses = new Dictionary(); internal static bool IsLevelSystemEnabled() { ConfigEntry enableLevelSystem = CreatureManagerPlugin.EnableLevelSystem; if (enableLevelSystem == null) { return true; } return enableLevelSystem.Value != CreatureManagerPlugin.Toggle.Off; } internal static void Load(List definitions) { lock (Sync) { ActiveDefinitions = definitions.ToArray(); } InvalidateRuleSearchCache(); } internal static void ResetRuntimeState() { ExplicitLevelContexts.Clear(); ManagedSetLevelDepth = 0; NextKarmaBonusRequestId = 0; PendingLevelCharacters.Clear(); PendingKarmaBonusRequests.Clear(); ResolvedKarmaBonuses.Clear(); InvalidateRuleSearchCache(); } internal static void RegisterRpcs() { if (ZRoutedRpc.instance != null && RegisteredRoutedRpc != ZRoutedRpc.instance) { ZRoutedRpc.instance.Register("CreatureManager_LevelKarmaBonusRequest", (Action)RPC_KarmaBonusRequest); ZRoutedRpc.instance.Register("CreatureManager_LevelKarmaBonusResponse", (Action)RPC_KarmaBonusResponse); RegisteredRoutedRpc = ZRoutedRpc.instance; } } internal static void UpdatePendingApplications() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) RegisterRpcs(); if (!CreatureDomainManager.IsSynchronizedConfigurationReady()) { return; } PruneTimedOutKarmaBonusRequests(); if (!IsLevelSystemEnabled()) { PendingLevelCharacters.Clear(); PendingKarmaBonusRequests.Clear(); ResolvedKarmaBonuses.Clear(); } else { if (PendingLevelCharacters.Count == 0) { return; } KeyValuePair[] array = PendingLevelCharacters.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; Character value = keyValuePair.Value; if ((Object)(object)value == (Object)null || value.IsPlayer()) { PendingLevelCharacters.Remove(keyValuePair.Key); continue; } ZNetView nview = value.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { ForgetPendingLevelCharacter(value, value.GetZDOID()); continue; } ZDO zDO = nview.GetZDO(); if (zDO == null) { continue; } if (IsDeadOrWithoutHealth(value, zDO)) { ForgetPendingLevelCharacter(value, zDO.m_uid); } else if (zDO.GetBool("CreatureManager_LevelProcessingComplete", false)) { SynchronizeStoredRuntimeState(value, zDO); if (nview.IsOwner()) { CreatureModifierManager.TryRollModifiers(value); } ForgetPendingLevelCharacter(value, zDO.m_uid); } else if (ShouldPreserveExistingRolls(value)) { SynchronizeStoredRuntimeState(value, zDO); ForgetPendingLevelCharacter(value, zDO.m_uid); } else if (nview.IsOwner()) { TryApplyLevel(value); } else { PendingKarmaBonusRequests.Remove(zDO.m_uid); ResolvedKarmaBonuses.Remove(zDO.m_uid); } } } } internal static void ForgetCharacter(Character character) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character != (Object)null) { ForgetPendingLevelCharacter(character, character.GetZDOID()); } } private static void InvalidateRuleSearchCache() { CachedRuleSearchCharacter = null; CachedRuleSearchFrame = -1; CachedRuleSearch = null; } internal static Dictionary GetGlobalModifierDefinitions() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); LevelDefinition[] activeDefinitions; lock (Sync) { activeDefinitions = ActiveDefinitions; } LevelDefinition[] array = activeDefinitions; foreach (LevelDefinition levelDefinition in array) { string target = (levelDefinition.Target ?? "").Trim(); string text = (levelDefinition.Biome ?? "").Trim(); if (!IsGlobalTarget(target) || text.Length > 0) { continue; } if (levelDefinition.ModifiersCleared) { dictionary.Clear(); } else { if (levelDefinition.Modifiers == null) { continue; } foreach (KeyValuePair modifier in levelDefinition.Modifiers) { if (!dictionary.TryGetValue(modifier.Key, out var value)) { dictionary[modifier.Key] = modifier.Value.Clone(); } else { value.OverlayFrom(modifier.Value); } } } } string[] array2 = (from entry in dictionary where !entry.Value.Chance.HasValue || entry.Value.Chance.Value <= 0f select entry.Key).ToArray(); foreach (string key in array2) { dictionary.Remove(key); } return dictionary; } internal static void TryApplyLevel(Character character) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_0210: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || character.IsPlayer()) { return; } if (!CreatureDomainManager.IsSynchronizedConfigurationReady()) { PendingLevelCharacters[((Object)character).GetInstanceID()] = character; return; } if (!IsLevelSystemEnabled()) { ForgetPendingLevelCharacter(character, character.GetZDOID()); return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } if (!nview.IsOwner()) { PendingLevelCharacters[((Object)character).GetInstanceID()] = character; return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (IsDeadOrWithoutHealth(character, zDO)) { ForgetPendingLevelCharacter(character, zDO.m_uid); return; } if (zDO.GetBool("CreatureManager_LevelProcessingComplete", false)) { SynchronizeStoredRuntimeState(character, zDO); CreatureModifierManager.TryRollModifiers(character); ForgetPendingLevelCharacter(character, zDO.m_uid); return; } if (ShouldPreserveExistingRolls(character)) { SynchronizeStoredRuntimeState(character, zDO); ForgetPendingLevelCharacter(character, zDO.m_uid); return; } if (!zDO.GetBool("CreatureManager_LevelApplied", false)) { if (TryAdoptPreexistingExternalLevel(character, zDO)) { ReapplyLevelDependentRuntimeState(character, zDO); } else if (ShouldRollLevel(character)) { if (!TryResolveKarmaBonus(character, zDO, out var bonus)) { PendingLevelCharacters[((Object)character).GetInstanceID()] = character; return; } int level = 1; List weights; bool flag = TrySelectLevelWeights(character, out weights) && TrySelectLevel(weights, GetPrefabName(((Component)character).gameObject), out level); if (flag || bonus > 0) { int num = Math.Max(1, ((!flag) ? 1 : level) + bonus); zDO.Set("CreatureManager_DesiredLevel", num); zDO.Set("CreatureManager_LevelApplied", true); SetManagedLevel(character, num); } } } float multiplier; if (zDO.GetBool("CreatureManager_LevelHealthApplied", false)) { RestoreStoredHealthMultiplier(character, zDO); } else if (TrySelectHealthMultiplier(character, out multiplier)) { ApplyHealthMultiplier(character, multiplier); zDO.Set("CreatureManager_LevelHealthMultiplier", multiplier); zDO.Set("CreatureManager_LevelHealthApplied", true); } if (!zDO.GetBool("CreatureManager_LevelDamageApplied", false) && TrySelectDamageMultiplier(character, out var multiplier2)) { zDO.Set("CreatureManager_LevelDamageMultiplier", multiplier2); zDO.Set("CreatureManager_LevelDamageApplied", true); } ApplyRuntimeVisuals(character); zDO.Set("CreatureManager_LevelProcessingComplete", true); CreatureModifierManager.TryRollModifiers(character); ForgetPendingLevelCharacter(character, zDO.m_uid); } private static bool TryResolveKarmaBonus(Character character, ZDO zdo, out int bonus) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) bonus = 0; if (!CreatureKarmaManager.RequiresAuthoritativeLevelBonus(zdo, character.IsBoss())) { return true; } if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { bonus = CreatureKarmaManager.GetLevelBonus(character); return true; } if (ResolvedKarmaBonuses.TryGetValue(zdo.m_uid, out bonus)) { ResolvedKarmaBonuses.Remove(zdo.m_uid); bonus = Math.Max(0, bonus); return true; } QueueKarmaBonusRequest(zdo); return false; } private static void QueueKarmaBonusRequest(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = zdo.m_uid; if (uid == ZDOID.None) { return; } if (!PendingKarmaBonusRequests.TryGetValue(uid, out PendingKarmaBonusRequest value)) { int num = ++NextKarmaBonusRequestId; if (num <= 0) { num = (NextKarmaBonusRequestId = 1); } value = new PendingKarmaBonusRequest { RequestId = num }; PendingKarmaBonusRequests[uid] = value; } float unscaledTime = Time.unscaledTime; if (unscaledTime < value.NextSendAt) { return; } RegisterRpcs(); if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(value.RequestId); val.Write(uid); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_LevelKarmaBonusRequest", new object[1] { val }); if (value.StartedAt < 0f) { value.StartedAt = unscaledTime; } float num2 = Mathf.Min(5f, 0.5f * (float)Math.Max(1, value.TimeoutCount + 1)); value.NextSendAt = unscaledTime + num2; } } private static void PruneTimedOutKarmaBonusRequests() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; KeyValuePair[] array = PendingKarmaBonusRequests.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; PendingKarmaBonusRequest value = keyValuePair.Value; if (!(value.StartedAt < 0f) && !(unscaledTime - value.StartedAt < 10f)) { value.StartedAt = unscaledTime; value.NextSendAt = unscaledTime; value.TimeoutCount++; if (value.TimeoutCount == 1 || value.TimeoutCount % 6 == 0) { CreatureManagerPlugin.Log.LogWarning((object)($"Still waiting for the server Karma level bonus for creature {keyValuePair.Key}; " + "keeping level application pending and retrying at a reduced rate.")); } } } } private static void RPC_KarmaBonusRequest(long sender, ZPackage package) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } try { int num = package.ReadInt(); ZDOID val = package.ReadZDOID(); bool flag = false; int val2 = 0; ZNetPeer peer = ZNet.instance.GetPeer(sender); if (num > 0 && val != ZDOID.None && peer != null && peer.IsReady() && ZDOMan.instance != null && (Object)(object)ZNetScene.instance != (Object)null) { ZDO zDO = ZDOMan.instance.GetZDO(val); GameObject val3 = ((zDO != null) ? ZNetScene.instance.GetPrefab(zDO.GetPrefab()) : null); if (zDO != null && zDO.GetOwner() == sender && (Object)(object)val3 != (Object)null) { Character component = val3.GetComponent(); if (component != null && (Object)(object)val3.GetComponent() == (Object)null) { CreatureKarmaManager.TrackPotentialBlockerZdo(zDO, component.IsBoss()); val2 = CreatureKarmaManager.GetAuthoritativeLevelBonus(zDO); flag = true; } } } ZPackage val4 = new ZPackage(); val4.Write(num); val4.Write(val); val4.Write(flag); val4.Write(Math.Max(0, val2)); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "CreatureManager_LevelKarmaBonusResponse", new object[1] { val4 }); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to process a Karma level bonus request: " + ex.Message)); } } private static void RPC_KarmaBonusResponse(long sender, ZPackage package) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_0104: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID()) { return; } try { int num = package.ReadInt(); ZDOID val = package.ReadZDOID(); bool flag = package.ReadBool(); int value = Math.Max(0, package.ReadInt()); if (!PendingKarmaBonusRequests.TryGetValue(val, out PendingKarmaBonusRequest value2) || value2.RequestId != num) { return; } if (TryFindPendingLevelCharacter(val, out Character character) && !((Object)(object)character.m_nview == (Object)null) && character.m_nview.IsValid() && character.m_nview.IsOwner()) { ZDO zDO = character.m_nview.GetZDO(); if (((zDO != null) ? new long?(zDO.GetOwner()) : ((long?)null)) == ZRoutedRpc.instance.m_id) { if (flag) { PendingKarmaBonusRequests.Remove(val); ResolvedKarmaBonuses[val] = value; TryApplyLevel(character); } return; } } PendingKarmaBonusRequests.Remove(val); ResolvedKarmaBonuses.Remove(val); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to process a Karma level bonus response: " + ex.Message)); } } private static bool TryFindPendingLevelCharacter(ZDOID characterId, out Character character) { //IL_0024: 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) foreach (Character value in PendingLevelCharacters.Values) { if ((Object)(object)value != (Object)null && value.GetZDOID() == characterId) { character = value; return true; } } character = null; return false; } private static void ForgetPendingLevelCharacter(Character character, ZDOID characterId) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character != (Object)null) { PendingLevelCharacters.Remove(((Object)character).GetInstanceID()); } if (characterId != ZDOID.None) { PendingKarmaBonusRequests.Remove(characterId); ResolvedKarmaBonuses.Remove(characterId); } } private static void SynchronizeStoredRuntimeState(Character character, ZDO zdo) { int val = zdo.GetInt("CreatureManager_DesiredLevel", zdo.GetInt(ZDOVars.s_level, Math.Max(1, character.GetLevel()))); val = Math.Max(1, val); if (character.GetLevel() != val) { character.m_level = val; } RestoreStoredHealthMultiplier(character, zdo); ApplyRuntimeVisuals(character); } private static void RestoreStoredHealthMultiplier(Character character, ZDO zdo) { ZNetView nview = character.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner() && zdo.GetBool("CreatureManager_LevelHealthApplied", false)) { float multiplier = zdo.GetFloat("CreatureManager_LevelHealthMultiplier", 1f); ApplyHealthMultiplier(character, multiplier); } } internal static float CaptureStoredHealthDeficit(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer() || !TryGetOwnedZdo(character, out ZDO zdo) || !zdo.GetBool("CreatureManager_LevelHealthApplied", false)) { return float.NaN; } float maxHealth = character.GetMaxHealth(); float health = character.GetHealth(); if (!(maxHealth > 0f) || !(health > 0f)) { return float.NaN; } return Mathf.Max(0f, maxHealth - health); } internal static void RestoreStoredHealthDeficit(Character character, float missingHealth) { if (!float.IsNaN(missingHealth) && !float.IsInfinity(missingHealth) && !((Object)(object)character == (Object)null) && !character.IsPlayer() && TryGetOwnedZdo(character, out ZDO zdo) && !(character.GetHealth() <= 0f)) { RestoreStoredHealthMultiplier(character, zdo); float maxHealth = character.GetMaxHealth(); if (!(maxHealth <= 0f)) { float num = Mathf.Min(1f, maxHealth); character.SetHealth(Mathf.Clamp(maxHealth - missingHealth, num, maxHealth)); } } } private static bool TryGetOwnedZdo(Character character, out ZDO zdo) { ZNetView nview = character.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { zdo = zDO; return true; } } zdo = null; return false; } private static bool IsDeadOrWithoutHealth(Character character, ZDO zdo) { if (character.IsDead() || zdo.GetBool(ZDOVars.s_dead, false)) { return true; } float num = zdo.GetFloat(ZDOVars.s_health, float.PositiveInfinity); if (!float.IsNaN(num) && !float.IsInfinity(num)) { return num <= 0f; } return false; } internal static bool HasManagedLevel(Character character) { if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO != null) { return zDO.GetBool("CreatureManager_LevelApplied", false); } return false; } internal static bool IsReadyForModifierApplication(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer() || !CreatureDomainManager.IsSynchronizedConfigurationReady()) { return false; } if (ShouldPreserveExistingRolls(character)) { return true; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO != null) { return zDO.GetBool("CreatureManager_LevelProcessingComplete", false); } return false; } internal static bool TryApplyForcedLevel(Character character, int level, out string error) { error = ""; if (!IsLevelSystemEnabled()) { error = "CreatureManager level system is disabled."; return false; } if ((Object)(object)character == (Object)null || character.IsPlayer() || level < 1) { error = "The spawned prefab is not a supported creature or the requested level is invalid."; return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { error = "The spawned creature has no owned network state."; return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { error = "The spawned creature has no ZDO."; return false; } zDO.Set("CreatureManager_DesiredLevel", level); zDO.Set("CreatureManager_DesiredLevelSource", "cm:spawn"); zDO.Set("CreatureManager_LevelApplied", true); SetManagedLevel(character, level); ReapplyLevelDependentRuntimeState(character, zDO); ApplyRuntimeVisuals(character); return true; } internal static bool TryApplyRotatedLevelEffects(LevelEffects levelEffects, int level) { if (!IsLevelSystemEnabled() || (Object)(object)levelEffects == (Object)null || levelEffects.m_levelSetups == null || levelEffects.m_levelSetups.Count == 0) { return false; } Character componentInParent = ((Component)levelEffects).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || componentInParent.IsPlayer()) { return false; } if (!TryGetScaleRuleScope(componentInParent, out var _)) { return false; } try { CreatureLevelEffectsState creatureLevelEffectsState = CreatureLevelEffectsState.Get(levelEffects); creatureLevelEffectsState.EnsureInitialized(levelEffects); ApplyLevelEffectsScale(levelEffects, creatureLevelEffectsState, componentInParent, level); ApplyLevelEffectsVisualState(levelEffects, creatureLevelEffectsState, level); return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogDebug((object)("Failed to apply rotated LevelEffects for '" + GetPrefabName(((Component)componentInParent).gameObject) + "': " + ex.Message)); return false; } } internal static void RestoreConfiguredLevel(Character character, int level) { if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (ShouldPreserveExistingRolls(character)) { RestoreStoredHealthMultiplier(character, zDO); } else { if (!zDO.GetBool("CreatureManager_LevelApplied", false)) { return; } int num = zDO.GetInt("CreatureManager_DesiredLevel", 0); if (num > 0) { if (num == level) { RestoreStoredHealthMultiplier(character, zDO); } else if (!TryAdoptExternalLevelOverride(character, zDO, level)) { SetManagedLevel(character, num); RestoreStoredHealthMultiplier(character, zDO); } } } } internal static void BeginExplicitExternalLevelContext(string reason, Character? source = null) { ExplicitLevelContexts.Push((reason, source)); } internal static void EndExplicitExternalLevelContext() { if (ExplicitLevelContexts.Count > 0) { ExplicitLevelContexts.Pop(); } } internal static bool TryAdoptContextualExternalLevel(Character character, int level) { if (ExplicitLevelContexts.Count <= 0 || ManagedSetLevelDepth > 0) { return false; } if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return false; } int num = Math.Max(1, level); var (text, val) = ExplicitLevelContexts.Peek(); zDO.Set("CreatureManager_DesiredLevel", num); zDO.Set("CreatureManager_DesiredLevelSource", text); zDO.Set("CreatureManager_LevelApplied", true); if ((Object)(object)val != (Object)null) { CreatureModifierManager.InheritModifiers(val, character); } if (ShouldPreserveExistingRolls(character)) { InheritLevelRuntimeState(val, character, zDO); } else { ReapplyLevelDependentRuntimeState(character, zDO); } return true; } private static bool TryAdoptPreexistingExternalLevel(Character character, ZDO zdo) { if (CreatureManagerSpawnLifecycle.IsManagedSpawn(character) || ShouldRollLevel(character)) { return false; } int num = Math.Max(1, character.GetLevel()); if (num <= 1) { return false; } zdo.Set("CreatureManager_DesiredLevel", num); zdo.Set("CreatureManager_LevelApplied", true); return true; } private static bool TryAdoptExternalLevelOverride(Character character, ZDO zdo, int level) { if (ManagedSetLevelDepth > 0) { return false; } int num = Math.Max(1, level); zdo.Set("CreatureManager_DesiredLevel", num); zdo.Set("CreatureManager_LevelApplied", true); ReapplyLevelDependentRuntimeState(character, zdo); return true; } private static void SetManagedLevel(Character character, int level) { float maxHealth = character.GetMaxHealth(); float health = character.GetHealth(); float num = Mathf.Max(0f, maxHealth - health); ManagedSetLevelDepth++; try { character.SetLevel(level); float maxHealth2 = character.GetMaxHealth(); if (health > 0f && maxHealth > 0f && maxHealth2 > 0f) { character.SetHealth(Mathf.Max(1f, maxHealth2 - num)); } } finally { ManagedSetLevelDepth--; } } private static void ReapplyLevelDependentRuntimeState(Character character, ZDO zdo) { if (TrySelectHealthMultiplier(character, out var multiplier)) { ApplyHealthMultiplier(character, multiplier); zdo.Set("CreatureManager_LevelHealthMultiplier", multiplier); zdo.Set("CreatureManager_LevelHealthApplied", true); } else if (zdo.GetBool("CreatureManager_LevelHealthApplied", false)) { ApplyHealthMultiplier(character, 1f); zdo.Set("CreatureManager_LevelHealthMultiplier", 1f); zdo.Set("CreatureManager_LevelHealthApplied", false); } if (TrySelectDamageMultiplier(character, out var multiplier2)) { zdo.Set("CreatureManager_LevelDamageMultiplier", multiplier2); zdo.Set("CreatureManager_LevelDamageApplied", true); } else if (zdo.GetBool("CreatureManager_LevelDamageApplied", false)) { zdo.Set("CreatureManager_LevelDamageMultiplier", 1f); zdo.Set("CreatureManager_LevelDamageApplied", false); } } private static void InheritLevelRuntimeState(Character? source, Character target, ZDO targetZdo) { if ((Object)(object)source == (Object)null || (Object)(object)source == (Object)(object)target) { return; } ZNetView nview = source.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (zDO.GetBool("CreatureManager_LevelHealthApplied", false)) { float num = Mathf.Max(0f, zDO.GetFloat("CreatureManager_LevelHealthMultiplier", 1f)); if (num > 0f) { ApplyHealthMultiplier(target, num); targetZdo.Set("CreatureManager_LevelHealthMultiplier", num); targetZdo.Set("CreatureManager_LevelHealthApplied", true); } } if (zDO.GetBool("CreatureManager_LevelDamageApplied", false)) { float num2 = Mathf.Max(0f, zDO.GetFloat("CreatureManager_LevelDamageMultiplier", 1f)); targetZdo.Set("CreatureManager_LevelDamageMultiplier", num2); targetZdo.Set("CreatureManager_LevelDamageApplied", true); } } internal static void ApplyRuntimeVisuals(Character character) { if (IsLevelSystemEnabled() && !((Object)(object)character == (Object)null) && !character.IsPlayer() && CreatureDomainManager.IsSynchronizedConfigurationReady() && TryGetScaleRuleScope(character, out var _)) { bool flag = false; LevelEffects[] componentsInChildren = ((Component)character).GetComponentsInChildren(true); foreach (LevelEffects levelEffects in componentsInChildren) { flag |= TryApplyRotatedLevelEffects(levelEffects, character.GetLevel()); } if (flag) { CreatureCharacterScaleState.RestoreIfPresent(character); } else { ApplyCharacterScaleFallback(character, character.GetLevel()); } } } private static void ApplyLevelEffectsScale(LevelEffects levelEffects, CreatureLevelEffectsState state, Character character, int level) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) float levelScaleMultiplier = GetLevelScaleMultiplier(character, level); ((Component)levelEffects).transform.localScale = state.OriginalLocalScale * levelScaleMultiplier; } private static void ApplyCharacterScaleFallback(Character character, int level) { //IL_0015: 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) CreatureCharacterScaleState creatureCharacterScaleState = CreatureCharacterScaleState.Get(character); creatureCharacterScaleState.EnsureInitialized(character); ((Component)character).transform.localScale = creatureCharacterScaleState.OriginalLocalScale * GetLevelScaleMultiplier(character, level); } private static float GetLevelScaleMultiplier(Character character, int level) { float scalePerLevel; float num = ((ShouldApplyLevelScale(character) && TrySelectScalePerLevel(character, out scalePerLevel)) ? scalePerLevel : 0f); return 1f + (float)Math.Max(0, level - 1) * Mathf.Max(0f, num); } private static bool ShouldApplyLevelScale(Character character) { if (IsDungeonCreature(character)) { return false; } if (IsSaddleableCreature(character)) { ConfigEntry applyLevelScaleToSaddleableCreatures = CreatureManagerPlugin.ApplyLevelScaleToSaddleableCreatures; if (applyLevelScaleToSaddleableCreatures == null || applyLevelScaleToSaddleableCreatures.Value != CreatureManagerPlugin.Toggle.On) { return false; } } return true; } private static bool IsSaddleableCreature(Character character) { Tameable component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.m_saddleItem != (Object)null) { return true; } Component[] components = ((Component)character).GetComponents(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "Saddle") { return true; } } return false; } internal static bool IsDungeonCreature(Character character) { //IL_0008: 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) Transform transform = ((Component)character).transform; if (!(transform.localPosition.y >= 4500f)) { return transform.position.y >= 4500f; } return true; } private static void ApplyLevelEffectsVisualState(LevelEffects levelEffects, CreatureLevelEffectsState state, int level) { int num = levelEffects.m_levelSetups.Count + 1; int num2 = ((level > 1) ? ((level - 1) % num) : 0); RestoreLevelEffectsBase(levelEffects, state); if (num2 == 0) { return; } LevelSetup val = levelEffects.m_levelSetups[num2 - 1]; if (val != null) { ApplyLevelEffectsMaterial(levelEffects, state, num2, val); if ((Object)(object)val.m_enableObject != (Object)null) { val.m_enableObject.SetActive(true); } } } private static void RestoreLevelEffectsBase(LevelEffects levelEffects, CreatureLevelEffectsState state) { if ((Object)(object)levelEffects.m_mainRender != (Object)null && (Object)(object)state.OriginalMainMaterial != (Object)null) { Material[] sharedMaterials = levelEffects.m_mainRender.sharedMaterials; if (sharedMaterials.Length != 0) { sharedMaterials[0] = state.OriginalMainMaterial; levelEffects.m_mainRender.sharedMaterials = sharedMaterials; } } if ((Object)(object)levelEffects.m_baseEnableObject != (Object)null) { levelEffects.m_baseEnableObject.SetActive(state.GetOriginalActive(levelEffects.m_baseEnableObject, fallback: true)); } foreach (LevelSetup levelSetup in levelEffects.m_levelSetups) { if ((Object)(object)levelSetup?.m_enableObject != (Object)null) { levelSetup.m_enableObject.SetActive(false); } } } private static void ApplyLevelEffectsMaterial(LevelEffects levelEffects, CreatureLevelEffectsState state, int visualState, LevelSetup setup) { Renderer mainRender = levelEffects.m_mainRender; if (!((Object)(object)mainRender == (Object)null) && !((Object)(object)state.OriginalMainMaterial == (Object)null)) { Material orCreateMaterial = state.GetOrCreateMaterial(visualState, setup); Material[] sharedMaterials = mainRender.sharedMaterials; if (sharedMaterials.Length != 0) { sharedMaterials[0] = orCreateMaterial; mainRender.sharedMaterials = sharedMaterials; } } } internal static void ApplyColorProperties(Material material, LevelSetup setup) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty("_Hue")) { material.SetFloat("_Hue", setup.m_hue); } if (material.HasProperty("_Saturation")) { material.SetFloat("_Saturation", setup.m_saturation); } if (material.HasProperty("_Value")) { material.SetFloat("_Value", setup.m_value); } if (setup.m_setEmissiveColor && material.HasProperty("_EmissionColor")) { material.SetColor("_EmissionColor", setup.m_emissiveColor); } } private static bool TrySelectLevelWeights(Character character, out List weights) { weights = new List(); if (!TrySelectRule(character, delegate(LevelDefinition levelDefinition) { List level = levelDefinition.Level; return level != null && level.Count > 0; }, out LevelDefinition rule)) { return false; } weights = rule.Level ?? new List(); return weights.Count > 0; } private static bool ShouldRollLevel(Character character) { return GetSpawnPolicy(character).RollLevel; } private static bool ShouldPreserveExistingRolls(Character character) { return GetSpawnPolicy(character).PreserveExistingRolls; } private static SpawnPolicy GetSpawnPolicy(Character character) { return GetSpawnPolicy(CreatureManagerSpawnLifecycle.GetSpawnSource(character)); } private static SpawnPolicy GetSpawnPolicy(CreatureSpawnSourceKind source) { switch (source) { case CreatureSpawnSourceKind.Command: return new SpawnPolicy(rollLevel: false, LevelRuleScope.BaselineOnly, LevelRuleScope.BaselineOnly, allowHealthDistanceScaling: true, ModifierApplicationMode.Block, LevelRuleScope.Full, allowModifierDistanceScaling: false); case CreatureSpawnSourceKind.PlayerSummon: return new SpawnPolicy(rollLevel: false, LevelRuleScope.BaselineOnly, LevelRuleScope.BaselineOnly, allowHealthDistanceScaling: false, ModifierApplicationMode.Block, LevelRuleScope.Full, allowModifierDistanceScaling: false); case CreatureSpawnSourceKind.Breeding: case CreatureSpawnSourceKind.Egg: return new SpawnPolicy(rollLevel: false, LevelRuleScope.BaselineOnly, LevelRuleScope.BaselineOnly, allowHealthDistanceScaling: false, ModifierApplicationMode.Roll, LevelRuleScope.BaselineOnly, allowModifierDistanceScaling: false); case CreatureSpawnSourceKind.Growup: case CreatureSpawnSourceKind.TamedRestore: return new SpawnPolicy(rollLevel: false, null, null, allowHealthDistanceScaling: false, ModifierApplicationMode.Keep, LevelRuleScope.Full, allowModifierDistanceScaling: false); default: return new SpawnPolicy(rollLevel: true, LevelRuleScope.Full, LevelRuleScope.Full, allowHealthDistanceScaling: true, ModifierApplicationMode.Roll, LevelRuleScope.Full, allowModifierDistanceScaling: true); } } private static bool TryGetScaleRuleScope(Character character, out LevelRuleScope scope) { return TryGetGeneralRuleScope(character, out scope); } private static bool TryGetHealthRuleScope(Character character, out LevelRuleScope scope, out bool allowDistanceScaling) { SpawnPolicy spawnPolicy = GetSpawnPolicy(character); scope = spawnPolicy.HealthScope ?? LevelRuleScope.Full; allowDistanceScaling = spawnPolicy.AllowHealthDistanceScaling; return spawnPolicy.HealthScope.HasValue; } private static bool TryGetDamageRuleScope(Character character, out LevelRuleScope scope, out bool allowDistanceScaling) { return TryGetHealthRuleScope(character, out scope, out allowDistanceScaling); } private static bool TryGetGeneralRuleScope(Character character, out LevelRuleScope scope) { SpawnPolicy spawnPolicy = GetSpawnPolicy(character); scope = spawnPolicy.GeneralScope ?? LevelRuleScope.Full; return spawnPolicy.GeneralScope.HasValue; } internal static bool ShouldRollModifiers(Character character) { if (AreModifiersEnabled(character)) { return GetModifierMode(character) == ModifierApplicationMode.Roll; } return false; } internal static bool BlocksModifierRoll(Character character) { return GetModifierMode(character) == ModifierApplicationMode.Block; } internal static bool AllowsModifierEffects(Character character) { if (IsLevelSystemEnabled() && AreModifiersEnabled(character)) { return GetModifierMode(character) != ModifierApplicationMode.Block; } return false; } internal static bool AllowsModifierEffects(ZDO zdo, bool isBoss, bool isEnforcer) { if (zdo != null && IsLevelSystemEnabled() && AreModifiersEnabled(isBoss, isEnforcer)) { return GetSpawnPolicy(CreatureManagerSpawnLifecycle.GetSpawnSource(zdo)).ModifierMode != ModifierApplicationMode.Block; } return false; } private static bool AreModifiersEnabled(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } return AreModifiersEnabled(character.IsBoss(), CreatureKarmaManager.IsEnforcer(character)); } private static bool AreModifiersEnabled(bool isBoss, bool isEnforcer) { int num; if (!isBoss) { ConfigEntry enableGlobalModifiers = CreatureManagerPlugin.EnableGlobalModifiers; num = ((enableGlobalModifiers == null || enableGlobalModifiers.Value != CreatureManagerPlugin.Toggle.Off) ? 1 : 0); } else { ConfigEntry enableBossModifiers = CreatureManagerPlugin.EnableBossModifiers; num = ((enableBossModifiers == null || enableBossModifiers.Value != CreatureManagerPlugin.Toggle.Off) ? 1 : 0); } bool result = (byte)num != 0; ConfigEntry enableEnforcerModifiers = CreatureManagerPlugin.EnableEnforcerModifiers; bool result2 = enableEnforcerModifiers == null || enableEnforcerModifiers.Value != CreatureManagerPlugin.Toggle.Off; if (!isEnforcer) { return result; } return result2; } private static ModifierApplicationMode GetModifierMode(Character character) { return GetSpawnPolicy(character).ModifierMode; } private static bool TryGetModifierRuleScope(Character character, out LevelRuleScope scope, out bool allowDistanceScaling) { SpawnPolicy spawnPolicy = GetSpawnPolicy(character); scope = spawnPolicy.ModifierScope; allowDistanceScaling = spawnPolicy.AllowModifierDistanceScaling; return spawnPolicy.ModifierMode == ModifierApplicationMode.Roll; } private static bool TrySelectHealthMultiplier(Character character, out float multiplier) { multiplier = 1f; if (!TryGetHealthRuleScope(character, out var scope, out var allowDistanceScaling)) { return false; } bool flag = false; float num = 1f; if (TrySelectFloatValue(character, (LevelDefinition rule) => rule.Health, out var value, scope)) { num = Mathf.Max(0f, value); flag = true; } float perLevel = 1f; if (TrySelectFloatValue(character, (LevelDefinition rule) => rule.HealthPerLevel, out var value2, scope)) { perLevel = Mathf.Max(0f, value2); flag = true; } float num2 = 1f; if (allowDistanceScaling && TrySelectDistanceScalingMultiplier(character, 1, out var multiplier2, scope)) { num2 = multiplier2; flag = true; } if (!flag) { return false; } multiplier = num * GetPerLevelMultiplier(character, perLevel) * num2 / GetVanillaLevelHealthMultiplier(character); return multiplier > 0f; } private static float GetVanillaLevelHealthMultiplier(Character character) { return Mathf.Max(1, character.GetLevel()); } private static bool TrySelectDamageMultiplier(Character character, out float multiplier) { multiplier = 1f; if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } if (!TryGetDamageRuleScope(character, out var scope, out var allowDistanceScaling)) { return false; } bool flag = false; float num = 1f; if (TrySelectFloatValue(character, (LevelDefinition rule) => rule.Damage, out var value, scope)) { num = Mathf.Max(0f, value); flag = true; } float perLevel = 0f; if (TrySelectFloatValue(character, (LevelDefinition rule) => rule.DamagePerLevel, out var value2, scope)) { perLevel = Mathf.Max(0f, value2); flag = true; } float num2 = 1f; if (allowDistanceScaling && TrySelectDistanceScalingMultiplier(character, 0, out var multiplier2, scope)) { num2 = multiplier2; flag = true; } if (!flag) { return false; } multiplier = num * GetPerLevelMultiplier(character, perLevel) * num2; return true; } private static bool TrySelectScalePerLevel(Character character, out float scalePerLevel) { scalePerLevel = 0f; if (!TryGetScaleRuleScope(character, out var scope)) { return false; } if (!TrySelectFloatValue(character, (LevelDefinition rule) => rule.ScalePerLevel, out var value, scope)) { return false; } scalePerLevel = Mathf.Max(0f, value); return true; } internal static bool TryGetDamageMultiplier(Character character, out float multiplier) { multiplier = 1f; if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null || !zDO.GetBool("CreatureManager_LevelDamageApplied", false)) { return false; } multiplier = Mathf.Max(0f, zDO.GetFloat("CreatureManager_LevelDamageMultiplier", 1f)); return !Mathf.Approximately(multiplier, 1f); } internal static bool TrySelectModifierChances(Character character, out ModifierChanceDefinition chances) { chances = new ModifierChanceDefinition(); if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return false; } if (!TryGetModifierRuleScope(character, out var scope, out var allowDistanceScaling)) { return false; } LevelRuleSearch ruleSearch = GetRuleSearch(character, scope); float multiplier = 1f; if (allowDistanceScaling) { TrySelectModifierDistanceScalingMultiplier(ruleSearch, out multiplier); } bool result = false; foreach (string knownModifierKey in CreatureModifierManager.GetKnownModifierKeys()) { if (TrySelectModifierChance(ruleSearch, knownModifierKey, out var chance) && CreatureModifierManager.TrySetModifierChance(chances, knownModifierKey, Mathf.Clamp(chance * multiplier, 0f, 100f))) { result = true; } } return result; } private static bool TrySelectModifierChance(LevelRuleSearch search, string modifier, out float chance) { return TrySelectModifierValue(search, modifier, (ModifierDefinition value) => value.Chance, out chance); } internal static bool TrySelectModifierPowers(Character character, out ModifierPowerDefinition powers) { powers = new ModifierPowerDefinition(); if (!IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return false; } if (!TryGetModifierRuleScope(character, out var scope, out var _)) { return false; } LevelRuleSearch ruleSearch = GetRuleSearch(character, scope); bool result = false; foreach (string knownModifierKey in CreatureModifierManager.GetKnownModifierKeys()) { if (TrySelectModifierPower(ruleSearch, knownModifierKey, out var power) && CreatureModifierManager.TrySetModifierPower(powers, knownModifierKey, CreatureModifierManager.ResolveModifierPower(knownModifierKey, power))) { result = true; } } if (TrySelectModifierCooldown(ruleSearch, "deathward", out var cooldown)) { powers.DeathwardCooldown = Mathf.Max(0f, cooldown); result = true; } if (TrySelectModifierInteger(ruleSearch, "deathward", (ModifierDefinition modifierDefinition) => modifierDefinition.MaxActivations, out var result2)) { powers.DeathwardMaxActivations = Math.Max(1, result2); result = true; } if (TrySelectModifierCooldown(ruleSearch, "blink", out var cooldown2)) { powers.BlinkCooldown = Mathf.Max(0f, cooldown2); result = true; } if (TrySelectModifierCooldown(ruleSearch, "juggernaut", out var cooldown3)) { powers.KnockbackCooldown = Mathf.Max(0f, cooldown3); result = true; } if (TrySelectModifierValue(ruleSearch, "blamer", (ModifierDefinition modifierDefinition) => modifierDefinition.MaxKarmaGain, out var value)) { powers.BlamerMaxKarmaGain = Mathf.Max(0f, value); result = true; } if (TrySelectModifierValue(ruleSearch, "blamer", (ModifierDefinition modifierDefinition) => modifierDefinition.FleeHealthRatio, out var value2)) { powers.BlamerFleeHealthRatio = Mathf.Clamp01(value2); result = true; } if (TrySelectModifierMaxRange(ruleSearch, "blink", out var maxRange)) { powers.BlinkMaxRange = Mathf.Max(0f, maxRange); result = true; } if (TrySelectModifierStartEffect(ruleSearch, "blink", out string startEffect)) { powers.BlinkStartEffect = startEffect; result = true; } if (TrySelectModifierValue(ruleSearch, "exposed", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value3)) { powers.ExposedProcChance = Mathf.Clamp01(value3); result = true; } if (TrySelectModifierValue(ruleSearch, "exposed", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value4)) { powers.ExposedDuration = Mathf.Max(0.1f, value4); result = true; } if (TrySelectModifierValue(ruleSearch, "weakened", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value5)) { powers.WeakenedProcChance = Mathf.Clamp01(value5); result = true; } if (TrySelectModifierValue(ruleSearch, "weakened", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value6)) { powers.WeakenedDuration = Mathf.Max(0.1f, value6); result = true; } if (TrySelectModifierValue(ruleSearch, "withered", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value7)) { powers.WitheredProcChance = Mathf.Clamp01(value7); result = true; } if (TrySelectModifierValue(ruleSearch, "withered", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value8)) { powers.WitheredDuration = Mathf.Max(0.1f, value8); result = true; } if (TrySelectModifierValue(ruleSearch, "corrosive", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value9)) { powers.CorrosiveProcChance = Mathf.Clamp01(value9); result = true; } if (TrySelectModifierValue(ruleSearch, "corrosive", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value10)) { powers.CorrosiveDuration = Mathf.Max(0.1f, value10); result = true; } if (TrySelectModifierValue(ruleSearch, "crippling", (ModifierDefinition modifierDefinition) => modifierDefinition.SecondaryPower, out var value11)) { powers.CripplingJump = Mathf.Clamp01(value11); result = true; } if (TrySelectModifierValue(ruleSearch, "crippling", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value12)) { powers.CripplingProcChance = Mathf.Clamp01(value12); result = true; } if (TrySelectModifierValue(ruleSearch, "crippling", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value13)) { powers.CripplingDuration = Mathf.Max(0.1f, value13); result = true; } if (TrySelectModifierValue(ruleSearch, "disruptive", (ModifierDefinition modifierDefinition) => modifierDefinition.SecondaryPower, out var value14)) { powers.DisruptiveEitr = Mathf.Clamp01(value14); result = true; } if (TrySelectModifierValue(ruleSearch, "disruptive", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value15)) { powers.DisruptiveProcChance = Mathf.Clamp01(value15); result = true; } if (TrySelectModifierValue(ruleSearch, "disruptive", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value16)) { powers.DisruptiveDuration = Mathf.Max(0.1f, value16); result = true; } if (TrySelectModifierValue(ruleSearch, "reflection", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value17)) { powers.ReflectionProcChance = Mathf.Clamp01(value17); result = true; } if (TrySelectModifierValue(ruleSearch, "adrenalineDrain", (ModifierDefinition modifierDefinition) => modifierDefinition.SecondaryPower, out var value18)) { powers.AdrenalineDrainGainReduction = Mathf.Clamp01(value18); result = true; } if (TrySelectModifierValue(ruleSearch, "adrenalineDrain", (ModifierDefinition modifierDefinition) => modifierDefinition.ProcChance, out var value19)) { powers.AdrenalineDrainProcChance = Mathf.Clamp01(value19); result = true; } if (TrySelectModifierValue(ruleSearch, "adrenalineDrain", (ModifierDefinition modifierDefinition) => modifierDefinition.Duration, out var value20)) { powers.AdrenalineDrainDuration = Mathf.Max(0.1f, value20); result = true; } if (TrySelectModifierValue(ruleSearch, "toxicDeath", (ModifierDefinition modifierDefinition) => modifierDefinition.Radius, out var value21)) { powers.ToxicDeathRadius = Mathf.Max(0f, value21); result = true; } if (TrySelectModifierText(ruleSearch, "toxicDeath", (ModifierDefinition modifierDefinition) => modifierDefinition.TriggerEffect, out string value22)) { powers.ToxicDeathTriggerEffect = value22; result = true; } if (TrySelectModifierInteger(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingHealMaxActivations, out var result3)) { powers.ReapingHealMaxActivations = Math.Max(1, result3); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingMaxHealthPerKill, out var value23)) { powers.ReapingMaxHealthPerKill = Mathf.Max(0f, value23); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingMaxHealthCap, out var value24)) { powers.ReapingMaxHealthCap = Mathf.Max(0f, value24); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingDamagePerKill, out var value25)) { powers.ReapingDamagePerKill = Mathf.Max(0f, value25); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingDamageCap, out var value26)) { powers.ReapingDamageCap = Mathf.Max(0f, value26); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingScalePerKill, out var value27)) { powers.ReapingScalePerKill = Mathf.Max(0f, value27); result = true; } if (TrySelectModifierValue(ruleSearch, "reaping", (ModifierDefinition modifierDefinition) => modifierDefinition.ReapingScaleCap, out var value28)) { powers.ReapingScaleCap = Mathf.Max(0f, value28); result = true; } return result; } private static bool TrySelectModifierPower(LevelRuleSearch search, string modifier, out float power) { return TrySelectModifierValue(search, modifier, (ModifierDefinition value) => value.Power, out power); } private static bool TrySelectModifierCooldown(LevelRuleSearch search, string modifier, out float cooldown) { return TrySelectModifierValue(search, modifier, (ModifierDefinition value) => value.Cooldown, out cooldown); } private static bool TrySelectModifierMaxRange(LevelRuleSearch search, string modifier, out float maxRange) { return TrySelectModifierValue(search, modifier, (ModifierDefinition value) => value.MaxRange, out maxRange); } private static bool TrySelectModifierInteger(LevelRuleSearch search, string modifier, Func selector, out int result) { result = 0; foreach (LevelRuleCandidate candidate in search.Candidates) { LevelDefinition definition = candidate.Definition; if (definition.ModifiersCleared) { return false; } ModifierDefinition modifierDefinition = TryGetModifier(definition, modifier); int? num = ((modifierDefinition == null) ? ((int?)null) : selector(modifierDefinition)); if (num.HasValue) { result = num.Value; return true; } } return false; } private static bool TrySelectModifierStartEffect(LevelRuleSearch search, string modifier, out string startEffect) { return TrySelectModifierText(search, modifier, (ModifierDefinition value) => value.StartEffect, out startEffect); } private static bool TrySelectModifierText(LevelRuleSearch search, string modifier, Func selector, out string value) { value = ""; foreach (LevelRuleCandidate candidate in search.Candidates) { LevelDefinition definition = candidate.Definition; if (definition.ModifiersCleared) { return false; } ModifierDefinition modifierDefinition = TryGetModifier(definition, modifier); string text = ((modifierDefinition != null) ? selector(modifierDefinition) : null); if (text != null) { value = text; return true; } } return false; } private static bool TrySelectModifierValue(LevelRuleSearch search, string modifier, Func selector, out float value) { value = 0f; foreach (LevelRuleCandidate candidate in search.Candidates) { LevelDefinition definition = candidate.Definition; if (definition.ModifiersCleared) { return false; } ModifierDefinition modifierDefinition = TryGetModifier(definition, modifier); float? num = ((modifierDefinition != null) ? selector(modifierDefinition) : ((float?)null)); if (num.HasValue) { value = num.Value; return true; } } return false; } private static ModifierDefinition? TryGetModifier(LevelDefinition rule, string modifier) { if (rule.Modifiers == null || !rule.Modifiers.TryGetValue(modifier, out ModifierDefinition value)) { return null; } return value; } private static bool HasDistanceScalingValue(List? scaling, int index) { if (scaling != null && scaling.Count > index) { return scaling[index] > 0f; } return false; } private static bool TrySelectRule(Character character, Func hasEffect, out LevelDefinition rule, LevelRuleScope scope = LevelRuleScope.Full) { rule = null; foreach (LevelRuleCandidate candidate in GetRuleSearch(character, scope).Candidates) { if (hasEffect(candidate.Definition)) { rule = candidate.Definition; return true; } } return false; } private static LevelRuleSearch GetRuleSearch(Character character, LevelRuleScope scope) { int frameCount = Time.frameCount; if (CachedRuleSearch != null && CachedRuleSearchFrame == frameCount && CachedRuleSearchScope == scope && (Object)(object)CachedRuleSearchCharacter == (Object)(object)character) { return CachedRuleSearch; } LevelDefinition[] activeDefinitions; lock (Sync) { activeDefinitions = ActiveDefinitions; } LevelRuleContext context = LevelRuleContext.From(character); List list = new List(activeDefinitions.Length); for (int i = 0; i < activeDefinitions.Length; i++) { if (TryMatchRule(activeDefinitions[i], context, i, scope, out var candidate)) { list.Add(candidate); } } list.Sort((LevelRuleCandidate left, LevelRuleCandidate right) => right.CompareTo(left)); CachedRuleSearchCharacter = character; CachedRuleSearchScope = scope; CachedRuleSearchFrame = frameCount; CachedRuleSearch = new LevelRuleSearch(context, list); return CachedRuleSearch; } private static bool TrySelectFloatValue(Character character, Func selector, out float value, LevelRuleScope scope = LevelRuleScope.Full) { value = 0f; if (!TrySelectRule(character, (LevelDefinition arg) => selector(arg).HasValue, out LevelDefinition rule, scope)) { return false; } float? num = selector(rule); if (!num.HasValue) { return false; } value = num.Value; return true; } private static bool TrySelectDistanceScalingMultiplier(Character character, int valueIndex, out float multiplier, LevelRuleScope scope = LevelRuleScope.Full) { multiplier = 1f; if (!TrySelectRule(character, (LevelDefinition levelDefinition) => HasDistanceScalingValue(levelDefinition.DistanceScaling, valueIndex), out LevelDefinition rule, scope)) { return false; } multiplier = GetDistanceScalingMultiplier(GetRuleSearch(character, scope).Context.Distance, rule.DistanceScaling, valueIndex); return true; } private static bool TrySelectModifierDistanceScalingMultiplier(LevelRuleSearch search, out float multiplier) { multiplier = 1f; foreach (LevelRuleCandidate candidate in search.Candidates) { LevelDefinition definition = candidate.Definition; if (HasModifierDistanceScalingValue(definition.ModifierDistanceScaling)) { multiplier = GetModifierDistanceScalingMultiplier(search.Context.Distance, definition.ModifierDistanceScaling); return true; } } return false; } private static bool HasModifierDistanceScalingValue(List? scaling) { if (scaling != null) { return scaling.Count == 3; } return false; } private static bool TryMatchRule(LevelDefinition definition, LevelRuleContext context, int index, LevelRuleScope scope, out LevelRuleCandidate candidate) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) candidate = new LevelRuleCandidate(definition, 0, index); string text = (definition.Target ?? "").Trim(); if (text.Length == 0) { return false; } bool flag = context.IsBoss && !context.IsEnforcer; if (scope == LevelRuleScope.BaselineOnly && (flag ? (!IsBossTarget(text)) : (!IsGlobalTarget(text)))) { return false; } string text2 = (definition.Biome ?? "").Trim(); bool flag2 = text2.Length > 0; if (flag2 && !MatchesBiome(text2, context.Biome)) { return false; } int num = (flag2 ? 50 : 0); if (IsGlobalTarget(text)) { if (flag) { return false; } candidate = new LevelRuleCandidate(definition, num, index); return true; } if (IsBossTarget(text)) { if (!flag) { return false; } candidate = new LevelRuleCandidate(definition, 150 + num, index); return true; } if (string.Equals(text, context.PrefabName, StringComparison.OrdinalIgnoreCase)) { candidate = new LevelRuleCandidate(definition, 300 + num, index); return true; } List prefabs = definition.Prefabs; if (prefabs != null && prefabs.Count > 0) { bool flag3 = false; foreach (string item in prefabs) { if (string.Equals(item, context.PrefabName, StringComparison.OrdinalIgnoreCase)) { flag3 = true; break; } } if (!flag3) { return false; } candidate = new LevelRuleCandidate(definition, 200 + num, index); return true; } if ((context.IsEnforcer || !context.IsBoss || BossCanUsePresetBiomeRule(definition)) && MatchesBiome(text, context.Biome)) { candidate = new LevelRuleCandidate(definition, 100, index); return true; } return false; } private static float GetPerLevelMultiplier(Character character, float perLevel) { if (perLevel <= 0f) { return 1f; } int num = Math.Max(1, character.GetLevel()); return Mathf.Max(0f, 1f + (float)(num - 1) * perLevel); } private static bool IsGlobalTarget(string target) { return string.Equals(target, "global", StringComparison.OrdinalIgnoreCase); } private static bool IsBossTarget(string target) { return string.Equals(target, "boss", StringComparison.OrdinalIgnoreCase); } private static bool BossCanUsePresetBiomeRule(LevelDefinition definition) { if (definition.IsPreset) { ConfigEntry bossesFollowBiomeLevelPreset = CreatureManagerPlugin.BossesFollowBiomeLevelPreset; if (bossesFollowBiomeLevelPreset == null) { return false; } return bossesFollowBiomeLevelPreset.Value == CreatureManagerPlugin.Toggle.On; } return false; } private static float GetDistanceScalingMultiplier(float distance, List? scaling, int valueIndex) { if (scaling == null || scaling.Count <= valueIndex) { return 1f; } float num = Mathf.Max(0f, scaling[valueIndex]); if (num <= 0f) { return 1f; } float num2 = ((scaling.Count >= 3 && scaling[2] > 0f) ? scaling[2] : 1000f); int num3 = Mathf.FloorToInt(distance / num2); if (scaling.Count >= 4 && scaling[3] > 0f) { num3 = Math.Min(num3, Mathf.FloorToInt(scaling[3])); } return Mathf.Max(0f, 1f + (float)num3 * num); } private static float GetModifierDistanceScalingMultiplier(float distance, List? scaling) { if (scaling == null || scaling.Count != 3) { return 1f; } float num = Mathf.Max(0f, scaling[0]); float num2 = ((scaling[1] > 0f) ? scaling[1] : 1000f); int num3 = Mathf.FloorToInt(distance / num2); if (scaling[2] > 0f) { num3 = Math.Min(num3, Mathf.FloorToInt(scaling[2])); } return Mathf.Max(0f, 1f + (float)num3 * num); } private static void ApplyHealthMultiplier(Character character, float multiplier) { if (multiplier <= 0f || float.IsNaN(multiplier) || float.IsInfinity(multiplier)) { return; } float num = character.GetMaxHealthBase() * (float)Math.Max(1, character.GetLevel()) * multiplier; if (num <= 0f || float.IsNaN(num) || float.IsInfinity(num)) { return; } float maxHealth = character.GetMaxHealth(); if (!Mathf.Approximately(maxHealth, num)) { float health = character.GetHealth(); float num2 = Mathf.Max(0f, maxHealth - health); character.SetMaxHealth(num); if (maxHealth > 0f && health > 0f) { float num3 = Mathf.Min(1f, num); character.SetHealth(Mathf.Clamp(num - num2, num3, num)); } } } private static bool TrySelectLevel(List weights, string prefabName, out int level) { level = 1; if (weights.Count == 0) { CreatureManagerPlugin.Log.LogWarning((object)("Level rule for '" + prefabName + "' has no level weights. Use [weightLevel1, weightLevel2, ...].")); return false; } float num = 0f; int num2 = 1; for (int i = 0; i < weights.Count; i++) { if (!(weights[i] <= 0f)) { num += weights[i]; num2 = i + 1; } } if (num <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level rule for '" + prefabName + "' has no positive level weights.")); return false; } float num3 = Random.Range(0f, num); for (int j = 0; j < weights.Count; j++) { float num4 = Math.Max(0f, weights[j]); if (num3 < num4) { level = j + 1; return true; } num3 -= num4; } level = num2; return true; } private static Biome GetBiome(Vector3 position) { //IL_0023: 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_003d: 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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (WorldGenerator.instance == null) { return (Biome)0; } try { return WorldGenerator.instance.GetBiome(position); } catch (Exception ex) { CreatureManagerPlugin.Log.LogDebug((object)$"Failed to resolve biome for level rule at {position}: {ex.Message}"); return (Biome)0; } } private static float GetHorizontalDistance(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return Mathf.Sqrt(position.x * position.x + position.z * position.z); } private static string NormalizeBiomeName(string biomeName) { return new string((biomeName ?? "").Where((char character) => !char.IsWhiteSpace(character) && character != '_' && character != '-').Select(char.ToLowerInvariant).ToArray()); } private unsafe static bool MatchesBiome(string biomeName, Biome currentBiome) { //IL_001b: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) string text = NormalizeBiomeName(biomeName); if (text.Length == 0) { return false; } if (CreatureKarmaManager.TryResolveBiomeName(biomeName, out var biome)) { return (currentBiome & biome) > 0; } if (NormalizeBiomeName(((object)(*(Biome*)(¤tBiome))/*cast due to .constrained prefix*/).ToString()) == text) { return true; } if (CreatureKarmaManager.TryGetBiomeDisplayName(currentBiome, out string displayName)) { return NormalizeBiomeName(displayName) == text; } return false; } private static string GetPrefabName(GameObject gameObject) { string text = ((Object)gameObject).name; int num = text.IndexOf("(Clone)", StringComparison.Ordinal); if (num >= 0) { text = text.Substring(0, num); } return text.Trim(); } } internal sealed class CreatureLevelEffectsState : MonoBehaviour { private readonly Dictionary MaterialsByVisualState = new Dictionary(); private readonly Dictionary OriginalActiveStates = new Dictionary(); private bool Initialized; internal Vector3 OriginalLocalScale { get; private set; } = Vector3.one; internal Material? OriginalMainMaterial { get; private set; } internal static CreatureLevelEffectsState Get(LevelEffects levelEffects) { CreatureLevelEffectsState component = ((Component)levelEffects).GetComponent(); if (!((Object)(object)component != (Object)null)) { return ((Component)levelEffects).gameObject.AddComponent(); } return component; } internal void EnsureInitialized(LevelEffects levelEffects) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Initialized) { return; } Initialized = true; OriginalLocalScale = ((Component)levelEffects).transform.localScale; OriginalMainMaterial = GetFirstMaterial(levelEffects.m_mainRender); RecordOriginalActive(levelEffects.m_baseEnableObject); foreach (LevelSetup levelSetup in levelEffects.m_levelSetups) { RecordOriginalActive(levelSetup?.m_enableObject); } } internal bool GetOriginalActive(GameObject gameObject, bool fallback) { if (!OriginalActiveStates.TryGetValue(gameObject, out var value)) { return fallback; } return value; } internal Material GetOrCreateMaterial(int visualState, LevelSetup setup) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (MaterialsByVisualState.TryGetValue(visualState, out Material value) && (Object)(object)value != (Object)null) { return value; } value = (((Object)(object)OriginalMainMaterial != (Object)null) ? new Material(OriginalMainMaterial) : new Material(Shader.Find("Standard"))); CreatureLevelManager.ApplyColorProperties(value, setup); MaterialsByVisualState[visualState] = value; return value; } private void RecordOriginalActive(GameObject? gameObject) { if ((Object)(object)gameObject != (Object)null && !OriginalActiveStates.ContainsKey(gameObject)) { OriginalActiveStates[gameObject] = gameObject.activeSelf; } } private static Material? GetFirstMaterial(Renderer? renderer) { if ((Object)(object)renderer == (Object)null) { return null; } Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials.Length == 0) { return null; } return sharedMaterials[0]; } private void OnDestroy() { foreach (Material value in MaterialsByVisualState.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } MaterialsByVisualState.Clear(); OriginalActiveStates.Clear(); } } internal sealed class CreatureCharacterScaleState : MonoBehaviour { private bool Initialized { get; set; } internal Vector3 OriginalLocalScale { get; private set; } = Vector3.one; internal static CreatureCharacterScaleState Get(Character character) { CreatureCharacterScaleState component = ((Component)character).GetComponent(); if (!((Object)(object)component != (Object)null)) { return ((Component)character).gameObject.AddComponent(); } return component; } internal static void RestoreIfPresent(Character character) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) CreatureCharacterScaleState component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && component.Initialized) { ((Component)character).transform.localScale = component.OriginalLocalScale; } } internal void EnsureInitialized(Character character) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!Initialized) { OriginalLocalScale = ((Component)character).transform.localScale; Initialized = true; } } } internal static class CreatureLocalization { internal static string Localize(string key, string fallback) { string text = (key.StartsWith("$", StringComparison.Ordinal) ? key.Substring(1) : key); string text2 = "$" + text; Localization instance = Localization.instance; if (instance == null) { return fallback; } try { string text3 = instance.Localize(text2); return (string.IsNullOrEmpty(text3) || string.Equals(text3, text2, StringComparison.Ordinal) || string.Equals(text3, text, StringComparison.Ordinal)) ? fallback : text3; } catch { return fallback; } } internal static string LocalizeText(string text) { if (string.IsNullOrEmpty(text) || Localization.instance == null) { return text; } try { return Localization.instance.Localize(text); } catch { return text; } } internal static string Format(string key, string fallback, params (string Name, string Value)[] placeholders) { string text = Localize(key, fallback); for (int i = 0; i < placeholders.Length; i++) { (string Name, string Value) tuple = placeholders[i]; string item = tuple.Name; string item2 = tuple.Value; text = text.Replace("{" + item + "}", item2); } return text; } } internal static class CreatureModifierManager { [Flags] private enum ModifierMask : long { None = 0L, Armored = 1L, Enraged = 2L, Deathward = 4L, Swift = 8L, Regenerating = 0x10L, Vampiric = 0x20L, Fire = 0x40L, Frost = 0x80L, Lightning = 0x100L, ToxicDeath = 0x200L, ArmorPiercing = 0x400L, Staggering = 0x800L, AttackSpeed = 0x1000L, Exposed = 0x2000L, Weakened = 0x4000L, Withered = 0x8000L, Reflection = 0x10000L, Vortex = 0x20000L, Crippling = 0x40000L, Disruptive = 0x80000L, Adaptive = 0x100000L, Omen = 0x200000L, Reaping = 0x400000L, Spirit = 0x800000L, Blink = 0x1000000L, Knockback = 0x2000000L, Unflinching = 0x4000000L, AdrenalineDrain = 0x8000000L, Corrosive = 0x10000000L, Undodgeable = 0x20000000L, Chameleon = 0x40000000L, Blamer = 0x80000000L } private enum ModifierGroup { Offense, Defense, Affliction, Special } internal enum ChameleonDamageType { None, Blunt, Pierce, Slash, Fire, Poison, Lightning, Frost, Spirit } private sealed class PlayerDebuffPowers { internal float Exposed; internal float Weakened; internal float Withered; internal float Crippling; internal float CripplingJump; internal float Disruptive; internal float DisruptiveEitr; internal float Corrosive; } internal sealed class VortexProjectileImpactContext { internal Projectile Projectile { get; } internal Character Target { get; } internal EffectList OriginalHitEffects { get; } internal bool DecisionWritten { get; set; } internal bool HitEffectsSuppressed { get; set; } internal VortexProjectileImpactContext(Projectile projectile, Character target, EffectList hitEffects) { Projectile = projectile; Target = target; OriginalHitEffects = hitEffects; } } internal struct VortexProjectileImpactScopeState { internal VortexProjectileImpactContext? Current { get; } internal VortexProjectileImpactContext? Previous { get; } internal bool Changed { get; } internal VortexProjectileImpactScopeState(VortexProjectileImpactContext? current, VortexProjectileImpactContext? previous, bool changed) { Current = current; Previous = previous; Changed = changed; } } internal struct VortexDirectProjectileDamageState { internal HitData? Hit { get; } internal HitType OriginalHitType { get; } internal bool IsActive => Hit != null; internal VortexDirectProjectileDamageState(HitData hit, HitType originalHitType) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Hit = hit; OriginalHitType = originalHitType; } } private sealed class PlayerRuntimeDebuffs { internal bool MovementApplied; internal float MovementPower; internal float JumpPower; internal float OriginalCrouchSpeed; internal float OriginalWalkSpeed; internal float OriginalSpeed; internal float OriginalRunSpeed; internal float OriginalJumpForce; internal float OriginalJumpForceForward; internal float AppliedCrouchSpeed; internal float AppliedWalkSpeed; internal float AppliedSpeed; internal float AppliedRunSpeed; internal float AppliedJumpForce; internal float AppliedJumpForceForward; internal float LastStamina = -1f; internal float LastEitr = -1f; internal readonly Dictionary DurabilitySnapshots = new Dictionary(); internal readonly List CurrentDurabilityItems = new List(); internal readonly List RemovedDurabilityItems = new List(); } private sealed class PassiveModifierSchedule { internal float NextRegeneration; internal float NextChameleon; internal float NextBlamer; } private enum BlamerKarmaAddResult { Failed, Added, Saturated, Pending } private sealed class PendingBlamerKarmaRequest { internal long RequestId; internal float SentAt; internal ZDOID TargetId; } private sealed class ServerBlamerKarmaState { internal float Accumulated; internal float NextAllowedTime; internal long RequestOwner; internal long LastRequestId; internal bool HasCachedResponse; internal BlamerKarmaAddResult LastResult; internal float LastResponseAccumulated; } private sealed class ServerReflectionRequestState { internal bool RequestOwnerInitialized; internal long RequestOwner; internal long LastRequestId; internal float LastObservedHealth = float.NaN; internal float UnclaimedDamage; internal float UnclaimedDamageUntil; } private sealed class ReapingPeerRateState { internal double WindowStart; internal int RequestCount; } private readonly struct ReflectionPendingRequestKey : IEquatable { internal ZDOID Source { get; } internal ZDOID Target { get; } internal long RequestId { get; } internal ReflectionPendingRequestKey(ZDOID source, ZDOID target, long requestId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0009: Unknown result type (might be due to invalid IL or missing references) Source = source; Target = target; RequestId = requestId; } public bool Equals(ReflectionPendingRequestKey other) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ZDOID val = Source; if (((ZDOID)(ref val)).Equals(other.Source)) { val = Target; if (((ZDOID)(ref val)).Equals(other.Target)) { return RequestId == other.RequestId; } } return false; } public override bool Equals(object? obj) { if (obj is ReflectionPendingRequestKey other) { return Equals(other); } return false; } public override int GetHashCode() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) return (((((object)Source/*cast due to .constrained prefix*/).GetHashCode() * 397) ^ ((object)Target/*cast due to .constrained prefix*/).GetHashCode()) * 397) ^ RequestId.GetHashCode(); } } private readonly struct ReflectionRequestKey : IEquatable { internal ZDOID Source { get; } internal ZDOID Target { get; } internal ReflectionRequestKey(ZDOID source, ZDOID target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0009: Unknown result type (might be due to invalid IL or missing references) Source = source; Target = target; } public bool Equals(ReflectionRequestKey other) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ZDOID val = Source; if (((ZDOID)(ref val)).Equals(other.Source)) { val = Target; return ((ZDOID)(ref val)).Equals(other.Target); } return false; } public override bool Equals(object? obj) { if (obj is ReflectionRequestKey other) { return Equals(other); } return false; } public override int GetHashCode() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) return (((object)Source/*cast due to .constrained prefix*/).GetHashCode() * 397) ^ ((object)Target/*cast due to .constrained prefix*/).GetHashCode(); } } private readonly struct ReapingRequestKey : IEquatable { internal ZDOID Reaper { get; } internal ZDOID Dead { get; } internal ReapingRequestKey(ZDOID reaper, ZDOID dead) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0009: Unknown result type (might be due to invalid IL or missing references) Reaper = reaper; Dead = dead; } public bool Equals(ReapingRequestKey other) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ZDOID val = Reaper; if (((ZDOID)(ref val)).Equals(other.Reaper)) { val = Dead; return ((ZDOID)(ref val)).Equals(other.Dead); } return false; } public override bool Equals(object? obj) { if (obj is ReapingRequestKey other) { return Equals(other); } return false; } public override int GetHashCode() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) return (((object)Reaper/*cast due to .constrained prefix*/).GetHashCode() * 397) ^ ((object)Dead/*cast due to .constrained prefix*/).GetHashCode(); } } private sealed class ModifierHotPathState { internal Character Character; internal ModifierMask Mask; internal float SwiftFactor = 1f; internal float AttackSpeedFactor = 1f; internal bool UndodgeableEffectActive; internal float UndodgeableDamageReduction; internal int EligibilityFrame = -1; internal bool EffectsAllowed; internal float NextValidationTime; } private sealed class ModifierHudRefreshState { internal Character Character; internal GameObject? HudGui; internal float NextRefreshTime; internal bool Result; internal ModifierMask Visible; internal float ArmoredReduction; internal float EnragedBonus; internal bool BlamerActive; } private sealed class HudIconState { internal bool Initialized; internal bool SlotsInitialized; internal ModifierMask Visible; internal int Armored; internal int Enraged; internal CreatureManagerPlugin.ModifierIconLayout Layout; internal readonly Image?[] Slots = (Image?[])(object)new Image[4]; internal bool Matches(ModifierMask visible, int armored, int enraged, CreatureManagerPlugin.ModifierIconLayout layout) { if (Initialized && Visible == visible && Armored == armored && Enraged == enraged) { return Layout == layout; } return false; } internal void Set(ModifierMask visible, int armored, int enraged, CreatureManagerPlugin.ModifierIconLayout layout) { Initialized = true; Visible = visible; Armored = armored; Enraged = enraged; Layout = layout; } } private sealed class BlamerAlertIconState { internal Image? Icon; internal bool VisibilityInitialized; internal bool Visible; } private sealed class HudContentState { internal bool LevelContentSearched; internal RectTransform? LevelContent; internal bool BossContentSearched; internal RectTransform? BossContent; internal bool ResistanceTextSearched; internal TextMeshProUGUI? ResistanceText; } private readonly struct ReapingSettings { internal float HealPerKill { get; } internal int HealMaxActivations { get; } internal float MaxHealthPerKill { get; } internal float MaxHealthCap { get; } internal float DamagePerKill { get; } internal float DamageCap { get; } internal float ScalePerKill { get; } internal float ScaleCap { get; } internal bool HasAnyGain { get { if ((!(HealPerKill > 0f) || HealMaxActivations <= 0) && (!(MaxHealthPerKill > 0f) || !(MaxHealthCap > 0f)) && (!(DamagePerKill > 0f) || !(DamageCap > 0f))) { if (ScalePerKill > 0f) { return ScaleCap > 0f; } return false; } return true; } } internal ReapingSettings(float healPerKill, int healMaxActivations, float maxHealthPerKill, float maxHealthCap, float damagePerKill, float damageCap, float scalePerKill, float scaleCap) { HealPerKill = Mathf.Max(0f, healPerKill); HealMaxActivations = Math.Max(1, healMaxActivations); MaxHealthPerKill = Mathf.Max(0f, maxHealthPerKill); MaxHealthCap = Mathf.Max(0f, maxHealthCap); DamagePerKill = Mathf.Max(0f, damagePerKill); DamageCap = Mathf.Max(0f, damageCap); ScalePerKill = Mathf.Max(0f, scalePerKill); ScaleCap = Mathf.Max(0f, scaleCap); } } internal sealed class BlinkAttackAiOverrideState { private readonly struct Entry { internal SharedData Shared { get; } internal float Range { get; } internal float MaxAngle { get; } internal Entry(SharedData shared, float range, float maxAngle) { Shared = shared; Range = range; MaxAngle = maxAngle; } } private readonly List _entries = new List(); internal int Count => _entries.Count; internal void Override(SharedData shared, float maxRange, float maxAngle) { foreach (Entry entry in _entries) { if (entry.Shared == shared) { return; } } float num = Mathf.Max(shared.m_aiAttackRange, maxRange); float num2 = Mathf.Max(shared.m_aiAttackMaxAngle, maxAngle); if (!Mathf.Approximately(num, shared.m_aiAttackRange) || !Mathf.Approximately(num2, shared.m_aiAttackMaxAngle)) { _entries.Add(new Entry(shared, shared.m_aiAttackRange, shared.m_aiAttackMaxAngle)); shared.m_aiAttackRange = num; shared.m_aiAttackMaxAngle = num2; } } internal void Restore() { for (int num = _entries.Count - 1; num >= 0; num--) { Entry entry = _entries[num]; entry.Shared.m_aiAttackRange = entry.Range; entry.Shared.m_aiAttackMaxAngle = entry.MaxAngle; } _entries.Clear(); } } internal enum DelayedDamageAttributionKind { Unknown, Unattributed, Exact, Ambiguous } internal readonly struct DelayedDamageAttribution { internal DelayedDamageAttributionKind Kind { get; } internal ZDOID Source { get; } internal bool SourceWasPlayer { get; } internal bool IsExact { get { //IL_000a: 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) if (Kind == DelayedDamageAttributionKind.Exact) { return Source != ZDOID.None; } return false; } } internal DelayedDamageAttribution(DelayedDamageAttributionKind kind, ZDOID source, bool sourceWasPlayer = false) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Kind = kind; Source = source; SourceWasPlayer = sourceWasPlayer; } internal static DelayedDamageAttribution FromSource(ZDOID source, bool sourceWasPlayer) { //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_0017: 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) if (!(source == ZDOID.None)) { return new DelayedDamageAttribution(DelayedDamageAttributionKind.Exact, source, sourceWasPlayer); } return new DelayedDamageAttribution(DelayedDamageAttributionKind.Unattributed, ZDOID.None); } } private sealed class DelayedDamageSourceLedger { internal Character Target; internal SE_Poison? PoisonStatus; internal DelayedDamageAttribution Poison; internal SE_Burning? FireStatus; internal DelayedDamageAttribution Fire; internal SE_Burning? SpiritStatus; internal DelayedDamageAttribution Spirit; } private readonly struct DelayedDamageDeathCredit { internal Character Target { get; } internal ZDOID Source { get; } internal bool SourceWasPlayer { get; } internal DelayedDamageDeathCredit(Character target, ZDOID source, bool sourceWasPlayer) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Target = target; Source = source; SourceWasPlayer = sourceWasPlayer; } } internal enum DeathAttributionKind { None, Direct, Delayed } internal readonly struct FinalDeathAttribution { internal ZDOID Source { get; } internal DeathAttributionKind Kind { get; } internal bool SourceWasPlayer { get; } internal Character? ResolvedSource { get; } internal bool HasSource { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (Source != ZDOID.None) { return Kind != DeathAttributionKind.None; } return false; } } internal FinalDeathAttribution(ZDOID source, DeathAttributionKind kind, bool sourceWasPlayer, Character? resolvedSource) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Source = source; Kind = kind; SourceWasPlayer = sourceWasPlayer; ResolvedSource = resolvedSource; } } internal struct RpcDamageContext { internal Character? Target; internal HitData? Hit; internal float ArmorPiercing; internal float ResolvedDelayedDamage; internal bool DelayedDamageCaptured; } internal struct DelayedDamageTickContext { internal Character? Target; internal HitType HitType; internal DelayedDamageAttribution Attribution; } internal readonly struct RpcDamageScopeState { private readonly RpcDamageContext _previous; internal ChameleonDamageOverrideState Chameleon { get; } internal RpcDamageContext Previous => _previous; internal bool Changed { get; } internal RpcDamageScopeState(RpcDamageContext previous, ChameleonDamageOverrideState chameleon) { _previous = previous; Chameleon = chameleon; Changed = true; } } internal readonly struct DelayedDamageTickScopeState { private readonly DelayedDamageTickContext _previous; internal DelayedDamageTickContext Previous => _previous; internal bool Changed { get; } internal DelayedDamageTickScopeState(DelayedDamageTickContext previous) { _previous = previous; Changed = true; } } internal readonly struct ChameleonDamageOverrideState { internal Character? Target { get; } internal DamageModifiers Original { get; } internal ChameleonDamageType DamageType { get; } internal bool IsActive => (Object)(object)Target != (Object)null; internal ChameleonDamageOverrideState(Character target, DamageModifiers original, ChameleonDamageType damageType) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Target = target; Original = original; DamageType = damageType; } } internal enum UndodgeableSourcePath { None, Melee, Area, ProjectileTarget, AoeTarget } internal struct UndodgeableSourceContext { internal Character? Attacker { get; } internal Player? IntendedTarget { get; } internal UndodgeableSourcePath Path { get; } internal bool SourceDodgeable { get; } internal bool IsActive { get { if ((Object)(object)Attacker != (Object)null) { return SourceDodgeable; } return false; } } internal UndodgeableSourceContext(Character attacker, Player? intendedTarget, UndodgeableSourcePath path, bool sourceDodgeable) { Attacker = attacker; IntendedTarget = intendedTarget; Path = path; SourceDodgeable = sourceDodgeable; } } internal readonly struct UndodgeableSourceScopeState { internal UndodgeableSourceContext Previous { get; } internal bool Changed { get; } internal UndodgeableSourceScopeState(UndodgeableSourceContext previous, bool changed) { Previous = previous; Changed = changed; } } internal struct UndodgeableScopeState { internal UndodgeableSourceScopeState Source { get; } internal bool IsActive => Source.Changed; internal UndodgeableScopeState(UndodgeableSourceScopeState source) { Source = source; } } internal struct UndodgeableDamageScopeState { internal UndodgeableSourceContext Restore { get; } internal bool Changed { get; } internal UndodgeableDamageScopeState(UndodgeableSourceContext restore, bool changed) { Restore = restore; Changed = changed; } } internal readonly struct BlamerFleeOverrideState { internal MonsterAI? MonsterAI { get; } internal float FleeIfLowHealth { get; } internal float FleeTimeSinceHurt { get; } internal bool IsActive => (Object)(object)MonsterAI != (Object)null; internal BlamerFleeOverrideState(MonsterAI monsterAI, float fleeIfLowHealth, float fleeTimeSinceHurt) { MonsterAI = monsterAI; FleeIfLowHealth = fleeIfLowHealth; FleeTimeSinceHurt = fleeTimeSinceHurt; } } internal readonly struct DirectDamageState { internal Character? VampiricAttacker { get; } internal float VampiricPower { get; } internal Character? ReflectionAttacker { get; } internal float ReflectionPower { get; } internal float HealthBefore { get; } internal bool HasVampiric { get { if ((Object)(object)VampiricAttacker != (Object)null) { return VampiricPower > 0f; } return false; } } internal bool HasReflection { get { if ((Object)(object)ReflectionAttacker != (Object)null) { return ReflectionPower > 0f; } return false; } } internal bool IsValid { get { if (HealthBefore > 0f) { if (!HasVampiric) { return HasReflection; } return true; } return false; } } internal DirectDamageState(Character? vampiricAttacker, float vampiricPower, Character? reflectionAttacker, float reflectionPower, float healthBefore) { VampiricAttacker = vampiricAttacker; VampiricPower = vampiricPower; ReflectionAttacker = reflectionAttacker; ReflectionPower = reflectionPower; HealthBefore = healthBefore; } } internal readonly struct ApplyDamageState { internal DirectDamageState DirectDamage { get; } internal bool EligibleAtEntry { get; } internal float HealthBefore { get; } internal ApplyDamageState(DirectDamageState directDamage, bool eligibleAtEntry, float healthBefore) { DirectDamage = directDamage; EligibleAtEntry = eligibleAtEntry; HealthBefore = healthBefore; } } private readonly struct VampiricDamageContext { internal Character Attacker { get; } internal Character Target { get; } internal float Power { get; } internal VampiricDamageContext(Character attacker, Character target, float power) { Attacker = attacker; Target = target; Power = power; } } private readonly struct ReflectionDamageContext { internal Character Attacker { get; } internal Character Target { get; } internal float Power { get; } internal ReflectionDamageContext(Character attacker, Character target, float power) { Attacker = attacker; Target = target; Power = power; } } private readonly struct KnockbackHitContext { internal Character Attacker { get; } internal int AttackerId { get; } internal float Cooldown { get; } internal KnockbackHitContext(Character attacker, float cooldown) { Attacker = attacker; AttackerId = ((Object)attacker).GetInstanceID(); Cooldown = Mathf.Max(0f, cooldown); } } internal readonly struct BlockAttackModifierState { internal float PreviousStaggerMultiplier { get; } internal Character? UnflinchingAttacker { get; } internal bool OriginalStaggerWhenBlocked { get; } internal BlockAttackModifierState(float previousStaggerMultiplier, Character? unflinchingAttacker, bool originalStaggerWhenBlocked) { PreviousStaggerMultiplier = previousStaggerMultiplier; UnflinchingAttacker = unflinchingAttacker; OriginalStaggerWhenBlocked = originalStaggerWhenBlocked; } } private sealed class ModifierSpec { private readonly Func _powerClamp; private readonly Func _description; internal ModifierGroup Group { get; } internal string Key { get; } internal string DisplayName { get; } internal ModifierMask Mask { get; } internal string PowerKey { get; } internal float DefaultPower { get; } internal Func Sprite { get; } internal string? ProcChanceKey { get; } internal ModifierSpec(ModifierGroup group, string key, string displayName, ModifierMask mask, string powerKey, float defaultPower, Func sprite, Func description, Func? powerClamp = null, string? procChanceKey = null) { Group = group; Key = key; DisplayName = displayName; Mask = mask; PowerKey = powerKey; DefaultPower = defaultPower; Sprite = sprite; ProcChanceKey = procChanceKey; _powerClamp = powerClamp ?? ((Func)((float? value) => ClampPower(value, defaultPower))); _description = description; } internal float ResolvePower(float? configuredPower) { return _powerClamp(configuredPower); } internal float? GetChance(ModifierChanceDefinition chances) { return chances.Get(Key); } internal void SetChance(ModifierChanceDefinition chances, float chance) { chances.Set(Key, chance); } internal float? GetPower(ModifierPowerDefinition powers) { return powers.Get(Key); } internal void SetPower(ModifierPowerDefinition powers, float power) { powers.Set(Key, power); } internal string Describe(float power) { return _description(power); } } private enum AdaptiveDamageType { None, Physical, Fire, Frost, Lightning, Poison, Spirit, Blunt, Slash, Pierce, Chop, Pickaxe } private readonly struct DamageSnapshot { private readonly float Damage; private readonly float Blunt; private readonly float Slash; private readonly float Pierce; private readonly float Chop; private readonly float Pickaxe; private readonly float Fire; private readonly float Frost; private readonly float Lightning; private readonly float Poison; private readonly float Spirit; internal float Total => Damage + Blunt + Slash + Pierce + Chop + Pickaxe + Fire + Frost + Lightning + Poison + Spirit; internal float Physical => Damage + Blunt + Slash + Pierce + Chop + Pickaxe; private DamageSnapshot(DamageTypes damage) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) Damage = damage.m_damage; Blunt = damage.m_blunt; Slash = damage.m_slash; Pierce = damage.m_pierce; Chop = damage.m_chop; Pickaxe = damage.m_pickaxe; Fire = damage.m_fire; Frost = damage.m_frost; Lightning = damage.m_lightning; Poison = damage.m_poison; Spirit = damage.m_spirit; } internal static DamageSnapshot From(DamageTypes damage) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return new DamageSnapshot(damage); } internal AdaptiveDamageType DominantType() { AdaptiveDamageType type = AdaptiveDamageType.None; float value = 0f; Consider(Damage, AdaptiveDamageType.Physical, ref value, ref type); Consider(Blunt, AdaptiveDamageType.Blunt, ref value, ref type); Consider(Slash, AdaptiveDamageType.Slash, ref value, ref type); Consider(Pierce, AdaptiveDamageType.Pierce, ref value, ref type); Consider(Chop, AdaptiveDamageType.Chop, ref value, ref type); Consider(Pickaxe, AdaptiveDamageType.Pickaxe, ref value, ref type); Consider(Fire, AdaptiveDamageType.Fire, ref value, ref type); Consider(Frost, AdaptiveDamageType.Frost, ref value, ref type); Consider(Lightning, AdaptiveDamageType.Lightning, ref value, ref type); Consider(Poison, AdaptiveDamageType.Poison, ref value, ref type); Consider(Spirit, AdaptiveDamageType.Spirit, ref value, ref type); return type; } private static void Consider(float candidate, AdaptiveDamageType candidateType, ref float value, ref AdaptiveDamageType type) { if (candidate > value) { value = candidate; type = candidateType; } } } private const string AppliedKey = "CreatureManager_ModifiersApplied"; private const string Mask64Key = "CreatureManager_ModifierMask64"; private const string ArmoredReductionKey = "CreatureManager_ArmoredReduction"; private const string EnragedBonusKey = "CreatureManager_EnragedBonus"; private const string DeathwardHealthKey = "CreatureManager_DeathwardHealth"; private const string DeathwardCooldownKey = "CreatureManager_DeathwardCooldown"; private const string DeathwardMaxActivationsKey = "CreatureManager_DeathwardMaxActivations"; private const string DeathwardActivationCountKey = "CreatureManager_DeathwardActivationCount"; private const string DeathwardNextReadyTimeKey = "CreatureManager_DeathwardNextReadyTime"; private const string SwiftPowerKey = "CreatureManager_SwiftPower"; private const string RegeneratingPowerKey = "CreatureManager_RegeneratingPower"; private const string VampiricPowerKey = "CreatureManager_VampiricPower"; private const string FirePowerKey = "CreatureManager_FirePower"; private const string FrostPowerKey = "CreatureManager_FrostPower"; private const string LightningPowerKey = "CreatureManager_LightningPower"; private const string SpiritPowerKey = "CreatureManager_SpiritPower"; private const string ToxicDeathPowerKey = "CreatureManager_ToxicDeathPower"; private const string ArmorPiercingPowerKey = "CreatureManager_ArmorPiercingPower"; private const string StaggeringPowerKey = "CreatureManager_StaggeringPower"; private const string UndodgeableDamageReductionKey = "CreatureManager_UndodgeableDamageReduction"; private const string AttackSpeedPowerKey = "CreatureManager_AttackSpeedPower"; private const string ExposedChanceKey = "CreatureManager_ExposedChance"; private const string ExposedPowerKey = "CreatureManager_ExposedPower"; private const string ExposedDurationKey = "CreatureManager_ExposedDuration"; private const string WeakenedChanceKey = "CreatureManager_WeakenedChance"; private const string WeakenedPowerKey = "CreatureManager_WeakenedPower"; private const string WeakenedDurationKey = "CreatureManager_WeakenedDuration"; private const string WitheredChanceKey = "CreatureManager_WitheredChance"; private const string WitheredPowerKey = "CreatureManager_WitheredPower"; private const string WitheredDurationKey = "CreatureManager_WitheredDuration"; private const string ReflectionPowerKey = "CreatureManager_ReflectionPower"; private const string ReflectionChanceKey = "CreatureManager_ReflectionChance"; private const string VortexPowerKey = "CreatureManager_VortexPower"; private const string CripplingChanceKey = "CreatureManager_CripplingChance"; private const string CripplingPowerKey = "CreatureManager_CripplingPower"; private const string CripplingJumpPowerKey = "CreatureManager_CripplingJumpPower"; private const string CripplingDurationKey = "CreatureManager_CripplingDuration"; private const string DisruptiveChanceKey = "CreatureManager_DisruptiveChance"; private const string DisruptivePowerKey = "CreatureManager_DisruptivePower"; private const string DisruptiveEitrPowerKey = "CreatureManager_DisruptiveEitrPower"; private const string DisruptiveDurationKey = "CreatureManager_DisruptiveDuration"; private const string AdrenalineDrainChanceKey = "CreatureManager_AdrenalineDrainChance"; private const string AdrenalineDrainPowerKey = "CreatureManager_AdrenalineDrainPower"; private const string AdrenalineDrainGainReductionKey = "CreatureManager_AdrenalineDrainGainReduction"; private const string AdrenalineDrainDurationKey = "CreatureManager_AdrenalineDrainDuration"; private const string CorrosiveChanceKey = "CreatureManager_CorrosiveChance"; private const string CorrosivePowerKey = "CreatureManager_CorrosivePower"; private const string CorrosiveDurationKey = "CreatureManager_CorrosiveDuration"; private const string ToxicDeathRadiusKey = "CreatureManager_ToxicDeathRadius"; private const string ToxicDeathTriggerEffectKey = "CreatureManager_ToxicDeathTriggerEffect"; private const string AdaptivePowerKey = "CreatureManager_AdaptivePower"; private const string UnflinchingPowerKey = "CreatureManager_UnflinchingPower"; private const string ChameleonIntervalKey = "CreatureManager_ChameleonInterval"; private const string ChameleonTypeKey = "CreatureManager_ChameleonType"; private const string OmenPowerKey = "CreatureManager_OmenPower"; private const string ReapingPowerKey = "CreatureManager_ReapingPower"; private const string ReapingHealMaxActivationsKey = "CreatureManager_ReapingHealMaxActivations"; private const string ReapingMaxHealthPerKillKey = "CreatureManager_ReapingMaxHealthPerKill"; private const string ReapingMaxHealthCapKey = "CreatureManager_ReapingMaxHealthCap"; private const string ReapingDamagePerKillKey = "CreatureManager_ReapingDamagePerKill"; private const string ReapingDamageCapKey = "CreatureManager_ReapingDamageCap"; private const string ReapingScalePerKillKey = "CreatureManager_ReapingScalePerKill"; private const string ReapingScaleCapKey = "CreatureManager_ReapingScaleCap"; private const string BlinkPowerKey = "CreatureManager_BlinkPower"; private const string BlinkCooldownKey = "CreatureManager_BlinkCooldown"; private const string BlinkMaxRangeKey = "CreatureManager_BlinkMaxRange"; private const string BlinkStartEffectKey = "CreatureManager_BlinkStartEffect"; private const string BlinkNextTimeKey = "CreatureManager_BlinkNextTime"; private const string BlinkAlertStartTimeKey = "CreatureManager_BlinkAlertStartTime"; private const string KnockbackPowerKey = "CreatureManager_KnockbackPower"; private const string KnockbackCooldownKey = "CreatureManager_KnockbackCooldown"; private const string KnockbackNextReadyTimeKey = "CreatureManager_KnockbackNextReadyTime"; private const string BlamerKarmaPerSecondKey = "CreatureManager_BlamerKarmaPerSecond"; private const string BlamerMaxKarmaGainKey = "CreatureManager_BlamerMaxKarmaGain"; private const string BlamerFleeHealthRatioKey = "CreatureManager_BlamerFleeHealthRatio"; private const string BlamerAccumulatedKarmaKey = "CreatureManager_BlamerAccumulatedKarma"; private const string BlamerActiveKey = "CreatureManager_BlamerActive"; private const string KarmaEnforcerKey = "CreatureManager_KarmaEnforcer"; private const string KarmaEnforcerSummonedKey = "CreatureManager_KarmaEnforcerSummoned"; private const string LevelHealthAppliedKey = "CreatureManager_LevelHealthApplied"; private const string LevelHealthMultiplierKey = "CreatureManager_LevelHealthMultiplier"; private const string ReapingBaseMaxHealthKey = "CreatureManager_ReapingBaseMaxHealth"; private const string ReapingHealActivationCountKey = "CreatureManager_ReapingHealActivationCount"; private const string ReapingBonusHealthKey = "CreatureManager_ReapingBonusHealth"; private const string ReapingDamageBonusKey = "CreatureManager_ReapingDamageBonus"; private const string ReapingScaleBonusKey = "CreatureManager_ReapingScaleBonus"; private const string ReapingBaseScaleKey = "CreatureManager_ReapingBaseScale"; private const string AdaptiveTypeKey = "CreatureManager_AdaptiveType"; private const string AdaptiveUntilKey = "CreatureManager_AdaptiveUntil"; private const string PlayerExposedPowerKey = "CreatureManager_PlayerExposedPower"; private const string PlayerExposedUntilKey = "CreatureManager_PlayerExposedUntil"; private const string PlayerWeakenedPowerKey = "CreatureManager_PlayerWeakenedPower"; private const string PlayerWeakenedUntilKey = "CreatureManager_PlayerWeakenedUntil"; private const string PlayerWitheredPowerKey = "CreatureManager_PlayerWitheredPower"; private const string PlayerWitheredUntilKey = "CreatureManager_PlayerWitheredUntil"; private const string PlayerCripplingPowerKey = "CreatureManager_PlayerCripplingPower"; private const string PlayerCripplingJumpPowerKey = "CreatureManager_PlayerCripplingJumpPower"; private const string PlayerCripplingUntilKey = "CreatureManager_PlayerCripplingUntil"; private const string PlayerDisruptivePowerKey = "CreatureManager_PlayerDisruptivePower"; private const string PlayerDisruptiveEitrPowerKey = "CreatureManager_PlayerDisruptiveEitrPower"; private const string PlayerDisruptiveUntilKey = "CreatureManager_PlayerDisruptiveUntil"; private const string PlayerCorrosivePowerKey = "CreatureManager_PlayerCorrosivePower"; private const string PlayerCorrosiveUntilKey = "CreatureManager_PlayerCorrosiveUntil"; private const string LevelContentName = "CreatureManager_LevelContent"; private const string StarGroupName = "CreatureManager_StarGroup"; private const string StarSlotNamePrefix = "CreatureManager_StarSlot"; private const string StarNumberName = "CreatureManager_StarNumber"; private const string IconContainerName = "CreatureManager_ModifierIcons"; private const string OffenseIconSlotName = "CreatureManager_ModifierSlot_Offense"; private const string DefenseIconSlotName = "CreatureManager_ModifierSlot_Defense"; private const string AfflictionIconSlotName = "CreatureManager_ModifierSlot_Affliction"; private const string SpecialIconSlotName = "CreatureManager_ModifierSlot_Special"; private const string BlamerActiveIconName = "CreatureManager_BlamerActiveIcon"; private const string BossLevelContentName = "CreatureManager_BossLevelContent"; private const string ResistanceTextName = "CreatureManager_ResistanceText"; private const string ExposedStatusName = "CreatureManager_ExposedStatus"; private const string WeakenedStatusName = "CreatureManager_WeakenedStatus"; private const string WitheredStatusName = "CreatureManager_WitheredStatus"; private const string CripplingStatusName = "CreatureManager_CripplingStatus"; private const string DisruptiveStatusName = "CreatureManager_DisruptiveStatus"; private const string AdrenalineDrainStatusName = "CreatureManager_AdrenalineDrainStatus"; private const string CorrosiveStatusName = "CreatureManager_CorrosiveStatus"; private const string ReflectionDamageRequestRpc = "CreatureManager_RequestReflectionDamage"; private const string ReflectionDamageRpc = "CreatureManager_ReflectionDamage"; private const string ReflectionEffectRpc = "CreatureManager_ReflectionEffect"; private const string VortexHitEffectRpc = "CreatureManager_VortexHitEffect"; private const string VortexHitEffectRequestRpc = "CreatureManager_VortexHitEffectRequest"; private const string BlinkEffectRpc = "CreatureManager_BlinkEffect"; private const string DeathwardEffectRpc = "CreatureManager_DeathwardEffect"; private const string ReapingFeedbackRpc = "CreatureManager_ReapingFeedback"; private const string ReapingDirectKillRequestRpc = "CreatureManager_RequestReapingDirectKill"; private const string ReapingDirectKillRpc = "CreatureManager_ReapingDirectKill"; private const string ReapingRespawnRequestRpc = "CreatureManager_ReapingRespawn"; private const string KnockbackCooldownRequestRpc = "CreatureManager_RequestKnockbackCooldown"; private const string KnockbackCooldownRpc = "CreatureManager_CommitKnockbackCooldown"; private const string BlamerKarmaRequestRpc = "CreatureManager_BlamerKarmaRequest"; private const string BlamerKarmaResponseRpc = "CreatureManager_BlamerKarmaResponse"; private const string DeathwardTriggerEffectPrefab = "fx_StaffShield_Break"; private const string ReapingTriggerEffectPrefab = "fx_tentaroot_death"; private const float ArmoredDefaultPower = 0.35f; private const float EnragedDefaultPower = 0.15f; private const float DeathwardDefaultPower = 0.2f; private const float DeathwardDefaultCooldown = 10f; private const int DeathwardDefaultMaxActivations = 3; private const float SwiftDefaultPower = 0.25f; private const float RegeneratingDefaultPower = 0.003f; private const float VampiricDefaultPower = 0.3f; private const float ElementalDefaultPower = 0.25f; private const float FireDefaultPower = 0.2f; private const float FrostDefaultPower = 0.1f; private const float LightningDefaultPower = 0.1f; private const float ToxicDeathDefaultPower = 0.25f; private const float ToxicDeathDefaultRadius = 8f; private const string ToxicDeathDefaultTriggerEffect = "blob_aoe"; private const float ArmorPiercingDefaultPower = 0.3f; private const float StaggeringDefaultPower = 0.5f; private const float UndodgeableDefaultDamageReduction = 0.25f; private const float AttackSpeedDefaultPower = 0.35f; private const float ExposedDefaultPower = 0.2f; private const float WeakenedDefaultPower = 0.2f; private const float WitheredDefaultPower = 0.5f; private const float ReflectionDefaultPower = 0.2f; private const float ReflectionDefaultProcChance = 0.5f; private const float VortexDefaultPower = 0.5f; private const float CripplingDefaultPower = 0.5f; private const float DisruptiveDefaultPower = 0.5f; private const float AdrenalineDrainDefaultPower = 0.5f; private const float AdrenalineDrainDefaultGainReduction = 0.5f; private const float AdrenalineDrainDefaultDuration = 5f; private const float CorrosiveDefaultPower = 0.5f; private const float AdaptiveDefaultPower = 0.35f; private const float UnflinchingDefaultPower = 1f; private const float ChameleonDefaultInterval = 10f; private const float OmenDefaultPower = 0.25f; private const float ReapingDefaultPower = 0.1f; private const int ReapingDefaultHealMaxActivations = 20; private const float ReapingDefaultMaxHealthPerKill = 0.1f; private const float ReapingDefaultMaxHealthCap = 2f; private const float ReapingDefaultDamagePerKill = 0f; private const float ReapingDefaultDamageCap = 0f; private const float ReapingDefaultScalePerKill = 0f; private const float ReapingDefaultScaleCap = 0f; private const float BlinkFixedProcChance = 1f; private const float BlinkDefaultCooldown = 6f; private const float BlinkDefaultMaxRange = 16f; private const string BlinkDefaultStartEffect = "fx_Adrenaline1"; private const float KnockbackDefaultPower = 150f; private const float KnockbackDefaultCooldown = 5f; private const float BlamerDefaultKarmaPerSecond = 1f; private const float BlamerTickInterval = 1f; private const float BlamerSaturatedRetryInterval = 5f; private const float BlamerDefaultMaxKarmaGain = 60f; private const float BlamerDefaultFleeHealthRatio = 0.75f; private const float BlamerKarmaRequestTimeout = 5f; private const float BlamerServerMinimumRequestInterval = 0.9f; private const float BlamerServerTargetValidationRange = 128f; private const float BlamerRejectionLogInterval = 10f; private const float BlamerGlobalRejectionLogInterval = 2f; private const float ReflectionServerMinimumRequestInterval = 0.05f; private const float ReflectionHealthSyncTimeout = 2f; private const float ReflectionDamageAuthorizationLifetime = 2f; private const float ReflectionDamageAuthorizationTolerance = 0.1f; private const int MaximumPendingReflectionRequests = 512; private const int MaximumPendingReflectionRequestsPerPeer = 64; private const float VortexEffectServerMinimumRequestInterval = 0.075f; private const float KnockbackNetworkMinimumRequestInterval = 0.05f; private const float ModifierRequestValidationRange = 128f; private const float ReapingDeathSyncTimeout = 2f; private const float ReapingDeathPositionTolerance = 8f; private const int MaximumPendingReapingDeaths = 512; private const int MaximumPendingReapingDeathsPerPeer = 64; private const int MaximumReapingRequestsPerPeerWindow = 128; private const double ReapingPeerRequestWindowSeconds = 1.0; private const int MaximumAuthorizedReapingDeaths = 4096; private const int MaximumQueuedReapingAuthorizations = 8192; private const int MaximumReapingAuthorizationPruneChecksPerTick = 64; private const byte VortexPreResolvedFlag = 128; private const byte VortexProcFlag = 64; private const byte VortexOriginalHitTypeMask = 63; private const float BlinkAiMaxAngle = 90f; private const float BlinkDestinationRadius = 2f; private const float ReapingRadius = 24f; private const int InitialReapingOverlapBufferSize = 128; private const float InactivePlayerDebuffProbeInterval = 1f; private const float RuntimeModifierReprobeInterval = 1f; private const float ModifierHotPathValidationInterval = 1f; private const float HudModifierRefreshInterval = 0.25f; private const float PlayerDebuffDefaultProcChance = 0.5f; private const float PlayerDebuffDuration = 5f; private const float ControlDebuffDuration = 3f; private const float AdaptiveDuration = 5f; private const float EffectPlaybackRange = 100f; private const float VortexEffectRequestValidationRange = 64f; private const int MaxActiveModifiers = 4; private const int ModifierIconSize = 17; private const float BlamerActiveIconScale = 2f; private const float BlamerActiveIconGap = 2f; private const float IconSpacing = 1f; private const float LevelContentWidth = 104f; private const float LevelContentHeight = 20f; private const int StarIconSize = 17; private const int StarLayerBaseSize = 16; private const float StarNumberBaseFontSize = 15f; private const float StarNumberBaseWidth = 20f; private const float HudEdgeOpticalBleedRatio = 0.125f; private const float StarLayerTolerance = 5f; private const float LevelContentBelowHealthGap = 2f; private const float ResistanceTextGap = 2f; private const float ResistanceLineHeight = 14f; private const float ResistanceTextMinWidth = 150f; private const float BossContentWidth = 220f; private const float BossContentBelowHealthGap = 2f; private static readonly string[] SneakMethodNames = new string[5] { "IsCrouching", "IsCrouch", "IsSneaking", "InSneak", "IsStealth" }; private static readonly string[] SneakFieldNames = new string[6] { "m_crouching", "m_crouch", "m_crouchToggled", "m_isCrouching", "m_isCrouch", "m_sneaking" }; private static Sprite? ArmoredSprite; private static Sprite? EnragedSprite; private static Sprite? DeathwardSprite; private static Sprite? SwiftSprite; private static Sprite? RegeneratingSprite; private static Sprite? VampiricSprite; private static Sprite? FireSprite; private static Sprite? FrostSprite; private static Sprite? LightningSprite; private static Sprite? SpiritSprite; private static Sprite? ToxicDeathSprite; private static Sprite? ArmorPiercingSprite; private static Sprite? StaggeringSprite; private static Sprite? UndodgeableSprite; private static Sprite? AttackSpeedSprite; private static Sprite? ExposedSprite; private static Sprite? WeakenedSprite; private static Sprite? WitheredSprite; private static Sprite? ReflectionSprite; private static Sprite? VortexSprite; private static Sprite? CripplingSprite; private static Sprite? DisruptiveSprite; private static Sprite? AdrenalineDrainSprite; private static Sprite? CorrosiveSprite; private static Sprite? AdaptiveSprite; private static Sprite? UnflinchingSprite; private static Sprite? ChameleonSprite; private static Sprite? OmenSprite; private static Sprite? ReapingSprite; private static Sprite? BlinkSprite; private static Sprite? KnockbackSprite; private static Sprite? BlamerSprite; private static Sprite? FallbackStarSprite; private static readonly ModifierSpec[] ModifierSpecs = new ModifierSpec[32] { new ModifierSpec(ModifierGroup.Offense, "enraged", "Enraged", ModifierMask.Enraged, "CreatureManager_EnragedBonus", 0.15f, GetEnragedSprite, (float power) => "Increases outgoing damage by " + FormatPercent(power) + "."), new ModifierSpec(ModifierGroup.Offense, "fire", "Fire", ModifierMask.Fire, "CreatureManager_FirePower", 0.2f, GetFireSprite, (float power) => "Adds fire damage equal to " + FormatPercent(power) + " of the original hit damage."), new ModifierSpec(ModifierGroup.Offense, "frost", "Frost", ModifierMask.Frost, "CreatureManager_FrostPower", 0.1f, GetFrostSprite, (float power) => "Adds frost damage equal to " + FormatPercent(power) + " of the original hit damage."), new ModifierSpec(ModifierGroup.Offense, "lightning", "Lightning", ModifierMask.Lightning, "CreatureManager_LightningPower", 0.1f, GetLightningSprite, (float power) => "Adds lightning damage equal to " + FormatPercent(power) + " of the original hit damage."), new ModifierSpec(ModifierGroup.Offense, "spirit", "Spirit", ModifierMask.Spirit, "CreatureManager_SpiritPower", 0.25f, GetSpiritSprite, (float power) => "Adds spirit damage equal to " + FormatPercent(power) + " of the original hit damage. Against players, a damaging hit adds that amount to vanilla Spirit damage-over-time; its ticks bypass resistance and armor."), new ModifierSpec(ModifierGroup.Offense, "armorPiercing", "Armor Piercing", ModifierMask.ArmorPiercing, "CreatureManager_ArmorPiercingPower", 0.3f, GetArmorPiercingSprite, (float power) => "Against players, ignores " + FormatPercent(power) + " of body armor for that hit."), new ModifierSpec(ModifierGroup.Offense, "staggering", "Staggering", ModifierMask.Staggering, "CreatureManager_StaggeringPower", 0.5f, GetStaggeringSprite, (float power) => "Increases outgoing normal-hit and block-stagger buildup by " + FormatPercent(power) + "."), new ModifierSpec(ModifierGroup.Offense, "undodgeable", "Undodgeable", ModifierMask.Undodgeable, "CreatureManager_UndodgeableDamageReduction", 0.25f, GetUndodgeableSprite, (float power) => "Attacks against players ignore dodge invulnerability but deal " + FormatPercent(power) + " less damage. Blocking and parrying remain available.", ClampUndodgeableDamageReduction), new ModifierSpec(ModifierGroup.Defense, "armored", "Armored", ModifierMask.Armored, "CreatureManager_ArmoredReduction", 0.35f, GetArmoredSprite, (float power) => "Reduces incoming damage by " + FormatPercent(power) + "."), new ModifierSpec(ModifierGroup.Defense, "deathward", "Deathward", ModifierMask.Deathward, "CreatureManager_DeathwardHealth", 0.2f, GetDeathwardSprite, (float power) => DescribeDeathward(power, 10f, 3), ClampDeathwardHealth), new ModifierSpec(ModifierGroup.Defense, "regenerating", "Regenerating", ModifierMask.Regenerating, "CreatureManager_RegeneratingPower", 0.003f, GetRegeneratingSprite, (float power) => "Heals " + FormatPercent(power) + " of max health every second."), new ModifierSpec(ModifierGroup.Defense, "reflection", "Reflection", ModifierMask.Reflection, "CreatureManager_ReflectionPower", 0.2f, GetReflectionSprite, (float power) => DescribeReflection(power, 0.5f), null, "CreatureManager_ReflectionChance"), new ModifierSpec(ModifierGroup.Defense, "vortex", "Vortex", ModifierMask.Vortex, "CreatureManager_VortexPower", 0.5f, GetVortexSprite, (float power) => "On projectile hits, " + FormatPercent(power) + " proc chance to ignore all damage, push, stagger, and status effects."), new ModifierSpec(ModifierGroup.Defense, "adaptive", "Adaptive", ModifierMask.Adaptive, "CreatureManager_AdaptivePower", 0.35f, GetAdaptiveSprite, (float power) => "Remembers one hit's dominant damage type for " + FormatSeconds(5f) + " without changing it. Matching damage is reduced by " + FormatPercent(power) + " until the memory expires."), new ModifierSpec(ModifierGroup.Defense, "unflinching", "Unflinching", ModifierMask.Unflinching, "CreatureManager_UnflinchingPower", 1f, GetUnflinchingSprite, (float _) => "Cannot be staggered by normal hits or perfect parries, preventing the vanilla double-damage stagger window."), new ModifierSpec(ModifierGroup.Defense, "chameleon", "Chameleon", ModifierMask.Chameleon, "CreatureManager_ChameleonInterval", 10f, GetChameleonSprite, (float interval) => DescribeChameleon(interval), ResolveChameleonInterval), new ModifierSpec(ModifierGroup.Affliction, "exposed", "Exposed", ModifierMask.Exposed, "CreatureManager_ExposedPower", 0.2f, GetExposedSprite, (float power) => DescribePlayerAffliction("exposed", "make that player take", power, 0.5f, 5f, 5f, "more damage"), null, "CreatureManager_ExposedChance"), new ModifierSpec(ModifierGroup.Affliction, "weakened", "Weakened", ModifierMask.Weakened, "CreatureManager_WeakenedPower", 0.2f, GetWeakenedSprite, (float power) => DescribePlayerAffliction("weakened", "make that player deal", power, 0.5f, 5f, 5f, "less damage"), null, "CreatureManager_WeakenedChance"), new ModifierSpec(ModifierGroup.Affliction, "withered", "Withered", ModifierMask.Withered, "CreatureManager_WitheredPower", 0.5f, GetWitheredSprite, (float power) => DescribePlayerAffliction("withered", "reduce that player's healing received by", power, 0.5f, 5f, 5f), null, "CreatureManager_WitheredChance"), new ModifierSpec(ModifierGroup.Affliction, "crippling", "Crippling", ModifierMask.Crippling, "CreatureManager_CripplingPower", 0.5f, GetCripplingSprite, (float power) => DescribeCrippling(power, 0.5f, 0.5f, 3f), null, "CreatureManager_CripplingChance"), new ModifierSpec(ModifierGroup.Affliction, "disruptive", "Disruptive", ModifierMask.Disruptive, "CreatureManager_DisruptivePower", 0.5f, GetDisruptiveSprite, (float power) => DescribeDisruptive(power, 0.5f, 0.5f, 3f), null, "CreatureManager_DisruptiveChance"), new ModifierSpec(ModifierGroup.Affliction, "adrenalineDrain", "Adrenaline Drain", ModifierMask.AdrenalineDrain, "CreatureManager_AdrenalineDrainPower", 0.5f, GetAdrenalineDrainSprite, (float power) => DescribeAdrenalineDrain(power, 0.5f, 0.5f, 5f), null, "CreatureManager_AdrenalineDrainChance"), new ModifierSpec(ModifierGroup.Affliction, "corrosive", "Corrosive", ModifierMask.Corrosive, "CreatureManager_CorrosivePower", 0.5f, GetCorrosiveSprite, (float power) => DescribeCorrosive(power, 0.5f, 5f), null, "CreatureManager_CorrosiveChance"), new ModifierSpec(ModifierGroup.Affliction, "toxicDeath", "Toxic Death", ModifierMask.ToxicDeath, "CreatureManager_ToxicDeathPower", 0.25f, GetToxicDeathSprite, (float power) => DescribeToxicDeath(power, 8f)), new ModifierSpec(ModifierGroup.Special, "swift", "Swift", ModifierMask.Swift, "CreatureManager_SwiftPower", 0.25f, GetSwiftSprite, (float power) => "Increases movement speed, acceleration, and turning speed by " + FormatPercent(power) + "."), new ModifierSpec(ModifierGroup.Special, "attackSpeed", "Attack Speed", ModifierMask.AttackSpeed, "CreatureManager_AttackSpeedPower", 0.35f, GetAttackSpeedSprite, (float power) => "Increases attack animation speed by " + FormatPercent(power) + " and shortens both weapon and creature AI attack intervals to " + FormatPercent(1f / Mathf.Max(1f, 1f + power)) + " of normal."), new ModifierSpec(ModifierGroup.Special, "vampiric", "Vampiric", ModifierMask.Vampiric, "CreatureManager_VampiricPower", 0.3f, GetVampiricSprite, (float power) => "Heals the creature for " + FormatPercent(power) + " of health removed by direct hits. Delayed damage-over-time is excluded."), new ModifierSpec(ModifierGroup.Special, "reaping", "Reaping", ModifierMask.Reaping, "CreatureManager_ReapingPower", 0.1f, GetReapingSprite, (float power) => DescribeReaping(power, 0.1f, 0f, 0f)), new ModifierSpec(ModifierGroup.Special, "blink", "Blink", ModifierMask.Blink, "CreatureManager_BlinkPower", 1f, GetBlinkSprite, (float _) => DescribeBlink(6f, 16f)), new ModifierSpec(ModifierGroup.Special, "omen", "Omen", ModifierMask.Omen, "CreatureManager_OmenPower", 0.25f, GetOmenSprite, (float power) => "When killed directly by a player or by poison, fire, or spirit damage over time attributed unambiguously to a player, has a " + FormatPercent(power) + " chance to force an Enforcer summon check. Cooldown blocking follows the server setting."), new ModifierSpec(ModifierGroup.Special, "juggernaut", "Juggernaut", ModifierMask.Knockback, "CreatureManager_KnockbackPower", 150f, GetKnockbackSprite, (float power) => DescribeKnockback(power, 5f), ClampKnockbackPower), new ModifierSpec(ModifierGroup.Special, "blamer", "Blamer", ModifierMask.Blamer, "CreatureManager_BlamerKarmaPerSecond", 1f, GetBlamerSprite, (float power) => DescribeBlamer(power, 60f, 0.75f), ResolveBlamerKarmaPerSecond) }; private static readonly ModifierGroup[] ModifierGroupOrder = new ModifierGroup[4] { ModifierGroup.Offense, ModifierGroup.Defense, ModifierGroup.Affliction, ModifierGroup.Special }; private static readonly Dictionary ModifierSpecsByKey = BuildModifierSpecsByKey(); private static readonly int ExposedStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_ExposedStatus"); private static readonly int WeakenedStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_WeakenedStatus"); private static readonly int WitheredStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_WitheredStatus"); private static readonly int CripplingStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_CripplingStatus"); private static readonly int DisruptiveStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_DisruptiveStatus"); private static readonly int AdrenalineDrainStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_AdrenalineDrainStatus"); private static readonly int CorrosiveStatusHash = StringExtensionMethods.GetStableHashCode("CreatureManager_CorrosiveStatus"); private static readonly Dictionary ActivePlayerDebuffs = new Dictionary(); private static readonly Dictionary ActivePlayerRuntimeDebuffs = new Dictionary(); private static readonly HashSet PlayerControlDebuffWakeIds = new HashSet(); private static readonly Dictionary NextInactivePlayerDebuffProbeTimes = new Dictionary(); private static readonly Dictionary ModifierHotPathStates = new Dictionary(); private static readonly Dictionary ModifierHudRefreshStates = new Dictionary(); private static readonly Dictionary RuntimeModifierReprobeTimes = new Dictionary(); private static readonly Dictionary PendingReapingHealthBonusRatios = new Dictionary(); private static readonly Dictionary PendingPlayerSpiritDamage = new Dictionary(); private static readonly Dictionary DelayedDamageSourceLedgers = new Dictionary(); private static readonly Dictionary PendingDelayedDamageDeathCredits = new Dictionary(); private static readonly Dictionary PendingVampiricDamage = new Dictionary(); private static readonly Dictionary PendingReflectionDamage = new Dictionary(); private static readonly HashSet FinalDeathwardConsumedHits = new HashSet(); private static readonly Dictionary PendingVortexProjectileDecisions = new Dictionary(); private static readonly Dictionary PendingKnockbackHits = new Dictionary(); private static readonly Dictionary PendingKnockbackReservations = new Dictionary(); private static readonly Dictionary LocalKnockbackReadyTimes = new Dictionary(); private static readonly Dictionary PendingBlamerKarmaRequests = new Dictionary(); private static readonly Dictionary ServerBlamerKarmaStates = new Dictionary(); private static readonly Dictionary NextBlamerRejectionLogTimes = new Dictionary(); private static readonly Dictionary ServerReflectionRequestStates = new Dictionary(); private static readonly Dictionary ServerPendingReflectionRequests = new Dictionary(); private static readonly Dictionary ServerPendingReflectionRequestCounts = new Dictionary(); private static readonly Dictionary ServerReflectionNextAllowedTimes = new Dictionary(); private static readonly Dictionary ServerVortexEffectNextAllowedTimes = new Dictionary(); private static readonly Dictionary ServerVortexPeerNextAllowedTimes = new Dictionary(); private static readonly Dictionary ServerKnockbackNextAllowedTimes = new Dictionary(); private static readonly Dictionary ServerPendingReapingRequests = new Dictionary(); private static readonly Dictionary ServerPendingReapingRequestCounts = new Dictionary(); private static readonly Dictionary ServerReapingPeerRateStates = new Dictionary(); private static readonly HashSet ServerPendingReapingRespawns = new HashSet(); private static readonly Dictionary> ServerAuthorizedReapingDeaths = new Dictionary>(); private static readonly Queue ServerAuthorizedReapingDeathPruneQueue = new Queue(); private static readonly HashSet ServerQueuedReapingDeathAuthorizations = new HashSet(); private static readonly List ExpiredServerRequestCharacterIds = new List(); private static readonly List ExpiredServerRequestPeerIds = new List(); private static int ServerAuthorizedReapingDeathCount; private static readonly Stack BlinkAttackAiOverridePool = new Stack(); private static readonly FieldInfo? HitRangedField = typeof(HitData).GetField("m_ranged", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly EffectList SuppressedProjectileHitEffects = new EffectList(); private static readonly string[] VortexHitEffectPrefabNames = new string[1] { "fx_StaffShield_Hit" }; private static readonly string[] ReflectionEffectPrefabNames = new string[2] { "fx_ShieldCharge_5", "sfx_metal_shield_blocked_overlay" }; private static readonly HashSet MissingDeathwardEffectPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet MissingReapingEffectPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); private static ZRoutedRpc? RegisteredRoutedRpc; private static FieldInfo? CachedMonsterAiTargetField; private static bool MonsterAiTargetLookupDone; private static MethodInfo? CachedSneakMethod; private static FieldInfo? CachedSneakField; private static bool SneakMemberLookupDone; private static int HoverRaycastMask = -1; private static TMP_FontAsset? CachedHudFont; private static readonly HashSet PassiveModifierProbeDoneCharacters = new HashSet(); private static readonly HashSet ActivePassiveModifierCharacters = new HashSet(); private static readonly Dictionary PassiveModifierSchedules = new Dictionary(); private static readonly Dictionary ActiveReapingModifierCharacters = new Dictionary(); private static readonly Dictionary UnqueryableReapingModifierCharacters = new Dictionary(); private static readonly HashSet ReapingNearbyCandidateIds = new HashSet(128); private static readonly List ReapingStaleCandidateIds = new List(); private static Collider[] ReapingOverlapBuffer = (Collider[])(object)new Collider[128]; private static Coroutine? PendingReapingPhysicsSync; private static ZNetScene? ReapingPhysicsSyncHost; private static readonly ChameleonDamageType[] ChameleonDamageTypes = new ChameleonDamageType[8] { ChameleonDamageType.Blunt, ChameleonDamageType.Pierce, ChameleonDamageType.Slash, ChameleonDamageType.Fire, ChameleonDamageType.Poison, ChameleonDamageType.Lightning, ChameleonDamageType.Frost, ChameleonDamageType.Spirit }; private static ConditionalWeakTable HudIconStates = new ConditionalWeakTable(); private static ConditionalWeakTable BlamerAlertIconStates = new ConditionalWeakTable(); private static ConditionalWeakTable HudContentStates = new ConditionalWeakTable(); private static int CachedHoverFrame = -1; private static float CachedHoverRange; private static bool CachedHoverValid; private static Character? CachedHoveredCharacter; private static int CachedSneakFrame = -1; private static bool CachedSneakState; private static bool UndodgeableRuntimeFailureReported; private static bool DeathwardRuntimeFailureReported; private static int VortexHitTypeEncodingState; private static long NextBlamerKarmaRequestId; private static float NextBlamerGlobalRejectionLogTime; private static long NextReflectionRequestId = DateTime.UtcNow.Ticks; private static uint ReflectionRequestEpoch; private static uint ReapingDeathEpoch; [ThreadStatic] private static float BlockStaggerMultiplier; [ThreadStatic] private static UndodgeableSourceContext CurrentUndodgeableSourceContext; [ThreadStatic] private static VortexProjectileImpactContext? CurrentVortexProjectileImpact; [ThreadStatic] private static RpcDamageContext CurrentRpcDamageContext; [ThreadStatic] private static DelayedDamageTickContext CurrentDelayedDamageTickContext; private static void ApplyHudFont(TMP_Text target, TMP_Text? preferred = null) { if ((Object)(object)preferred != (Object)null && (Object)(object)preferred.font != (Object)null) { target.font = preferred.font; if ((Object)(object)preferred.fontSharedMaterial != (Object)null) { target.fontSharedMaterial = preferred.fontSharedMaterial; } } else { TMP_FontAsset val = ResolveHudFont(); if ((Object)(object)val != (Object)null) { target.font = val; } } } private static TMP_FontAsset? ResolveHudFont() { if ((Object)(object)CachedHudFont != (Object)null) { return CachedHudFont; } if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_hoverName != (Object)null && (Object)(object)((TMP_Text)Hud.instance.m_hoverName).font != (Object)null) { return CachedHudFont = ((TMP_Text)Hud.instance.m_hoverName).font; } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == "Valheim-AveriaSansLibre") { return CachedHudFont = val; } } if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null) { return CachedHudFont = TMP_Settings.defaultFontAsset; } TMP_Text[] array2 = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val2 in array2) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null) { return CachedHudFont = val2.font; } } array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val3 in array) { if ((Object)(object)val3 != (Object)null) { return CachedHudFont = val3; } } return null; } internal static IReadOnlyList GetKnownModifierKeys() { return CreatureModifierCatalog.Keys; } internal static bool TryNormalizeForcedModifierKeys(IEnumerable modifiers, out List normalized, out string error) { normalized = new List(); error = ""; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string item in modifiers ?? Enumerable.Empty()) { string text = (item ?? "").Trim(); if (text.Length != 0) { if (!TryGetModifierSpec(text, out ModifierSpec spec)) { error = "Unknown modifier '" + text + "'."; return false; } if (hashSet.Add(spec.Key)) { normalized.Add(spec.Key); } } } if (normalized.Count > 4) { error = $"At most {4} modifiers can be assigned to one creature."; return false; } return true; } internal static bool TryGetModifierSprite(string modifier, out Sprite sprite) { if (TryGetModifierSpec(modifier, out ModifierSpec spec)) { sprite = spec.Sprite(); return true; } sprite = null; return false; } internal static string GetModifierDisplayName(string modifier) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return modifier.Trim(); } return CreatureLocalization.Localize("cm_modifier_" + GetModifierLocalizationId(spec.Key) + "_name", spec.DisplayName); } internal static string GetModifierGroupHeading(string modifier) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return string.Empty; } return spec.Group switch { ModifierGroup.Offense => CreatureLocalization.Localize("cm_group_offense", "Offense: Enraged to Undodgeable"), ModifierGroup.Defense => CreatureLocalization.Localize("cm_group_defense", "Defense: Armored to Chameleon"), ModifierGroup.Affliction => CreatureLocalization.Localize("cm_group_affliction", "Affliction: Exposed to Toxic Death"), ModifierGroup.Special => CreatureLocalization.Localize("cm_group_special", "Special: Swift to Blamer"), _ => string.Empty, }; } internal static float ResolveModifierPower(string modifier, float? configuredPower) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return 0f; } return spec.ResolvePower(configuredPower); } internal static string GetModifierCompendiumText(string modifier, ModifierDefinition definition) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return "Power: " + FormatPercent(definition.Power.GetValueOrDefault()) + "."; } float num = spec.ResolvePower(definition.Power); return spec.Mask switch { ModifierMask.Exposed => DescribePlayerAffliction("exposed", "make that player take", num, definition.ProcChance, definition.Duration, 5f, "more damage"), ModifierMask.Weakened => DescribePlayerAffliction("weakened", "make that player deal", num, definition.ProcChance, definition.Duration, 5f, "less damage"), ModifierMask.Withered => DescribePlayerAffliction("withered", "reduce that player's healing received by", num, definition.ProcChance, definition.Duration, 5f), ModifierMask.Crippling => DescribeCrippling(num, Mathf.Clamp01(definition.SecondaryPower ?? 0.5f), ResolvePlayerDebuffProcChance(definition.ProcChance), ResolvePlayerDebuffDuration(definition.Duration, 3f)), ModifierMask.Disruptive => DescribeDisruptive(num, Mathf.Clamp01(definition.SecondaryPower ?? 0.5f), ResolvePlayerDebuffProcChance(definition.ProcChance), ResolvePlayerDebuffDuration(definition.Duration, 3f)), ModifierMask.Reflection => DescribeReflection(num, ResolvePlayerDebuffProcChance(definition.ProcChance)), ModifierMask.AdrenalineDrain => DescribeAdrenalineDrain(num, Mathf.Clamp01(definition.SecondaryPower ?? 0.5f), ResolvePlayerDebuffProcChance(definition.ProcChance), ResolvePlayerDebuffDuration(definition.Duration, 5f)), ModifierMask.Corrosive => DescribeCorrosive(num, ResolvePlayerDebuffProcChance(definition.ProcChance), ResolvePlayerDebuffDuration(definition.Duration, 5f)), ModifierMask.ToxicDeath => DescribeToxicDeath(num, Mathf.Max(0f, definition.Radius ?? 8f)), ModifierMask.Blink => DescribeBlink(ResolveBlinkCooldown(definition.Cooldown), ResolveBlinkMaxRange(definition.MaxRange)), ModifierMask.Knockback => DescribeKnockback(num, ResolveKnockbackCooldown(definition.Cooldown)), ModifierMask.Blamer => DescribeBlamer(num, ResolveBlamerMaxKarmaGain(definition.MaxKarmaGain), ResolveBlamerFleeHealthRatio(definition.FleeHealthRatio)), ModifierMask.Deathward => DescribeDeathward(num, ResolveDeathwardCooldown(definition.Cooldown), ResolveDeathwardMaxActivations(definition.MaxActivations)), ModifierMask.Reaping => DescribeReaping(num, definition.ReapingMaxHealthPerKill ?? 0.1f, definition.ReapingDamagePerKill.GetValueOrDefault(), definition.ReapingScalePerKill.GetValueOrDefault()), _ => DescribeSimpleModifier(spec, num), }; } private static string DescribeSimpleModifier(ModifierSpec spec, float power) { string text = spec.Describe(power); return spec.Mask switch { ModifierMask.Enraged => LocalizeModifierDescription("enraged", text, ("power", FormatPercent(power))), ModifierMask.Fire => LocalizeModifierDescription("fire", text, ("power", FormatPercent(power))), ModifierMask.Frost => LocalizeModifierDescription("frost", text, ("power", FormatPercent(power))), ModifierMask.Lightning => LocalizeModifierDescription("lightning", text, ("power", FormatPercent(power))), ModifierMask.Spirit => LocalizeModifierDescription("spirit", text, ("power", FormatPercent(power))), ModifierMask.ArmorPiercing => LocalizeModifierDescription("armor_piercing", text, ("power", FormatPercent(power))), ModifierMask.Staggering => LocalizeModifierDescription("staggering", text, ("power", FormatPercent(power))), ModifierMask.Undodgeable => LocalizeModifierDescription("undodgeable", text, ("power", FormatPercent(power))), ModifierMask.Armored => LocalizeModifierDescription("armored", text, ("power", FormatPercent(power))), ModifierMask.Regenerating => LocalizeModifierDescription("regenerating", text, ("power", FormatPercent(power))), ModifierMask.Vortex => LocalizeModifierDescription("vortex", text, ("power", FormatPercent(power))), ModifierMask.Adaptive => LocalizeModifierDescription("adaptive", text, ("duration", FormatSeconds(5f)), ("power", FormatPercent(power))), ModifierMask.Unflinching => LocalizeModifierDescription("unflinching", text), ModifierMask.Chameleon => LocalizeModifierDescription("chameleon", text, ("interval", FormatSeconds(power))), ModifierMask.Swift => LocalizeModifierDescription("swift", text, ("power", FormatPercent(power))), ModifierMask.AttackSpeed => LocalizeModifierDescription("attack_speed", text, ("power", FormatPercent(power)), ("interval", FormatPercent(1f / Mathf.Max(1f, 1f + power)))), ModifierMask.Vampiric => LocalizeModifierDescription("vampiric", text, ("power", FormatPercent(power))), ModifierMask.Omen => LocalizeModifierDescription("omen", text, ("power", FormatPercent(power))), _ => text, }; } private static string GetModifierLocalizationId(string modifier) { StringBuilder stringBuilder = new StringBuilder(modifier.Length + 4); for (int i = 0; i < modifier.Length; i++) { char c = modifier[i]; if (i > 0 && char.IsUpper(c)) { stringBuilder.Append('_'); } stringBuilder.Append(char.ToLowerInvariant(c)); } return stringBuilder.ToString(); } private static string LocalizeModifierDescription(string modifierId, string fallback, params (string Name, string Value)[] placeholders) { return CreatureLocalization.Format("cm_modifier_" + modifierId + "_description", fallback, placeholders); } private static string DescribePlayerAffliction(string modifierId, string action, float power, float? configuredProcChance, float? configuredDuration, float fallbackDuration, string suffix = "") { float value = ResolvePlayerDebuffProcChance(configuredProcChance); float seconds = ResolvePlayerDebuffDuration(configuredDuration, fallbackDuration); string text = ((suffix.Length == 0) ? (FormatPercent(power) ?? "") : (FormatPercent(power) + " " + suffix)); string fallback = "On damaging a player, has a " + FormatPercent(value) + " chance to " + action + " " + text + " for " + FormatSeconds(seconds) + "."; return LocalizeModifierDescription(modifierId, fallback, ("chance", FormatPercent(value)), ("power", FormatPercent(power)), ("duration", FormatSeconds(seconds))); } private static string DescribeCrippling(float movement, float jump, float procChance, float duration) { string fallback = "On damaging a player, has a " + FormatPercent(procChance) + " chance to reduce movement by " + FormatPercent(movement) + " and jump force by " + FormatPercent(jump) + " for " + FormatSeconds(duration) + "."; return LocalizeModifierDescription("crippling", fallback, ("chance", FormatPercent(procChance)), ("movement", FormatPercent(movement)), ("jump", FormatPercent(jump)), ("duration", FormatSeconds(duration))); } private static string DescribeDisruptive(float stamina, float eitr, float procChance, float duration) { string fallback = "On damaging a player, has a " + FormatPercent(procChance) + " chance to reduce stamina recovery by " + FormatPercent(stamina) + " and eitr recovery by " + FormatPercent(eitr) + " for " + FormatSeconds(duration) + "."; return LocalizeModifierDescription("disruptive", fallback, ("chance", FormatPercent(procChance)), ("stamina", FormatPercent(stamina)), ("eitr", FormatPercent(eitr)), ("duration", FormatSeconds(duration))); } private static string DescribeReflection(float power, float procChance) { string fallback = "On direct melee hits, has a " + FormatPercent(procChance) + " chance to remove health from the hostile attacker equal to " + FormatPercent(power) + " of the creature's actual health loss. The reflected health loss bypasses defense and resistance."; return LocalizeModifierDescription("reflection", fallback, ("chance", FormatPercent(procChance)), ("power", FormatPercent(power))); } private static string DescribeAdrenalineDrain(float power, float gainReduction, float procChance, float duration) { string fallback = "On damaging a player, has a " + FormatPercent(procChance) + " chance to remove " + FormatPercent(power) + " of current adrenaline and reduce adrenaline gained by " + FormatPercent(gainReduction) + " for " + FormatSeconds(duration) + "."; return LocalizeModifierDescription("adrenaline_drain", fallback, ("chance", FormatPercent(procChance)), ("power", FormatPercent(power)), ("gain", FormatPercent(gainReduction)), ("duration", FormatSeconds(duration))); } private static string DescribeCorrosive(float power, float procChance, float duration) { string fallback = "On damaging a player, has a " + FormatPercent(procChance) + " chance to increase durability loss for equipped helmet, chest, legs, cape, weapons, and shield by " + FormatPercent(power) + " for " + FormatSeconds(duration) + "."; return LocalizeModifierDescription("corrosive", fallback, ("chance", FormatPercent(procChance)), ("power", FormatPercent(power)), ("duration", FormatSeconds(duration))); } private static string DescribeToxicDeath(float power, float radius) { string text = radius.ToString("0.##", CultureInfo.InvariantCulture) + "m"; string fallback = "On death, poisons players within " + text + " for " + FormatPercent(power) + " of each player's max health."; return LocalizeModifierDescription("toxic_death", fallback, ("radius", text), ("power", FormatPercent(power))); } private static Dictionary BuildModifierSpecsByKey() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List list = new List(); HashSet hashSet = new HashSet(); ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (dictionary.ContainsKey(modifierSpec.Key)) { list.Add("duplicate runtime key '" + modifierSpec.Key + "'"); continue; } dictionary.Add(modifierSpec.Key, modifierSpec); long mask = (long)modifierSpec.Mask; if (mask <= 0 || (mask & (mask - 1)) != 0L) { list.Add("runtime spec '" + modifierSpec.Key + "' must use exactly one modifier mask bit"); } else if (!hashSet.Add(modifierSpec.Mask)) { list.Add($"runtime spec '{modifierSpec.Key}' reuses modifier mask '{modifierSpec.Mask}'"); } } int length = Enum.GetValues(typeof(ModifierGroup)).Length; if (ModifierGroupOrder.Length != 4 || length != 4) { list.Add($"HUD slot count {4}, modifier group count {length}, and group order count {ModifierGroupOrder.Length} must match"); } HashSet hashSet2 = new HashSet(); for (int j = 0; j < ModifierGroupOrder.Length; j++) { ModifierGroup modifierGroup = ModifierGroupOrder[j]; if (!hashSet2.Add(modifierGroup)) { list.Add($"HUD group order contains duplicate '{modifierGroup}'"); } if (modifierGroup != (ModifierGroup)j) { list.Add($"HUD group '{modifierGroup}' must occupy slot {j}, but its enum value is {(int)modifierGroup}"); } } modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec2 in modifierSpecs) { if (!hashSet2.Contains(modifierSpec2.Group)) { list.Add($"runtime spec '{modifierSpec2.Key}' uses HUD group '{modifierSpec2.Group}' missing from group order"); } } HashSet hashSet3 = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string key in CreatureModifierCatalog.Keys) { if (!hashSet3.Add(key)) { list.Add("duplicate catalog key '" + key + "'"); } } foreach (string key2 in CreatureModifierCatalog.Keys) { if (!dictionary.TryGetValue(key2, out var value)) { list.Add("missing runtime spec '" + key2 + "'"); } else if (!string.Equals(value.Key, key2, StringComparison.Ordinal)) { list.Add("runtime key '" + value.Key + "' must use catalog spelling '" + key2 + "'"); } } foreach (string key3 in dictionary.Keys) { if (!hashSet3.Contains(key3)) { list.Add("unexpected runtime spec '" + key3 + "'"); } } if (list.Count > 0) { throw new InvalidOperationException(string.Format("CreatureManager modifier catalog invariant failure; the {0} runtime specs, mask bits, and HUD groups must stay aligned: {1}.", CreatureModifierCatalog.Keys.Count, string.Join(", ", list))); } return dictionary; } private static bool TryGetModifierSpec(string modifier, out ModifierSpec spec) { if (ModifierSpecsByKey.TryGetValue(modifier.Trim(), out ModifierSpec value)) { spec = value; return true; } spec = null; return false; } internal static bool TrySetModifierChance(ModifierChanceDefinition chances, string modifier, float chance) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return false; } spec.SetChance(chances, chance); return true; } internal static bool TrySetModifierPower(ModifierPowerDefinition powers, string modifier, float power) { if (!TryGetModifierSpec(modifier, out ModifierSpec spec)) { return false; } spec.SetPower(powers, power); return true; } private static string FormatPercent(float value) { return (Mathf.Max(0f, value) * 100f).ToString("0.##", CultureInfo.InvariantCulture) + "%"; } private static string FormatSeconds(float seconds) { string text = seconds.ToString("0.##", CultureInfo.InvariantCulture); return CreatureLocalization.Format("cm_unit_seconds", text + "s", ("value", text)); } private static string FormatMeters(float meters) { return meters.ToString("0.##", CultureInfo.InvariantCulture) + "m"; } private static string DescribeBlink(float cooldown, float maxRange) { string fallback = "Can teleport near a player target within " + FormatMeters(maxRange) + " every " + FormatSeconds(cooldown) + "."; string text = LocalizeModifierDescription("blink", fallback, ("range", FormatMeters(maxRange)), ("cooldown", FormatSeconds(cooldown))); float num = ResolveBlinkAlertGracePeriod(); if (num <= 0f) { return text; } string text2 = CreatureLocalization.Format("cm_modifier_blink_alert_grace", "Blink and its extended attack range are disabled for {duration} after becoming alerted.", ("duration", FormatSeconds(num))); return text + " " + text2; } private static string DescribeDeathward(float healthRatio, float cooldown, int maxActivations) { string fallback = $"Cancels lethal damage and restores health to {FormatPercent(healthRatio)} of max health, with a {FormatSeconds(cooldown)} cooldown and a limit of {maxActivations} activations per creature."; return LocalizeModifierDescription("deathward", fallback, ("health", FormatPercent(healthRatio)), ("cooldown", FormatSeconds(cooldown)), ("activations", maxActivations.ToString(CultureInfo.InvariantCulture))); } private static string DescribeKnockback(float minimumPushForce, float cooldown) { string text = minimumPushForce.ToString("0.##", CultureInfo.InvariantCulture); string fallback = "On a damaging hit against a player, raises push force to at least " + text + ". A hit that reaches vanilla pushback starts a " + FormatSeconds(cooldown) + " cooldown. Attack hits cannot push this creature."; return LocalizeModifierDescription("juggernaut", fallback, ("force", text), ("cooldown", FormatSeconds(cooldown))); } private static string DescribeChameleon(float interval) { string fallback = "While alerted, becomes immune to one non-immune damage type and changes that immunity every " + FormatSeconds(interval) + "."; return LocalizeModifierDescription("chameleon", fallback, ("interval", FormatSeconds(interval))); } private static string DescribeBlamer(float karmaPerSecond, float maxKarmaGain, float fleeHealthRatio) { string text = karmaPerSecond.ToString("0.##", CultureInfo.InvariantCulture); string text2 = ((maxKarmaGain <= 0f) ? CreatureLocalization.Localize("cm_value_unlimited", "unlimited") : maxKarmaGain.ToString("0.##", CultureInfo.InvariantCulture)); string text3 = FormatPercent(fleeHealthRatio); string fallback = "Below " + text3 + " health, flees from hostile players and adds " + text + " Karma per second to its current region while fleeing, up to " + text2 + " Karma over its lifetime."; return LocalizeModifierDescription("blamer", fallback, ("health", text3), ("karma", text), ("cap", text2)); } private static string DescribeReaping(float healPerKill, float maxHealthPerKill, float damagePerKill, float scalePerKill) { string fallback = "When a player or this creature kills a nearby creature, this creature heals for " + FormatPercent(healPerKill) + " of its base max health and gains " + FormatPercent(maxHealthPerKill) + " base max health, " + FormatPercent(damagePerKill) + " outgoing damage, and " + FormatPercent(scalePerKill) + " size."; return LocalizeModifierDescription("reaping", fallback, ("heal", FormatPercent(healPerKill)), ("health", FormatPercent(maxHealthPerKill)), ("damage", FormatPercent(damagePerKill)), ("scale", FormatPercent(scalePerKill))); } private static float ResolveDeathwardCooldown(float? configuredCooldown) { if (configuredCooldown.HasValue && !float.IsNaN(configuredCooldown.Value)) { return Mathf.Max(0f, configuredCooldown.Value); } return 10f; } private static int ResolveDeathwardMaxActivations(int? configuredMaxActivations) { if (configuredMaxActivations.HasValue && configuredMaxActivations.Value >= 1) { return configuredMaxActivations.Value; } return 3; } private static float ResolveBlinkCooldown(float? configuredCooldown) { if (configuredCooldown.HasValue && !float.IsNaN(configuredCooldown.Value)) { return Mathf.Max(0f, configuredCooldown.Value); } return 6f; } private static float ResolveKnockbackCooldown(float? configuredCooldown) { if (configuredCooldown.HasValue && !float.IsNaN(configuredCooldown.Value) && !float.IsInfinity(configuredCooldown.Value)) { return Mathf.Max(0f, configuredCooldown.Value); } return 5f; } private static float ResolveChameleonInterval(float? configuredInterval) { if (configuredInterval.HasValue && !float.IsNaN(configuredInterval.Value) && !float.IsInfinity(configuredInterval.Value)) { return Mathf.Max(0.1f, configuredInterval.Value); } return 10f; } private static float ResolveBlamerKarmaPerSecond(float? configuredPower) { if (configuredPower.HasValue && !float.IsNaN(configuredPower.Value) && !float.IsInfinity(configuredPower.Value)) { return Mathf.Max(0f, configuredPower.Value); } return 1f; } private static float ResolveBlamerMaxKarmaGain(float? configuredCap) { if (configuredCap.HasValue && !float.IsNaN(configuredCap.Value) && !float.IsInfinity(configuredCap.Value)) { return Mathf.Max(0f, configuredCap.Value); } return 60f; } private static float ResolveBlamerFleeHealthRatio(float? configuredRatio) { if (configuredRatio.HasValue && !float.IsNaN(configuredRatio.Value) && !float.IsInfinity(configuredRatio.Value)) { return Mathf.Clamp01(configuredRatio.Value); } return 0.75f; } private static float ClampKnockbackPower(float? configuredPower) { if (configuredPower.HasValue && !float.IsNaN(configuredPower.Value)) { return Mathf.Max(0f, configuredPower.Value); } return 150f; } private static float ResolveBlinkMaxRange(float? configuredMaxRange) { return Mathf.Max(0f, configuredMaxRange ?? 16f); } private static string ResolveBlinkStartEffect(string? configuredStartEffect) { string text = (configuredStartEffect ?? "fx_Adrenaline1").Trim(); if (!string.Equals(text, "none", StringComparison.OrdinalIgnoreCase)) { return text; } return ""; } internal static void RegisterRpcs() { if (ZRoutedRpc.instance != null && RegisteredRoutedRpc != ZRoutedRpc.instance) { ZRoutedRpc.instance.Register("CreatureManager_VortexHitEffect", (Action)RPC_VortexHitEffect); ZRoutedRpc.instance.Register("CreatureManager_ReflectionEffect", (Action)RPC_ReflectionEffect); ZRoutedRpc.instance.Register("CreatureManager_BlinkEffect", (Action)RPC_BlinkEffect); ZRoutedRpc.instance.Register("CreatureManager_DeathwardEffect", (Action)RPC_DeathwardEffect); ZRoutedRpc.instance.Register("CreatureManager_ReapingFeedback", (Action)RPC_ReapingFeedback); ZRoutedRpc.instance.Register("CreatureManager_BlamerKarmaRequest", (Action)RPC_BlamerKarmaRequest); ZRoutedRpc.instance.Register("CreatureManager_BlamerKarmaResponse", (Action)RPC_BlamerKarmaResponse); EnsureVortexHitTypeEncodingSupported(); RegisteredRoutedRpc = ZRoutedRpc.instance; } } internal static void RegisterCharacterRpcs(Character character) { if (!((Object)(object)character == (Object)null) && !((Object)(object)character.m_nview == (Object)null)) { character.m_nview.Register("CreatureManager_RequestReflectionDamage", (Action)delegate(long sender, long requestId, ZDOID targetId, float amount) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) RPC_ReflectionDamageRequest(character, sender, requestId, targetId, amount); }); character.m_nview.Register("CreatureManager_ReflectionDamage", (Action)delegate(long sender, ZDOID sourceId, float amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) ApplyAuthorizedReflectionDamage(character, sender, sourceId, amount); }); character.m_nview.Register("CreatureManager_RequestKnockbackCooldown", (Action)delegate(long sender, ZDOID targetId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) RPC_KnockbackCooldownRequest(character, sender, targetId); }); character.m_nview.Register("CreatureManager_CommitKnockbackCooldown", (Action)delegate(long sender, float nextReadyTime) { CommitAuthorizedKnockbackCooldown(character, sender, nextReadyTime); }); character.m_nview.Register("CreatureManager_RequestReapingDirectKill", (Action)delegate(long sender, ZDOID deadId, Vector3 deathPosition) { //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) RPC_ReapingDirectKillRequest(character, sender, deadId, deathPosition); }); character.m_nview.Register("CreatureManager_ReapingDirectKill", (Action)delegate(long sender, ZDOID deadId, Vector3 deathPosition) { //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) ApplyAuthorizedReapingGain(character, sender, deadId, deathPosition); }); character.m_nview.Register("CreatureManager_ReapingRespawn", (Action)delegate(long sender) { RPC_ReapingRespawnRequest(character, sender); }); character.m_nview.Register("CreatureManager_VortexHitEffectRequest", (Action)delegate(long sender, Vector3 position) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) RPC_VortexHitEffectRequest(character, sender, position); }); InitializeServerReflectionObservation(character); } } internal static void ForgetCharacter(Character character) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return; } int instanceID = ((Object)character).GetInstanceID(); UntrackRuntimeModifiers(character); ActivePlayerDebuffs.Remove(instanceID); ActivePlayerRuntimeDebuffs.Remove(instanceID); PlayerControlDebuffWakeIds.Remove(instanceID); NextInactivePlayerDebuffProbeTimes.Remove(instanceID); ModifierHotPathStates.Remove(instanceID); ModifierHudRefreshStates.Remove(instanceID); RuntimeModifierReprobeTimes.Remove(instanceID); PendingReapingHealthBonusRatios.Remove(instanceID); DelayedDamageSourceLedgers.Remove(instanceID); PendingDelayedDamageDeathCredits.Remove(instanceID); PendingKnockbackReservations.Remove(instanceID); LocalKnockbackReadyTimes.Remove(instanceID); ResetBlamerKarmaNetworkState(character); ZDOID characterId = character.GetZDOID(); if (characterId != ZDOID.None) { RemoveServerReflectionState(characterId); ServerVortexEffectNextAllowedTimes.Remove(characterId); ServerKnockbackNextAllowedTimes.Remove(characterId); RemoveReapingAuthorizationsForReaper(characterId); ReapingRequestKey[] array = ServerPendingReapingRequests.Keys.Where(delegate(ReapingRequestKey request) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) ZDOID reaper = request.Reaper; return ((ZDOID)(ref reaper)).Equals(characterId); }).ToArray(); for (int num = 0; num < array.Length; num++) { ReleasePendingReapingRequest(array[num]); } ServerPendingReapingRespawns.Remove(characterId); ClearReapingDeathAuthorization(characterId); } } private static void ResetBlamerKarmaNetworkState(Character character) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0036: Unknown result type (might be due to invalid IL or missing references) PendingBlamerKarmaRequests.Remove(((Object)character).GetInstanceID()); ZDOID zDOID = character.GetZDOID(); if (zDOID != ZDOID.None) { ServerBlamerKarmaStates.Remove(zDOID); NextBlamerRejectionLogTimes.Remove(zDOID); } } internal static void ResetRuntimeState() { ReflectionRequestEpoch++; ReapingDeathEpoch++; ActivePlayerDebuffs.Clear(); ActivePlayerRuntimeDebuffs.Clear(); PlayerControlDebuffWakeIds.Clear(); NextInactivePlayerDebuffProbeTimes.Clear(); ModifierHotPathStates.Clear(); ModifierHudRefreshStates.Clear(); RuntimeModifierReprobeTimes.Clear(); PendingReapingHealthBonusRatios.Clear(); DelayedDamageSourceLedgers.Clear(); PendingDelayedDamageDeathCredits.Clear(); PassiveModifierProbeDoneCharacters.Clear(); ActivePassiveModifierCharacters.Clear(); PassiveModifierSchedules.Clear(); ActiveReapingModifierCharacters.Clear(); UnqueryableReapingModifierCharacters.Clear(); ReapingNearbyCandidateIds.Clear(); ReapingStaleCandidateIds.Clear(); ReapingOverlapBuffer = (Collider[])(object)new Collider[128]; CancelPendingReapingPhysicsSync(); BlinkAttackAiOverridePool.Clear(); HudIconStates = new ConditionalWeakTable(); BlamerAlertIconStates = new ConditionalWeakTable(); HudContentStates = new ConditionalWeakTable(); CachedHoveredCharacter = null; CachedHoverFrame = -1; CachedHoverValid = false; CachedSneakFrame = -1; CachedSneakState = false; PendingPlayerSpiritDamage.Clear(); PendingVampiricDamage.Clear(); PendingReflectionDamage.Clear(); FinalDeathwardConsumedHits.Clear(); PendingVortexProjectileDecisions.Clear(); PendingKnockbackHits.Clear(); PendingKnockbackReservations.Clear(); LocalKnockbackReadyTimes.Clear(); PendingBlamerKarmaRequests.Clear(); ServerBlamerKarmaStates.Clear(); NextBlamerRejectionLogTimes.Clear(); ServerReflectionRequestStates.Clear(); ServerPendingReflectionRequests.Clear(); ServerPendingReflectionRequestCounts.Clear(); ServerReflectionNextAllowedTimes.Clear(); ServerVortexEffectNextAllowedTimes.Clear(); ServerVortexPeerNextAllowedTimes.Clear(); ServerKnockbackNextAllowedTimes.Clear(); ServerPendingReapingRequests.Clear(); ServerPendingReapingRequestCounts.Clear(); ServerReapingPeerRateStates.Clear(); ServerPendingReapingRespawns.Clear(); ServerAuthorizedReapingDeaths.Clear(); ServerAuthorizedReapingDeathPruneQueue.Clear(); ServerQueuedReapingDeathAuthorizations.Clear(); ExpiredServerRequestCharacterIds.Clear(); ExpiredServerRequestPeerIds.Clear(); ServerAuthorizedReapingDeathCount = 0; NextBlamerKarmaRequestId = 0L; NextBlamerGlobalRejectionLogTime = 0f; NextReflectionRequestId = DateTime.UtcNow.Ticks; CurrentVortexProjectileImpact = null; CurrentRpcDamageContext = default(RpcDamageContext); CurrentDelayedDamageTickContext = default(DelayedDamageTickContext); MissingDeathwardEffectPrefabs.Clear(); MissingReapingEffectPrefabs.Clear(); UndodgeableRuntimeFailureReported = false; DeathwardRuntimeFailureReported = false; CurrentUndodgeableSourceContext = default(UndodgeableSourceContext); BlockStaggerMultiplier = 0f; } internal static void PruneServerNetworkState() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_019e: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } ZDOID[] array = ServerBlamerKarmaStates.Keys.Concat(NextBlamerRejectionLogTimes.Keys).Distinct().ToArray(); foreach (ZDOID val in array) { ZDO zDO = ZDOMan.instance.GetZDO(val); float num = ((zDO != null) ? zDO.GetFloat(ZDOVars.s_health, float.PositiveInfinity) : 0f); if (zDO == null || zDO.GetBool(ZDOVars.s_dead, false) || (!float.IsInfinity(num) && (float.IsNaN(num) || !(num > 0f)))) { ServerBlamerKarmaStates.Remove(val); NextBlamerRejectionLogTimes.Remove(val); } } float networkTimeSeconds = GetNetworkTimeSeconds(); double preciseNow = GetNetworkTimeSecondsDouble(); KeyValuePair[] array2 = ServerReflectionRequestStates.ToArray(); for (int i = 0; i < array2.Length; i++) { KeyValuePair keyValuePair = array2[i]; ZDO zDO2 = ZDOMan.instance.GetZDO(keyValuePair.Key); float num2 = ((zDO2 != null) ? zDO2.GetFloat(ZDOVars.s_health, float.NaN) : float.NaN); if (zDO2 != null && !zDO2.GetBool(ZDOVars.s_dead, false) && (float.IsNaN(num2) || (!float.IsInfinity(num2) && num2 > 0f))) { Character character; float fallbackHealth = (TryFindCharacter(keyValuePair.Key, out character) ? character.GetHealth() : float.NaN); ObserveServerReflectionHealth(zDO2, keyValuePair.Value, networkTimeSeconds, fallbackHealth); } else { RemoveServerReflectionState(keyValuePair.Key); } } ReflectionRequestKey[] array3 = (from entry in ServerReflectionNextAllowedTimes where entry.Value <= preciseNow select entry.Key).ToArray(); foreach (ReflectionRequestKey key in array3) { ServerReflectionNextAllowedTimes.Remove(key); } PruneExpiredModifierRequestTimes(preciseNow); PruneAuthorizedReapingDeaths(); } private static void PruneExpiredModifierRequestTimes(double now) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) ExpiredServerRequestCharacterIds.Clear(); foreach (KeyValuePair serverVortexEffectNextAllowedTime in ServerVortexEffectNextAllowedTimes) { if (serverVortexEffectNextAllowedTime.Value <= now) { ExpiredServerRequestCharacterIds.Add(serverVortexEffectNextAllowedTime.Key); } } foreach (ZDOID expiredServerRequestCharacterId in ExpiredServerRequestCharacterIds) { ServerVortexEffectNextAllowedTimes.Remove(expiredServerRequestCharacterId); } ExpiredServerRequestCharacterIds.Clear(); foreach (KeyValuePair serverKnockbackNextAllowedTime in ServerKnockbackNextAllowedTimes) { if (serverKnockbackNextAllowedTime.Value <= now) { ExpiredServerRequestCharacterIds.Add(serverKnockbackNextAllowedTime.Key); } } foreach (ZDOID expiredServerRequestCharacterId2 in ExpiredServerRequestCharacterIds) { ServerKnockbackNextAllowedTimes.Remove(expiredServerRequestCharacterId2); } ExpiredServerRequestCharacterIds.Clear(); ExpiredServerRequestPeerIds.Clear(); foreach (KeyValuePair serverVortexPeerNextAllowedTime in ServerVortexPeerNextAllowedTimes) { if (serverVortexPeerNextAllowedTime.Value <= now) { ExpiredServerRequestPeerIds.Add(serverVortexPeerNextAllowedTime.Key); } } foreach (long expiredServerRequestPeerId in ExpiredServerRequestPeerIds) { ServerVortexPeerNextAllowedTimes.Remove(expiredServerRequestPeerId); } ExpiredServerRequestPeerIds.Clear(); foreach (KeyValuePair serverReapingPeerRateState in ServerReapingPeerRateStates) { if (now < serverReapingPeerRateState.Value.WindowStart || now - serverReapingPeerRateState.Value.WindowStart >= 1.0) { ExpiredServerRequestPeerIds.Add(serverReapingPeerRateState.Key); } } foreach (long expiredServerRequestPeerId2 in ExpiredServerRequestPeerIds) { ServerReapingPeerRateStates.Remove(expiredServerRequestPeerId2); } ExpiredServerRequestPeerIds.Clear(); } private static void PruneAuthorizedReapingDeaths() { //IL_0049: 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_006d: 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_00a9: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null || ServerAuthorizedReapingDeathPruneQueue.Count == 0) { return; } int num = Math.Min(64, ServerAuthorizedReapingDeathPruneQueue.Count); while (num-- > 0) { ReapingRequestKey item = ServerAuthorizedReapingDeathPruneQueue.Dequeue(); ServerQueuedReapingDeathAuthorizations.Remove(item); if (!ServerAuthorizedReapingDeaths.TryGetValue(item.Reaper, out HashSet value) || !value.Contains(item.Dead)) { continue; } if (ZDOMan.instance.GetZDO(item.Dead) == null) { if (value.Remove(item.Dead)) { ServerAuthorizedReapingDeathCount = Math.Max(0, ServerAuthorizedReapingDeathCount - 1); } if (value.Count == 0) { ServerAuthorizedReapingDeaths.Remove(item.Reaper); } } else if (ServerQueuedReapingDeathAuthorizations.Add(item)) { ServerAuthorizedReapingDeathPruneQueue.Enqueue(item); } } } internal static void TryRollModifiers(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer() || !CreatureDomainManager.IsSynchronizedConfigurationReady() || !CreatureLevelManager.IsLevelSystemEnabled() || !CreatureLevelManager.IsReadyForModifierApplication(character)) { return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (zDO.GetBool("CreatureManager_ModifiersApplied", false)) { if (CreatureLevelManager.AllowsModifierEffects(character)) { ApplyRuntimeModifierStats(character, zDO); } return; } if (!CreatureLevelManager.ShouldRollModifiers(character)) { if (CreatureLevelManager.BlocksModifierRoll(character)) { StoreInitialModifierState(character, zDO, ModifierMask.None, new ModifierPowerDefinition()); } return; } Dictionary modifiers; bool fallbackBlocked; bool num = CreatureKarmaManager.TryGetEnforcerModifierDefinitions(character, out modifiers, out fallbackBlocked); ModifierChanceDefinition chances = new ModifierChanceDefinition(); bool flag = false; if (!num || !fallbackBlocked) { flag = CreatureLevelManager.TrySelectModifierChances(character, out chances); } if (num) { flag = ApplyModifierChances(chances, modifiers) || flag; } ModifierMask mask = (flag ? RollConfiguredMask(character, chances) : ModifierMask.None); ModifierPowerDefinition powers = new ModifierPowerDefinition(); bool flag2 = false; if (!num || !fallbackBlocked) { flag2 = CreatureLevelManager.TrySelectModifierPowers(character, out powers); } if (num) { flag2 = ApplyModifierPowers(powers, modifiers) || flag2; } if (!flag2) { powers = new ModifierPowerDefinition(); } StoreInitialModifierState(character, zDO, mask, powers); } internal static bool TryApplyForcedModifiers(Character character, IReadOnlyCollection modifiers, out string error) { error = ""; if (!CreatureLevelManager.IsLevelSystemEnabled()) { error = "CreatureManager level system is disabled."; return false; } if ((Object)(object)character == (Object)null || character.IsPlayer()) { error = "The spawned prefab is not a supported creature."; return false; } if (!TryNormalizeForcedModifierKeys(modifiers, out List normalized, out error)) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { error = "The spawned creature has no owned network state."; return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { error = "The spawned creature has no ZDO."; return false; } ModifierMask modifierMask = ModifierMask.None; foreach (string item in normalized) { modifierMask |= ModifierSpecsByKey[item].Mask; } if (!CreatureLevelManager.TrySelectModifierPowers(character, out ModifierPowerDefinition powers)) { powers = new ModifierPowerDefinition(); } StoreInitialModifierState(character, zDO, modifierMask, powers); return true; } private static void StoreInitialModifierState(Character character, ZDO zdo, ModifierMask mask, ModifierPowerDefinition powers) { UntrackRuntimeModifiers(character); ResetBlamerKarmaNetworkState(character); InvalidateModifierCaches(character); zdo.Set("CreatureManager_ModifiersApplied", false); ClearStoredRuntimeModifierState(zdo); SetStoredModifierMask(zdo, mask); StoreModifierPowers(zdo, mask, powers); zdo.Set("CreatureManager_ModifiersApplied", true); RefreshModifierHotPathState(character, zdo); if (HasModifier(mask, ModifierMask.Chameleon)) { InitializeChameleonState(character, zdo); } if (HasModifier(mask, ModifierMask.Blamer)) { GetPassiveModifierSchedule(character, zdo, Time.time).NextBlamer = Time.time + 1f; } if (HasModifier(mask, ModifierMask.Blink)) { BaseAI baseAI = character.GetBaseAI(); if (baseAI != null && baseAI.IsAlerted()) { zdo.Set("CreatureManager_BlinkAlertStartTime", GetNetworkTimeSeconds()); } } ApplyRuntimeModifierStats(character, zdo); } internal static bool InheritModifiers(Character source, Character target) { if (!CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)source == (Object)null || (Object)(object)target == (Object)null || (Object)(object)source == (Object)(object)target || target.IsPlayer()) { return false; } if (!TryGetZdo(source, out ZDO zdo) || !TryGetZdo(target, out ZDO zdo2)) { return false; } ZNetView nview = target.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return false; } if (!zdo.GetBool("CreatureManager_ModifiersApplied", false)) { TryRollModifiers(source); } if (!zdo.GetBool("CreatureManager_ModifiersApplied", false)) { return false; } UntrackRuntimeModifiers(target); ResetBlamerKarmaNetworkState(target); InvalidateModifierCaches(target); zdo2.Set("CreatureManager_ModifiersApplied", false); ClearStoredRuntimeModifierState(zdo2); ModifierMask storedModifierMask = GetStoredModifierMask(zdo); CaptureInheritedReapingHealthRatio(zdo, target, storedModifierMask); SetStoredModifierMask(zdo2, storedModifierMask); CopyStoredModifierPowers(zdo, zdo2); CopyStoredRuntimeModifierState(zdo, zdo2, storedModifierMask); zdo2.Set("CreatureManager_ModifiersApplied", true); RefreshModifierHotPathState(target, zdo2); PassiveModifierSchedule passiveModifierSchedule = null; if (HasModifier(storedModifierMask, ModifierMask.Chameleon)) { passiveModifierSchedule = GetPassiveModifierSchedule(target, zdo2, Time.time); float num = ResolveChameleonInterval(zdo2.GetFloat("CreatureManager_ChameleonInterval", 10f)); passiveModifierSchedule.NextChameleon = Time.time + num; } if (HasModifier(storedModifierMask, ModifierMask.Blamer)) { if (passiveModifierSchedule == null) { passiveModifierSchedule = GetPassiveModifierSchedule(target, zdo2, Time.time); } passiveModifierSchedule.NextBlamer = Time.time + 1f; } ApplyRuntimeModifierStats(target, zdo2); return true; } private static void CaptureInheritedReapingHealthRatio(ZDO source, Character target, ModifierMask mask) { int instanceID = ((Object)target).GetInstanceID(); PendingReapingHealthBonusRatios.Remove(instanceID); if (HasModifier(mask, ModifierMask.Reaping)) { float num = source.GetFloat("CreatureManager_ReapingBaseMaxHealth", 0f); float num2 = source.GetFloat("CreatureManager_ReapingBonusHealth", 0f); if (num > 0f && num2 > 0f) { PendingReapingHealthBonusRatios[instanceID] = num2 / num; } } } private static void ClearStoredRuntimeModifierState(ZDO zdo) { zdo.RemoveFloat("CreatureManager_DeathwardNextReadyTime"); zdo.RemoveInt("CreatureManager_DeathwardActivationCount"); zdo.RemoveFloat("CreatureManager_BlinkNextTime"); zdo.RemoveFloat("CreatureManager_BlinkAlertStartTime"); zdo.RemoveFloat("CreatureManager_KnockbackNextReadyTime"); zdo.RemoveFloat("CreatureManager_ReapingBaseMaxHealth"); zdo.RemoveInt("CreatureManager_ReapingHealActivationCount"); zdo.RemoveFloat("CreatureManager_ReapingBonusHealth"); zdo.RemoveFloat("CreatureManager_ReapingDamageBonus"); zdo.RemoveFloat("CreatureManager_ReapingScaleBonus"); zdo.RemoveVec3("CreatureManager_ReapingBaseScale"); zdo.RemoveInt("CreatureManager_AdaptiveType"); zdo.RemoveFloat("CreatureManager_AdaptiveUntil"); zdo.RemoveInt("CreatureManager_ChameleonType"); zdo.RemoveFloat("CreatureManager_BlamerAccumulatedKarma"); zdo.RemoveInt("CreatureManager_BlamerActive"); } private static void CopyStoredRuntimeModifierState(ZDO source, ZDO target, ModifierMask mask) { if (HasModifier(mask, ModifierMask.Deathward)) { CopyNonZeroFloat(source, target, "CreatureManager_DeathwardNextReadyTime"); CopyNonZeroInt(source, target, "CreatureManager_DeathwardActivationCount"); } if (HasModifier(mask, ModifierMask.Knockback)) { CopyNonZeroFloat(source, target, "CreatureManager_KnockbackNextReadyTime"); } if (HasModifier(mask, ModifierMask.Blink)) { CopyNonZeroFloat(source, target, "CreatureManager_BlinkNextTime"); CopyNonZeroFloat(source, target, "CreatureManager_BlinkAlertStartTime"); } if (HasModifier(mask, ModifierMask.Reaping)) { CopyNonZeroInt(source, target, "CreatureManager_ReapingHealActivationCount"); CopyNonZeroFloat(source, target, "CreatureManager_ReapingBonusHealth"); CopyNonZeroFloat(source, target, "CreatureManager_ReapingDamageBonus"); CopyNonZeroFloat(source, target, "CreatureManager_ReapingScaleBonus"); } if (HasModifier(mask, ModifierMask.Adaptive)) { CopyNonZeroInt(source, target, "CreatureManager_AdaptiveType"); CopyNonZeroFloat(source, target, "CreatureManager_AdaptiveUntil"); } if (HasModifier(mask, ModifierMask.Chameleon)) { CopyNonZeroInt(source, target, "CreatureManager_ChameleonType"); } if (HasModifier(mask, ModifierMask.Blamer)) { CopyNonZeroFloat(source, target, "CreatureManager_BlamerAccumulatedKarma"); } } private static void CopyNonZeroFloat(ZDO source, ZDO target, string key) { float num = source.GetFloat(key, 0f); if (num != 0f) { target.Set(key, num); } } private static void CopyNonZeroInt(ZDO source, ZDO target, string key) { int num = source.GetInt(key, 0); if (num != 0) { target.Set(key, num); } } internal static bool NeedsPassiveModifierUpdate(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } int instanceID = ((Object)character).GetInstanceID(); if (!CreatureLevelManager.IsLevelSystemEnabled()) { PassiveModifierSchedules.Remove(instanceID); return false; } if (ActivePassiveModifierCharacters.Contains(instanceID)) { ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { PassiveModifierSchedules.Remove(instanceID); if (TryGetZdo(character, out ZDO zdo) && zdo.GetBool("CreatureManager_ModifiersApplied", false)) { TrackRuntimeModifiers(character, zdo); } else { ActivePassiveModifierCharacters.Remove(instanceID); PassiveModifierProbeDoneCharacters.Remove(instanceID); RuntimeModifierReprobeTimes[instanceID] = Time.unscaledTime + 1f; } return false; } if (TryGetZdo(character, out ZDO zdo2)) { return IsPassiveModifierUpdateDue(character, zdo2, Time.time); } return true; } if (PassiveModifierProbeDoneCharacters.Contains(instanceID)) { return false; } if (RuntimeModifierReprobeTimes.TryGetValue(instanceID, out var value)) { if (Time.unscaledTime < value) { return false; } RuntimeModifierReprobeTimes.Remove(instanceID); } if (TryGetZdo(character, out ZDO zdo3) && zdo3.GetBool("CreatureManager_ModifiersApplied", false)) { TrackRuntimeModifiers(character, zdo3); ZNetView nview2 = character.m_nview; if (ActivePassiveModifierCharacters.Contains(instanceID) && (Object)(object)nview2 != (Object)null && nview2.IsValid()) { return nview2.IsOwner(); } return false; } RuntimeModifierReprobeTimes[instanceID] = Time.unscaledTime + 1f; return false; } private static bool IsPassiveModifierUpdateDue(Character character, ZDO zdo, float now) { ModifierMask storedModifierMask = GetStoredModifierMask(zdo); PassiveModifierSchedule passiveModifierSchedule = GetPassiveModifierSchedule(character, zdo, now); if ((!HasModifier(storedModifierMask, ModifierMask.Regenerating) || !(now >= passiveModifierSchedule.NextRegeneration)) && (!HasModifier(storedModifierMask, ModifierMask.Chameleon) || !(now >= passiveModifierSchedule.NextChameleon))) { if (HasModifier(storedModifierMask, ModifierMask.Blamer)) { return now >= passiveModifierSchedule.NextBlamer; } return false; } return true; } private static PassiveModifierSchedule GetPassiveModifierSchedule(Character character, ZDO zdo, float now) { int instanceID = ((Object)character).GetInstanceID(); if (!PassiveModifierSchedules.TryGetValue(instanceID, out PassiveModifierSchedule value)) { float num = ResolveChameleonInterval(zdo.GetFloat("CreatureManager_ChameleonInterval", 10f)); value = new PassiveModifierSchedule { NextRegeneration = now + 1f, NextChameleon = now + num, NextBlamer = now + 1f }; PassiveModifierSchedules[instanceID] = value; } return value; } private static void TrackRuntimeModifiers(Character character, ZDO zdo) { int instanceID = ((Object)character).GetInstanceID(); PassiveModifierProbeDoneCharacters.Add(instanceID); ModifierMask storedModifierMask = GetStoredModifierMask(zdo); if (character.IsDead()) { ActivePassiveModifierCharacters.Remove(instanceID); PassiveModifierSchedules.Remove(instanceID); ActiveReapingModifierCharacters.Remove(instanceID); UnqueryableReapingModifierCharacters.Remove(instanceID); RuntimeModifierReprobeTimes.Remove(instanceID); return; } if (!CreatureLevelManager.AllowsModifierEffects(character)) { ZNetView nview = character.m_nview; if (HasModifier(storedModifierMask, ModifierMask.Blamer) && (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner()) { SetBlamerActive(character, zdo, active: false); } ActivePassiveModifierCharacters.Remove(instanceID); PassiveModifierSchedules.Remove(instanceID); ActiveReapingModifierCharacters.Remove(instanceID); UnqueryableReapingModifierCharacters.Remove(instanceID); ModifierMask modifierMask = ModifierMask.Regenerating | ModifierMask.Reaping | ModifierMask.Chameleon | ModifierMask.Blamer; if ((storedModifierMask & modifierMask) != ModifierMask.None) { PassiveModifierProbeDoneCharacters.Remove(instanceID); RuntimeModifierReprobeTimes[instanceID] = Time.unscaledTime + 1f; } else { RuntimeModifierReprobeTimes.Remove(instanceID); } return; } bool flag = HasModifier(storedModifierMask, ModifierMask.Regenerating) && zdo.GetFloat("CreatureManager_RegeneratingPower", 0f) > 0f; bool flag2 = HasModifier(storedModifierMask, ModifierMask.Chameleon) && zdo.GetFloat("CreatureManager_ChameleonInterval", 0f) > 0f && HasEligibleChameleonType(character); ZNetView nview2 = character.m_nview; bool flag3 = (Object)(object)nview2 != (Object)null && nview2.IsValid() && nview2.IsOwner(); if (!flag3) { PendingBlamerKarmaRequests.Remove(instanceID); } bool flag4 = HasModifier(storedModifierMask, ModifierMask.Blamer); bool flag5 = HasBlamerKarmaRemaining(zdo); if (flag4 && !flag5 && flag3) { ExhaustBlamer(character, zdo); storedModifierMask = GetStoredModifierMask(zdo); flag4 = false; } float num = zdo.GetFloat("CreatureManager_BlamerKarmaPerSecond", 0f); float num2 = zdo.GetFloat("CreatureManager_BlamerFleeHealthRatio", 0.75f); bool flag6 = flag4 && num > 0f && num2 > 0f && flag5 && !character.IsTamed() && character.GetBaseAI() is MonsterAI && !CreatureKarmaManager.IsKarmaSummonedCreature(character); if (flag4 && !flag6 && flag3) { SetBlamerActive(character, zdo, active: false); } bool num3 = HasModifier(storedModifierMask, ModifierMask.Reaping) && HasStoredReapingSettings(zdo); bool num4 = flag || flag2 || flag6; if (num4 && flag3) { ActivePassiveModifierCharacters.Add(instanceID); } else { ActivePassiveModifierCharacters.Remove(instanceID); PassiveModifierSchedules.Remove(instanceID); } bool flag7 = flag4 && !flag5; if ((num4 || flag7) && !flag3) { PassiveModifierProbeDoneCharacters.Remove(instanceID); RuntimeModifierReprobeTimes[instanceID] = Time.unscaledTime + 1f; } else { RuntimeModifierReprobeTimes.Remove(instanceID); } if (num3) { Character value; bool num5 = !ActiveReapingModifierCharacters.TryGetValue(instanceID, out value) || value != character; ActiveReapingModifierCharacters[instanceID] = character; if (num5) { if (HasQueryableReapingCollider(character)) { UnqueryableReapingModifierCharacters.Remove(instanceID); } else { UnqueryableReapingModifierCharacters[instanceID] = character; } } } else { ActiveReapingModifierCharacters.Remove(instanceID); UnqueryableReapingModifierCharacters.Remove(instanceID); } } private static void UntrackRuntimeModifiers(Character character) { if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); PassiveModifierProbeDoneCharacters.Remove(instanceID); ActivePassiveModifierCharacters.Remove(instanceID); PassiveModifierSchedules.Remove(instanceID); ActiveReapingModifierCharacters.Remove(instanceID); UnqueryableReapingModifierCharacters.Remove(instanceID); RuntimeModifierReprobeTimes.Remove(instanceID); } } private static bool HasQueryableReapingCollider(Character character) { int reapingCharacterLayerMask = GetReapingCharacterLayerMask(); Collider[] componentsInChildren = ((Component)character).GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null && val.enabled && ((Component)val).gameObject.activeInHierarchy && (reapingCharacterLayerMask & (1 << ((Component)val).gameObject.layer)) != 0) { return true; } } return false; } private static void StoreModifierPowers(ZDO zdo, ModifierMask mask, ModifierPowerDefinition powers) { ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { bool flag = HasModifier(mask, modifierSpec.Mask); if (modifierSpec.ProcChanceKey != null) { if (flag) { zdo.Set(modifierSpec.ProcChanceKey, ResolveStoredPlayerDebuffProcChance(modifierSpec.Mask, powers)); } else { zdo.RemoveFloat(modifierSpec.ProcChanceKey); } } if (flag) { zdo.Set(modifierSpec.PowerKey, modifierSpec.ResolvePower(modifierSpec.GetPower(powers))); } else { zdo.RemoveFloat(modifierSpec.PowerKey); } } bool active = HasModifier(mask, ModifierMask.Exposed); SetActiveFloat(zdo, "CreatureManager_ExposedDuration", active, ResolvePlayerDebuffDuration(powers.ExposedDuration, 5f)); bool active2 = HasModifier(mask, ModifierMask.Weakened); SetActiveFloat(zdo, "CreatureManager_WeakenedDuration", active2, ResolvePlayerDebuffDuration(powers.WeakenedDuration, 5f)); bool active3 = HasModifier(mask, ModifierMask.Withered); SetActiveFloat(zdo, "CreatureManager_WitheredDuration", active3, ResolvePlayerDebuffDuration(powers.WitheredDuration, 5f)); bool active4 = HasModifier(mask, ModifierMask.Crippling); SetActiveFloat(zdo, "CreatureManager_CripplingJumpPower", active4, Mathf.Clamp01(powers.CripplingJump ?? 0.5f)); SetActiveFloat(zdo, "CreatureManager_CripplingDuration", active4, ResolvePlayerDebuffDuration(powers.CripplingDuration, 3f)); bool active5 = HasModifier(mask, ModifierMask.Disruptive); SetActiveFloat(zdo, "CreatureManager_DisruptiveEitrPower", active5, Mathf.Clamp01(powers.DisruptiveEitr ?? 0.5f)); SetActiveFloat(zdo, "CreatureManager_DisruptiveDuration", active5, ResolvePlayerDebuffDuration(powers.DisruptiveDuration, 3f)); bool active6 = HasModifier(mask, ModifierMask.AdrenalineDrain); SetActiveFloat(zdo, "CreatureManager_AdrenalineDrainGainReduction", active6, Mathf.Clamp01(powers.AdrenalineDrainGainReduction ?? 0.5f)); SetActiveFloat(zdo, "CreatureManager_AdrenalineDrainDuration", active6, ResolvePlayerDebuffDuration(powers.AdrenalineDrainDuration, 5f)); bool active7 = HasModifier(mask, ModifierMask.Corrosive); SetActiveFloat(zdo, "CreatureManager_CorrosiveDuration", active7, ResolvePlayerDebuffDuration(powers.CorrosiveDuration, 5f)); bool active8 = HasModifier(mask, ModifierMask.ToxicDeath); SetActiveFloat(zdo, "CreatureManager_ToxicDeathRadius", active8, Mathf.Max(0f, powers.ToxicDeathRadius ?? 8f)); SetActiveString(zdo, "CreatureManager_ToxicDeathTriggerEffect", active8, ResolveTriggerEffect(powers.ToxicDeathTriggerEffect, "blob_aoe")); bool active9 = HasModifier(mask, ModifierMask.Deathward); SetActiveFloat(zdo, "CreatureManager_DeathwardCooldown", active9, ResolveDeathwardCooldown(powers.DeathwardCooldown)); SetActiveInt(zdo, "CreatureManager_DeathwardMaxActivations", active9, ResolveDeathwardMaxActivations(powers.DeathwardMaxActivations)); bool active10 = HasModifier(mask, ModifierMask.Reaping); SetActiveInt(zdo, "CreatureManager_ReapingHealMaxActivations", active10, Math.Max(1, powers.ReapingHealMaxActivations ?? 20)); SetActiveFloat(zdo, "CreatureManager_ReapingMaxHealthPerKill", active10, Mathf.Max(0f, powers.ReapingMaxHealthPerKill ?? 0.1f)); SetActiveFloat(zdo, "CreatureManager_ReapingMaxHealthCap", active10, Mathf.Max(0f, powers.ReapingMaxHealthCap ?? 2f)); SetActiveFloat(zdo, "CreatureManager_ReapingDamagePerKill", active10, Mathf.Max(0f, powers.ReapingDamagePerKill.GetValueOrDefault())); SetActiveFloat(zdo, "CreatureManager_ReapingDamageCap", active10, Mathf.Max(0f, powers.ReapingDamageCap.GetValueOrDefault())); SetActiveFloat(zdo, "CreatureManager_ReapingScalePerKill", active10, Mathf.Max(0f, powers.ReapingScalePerKill.GetValueOrDefault())); SetActiveFloat(zdo, "CreatureManager_ReapingScaleCap", active10, Mathf.Max(0f, powers.ReapingScaleCap.GetValueOrDefault())); bool active11 = HasModifier(mask, ModifierMask.Blink); SetActiveFloat(zdo, "CreatureManager_BlinkCooldown", active11, ResolveBlinkCooldown(powers.BlinkCooldown)); SetActiveFloat(zdo, "CreatureManager_BlinkMaxRange", active11, ResolveBlinkMaxRange(powers.BlinkMaxRange)); SetActiveString(zdo, "CreatureManager_BlinkStartEffect", active11, ResolveBlinkStartEffect(powers.BlinkStartEffect)); bool active12 = HasModifier(mask, ModifierMask.Knockback); SetActiveFloat(zdo, "CreatureManager_KnockbackCooldown", active12, ResolveKnockbackCooldown(powers.KnockbackCooldown)); bool active13 = HasModifier(mask, ModifierMask.Blamer); SetActiveFloat(zdo, "CreatureManager_BlamerMaxKarmaGain", active13, ResolveBlamerMaxKarmaGain(powers.BlamerMaxKarmaGain)); SetActiveFloat(zdo, "CreatureManager_BlamerFleeHealthRatio", active13, ResolveBlamerFleeHealthRatio(powers.BlamerFleeHealthRatio)); } private static void SetActiveFloat(ZDO zdo, string key, bool active, float value) { if (active) { zdo.Set(key, value); } else { zdo.RemoveFloat(key); } } private static void SetActiveInt(ZDO zdo, string key, bool active, int value) { if (active) { zdo.Set(key, value); } else { zdo.RemoveInt(key); } } private static void SetActiveString(ZDO zdo, string key, bool active, string value) { zdo.Set(key, active ? value : ""); } private static void CopyStoredModifierPowers(ZDO source, ZDO target) { ModifierMask storedModifierMask = GetStoredModifierMask(source); ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { bool active = HasModifier(storedModifierMask, modifierSpec.Mask); if (modifierSpec.ProcChanceKey != null) { SetActiveFloat(target, modifierSpec.ProcChanceKey, active, source.GetFloat(modifierSpec.ProcChanceKey, 0f)); } SetActiveFloat(target, modifierSpec.PowerKey, active, source.GetFloat(modifierSpec.PowerKey, 0f)); } bool active2 = HasModifier(storedModifierMask, ModifierMask.Exposed); SetActiveFloat(target, "CreatureManager_ExposedDuration", active2, source.GetFloat("CreatureManager_ExposedDuration", 0f)); bool active3 = HasModifier(storedModifierMask, ModifierMask.Weakened); SetActiveFloat(target, "CreatureManager_WeakenedDuration", active3, source.GetFloat("CreatureManager_WeakenedDuration", 0f)); bool active4 = HasModifier(storedModifierMask, ModifierMask.Withered); SetActiveFloat(target, "CreatureManager_WitheredDuration", active4, source.GetFloat("CreatureManager_WitheredDuration", 0f)); bool active5 = HasModifier(storedModifierMask, ModifierMask.Crippling); SetActiveFloat(target, "CreatureManager_CripplingJumpPower", active5, source.GetFloat("CreatureManager_CripplingJumpPower", 0f)); SetActiveFloat(target, "CreatureManager_CripplingDuration", active5, source.GetFloat("CreatureManager_CripplingDuration", 0f)); bool active6 = HasModifier(storedModifierMask, ModifierMask.Disruptive); SetActiveFloat(target, "CreatureManager_DisruptiveEitrPower", active6, source.GetFloat("CreatureManager_DisruptiveEitrPower", 0f)); SetActiveFloat(target, "CreatureManager_DisruptiveDuration", active6, source.GetFloat("CreatureManager_DisruptiveDuration", 0f)); bool active7 = HasModifier(storedModifierMask, ModifierMask.AdrenalineDrain); SetActiveFloat(target, "CreatureManager_AdrenalineDrainGainReduction", active7, source.GetFloat("CreatureManager_AdrenalineDrainGainReduction", 0f)); SetActiveFloat(target, "CreatureManager_AdrenalineDrainDuration", active7, source.GetFloat("CreatureManager_AdrenalineDrainDuration", 0f)); bool active8 = HasModifier(storedModifierMask, ModifierMask.Corrosive); SetActiveFloat(target, "CreatureManager_CorrosiveDuration", active8, source.GetFloat("CreatureManager_CorrosiveDuration", 0f)); bool active9 = HasModifier(storedModifierMask, ModifierMask.ToxicDeath); SetActiveFloat(target, "CreatureManager_ToxicDeathRadius", active9, source.GetFloat("CreatureManager_ToxicDeathRadius", 0f)); SetActiveString(target, "CreatureManager_ToxicDeathTriggerEffect", active9, source.GetString("CreatureManager_ToxicDeathTriggerEffect", "")); bool active10 = HasModifier(storedModifierMask, ModifierMask.Deathward); SetActiveFloat(target, "CreatureManager_DeathwardCooldown", active10, source.GetFloat("CreatureManager_DeathwardCooldown", 0f)); SetActiveInt(target, "CreatureManager_DeathwardMaxActivations", active10, source.GetInt("CreatureManager_DeathwardMaxActivations", 0)); bool active11 = HasModifier(storedModifierMask, ModifierMask.Reaping); SetActiveInt(target, "CreatureManager_ReapingHealMaxActivations", active11, source.GetInt("CreatureManager_ReapingHealMaxActivations", 0)); SetActiveFloat(target, "CreatureManager_ReapingMaxHealthPerKill", active11, source.GetFloat("CreatureManager_ReapingMaxHealthPerKill", 0f)); SetActiveFloat(target, "CreatureManager_ReapingMaxHealthCap", active11, source.GetFloat("CreatureManager_ReapingMaxHealthCap", 0f)); SetActiveFloat(target, "CreatureManager_ReapingDamagePerKill", active11, source.GetFloat("CreatureManager_ReapingDamagePerKill", 0f)); SetActiveFloat(target, "CreatureManager_ReapingDamageCap", active11, source.GetFloat("CreatureManager_ReapingDamageCap", 0f)); SetActiveFloat(target, "CreatureManager_ReapingScalePerKill", active11, source.GetFloat("CreatureManager_ReapingScalePerKill", 0f)); SetActiveFloat(target, "CreatureManager_ReapingScaleCap", active11, source.GetFloat("CreatureManager_ReapingScaleCap", 0f)); bool active12 = HasModifier(storedModifierMask, ModifierMask.Blink); SetActiveFloat(target, "CreatureManager_BlinkCooldown", active12, source.GetFloat("CreatureManager_BlinkCooldown", 0f)); SetActiveFloat(target, "CreatureManager_BlinkMaxRange", active12, source.GetFloat("CreatureManager_BlinkMaxRange", 0f)); SetActiveString(target, "CreatureManager_BlinkStartEffect", active12, source.GetString("CreatureManager_BlinkStartEffect", "")); bool active13 = HasModifier(storedModifierMask, ModifierMask.Knockback); SetActiveFloat(target, "CreatureManager_KnockbackCooldown", active13, source.GetFloat("CreatureManager_KnockbackCooldown", 0f)); bool active14 = HasModifier(storedModifierMask, ModifierMask.Blamer); SetActiveFloat(target, "CreatureManager_BlamerMaxKarmaGain", active14, source.GetFloat("CreatureManager_BlamerMaxKarmaGain", 0f)); SetActiveFloat(target, "CreatureManager_BlamerFleeHealthRatio", active14, source.GetFloat("CreatureManager_BlamerFleeHealthRatio", 0f)); } private static float ClampPower(float? value, float fallback) { return Mathf.Clamp01(value ?? fallback); } private static float ClampUndodgeableDamageReduction(float? value) { float num = value ?? 0.25f; if (!float.IsNaN(num) && !float.IsInfinity(num)) { return Mathf.Clamp01(num); } return 0.25f; } private static float ClampChance(float? value) { if (value.HasValue && !float.IsNaN(value.Value)) { return Mathf.Clamp(value.Value, 0f, 100f); } return 0f; } private static float ResolvePlayerDebuffProcChance(float? value, float fallback = 0.5f) { return Mathf.Clamp01(value ?? fallback); } private static float ResolveStoredPlayerDebuffProcChance(ModifierMask mask, ModifierPowerDefinition powers) { object value = mask switch { ModifierMask.Exposed => powers.ExposedProcChance, ModifierMask.Weakened => powers.WeakenedProcChance, ModifierMask.Withered => powers.WitheredProcChance, ModifierMask.Crippling => powers.CripplingProcChance, ModifierMask.Disruptive => powers.DisruptiveProcChance, ModifierMask.AdrenalineDrain => powers.AdrenalineDrainProcChance, ModifierMask.Corrosive => powers.CorrosiveProcChance, ModifierMask.Reflection => powers.ReflectionProcChance, _ => (float?)null, }; float fallback = ((mask == ModifierMask.Reflection) ? 0.5f : 0.5f); return ResolvePlayerDebuffProcChance((float?)value, fallback) * 100f; } private static float ResolvePlayerDebuffDuration(float? value, float fallback) { return Mathf.Max(0.1f, value ?? fallback); } private static string ResolveTriggerEffect(string? configured, string fallback) { string text = ((configured == null) ? fallback : configured.Trim()); if (!string.Equals(text, "none", StringComparison.OrdinalIgnoreCase)) { return text; } return ""; } private static float ClampDeathwardHealth(float? value) { return Mathf.Clamp(value ?? 0.2f, 0.01f, 1f); } private static ModifierMask RollConfiguredMask(Character character, ModifierChanceDefinition chances) { ModifierMask modifierMask = ModifierMask.None; ModifierGroup[] modifierGroupOrder = ModifierGroupOrder; foreach (ModifierGroup modifierGroup in modifierGroupOrder) { float num = 0f; ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (modifierSpec.Group == modifierGroup && IsModifierApplicable(character, modifierSpec)) { num += ClampChance(modifierSpec.GetChance(chances)); } } if (num <= 0f) { continue; } float num2 = Random.Range(0f, (num > 100f) ? num : 100f); ModifierMask modifierMask2 = ModifierMask.None; modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec2 in modifierSpecs) { if (modifierSpec2.Group != modifierGroup || !IsModifierApplicable(character, modifierSpec2)) { continue; } float num3 = ClampChance(modifierSpec2.GetChance(chances)); if (!(num3 <= 0f)) { modifierMask2 = modifierSpec2.Mask; if (num2 < num3) { modifierMask |= modifierSpec2.Mask; modifierMask2 = ModifierMask.None; break; } num2 -= num3; } } if (num >= 100f && modifierMask2 != ModifierMask.None) { modifierMask |= modifierMask2; } } return modifierMask; } private static bool IsModifierApplicable(Character character, ModifierSpec spec) { return spec.Mask switch { ModifierMask.Unflinching => character.m_staggerWhenBlocked || character.m_staggerDamageFactor > 0f, ModifierMask.Chameleon => HasEligibleChameleonType(character), ModifierMask.Blamer => !character.IsTamed() && character.GetBaseAI() is MonsterAI, _ => true, }; } internal static bool ApplyModifierChances(ModifierChanceDefinition chances, Dictionary modifiers) { bool result = false; ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (modifiers.TryGetValue(modifierSpec.Key, out ModifierDefinition value) && value.Chance.HasValue) { modifierSpec.SetChance(chances, value.Chance.Value); result = true; } } return result; } internal static bool ApplyModifierPowers(ModifierPowerDefinition powers, Dictionary modifiers) { bool result = false; ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (modifiers.TryGetValue(modifierSpec.Key, out ModifierDefinition value) && value.Power.HasValue) { modifierSpec.SetPower(powers, value.Power.Value); result = true; } } if (modifiers.TryGetValue("deathward", out ModifierDefinition value2) && value2.Cooldown.HasValue) { powers.DeathwardCooldown = Mathf.Max(0f, value2.Cooldown.Value); result = true; } if (value2 != null && value2.MaxActivations.HasValue) { powers.DeathwardMaxActivations = ResolveDeathwardMaxActivations(value2.MaxActivations); result = true; } if (modifiers.TryGetValue("exposed", out ModifierDefinition value3)) { if (value3.ProcChance.HasValue) { powers.ExposedProcChance = Mathf.Clamp01(value3.ProcChance.Value); result = true; } if (value3.Duration.HasValue) { powers.ExposedDuration = ResolvePlayerDebuffDuration(value3.Duration, 5f); result = true; } } if (modifiers.TryGetValue("weakened", out ModifierDefinition value4)) { if (value4.ProcChance.HasValue) { powers.WeakenedProcChance = Mathf.Clamp01(value4.ProcChance.Value); result = true; } if (value4.Duration.HasValue) { powers.WeakenedDuration = ResolvePlayerDebuffDuration(value4.Duration, 5f); result = true; } } if (modifiers.TryGetValue("withered", out ModifierDefinition value5)) { if (value5.ProcChance.HasValue) { powers.WitheredProcChance = Mathf.Clamp01(value5.ProcChance.Value); result = true; } if (value5.Duration.HasValue) { powers.WitheredDuration = ResolvePlayerDebuffDuration(value5.Duration, 5f); result = true; } } if (modifiers.TryGetValue("crippling", out ModifierDefinition value6)) { if (value6.SecondaryPower.HasValue) { powers.CripplingJump = Mathf.Clamp01(value6.SecondaryPower.Value); result = true; } if (value6.ProcChance.HasValue) { powers.CripplingProcChance = Mathf.Clamp01(value6.ProcChance.Value); result = true; } if (value6.Duration.HasValue) { powers.CripplingDuration = ResolvePlayerDebuffDuration(value6.Duration, 3f); result = true; } } if (modifiers.TryGetValue("disruptive", out ModifierDefinition value7)) { if (value7.SecondaryPower.HasValue) { powers.DisruptiveEitr = Mathf.Clamp01(value7.SecondaryPower.Value); result = true; } if (value7.ProcChance.HasValue) { powers.DisruptiveProcChance = Mathf.Clamp01(value7.ProcChance.Value); result = true; } if (value7.Duration.HasValue) { powers.DisruptiveDuration = ResolvePlayerDebuffDuration(value7.Duration, 3f); result = true; } } if (modifiers.TryGetValue("reflection", out ModifierDefinition value8) && value8.ProcChance.HasValue) { powers.ReflectionProcChance = ResolvePlayerDebuffProcChance(value8.ProcChance); result = true; } if (modifiers.TryGetValue("adrenalineDrain", out ModifierDefinition value9)) { if (value9.SecondaryPower.HasValue) { powers.AdrenalineDrainGainReduction = Mathf.Clamp01(value9.SecondaryPower.Value); result = true; } if (value9.ProcChance.HasValue) { powers.AdrenalineDrainProcChance = Mathf.Clamp01(value9.ProcChance.Value); result = true; } if (value9.Duration.HasValue) { powers.AdrenalineDrainDuration = ResolvePlayerDebuffDuration(value9.Duration, 5f); result = true; } } if (modifiers.TryGetValue("corrosive", out ModifierDefinition value10)) { if (value10.ProcChance.HasValue) { powers.CorrosiveProcChance = Mathf.Clamp01(value10.ProcChance.Value); result = true; } if (value10.Duration.HasValue) { powers.CorrosiveDuration = ResolvePlayerDebuffDuration(value10.Duration, 5f); result = true; } } if (modifiers.TryGetValue("toxicDeath", out ModifierDefinition value11)) { if (value11.Radius.HasValue) { powers.ToxicDeathRadius = Mathf.Max(0f, value11.Radius.Value); result = true; } if (value11.TriggerEffect != null) { powers.ToxicDeathTriggerEffect = value11.TriggerEffect; result = true; } } if (modifiers.TryGetValue("reaping", out ModifierDefinition value12)) { if (value12.ReapingHealMaxActivations.HasValue) { powers.ReapingHealMaxActivations = Math.Max(1, value12.ReapingHealMaxActivations.Value); result = true; } if (value12.ReapingMaxHealthPerKill.HasValue) { powers.ReapingMaxHealthPerKill = Mathf.Max(0f, value12.ReapingMaxHealthPerKill.Value); result = true; } if (value12.ReapingMaxHealthCap.HasValue) { powers.ReapingMaxHealthCap = Mathf.Max(0f, value12.ReapingMaxHealthCap.Value); result = true; } if (value12.ReapingDamagePerKill.HasValue) { powers.ReapingDamagePerKill = Mathf.Max(0f, value12.ReapingDamagePerKill.Value); result = true; } if (value12.ReapingDamageCap.HasValue) { powers.ReapingDamageCap = Mathf.Max(0f, value12.ReapingDamageCap.Value); result = true; } if (value12.ReapingScalePerKill.HasValue) { powers.ReapingScalePerKill = Mathf.Max(0f, value12.ReapingScalePerKill.Value); result = true; } if (value12.ReapingScaleCap.HasValue) { powers.ReapingScaleCap = Mathf.Max(0f, value12.ReapingScaleCap.Value); result = true; } } if (modifiers.TryGetValue("blink", out ModifierDefinition value13) && value13.Cooldown.HasValue) { powers.BlinkCooldown = Mathf.Max(0f, value13.Cooldown.Value); result = true; } if (value13 != null && value13.MaxRange.HasValue) { powers.BlinkMaxRange = Mathf.Max(0f, value13.MaxRange.Value); result = true; } if (value13 != null && value13.StartEffect != null) { powers.BlinkStartEffect = value13.StartEffect; result = true; } if (modifiers.TryGetValue("juggernaut", out ModifierDefinition value14) && value14.Cooldown.HasValue) { powers.KnockbackCooldown = ResolveKnockbackCooldown(value14.Cooldown); result = true; } if (modifiers.TryGetValue("blamer", out ModifierDefinition value15)) { if (value15.MaxKarmaGain.HasValue) { powers.BlamerMaxKarmaGain = ResolveBlamerMaxKarmaGain(value15.MaxKarmaGain); result = true; } if (value15.FleeHealthRatio.HasValue) { powers.BlamerFleeHealthRatio = ResolveBlamerFleeHealthRatio(value15.FleeHealthRatio); result = true; } } return result; } internal static RpcDamageScopeState BeginRpcDamageScope(Character target, HitData hit) { RpcDamageContext currentRpcDamageContext = CurrentRpcDamageContext; CurrentRpcDamageContext = new RpcDamageContext { Target = target, Hit = hit }; try { return new RpcDamageScopeState(currentRpcDamageContext, BeginChameleonDamageOverride(target)); } catch { CurrentRpcDamageContext = currentRpcDamageContext; throw; } } internal static void EndRpcDamageScope(RpcDamageScopeState state) { if (!state.Changed) { return; } try { EndChameleonDamageOverride(state.Chameleon); } finally { CurrentRpcDamageContext = state.Previous; } } internal static void CapturePostMitigationDelayedDamage(Character target, HitData hit) { if (!((Object)(object)target == (Object)null) && hit != null && target is Player && CurrentRpcDamageContext.Target == target && CurrentRpcDamageContext.Hit == hit) { float num = SanitizePositiveDamage(hit.m_damage.m_poison); float num2 = SanitizePositiveDamage(hit.m_damage.m_fire); float num3 = SanitizePositiveDamage(hit.m_damage.m_spirit); CurrentRpcDamageContext.ResolvedDelayedDamage = SanitizePositiveDamage(num + num2 + num3); CurrentRpcDamageContext.DelayedDamageCaptured = true; } } internal static ChameleonDamageOverrideState BeginChameleonDamageOverride(Character target) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (CreatureLevelManager.IsLevelSystemEnabled() && !((Object)(object)target == (Object)null) && !target.IsPlayer() && CreatureLevelManager.AllowsModifierEffects(target)) { BaseAI baseAI = target.GetBaseAI(); if (baseAI != null && baseAI.IsAlerted() && TryGetZdo(target, out ZDO zdo) && HasModifier(zdo, ModifierMask.Chameleon)) { ChameleonDamageType chameleonDamageType = GetChameleonDamageType(zdo); if (chameleonDamageType == ChameleonDamageType.None || (int)GetChameleonDamageModifier(target.m_damageModifiers, chameleonDamageType) == 3) { return default(ChameleonDamageOverrideState); } DamageModifiers damageModifiers = target.m_damageModifiers; DamageModifiers modifiers = damageModifiers; SetChameleonDamageModifier(ref modifiers, chameleonDamageType, (DamageModifier)3); target.m_damageModifiers = modifiers; return new ChameleonDamageOverrideState(target, damageModifiers, chameleonDamageType); } } return default(ChameleonDamageOverrideState); } internal static void EndChameleonDamageOverride(ChameleonDamageOverrideState state) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_004f: Unknown result type (might be due to invalid IL or missing references) if (state.IsActive && !((Object)(object)state.Target == (Object)null)) { DamageModifiers modifiers = state.Target.m_damageModifiers; SetChameleonDamageModifier(ref modifiers, state.DamageType, GetChameleonDamageModifier(state.Original, state.DamageType)); state.Target.m_damageModifiers = modifiers; } } internal static VortexProjectileImpactScopeState BeginVortexProjectileImpact(Projectile projectile, Collider? collider, bool water) { VortexProjectileImpactContext currentVortexProjectileImpact = CurrentVortexProjectileImpact; CurrentVortexProjectileImpact = null; if (!CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || water || projectile.m_aoe > 0f) { return new VortexProjectileImpactScopeState(null, currentVortexProjectileImpact, changed: true); } GameObject val = Projectile.FindHitObject(collider); Character val2 = (((Object)(object)val != (Object)null) ? (val.GetComponent() ?? val.GetComponentInParent()) : null); if ((Object)(object)val2 == (Object)null || val2.IsPlayer()) { return new VortexProjectileImpactScopeState(null, currentVortexProjectileImpact, changed: true); } return new VortexProjectileImpactScopeState(CurrentVortexProjectileImpact = new VortexProjectileImpactContext(projectile, val2, projectile.m_hitEffects), currentVortexProjectileImpact, changed: true); } internal static void EndVortexProjectileImpact(ref VortexProjectileImpactScopeState state) { if (!state.Changed) { state = default(VortexProjectileImpactScopeState); return; } VortexProjectileImpactContext current = state.Current; VortexProjectileImpactContext previous = state.Previous; state = default(VortexProjectileImpactScopeState); if (current != null && current.HitEffectsSuppressed && (Object)(object)current.Projectile != (Object)null) { current.Projectile.m_hitEffects = current.OriginalHitEffects; } if (CurrentVortexProjectileImpact == current) { CurrentVortexProjectileImpact = previous; } } internal static VortexDirectProjectileDamageState PrepareVortexDirectProjectileDamage(Character target, HitData hit) { //IL_0089: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) VortexProjectileImpactContext currentVortexProjectileImpact = CurrentVortexProjectileImpact; if (currentVortexProjectileImpact == null || currentVortexProjectileImpact.DecisionWritten || (Object)(object)target == (Object)null || hit == null || currentVortexProjectileImpact.Target != target || !IsProjectileHit(hit) || !TryGetModifierPower(target, ModifierMask.Vortex, "CreatureManager_VortexPower", 0.5f, out var power)) { return default(VortexDirectProjectileDamageState); } if (!EnsureVortexHitTypeEncodingSupported()) { return default(VortexDirectProjectileDamageState); } bool flag = power > 0f && Random.Range(0f, 1f) < Mathf.Clamp01(power); if (!TryEncodeVortexHitType(hit.m_hitType, flag, out var encoded)) { return default(VortexDirectProjectileDamageState); } currentVortexProjectileImpact.DecisionWritten = true; HitType hitType = hit.m_hitType; hit.m_hitType = encoded; if (flag) { NeutralizeVortexProjectileHit(hit); currentVortexProjectileImpact.Projectile.m_hitEffects = SuppressedProjectileHitEffects; currentVortexProjectileImpact.HitEffectsSuppressed = true; } return new VortexDirectProjectileDamageState(hit, hitType); } internal static void RestoreVortexDirectProjectileDamage(ref VortexDirectProjectileDamageState state) { //IL_0017: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!state.IsActive) { state = default(VortexDirectProjectileDamageState); return; } HitData? hit = state.Hit; HitType originalHitType = state.OriginalHitType; state = default(VortexDirectProjectileDamageState); hit.m_hitType = originalHitType; } internal static void CaptureVortexProjectileDecision(HitData hit) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (hit != null && EnsureVortexHitTypeEncodingSupported() && TryDecodeVortexHitType(hit.m_hitType, out var original, out var procced)) { hit.m_hitType = original; PendingVortexProjectileDecisions[hit] = procced; } } private static bool EnsureVortexHitTypeEncodingSupported() { if (VortexHitTypeEncodingState != 0) { return VortexHitTypeEncodingState > 0; } bool flag; try { flag = Enum.GetValues(typeof(HitType)).Cast().All(delegate(HitType value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) int num = Convert.ToInt32(value, CultureInfo.InvariantCulture); return num >= 0 && num <= 63; }); } catch { flag = false; } VortexHitTypeEncodingState = (flag ? 1 : (-1)); if (!flag) { CreatureManagerPlugin.Log.LogError((object)"Vortex projectile pre-resolution is disabled because HitData.HitType no longer fits in the reserved 6-bit transport range."); } return flag; } private static bool TryEncodeVortexHitType(HitType original, bool procced, out HitType encoded) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown int num = Convert.ToInt32(original, CultureInfo.InvariantCulture); if (num < 0 || num > 63) { encoded = (HitType)(int)original; return false; } encoded = (HitType)(byte)(num | 0x80 | (procced ? 64 : 0)); return true; } private static bool TryDecodeVortexHitType(HitType encoded, out HitType original, out bool procced) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected I4, but got Unknown int num = Convert.ToInt32(encoded, CultureInfo.InvariantCulture); if ((num & 0x80) == 0 || (num & -256) != 0) { original = (HitType)(int)encoded; procced = false; return false; } original = (HitType)(byte)(num & 0x3F); procced = (num & 0x40) != 0; return true; } internal static bool ApplyDamageModifiers(Character target, HitData hit) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)target == (Object)null || hit == null) { return true; } ZNetView nview = target.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && !nview.IsOwner()) { return true; } DamageSnapshot incoming = DamageSnapshot.From(hit.m_damage); Character attacker = hit.GetAttacker(); bool flag = HasAnyActiveModifier(target); if (flag && TryApplyVortexProjectileIgnore(target, hit)) { return false; } if (flag) { ApplyAdaptiveReduction(target, hit, incoming); } bool num = (Object)(object)attacker != (Object)null && HasAnyActiveModifier(attacker); if ((Object)(object)attacker != (Object)null && CreatureLevelManager.TryGetDamageMultiplier(attacker, out var multiplier)) { ((DamageTypes)(ref hit.m_damage)).Modify(multiplier); } if (num && TryGetReapingDamageBonus(attacker, out var bonus)) { ((DamageTypes)(ref hit.m_damage)).Modify(1f + bonus); } if (num && TryGetEnragedBonus(attacker, out var bonus2)) { ((DamageTypes)(ref hit.m_damage)).Modify(1f + bonus2); } if (num) { ApplyOutgoingModifierEffects(attacker, target, hit); } Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null) { ApplyPlayerOutgoingDebuffs(val, hit); } Player val2 = (Player)(object)((target is Player) ? target : null); if (val2 != null) { ApplyPlayerIncomingDebuffs(val2, hit); } if (flag && TryGetArmoredReduction(target, out var reduction)) { ((DamageTypes)(ref hit.m_damage)).Modify(1f - reduction); } if (flag) { if (hit.GetTotalDamage() > 0.1f) { QueueReflectionDamage(target, attacker, hit); StoreAdaptiveMemory(target, incoming); } ApplyIncomingControlImmunities(target, hit); } return true; } private static void ApplyIncomingControlImmunities(Character target, HitData hit) { if (TryGetZdo(target, out ZDO zdo)) { ModifierMask storedModifierMask = GetStoredModifierMask(zdo); if (HasModifier(storedModifierMask, ModifierMask.Unflinching)) { hit.m_staggerMultiplier = 0f; } if (HasModifier(storedModifierMask, ModifierMask.Knockback)) { hit.m_pushForce = 0f; } } } private static void QueueReflectionDamage(Character target, Character? attacker, HitData hit) { if (!((Object)(object)attacker == (Object)null) && !IsProjectileHit(hit) && IsHostileAttacker(target, attacker) && TryGetProcModifierState(target, ModifierMask.Reflection, "CreatureManager_ReflectionChance", out ZDO zdo, out float chance) && !(chance <= 0f)) { float num = Mathf.Clamp01(zdo.GetFloat("CreatureManager_ReflectionPower", 0.2f)); if (!(num <= 0f)) { PendingReflectionDamage[hit] = new ReflectionDamageContext(attacker, target, num); } } } private static bool IsHostileAttacker(Character target, Character attacker) { if ((Object)(object)target == (Object)null || (Object)(object)attacker == (Object)null || (Object)(object)target == (Object)(object)attacker) { return false; } try { return BaseAI.IsEnemy(target, attacker) || BaseAI.IsEnemy(attacker, target); } catch { return attacker.IsPlayer(); } } private static bool TryApplyVortexProjectileIgnore(Character target, HitData hit) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (!IsProjectileHit(hit)) { return false; } bool value; bool flag = PendingVortexProjectileDecisions.TryGetValue(hit, out value); if (flag) { PendingVortexProjectileDecisions.Remove(hit); } if (!TryGetModifierPower(target, ModifierMask.Vortex, "CreatureManager_VortexPower", 0.5f, out var power)) { return false; } bool num; if (!flag) { if (!(power > 0f)) { goto IL_006d; } num = Random.Range(0f, 1f) < Mathf.Clamp01(power); } else { num = value; } if (num) { PlayVortexHitEffects(target, hit.m_point); NeutralizeVortexProjectileHit(hit); return true; } goto IL_006d; IL_006d: return false; } private static void NeutralizeVortexProjectileHit(HitData hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) ((DamageTypes)(ref hit.m_damage)).Modify(0f); hit.m_pushForce = 0f; hit.m_staggerMultiplier = 0f; hit.m_statusEffectHash = 0; hit.m_backstabBonus = 1f; hit.m_blockable = false; hit.m_healthReturn = 0f; hit.m_attacker = ZDOID.None; } private static void PlayVortexHitEffects(Character target, Vector3 hitPoint) { //IL_0016: 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) //IL_00aa: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((((Vector3)(ref hitPoint)).sqrMagnitude > 0.001f) ? hitPoint : target.GetCenterPoint()); ZDOID zDOID = target.GetZDOID(); if ((Object)(object)ZNet.instance != (Object)null && ZRoutedRpc.instance != null) { if (ZNet.instance.IsServer()) { BroadcastVortexHitEffects(val, zDOID); return; } ZNetView nview = target.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner()) { try { nview.InvokeRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_VortexHitEffectRequest", new object[1] { val }); return; } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to request Vortex hit effects: " + ex.Message)); } } } PlayVortexHitEffectsLocal(val, zDOID, ((Component)target).transform); } private static void RPC_VortexHitEffectRequest(Character target, long sender, Vector3 position) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null || (Object)(object)target == (Object)null || target.IsDead() || !IsFinite(position) || !TryGetZdo(target, out ZDO zdo) || zdo.GetOwner() != sender || !TryGetModifierPower(target, ModifierMask.Vortex, "CreatureManager_VortexPower", 0.5f, out var _)) { return; } Vector3 val = position - target.GetCenterPoint(); if (((Vector3)(ref val)).sqrMagnitude > 4096f) { return; } ZDOID zDOID = target.GetZDOID(); if (!(zDOID == ZDOID.None)) { double networkTimeSecondsDouble = GetNetworkTimeSecondsDouble(); if ((!ServerVortexPeerNextAllowedTimes.TryGetValue(sender, out var value) || !(networkTimeSecondsDouble < value)) && (!ServerVortexEffectNextAllowedTimes.TryGetValue(zDOID, out var value2) || !(networkTimeSecondsDouble < value2))) { ServerVortexPeerNextAllowedTimes[sender] = networkTimeSecondsDouble + 0.07500000298023224; ServerVortexEffectNextAllowedTimes[zDOID] = networkTimeSecondsDouble + 0.07500000298023224; BroadcastVortexHitEffects(position, zDOID); } } } private static void BroadcastVortexHitEffects(Vector3 position, ZDOID targetId) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(position); val.Write(targetId); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_VortexHitEffect", new object[1] { val }); } } private static void RPC_VortexHitEffect(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender)) { return; } try { Vector3 position = package.ReadVector3(); ZDOID targetId = package.ReadZDOID(); PlayVortexHitEffectsLocal(position, targetId); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to play Vortex hit effects: " + ex.Message)); } } private static void RPC_ReflectionEffect(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender)) { return; } try { Vector3 position = package.ReadVector3(); Vector3 direction = package.ReadVector3(); PlayReflectionEffectsLocal(position, direction); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to play Reflection effects: " + ex.Message)); } } private static void RPC_BlinkEffect(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender)) { return; } try { Vector3 position = package.ReadVector3(); string prefabName = package.ReadString(); PlayBlinkStartEffectLocal(position, prefabName); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to play Blink start effect: " + ex.Message)); } } private static void RPC_DeathwardEffect(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender)) { return; } try { PlayDeathwardTriggerEffectsLocal(package.ReadVector3()); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to play Deathward trigger effects: " + ex.Message)); } } private static void RPC_ReapingFeedback(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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 (!IsTrustedServerRpc(sender)) { return; } try { Vector3 position = package.ReadVector3(); ZDOID characterId = package.ReadZDOID(); bool scaleChanged = package.ReadBool(); Vector3 scale = package.ReadVector3(); ApplyReapingFeedbackLocal(position, characterId, scaleChanged, scale); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to apply Reaping feedback: " + ex.Message)); } } private static void PlayDeathwardTriggerEffects(Vector3 position) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(position); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_DeathwardEffect", new object[1] { val }); } else { PlayDeathwardTriggerEffectsLocal(position); } } private static void BroadcastReapingFeedback(Character character, bool scaleChanged) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_007c: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) Vector3 centerPoint = character.GetCenterPoint(); ZDOID zDOID = character.GetZDOID(); Vector3 localScale = ((Component)character).transform.localScale; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(centerPoint); val.Write(zDOID); val.Write(scaleChanged); val.Write(localScale); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_ReapingFeedback", new object[1] { val }); } else { ApplyReapingFeedbackLocal(centerPoint, zDOID, scaleChanged, localScale); } } private static void PlayBlinkStartEffect(Vector3 position, string prefabName) { //IL_005c: 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_002f: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (prefabName.Length != 0) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(position); val.Write(prefabName); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_BlinkEffect", new object[1] { val }); } else { PlayBlinkStartEffectLocal(position, prefabName); } } } private static bool IsTrustedServerRpc(long sender) { if (ZRoutedRpc.instance != null) { return sender == ZRoutedRpc.instance.GetServerPeerID(); } return false; } private static void PlayVortexHitEffectsLocal(Vector3 position, ZDOID targetId, Transform? knownTarget = null) { //IL_000d: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0077: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null || !IsEffectWithinPlaybackRange(position)) { return; } Transform val = knownTarget; if ((Object)(object)val == (Object)null && targetId != ZDOID.None) { GameObject val2 = ZNetScene.instance.FindInstance(targetId); val = (((Object)(object)val2 != (Object)null) ? val2.transform : null); } string[] vortexHitEffectPrefabNames = VortexHitEffectPrefabNames; foreach (string text in vortexHitEffectPrefabNames) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { GameObject val3 = Object.Instantiate(prefab, position, Quaternion.identity); if ((Object)(object)val != (Object)null) { val3.transform.SetParent(val, true); } } } } private static void PlayReflectionEffects(Character source, Character target, Vector3 hitPoint) { //IL_0016: 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) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((((Vector3)(ref hitPoint)).sqrMagnitude > 0.001f) ? hitPoint : source.GetCenterPoint()); Vector3 val2 = target.GetCenterPoint() - val; if (((Vector3)(ref val2)).sqrMagnitude <= 0.001f) { val2 = ((Component)source).transform.forward; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZPackage val3 = new ZPackage(); val3.Write(val); val3.Write(val2); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "CreatureManager_ReflectionEffect", new object[1] { val3 }); } else { PlayReflectionEffectsLocal(val, val2); } } private static void PlayReflectionEffectsLocal(Vector3 position, Vector3 direction) { //IL_000d: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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 ((Object)(object)ZNetScene.instance == (Object)null || !IsEffectWithinPlaybackRange(position)) { return; } Quaternion val = ((((Vector3)(ref direction)).sqrMagnitude > 0.001f) ? Quaternion.LookRotation(((Vector3)(ref direction)).normalized, Vector3.up) : Quaternion.identity); string[] reflectionEffectPrefabNames = ReflectionEffectPrefabNames; foreach (string text in reflectionEffectPrefabNames) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, position, val); } } } private static void PlayBlinkStartEffectLocal(Vector3 position, string prefabName) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (prefabName.Length != 0 && !((Object)(object)ZNetScene.instance == (Object)null) && IsEffectWithinPlaybackRange(position)) { GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, position, Quaternion.identity); } } } private static void PlayDeathwardTriggerEffectsLocal(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) PlayConfiguredEffectsLocal(position, "fx_StaffShield_Break", MissingDeathwardEffectPrefabs, "Deathward"); } private static void ApplyReapingFeedbackLocal(Vector3 position, ZDOID characterId, bool scaleChanged, Vector3 scale) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (scaleChanged && (Object)(object)ZNetScene.instance != (Object)null) { GameObject val = ZNetScene.instance.FindInstance(characterId); if ((Object)(object)val != (Object)null && scale != Vector3.zero) { val.transform.localScale = scale; ScheduleReapingPhysicsSync(); } } PlayConfiguredEffectsLocal(position, "fx_tentaroot_death", MissingReapingEffectPrefabs, "Reaping"); } private static void ScheduleReapingPhysicsSync() { if (PendingReapingPhysicsSync != null) { return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { Physics.SyncTransforms(); return; } ReapingPhysicsSyncHost = instance; PendingReapingPhysicsSync = ((MonoBehaviour)instance).StartCoroutine(FlushReapingPhysicsTransformsNextFrame()); if (PendingReapingPhysicsSync == null) { ReapingPhysicsSyncHost = null; Physics.SyncTransforms(); } } private static IEnumerator FlushReapingPhysicsTransformsNextFrame() { yield return null; PendingReapingPhysicsSync = null; ReapingPhysicsSyncHost = null; Physics.SyncTransforms(); } private static void CancelPendingReapingPhysicsSync() { Coroutine pendingReapingPhysicsSync = PendingReapingPhysicsSync; ZNetScene reapingPhysicsSyncHost = ReapingPhysicsSyncHost; PendingReapingPhysicsSync = null; ReapingPhysicsSyncHost = null; if (pendingReapingPhysicsSync != null && (Object)(object)reapingPhysicsSyncHost != (Object)null) { ((MonoBehaviour)reapingPhysicsSyncHost).StopCoroutine(pendingReapingPhysicsSync); } } private static void PlayConfiguredEffectsLocal(Vector3 position, string prefabNames, HashSet missingPrefabs, string effectOwner) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (prefabNames.Length == 0 || string.Equals(prefabNames, "none", StringComparison.OrdinalIgnoreCase) || (Object)(object)ZNetScene.instance == (Object)null || !IsEffectWithinPlaybackRange(position)) { return; } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); string[] array = prefabNames.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && !string.Equals(text, "none", StringComparison.OrdinalIgnoreCase) && hashSet.Add(text)) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, position, Quaternion.identity); } else if (missingPrefabs.Add(text)) { CreatureManagerPlugin.Log.LogWarning((object)(effectOwner + " trigger effect prefab '" + text + "' was not found.")); } } } } private static bool IsEffectWithinPlaybackRange(Vector3 position) { //IL_0017: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } Vector3 val = ((Component)localPlayer).transform.position - position; return ((Vector3)(ref val)).sqrMagnitude <= 10000f; } private static void ApplyAdaptiveReduction(Character target, HitData hit, DamageSnapshot incoming) { AdaptiveDamageType adaptiveDamageType = incoming.DominantType(); if (adaptiveDamageType != AdaptiveDamageType.None && TryGetModifierPower(target, ModifierMask.Adaptive, "CreatureManager_AdaptivePower", 0.35f, out var power) && TryGetZdo(target, out ZDO zdo) && !(zdo.GetFloat("CreatureManager_AdaptiveUntil", 0f) <= GetNetworkTimeSeconds()) && zdo.GetInt("CreatureManager_AdaptiveType", 0) == (int)adaptiveDamageType) { ScaleDamageType(ref hit.m_damage, adaptiveDamageType, 1f - power); } } private static void StoreAdaptiveMemory(Character target, DamageSnapshot incoming) { AdaptiveDamageType adaptiveDamageType = incoming.DominantType(); if (adaptiveDamageType != AdaptiveDamageType.None && TryGetModifierPower(target, ModifierMask.Adaptive, "CreatureManager_AdaptivePower", 0.35f, out var _) && TryGetZdo(target, out ZDO zdo)) { float networkTimeSeconds = GetNetworkTimeSeconds(); if (zdo.GetInt("CreatureManager_AdaptiveType", 0) == 0 || !(zdo.GetFloat("CreatureManager_AdaptiveUntil", 0f) > networkTimeSeconds)) { zdo.Set("CreatureManager_AdaptiveType", (int)adaptiveDamageType); zdo.Set("CreatureManager_AdaptiveUntil", networkTimeSeconds + 5f); } } } private static void ScaleDamageType(ref DamageTypes damage, AdaptiveDamageType type, float multiplier) { multiplier = Mathf.Clamp01(multiplier); switch (type) { case AdaptiveDamageType.Physical: damage.m_damage *= multiplier; break; case AdaptiveDamageType.Blunt: damage.m_blunt *= multiplier; break; case AdaptiveDamageType.Slash: damage.m_slash *= multiplier; break; case AdaptiveDamageType.Pierce: damage.m_pierce *= multiplier; break; case AdaptiveDamageType.Chop: damage.m_chop *= multiplier; break; case AdaptiveDamageType.Pickaxe: damage.m_pickaxe *= multiplier; break; case AdaptiveDamageType.Fire: damage.m_fire *= multiplier; break; case AdaptiveDamageType.Frost: damage.m_frost *= multiplier; break; case AdaptiveDamageType.Lightning: damage.m_lightning *= multiplier; break; case AdaptiveDamageType.Poison: damage.m_poison *= multiplier; break; case AdaptiveDamageType.Spirit: damage.m_spirit *= multiplier; break; } } private static bool IsProjectileHit(HitData hit) { object obj = HitRangedField?.GetValue(hit); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static void RPC_ReflectionDamageRequest(Character source, long sender, long requestId, ZDOID targetId, float amount) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00eb: 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_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) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || ZRoutedRpc.instance == null || (Object)(object)source == (Object)null || source.IsPlayer() || (Object)(object)source.m_nview == (Object)null || !source.m_nview.IsValid() || requestId <= 0 || targetId == ZDOID.None || amount <= 0f || float.IsNaN(amount) || float.IsInfinity(amount) || !TryGetProcModifierState(source, ModifierMask.Reflection, "CreatureManager_ReflectionChance", out ZDO zdo, out float chance) || zdo.GetOwner() != sender || !TryFindCharacter(targetId, out Character character) || (Object)(object)character == (Object)(object)source || character.IsDead() || !IsHostileAttacker(source, character) || Vector3.Distance(((Component)source).transform.position, ((Component)character).transform.position) > 128f || !IsServerObservedReflectionMelee(source, targetId)) { return; } ZDOID zDOID = source.GetZDOID(); float num = Mathf.Clamp01(zdo.GetFloat("CreatureManager_ReflectionPower", 0.2f)); if (zDOID == ZDOID.None || num <= 0f) { return; } float num2 = Mathf.Max(0f, source.GetMaxHealth()) * Mathf.Clamp01(num); if (num2 <= 0f || amount > num2 + 0.1f) { return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } if (!ServerReflectionRequestStates.TryGetValue(zDOID, out ServerReflectionRequestState value)) { value = new ServerReflectionRequestState(); ServerReflectionRequestStates[zDOID] = value; ObserveServerReflectionHealth(zdo, value, GetNetworkTimeSeconds(), source.GetHealth()); } if (!value.RequestOwnerInitialized) { value.RequestOwnerInitialized = true; value.RequestOwner = sender; } else if (value.RequestOwner != sender) { ReleasePendingReflectionRequestsForSource(zDOID); value.RequestOwner = sender; value.LastRequestId = 0L; ResetServerReflectionObservation(zdo, value); } if (requestId <= value.LastRequestId || ServerPendingReflectionRequests.Count >= 512 || (ServerPendingReflectionRequestCounts.TryGetValue(sender, out var value2) && value2 >= 64)) { return; } value.LastRequestId = requestId; ReflectionPendingRequestKey reflectionPendingRequestKey = new ReflectionPendingRequestKey(zDOID, targetId, requestId); ServerPendingReflectionRequests.Add(reflectionPendingRequestKey, sender); ServerPendingReflectionRequestCounts[sender] = value2 + 1; ZDOMan instance = ZDOMan.instance; uint reflectionRequestEpoch = ReflectionRequestEpoch; try { ((MonoBehaviour)ZNet.instance).StartCoroutine(AuthorizeReflectionAfterHealthSync(source, character, sender, zDOID, targetId, Mathf.Min(amount, num2), num, chance, value, reflectionPendingRequestKey, instance, reflectionRequestEpoch)); } catch { ReleasePendingReflectionRequest(reflectionPendingRequestKey); throw; } } private static IEnumerator AuthorizeReflectionAfterHealthSync(Character source, Character target, long sender, ZDOID sourceId, ZDOID targetId, float amount, float reflectionPower, float procChance, ServerReflectionRequestState requestState, ReflectionPendingRequestKey pendingRequest, ZDOMan authority, uint epoch) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) float deadline = Time.realtimeSinceStartup + 2f; try { ServerReflectionRequestState value; long value2; while (Time.realtimeSinceStartup <= deadline && ZDOMan.instance == authority && epoch == ReflectionRequestEpoch && ServerReflectionRequestStates.TryGetValue(sourceId, out value) && value == requestState && requestState.RequestOwner == sender && ServerPendingReflectionRequests.TryGetValue(pendingRequest, out value2) && value2 == sender && !((Object)(object)source == (Object)null) && !(source.GetZDOID() != sourceId) && !((Object)(object)source.m_nview == (Object)null) && source.m_nview.IsValid() && !((Object)(object)target == (Object)null) && !(target.GetZDOID() != targetId) && !target.IsDead() && !((Object)(object)target.m_nview == (Object)null) && target.m_nview.IsValid() && IsHostileAttacker(source, target) && !(Vector3.Distance(((Component)source).transform.position, ((Component)target).transform.position) > 128f)) { ZDO zDO = authority.GetZDO(sourceId); if (zDO == null || zDO.GetOwner() != sender) { break; } float networkTimeSeconds = GetNetworkTimeSeconds(); ObserveServerReflectionHealth(zDO, requestState, networkTimeSeconds, source.GetHealth()); float actualDamage = amount / reflectionPower; if (TryConsumeServerReflectionDamage(requestState, actualDamage, networkTimeSeconds)) { ReflectionRequestKey key = new ReflectionRequestKey(sourceId, targetId); double networkTimeSecondsDouble = GetNetworkTimeSecondsDouble(); double value3; bool num = ServerReflectionNextAllowedTimes.TryGetValue(key, out value3) && networkTimeSecondsDouble < value3; ServerReflectionNextAllowedTimes[key] = networkTimeSecondsDouble + 0.05000000074505806; if (!num && !(Random.Range(0f, 100f) >= procChance)) { ZNetView nview = target.m_nview; PlayReflectionEffects(source, target, source.GetCenterPoint()); nview.InvokeRPC("CreatureManager_ReflectionDamage", new object[2] { sourceId, amount }); } break; } yield return null; } } finally { ReleasePendingReflectionRequest(pendingRequest); } } private static bool IsServerObservedReflectionMelee(Character source, ZDOID targetId) { //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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) ZNetView nview = source.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return true; } HitData lastHit = source.m_lastHit; if (lastHit != null && !IsProjectileHit(lastHit) && lastHit.m_attacker != ZDOID.None) { return lastHit.m_attacker == targetId; } return false; } private static bool TryConsumeServerReflectionDamage(ServerReflectionRequestState requestState, float actualDamage, float now) { if (actualDamage <= 0f || float.IsNaN(actualDamage) || float.IsInfinity(actualDamage) || requestState.UnclaimedDamageUntil < now || requestState.UnclaimedDamage <= 0f || requestState.UnclaimedDamage + 0.1f < actualDamage) { return false; } requestState.UnclaimedDamage = Mathf.Max(0f, requestState.UnclaimedDamage - actualDamage); if (requestState.UnclaimedDamage <= 0.1f) { requestState.UnclaimedDamage = 0f; requestState.UnclaimedDamageUntil = 0f; } return true; } private static void InitializeServerReflectionObservation(Character source) { if ((Object)(object)source != (Object)null && TryGetZdo(source, out ZDO zdo)) { InitializeServerReflectionObservation(source, zdo); } } private static void InitializeServerReflectionObservation(Character source, ZDO zdo) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)source == (Object)null || source.IsPlayer() || zdo == null) { return; } ZDOID zDOID = source.GetZDOID(); if (zDOID == ZDOID.None) { return; } if (!HasModifier(zdo, ModifierMask.Reflection)) { RemoveServerReflectionState(zDOID); return; } if (!ServerReflectionRequestStates.TryGetValue(zDOID, out ServerReflectionRequestState value)) { value = new ServerReflectionRequestState(); ServerReflectionRequestStates[zDOID] = value; } ObserveServerReflectionHealth(zdo, value, GetNetworkTimeSeconds(), source.GetHealth()); } private static void ObserveServerReflectionHealth(ZDO sourceZdo, ServerReflectionRequestState requestState, float now, float fallbackHealth = float.NaN) { if (requestState.UnclaimedDamageUntil < now) { requestState.UnclaimedDamage = 0f; requestState.UnclaimedDamageUntil = 0f; } float num = sourceZdo.GetFloat(ZDOVars.s_health, fallbackHealth); if (!float.IsNaN(num) && !float.IsInfinity(num) && !(num < 0f)) { float lastObservedHealth = requestState.LastObservedHealth; if (!float.IsNaN(lastObservedHealth) && !float.IsInfinity(lastObservedHealth) && num + 0.1f < lastObservedHealth) { requestState.UnclaimedDamage += lastObservedHealth - num; requestState.UnclaimedDamageUntil = now + 2f; } requestState.LastObservedHealth = num; } } private static void ResetServerReflectionObservation(ZDO sourceZdo, ServerReflectionRequestState requestState) { requestState.LastObservedHealth = float.NaN; requestState.UnclaimedDamage = 0f; requestState.UnclaimedDamageUntil = 0f; ObserveServerReflectionHealth(sourceZdo, requestState, GetNetworkTimeSeconds()); } private static void RemoveServerReflectionState(ZDOID characterId) { //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_0013: Unknown result type (might be due to invalid IL or missing references) ServerReflectionRequestStates.Remove(characterId); ReflectionPendingRequestKey[] array = ServerPendingReflectionRequests.Keys.Where(delegate(ReflectionPendingRequestKey request) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ZDOID val = request.Source; if (!((ZDOID)(ref val)).Equals(characterId)) { val = request.Target; return ((ZDOID)(ref val)).Equals(characterId); } return true; }).ToArray(); for (int num = 0; num < array.Length; num++) { ReleasePendingReflectionRequest(array[num]); } ReflectionRequestKey[] array2 = ServerReflectionNextAllowedTimes.Keys.Where(delegate(ReflectionRequestKey request) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ZDOID val = request.Source; if (!((ZDOID)(ref val)).Equals(characterId)) { val = request.Target; return ((ZDOID)(ref val)).Equals(characterId); } return true; }).ToArray(); foreach (ReflectionRequestKey key in array2) { ServerReflectionNextAllowedTimes.Remove(key); } } private static void ReleasePendingReflectionRequestsForSource(ZDOID sourceId) { //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) ReflectionPendingRequestKey[] array = ServerPendingReflectionRequests.Keys.Where(delegate(ReflectionPendingRequestKey request) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) ZDOID source = request.Source; return ((ZDOID)(ref source)).Equals(sourceId); }).ToArray(); for (int num = 0; num < array.Length; num++) { ReleasePendingReflectionRequest(array[num]); } } private static void ReleasePendingReflectionRequest(ReflectionPendingRequestKey request) { if (ServerPendingReflectionRequests.TryGetValue(request, out var value) && ServerPendingReflectionRequests.Remove(request)) { if (!ServerPendingReflectionRequestCounts.TryGetValue(value, out var value2) || value2 <= 1) { ServerPendingReflectionRequestCounts.Remove(value); } else { ServerPendingReflectionRequestCounts[value] = value2 - 1; } } } private static void ApplyAuthorizedReflectionDamage(Character target, long sender, ZDOID sourceId, float amount) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender) || !CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)target == (Object)null || (Object)(object)target.m_nview == (Object)null || !target.m_nview.IsValid() || !target.m_nview.IsOwner() || sourceId == ZDOID.None || amount <= 0f || float.IsNaN(amount) || float.IsInfinity(amount) || target.IsDead() || target.InGodMode() || target.InGhostMode()) { return; } float num = Mathf.Min(amount, Mathf.Max(0f, target.GetHealth())); if (num > 0f) { HitData val = new HitData { m_attacker = sourceId, m_hitType = (HitType)1, m_point = target.GetCenterPoint() }; val.m_damage.m_damage = num; target.m_lastHit = val; if ((Object)(object)DamageText.instance != (Object)null && ZRoutedRpc.instance != null) { DamageText.instance.ShowText((TextType)0, target.GetCenterPoint(), num, target.IsPlayer() || target.IsTamed()); } target.UseHealth(num); target.CheckDeath(); } } internal static void ClearTransientDamageContext(HitData? hit = null) { if (hit != null) { PendingPlayerSpiritDamage.Remove(hit); PendingVampiricDamage.Remove(hit); PendingReflectionDamage.Remove(hit); FinalDeathwardConsumedHits.Remove(hit); PendingVortexProjectileDecisions.Remove(hit); if (PendingKnockbackHits.TryGetValue(hit, out var value)) { PendingKnockbackHits.Remove(hit); ReleaseKnockbackReservation(value, hit); } } } internal static void ApplyArmorPiercing(ref float armor) { if (!(CurrentRpcDamageContext.ArmorPiercing <= 0f)) { armor *= 1f - Mathf.Clamp01(CurrentRpcDamageContext.ArmorPiercing); CurrentRpcDamageContext.ArmorPiercing = 0f; } } internal static void ApplyAttackAnimationSpeed(CharacterAnimEvent animEvent) { if (!((Object)(object)animEvent == (Object)null) && !((Object)(object)animEvent.m_character == (Object)null) && !((Object)(object)animEvent.m_animator == (Object)null) && !animEvent.m_character.IsPlayer() && animEvent.m_character.InAttack() && TryGetModifierHotPathState(animEvent.m_character, ModifierMask.AttackSpeed, out ModifierHotPathState state) && animEvent.m_animator.speed > 0.001f) { animEvent.m_animator.speed = Mathf.Max(animEvent.m_animator.speed, state.AttackSpeedFactor); } } internal static void ApplyAttackIntervalSpeed(Humanoid character, ItemData weapon, bool started) { if (started && !((Object)(object)character == (Object)null) && weapon?.m_shared != null && !((Character)character).IsPlayer() && TryGetModifierHotPathState((Character)(object)character, ModifierMask.AttackSpeed, out ModifierHotPathState state)) { float attackSpeedFactor = state.AttackSpeedFactor; float num = 1f - 1f / attackSpeedFactor; weapon.m_lastAttackTime -= weapon.m_shared.m_aiAttackInterval * num; } } internal static void ApplyMinimumAttackIntervalSpeed(Humanoid character, ref float timeSinceLastAttack) { if (!((Object)(object)character == (Object)null) && !((Character)character).IsPlayer() && TryGetModifierHotPathState((Character)(object)character, ModifierMask.AttackSpeed, out ModifierHotPathState state)) { timeSinceLastAttack *= state.AttackSpeedFactor; } } private static float GetAttackSpeedFactor(float power) { return Mathf.Max(1f, 1f + power); } internal static bool TryGetSwiftMovementFactor(Character? character, out float factor) { factor = 1f; if ((Object)(object)character == (Object)null || character.IsPlayer() || !TryGetModifierHotPathState(character, ModifierMask.Swift, out ModifierHotPathState state)) { return false; } factor = state.SwiftFactor; return factor > 1f; } internal static BlockAttackModifierState BeginBlockAttackModifierScope(Character? attacker) { float blockStaggerMultiplier = BlockStaggerMultiplier; BlockStaggerMultiplier = (((Object)(object)attacker != (Object)null && TryGetModifierPower(attacker, ModifierMask.Staggering, "CreatureManager_StaggeringPower", 0.5f, out var power)) ? (1f + power) : 0f); Character unflinchingAttacker = null; bool originalStaggerWhenBlocked = false; if ((Object)(object)attacker != (Object)null && TryGetModifierPower(attacker, ModifierMask.Unflinching, "CreatureManager_UnflinchingPower", 1f, out var _)) { unflinchingAttacker = attacker; originalStaggerWhenBlocked = attacker.m_staggerWhenBlocked; attacker.m_staggerWhenBlocked = false; } return new BlockAttackModifierState(blockStaggerMultiplier, unflinchingAttacker, originalStaggerWhenBlocked); } internal static void EndBlockAttackModifierScope(BlockAttackModifierState state) { try { if ((Object)(object)state.UnflinchingAttacker != (Object)null) { state.UnflinchingAttacker.m_staggerWhenBlocked = state.OriginalStaggerWhenBlocked; } } finally { BlockStaggerMultiplier = state.PreviousStaggerMultiplier; } } internal static void ApplyBlockStaggerBonus(ref float damage) { if (damage > 0f && BlockStaggerMultiplier > 1f) { damage *= BlockStaggerMultiplier; } } internal static BlamerFleeOverrideState BeginBlamerFleeOverride(MonsterAI monsterAI) { Character val = ((BaseAI)(monsterAI?)).m_character; if ((Object)(object)val == (Object)null || val.IsPlayer() || val.IsTamed() || !TryGetHotPathModifierZdo(val, ModifierMask.Blamer, out ZDO zdo) || !zdo.GetBool("CreatureManager_BlamerActive", false) || !HasBlamerKarmaRemaining(zdo) || !IsBlamerFleeTargetValid(val, monsterAI, zdo)) { return default(BlamerFleeOverrideState); } BlamerFleeOverrideState result = new BlamerFleeOverrideState(monsterAI, monsterAI.m_fleeIfLowHealth, monsterAI.m_fleeTimeSinceHurt); monsterAI.m_fleeIfLowHealth = ResolveBlamerFleeHealthRatio(zdo.GetFloat("CreatureManager_BlamerFleeHealthRatio", 0.75f)); monsterAI.m_fleeTimeSinceHurt = float.MaxValue; return result; } internal static void EndBlamerFleeOverride(BlamerFleeOverrideState state) { if (state.IsActive && !((Object)(object)state.MonsterAI == (Object)null)) { state.MonsterAI.m_fleeIfLowHealth = state.FleeIfLowHealth; state.MonsterAI.m_fleeTimeSinceHurt = state.FleeTimeSinceHurt; } } private static bool IsBlamerFleeTargetValid(Character character, MonsterAI monsterAI, ZDO zdo) { float num = ResolveBlamerFleeHealthRatio(zdo.GetFloat("CreatureManager_BlamerFleeHealthRatio", 0.75f)); if (character.GetHealthPercentage() >= num || !((BaseAI)monsterAI).IsAlerted()) { return false; } Character? obj = TryGetMonsterAiTarget(monsterAI); Player val = (Player)(object)((obj is Player) ? obj : null); if (val != null && !((Character)val).IsDead()) { return IsHostileAttacker(character, (Character)(object)val); } return false; } internal static BlinkAttackAiOverrideState? BeginBlinkAttackAiOverride(MonsterAI monsterAI) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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) Character obj = ((BaseAI)(monsterAI?)).m_character; Humanoid val = (Humanoid)(object)((obj is Humanoid) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } Character val2 = (Character)(object)val; if (val2.IsPlayer() || !TryGetHotPathModifierZdo(val2, ModifierMask.Blink, out ZDO zdo) || IsBlinkAlertGraceActive(val2, zdo) || zdo.GetFloat("CreatureManager_BlinkNextTime", 0f) > GetNetworkTimeSeconds()) { return null; } float num = ResolveBlinkMaxRange(zdo.GetFloat("CreatureManager_BlinkMaxRange", 16f)); Player val3 = TryGetBlinkTarget(val2); if (num <= 0f || (Object)(object)val3 == (Object)null || Utils.DistanceXZ(((Component)val2).transform.position, ((Component)val3).transform.position) > num) { return null; } BlinkAttackAiOverrideState blinkAttackAiOverrideState = ((BlinkAttackAiOverridePool.Count > 0) ? BlinkAttackAiOverridePool.Pop() : new BlinkAttackAiOverrideState()); try { foreach (ItemData allItem in val.GetInventory().GetAllItems()) { if (allItem != null) { SharedData shared = allItem.m_shared; if (shared != null && allItem.IsWeapon() && (int)shared.m_aiTargetType == 0) { blinkAttackAiOverrideState.Override(shared, num, 90f); } } } if (blinkAttackAiOverrideState.Count > 0) { return blinkAttackAiOverrideState; } } catch { blinkAttackAiOverrideState.Restore(); BlinkAttackAiOverridePool.Push(blinkAttackAiOverrideState); throw; } BlinkAttackAiOverridePool.Push(blinkAttackAiOverrideState); return null; } internal static void EndBlinkAttackAiOverride(BlinkAttackAiOverrideState? state) { if (state != null) { state.Restore(); BlinkAttackAiOverridePool.Push(state); } } internal static void HandleBlinkAlertStateChanged(MonsterAI monsterAI, bool wasAlerted) { bool flag = ((BaseAI)monsterAI).IsAlerted(); if (wasAlerted == flag) { return; } Character character = ((BaseAI)monsterAI).m_character; ZNetView val = character?.m_nview; if (!((Object)(object)character == (Object)null) && !character.IsPlayer() && !((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner() && TryGetZdo(character, out ZDO zdo) && HasModifier(GetStoredModifierMask(zdo), ModifierMask.Blink)) { if (flag) { zdo.Set("CreatureManager_BlinkAlertStartTime", GetNetworkTimeSeconds()); } else { zdo.RemoveFloat("CreatureManager_BlinkAlertStartTime"); } } } internal static void TryBlinkOnAttackStart(Humanoid humanoid, ItemData weapon, bool started) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)humanoid == (Object)null) { return; } ZNetView nview = ((Character)humanoid).m_nview; if (((Character)humanoid).IsPlayer() || (Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || !TryGetZdo((Character)(object)humanoid, out ZDO zdo) || !HasModifier(GetStoredModifierMask(zdo), ModifierMask.Blink) || !started || weapon == null || weapon.m_shared?.m_aiTargetType != (AiTarget?)0 || ((Character)humanoid).IsTamed() || !CreatureLevelManager.AllowsModifierEffects((Character)(object)humanoid) || IsBlinkAlertGraceActive((Character)(object)humanoid, zdo)) { return; } float networkTimeSeconds = GetNetworkTimeSeconds(); if (zdo.GetFloat("CreatureManager_BlinkNextTime", 0f) > networkTimeSeconds) { return; } Player val = TryGetBlinkTarget((Character)(object)humanoid); if (!((Object)(object)val == (Object)null)) { float num = ResolveBlinkMaxRange(zdo.GetFloat("CreatureManager_BlinkMaxRange", 16f)); float num2 = Utils.DistanceXZ(((Component)humanoid).transform.position, ((Component)val).transform.position); if (!(num <= 0f) && !(num2 > num) && TryGetBlinkDestination(val, out var destination)) { float num3 = ResolveBlinkCooldown(zdo.GetFloat("CreatureManager_BlinkCooldown", 6f)); zdo.Set("CreatureManager_BlinkNextTime", networkTimeSeconds + num3); Vector3 position = ((Component)humanoid).transform.position; string prefabName = ResolveBlinkStartEffect(zdo.GetString("CreatureManager_BlinkStartEffect", "fx_Adrenaline1")); PlayBlinkStartEffect(position, prefabName); TeleportCharacter((Character)(object)humanoid, destination, ((Component)val).transform.position); } } } private static bool IsBlinkAlertGraceActive(Character character, ZDO zdo) { float num = ResolveBlinkAlertGracePeriod(); ZNetView nview = character.m_nview; bool flag = (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner(); BaseAI baseAI = character.GetBaseAI(); float num2 = zdo.GetFloat("CreatureManager_BlinkAlertStartTime", 0f); if ((Object)(object)baseAI == (Object)null || !baseAI.IsAlerted()) { if (flag && num2 > 0f) { zdo.RemoveFloat("CreatureManager_BlinkAlertStartTime"); } return num > 0f; } float networkTimeSeconds = GetNetworkTimeSeconds(); if (num2 <= 0f) { if (!flag) { return num > 0f; } num2 = networkTimeSeconds; zdo.Set("CreatureManager_BlinkAlertStartTime", num2); } if (num > 0f) { return networkTimeSeconds - num2 < num; } return false; } private static float ResolveBlinkAlertGracePeriod() { return Mathf.Clamp(CreatureManagerPlugin.BlinkAlertGracePeriod?.Value ?? 0f, 0f, 10f); } private static Player? TryGetBlinkTarget(Character character) { BaseAI baseAI = character.GetBaseAI(); if ((Object)(object)baseAI == (Object)null) { return null; } Character? obj = TryGetMonsterAiTarget((MonsterAI?)(object)((baseAI is MonsterAI) ? baseAI : null)); Player val = (Player)(object)((obj is Player) ? obj : null); if (val == null) { return null; } if (((Character)val).IsDead()) { return null; } if (!IsHostileAttacker(character, (Character)(object)val)) { return null; } return val; } internal static UndodgeableScopeState BeginUndodgeableAttackScope(Attack? attack, UndodgeableSourcePath sourcePath) { Character val = null; ZNetView sourceView = null; bool sourceDodgeable = false; try { val = (Character)(object)attack?.m_character; sourceView = val?.m_nview; ItemData val2 = attack?.m_weapon; sourceDodgeable = val2?.m_shared != null && val2.m_shared.m_dodgeable; } catch (Exception exception) { ReportUndodgeableRuntimeFailure("begin_attack_scope", exception); } return new UndodgeableScopeState(BeginUndodgeableSourceScopeCore(val, sourceView, null, sourcePath, sourceDodgeable, requireSourceOwner: true, sourcePath != UndodgeableSourcePath.None)); } internal static UndodgeableScopeState BeginUndodgeableProjectileScope(Projectile? projectile, IDestructible? destructible) { Player val = (Player)(object)((destructible is Player) ? destructible : null); Character attacker = projectile?.m_owner; ZNetView sourceView = projectile?.m_nview; bool sourceDodgeable = projectile?.m_dodgeable ?? false; return new UndodgeableScopeState(BeginUndodgeableSourceScopeCore(attacker, sourceView, val, UndodgeableSourcePath.ProjectileTarget, sourceDodgeable, requireSourceOwner: true, (Object)(object)val != (Object)null)); } internal static UndodgeableScopeState BeginUndodgeableAoeScope(Aoe? aoe, Collider? collider) { Character val = aoe?.m_owner; ZNetView val2 = aoe?.m_nview; bool flag = aoe?.m_dodgeable ?? false; bool flag2 = aoe?.m_useTriggers ?? false; Player val3 = null; bool executionEligible = false; bool flag3 = !flag2; bool flag4 = false; try { flag4 = (Object)(object)aoe != (Object)null && flag && (Object)(object)val != (Object)null && (!flag3 || IsLocallyOwnedUndodgeableSource(val, val2)) && HasActiveUndodgeableModifier(val); if (flag4) { GameObject obj = (((Object)(object)collider == (Object)null) ? null : Projectile.FindHitObject(collider)); Character obj2 = ((obj != null) ? obj.GetComponent() : null); val3 = (Player)(object)((obj2 is Player) ? obj2 : null); if ((Object)(object)val3 != (Object)null) { executionEligible = (!flag2 && !((Object)(object)val2 == (Object)null)) || ((Character)val3).IsOwner(); } } } catch (Exception exception) { ReportUndodgeableRuntimeFailure("resolve_aoe_target", exception); } return new UndodgeableScopeState(BeginUndodgeableSourceScopeCore(val, val2, val3, UndodgeableSourcePath.AoeTarget, flag, flag3, executionEligible, flag4)); } internal static void EndUndodgeableScope(ref UndodgeableScopeState state) { if (!state.IsActive) { state = default(UndodgeableScopeState); return; } UndodgeableScopeState undodgeableScopeState = state; state = default(UndodgeableScopeState); if (undodgeableScopeState.Source.Changed) { CurrentUndodgeableSourceContext = undodgeableScopeState.Source.Previous; } } private static UndodgeableSourceScopeState BeginUndodgeableSourceScopeCore(Character? attacker, ZNetView? sourceView, Player? intendedTarget, UndodgeableSourcePath path, bool sourceDodgeable, bool requireSourceOwner, bool executionEligible, bool sourcePrevalidated = false) { UndodgeableSourceContext currentUndodgeableSourceContext = CurrentUndodgeableSourceContext; UndodgeableSourceContext currentUndodgeableSourceContext2 = default(UndodgeableSourceContext); try { if (executionEligible && sourceDodgeable && (Object)(object)attacker != (Object)null && path != UndodgeableSourcePath.None && (sourcePrevalidated || ((!requireSourceOwner || IsLocallyOwnedUndodgeableSource(attacker, sourceView)) && HasActiveUndodgeableModifier(attacker)))) { currentUndodgeableSourceContext2 = new UndodgeableSourceContext(attacker, intendedTarget, path, sourceDodgeable); } } catch (Exception exception) { ReportUndodgeableRuntimeFailure("begin_source_scope", exception); } bool flag = currentUndodgeableSourceContext.IsActive || currentUndodgeableSourceContext2.IsActive; if (flag) { CurrentUndodgeableSourceContext = currentUndodgeableSourceContext2; } return new UndodgeableSourceScopeState(currentUndodgeableSourceContext, flag); } internal static bool ApplyUndodgeableDodgeOverride(Player? target, ref bool dodgeInvincible) { if (!dodgeInvincible || (Object)(object)target == (Object)null) { return false; } try { UndodgeableSourceContext currentUndodgeableSourceContext = CurrentUndodgeableSourceContext; if (!currentUndodgeableSourceContext.IsActive || ((Object)(object)currentUndodgeableSourceContext.IntendedTarget != (Object)null && currentUndodgeableSourceContext.IntendedTarget != target)) { return false; } dodgeInvincible = false; return true; } catch (Exception exception) { ReportUndodgeableRuntimeFailure("dodge_override", exception); return false; } } internal static UndodgeableDamageScopeState BeginUndodgeableDamageScope() { UndodgeableSourceContext currentUndodgeableSourceContext = CurrentUndodgeableSourceContext; if (!currentUndodgeableSourceContext.IsActive) { return default(UndodgeableDamageScopeState); } CurrentUndodgeableSourceContext = default(UndodgeableSourceContext); return new UndodgeableDamageScopeState(currentUndodgeableSourceContext, changed: true); } internal static void EndUndodgeableDamageScope(ref UndodgeableDamageScopeState state) { if (!state.Changed) { state = default(UndodgeableDamageScopeState); return; } UndodgeableSourceContext restore = state.Restore; state = default(UndodgeableDamageScopeState); CurrentUndodgeableSourceContext = restore; } internal static void ApplyUndodgeableBeforeDamage(Character? target, HitData? hit) { try { if (target is Player && hit != null && HasActiveUndodgeableModifier(hit.GetAttacker())) { hit.m_dodgeable = false; } } catch (Exception exception) { ReportUndodgeableRuntimeFailure("damage_dispatch", exception); } } private static bool HasActiveUndodgeableModifier(Character? character) { float damageReduction; return TryGetActiveUndodgeableModifier(character, out damageReduction); } private static bool TryGetActiveUndodgeableModifier(Character? character, out float damageReduction) { damageReduction = 0f; if ((Object)(object)character == (Object)null || !TryGetModifierHotPathState(character, ModifierMask.Undodgeable, out ModifierHotPathState state) || !state.UndodgeableEffectActive) { return false; } damageReduction = state.UndodgeableDamageReduction; return true; } private static bool IsLocallyOwnedUndodgeableSource(Character attacker, ZNetView? sourceView) { if ((Object)(object)sourceView == (Object)null) { return true; } if (sourceView.IsValid()) { return sourceView.IsOwner(); } ZNetView nview = attacker.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { return nview.IsOwner(); } return false; } private static void ReportUndodgeableRuntimeFailure(string stage, Exception exception) { if (UndodgeableRuntimeFailureReported) { return; } UndodgeableRuntimeFailureReported = true; try { CreatureManagerPlugin.Log.LogWarning((object)("[Undodgeable] one evaluation failed open stage=" + stage + "; " + exception.GetType().Name + ": " + exception.Message)); } catch { } } private static Character? TryGetMonsterAiTarget(MonsterAI? monsterAI) { if ((Object)(object)monsterAI == (Object)null) { return null; } if (!MonsterAiTargetLookupDone) { CachedMonsterAiTargetField = typeof(MonsterAI).GetField("m_targetCreature", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MonsterAiTargetLookupDone = true; } object? obj = CachedMonsterAiTargetField?.GetValue(monsterAI); return (Character?)((obj is Character) ? obj : null); } private static bool TryGetBlinkDestination(Player target, out Vector3 destination) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)target).transform.position; Vector2 val = Random.insideUnitCircle * 2f; destination = new Vector3(position.x + val.x, position.y, position.z + val.y); return true; } private static void TeleportCharacter(Character character, Vector3 destination, Vector3 lookAt) { //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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = lookAt - destination; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { ((Component)character).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } Rigidbody val2 = default(Rigidbody); if (((Component)character).TryGetComponent(ref val2)) { val2.linearVelocity = Vector3.zero; val2.angularVelocity = Vector3.zero; val2.position = destination; } ((Component)character).transform.position = destination; } private static void ApplyOutgoingModifierEffects(Character attacker, Character target, HitData hit) { if (target is Player && TryGetActiveUndodgeableModifier(attacker, out var damageReduction)) { hit.m_dodgeable = false; ((DamageTypes)(ref hit.m_damage)).Modify(1f - damageReduction); } float num = Mathf.Max(0f, hit.GetTotalDamage()); if (num <= 0f) { return; } if (TryGetModifierPower(attacker, ModifierMask.Staggering, "CreatureManager_StaggeringPower", 0.5f, out var power)) { hit.m_staggerMultiplier *= 1f + power; } if (TryGetModifierPower(attacker, ModifierMask.Fire, "CreatureManager_FirePower", 0.2f, out var power2)) { hit.m_damage.m_fire += num * power2; } if (TryGetModifierPower(attacker, ModifierMask.Frost, "CreatureManager_FrostPower", 0.1f, out var power3)) { hit.m_damage.m_frost += num * power3; } if (TryGetModifierPower(attacker, ModifierMask.Lightning, "CreatureManager_LightningPower", 0.1f, out var power4)) { hit.m_damage.m_lightning += num * power4; } if (TryGetModifierPower(attacker, ModifierMask.Spirit, "CreatureManager_SpiritPower", 0.25f, out var power5)) { float num2 = num * power5; if (target is Player) { QueuePlayerSpiritDamage(hit, num2); } else { hit.m_damage.m_spirit += num2; } } if (TryGetModifierPower(attacker, ModifierMask.Vampiric, "CreatureManager_VampiricPower", 0.3f, out var power6)) { PendingVampiricDamage[hit] = new VampiricDamageContext(attacker, target, power6); } if (target is Player && TryGetModifierPower(attacker, ModifierMask.ArmorPiercing, "CreatureManager_ArmorPiercingPower", 0.3f, out var power7)) { CurrentRpcDamageContext.ArmorPiercing = Mathf.Max(CurrentRpcDamageContext.ArmorPiercing, power7); } TryArmKnockback(attacker, target, hit); } internal static ApplyDamageState BeginApplyDamage(Character target, HitData hit) { bool eligibleAtEntry = (Object)(object)target != (Object)null && hit != null && !target.IsDebugFlying() && !target.IsDead() && !target.IsTeleporting() && !target.InCutscene(); float healthBefore = (((Object)(object)target == (Object)null) ? 0f : Mathf.Max(0f, target.GetHealth())); return new ApplyDamageState(BeginDirectDamage(target, hit), eligibleAtEntry, healthBefore); } internal static void CompleteApplyDamage(Character target, HitData hit, ApplyDamageState state) { if (hit == null || !FinalDeathwardConsumedHits.Remove(hit)) { CompleteDirectDamage(target, state.DirectDamage); if (state.EligibleAtEntry && !((Object)(object)target == (Object)null) && hit != null) { bool hasResolvedOriginalHit = GetResolvedOriginalPlayerHitDamage(target, hit) > 0.1f; TryApplyPendingPlayerSpiritDamage(target, hit, hasResolvedOriginalHit); TryApplyPlayerDebuffModifiers(target, hit, hasResolvedOriginalHit); CaptureDelayedDamageDeathCredit(target, hit, state.HealthBefore); } } } internal static DirectDamageState BeginDirectDamage(Character target, HitData hit) { if ((Object)(object)target == (Object)null || hit == null) { return default(DirectDamageState); } Character val = null; float vampiricPower = 0f; if (PendingVampiricDamage.TryGetValue(hit, out var value)) { PendingVampiricDamage.Remove(hit); if (value.Target == target && (Object)(object)value.Attacker != (Object)null && value.Power > 0f) { val = value.Attacker; vampiricPower = value.Power; } } Character val2 = null; float reflectionPower = 0f; if (PendingReflectionDamage.TryGetValue(hit, out var value2)) { PendingReflectionDamage.Remove(hit); if (value2.Target == target && (Object)(object)value2.Attacker != (Object)null && value2.Power > 0f) { val2 = value2.Attacker; reflectionPower = value2.Power; } } if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null) { return default(DirectDamageState); } return new DirectDamageState(val, vampiricPower, val2, reflectionPower, Mathf.Max(0f, target.GetHealth())); } internal static void CompleteDirectDamage(Character target, DirectDamageState state) { if ((Object)(object)target == (Object)null || !state.IsValid) { return; } float num = Mathf.Max(0f, target.GetHealth()); float num2 = Mathf.Clamp(state.HealthBefore - num, 0f, state.HealthBefore); if (!(num2 <= 0f)) { if (state.HasVampiric && (Object)(object)state.VampiricAttacker != (Object)null) { state.VampiricAttacker.Heal(num2 * state.VampiricPower, true); } if (state.HasReflection && (Object)(object)state.ReflectionAttacker != (Object)null) { SendExactReflectionDamage(state.ReflectionAttacker, target, num2 * state.ReflectionPower); } } } private static void SendExactReflectionDamage(Character target, Character source, float amount) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || (Object)(object)source == (Object)null || (Object)(object)source.m_nview == (Object)null || !source.m_nview.IsValid() || target.IsDead() || amount <= 0f || ZRoutedRpc.instance == null) { return; } ZDOID zDOID = target.GetZDOID(); if (!(zDOID == ZDOID.None)) { long num = ++NextReflectionRequestId; if (num <= 0) { num = (NextReflectionRequestId = 1L); } source.m_nview.InvokeRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_RequestReflectionDamage", new object[3] { num, zDOID, amount }); } } private static void TryArmKnockback(Character attacker, Character target, HitData hit) { if (!(target is Player) || (Object)(object)attacker == (Object)null || attacker.IsPlayer() || hit == null || hit.GetTotalDamage() <= 0f || !IsHostileAttacker(target, attacker) || !TryGetZdo(attacker, out ZDO zdo) || !HasModifier(zdo, ModifierMask.Knockback) || !CreatureLevelManager.AllowsModifierEffects(attacker)) { return; } float num = ClampKnockbackPower(zdo.GetFloat("CreatureManager_KnockbackPower", 150f)); if (!(num <= 0f)) { int instanceID = ((Object)attacker).GetInstanceID(); float networkTimeSeconds = GetNetworkTimeSeconds(); double networkTimeSecondsDouble = GetNetworkTimeSecondsDouble(); if (!(zdo.GetFloat("CreatureManager_KnockbackNextReadyTime", 0f) > networkTimeSeconds) && (!LocalKnockbackReadyTimes.TryGetValue(instanceID, out var value) || !(value > networkTimeSecondsDouble)) && !PendingKnockbackReservations.ContainsKey(instanceID) && !PendingKnockbackHits.ContainsKey(hit)) { LocalKnockbackReadyTimes.Remove(instanceID); float cooldown = ResolveKnockbackCooldown(zdo.GetFloat("CreatureManager_KnockbackCooldown", 5f)); hit.m_pushForce = Mathf.Max(hit.m_pushForce, num); PendingKnockbackHits[hit] = new KnockbackHitContext(attacker, cooldown); PendingKnockbackReservations[instanceID] = hit; } } } internal static void ConfirmKnockbackPush(Character target, HitData hit) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if (!(target is Player) || hit == null || hit.m_pushForce == 0f || hit.m_dir == Vector3.zero || !PendingKnockbackHits.TryGetValue(hit, out var value)) { return; } PendingKnockbackHits.Remove(hit); ReleaseKnockbackReservation(value, hit); LocalKnockbackReadyTimes[value.AttackerId] = GetNetworkTimeSecondsDouble() + (double)value.Cooldown; if ((Object)(object)value.Attacker != (Object)null && (Object)(object)value.Attacker.m_nview != (Object)null && value.Attacker.m_nview.IsValid() && ZRoutedRpc.instance != null) { ZDOID zDOID = target.GetZDOID(); if (zDOID != ZDOID.None) { value.Attacker.m_nview.InvokeRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_RequestKnockbackCooldown", new object[1] { zDOID }); } } } private static void RPC_KnockbackCooldownRequest(Character attacker, long sender, ZDOID targetId) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_0141: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)attacker == (Object)null) && !attacker.IsPlayer() && !attacker.IsDead() && !((Object)(object)attacker.m_nview == (Object)null) && attacker.m_nview.IsValid() && TryGetZdo(attacker, out ZDO zdo) && HasModifier(zdo, ModifierMask.Knockback) && CreatureLevelManager.AllowsModifierEffects(attacker) && TryFindCharacter(targetId, out Character character) && character is Player && TryGetCharacterZdo(character, out ZDO zdo2) && zdo2.GetOwner() == sender && IsHostileAttacker(character, attacker) && !(Vector3.Distance(((Component)attacker).transform.position, ((Component)character).transform.position) > 128f)) { ZDOID zDOID = attacker.GetZDOID(); float networkTimeSeconds = GetNetworkTimeSeconds(); double networkTimeSecondsDouble = GetNetworkTimeSecondsDouble(); if (!(zDOID == ZDOID.None) && !(zdo.GetFloat("CreatureManager_KnockbackNextReadyTime", 0f) > networkTimeSeconds) && (!ServerKnockbackNextAllowedTimes.TryGetValue(zDOID, out var value) || !(value > networkTimeSecondsDouble))) { float num = ResolveKnockbackCooldown(zdo.GetFloat("CreatureManager_KnockbackCooldown", 5f)); float num2 = networkTimeSeconds + num; ServerKnockbackNextAllowedTimes[zDOID] = networkTimeSecondsDouble + (double)Mathf.Max(num, 0.05f); attacker.m_nview.InvokeRPC("CreatureManager_CommitKnockbackCooldown", new object[1] { num2 }); } } } private static void CommitAuthorizedKnockbackCooldown(Character attacker, long sender, float nextReadyTime) { if (IsTrustedServerRpc(sender) && CreatureLevelManager.IsLevelSystemEnabled() && !((Object)(object)attacker == (Object)null) && !attacker.IsPlayer() && !((Object)(object)attacker.m_nview == (Object)null) && attacker.m_nview.IsValid() && attacker.m_nview.IsOwner() && !(nextReadyTime < 0f) && !float.IsNaN(nextReadyTime) && !float.IsInfinity(nextReadyTime) && TryGetZdo(attacker, out ZDO zdo) && HasModifier(zdo, ModifierMask.Knockback) && CreatureLevelManager.AllowsModifierEffects(attacker) && nextReadyTime > zdo.GetFloat("CreatureManager_KnockbackNextReadyTime", 0f)) { zdo.Set("CreatureManager_KnockbackNextReadyTime", nextReadyTime); } } private static void ReleaseKnockbackReservation(KnockbackHitContext context, HitData hit) { if (PendingKnockbackReservations.TryGetValue(context.AttackerId, out HitData value) && value == hit) { PendingKnockbackReservations.Remove(context.AttackerId); } } private static void QueuePlayerSpiritDamage(HitData sourceHit, float amount) { if (sourceHit != null && !(amount <= 0f)) { if (PendingPlayerSpiritDamage.TryGetValue(sourceHit, out var value)) { PendingPlayerSpiritDamage[sourceHit] = value + amount; } else { PendingPlayerSpiritDamage[sourceHit] = amount; } } } internal static void TryApplyPendingPlayerSpiritDamage(Character target, HitData hit, bool hasResolvedOriginalHit) { if (hit != null && PendingPlayerSpiritDamage.TryGetValue(hit, out var value)) { PendingPlayerSpiritDamage.Remove(hit); Player val = (Player)(object)((target is Player) ? target : null); if (val != null && !((Character)val).IsDead() && hasResolvedOriginalHit) { ((Character)val).AddSpiritDamage(value); } } } internal static void TryApplyPlayerDebuffModifiers(Character target, HitData hit, bool hasResolvedOriginalHit) { if (!CreatureLevelManager.IsLevelSystemEnabled()) { return; } Player val = (Player)(object)((target is Player) ? target : null); if (val == null || hit == null || !hasResolvedOriginalHit || IsVanillaDelayedDamageTick(target, hit)) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null) && !attacker.IsPlayer() && !attacker.IsTamed()) { TryApplyPlayerDebuff(attacker, val, ModifierMask.Exposed, "CreatureManager_ExposedChance", "CreatureManager_ExposedPower", 0.2f, ExposedStatusHash, "CreatureManager_PlayerExposedPower", "CreatureManager_PlayerExposedUntil", "CreatureManager_ExposedDuration", 5f, delegate(PlayerDebuffPowers state, float value) { state.Exposed = value; }); TryApplyPlayerDebuff(attacker, val, ModifierMask.Weakened, "CreatureManager_WeakenedChance", "CreatureManager_WeakenedPower", 0.2f, WeakenedStatusHash, "CreatureManager_PlayerWeakenedPower", "CreatureManager_PlayerWeakenedUntil", "CreatureManager_WeakenedDuration", 5f, delegate(PlayerDebuffPowers state, float value) { state.Weakened = value; }); TryApplyPlayerDebuff(attacker, val, ModifierMask.Withered, "CreatureManager_WitheredChance", "CreatureManager_WitheredPower", 0.5f, WitheredStatusHash, "CreatureManager_PlayerWitheredPower", "CreatureManager_PlayerWitheredUntil", "CreatureManager_WitheredDuration", 5f, delegate(PlayerDebuffPowers state, float value) { state.Withered = value; }); TryApplySplitPlayerDebuff(attacker, val, ModifierMask.Crippling, "CreatureManager_CripplingChance", "CreatureManager_CripplingPower", 0.5f, "CreatureManager_CripplingJumpPower", 0.5f, CripplingStatusHash, "CreatureManager_PlayerCripplingPower", "CreatureManager_PlayerCripplingJumpPower", "CreatureManager_PlayerCripplingUntil", "CreatureManager_CripplingDuration", 3f, delegate(PlayerDebuffPowers state, float value) { state.Crippling = value; }, delegate(PlayerDebuffPowers state, float value) { state.CripplingJump = value; }); TryApplySplitPlayerDebuff(attacker, val, ModifierMask.Disruptive, "CreatureManager_DisruptiveChance", "CreatureManager_DisruptivePower", 0.5f, "CreatureManager_DisruptiveEitrPower", 0.5f, DisruptiveStatusHash, "CreatureManager_PlayerDisruptivePower", "CreatureManager_PlayerDisruptiveEitrPower", "CreatureManager_PlayerDisruptiveUntil", "CreatureManager_DisruptiveDuration", 3f, delegate(PlayerDebuffPowers state, float value) { state.Disruptive = value; }, delegate(PlayerDebuffPowers state, float value) { state.DisruptiveEitr = value; }); TryApplyPlayerDebuff(attacker, val, ModifierMask.Corrosive, "CreatureManager_CorrosiveChance", "CreatureManager_CorrosivePower", 0.5f, CorrosiveStatusHash, "CreatureManager_PlayerCorrosivePower", "CreatureManager_PlayerCorrosiveUntil", "CreatureManager_CorrosiveDuration", 5f, delegate(PlayerDebuffPowers state, float value) { state.Corrosive = value; }); TryDrainPlayerAdrenaline(attacker, val); } } private static float GetResolvedOriginalPlayerHitDamage(Character target, HitData hit) { float num = SanitizePositiveDamage(hit.GetTotalDamage()); if (!(target is Player) || !CurrentRpcDamageContext.DelayedDamageCaptured || CurrentRpcDamageContext.Target != target || CurrentRpcDamageContext.Hit != hit) { return num; } return num + CurrentRpcDamageContext.ResolvedDelayedDamage; } private static float SanitizePositiveDamage(float damage) { if (!float.IsNaN(damage) && !float.IsInfinity(damage) && !(damage <= 0f)) { return damage; } return 0f; } private static bool IsVanillaDelayedDamageTick(Character target, HitData hit) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 if (CurrentDelayedDamageTickContext.Target == target && CurrentDelayedDamageTickContext.HitType == hit.m_hitType) { if ((int)hit.m_hitType != 7) { return (int)hit.m_hitType == 5; } return true; } return false; } internal static void RecordPoisonDamageSource(SE_Poison status, bool accepted) { if (accepted && !((Object)(object)status == (Object)null) && !((Object)(object)((StatusEffect)status).m_character == (Object)null)) { Character character = ((StatusEffect)status).m_character; DelayedDamageSourceLedger delayedDamageSourceLedger = GetDelayedDamageSourceLedger(character); delayedDamageSourceLedger.PoisonStatus = status; delayedDamageSourceLedger.Poison = GetCurrentDelayedDamageAttribution(character); } } internal static void RecordFireDamageSource(SE_Burning status, bool poolWasEmpty, bool accepted) { RecordAccumulatingDelayedDamageSource(status, poolWasEmpty, accepted, spirit: false); } internal static void RecordSpiritDamageSource(SE_Burning status, bool poolWasEmpty, bool accepted) { RecordAccumulatingDelayedDamageSource(status, poolWasEmpty, accepted, spirit: true); } private static void RecordAccumulatingDelayedDamageSource(SE_Burning status, bool poolWasEmpty, bool accepted, bool spirit) { if (accepted && !((Object)(object)status == (Object)null) && !((Object)(object)((StatusEffect)status).m_character == (Object)null)) { Character character = ((StatusEffect)status).m_character; DelayedDamageSourceLedger delayedDamageSourceLedger = GetDelayedDamageSourceLedger(character); DelayedDamageAttribution currentDelayedDamageAttribution = GetCurrentDelayedDamageAttribution(character); if (spirit) { bool flag = delayedDamageSourceLedger.SpiritStatus == status; delayedDamageSourceLedger.Spirit = (poolWasEmpty ? currentDelayedDamageAttribution : MergeDelayedDamageAttribution(flag ? delayedDamageSourceLedger.Spirit : default(DelayedDamageAttribution), currentDelayedDamageAttribution)); delayedDamageSourceLedger.SpiritStatus = status; } else { bool flag2 = delayedDamageSourceLedger.FireStatus == status; delayedDamageSourceLedger.Fire = (poolWasEmpty ? currentDelayedDamageAttribution : MergeDelayedDamageAttribution(flag2 ? delayedDamageSourceLedger.Fire : default(DelayedDamageAttribution), currentDelayedDamageAttribution)); delayedDamageSourceLedger.FireStatus = status; } } } private static DelayedDamageAttribution MergeDelayedDamageAttribution(DelayedDamageAttribution current, DelayedDamageAttribution incoming) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (current.Kind == DelayedDamageAttributionKind.Exact && incoming.Kind == DelayedDamageAttributionKind.Exact && current.Source == incoming.Source) { return DelayedDamageAttribution.FromSource(current.Source, current.SourceWasPlayer || incoming.SourceWasPlayer); } if (current.Kind == DelayedDamageAttributionKind.Unattributed && incoming.Kind == DelayedDamageAttributionKind.Unattributed) { return current; } return new DelayedDamageAttribution(DelayedDamageAttributionKind.Ambiguous, ZDOID.None); } private static DelayedDamageSourceLedger GetDelayedDamageSourceLedger(Character target) { int instanceID = ((Object)target).GetInstanceID(); if (!DelayedDamageSourceLedgers.TryGetValue(instanceID, out DelayedDamageSourceLedger value) || value.Target != target) { value = new DelayedDamageSourceLedger { Target = target }; DelayedDamageSourceLedgers[instanceID] = value; } return value; } private static DelayedDamageAttribution GetCurrentDelayedDamageAttribution(Character target) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (CurrentRpcDamageContext.Target != target || CurrentRpcDamageContext.Hit == null) { return DelayedDamageAttribution.FromSource(ZDOID.None, sourceWasPlayer: false); } HitData? hit = CurrentRpcDamageContext.Hit; Character attacker = hit.GetAttacker(); return DelayedDamageAttribution.FromSource(hit.m_attacker, (Object)(object)attacker != (Object)null && attacker.IsPlayer()); } internal static DelayedDamageTickScopeState BeginPoisonDamageTick(SE_Poison status) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) DelayedDamageTickContext currentDelayedDamageTickContext = CurrentDelayedDamageTickContext; DelayedDamageAttribution attribution = default(DelayedDamageAttribution); Character val = ((StatusEffect)(status?)).m_character; if ((Object)(object)val != (Object)null && DelayedDamageSourceLedgers.TryGetValue(((Object)val).GetInstanceID(), out DelayedDamageSourceLedger value) && value.Target == val && value.PoisonStatus == status) { attribution = value.Poison; } CurrentDelayedDamageTickContext = new DelayedDamageTickContext { Target = val, HitType = (HitType)7, Attribution = attribution }; return new DelayedDamageTickScopeState(currentDelayedDamageTickContext); } internal static DelayedDamageTickScopeState BeginBurningDamageTick(SE_Burning status) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) DelayedDamageTickContext currentDelayedDamageTickContext = CurrentDelayedDamageTickContext; Character val = ((StatusEffect)(status?)).m_character; DelayedDamageAttribution fire = default(DelayedDamageAttribution); DelayedDamageAttribution spirit = default(DelayedDamageAttribution); if ((Object)(object)val != (Object)null && DelayedDamageSourceLedgers.TryGetValue(((Object)val).GetInstanceID(), out DelayedDamageSourceLedger value) && value.Target == val) { if (value.FireStatus == status) { fire = value.Fire; } if (value.SpiritStatus == status) { spirit = value.Spirit; } } bool hasFire = (Object)(object)status != (Object)null && status.m_fireDamagePerHit > 0f; bool hasSpirit = (Object)(object)status != (Object)null && status.m_spiritDamagePerHit > 0f; CurrentDelayedDamageTickContext = new DelayedDamageTickContext { Target = val, HitType = (HitType)5, Attribution = CombineDelayedDamageTickAttribution(fire, hasFire, spirit, hasSpirit) }; return new DelayedDamageTickScopeState(currentDelayedDamageTickContext); } internal static void EndDelayedDamageTick(DelayedDamageTickScopeState state) { if (state.Changed) { CurrentDelayedDamageTickContext = state.Previous; } } private static DelayedDamageAttribution CombineDelayedDamageTickAttribution(DelayedDamageAttribution fire, bool hasFire, DelayedDamageAttribution spirit, bool hasSpirit) { //IL_0077: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!hasFire) { if (!hasSpirit) { return default(DelayedDamageAttribution); } return spirit; } if (!hasSpirit) { return fire; } if (fire.Kind == DelayedDamageAttributionKind.Exact && spirit.Kind == DelayedDamageAttributionKind.Exact && fire.Source == spirit.Source) { return DelayedDamageAttribution.FromSource(fire.Source, fire.SourceWasPlayer || spirit.SourceWasPlayer); } if (fire.Kind == DelayedDamageAttributionKind.Unattributed && spirit.Kind == DelayedDamageAttributionKind.Unattributed) { return fire; } return new DelayedDamageAttribution(DelayedDamageAttributionKind.Ambiguous, ZDOID.None); } private static void CaptureDelayedDamageDeathCredit(Character target, HitData hit, float healthBefore) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!(healthBefore <= 0f) && !(target.GetHealth() > 0f) && target.m_lastHit == hit) { int instanceID = ((Object)target).GetInstanceID(); if (CurrentDelayedDamageTickContext.Target != target || CurrentDelayedDamageTickContext.HitType != hit.m_hitType) { PendingDelayedDamageDeathCredits.Remove(instanceID); return; } DelayedDamageAttribution attribution = CurrentDelayedDamageTickContext.Attribution; PendingDelayedDamageDeathCredits[instanceID] = new DelayedDamageDeathCredit(target, attribution.IsExact ? attribution.Source : ZDOID.None, attribution.IsExact && attribution.SourceWasPlayer); } } internal static void ClearRecoveredDelayedDamageDeathCredit(Character character) { if ((Object)(object)character != (Object)null && character.GetHealth() > 0f) { PendingDelayedDamageDeathCredits.Remove(((Object)character).GetInstanceID()); } } internal static void ApplyPlayerHealingDebuff(Character character, ref float amount) { if (CreatureLevelManager.IsLevelSystemEnabled()) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null && !(amount <= 0f) && TryGetActivePlayerDebuffPower(val, WitheredStatusHash, "CreatureManager_PlayerWitheredPower", "CreatureManager_PlayerWitheredUntil", (PlayerDebuffPowers state) => state.Withered, 0.5f, out var power)) { amount *= 1f - power; } } } internal static void RegisterStatusEffects(ObjectDB objectDb) { if (!((Object)(object)objectDb == (Object)null) && objectDb.m_StatusEffects != null) { RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_ExposedStatus", ExposedStatusHash, "$cm_modifier_exposed_name", "$cm_status_exposed_tooltip", GetExposedSprite()); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_WeakenedStatus", WeakenedStatusHash, "$cm_modifier_weakened_name", "$cm_status_weakened_tooltip", GetWeakenedSprite()); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_WitheredStatus", WitheredStatusHash, "$cm_modifier_withered_name", "$cm_status_withered_tooltip", GetWitheredSprite()); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_CripplingStatus", CripplingStatusHash, "$cm_modifier_crippling_name", "$cm_status_crippling_tooltip", GetCripplingSprite(), 3f); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_DisruptiveStatus", DisruptiveStatusHash, "$cm_modifier_disruptive_name", "$cm_status_disruptive_tooltip", GetDisruptiveSprite(), 3f); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_AdrenalineDrainStatus", AdrenalineDrainStatusHash, "$cm_modifier_adrenaline_drain_name", "$cm_status_adrenaline_drain_tooltip", GetAdrenalineDrainSprite()); RegisterPlayerDebuffStatusEffect(objectDb, "CreatureManager_CorrosiveStatus", CorrosiveStatusHash, "$cm_modifier_corrosive_name", "$cm_status_corrosive_tooltip", GetCorrosiveSprite()); } } private static void RegisterPlayerDebuffStatusEffect(ObjectDB objectDb, string internalName, int hash, string displayName, string tooltip, Sprite icon, float duration = 5f) { if (!((Object)(object)objectDb.GetStatusEffect(hash) != (Object)null)) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = internalName; ((StatusEffect)val).m_name = displayName; ((StatusEffect)val).m_tooltip = tooltip; ((StatusEffect)val).m_ttl = duration; ((StatusEffect)val).m_icon = icon; objectDb.m_StatusEffects.Add((StatusEffect)(object)val); } } private static void ApplyPlayerIncomingDebuffs(Player player, HitData hit) { if (!(hit.GetTotalDamage() <= 0f) && TryGetActivePlayerDebuffPower(player, ExposedStatusHash, "CreatureManager_PlayerExposedPower", "CreatureManager_PlayerExposedUntil", (PlayerDebuffPowers state) => state.Exposed, 0.2f, out var power)) { ((DamageTypes)(ref hit.m_damage)).Modify(1f + power); } } private static void ApplyPlayerOutgoingDebuffs(Player player, HitData hit) { if (!(hit.GetTotalDamage() <= 0f) && TryGetActivePlayerDebuffPower(player, WeakenedStatusHash, "CreatureManager_PlayerWeakenedPower", "CreatureManager_PlayerWeakenedUntil", (PlayerDebuffPowers state) => state.Weakened, 0.2f, out var power)) { ((DamageTypes)(ref hit.m_damage)).Modify(1f - power); } } internal static bool ShouldUpdatePlayerControlDebuffs(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner()) { return false; } int instanceID = ((Object)player).GetInstanceID(); if (ActivePlayerRuntimeDebuffs.ContainsKey(instanceID) || PlayerControlDebuffWakeIds.Contains(instanceID)) { return true; } float unscaledTime = Time.unscaledTime; if (NextInactivePlayerDebuffProbeTimes.TryGetValue(instanceID, out var value) && unscaledTime < value) { return false; } NextInactivePlayerDebuffProbeTimes[instanceID] = unscaledTime + 1f; return true; } internal static void UpdatePlayerControlDebuffs(Player player) { if ((Object)(object)player == (Object)null) { return; } int instanceID = ((Object)player).GetInstanceID(); if (!CreatureLevelManager.IsLevelSystemEnabled()) { if (ActivePlayerRuntimeDebuffs.TryGetValue(instanceID, out PlayerRuntimeDebuffs value)) { RestoreCrippling(player, value); ClearCorrosiveTracking(value); ActivePlayerRuntimeDebuffs.Remove(instanceID); } PlayerControlDebuffWakeIds.Remove(instanceID); NextInactivePlayerDebuffProbeTimes[instanceID] = Time.unscaledTime + 1f; return; } float primaryPower; float secondaryPower; bool flag = TryGetActivePlayerDebuffPowers(player, CripplingStatusHash, "CreatureManager_PlayerCripplingPower", "CreatureManager_PlayerCripplingJumpPower", "CreatureManager_PlayerCripplingUntil", (PlayerDebuffPowers state) => state.Crippling, (PlayerDebuffPowers state) => state.CripplingJump, 0.5f, 0.5f, out primaryPower, out secondaryPower); float primaryPower2; float secondaryPower2; bool flag2 = TryGetActivePlayerDebuffPowers(player, DisruptiveStatusHash, "CreatureManager_PlayerDisruptivePower", "CreatureManager_PlayerDisruptiveEitrPower", "CreatureManager_PlayerDisruptiveUntil", (PlayerDebuffPowers state) => state.Disruptive, (PlayerDebuffPowers state) => state.DisruptiveEitr, 0.5f, 0.5f, out primaryPower2, out secondaryPower2); float power = 0f; bool flag3 = player == Player.m_localPlayer && TryGetActivePlayerDebuffPower(player, CorrosiveStatusHash, "CreatureManager_PlayerCorrosivePower", "CreatureManager_PlayerCorrosiveUntil", (PlayerDebuffPowers state) => state.Corrosive, 0.5f, out power); if (!flag && !flag2 && !flag3 && !ActivePlayerRuntimeDebuffs.TryGetValue(instanceID, out PlayerRuntimeDebuffs _)) { PlayerControlDebuffWakeIds.Remove(instanceID); NextInactivePlayerDebuffProbeTimes[instanceID] = Time.unscaledTime + 1f; return; } if (!ActivePlayerRuntimeDebuffs.TryGetValue(instanceID, out PlayerRuntimeDebuffs value3)) { value3 = new PlayerRuntimeDebuffs(); ActivePlayerRuntimeDebuffs[instanceID] = value3; } if (flag) { ApplyCrippling(player, value3, primaryPower, secondaryPower); } else { RestoreCrippling(player, value3); } if (flag2) { ApplyDisruptive(player, value3, primaryPower2, secondaryPower2); } else { value3.LastStamina = -1f; value3.LastEitr = -1f; } if (flag3) { ApplyCorrosiveDurability(player, value3, power); } else { ClearCorrosiveTracking(value3); } if (!flag && !flag2 && !flag3 && !value3.MovementApplied) { ActivePlayerRuntimeDebuffs.Remove(instanceID); PlayerControlDebuffWakeIds.Remove(instanceID); NextInactivePlayerDebuffProbeTimes[instanceID] = Time.unscaledTime + 1f; } } private static void ApplyCrippling(Player player, PlayerRuntimeDebuffs runtime, float movementPower, float jumpPower) { movementPower = Mathf.Clamp01(movementPower); jumpPower = Mathf.Clamp01(jumpPower); if (!runtime.MovementApplied || !Mathf.Approximately(runtime.MovementPower, movementPower) || !Mathf.Approximately(runtime.JumpPower, jumpPower)) { RestoreCrippling(player, runtime); runtime.OriginalCrouchSpeed = ((Character)player).m_crouchSpeed; runtime.OriginalWalkSpeed = ((Character)player).m_walkSpeed; runtime.OriginalSpeed = ((Character)player).m_speed; runtime.OriginalRunSpeed = ((Character)player).m_runSpeed; runtime.OriginalJumpForce = ((Character)player).m_jumpForce; runtime.OriginalJumpForceForward = ((Character)player).m_jumpForceForward; runtime.MovementPower = movementPower; runtime.JumpPower = jumpPower; runtime.MovementApplied = true; float num = 1f - movementPower; ((Character)player).m_crouchSpeed = ((Character)player).m_crouchSpeed * num; ((Character)player).m_walkSpeed = ((Character)player).m_walkSpeed * num; ((Character)player).m_speed = ((Character)player).m_speed * num; ((Character)player).m_runSpeed = ((Character)player).m_runSpeed * num; float num2 = 1f - jumpPower; ((Character)player).m_jumpForce = ((Character)player).m_jumpForce * num2; ((Character)player).m_jumpForceForward = ((Character)player).m_jumpForceForward * num2; runtime.AppliedCrouchSpeed = ((Character)player).m_crouchSpeed; runtime.AppliedWalkSpeed = ((Character)player).m_walkSpeed; runtime.AppliedSpeed = ((Character)player).m_speed; runtime.AppliedRunSpeed = ((Character)player).m_runSpeed; runtime.AppliedJumpForce = ((Character)player).m_jumpForce; runtime.AppliedJumpForceForward = ((Character)player).m_jumpForceForward; } } private static void RestoreCrippling(Player player, PlayerRuntimeDebuffs runtime) { if (runtime.MovementApplied) { RestoreCripplingValue(ref ((Character)player).m_crouchSpeed, runtime.AppliedCrouchSpeed, runtime.OriginalCrouchSpeed); RestoreCripplingValue(ref ((Character)player).m_walkSpeed, runtime.AppliedWalkSpeed, runtime.OriginalWalkSpeed); RestoreCripplingValue(ref ((Character)player).m_speed, runtime.AppliedSpeed, runtime.OriginalSpeed); RestoreCripplingValue(ref ((Character)player).m_runSpeed, runtime.AppliedRunSpeed, runtime.OriginalRunSpeed); RestoreCripplingValue(ref ((Character)player).m_jumpForce, runtime.AppliedJumpForce, runtime.OriginalJumpForce); RestoreCripplingValue(ref ((Character)player).m_jumpForceForward, runtime.AppliedJumpForceForward, runtime.OriginalJumpForceForward); runtime.MovementApplied = false; runtime.MovementPower = 0f; runtime.JumpPower = 0f; } } private static void RestoreCripplingValue(ref float current, float applied, float original) { if (Mathf.Approximately(current, applied)) { current = original; } } private static void ApplyDisruptive(Player player, PlayerRuntimeDebuffs runtime, float staminaPower, float eitrPower) { staminaPower = Mathf.Clamp01(staminaPower); eitrPower = Mathf.Clamp01(eitrPower); float stamina = player.GetStamina(); if (runtime.LastStamina >= 0f && stamina > runtime.LastStamina) { ((Character)player).UseStamina((stamina - runtime.LastStamina) * staminaPower); stamina = player.GetStamina(); } runtime.LastStamina = stamina; float eitr = player.GetEitr(); if (runtime.LastEitr >= 0f && eitr > runtime.LastEitr) { ((Character)player).UseEitr((eitr - runtime.LastEitr) * eitrPower); eitr = player.GetEitr(); } runtime.LastEitr = eitr; } private static void ApplyCorrosiveDurability(Player player, PlayerRuntimeDebuffs runtime, float power) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { ClearCorrosiveTracking(runtime); return; } power = Mathf.Clamp01(power); List currentDurabilityItems = runtime.CurrentDurabilityItems; Dictionary durabilitySnapshots = runtime.DurabilitySnapshots; currentDurabilityItems.Clear(); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && IsCorrosiveDurabilityTarget(allItem)) { currentDurabilityItems.Add(allItem); float durability = allItem.m_durability; if (durabilitySnapshots.TryGetValue(allItem, out var value) && durability < value && power > 0f) { float num = (value - durability) * power; allItem.m_durability = Mathf.Max(0f, durability - num); durability = allItem.m_durability; } durabilitySnapshots[allItem] = durability; } } List removedDurabilityItems = runtime.RemovedDurabilityItems; removedDurabilityItems.Clear(); foreach (ItemData key in durabilitySnapshots.Keys) { if (!currentDurabilityItems.Contains(key)) { removedDurabilityItems.Add(key); } } foreach (ItemData item in removedDurabilityItems) { durabilitySnapshots.Remove(item); } } private static bool IsCorrosiveDurabilityTarget(ItemData item) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 SharedData shared = item.m_shared; if (!item.m_equipped || shared == null || !shared.m_useDurability) { return false; } ItemType itemType = shared.m_itemType; if (!item.IsWeapon() && (int)itemType != 5 && (int)itemType != 6 && (int)itemType != 7 && (int)itemType != 11) { return (int)itemType == 17; } return true; } private static void ClearCorrosiveTracking(PlayerRuntimeDebuffs runtime) { runtime.DurabilitySnapshots.Clear(); runtime.CurrentDurabilityItems.Clear(); runtime.RemovedDurabilityItems.Clear(); } private static void TryDrainPlayerAdrenaline(Character attacker, Player player) { ZNetView nview = ((Character)player).m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || !TryGetProcModifierState(attacker, ModifierMask.AdrenalineDrain, "CreatureManager_AdrenalineDrainChance", out ZDO zdo, out float chance) || Random.Range(0f, 100f) >= chance) { return; } float num = Mathf.Clamp01(zdo.GetFloat("CreatureManager_AdrenalineDrainPower", 0.5f)); float num2 = Mathf.Max(0f, player.GetAdrenaline()) * num; if (num2 > 0f) { ((Character)player).AddAdrenaline(0f - num2); } float num3 = Mathf.Clamp01(zdo.GetFloat("CreatureManager_AdrenalineDrainGainReduction", 0.5f)); float ttl = ResolvePlayerDebuffDuration(zdo.GetFloat("CreatureManager_AdrenalineDrainDuration", 5f), 5f); if (!(num3 <= 0f)) { StatusEffect obj = ((Character)player).GetSEMan().AddStatusEffect(AdrenalineDrainStatusHash, true, 0, 0f); SE_Stats val = (SE_Stats)(object)((obj is SE_Stats) ? obj : null); if (val != null) { ((StatusEffect)val).m_ttl = ttl; val.m_adrenalineModifier = 0f - num3; } } } private static void TryApplyPlayerDebuff(Character attacker, Player player, ModifierMask modifier, string chanceKey, string powerKey, float fallbackPower, int statusHash, string playerPowerKey, string playerUntilKey, string durationKey, float fallbackDuration, Action powerSetter) { if (!TryGetProcModifierState(attacker, modifier, chanceKey, out ZDO zdo, out float chance) || Random.Range(0f, 100f) >= chance) { return; } float num = Mathf.Clamp01(zdo.GetFloat(powerKey, fallbackPower)); if (num <= 0f) { return; } float num2 = ResolvePlayerDebuffDuration(zdo.GetFloat(durationKey, fallbackDuration), fallbackDuration); StatusEffect val = ((Character)player).GetSEMan().AddStatusEffect(statusHash, true, 0, 0f); if (!((Object)(object)val == (Object)null)) { val.m_ttl = num2; int instanceID = ((Object)player).GetInstanceID(); WakePlayerControlDebuffUpdate(instanceID, statusHash); if (!ActivePlayerDebuffs.TryGetValue(instanceID, out PlayerDebuffPowers value)) { value = new PlayerDebuffPowers(); ActivePlayerDebuffs[instanceID] = value; } powerSetter(value, num); StorePlayerDebuff(player, playerPowerKey, playerUntilKey, num, num2); } } private static void TryApplySplitPlayerDebuff(Character attacker, Player player, ModifierMask modifier, string chanceKey, string primaryPowerKey, float fallbackPrimaryPower, string secondaryPowerKey, float fallbackSecondaryPower, int statusHash, string playerPrimaryPowerKey, string playerSecondaryPowerKey, string playerUntilKey, string durationKey, float fallbackDuration, Action primarySetter, Action secondarySetter) { if (!TryGetProcModifierState(attacker, modifier, chanceKey, out ZDO zdo, out float chance) || Random.Range(0f, 100f) >= chance) { return; } float num = Mathf.Clamp01(zdo.GetFloat(primaryPowerKey, fallbackPrimaryPower)); float num2 = Mathf.Clamp01(zdo.GetFloat(secondaryPowerKey, fallbackSecondaryPower)); if (num <= 0f && num2 <= 0f) { return; } float num3 = ResolvePlayerDebuffDuration(zdo.GetFloat(durationKey, fallbackDuration), fallbackDuration); StatusEffect val = ((Character)player).GetSEMan().AddStatusEffect(statusHash, true, 0, 0f); if (!((Object)(object)val == (Object)null)) { val.m_ttl = num3; int instanceID = ((Object)player).GetInstanceID(); WakePlayerControlDebuffUpdate(instanceID, statusHash); if (!ActivePlayerDebuffs.TryGetValue(instanceID, out PlayerDebuffPowers value)) { value = new PlayerDebuffPowers(); ActivePlayerDebuffs[instanceID] = value; } primarySetter(value, num); secondarySetter(value, num2); StorePlayerDebuff(player, playerPrimaryPowerKey, playerUntilKey, num, num3, playerSecondaryPowerKey, num2); } } private static void WakePlayerControlDebuffUpdate(int playerId, int statusHash) { if (statusHash == CripplingStatusHash || statusHash == DisruptiveStatusHash || statusHash == CorrosiveStatusHash) { PlayerControlDebuffWakeIds.Add(playerId); NextInactivePlayerDebuffProbeTimes.Remove(playerId); } } private static bool TryGetProcModifierState(Character character, ModifierMask modifier, string chanceKey, out ZDO zdo, out float chance) { zdo = null; chance = 0f; if (!TryGetZdo(character, out zdo) || !HasModifier(zdo, modifier) || !CreatureLevelManager.AllowsModifierEffects(character)) { return false; } chance = ClampChance(zdo.GetFloat(chanceKey, 0f)); return chance > 0f; } private static bool TryGetActivePlayerDebuffPower(Player player, int statusHash, string powerKey, string untilKey, Func selector, float fallbackPower, out float power) { power = 0f; if (TryGetStoredPlayerDebuff(player, powerKey, untilKey, out power)) { return true; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null || !sEMan.HaveStatusEffect(statusHash)) { return false; } if (!ActivePlayerDebuffs.TryGetValue(((Object)player).GetInstanceID(), out PlayerDebuffPowers value)) { power = fallbackPower; return power > 0f; } power = Mathf.Clamp01(selector(value)); if (power <= 0f) { power = fallbackPower; } return power > 0f; } private static bool TryGetActivePlayerDebuffPowers(Player player, int statusHash, string primaryPowerKey, string secondaryPowerKey, string untilKey, Func primarySelector, Func secondarySelector, float fallbackPrimaryPower, float fallbackSecondaryPower, out float primaryPower, out float secondaryPower) { primaryPower = 0f; secondaryPower = 0f; if (TryGetStoredPlayerDebuffState(player, untilKey, out ZDO zdo)) { primaryPower = Mathf.Clamp01(zdo.GetFloat(primaryPowerKey, fallbackPrimaryPower)); secondaryPower = Mathf.Clamp01(zdo.GetFloat(secondaryPowerKey, fallbackSecondaryPower)); if (!(primaryPower > 0f)) { return secondaryPower > 0f; } return true; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null || !sEMan.HaveStatusEffect(statusHash)) { return false; } if (!ActivePlayerDebuffs.TryGetValue(((Object)player).GetInstanceID(), out PlayerDebuffPowers value)) { primaryPower = fallbackPrimaryPower; secondaryPower = fallbackSecondaryPower; if (!(primaryPower > 0f)) { return secondaryPower > 0f; } return true; } primaryPower = Mathf.Clamp01(primarySelector(value)); secondaryPower = Mathf.Clamp01(secondarySelector(value)); if (!(primaryPower > 0f)) { return secondaryPower > 0f; } return true; } private static void StorePlayerDebuff(Player player, string powerKey, string untilKey, float power, float duration, string? secondaryPowerKey = null, float secondaryPower = 0f) { ZNetView nview = ((Character)player).m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } ZDO zDO = nview.GetZDO(); if (zDO != null) { zDO.Set(powerKey, Mathf.Clamp01(power)); if (secondaryPowerKey != null) { zDO.Set(secondaryPowerKey, Mathf.Clamp01(secondaryPower)); } zDO.Set(untilKey, GetNetworkTimeSeconds() + Mathf.Max(0.1f, duration)); } } private static bool TryGetStoredPlayerDebuff(Player player, string powerKey, string untilKey, out float power) { power = 0f; if (!TryGetStoredPlayerDebuffState(player, untilKey, out ZDO zdo)) { return false; } power = Mathf.Clamp01(zdo.GetFloat(powerKey, 0f)); return power > 0f; } private static bool TryGetStoredPlayerDebuffState(Player player, string untilKey, out ZDO zdo) { zdo = null; ZNetView nview = ((Character)player).m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } zdo = nview.GetZDO(); if (zdo != null) { return zdo.GetFloat(untilKey, 0f) > GetNetworkTimeSeconds(); } return false; } private static float GetNetworkTimeSeconds() { if (!((Object)(object)ZNet.instance != (Object)null)) { return Time.time; } return (float)ZNet.instance.GetTimeSeconds(); } private static double GetNetworkTimeSecondsDouble() { if (!((Object)(object)ZNet.instance != (Object)null)) { return Time.time; } return ZNet.instance.GetTimeSeconds(); } internal static float ResolveFinalDeathwardDamage(Character target, HitData hit, float finalDamage) { //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || hit == null || finalDamage <= 0.1f || float.IsNaN(finalDamage) || float.IsInfinity(finalDamage)) { return finalDamage; } DamageTypes damage = hit.m_damage; float originalHealth = float.NaN; ZDO zdo = null; int num = 0; float originalNextReadyTime = 0f; bool flag = false; try { ZNetView nview = target.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && !nview.IsOwner()) { return finalDamage; } if (!TryGetDeathwardHealth(target, out var healthRatio)) { return finalDamage; } float health = target.GetHealth(); float maxHealth = target.GetMaxHealth(); if (target.InGodMode() || target.InGhostMode() || health <= 0f || float.IsNaN(health) || float.IsInfinity(health) || maxHealth <= 0f || float.IsNaN(maxHealth) || float.IsInfinity(maxHealth) || health - finalDamage > 0f) { return finalDamage; } if (!TryGetZdo(target, out ZDO zdo2)) { return finalDamage; } num = zdo2.GetInt("CreatureManager_DeathwardActivationCount", 0); int num2 = Math.Max(0, num); int num3 = ResolveDeathwardMaxActivations(zdo2.GetInt("CreatureManager_DeathwardMaxActivations", 3)); if (num2 >= num3) { return finalDamage; } float num4 = ResolveDeathwardCooldown(zdo2.GetFloat("CreatureManager_DeathwardCooldown", 10f)); originalHealth = health; zdo = zdo2; originalNextReadyTime = zdo2.GetFloat("CreatureManager_DeathwardNextReadyTime", 0f); flag = true; zdo2.Set("CreatureManager_DeathwardActivationCount", num2 + 1); zdo2.Set("CreatureManager_DeathwardNextReadyTime", GetNetworkTimeSeconds() + num4); InvalidateModifierHudState(target); ((DamageTypes)(ref hit.m_damage)).Modify(0f); target.SetHealth(Mathf.Max(1f, maxHealth * healthRatio)); FinalDeathwardConsumedHits.Add(hit); PlayDeathwardTriggerEffects(target.GetCenterPoint()); return 0f; } catch (Exception exception) { FinalDeathwardConsumedHits.Remove(hit); if (flag) { TryRollbackDeathwardCommit(target, hit, damage, originalHealth, zdo, num, originalNextReadyTime); } ReportDeathwardRuntimeFailure(exception); return finalDamage; } } private static void TryRollbackDeathwardCommit(Character target, HitData hit, DamageTypes originalDamage, float originalHealth, ZDO? zdo, int originalActivationCount, float originalNextReadyTime) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { hit.m_damage = originalDamage; } catch { } try { if (!float.IsNaN(originalHealth) && !float.IsInfinity(originalHealth)) { target.SetHealth(originalHealth); } } catch { } try { if (zdo != null) { zdo.Set("CreatureManager_DeathwardActivationCount", originalActivationCount); zdo.Set("CreatureManager_DeathwardNextReadyTime", originalNextReadyTime); InvalidateModifierHudState(target); } } catch { } } private static void ReportDeathwardRuntimeFailure(Exception exception) { if (DeathwardRuntimeFailureReported) { return; } DeathwardRuntimeFailureReported = true; try { CreatureManagerPlugin.Log.LogWarning((object)("[Deathward] final-damage evaluation failed open; " + exception.GetType().Name + ": " + exception.Message)); } catch { } } internal static void UpdatePassiveModifiers(Character character) { if (!CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return; } if (!TryGetZdo(character, out ZDO zdo) || !zdo.GetBool("CreatureManager_ModifiersApplied", false)) { UntrackRuntimeModifiers(character); return; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ApplyRuntimeModifierStats(character, zdo); if (!CreatureLevelManager.AllowsModifierEffects(character)) { return; } float time = Time.time; ModifierMask storedModifierMask = GetStoredModifierMask(zdo); PassiveModifierSchedule passiveModifierSchedule = GetPassiveModifierSchedule(character, zdo, time); if (HasModifier(storedModifierMask, ModifierMask.Regenerating) && zdo.GetFloat("CreatureManager_RegeneratingPower", 0f) > 0f && time >= passiveModifierSchedule.NextRegeneration) { passiveModifierSchedule.NextRegeneration = time + 1f; float num = character.GetMaxHealth() * Mathf.Clamp01(zdo.GetFloat("CreatureManager_RegeneratingPower", 0.003f)); if (num > 0f) { character.Heal(num, false); } } if (HasModifier(storedModifierMask, ModifierMask.Chameleon) && time >= passiveModifierSchedule.NextChameleon) { UpdateChameleon(character, zdo, time, passiveModifierSchedule); } if (HasModifier(storedModifierMask, ModifierMask.Blamer) && time >= passiveModifierSchedule.NextBlamer) { UpdateBlamer(character, zdo, time, passiveModifierSchedule); } TrackRuntimeModifiers(character, zdo); } private static void InitializeChameleonState(Character character, ZDO zdo) { ChameleonDamageType chameleonDamageType = SelectChameleonDamageType(character, ChameleonDamageType.None); zdo.Set("CreatureManager_ChameleonType", (int)chameleonDamageType); float num = ResolveChameleonInterval(zdo.GetFloat("CreatureManager_ChameleonInterval", 10f)); GetPassiveModifierSchedule(character, zdo, Time.time).NextChameleon = Time.time + num; } private static void UpdateChameleon(Character character, ZDO zdo, float now, PassiveModifierSchedule schedule) { float num = ResolveChameleonInterval(zdo.GetFloat("CreatureManager_ChameleonInterval", 10f)); schedule.NextChameleon = now + num; BaseAI baseAI = character.GetBaseAI(); if (!((Object)(object)baseAI == (Object)null) && baseAI.IsAlerted()) { ChameleonDamageType chameleonDamageType = GetChameleonDamageType(zdo); ChameleonDamageType chameleonDamageType2 = SelectChameleonDamageType(character, chameleonDamageType); if (chameleonDamageType2 != chameleonDamageType) { zdo.Set("CreatureManager_ChameleonType", (int)chameleonDamageType2); InvalidateModifierHudState(character); } } } private static void UpdateBlamer(Character character, ZDO zdo, float now, PassiveModifierSchedule schedule) { schedule.NextBlamer = now + 1f; if (!character.IsTamed() && !CreatureKarmaManager.IsKarmaSummonedCreature(character)) { BaseAI baseAI = character.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val != null && IsBlamerFleeTargetValid(character, val, zdo)) { float num = ResolveBlamerKarmaPerSecond(zdo.GetFloat("CreatureManager_BlamerKarmaPerSecond", 1f)); float num2 = ResolveBlamerMaxKarmaGain(zdo.GetFloat("CreatureManager_BlamerMaxKarmaGain", 60f)); float blamerAccumulatedKarma = GetBlamerAccumulatedKarma(zdo); float num3 = ((num2 > 0f) ? Mathf.Min(num, Mathf.Max(0f, num2 - blamerAccumulatedKarma)) : num); if (num3 <= 0f) { if (num2 > 0f && blamerAccumulatedKarma >= num2) { ExhaustBlamer(character, zdo); } else { SetBlamerActive(character, zdo, active: false); } return; } SetBlamerActive(character, zdo, active: true); float confirmedAccumulated; switch (TryAddBlamerKarma(character, zdo, num3, out confirmedAccumulated)) { case BlamerKarmaAddResult.Failed: case BlamerKarmaAddResult.Pending: break; case BlamerKarmaAddResult.Saturated: schedule.NextBlamer = now + 5f; SetBlamerActive(character, zdo, active: false); break; default: CommitBlamerKarma(character, zdo, confirmedAccumulated, active: true); break; } return; } } SetBlamerActive(character, zdo, active: false); } private static BlamerKarmaAddResult TryAddBlamerKarma(Character character, ZDO zdo, float amount, out float confirmedAccumulated) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) float num = (confirmedAccumulated = GetBlamerAccumulatedKarma(zdo)); if ((Object)(object)ZNet.instance == (Object)null) { switch (CreatureKarmaManager.TryAddBlamerKarma(((Component)character).transform.position, amount)) { case CreatureKarmaManager.KarmaAddResult.Added: confirmedAccumulated = num + amount; return BlamerKarmaAddResult.Added; case CreatureKarmaManager.KarmaAddResult.Saturated: return BlamerKarmaAddResult.Saturated; default: return BlamerKarmaAddResult.Failed; } } if (ZNet.instance.IsServer()) { ServerBlamerKarmaState state; BlamerKarmaAddResult blamerKarmaAddResult = TryGrantServerBlamerKarma(zdo, out confirmedAccumulated, out state); state.RequestOwner = zdo.GetOwner(); state.HasCachedResponse = false; if (blamerKarmaAddResult != BlamerKarmaAddResult.Added && !(confirmedAccumulated > num)) { return blamerKarmaAddResult; } return BlamerKarmaAddResult.Added; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || ZRoutedRpc.instance == null) { return BlamerKarmaAddResult.Failed; } BaseAI baseAI = character.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val != null) { Character? obj = TryGetMonsterAiTarget(val); Player val2 = (Player)(object)((obj is Player) ? obj : null); if (val2 != null && !((Character)val2).IsDead()) { ZDOID zDOID = ((Character)val2).GetZDOID(); if (zDOID == ZDOID.None) { return BlamerKarmaAddResult.Failed; } int instanceID = ((Object)character).GetInstanceID(); float unscaledTime = Time.unscaledTime; if (PendingBlamerKarmaRequests.TryGetValue(instanceID, out PendingBlamerKarmaRequest value)) { if (!(value.TargetId != zDOID)) { if (unscaledTime - value.SentAt < 5f) { return BlamerKarmaAddResult.Pending; } value.SentAt = unscaledTime; if (!SendBlamerKarmaRequest(zdo.m_uid, value)) { return BlamerKarmaAddResult.Failed; } return BlamerKarmaAddResult.Pending; } PendingBlamerKarmaRequests.Remove(instanceID); } long num2 = ++NextBlamerKarmaRequestId; if (num2 <= 0) { num2 = (NextBlamerKarmaRequestId = 1L); } PendingBlamerKarmaRequests[instanceID] = new PendingBlamerKarmaRequest { RequestId = num2, SentAt = unscaledTime, TargetId = zDOID }; if (!SendBlamerKarmaRequest(zdo.m_uid, PendingBlamerKarmaRequests[instanceID])) { return BlamerKarmaAddResult.Failed; } return BlamerKarmaAddResult.Pending; } } return BlamerKarmaAddResult.Failed; } private static bool SendBlamerKarmaRequest(ZDOID creatureId, PendingBlamerKarmaRequest pending) { //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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (creatureId == ZDOID.None || ZRoutedRpc.instance == null) { return false; } try { ZPackage val = new ZPackage(); val.Write(creatureId); val.Write(pending.RequestId); val.Write(pending.TargetId); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_BlamerKarmaRequest", new object[1] { val }); return true; } catch { return false; } } private static void RPC_BlamerKarmaRequest(long sender, ZPackage package) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || ZRoutedRpc.instance == null) { return; } ZDOID val; long num; ZDOID targetId; try { val = package.ReadZDOID(); num = package.ReadLong(); targetId = package.ReadZDOID(); } catch { return; } if (val == ZDOID.None || num <= 0) { return; } ZDO zDO = ZDOMan.instance.GetZDO(val); if (zDO == null) { SendBlamerKarmaResponse(sender, val, num, BlamerKarmaAddResult.Failed, 0f); return; } BlamerKarmaAddResult blamerKarmaAddResult = BlamerKarmaAddResult.Failed; float blamerAccumulatedKarma = GetBlamerAccumulatedKarma(zDO); if (zDO.GetOwner() != sender) { LogBlamerRequestRejection(zDO, sender, $"ZDO owner {zDO.GetOwner()} does not match the request sender"); SendBlamerKarmaResponse(sender, val, num, BlamerKarmaAddResult.Failed, Mathf.Max(0f, blamerAccumulatedKarma)); return; } ServerBlamerKarmaState state = GetServerBlamerKarmaState(zDO); if (state.RequestOwner != sender) { state.RequestOwner = sender; state.HasCachedResponse = false; } if (state.HasCachedResponse && state.LastRequestId == num) { SendBlamerKarmaResponse(sender, val, num, state.LastResult, state.LastResponseAccumulated); return; } if (state.HasCachedResponse && num < state.LastRequestId) { SendBlamerKarmaResponse(sender, val, num, BlamerKarmaAddResult.Failed, state.Accumulated); return; } if (IsServerBlamerRequestValid(zDO, targetId, out string rejectionReason)) { blamerKarmaAddResult = TryGrantServerBlamerKarma(zDO, out float _, out state); if (blamerKarmaAddResult == BlamerKarmaAddResult.Failed && Time.unscaledTime >= state.NextAllowedTime) { LogBlamerRequestRejection(zDO, sender, "the authoritative Karma grant was unavailable"); } } else { LogBlamerRequestRejection(zDO, sender, rejectionReason); } state.LastRequestId = num; state.HasCachedResponse = true; state.LastResult = blamerKarmaAddResult; state.LastResponseAccumulated = state.Accumulated; SendBlamerKarmaResponse(sender, val, num, blamerKarmaAddResult, state.Accumulated); } private static bool IsServerBlamerRequestValid(ZDO zdo, ZDOID targetId, out string rejectionReason) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) rejectionReason = "unknown validation failure"; float num = ResolveBlamerFleeHealthRatio(zdo.GetFloat("CreatureManager_BlamerFleeHealthRatio", 0.75f)); if (!zdo.GetBool("CreatureManager_ModifiersApplied", false) || !HasModifier(GetStoredModifierMask(zdo), ModifierMask.Blamer)) { rejectionReason = "the Blamer modifier is not present in the authoritative ZDO"; return false; } if (num <= 0f) { rejectionReason = "the configured flee-health ratio is zero"; return false; } Vector3 position = zdo.GetPosition(); float num2 = zdo.GetFloat(ZDOVars.s_health, float.NaN); if (!IsFinite(position) || float.IsNaN(num2) || float.IsInfinity(num2) || num2 <= 0f || zdo.GetBool(ZDOVars.s_dead, false) || zdo.GetBool(ZDOVars.s_tamed, false)) { rejectionReason = "the creature ZDO position or living health state is invalid"; return false; } if ((Object)(object)ZNetScene.instance == (Object)null) { rejectionReason = "the server scene is unavailable"; return false; } GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)prefab == (Object)null || (Object)(object)val == (Object)null || val is Player || (Object)(object)prefab.GetComponent() == (Object)null) { rejectionReason = "the creature prefab is missing Character or MonsterAI metadata"; return false; } bool isEnforcer = zdo.GetBool("CreatureManager_KarmaEnforcer", false); if (zdo.GetBool("CreatureManager_KarmaEnforcerSummoned", false) || !CreatureLevelManager.AllowsModifierEffects(zdo, val.IsBoss(), isEnforcer)) { rejectionReason = "modifier effects are disabled for this creature"; return false; } float num3 = zdo.GetFloat(ZDOVars.s_maxHealth, float.NaN); if (float.IsNaN(num3) || float.IsInfinity(num3) || num3 <= 0f) { int num4 = Mathf.Max(1, zdo.GetInt(ZDOVars.s_level, 1)); num3 = Mathf.Max(0.01f, val.m_health * (float)num4); if (zdo.GetBool("CreatureManager_LevelHealthApplied", false)) { float num5 = zdo.GetFloat("CreatureManager_LevelHealthMultiplier", float.NaN); if (float.IsNaN(num5) || float.IsInfinity(num5) || num5 <= 0f) { rejectionReason = "the authoritative maximum-health multiplier is invalid"; return false; } num3 *= num5; } } if (float.IsNaN(num3) || float.IsInfinity(num3) || num3 <= 0f) { rejectionReason = "the authoritative maximum health is invalid"; return false; } float num6 = Mathf.Clamp01(num2 / num3); if (num6 >= num) { rejectionReason = $"server health {num6:P0} is not below {num:P0}"; return false; } if (!zdo.GetBool(ZDOVars.s_alert, false) || !zdo.GetBool(ZDOVars.s_haveTargetHash, false)) { rejectionReason = "the server ZDO is not alerted or has no target"; return false; } if (!TryGetConnectedPlayerZdo(targetId, out ZDO targetZdo, out rejectionReason)) { return false; } Vector3 position2 = targetZdo.GetPosition(); if (!IsFinite(position2)) { rejectionReason = "the player target position is invalid"; return false; } float num7 = Vector3.Distance(position, position2); if (float.IsNaN(num7) || float.IsInfinity(num7) || num7 > 128f) { rejectionReason = $"the player target is {num7:0.#}m away"; return false; } rejectionReason = ""; return true; } private static bool TryGetConnectedPlayerZdo(ZDOID targetId, out ZDO targetZdo, out string rejectionReason) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) targetZdo = null; rejectionReason = "the player target is not a ready connected peer"; if (targetId == ZDOID.None || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } ZNetPeer val = null; foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers()) { if (connectedPeer != null && connectedPeer.IsReady() && connectedPeer.m_characterID == targetId) { val = connectedPeer; break; } } if (val == null) { return false; } targetZdo = ZDOMan.instance.GetZDO(targetId); if (targetZdo == null || targetZdo.GetOwner() != val.m_uid) { targetZdo = null; rejectionReason = "the player target ZDO is missing or is not owned by its connected peer"; return false; } GameObject prefab = ZNetScene.instance.GetPrefab(targetZdo.GetPrefab()); if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null) { targetZdo = null; rejectionReason = "the connected target ZDO does not identify a player"; return false; } float num = targetZdo.GetFloat(ZDOVars.s_health, float.NaN); if (targetZdo.GetBool(ZDOVars.s_dead, false) || float.IsNaN(num) || float.IsInfinity(num) || num <= 0f) { targetZdo = null; rejectionReason = "the connected player target is dead or has invalid health"; return false; } return true; } private static void LogBlamerRequestRejection(ZDO zdo, long sender, string reason) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0027: 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_00a4: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = zdo.m_uid; float unscaledTime = Time.unscaledTime; if (!(uid == ZDOID.None) && !(unscaledTime < NextBlamerGlobalRejectionLogTime) && (!NextBlamerRejectionLogTimes.TryGetValue(uid, out var value) || !(unscaledTime < value))) { NextBlamerRejectionLogTimes[uid] = unscaledTime + 10f; NextBlamerGlobalRejectionLogTime = unscaledTime + 2f; ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(zdo.GetPrefab()) : null); string text = (((Object)(object)val != (Object)null) ? Utils.GetPrefabName(val) : zdo.GetPrefab().ToString()); CreatureManagerPlugin.Log.LogWarning((object)("Blamer Karma request rejected for '" + text + "' " + $"({uid}) from owner {sender}: {reason}.")); } } private static ServerBlamerKarmaState GetServerBlamerKarmaState(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) ZDOID uid = zdo.m_uid; if (!ServerBlamerKarmaStates.TryGetValue(uid, out ServerBlamerKarmaState value)) { value = new ServerBlamerKarmaState(); ServerBlamerKarmaStates[uid] = value; } float blamerAccumulatedKarma = GetBlamerAccumulatedKarma(zdo); value.Accumulated = Mathf.Max(value.Accumulated, blamerAccumulatedKarma); return value; } private static BlamerKarmaAddResult TryGrantServerBlamerKarma(ZDO zdo, out float accumulated, out ServerBlamerKarmaState state) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) state = GetServerBlamerKarmaState(zdo); accumulated = state.Accumulated; float unscaledTime = Time.unscaledTime; if (unscaledTime < state.NextAllowedTime) { return BlamerKarmaAddResult.Failed; } float num = ResolveBlamerKarmaPerSecond(zdo.GetFloat("CreatureManager_BlamerKarmaPerSecond", 1f)); float num2 = ResolveBlamerMaxKarmaGain(zdo.GetFloat("CreatureManager_BlamerMaxKarmaGain", 60f)); float num3 = ((num2 > 0f) ? Mathf.Min(num, Mathf.Max(0f, num2 - state.Accumulated)) : num); if (num3 <= 0f || float.IsNaN(num3) || float.IsInfinity(num3)) { return BlamerKarmaAddResult.Failed; } float num4 = ((num2 > 0f) ? Mathf.Min(num2, state.Accumulated + num3) : (state.Accumulated + num3)); if (float.IsNaN(num4) || float.IsInfinity(num4)) { return BlamerKarmaAddResult.Failed; } Vector3 position = zdo.GetPosition(); if (!IsFinite(position)) { return BlamerKarmaAddResult.Failed; } switch (CreatureKarmaManager.TryAddBlamerKarma(position, num3)) { case CreatureKarmaManager.KarmaAddResult.Saturated: return BlamerKarmaAddResult.Saturated; default: return BlamerKarmaAddResult.Failed; case CreatureKarmaManager.KarmaAddResult.Added: state.Accumulated = num4; state.NextAllowedTime = unscaledTime + 0.9f; accumulated = state.Accumulated; return BlamerKarmaAddResult.Added; } } 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) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } private static void SendBlamerKarmaResponse(long target, ZDOID creatureId, long requestId, BlamerKarmaAddResult result, float accumulated) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance == null) { return; } try { ZPackage val = new ZPackage(); val.Write(creatureId); val.Write(requestId); val.Write((int)result); val.Write(accumulated); ZRoutedRpc.instance.InvokeRoutedRPC(target, "CreatureManager_BlamerKarmaResponse", new object[1] { val }); } catch { } } private static void RPC_BlamerKarmaResponse(long sender, ZPackage package) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_00f2: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedServerRpc(sender)) { return; } ZDOID val; long num; BlamerKarmaAddResult blamerKarmaAddResult; float num3; try { val = package.ReadZDOID(); num = package.ReadLong(); int num2 = package.ReadInt(); if (num2 < 0 || num2 > 3) { return; } blamerKarmaAddResult = (BlamerKarmaAddResult)num2; num3 = package.ReadSingle(); } catch { return; } if (val == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null) { return; } GameObject val2 = ZNetScene.instance.FindInstance(val); Character val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent() : null); if ((Object)(object)val3 == (Object)null) { return; } int instanceID = ((Object)val3).GetInstanceID(); if (!PendingBlamerKarmaRequests.TryGetValue(instanceID, out PendingBlamerKarmaRequest value) || value.RequestId != num) { return; } PendingBlamerKarmaRequests.Remove(instanceID); ZNetView nview = val3.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || !TryGetZdo(val3, out ZDO zdo) || zdo.m_uid != val || zdo.GetOwner() != ZRoutedRpc.instance.m_id || !zdo.GetBool("CreatureManager_ModifiersApplied", false) || !HasModifier(GetStoredModifierMask(zdo), ModifierMask.Blamer)) { return; } int num4; if (!val3.IsTamed() && !CreatureKarmaManager.IsKarmaSummonedCreature(val3)) { BaseAI baseAI = val3.GetBaseAI(); MonsterAI val4 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val4 != null) { num4 = (IsBlamerFleeTargetValid(val3, val4, zdo) ? 1 : 0); goto IL_0163; } } num4 = 0; goto IL_0163; IL_0163: bool active = (byte)num4 != 0; switch (blamerKarmaAddResult) { case BlamerKarmaAddResult.Saturated: { if (PassiveModifierSchedules.TryGetValue(instanceID, out PassiveModifierSchedule value2)) { value2.NextBlamer = Time.time + 5f; } SetBlamerActive(val3, zdo, active: false); break; } default: if (!(num3 > GetBlamerAccumulatedKarma(zdo))) { break; } goto case BlamerKarmaAddResult.Added; case BlamerKarmaAddResult.Added: CommitBlamerKarma(val3, zdo, num3, active); break; } } private static void CommitBlamerKarma(Character character, ZDO zdo, float accumulated, bool active) { if (!float.IsNaN(accumulated) && !float.IsInfinity(accumulated)) { float num = ResolveBlamerMaxKarmaGain(zdo.GetFloat("CreatureManager_BlamerMaxKarmaGain", 60f)); float blamerAccumulatedKarma = GetBlamerAccumulatedKarma(zdo); float num2 = Mathf.Max(blamerAccumulatedKarma, Mathf.Max(0f, accumulated)); if (num > 0f) { num2 = Mathf.Min(num, num2); } if (!Mathf.Approximately(blamerAccumulatedKarma, num2)) { zdo.Set("CreatureManager_BlamerAccumulatedKarma", num2); } if (num > 0f && num2 >= num) { ExhaustBlamer(character, zdo); } else { SetBlamerActive(character, zdo, active); } } } private static bool HasBlamerKarmaRemaining(ZDO zdo) { float num = ResolveBlamerMaxKarmaGain(zdo.GetFloat("CreatureManager_BlamerMaxKarmaGain", 60f)); if (!(num <= 0f)) { return GetBlamerAccumulatedKarma(zdo) < num; } return true; } private static float GetBlamerAccumulatedKarma(ZDO zdo) { float num = zdo.GetFloat("CreatureManager_BlamerAccumulatedKarma", 0f); if (!float.IsNaN(num) && !float.IsInfinity(num)) { return Mathf.Max(0f, num); } return 0f; } private static void SetBlamerActive(Character character, ZDO zdo, bool active) { if (zdo.GetBool("CreatureManager_BlamerActive", false) != active) { zdo.Set("CreatureManager_BlamerActive", active); InvalidateModifierHudState(character); } } private static void ExhaustBlamer(Character character, ZDO zdo) { ModifierMask storedModifierMask = GetStoredModifierMask(zdo); if (HasModifier(storedModifierMask, ModifierMask.Blamer)) { SetStoredModifierMask(zdo, storedModifierMask & ~ModifierMask.Blamer); zdo.RemoveFloat("CreatureManager_BlamerKarmaPerSecond"); zdo.RemoveFloat("CreatureManager_BlamerMaxKarmaGain"); zdo.RemoveFloat("CreatureManager_BlamerFleeHealthRatio"); zdo.RemoveFloat("CreatureManager_BlamerAccumulatedKarma"); zdo.RemoveInt("CreatureManager_BlamerActive"); RefreshModifierHotPathState(character, zdo); InvalidateModifierHudState(character); } } private static bool HasEligibleChameleonType(Character character) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 if ((Object)(object)character == (Object)null) { return false; } ChameleonDamageType[] chameleonDamageTypes = ChameleonDamageTypes; foreach (ChameleonDamageType type in chameleonDamageTypes) { if ((int)GetChameleonDamageModifier(character.m_damageModifiers, type) != 3) { return true; } } return false; } private static ChameleonDamageType SelectChameleonDamageType(Character character, ChameleonDamageType previous) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 List list = new List(ChameleonDamageTypes.Length); ChameleonDamageType[] chameleonDamageTypes = ChameleonDamageTypes; foreach (ChameleonDamageType chameleonDamageType in chameleonDamageTypes) { if ((int)GetChameleonDamageModifier(character.m_damageModifiers, chameleonDamageType) != 3) { list.Add(chameleonDamageType); } } if (list.Count == 0) { return ChameleonDamageType.None; } if (list.Count > 1) { list.Remove(previous); } return list[Random.Range(0, list.Count)]; } private static ChameleonDamageType GetChameleonDamageType(ZDO zdo) { int num = zdo.GetInt("CreatureManager_ChameleonType", 0); if (num < 1 || num > 8) { return ChameleonDamageType.None; } return (ChameleonDamageType)num; } private static DamageModifier GetChameleonDamageModifier(DamageModifiers modifiers, ChameleonDamageType type) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) return (DamageModifier)(type switch { ChameleonDamageType.Blunt => modifiers.m_blunt, ChameleonDamageType.Pierce => modifiers.m_pierce, ChameleonDamageType.Slash => modifiers.m_slash, ChameleonDamageType.Fire => modifiers.m_fire, ChameleonDamageType.Poison => modifiers.m_poison, ChameleonDamageType.Lightning => modifiers.m_lightning, ChameleonDamageType.Frost => modifiers.m_frost, ChameleonDamageType.Spirit => modifiers.m_spirit, _ => 0, }); } private static void SetChameleonDamageModifier(ref DamageModifiers modifiers, ChameleonDamageType type, DamageModifier value) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0042: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) switch (type) { case ChameleonDamageType.Blunt: modifiers.m_blunt = value; break; case ChameleonDamageType.Pierce: modifiers.m_pierce = value; break; case ChameleonDamageType.Slash: modifiers.m_slash = value; break; case ChameleonDamageType.Fire: modifiers.m_fire = value; break; case ChameleonDamageType.Poison: modifiers.m_poison = value; break; case ChameleonDamageType.Lightning: modifiers.m_lightning = value; break; case ChameleonDamageType.Frost: modifiers.m_frost = value; break; case ChameleonDamageType.Spirit: modifiers.m_spirit = value; break; } } internal static void HandleDeath(Character character, FinalDeathAttribution attribution) { //IL_0087: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_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_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown if (!CreatureLevelManager.IsLevelSystemEnabled() || (Object)(object)character == (Object)null || character.IsPlayer()) { return; } UntrackRuntimeModifiers(character); ZNetView nview = character.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && !nview.IsOwner()) { return; } Character attacker = ResolveFinalDeathAttributionCharacter(attribution); ClearDelayedDamageTracking(character); if (attribution.SourceWasPlayer) { ApplyReapingForNearbyDeaths(character); } else { TryApplyDirectReapingGain(attacker, character); } if (!TryGetModifierPower(character, ModifierMask.ToxicDeath, "CreatureManager_ToxicDeathPower", 0.25f, out var power)) { return; } Vector3 position = ((Component)character).transform.position; if (!TryGetZdo(character, out ZDO zdo)) { return; } float num = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ToxicDeathRadius", 8f)); string prefabName = ResolveTriggerEffect(zdo.GetString("CreatureManager_ToxicDeathTriggerEffect", "blob_aoe"), "blob_aoe"); PlayBlinkStartEffect(position, prefabName); foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && !((Character)allPlayer).IsDead() && !(Vector3.Distance(((Component)allPlayer).transform.position, position) > num)) { HitData val = new HitData { m_damage = { m_poison = ((Character)allPlayer).GetMaxHealth() * power }, m_point = ((Component)allPlayer).transform.position }; Vector3 val2 = ((Component)allPlayer).transform.position - position; val.m_dir = ((Vector3)(ref val2)).normalized; HitData val3 = val; ((Character)allPlayer).Damage(val3); } } } internal static void HandlePlayerDeath(Player player) { if (CreatureLevelManager.IsLevelSystemEnabled() && !((Object)(object)player == (Object)null)) { Character? attacker = ResolveFinalDeathAttributionCharacter(CaptureFinalDeathAttribution((Character)(object)player)); ClearDelayedDamageTracking((Character)(object)player); TryApplyDirectReapingGain(attacker, (Character)(object)player); } } internal static void NotifyPlayerRespawn(Player player) { ZNetView val = ((Character)(player?)).m_nview; if (!((Object)(object)player == (Object)null) && !((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner() && ZRoutedRpc.instance != null) { val.InvokeRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_ReapingRespawn", Array.Empty()); } } internal static FinalDeathAttribution CaptureFinalDeathAttribution(Character dead) { //IL_0036: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00b5: 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_00b7: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dead == (Object)null) { return default(FinalDeathAttribution); } int instanceID = ((Object)dead).GetInstanceID(); if (PendingDelayedDamageDeathCredits.TryGetValue(instanceID, out var value) && value.Target == dead) { if (value.Source == ZDOID.None) { return default(FinalDeathAttribution); } Character character; Character resolvedSource = (value.SourceWasPlayer ? null : (TryFindCharacter(value.Source, out character) ? character : null)); return new FinalDeathAttribution(value.Source, DeathAttributionKind.Delayed, value.SourceWasPlayer, resolvedSource); } HitData lastHit = dead.m_lastHit; Character val = ((lastHit != null) ? lastHit.GetAttacker() : null); ZDOID val2 = dead.m_lastHit?.m_attacker ?? ZDOID.None; if (val2 == ZDOID.None && (Object)(object)val != (Object)null) { val2 = val.GetZDOID(); } if (!(val2 == ZDOID.None)) { return new FinalDeathAttribution(val2, DeathAttributionKind.Direct, (Object)(object)val != (Object)null && val.IsPlayer(), val); } return default(FinalDeathAttribution); } private static Character? ResolveFinalDeathAttributionCharacter(FinalDeathAttribution attribution) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)attribution.ResolvedSource != (Object)null) { return attribution.ResolvedSource; } if (!attribution.HasSource || attribution.SourceWasPlayer) { return null; } if (!TryFindCharacter(attribution.Source, out Character character)) { return null; } return character; } private static void ClearDelayedDamageTracking(Character character) { int instanceID = ((Object)character).GetInstanceID(); DelayedDamageSourceLedgers.Remove(instanceID); PendingDelayedDamageDeathCredits.Remove(instanceID); } private static bool TryApplyDirectReapingGain(Character? attacker, Character dead) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)attacker == (Object)null || (Object)(object)dead == (Object)null || (Object)(object)attacker == (Object)(object)dead || attacker.IsPlayer() || attacker.IsDead() || !TryGetReapingSettings(attacker, out var _)) { return false; } Vector3 position = ((Component)dead).transform.position; Vector3 val = ((Component)attacker).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude > 576f) { return false; } return RequestReapingGain(attacker, dead, position); } private static bool RequestReapingGain(Character reaper, Character dead, Vector3 deathPosition) { //IL_001f: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) ZNetView val = reaper?.m_nview; if ((Object)(object)reaper == (Object)null || (Object)(object)dead == (Object)null || !IsFinite(deathPosition) || !IsFinite(((Component)reaper).transform.position) || (Object)(object)val == (Object)null || !val.IsValid() || !TryGetReapingSettings(reaper, out var settings)) { return false; } ZDOID zDOID = dead.GetZDOID(); if (zDOID == ZDOID.None) { return false; } if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null) { ZDO zDO = val.GetZDO(); if (val.IsOwner() && zDO != null) { return ApplyReapingGain(reaper, zDO, settings); } return false; } val.InvokeRPC(ZRoutedRpc.instance.GetServerPeerID(), "CreatureManager_RequestReapingDirectKill", new object[2] { zDOID, deathPosition }); return true; } private static void RPC_ReapingDirectKillRequest(Character reaper, long sender, ZDOID deadId, Vector3 deathPosition) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)reaper == (Object)null || reaper.IsPlayer() || reaper.IsDead() || (Object)(object)reaper.m_nview == (Object)null || !reaper.m_nview.IsValid() || deadId == ZDOID.None || !IsFinite(deathPosition) || !IsFinite(((Component)reaper).transform.position) || !TryGetReapingSettings(reaper, out var _)) { return; } Vector3 val = ((Component)reaper).transform.position - deathPosition; if (((Vector3)(ref val)).sqrMagnitude > 576f || !TryConsumeReapingPeerRequest(sender) || ServerAuthorizedReapingDeathCount >= 4096) { return; } ZDO zDO = ZDOMan.instance.GetZDO(deadId); if (zDO == null || zDO.GetOwner() != sender || !IsFinite(zDO.GetPosition())) { return; } val = zDO.GetPosition() - deathPosition; if (((Vector3)(ref val)).sqrMagnitude > 64f) { return; } ZDOID zDOID = reaper.GetZDOID(); ReapingRequestKey reapingRequestKey = new ReapingRequestKey(zDOID, deadId); if (zDOID == ZDOID.None || (ServerAuthorizedReapingDeaths.TryGetValue(zDOID, out HashSet value) && value.Contains(deadId)) || ServerPendingReapingRequests.ContainsKey(reapingRequestKey) || ServerPendingReapingRequests.Count >= 512 || (ServerPendingReapingRequestCounts.TryGetValue(sender, out var value2) && value2 >= 64)) { return; } ServerPendingReapingRequests.Add(reapingRequestKey, sender); ServerPendingReapingRequestCounts[sender] = value2 + 1; ZDOMan instance = ZDOMan.instance; uint reapingDeathEpoch = ReapingDeathEpoch; try { ((MonoBehaviour)ZNet.instance).StartCoroutine(AuthorizeReapingAfterDeathSync(reaper, sender, deadId, deathPosition, reapingRequestKey, instance, reapingDeathEpoch)); } catch { ReleasePendingReapingRequest(reapingRequestKey); throw; } } private static void RPC_ReapingRespawnRequest(Character character, long sender) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && character is Player && !((Object)(object)character.m_nview == (Object)null) && character.m_nview.IsValid() && TryGetCharacterZdo(character, out ZDO zdo) && zdo.GetOwner() == sender) { ZDOID zDOID = character.GetZDOID(); if (!(zDOID == ZDOID.None) && ServerPendingReapingRespawns.Add(zDOID)) { ((MonoBehaviour)ZNet.instance).StartCoroutine(AuthorizeReapingRespawnAfterSync(character, sender, zDOID)); } } } private static IEnumerator AuthorizeReapingRespawnAfterSync(Character character, long sender, ZDOID characterId) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float deadline = Time.realtimeSinceStartup + 2f; try { ZDO zdo; while (Time.realtimeSinceStartup <= deadline && !((Object)(object)character == (Object)null) && !((Object)(object)character.m_nview == (Object)null) && character.m_nview.IsValid() && TryGetCharacterZdo(character, out zdo) && zdo.GetOwner() == sender) { float num = zdo.GetFloat(ZDOVars.s_health, float.NaN); if (!zdo.GetBool(ZDOVars.s_dead, false) && !float.IsNaN(num) && !float.IsInfinity(num) && num > 0f) { ClearReapingDeathAuthorization(characterId); break; } yield return null; } } finally { ServerPendingReapingRespawns.Remove(characterId); } } private static IEnumerator AuthorizeReapingAfterDeathSync(Character reaper, long sender, ZDOID deadId, Vector3 deathPosition, ReapingRequestKey request, ZDOMan authority, uint epoch) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!IsFinite(deathPosition)) { ReleasePendingReapingRequest(request); yield break; } float deadline = Time.realtimeSinceStartup + 2f; try { while (Time.realtimeSinceStartup <= deadline && ZDOMan.instance == authority && epoch == ReapingDeathEpoch && !((Object)(object)reaper == (Object)null) && !(reaper.GetZDOID() != request.Reaper) && !((Object)(object)reaper.m_nview == (Object)null) && reaper.m_nview.IsValid() && IsFinite(((Component)reaper).transform.position)) { ZDO zDO = authority.GetZDO(deadId); if (zDO != null && zDO.GetOwner() == sender && IsFinite(zDO.GetPosition())) { Vector3 val = zDO.GetPosition() - deathPosition; if (((Vector3)(ref val)).sqrMagnitude > 64f) { break; } bool flag = zDO.GetBool(ZDOVars.s_dead, false) || zDO.GetFloat(ZDOVars.s_health, float.PositiveInfinity) <= 0f; if (!flag && TryFindCharacter(deadId, out Character character)) { flag = character.IsDead() || character.GetHealth() <= 0f; } if (flag) { AuthorizeReapingGain(reaper, deadId, deathPosition); break; } yield return null; continue; } break; } } finally { ReleasePendingReapingRequest(request); } } private static void ReleasePendingReapingRequest(ReapingRequestKey request) { if (ServerPendingReapingRequests.TryGetValue(request, out var value) && ServerPendingReapingRequests.Remove(request)) { if (!ServerPendingReapingRequestCounts.TryGetValue(value, out var value2) || value2 <= 1) { ServerPendingReapingRequestCounts.Remove(value); } else { ServerPendingReapingRequestCounts[value] = value2 - 1; } } } private static void AuthorizeReapingGain(Character reaper, ZDOID deadId, Vector3 deathPosition) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_00b7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)reaper == (Object)null || reaper.IsPlayer() || reaper.IsDead() || (Object)(object)reaper.m_nview == (Object)null || !reaper.m_nview.IsValid() || !IsFinite(deathPosition) || !IsFinite(((Component)reaper).transform.position) || !TryGetReapingSettings(reaper, out var _)) { return; } Vector3 val = ((Component)reaper).transform.position - deathPosition; if (((Vector3)(ref val)).sqrMagnitude > 576f) { return; } ZDOID zDOID = reaper.GetZDOID(); if (!(zDOID == ZDOID.None) && TryAddReapingDeathAuthorization(zDOID, deadId)) { if (reaper.m_nview.IsOwner() && ZRoutedRpc.instance != null) { ApplyAuthorizedReapingGain(reaper, ZRoutedRpc.instance.GetServerPeerID(), deadId, deathPosition); return; } reaper.m_nview.InvokeRPC("CreatureManager_ReapingDirectKill", new object[2] { deadId, deathPosition }); } } private static void ApplyAuthorizedReapingGain(Character reaper, long sender, ZDOID deadId, Vector3 deathPosition) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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) ZNetView val = reaper?.m_nview; if (!IsTrustedServerRpc(sender) || (Object)(object)reaper == (Object)null || reaper.IsPlayer() || reaper.IsDead() || (Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner() || deadId == ZDOID.None || !IsFinite(deathPosition) || !IsFinite(((Component)reaper).transform.position) || !TryGetReapingSettings(reaper, out var settings)) { return; } Vector3 val2 = ((Component)reaper).transform.position - deathPosition; if (!(((Vector3)(ref val2)).sqrMagnitude > 576f)) { ZDO zDO = val.GetZDO(); if (zDO != null) { ApplyReapingGain(reaper, zDO, settings); } } } private static bool TryConsumeReapingPeerRequest(long sender) { double networkTimeSecondsDouble = GetNetworkTimeSecondsDouble(); if (!ServerReapingPeerRateStates.TryGetValue(sender, out ReapingPeerRateState value)) { ServerReapingPeerRateStates[sender] = new ReapingPeerRateState { WindowStart = networkTimeSecondsDouble, RequestCount = 1 }; return true; } if (networkTimeSecondsDouble < value.WindowStart || networkTimeSecondsDouble - value.WindowStart >= 1.0) { value.WindowStart = networkTimeSecondsDouble; value.RequestCount = 1; return true; } if (value.RequestCount >= 128) { return false; } value.RequestCount++; return true; } private static bool TryAddReapingDeathAuthorization(ZDOID reaperId, ZDOID deadId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (ServerAuthorizedReapingDeathCount >= 4096) { return false; } ReapingRequestKey item = new ReapingRequestKey(reaperId, deadId); if (ServerAuthorizedReapingDeaths.TryGetValue(reaperId, out HashSet value) && value.Contains(deadId)) { return false; } if (!ServerQueuedReapingDeathAuthorizations.Contains(item) && ServerAuthorizedReapingDeathPruneQueue.Count >= 8192) { CompactReapingAuthorizationQueue(); if (!ServerQueuedReapingDeathAuthorizations.Contains(item) && ServerAuthorizedReapingDeathPruneQueue.Count >= 8192) { return false; } } HashSet hashSet = value ?? new HashSet(); if (!hashSet.Add(deadId)) { return false; } if (value == null) { ServerAuthorizedReapingDeaths[reaperId] = hashSet; } ServerAuthorizedReapingDeathCount++; if (ServerQueuedReapingDeathAuthorizations.Add(item)) { ServerAuthorizedReapingDeathPruneQueue.Enqueue(item); } return true; } private static void CompactReapingAuthorizationQueue() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) int count = ServerAuthorizedReapingDeathPruneQueue.Count; ServerQueuedReapingDeathAuthorizations.Clear(); for (int i = 0; i < count; i++) { ReapingRequestKey item = ServerAuthorizedReapingDeathPruneQueue.Dequeue(); if (ServerAuthorizedReapingDeaths.TryGetValue(item.Reaper, out HashSet value) && value.Contains(item.Dead) && ServerQueuedReapingDeathAuthorizations.Add(item)) { ServerAuthorizedReapingDeathPruneQueue.Enqueue(item); } } } private static void RemoveReapingAuthorizationsForReaper(ZDOID reaperId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (ServerAuthorizedReapingDeaths.TryGetValue(reaperId, out HashSet value)) { ServerAuthorizedReapingDeaths.Remove(reaperId); ServerAuthorizedReapingDeathCount = Math.Max(0, ServerAuthorizedReapingDeathCount - value.Count); } } private static void ClearReapingDeathAuthorization(ZDOID deadId) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) KeyValuePair>[] array = ServerAuthorizedReapingDeaths.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair> keyValuePair = array[i]; if (keyValuePair.Value.Remove(deadId)) { ServerAuthorizedReapingDeathCount = Math.Max(0, ServerAuthorizedReapingDeathCount - 1); } if (keyValuePair.Value.Count == 0) { ServerAuthorizedReapingDeaths.Remove(keyValuePair.Key); } } } private static void ApplyReapingForNearbyDeaths(Character dead) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (ActiveReapingModifierCharacters.Count == 0) { return; } Vector3 position = ((Component)dead).transform.position; int nearbyReapingOverlapCount = GetNearbyReapingOverlapCount(position); ReapingStaleCandidateIds.Clear(); try { for (int i = 0; i < nearbyReapingOverlapCount; i++) { Collider val = ReapingOverlapBuffer[i]; if ((Object)(object)val == (Object)null) { continue; } Character componentInParent = ((Component)val).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { int instanceID = ((Object)componentInParent).GetInstanceID(); if (ReapingNearbyCandidateIds.Add(instanceID) && ActiveReapingModifierCharacters.TryGetValue(instanceID, out Character value)) { ProcessNearbyReapingCandidate(value, instanceID, dead, position); } } } foreach (KeyValuePair unqueryableReapingModifierCharacter in UnqueryableReapingModifierCharacters) { int key = unqueryableReapingModifierCharacter.Key; if (ReapingNearbyCandidateIds.Add(key) && ActiveReapingModifierCharacters.TryGetValue(key, out Character value2)) { ProcessNearbyReapingCandidate(value2, key, dead, position); } } } finally { foreach (int reapingStaleCandidateId in ReapingStaleCandidateIds) { ActiveReapingModifierCharacters.Remove(reapingStaleCandidateId); UnqueryableReapingModifierCharacters.Remove(reapingStaleCandidateId); } ReapingStaleCandidateIds.Clear(); Array.Clear(ReapingOverlapBuffer, 0, nearbyReapingOverlapCount); ReapingNearbyCandidateIds.Clear(); } } private static void ProcessNearbyReapingCandidate(Character candidate, int candidateId, Character dead, Vector3 origin) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_003f: 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 ((Object)(object)candidate == (Object)null || (Object)(object)candidate == (Object)(object)dead || candidate.IsPlayer() || candidate.IsDead()) { ReapingStaleCandidateIds.Add(candidateId); return; } Vector3 val = ((Component)candidate).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > 576f) && CreatureLevelManager.AllowsModifierEffects(candidate) && TryGetReapingSettings(candidate, out var _)) { ZNetView nview = candidate.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid()) { RequestReapingGain(candidate, dead, origin); } } } private static int GetNearbyReapingOverlapCount(Vector3 origin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) int num; while (true) { num = Physics.OverlapSphereNonAlloc(origin, 24f, ReapingOverlapBuffer, GetReapingCharacterLayerMask(), (QueryTriggerInteraction)2); if (num < ReapingOverlapBuffer.Length) { break; } ReapingOverlapBuffer = (Collider[])(object)new Collider[checked(ReapingOverlapBuffer.Length * 2)]; } return num; } private static int GetReapingCharacterLayerMask() { int num = Character.s_characterLayerMask; if (Character.s_characterLayer >= 0) { num |= 1 << Character.s_characterLayer; } if (Character.s_characterNetLayer >= 0) { num |= 1 << Character.s_characterNetLayer; } if (Character.s_characterGhostLayer >= 0) { num |= 1 << Character.s_characterGhostLayer; } return num; } private static bool ApplyReapingGain(Character character, ZDO zdo, ReapingSettings settings) { //IL_023e: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !settings.HasAnyGain) { return false; } float num = EnsureReapingBaseMaxHealth(character, zdo); if (num <= 0f) { return false; } float health = character.GetHealth(); int num2 = Math.Max(0, zdo.GetInt("CreatureManager_ReapingHealActivationCount", 0)); float num3 = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingBonusHealth", 0f)); float num4 = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingDamageBonus", 0f)); float num5 = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingScaleBonus", 0f)); float num6 = num * settings.MaxHealthCap; bool flag = !CreatureLevelManager.IsDungeonCreature(character); bool flag2 = settings.HealPerKill > 0f && num2 < settings.HealMaxActivations; bool flag3 = settings.MaxHealthPerKill > 0f && num6 > num3; bool flag4 = settings.DamagePerKill > 0f && settings.DamageCap > num4; bool flag5 = flag && settings.ScalePerKill > 0f && settings.ScaleCap > num5; if (!flag2 && !flag3 && !flag4 && !flag5) { return false; } float num7 = Mathf.Min(num * settings.MaxHealthPerKill, Mathf.Max(0f, num6 - num3)); if (num7 > 0f) { num3 += num7; zdo.Set("CreatureManager_ReapingBonusHealth", num3); } float num8 = num + Mathf.Min(num3, num6); if (!Mathf.Approximately(character.GetMaxHealth(), num8)) { character.SetMaxHealth(num8); } float num9 = Mathf.Min(flag2 ? (num * settings.HealPerKill) : 0f, Mathf.Max(0f, num8 - health)); if (num9 > 0f) { num2++; zdo.Set("CreatureManager_ReapingHealActivationCount", num2); character.SetHealth(health + num9); ShowReapingHealText(character, num9); } float num10 = num4; if (settings.DamagePerKill > 0f && settings.DamageCap > num4) { num10 = Mathf.Min(settings.DamageCap, num4 + settings.DamagePerKill); zdo.Set("CreatureManager_ReapingDamageBonus", num10); } float num11 = num5; if (flag && settings.ScalePerKill > 0f && settings.ScaleCap > num5) { EnsureReapingBaseScale(character, zdo); num11 = Mathf.Min(settings.ScaleCap, num5 + settings.ScalePerKill); zdo.Set("CreatureManager_ReapingScaleBonus", num11); } bool scaleChanged = ApplyStoredReapingScale(character, zdo); if (!(num7 > 0f) && !(num9 > 0f) && !(num10 > num4) && !(num11 > num5)) { return false; } BroadcastReapingFeedback(character, scaleChanged); return true; } private static void ApplyStoredReapingHealth(Character character, ZDO zdo) { if (zdo == null || !HasModifier(zdo, ModifierMask.Reaping)) { return; } float num = zdo.GetFloat("CreatureManager_ReapingBaseMaxHealth", 0f); float num2 = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingBonusHealth", 0f)); if (!(num <= 0f) && !(num2 <= 0f)) { float num3 = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingMaxHealthCap", 2f)); float num4 = num + Mathf.Min(num2, num * num3); if (!Mathf.Approximately(character.GetMaxHealth(), num4)) { character.SetMaxHealth(num4); } } } private static bool ApplyStoredReapingScale(Character character, ZDO zdo) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !HasModifier(zdo, ModifierMask.Reaping)) { return false; } float num = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingScaleBonus", 0f)); Vector3 vec = zdo.GetVec3("CreatureManager_ReapingBaseScale", Vector3.zero); if (num <= 0f || vec == Vector3.zero) { return false; } if (CreatureLevelManager.IsDungeonCreature(character)) { return false; } Vector3 val = vec * (1f + num); if (Approximately(((Component)character).transform.localScale, val)) { return false; } ((Component)character).transform.localScale = val; if ((Object)(object)character.m_nview != (Object)null && character.m_nview.IsValid() && character.m_nview.IsOwner()) { zdo.Set(ZDOVars.s_scaleHash, val); } ScheduleReapingPhysicsSync(); return true; } private static float EnsureReapingBaseMaxHealth(Character character, ZDO zdo) { float num = zdo.GetFloat("CreatureManager_ReapingBaseMaxHealth", 0f); if (num > 0f) { return num; } num = character.GetMaxHealth(); if (num > 0f) { zdo.Set("CreatureManager_ReapingBaseMaxHealth", num); } return num; } private static Vector3 EnsureReapingBaseScale(Character character, ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) Vector3 vec = zdo.GetVec3("CreatureManager_ReapingBaseScale", Vector3.zero); if (vec != Vector3.zero) { return vec; } vec = ((Component)character).transform.localScale; if (vec != Vector3.zero) { zdo.Set("CreatureManager_ReapingBaseScale", vec); } return vec; } private static bool Approximately(Vector3 left, Vector3 right) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Approximately(left.x, right.x) && Mathf.Approximately(left.y, right.y)) { return Mathf.Approximately(left.z, right.z); } return false; } private static void ShowReapingHealText(Character character, float actualHeal) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!(actualHeal <= 0f) && ZRoutedRpc.instance != null) { Vector3 topPoint = character.GetTopPoint(); if ((Object)(object)DamageText.instance != (Object)null) { DamageText.instance.ShowText((TextType)4, topPoint, actualHeal, false); return; } ZPackage val = new ZPackage(); val.Write(4); val.Write(topPoint); val.Write(actualHeal.ToString("0.#", CultureInfo.InvariantCulture)); val.Write(false); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RPC_DamageText", new object[1] { val }); } } internal static bool TryGetOmenEnforcerChance(ZDO zdo, out float chance) { chance = 0f; if (zdo == null || !zdo.GetBool("CreatureManager_ModifiersApplied", false) || !HasModifier(GetStoredModifierMask(zdo), ModifierMask.Omen)) { return false; } chance = Mathf.Clamp01(zdo.GetFloat("CreatureManager_OmenPower", 0.25f)); return chance > 0f; } private static void ApplyRuntimeModifierStats(Character character, ZDO zdo) { TrackRuntimeModifiers(character, zdo); InitializeServerReflectionObservation(character, zdo); ApplyStoredReapingHealth(character, zdo); ApplyStoredReapingScale(character, zdo); } internal static void RefreshStoredReapingScale(Character character) { if (!((Object)(object)character == (Object)null) && !character.IsPlayer() && TryGetZdo(character, out ZDO zdo) && zdo.GetBool("CreatureManager_ModifiersApplied", false)) { ZNetView nview = character.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner() && EnsureInheritedReapingBases(character, zdo)) { ApplyStoredReapingHealth(character, zdo); } ApplyStoredReapingScale(character, zdo); } } private static bool EnsureInheritedReapingBases(Character character, ZDO zdo) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) if (!HasModifier(zdo, ModifierMask.Reaping)) { return false; } bool result = false; int instanceID = ((Object)character).GetInstanceID(); if (PendingReapingHealthBonusRatios.TryGetValue(instanceID, out var value)) { float maxHealth = character.GetMaxHealth(); if (maxHealth > 0f) { zdo.Set("CreatureManager_ReapingBaseMaxHealth", maxHealth); zdo.Set("CreatureManager_ReapingBonusHealth", maxHealth * Mathf.Max(0f, value)); PendingReapingHealthBonusRatios.Remove(instanceID); result = true; } } if (zdo.GetFloat("CreatureManager_ReapingBonusHealth", 0f) > 0f && zdo.GetFloat("CreatureManager_ReapingBaseMaxHealth", 0f) <= 0f) { result = EnsureReapingBaseMaxHealth(character, zdo) > 0f; } if (zdo.GetFloat("CreatureManager_ReapingScaleBonus", 0f) > 0f && zdo.GetVec3("CreatureManager_ReapingBaseScale", Vector3.zero) == Vector3.zero) { EnsureReapingBaseScale(character, zdo); } return result; } internal static void UpdateEnemyHud(Character character, bool isMount) { if ((Object)(object)character == (Object)null || character.IsPlayer() || (Object)(object)EnemyHud.instance == (Object)null || !EnemyHud.instance.m_huds.TryGetValue(character, out var value)) { return; } if (isMount) { UpdateBlamerAlertIcon(value, visible: false); } else { if ((Object)(object)value.m_gui == (Object)null) { return; } if ((Object)(object)value.m_name != (Object)null && CreatureKarmaManager.TryGetDisplayName(character, out string displayName)) { ((TMP_Text)value.m_name).text = displayName; } UpdateResistanceHud(character, value); if (!CreatureLevelManager.IsLevelSystemEnabled()) { UpdateBlamerAlertIcon(value, visible: false); HideManagedHudContent(value); return; } if (!TryGetHudModifierState(character, value.m_gui, out var visible, out var armoredReduction, out var enragedBonus, out var blamerActive, out var refreshed) && refreshed && (Object)(object)character.m_nview != (Object)null && character.m_nview.IsValid() && character.m_nview.IsOwner()) { TryRollModifiers(character); TryGetHudModifierState(character, value.m_gui, out visible, out armoredReduction, out enragedBonus, out blamerActive, out var _); } UpdateBlamerAlertIcon(value, blamerActive); bool flag = visible != ModifierMask.None; int num = Math.Max(0, character.GetLevel() - 1); if (character.IsBoss() || CreatureKarmaManager.IsBossHudOnly(character)) { UpdateBossHud(value, num, flag, armoredReduction, enragedBonus, visible); return; } SetBossContentActive(value, active: false); HideVanillaLevelBlocks(value); if (num <= 0 && !flag) { SetLevelContentActive(value, active: false); return; } RectTransform val = EnsureLevelContent(value, num > 0); if (!((Object)(object)val == (Object)null)) { ((Component)val).gameObject.SetActive(true); UpdateStarBadge(val, value.m_level2, value.m_level3, value.m_name, num, num <= 2); UpdateModifierIcons(EnsureIconContainer(val), visible, armoredReduction, enragedBonus); } } } private static void UpdateBlamerAlertIcon(HudData hud, bool visible) { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: 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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) RectTransform alerted = hud.m_alerted; if ((Object)(object)alerted == (Object)null) { return; } if (!BlamerAlertIconStates.TryGetValue(alerted, out BlamerAlertIconState value)) { if (!visible) { return; } value = new BlamerAlertIconState(); BlamerAlertIconStates.Add(alerted, value); } if ((Object)(object)value.Icon == (Object)null) { if (!visible) { return; } Transform val = ((Transform)alerted).Find("CreatureManager_BlamerActiveIcon"); Image val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); if ((Object)(object)val2 == (Object)null) { GameObject val3 = new GameObject("CreatureManager_BlamerActiveIcon", new Type[2] { typeof(RectTransform), typeof(Image) }); ((Transform)(RectTransform)val3.transform).SetParent((Transform)(object)alerted, false); val2 = val3.GetComponent(); } RectTransform val4 = (RectTransform)((Component)val2).transform; val4.anchorMin = new Vector2(0.5f, 1f); val4.anchorMax = new Vector2(0.5f, 1f); val4.pivot = new Vector2(0.5f, 0f); val4.anchoredPosition = new Vector2(0f, 2f); ((Transform)val4).localScale = Vector3.one; val2.sprite = GetBlamerSprite(); ((Graphic)val2).color = Color.white; val2.preserveAspect = true; ((Graphic)val2).raycastTarget = false; ((Behaviour)val2).enabled = true; value.Icon = val2; value.VisibilityInitialized = false; } float num = 34f; Vector2 val5 = default(Vector2); ((Vector2)(ref val5))..ctor(num, num); RectTransform rectTransform = ((Graphic)value.Icon).rectTransform; if (rectTransform.sizeDelta != val5) { rectTransform.sizeDelta = val5; } if (!value.VisibilityInitialized || value.Visible != visible) { ((Component)value.Icon).gameObject.SetActive(visible); value.VisibilityInitialized = true; value.Visible = visible; } } private static void HideVanillaLevelBlocks(HudData hud) { if ((Object)(object)hud.m_level2 != (Object)null) { ((Component)hud.m_level2).gameObject.SetActive(false); } if ((Object)(object)hud.m_level3 != (Object)null) { ((Component)hud.m_level3).gameObject.SetActive(false); } } private static void UpdateBossHud(HudData hud, int stars, bool hasModifiers, float armoredReduction, float enragedBonus, ModifierMask visibleModifiers) { SetLevelContentActive(hud, active: false); HideVanillaLevelBlocks(hud); if (stars <= 0 && !hasModifiers) { SetBossContentActive(hud, active: false); return; } RectTransform val = EnsureBossLevelContent(hud, stars > 0); if (!((Object)(object)val == (Object)null)) { ((Component)val).gameObject.SetActive(true); UpdateStarBadge(val, hud.m_level2, hud.m_level3, hud.m_name, stars, stars <= 2); UpdateModifierIcons(EnsureIconContainer(val), visibleModifiers, armoredReduction, enragedBonus); } } private static void HideManagedHudContent(HudData hud) { SetLevelContentActive(hud, active: false); SetBossContentActive(hud, active: false); } private static void UpdateResistanceHud(Character character, HudData hud) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hud.m_gui == (Object)null) { return; } float range = Mathf.Clamp(CreatureManagerPlugin.NormalCreatureNameplateRange.Value, 10f, 50f); ConfigEntry showSneakHoverResistances = CreatureManagerPlugin.ShowSneakHoverResistances; if (showSneakHoverResistances == null || showSneakHoverResistances.Value != CreatureManagerPlugin.Toggle.On || character.IsTamed() || !ShouldShowResistanceHud(character, range) || !TryBuildResistanceText(GetDisplayedDamageModifiers(character), out string text, out int lineCount)) { SetResistanceTextActive(hud, active: false); return; } TextMeshProUGUI val = EnsureResistanceText(hud); if (!((Object)(object)val == (Object)null)) { ((TMP_Text)val).text = text; PositionResistanceText(((TMP_Text)val).rectTransform, hud, lineCount); ((Component)val).gameObject.SetActive(true); } } private static DamageModifiers GetDisplayedDamageModifiers(Character character) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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) DamageModifiers modifiers = character.m_damageModifiers; BaseAI baseAI = character.GetBaseAI(); if (baseAI == null || !baseAI.IsAlerted() || !TryGetZdo(character, out ZDO zdo) || !HasModifier(zdo, ModifierMask.Chameleon) || !CreatureLevelManager.AllowsModifierEffects(character)) { return modifiers; } ChameleonDamageType chameleonDamageType = GetChameleonDamageType(zdo); if (chameleonDamageType != ChameleonDamageType.None) { SetChameleonDamageModifier(ref modifiers, chameleonDamageType, (DamageModifier)3); } return modifiers; } private static bool TryBuildResistanceText(DamageModifiers modifiers, out string text, out int lineCount) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); lineCount = 0; foreach (var (text2, val) in EnumerateDamageModifiers(modifiers)) { if ((int)val != 0 && (int)val != 4) { if (stringBuilder.Length > 0) { stringBuilder.AppendLine(); } stringBuilder.Append(CreatureLocalization.Localize("cm_damage_" + text2, text2)); stringBuilder.Append(": "); stringBuilder.Append(GetLocalizedDamageModifier(val)); lineCount++; } } text = stringBuilder.ToString(); return lineCount > 0; } private unsafe static string GetLocalizedDamageModifier(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown return (modifier - 1) switch { 1 => CreatureLocalization.Localize("cm_resistance_weak", "Weak"), 5 => CreatureLocalization.Localize("cm_resistance_very_weak", "VeryWeak"), 0 => CreatureLocalization.Localize("cm_resistance_resistant", "Resistant"), 4 => CreatureLocalization.Localize("cm_resistance_very_resistant", "VeryResistant"), 2 => CreatureLocalization.Localize("cm_resistance_immune", "Immune"), _ => ((object)(*(DamageModifier*)(&modifier))/*cast due to .constrained prefix*/).ToString(), }; } private static IEnumerable<(string Key, DamageModifier Value)> EnumerateDamageModifiers(DamageModifiers modifiers) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) yield return (Key: "blunt", Value: modifiers.m_blunt); yield return (Key: "slash", Value: modifiers.m_slash); yield return (Key: "pierce", Value: modifiers.m_pierce); yield return (Key: "chop", Value: modifiers.m_chop); yield return (Key: "pickaxe", Value: modifiers.m_pickaxe); yield return (Key: "fire", Value: modifiers.m_fire); yield return (Key: "frost", Value: modifiers.m_frost); yield return (Key: "lightning", Value: modifiers.m_lightning); yield return (Key: "poison", Value: modifiers.m_poison); yield return (Key: "spirit", Value: modifiers.m_spirit); } private static bool ShouldShowResistanceHud(Character character, float range) { //IL_001f: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !IsPlayerSneakingForFrame(localPlayer)) { return false; } if (Vector3.Distance(((Component)localPlayer).transform.position, ((Component)character).transform.position) > range) { return false; } return (Object)(object)GetHoveredCharacterForFrame(range) == (Object)(object)character; } private static bool IsPlayerSneakingForFrame(Player player) { int frameCount = Time.frameCount; if (CachedSneakFrame == frameCount) { return CachedSneakState; } CachedSneakFrame = frameCount; CachedSneakState = IsPlayerSneaking(player); return CachedSneakState; } private static bool IsPlayerSneaking(Player player) { if (!SneakMemberLookupDone) { Type typeFromHandle = typeof(Player); string[] sneakMethodNames = SneakMethodNames; foreach (string name in sneakMethodNames) { MethodInfo method = typeFromHandle.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.ReturnType == typeof(bool) && method.GetParameters().Length == 0) { CachedSneakMethod = method; break; } } if (CachedSneakMethod == null) { sneakMethodNames = SneakFieldNames; foreach (string name2 in sneakMethodNames) { FieldInfo field = typeFromHandle.GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { CachedSneakField = field; break; } } } SneakMemberLookupDone = true; } try { if (CachedSneakMethod != null) { object obj = CachedSneakMethod.Invoke(player, Array.Empty()); return obj is bool && (bool)obj; } if (CachedSneakField != null) { object obj = CachedSneakField.GetValue(player); return obj is bool && (bool)obj; } } catch { return false; } return false; } private static Character? GetHoveredCharacter(float range) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameCamera.instance == (Object)null) { return null; } if (HoverRaycastMask < 0) { HoverRaycastMask = LayerMask.GetMask(new string[11] { "item", "piece", "piece_nonsolid", "Default", "static_solid", "Default_small", "character", "character_net", "terrain", "vehicle", "character_trigger" }); } Transform transform = ((Component)GameCamera.instance).transform; RaycastHit[] array = Physics.RaycastAll(transform.position, transform.forward, range, HoverRaycastMask); Array.Sort(array, (RaycastHit left, RaycastHit right) => ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance)); RaycastHit[] array2 = array; for (int num = 0; num < array2.Length; num++) { RaycastHit val = array2[num]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponent() != (Object)null)) { Character componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && !componentInParent.IsPlayer()) { return componentInParent; } } } return null; } private static Character? GetHoveredCharacterForFrame(float range) { int frameCount = Time.frameCount; if (CachedHoverFrame == frameCount && Mathf.Approximately(CachedHoverRange, range)) { if (!CachedHoverValid) { return null; } return CachedHoveredCharacter; } CachedHoverFrame = frameCount; CachedHoverRange = range; CachedHoveredCharacter = GetHoveredCharacter(range); CachedHoverValid = (Object)(object)CachedHoveredCharacter != (Object)null; return CachedHoveredCharacter; } private static TextMeshProUGUI? EnsureResistanceText(HudData hud) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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) RectTransform resistanceHudParent = GetResistanceHudParent(hud); if ((Object)(object)resistanceHudParent == (Object)null) { return null; } HudContentState value = HudContentStates.GetValue(resistanceHudParent, (RectTransform _) => new HudContentState()); if (!value.ResistanceTextSearched) { value.ResistanceTextSearched = true; Transform val = ((Transform)resistanceHudParent).Find("CreatureManager_ResistanceText"); if ((Object)(object)val != (Object)null) { value.ResistanceText = ((Component)val).GetComponent(); ConfigureResistanceText(value.ResistanceText, hud.m_name); } } if ((Object)(object)value.ResistanceText != (Object)null) { return value.ResistanceText; } GameObject val2 = new GameObject("CreatureManager_ResistanceText", new Type[1] { typeof(RectTransform) }); val2.SetActive(false); RectTransform val3 = (RectTransform)val2.transform; ((Transform)val3).SetParent((Transform)(object)resistanceHudParent, false); ((Transform)val3).SetAsLastSibling(); TextMeshProUGUI val4 = val2.AddComponent(); ConfigureResistanceText(val4, hud.m_name); Outline obj = val2.AddComponent(); ((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.85f); ((Shadow)obj).effectDistance = new Vector2(1f, -1f); ((Shadow)obj).useGraphicAlpha = true; val2.SetActive(true); value.ResistanceText = val4; return val4; } private static void ConfigureResistanceText(TextMeshProUGUI text, TextMeshProUGUI? nameText) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) ApplyHudFont((TMP_Text)(object)text, (TMP_Text?)(object)nameText); ((Graphic)text).raycastTarget = false; ((TMP_Text)text).enableAutoSizing = false; ((TMP_Text)text).fontSize = 12f; ((TMP_Text)text).alignment = (TextAlignmentOptions)258; ((Graphic)text).color = new Color(0.86f, 0.95f, 1f, 0.95f); ((TMP_Text)text).richText = false; ((TMP_Text)text).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)text).overflowMode = (TextOverflowModes)0; } private static RectTransform? GetResistanceHudParent(HudData hud) { return GetHudContentParent(hud); } private static void PositionResistanceText(RectTransform rect, HudData hud, int lineCount) { //IL_0067: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)rect).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); float num = Mathf.Max(14f, 14f * (float)Mathf.Max(1, lineCount)); float activeLevelContentHeight = GetActiveLevelContentHeight(val); RectTransform healthRoot = GetHealthRoot(hud.m_healthFast); Rect rect2; if ((Object)(object)val != (Object)null && (Object)(object)healthRoot != (Object)null && (Object)(object)((Transform)healthRoot).parent == (Object)(object)val) { rect2 = healthRoot.rect; float num2 = Mathf.Max(150f, Mathf.Max(((Rect)(ref rect2)).width, healthRoot.sizeDelta.x)); rect2 = healthRoot.rect; float num3 = Mathf.Max(1f, Mathf.Max(((Rect)(ref rect2)).height, healthRoot.sizeDelta.y)); float num4 = healthRoot.anchoredPosition.y - healthRoot.pivot.y * num3; ApplyRectLayout(rect, healthRoot.anchorMin, healthRoot.anchorMax, new Vector2(0.5f, 1f), new Vector2(healthRoot.anchoredPosition.x, num4 - 2f - activeLevelContentHeight - 2f), new Vector2(num2, num)); return; } RectTransform val2 = (((Object)(object)hud.m_name != (Object)null) ? ((TMP_Text)hud.m_name).rectTransform : null); if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null && (Object)(object)((Transform)val2).parent == (Object)(object)val) { rect2 = val2.rect; float num5 = Mathf.Max(1f, Mathf.Max(((Rect)(ref rect2)).height, val2.sizeDelta.y)); float num6 = val2.anchoredPosition.y - val2.pivot.y * num5; Vector2 anchorMin = val2.anchorMin; Vector2 anchorMax = val2.anchorMax; Vector2 pivot = new Vector2(0.5f, 1f); Vector2 anchoredPosition = new Vector2(val2.anchoredPosition.x, num6 - activeLevelContentHeight - 2f); rect2 = val2.rect; ApplyRectLayout(rect, anchorMin, anchorMax, pivot, anchoredPosition, new Vector2(Mathf.Max(150f, Mathf.Max(((Rect)(ref rect2)).width, val2.sizeDelta.x)), num)); } else { ApplyRectLayout(rect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 1f), new Vector2(0f, 0f - activeLevelContentHeight - 2f), new Vector2(150f, num)); } } private static float GetActiveLevelContentHeight(RectTransform? parent) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)parent == (Object)null) { return 20f; } Transform val = ((Transform)parent).Find("CreatureManager_LevelContent"); if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { val = ((Transform)parent).Find("CreatureManager_BossLevelContent"); } if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { return 20f; } RectTransform val2 = (RectTransform)val; Rect rect = val2.rect; float num = Mathf.Max(20f, Mathf.Max(((Rect)(ref rect)).height, val2.sizeDelta.y)); Transform obj = val.Find("CreatureManager_StarGroup"); RectTransform val3 = (RectTransform)(object)((obj is RectTransform) ? obj : null); if ((Object)(object)val3 != (Object)null && ((Component)val3).gameObject.activeSelf) { float num2 = num; rect = val3.rect; num = Mathf.Max(num2, Mathf.Max(((Rect)(ref rect)).height, val3.sizeDelta.y)); } Transform obj2 = val.Find("CreatureManager_ModifierIcons"); RectTransform val4 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if ((Object)(object)val4 != (Object)null && HudIconStates.TryGetValue(val4, out HudIconState value) && value.Visible != ModifierMask.None) { float num3 = num; rect = val4.rect; num = Mathf.Max(num3, Mathf.Max(((Rect)(ref rect)).height, val4.sizeDelta.y)); } return num; } private static void SetResistanceTextActive(HudData hud, bool active) { RectTransform resistanceHudParent = GetResistanceHudParent(hud); if (!((Object)(object)resistanceHudParent == (Object)null)) { HudContentState value = HudContentStates.GetValue(resistanceHudParent, (RectTransform _) => new HudContentState()); if (!value.ResistanceTextSearched) { value.ResistanceTextSearched = true; Transform val = ((Transform)resistanceHudParent).Find("CreatureManager_ResistanceText"); value.ResistanceText = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); } if ((Object)(object)value.ResistanceText != (Object)null && ((Component)value.ResistanceText).gameObject.activeSelf != active) { ((Component)value.ResistanceText).gameObject.SetActive(active); } } } private static void SetBossContentActive(HudData hud, bool active) { RectTransform hudContentParent = GetHudContentParent(hud); if (!((Object)(object)hudContentParent == (Object)null)) { HudContentState value = HudContentStates.GetValue(hudContentParent, (RectTransform _) => new HudContentState()); if (!value.BossContentSearched) { value.BossContentSearched = true; ref RectTransform? bossContent = ref value.BossContent; Transform obj = ((Transform)hudContentParent).Find("CreatureManager_BossLevelContent"); bossContent = (RectTransform?)(object)((obj is RectTransform) ? obj : null); } if ((Object)(object)value.BossContent != (Object)null && ((Component)value.BossContent).gameObject.activeSelf != active) { ((Component)value.BossContent).gameObject.SetActive(active); } } } private static void SetLevelContentActive(HudData hud, bool active) { RectTransform hudContentParent = GetHudContentParent(hud); if (!((Object)(object)hudContentParent == (Object)null)) { HudContentState value = HudContentStates.GetValue(hudContentParent, (RectTransform _) => new HudContentState()); if (!value.LevelContentSearched) { value.LevelContentSearched = true; ref RectTransform? levelContent = ref value.LevelContent; Transform obj = ((Transform)hudContentParent).Find("CreatureManager_LevelContent"); levelContent = (RectTransform?)(object)((obj is RectTransform) ? obj : null); } if ((Object)(object)value.LevelContent != (Object)null && ((Component)value.LevelContent).gameObject.activeSelf != active) { ((Component)value.LevelContent).gameObject.SetActive(active); } } } private static bool TryFindVanillaStarSlot(RectTransform? block, ref RectTransform? sourceBlock, ref Vector2 sourceCenter, ref float bestScore) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)block == (Object)null) { return false; } Image[] componentsInChildren = ((Component)block).GetComponentsInChildren(true); bool result = false; Image[] array = componentsInChildren; foreach (Image val in array) { if (((Object)((Component)val).gameObject).name.StartsWith("star", StringComparison.OrdinalIgnoreCase) && !((Object)(object)val.sprite == (Object)null)) { float starImageScore = GetStarImageScore(((Graphic)val).color); if (!(starImageScore <= bestScore)) { sourceBlock = block; sourceCenter = GetRelativeCenter(block, ((Graphic)val).rectTransform); bestScore = starImageScore; result = true; } } } return result; } private static float GetStarImageScore(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(color.r, Mathf.Max(color.g, color.b)); float num2 = Mathf.Min(color.r, Mathf.Min(color.g, color.b)); float num3 = Mathf.Max(0f, color.r + color.g - color.b); return color.a * (num + num - num2 + num3); } private static RectTransform? EnsureLevelContent(HudData hud, bool showStars) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown RectTransform hudContentParent = GetHudContentParent(hud); if ((Object)(object)hudContentParent == (Object)null) { return null; } HudContentState value = HudContentStates.GetValue(hudContentParent, (RectTransform _) => new HudContentState()); if (!value.LevelContentSearched) { value.LevelContentSearched = true; ref RectTransform? levelContent = ref value.LevelContent; Transform obj = ((Transform)hudContentParent).Find("CreatureManager_LevelContent"); levelContent = (RectTransform?)(object)((obj is RectTransform) ? obj : null); } if ((Object)(object)value.LevelContent != (Object)null) { PositionLevelContent(value.LevelContent, hud, showStars); return value.LevelContent; } RectTransform val = (RectTransform)new GameObject("CreatureManager_LevelContent", new Type[2] { typeof(RectTransform), typeof(HorizontalLayoutGroup) }).transform; ((Transform)val).SetParent((Transform)(object)hudContentParent, false); ((Transform)val).SetAsLastSibling(); PositionLevelContent(val, hud, showStars); ConfigureContentLayout(val); EnsureStarGroup(val); EnsureStarNumber(val, hud.m_name); EnsureIconContainer(val); value.LevelContent = val; return val; } private static RectTransform? EnsureBossLevelContent(HudData hud, bool showStars) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown RectTransform hudContentParent = GetHudContentParent(hud); if ((Object)(object)hudContentParent == (Object)null) { return null; } HudContentState value = HudContentStates.GetValue(hudContentParent, (RectTransform _) => new HudContentState()); if (!value.BossContentSearched) { value.BossContentSearched = true; ref RectTransform? bossContent = ref value.BossContent; Transform obj = ((Transform)hudContentParent).Find("CreatureManager_BossLevelContent"); bossContent = (RectTransform?)(object)((obj is RectTransform) ? obj : null); } if ((Object)(object)value.BossContent != (Object)null) { PositionBossContent(value.BossContent, hud, showStars); return value.BossContent; } RectTransform val = (RectTransform)new GameObject("CreatureManager_BossLevelContent", new Type[2] { typeof(RectTransform), typeof(HorizontalLayoutGroup) }).transform; ((Transform)val).SetParent((Transform)(object)hudContentParent, false); ((Transform)val).SetAsLastSibling(); PositionBossContent(val, hud, showStars); ConfigureContentLayout(val); EnsureStarGroup(val); EnsureStarNumber(val, hud.m_name); EnsureIconContainer(val); value.BossContent = val; return val; } private static void ConfigureContentLayout(RectTransform content) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown HorizontalLayoutGroup component = ((Component)content).GetComponent(); if (!((Object)(object)component == (Object)null)) { ((LayoutGroup)component).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)component).spacing = 0f; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; int num = -Mathf.RoundToInt(2.125f); if (((LayoutGroup)component).padding.left != num || ((LayoutGroup)component).padding.right != 0 || ((LayoutGroup)component).padding.top != 0 || ((LayoutGroup)component).padding.bottom != 0) { ((LayoutGroup)component).padding = new RectOffset(num, 0, 0, 0); } } } private static RectTransform? GetHudContentParent(HudData hud) { RectTransform healthRoot = GetHealthRoot(hud.m_healthFast); if ((Object)(object)healthRoot != (Object)null) { Transform parent = ((Transform)healthRoot).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val != null) { return val; } } if ((Object)(object)hud.m_level2 != (Object)null) { Transform parent2 = ((Transform)hud.m_level2).parent; RectTransform val2 = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null); if (val2 != null) { return val2; } } if ((Object)(object)hud.m_level3 != (Object)null) { Transform parent3 = ((Transform)hud.m_level3).parent; RectTransform val3 = (RectTransform)(object)((parent3 is RectTransform) ? parent3 : null); if (val3 != null) { return val3; } } if ((Object)(object)hud.m_name != (Object)null) { Transform parent4 = ((Transform)((TMP_Text)hud.m_name).rectTransform).parent; RectTransform val4 = (RectTransform)(object)((parent4 is RectTransform) ? parent4 : null); if (val4 != null) { return val4; } } if ((Object)(object)hud.m_gui != (Object)null) { Transform transform = hud.m_gui.transform; RectTransform val5 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val5 != null) { return val5; } } return null; } private static RectTransform? GetHealthRoot(GuiBar? healthFast) { if ((Object)(object)healthFast == (Object)null) { return null; } Transform transform = ((Component)healthFast).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (!((Object)(object)val != (Object)null)) { return null; } Transform parent = ((Transform)val).parent; return (RectTransform?)(object)((parent is RectTransform) ? parent : null); } private static void PositionLevelContent(RectTransform content, HudData hud, bool showStars) { PositionHudContent(content, hud, 104f, 2f, GetLevelContentHeight(showStars)); } private static void PositionBossContent(RectTransform content, HudData hud, bool showStars) { PositionHudContent(content, hud, 220f, 2f, GetLevelContentHeight(showStars)); } private static void PositionHudContent(RectTransform content, HudData hud, float fallbackWidth, float belowReferenceGap, float contentHeight) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)content).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); RectTransform healthRoot = GetHealthRoot(hud.m_healthFast); if ((Object)(object)val != (Object)null && (Object)(object)healthRoot != (Object)null && (Object)(object)((Transform)healthRoot).parent == (Object)(object)val) { PositionBelowReference(content, healthRoot, belowReferenceGap, 1f, contentHeight); return; } RectTransform val2 = (((Object)(object)hud.m_level2 != (Object)null && (Object)(object)((Transform)hud.m_level2).parent == (Object)(object)val) ? hud.m_level2 : (((Object)(object)hud.m_level3 != (Object)null && (Object)(object)((Transform)hud.m_level3).parent == (Object)(object)val) ? hud.m_level3 : null)); if ((Object)(object)val2 != (Object)null) { Rect rect = val2.rect; float num = Mathf.Max(fallbackWidth, Mathf.Max(((Rect)(ref rect)).width, val2.sizeDelta.x)); ApplyRectLayout(content, val2.anchorMin, val2.anchorMax, val2.pivot, val2.anchoredPosition, new Vector2(num, contentHeight)); return; } RectTransform val3 = (((Object)(object)hud.m_name != (Object)null) ? ((TMP_Text)hud.m_name).rectTransform : null); if ((Object)(object)val != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)((Transform)val3).parent == (Object)(object)val) { PositionBelowReference(content, val3, belowReferenceGap, fallbackWidth, contentHeight); } else { ApplyRectLayout(content, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(fallbackWidth, contentHeight)); } } private static void PositionBelowReference(RectTransform content, RectTransform reference, float gap, float minimumWidth, float contentHeight) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_003f: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) Rect rect = reference.rect; float num = Mathf.Max(1f, Mathf.Max(((Rect)(ref rect)).width, reference.sizeDelta.x)); rect = reference.rect; float num2 = Mathf.Max(1f, Mathf.Max(((Rect)(ref rect)).height, reference.sizeDelta.y)); float num3 = Mathf.Max(minimumWidth, num); float num4 = reference.anchoredPosition.y - reference.pivot.y * num2 - gap - (1f - reference.pivot.y) * contentHeight; ApplyRectLayout(content, reference.anchorMin, reference.anchorMax, reference.pivot, new Vector2(reference.anchoredPosition.x, num4), new Vector2(num3, contentHeight)); } private static void ApplyRectLayout(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 sizeDelta) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) SetRectValue(rect, anchorMin, anchorMax, pivot, anchoredPosition); if (rect.sizeDelta != sizeDelta) { rect.sizeDelta = sizeDelta; } } private static void SetRectValue(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (rect.anchorMin != anchorMin) { rect.anchorMin = anchorMin; } if (rect.anchorMax != anchorMax) { rect.anchorMax = anchorMax; } if (rect.pivot != pivot) { rect.pivot = pivot; } if (rect.anchoredPosition != anchoredPosition) { rect.anchoredPosition = anchoredPosition; } } private static void UpdateStarBadge(RectTransform content, RectTransform? level2, RectTransform? level3, TextMeshProUGUI? nameText, int stars, bool showIndividualStars = false) { RectTransform val = EnsureStarGroup(content); TextMeshProUGUI val2 = EnsureStarNumber(content, nameText); int iconCount = ((!showIndividualStars) ? 1 : Mathf.Clamp(stars, 1, 2)); bool flag = stars > 0 && EnsureVanillaStarLayers(val, level2, level3, iconCount); ((Component)val).gameObject.SetActive(flag); bool flag2 = flag && !showIndividualStars; ((Component)val2).gameObject.SetActive(flag2); if (flag2 && (!int.TryParse(((TMP_Text)val2).text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result != stars)) { ((TMP_Text)val2).text = stars.ToString(CultureInfo.InvariantCulture); ConfigureStarNumber(val2); } } private static RectTransform EnsureStarGroup(RectTransform content) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Transform val = ((Transform)content).Find("CreatureManager_StarGroup"); if ((Object)(object)val != (Object)null) { return (RectTransform)val; } GameObject val2 = new GameObject("CreatureManager_StarGroup", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); RectTransform val3 = (RectTransform)val2.transform; ((Transform)val3).SetParent((Transform)(object)content, false); float num = 17f; val3.sizeDelta = new Vector2(num, num); LayoutElement component = val2.GetComponent(); component.minWidth = num; component.preferredWidth = num; component.minHeight = num; component.preferredHeight = num; return val3; } private static bool EnsureVanillaStarLayers(RectTransform starGroup, RectTransform? level2, RectTransform? level3, int iconCount) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) iconCount = Mathf.Clamp(iconCount, 1, 2); Transform obj = ((Transform)starGroup).Find("CreatureManager_StarSlot1"); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if ((Object)(object)val == (Object)null) { RectTransform sourceBlock = null; Vector2 sourceCenter = Vector2.zero; float bestScore = -1f; TryFindVanillaStarSlot(level2, ref sourceBlock, ref sourceCenter, ref bestScore); TryFindVanillaStarSlot(level3, ref sourceBlock, ref sourceCenter, ref bestScore); TryFindTemplateVanillaStarSlot(ref sourceBlock, ref sourceCenter, ref bestScore); val = CreateStarSlot(starGroup, 1); if ((Object)(object)sourceBlock != (Object)null) { Image[] componentsInChildren = ((Component)sourceBlock).GetComponentsInChildren(true); foreach (Image val2 in componentsInChildren) { if (((Object)((Component)val2).gameObject).name.StartsWith("star", StringComparison.OrdinalIgnoreCase) && !((Object)(object)val2.sprite == (Object)null) && Vector2.Distance(GetRelativeCenter(sourceBlock, ((Graphic)val2).rectTransform), sourceCenter) <= 5f) { CopyStarLayer(val, sourceBlock, sourceCenter, val2); } } } if (((Transform)val).childCount == 0) { CreateFallbackStarLayer(val); } } Transform obj2 = ((Transform)starGroup).Find("CreatureManager_StarSlot2"); RectTransform val3 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if (iconCount == 2 && (Object)(object)val3 == (Object)null) { GameObject obj3 = Object.Instantiate(((Component)val).gameObject, (Transform)(object)starGroup, false); ((Object)obj3).name = "CreatureManager_StarSlot2"; Transform transform = obj3.transform; val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null); } ConfigureStarSlots(starGroup, val, val3, iconCount); return true; } private static RectTransform CreateStarSlot(RectTransform starGroup, int index) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject("CreatureManager_StarSlot" + index, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent((Transform)(object)starGroup, false); val.anchorMin = new Vector2(0.5f, 0.5f); val.anchorMax = new Vector2(0.5f, 0.5f); val.pivot = new Vector2(0.5f, 0.5f); val.sizeDelta = new Vector2(16f, 16f); return val; } private static void CreateFallbackStarLayer(RectTransform slot) { //IL_0025: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0067: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CreatureManager_FallbackStar", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)slot, false); val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.anchoredPosition = Vector2.zero; val2.sizeDelta = new Vector2(16f, 16f); Image component = val.GetComponent(); component.sprite = GetFallbackStarSprite(); ((Graphic)component).color = Color.white; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; } private static void ConfigureStarSlots(RectTransform starGroup, RectTransform firstSlot, RectTransform? secondSlot, int iconCount) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) float num = 17f; float scale = num / 16f; float num2 = num * (float)iconCount; bool flag = false; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num2, num); if (starGroup.sizeDelta != val) { starGroup.sizeDelta = val; flag = true; } LayoutElement component = ((Component)starGroup).GetComponent(); if ((Object)(object)component != (Object)null && (!Mathf.Approximately(component.minWidth, num2) || !Mathf.Approximately(component.preferredWidth, num2) || !Mathf.Approximately(component.minHeight, num) || !Mathf.Approximately(component.preferredHeight, num))) { component.minWidth = num2; component.preferredWidth = num2; component.minHeight = num; component.preferredHeight = num; flag = true; } ((Component)firstSlot).gameObject.SetActive(true); flag |= ConfigureStarSlot(firstSlot, (Vector2)((iconCount == 2) ? new Vector2((0f - num) * 0.5f, 0f) : Vector2.zero), scale); if ((Object)(object)secondSlot != (Object)null) { ((Component)secondSlot).gameObject.SetActive(iconCount == 2); flag |= ConfigureStarSlot(secondSlot, new Vector2(num * 0.5f, 0f), scale); } if (flag) { LayoutRebuilder.MarkLayoutForRebuild(starGroup); Transform parent = ((Transform)starGroup).parent; RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val2 != null) { LayoutRebuilder.MarkLayoutForRebuild(val2); } } } private static bool ConfigureStarSlot(RectTransform slot, Vector2 position, float scale) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) bool result = false; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(16f, 16f); if (slot.sizeDelta != val) { slot.sizeDelta = val; result = true; } if (slot.anchoredPosition != position) { slot.anchoredPosition = position; result = true; } Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(scale, scale, 1f); if (((Transform)slot).localScale != val2) { ((Transform)slot).localScale = val2; result = true; } return result; } private static void TryFindTemplateVanillaStarSlot(ref RectTransform? sourceBlock, ref Vector2 sourceCenter, ref float score) { GameObject val = (((Object)(object)EnemyHud.instance != (Object)null) ? EnemyHud.instance.m_baseHud : null); if (!((Object)(object)val == (Object)null)) { Transform obj = val.transform.Find("level_2"); TryFindVanillaStarSlot((RectTransform?)(object)((obj is RectTransform) ? obj : null), ref sourceBlock, ref sourceCenter, ref score); Transform obj2 = val.transform.Find("level_3"); TryFindVanillaStarSlot((RectTransform?)(object)((obj2 is RectTransform) ? obj2 : null), ref sourceBlock, ref sourceCenter, ref score); } } private static void CopyStarLayer(RectTransform starGroup, RectTransform sourceBlock, Vector2 sourceCenter, Image source) { //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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CreatureManager_" + ((Object)((Component)source).gameObject).name, new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)starGroup, false); val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.pivot = ((Graphic)source).rectTransform.pivot; Vector2 relativeCenter = GetRelativeCenter(sourceBlock, ((Graphic)source).rectTransform); val2.anchoredPosition = relativeCenter - sourceCenter; Rect rect = ((Graphic)source).rectTransform.rect; float num = Mathf.Max(1f, ((Rect)(ref rect)).width); rect = ((Graphic)source).rectTransform.rect; val2.sizeDelta = new Vector2(num, Mathf.Max(1f, ((Rect)(ref rect)).height)); ((Transform)val2).localScale = ((Transform)((Graphic)source).rectTransform).localScale; Image component = val.GetComponent(); component.sprite = source.sprite; ((Graphic)component).color = ((Graphic)source).color; ((Graphic)component).material = ((Graphic)source).material; component.type = source.type; component.preserveAspect = source.preserveAspect; component.fillCenter = source.fillCenter; component.fillMethod = source.fillMethod; component.fillAmount = source.fillAmount; component.fillClockwise = source.fillClockwise; component.fillOrigin = source.fillOrigin; ((Graphic)component).raycastTarget = false; } private static Vector2 GetRelativeCenter(RectTransform root, RectTransform rect) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[4]; rect.GetWorldCorners(array); Vector3 val = (array[0] + array[2]) * 0.5f; Vector3 val2 = ((Transform)root).InverseTransformPoint(val); return new Vector2(val2.x, val2.y); } private static TextMeshProUGUI EnsureStarNumber(RectTransform content, TextMeshProUGUI? nameText) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Transform)content).Find("CreatureManager_StarNumber"); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent(); } GameObject val2 = new GameObject("CreatureManager_StarNumber", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val2.SetActive(false); ((Transform)(RectTransform)val2.transform).SetParent((Transform)(object)content, false); TextMeshProUGUI obj = val2.AddComponent(); ApplyHudFont((TMP_Text)(object)obj, (TMP_Text?)(object)nameText); ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).enableAutoSizing = false; ((TMP_Text)obj).alignment = (TextAlignmentOptions)4097; ((Graphic)obj).color = new Color(1f, 0.74f, 0.24f, 1f); ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj).overflowMode = (TextOverflowModes)0; ConfigureStarNumber(obj); val2.SetActive(true); return obj; } private static void ConfigureStarNumber(TextMeshProUGUI number) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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) float num = 17f; float num2 = num / 16f; float num3 = 15f * num2; if (!Mathf.Approximately(((TMP_Text)number).fontSize, num3)) { ((TMP_Text)number).fontSize = num3; } float num4 = (float)Mathf.Max(1, ((TMP_Text)number).text?.Length ?? 0) * num3 * 0.65f; float num5 = Mathf.Max(20f * num2, num4 + 2f * num2); float num6 = Mathf.Max(20f, num); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num5, num6); RectTransform rectTransform = ((TMP_Text)number).rectTransform; bool flag = false; if (rectTransform.sizeDelta != val) { rectTransform.sizeDelta = val; flag = true; } LayoutElement component = ((Component)number).GetComponent(); if (!Mathf.Approximately(component.minWidth, num5) || !Mathf.Approximately(component.preferredWidth, num5) || !Mathf.Approximately(component.minHeight, num6) || !Mathf.Approximately(component.preferredHeight, num6)) { component.minWidth = num5; component.preferredWidth = num5; component.minHeight = num6; component.preferredHeight = num6; flag = true; } if (flag) { Transform parent = ((Transform)rectTransform).parent; RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val2 != null) { LayoutRebuilder.MarkLayoutForRebuild(val2); } } } private static float GetLevelContentHeight(bool showStars) { if (!showStars) { return 20f; } return Mathf.Max(20f, 17f); } private static int GetModifierIconContainerWidth() { return Mathf.CeilToInt(71f); } private static RectTransform EnsureIconContainer(RectTransform row) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_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_0077: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_007e: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0022: Expected O, but got Unknown Transform val = ((Transform)row).Find("CreatureManager_ModifierIcons"); if ((Object)(object)val != (Object)null) { RectTransform val2 = (RectTransform)val; EnsureModifierSlots(val2); return val2; } RectTransform val3 = (RectTransform)new GameObject("CreatureManager_ModifierIcons", new Type[3] { typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(LayoutElement) }).transform; ((Transform)val3).SetParent((Transform)(object)row, false); ((Transform)val3).SetAsLastSibling(); ConfigureIconContainer(val3); EnsureModifierSlots(val3); return val3; } private static void ConfigureIconContainer(RectTransform container) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0078: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown int modifierIconContainerWidth = GetModifierIconContainerWidth(); int num = 17; float num2 = (float)num * 0.125f; SetRectValue(container, Vector2.one, Vector2.one, Vector2.one, new Vector2(num2, 0f)); LayoutElement component = ((Component)container).GetComponent(); component.ignoreLayout = true; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor((float)modifierIconContainerWidth, (float)num); bool flag = false; if (container.sizeDelta != val) { container.sizeDelta = val; flag = true; } HorizontalLayoutGroup component2 = ((Component)container).GetComponent(); if ((int)((LayoutGroup)component2).childAlignment != 5) { ((LayoutGroup)component2).childAlignment = (TextAnchor)5; } if (!Mathf.Approximately(((HorizontalOrVerticalLayoutGroup)component2).spacing, 1f)) { ((HorizontalOrVerticalLayoutGroup)component2).spacing = 1f; } if (((HorizontalOrVerticalLayoutGroup)component2).childControlWidth) { ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = false; } if (((HorizontalOrVerticalLayoutGroup)component2).childControlHeight) { ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = false; } if (((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth) { ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false; } if (((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight) { ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; } if (((LayoutGroup)component2).padding.left != 0 || ((LayoutGroup)component2).padding.right != 0 || ((LayoutGroup)component2).padding.top != 0 || ((LayoutGroup)component2).padding.bottom != 0) { ((LayoutGroup)component2).padding = new RectOffset(0, 0, 0, 0); } if (!Mathf.Approximately(component.minWidth, (float)modifierIconContainerWidth) || !Mathf.Approximately(component.preferredWidth, (float)modifierIconContainerWidth) || !Mathf.Approximately(component.minHeight, (float)num) || !Mathf.Approximately(component.preferredHeight, (float)num)) { component.minWidth = modifierIconContainerWidth; component.preferredWidth = modifierIconContainerWidth; component.minHeight = num; component.preferredHeight = num; flag = true; } if (flag) { LayoutRebuilder.MarkLayoutForRebuild(container); } } internal static void UpdateEnemyHuds(EnemyHud enemyHud) { if ((Object)(object)enemyHud == (Object)null) { return; } foreach (KeyValuePair hud in enemyHud.m_huds) { UpdateEnemyHud(hud.Key, hud.Value.m_isMount); } } private static bool TryGetHudModifierState(Character character, GameObject hudGui, out ModifierMask visible, out float armoredReduction, out float enragedBonus, out bool blamerActive, out bool refreshed) { int instanceID = ((Object)character).GetInstanceID(); float unscaledTime = Time.unscaledTime; if (ModifierHudRefreshStates.TryGetValue(instanceID, out ModifierHudRefreshState value) && value.Character == character && value.HudGui == hudGui && unscaledTime < value.NextRefreshTime) { visible = value.Visible; armoredReduction = value.ArmoredReduction; enragedBonus = value.EnragedBonus; blamerActive = value.BlamerActive; refreshed = false; return value.Result; } refreshed = true; bool result = ComputeHudModifierState(character, out visible, out armoredReduction, out enragedBonus, out blamerActive); if (!ModifierHudRefreshStates.TryGetValue(instanceID, out value) || value.Character != character) { value = new ModifierHudRefreshState { Character = character }; ModifierHudRefreshStates[instanceID] = value; } value.HudGui = hudGui; value.NextRefreshTime = unscaledTime + 0.25f; value.Result = result; value.Visible = visible; value.ArmoredReduction = armoredReduction; value.EnragedBonus = enragedBonus; value.BlamerActive = blamerActive; return result; } private static bool ComputeHudModifierState(Character character, out ModifierMask visible, out float armoredReduction, out float enragedBonus, out bool blamerActive) { visible = ModifierMask.None; armoredReduction = 0f; enragedBonus = 0f; blamerActive = false; if (!TryGetZdo(character, out ZDO zdo)) { return false; } if (!zdo.GetBool("CreatureManager_ModifiersApplied", false)) { return false; } ModifierMask storedModifierMask = GetStoredModifierMask(zdo); if (storedModifierMask != ModifierMask.None && !CreatureLevelManager.AllowsModifierEffects(character)) { return true; } if (HasModifier(storedModifierMask, ModifierMask.Armored)) { armoredReduction = Mathf.Clamp01(zdo.GetFloat("CreatureManager_ArmoredReduction", 0.35f)); } if (HasModifier(storedModifierMask, ModifierMask.Enraged)) { enragedBonus = Mathf.Clamp01(zdo.GetFloat("CreatureManager_EnragedBonus", 0.15f)); } blamerActive = HasModifier(storedModifierMask, ModifierMask.Blamer) && zdo.GetBool("CreatureManager_BlamerActive", false) && HasBlamerKarmaRemaining(zdo); ModifierSpec[] modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (IsModifierVisible(storedModifierMask, zdo, modifierSpec)) { visible |= modifierSpec.Mask; } } return true; } private static bool IsModifierVisible(ModifierMask stored, ZDO zdo, ModifierSpec spec) { if (!HasModifier(stored, spec.Mask)) { return false; } if (spec.Mask == ModifierMask.Deathward && !IsDeathwardReady(zdo)) { return false; } if (spec.Mask == ModifierMask.Reaping) { return HasStoredReapingSettings(zdo); } if (spec.Mask == ModifierMask.Chameleon) { return GetChameleonDamageType(zdo) != ChameleonDamageType.None; } if (spec.Mask == ModifierMask.Undodgeable) { return true; } if (spec.Mask == ModifierMask.Crippling) { if (zdo.GetFloat(spec.ProcChanceKey, 0f) > 0f) { if (!(zdo.GetFloat(spec.PowerKey, 0f) > 0f)) { return zdo.GetFloat("CreatureManager_CripplingJumpPower", 0f) > 0f; } return true; } return false; } if (spec.Mask == ModifierMask.Disruptive) { if (zdo.GetFloat(spec.ProcChanceKey, 0f) > 0f) { if (!(zdo.GetFloat(spec.PowerKey, 0f) > 0f)) { return zdo.GetFloat("CreatureManager_DisruptiveEitrPower", 0f) > 0f; } return true; } return false; } if (spec.ProcChanceKey != null) { if (zdo.GetFloat(spec.ProcChanceKey, 0f) > 0f) { return zdo.GetFloat(spec.PowerKey, 0f) > 0f; } return false; } return zdo.GetFloat(spec.PowerKey, 0f) > 0f; } private static bool TryGetArmoredReduction(Character character, out float reduction) { return TryGetModifierPower(character, ModifierMask.Armored, "CreatureManager_ArmoredReduction", 0.35f, out reduction); } private static bool TryGetEnragedBonus(Character character, out float bonus) { return TryGetModifierPower(character, ModifierMask.Enraged, "CreatureManager_EnragedBonus", 0.15f, out bonus); } private static bool TryGetReapingDamageBonus(Character character, out float bonus) { bonus = 0f; if (!TryGetZdo(character, out ZDO zdo) || !HasModifier(zdo, ModifierMask.Reaping) || !CreatureLevelManager.AllowsModifierEffects(character)) { return false; } bonus = Mathf.Max(0f, zdo.GetFloat("CreatureManager_ReapingDamageBonus", 0f)); return bonus > 0f; } private static bool TryGetReapingSettings(Character character, out ReapingSettings settings) { settings = default(ReapingSettings); if (!TryGetZdo(character, out ZDO zdo) || !HasModifier(zdo, ModifierMask.Reaping) || !CreatureLevelManager.AllowsModifierEffects(character)) { return false; } settings = new ReapingSettings(Mathf.Clamp01(zdo.GetFloat("CreatureManager_ReapingPower", 0.1f)), zdo.GetInt("CreatureManager_ReapingHealMaxActivations", 20), zdo.GetFloat("CreatureManager_ReapingMaxHealthPerKill", 0.1f), zdo.GetFloat("CreatureManager_ReapingMaxHealthCap", 2f), zdo.GetFloat("CreatureManager_ReapingDamagePerKill", 0f), zdo.GetFloat("CreatureManager_ReapingDamageCap", 0f), zdo.GetFloat("CreatureManager_ReapingScalePerKill", 0f), zdo.GetFloat("CreatureManager_ReapingScaleCap", 0f)); return settings.HasAnyGain; } private static bool HasStoredReapingSettings(ZDO zdo) { if ((!(zdo.GetFloat("CreatureManager_ReapingPower", 0f) > 0f) || zdo.GetInt("CreatureManager_ReapingHealMaxActivations", 0) <= 0) && (!(zdo.GetFloat("CreatureManager_ReapingMaxHealthPerKill", 0f) > 0f) || !(zdo.GetFloat("CreatureManager_ReapingMaxHealthCap", 0f) > 0f)) && (!(zdo.GetFloat("CreatureManager_ReapingDamagePerKill", 0f) > 0f) || !(zdo.GetFloat("CreatureManager_ReapingDamageCap", 0f) > 0f))) { if (zdo.GetFloat("CreatureManager_ReapingScalePerKill", 0f) > 0f) { return zdo.GetFloat("CreatureManager_ReapingScaleCap", 0f) > 0f; } return false; } return true; } private static bool TryGetDeathwardHealth(Character character, out float healthRatio) { healthRatio = 0f; if (!TryGetZdo(character, out ZDO zdo) || !HasModifier(zdo, ModifierMask.Deathward) || !CreatureLevelManager.AllowsModifierEffects(character) || !IsDeathwardReady(zdo)) { return false; } healthRatio = Mathf.Clamp01(zdo.GetFloat("CreatureManager_DeathwardHealth", 0.2f)); return healthRatio > 0f; } private static bool IsDeathwardReady(ZDO zdo) { int num = Math.Max(0, zdo.GetInt("CreatureManager_DeathwardActivationCount", 0)); int num2 = ResolveDeathwardMaxActivations(zdo.GetInt("CreatureManager_DeathwardMaxActivations", 3)); if (num < num2) { return zdo.GetFloat("CreatureManager_DeathwardNextReadyTime", 0f) <= GetNetworkTimeSeconds(); } return false; } private static bool TryGetModifierHotPathState(Character character, ModifierMask modifier, out ModifierHotPathState state) { state = null; if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } int instanceID = ((Object)character).GetInstanceID(); float unscaledTime = Time.unscaledTime; if (!ModifierHotPathStates.TryGetValue(instanceID, out state) || state.Character != character) { if (!TryGetZdo(character, out ZDO zdo) || !zdo.GetBool("CreatureManager_ModifiersApplied", false)) { ModifierHotPathStates.Remove(instanceID); state = null; return false; } state = new ModifierHotPathState { Character = character }; ModifierHotPathStates[instanceID] = state; UpdateModifierHotPathState(state, zdo, unscaledTime); } else if (unscaledTime >= state.NextValidationTime) { if (!TryGetZdo(character, out ZDO zdo2) || !zdo2.GetBool("CreatureManager_ModifiersApplied", false)) { ModifierHotPathStates.Remove(instanceID); state = null; return false; } UpdateModifierHotPathState(state, zdo2, unscaledTime); } if (!HasModifier(state.Mask, modifier)) { return false; } int frameCount = Time.frameCount; if (state.EligibilityFrame != frameCount) { state.EligibilityFrame = frameCount; state.EffectsAllowed = CreatureLevelManager.AllowsModifierEffects(character); } return state.EffectsAllowed; } private static bool TryGetHotPathModifierZdo(Character character, ModifierMask modifier, out ZDO zdo) { zdo = null; if (!TryGetModifierHotPathState(character, modifier, out ModifierHotPathState _)) { return false; } if (TryGetZdo(character, out zdo)) { return true; } ModifierHotPathStates.Remove(((Object)character).GetInstanceID()); return false; } private static void RefreshModifierHotPathState(Character character, ZDO zdo) { if (!((Object)(object)character == (Object)null) && zdo != null && zdo.GetBool("CreatureManager_ModifiersApplied", false)) { int instanceID = ((Object)character).GetInstanceID(); if (!ModifierHotPathStates.TryGetValue(instanceID, out ModifierHotPathState value) || value.Character != character) { value = new ModifierHotPathState { Character = character }; ModifierHotPathStates[instanceID] = value; } UpdateModifierHotPathState(value, zdo, Time.unscaledTime); } } private static void UpdateModifierHotPathState(ModifierHotPathState state, ZDO zdo, float now) { state.Mask = GetStoredModifierMask(zdo); state.SwiftFactor = (HasModifier(state.Mask, ModifierMask.Swift) ? Mathf.Max(1f, 1f + Mathf.Clamp01(zdo.GetFloat("CreatureManager_SwiftPower", 0.25f))) : 1f); state.AttackSpeedFactor = (HasModifier(state.Mask, ModifierMask.AttackSpeed) ? GetAttackSpeedFactor(Mathf.Clamp01(zdo.GetFloat("CreatureManager_AttackSpeedPower", 0.35f))) : 1f); state.UndodgeableEffectActive = HasModifier(state.Mask, ModifierMask.Undodgeable); state.UndodgeableDamageReduction = (state.UndodgeableEffectActive ? ClampUndodgeableDamageReduction(zdo.GetFloat("CreatureManager_UndodgeableDamageReduction", 0.25f)) : 0f); state.EligibilityFrame = -1; state.NextValidationTime = now + 1f; } private static void InvalidateModifierCaches(Character character) { if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); ModifierHotPathStates.Remove(instanceID); ModifierHudRefreshStates.Remove(instanceID); PendingReapingHealthBonusRatios.Remove(instanceID); } } private static void InvalidateModifierHudState(Character character) { if ((Object)(object)character != (Object)null) { ModifierHudRefreshStates.Remove(((Object)character).GetInstanceID()); } } private static bool TryGetModifierPower(Character character, ModifierMask modifier, string key, float fallback, out float power) { power = 0f; if (!TryGetZdo(character, out ZDO zdo) || !HasModifier(zdo, modifier) || !CreatureLevelManager.AllowsModifierEffects(character)) { return false; } power = Mathf.Clamp01(zdo.GetFloat(key, fallback)); return power > 0f; } private static bool HasModifier(ZDO zdo, ModifierMask modifier) { return HasModifier(GetStoredModifierMask(zdo), modifier); } private static bool HasModifier(ModifierMask stored, ModifierMask modifier) { return (stored & modifier) != 0; } private static bool HasAnyActiveModifier(Character character) { if (!TryGetZdo(character, out ZDO zdo) || GetStoredModifierMask(zdo) == ModifierMask.None) { return false; } return CreatureLevelManager.AllowsModifierEffects(character); } private static ModifierMask GetStoredModifierMask(ZDO zdo) { return (ModifierMask)zdo.GetLong("CreatureManager_ModifierMask64", 0L); } private static void SetStoredModifierMask(ZDO zdo, ModifierMask mask) { zdo.Set("CreatureManager_ModifierMask64", (long)mask); } private static bool TryFindCharacter(ZDOID id, out Character character) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) character = null; if (id == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject val = ZNetScene.instance.FindInstance(id); character = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); return (Object)(object)character != (Object)null; } private static bool TryGetCharacterZdo(Character character, out ZDO zdo) { zdo = null; if ((Object)(object)character == (Object)null) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } zdo = nview.GetZDO(); return zdo != null; } private static bool TryGetZdo(Character character, out ZDO zdo) { zdo = null; if ((Object)(object)character != (Object)null && !character.IsPlayer()) { return TryGetCharacterZdo(character, out zdo); } return false; } private static void EnsureModifierSlots(RectTransform row) { HudIconState hudIconState = GetHudIconState(row); bool flag = hudIconState.SlotsInitialized; if (flag) { for (int i = 0; i < hudIconState.Slots.Length; i++) { if (!((Object)(object)hudIconState.Slots[i] != (Object)null)) { flag = false; break; } } } if (!flag) { for (int j = 0; j < ModifierGroupOrder.Length; j++) { hudIconState.Slots[j] = EnsureModifierSlot(row, ModifierGroupOrder[j], j, 17); } hudIconState.Initialized = false; hudIconState.SlotsInitialized = true; } } private static void UpdateModifierIcons(RectTransform row, ModifierMask visible, float armoredReduction, float enragedBonus) { int armored = Mathf.RoundToInt(armoredReduction * 10000f); int enraged = Mathf.RoundToInt(enragedBonus * 10000f); CreatureManagerPlugin.ModifierIconLayout modifierIconLayout = CreatureManagerPlugin.ModifierHudIconLayout?.Value ?? CreatureManagerPlugin.ModifierIconLayout.FixedCategorySlots; HudIconState hudIconState = GetHudIconState(row); if (!hudIconState.Matches(visible, armored, enraged, modifierIconLayout)) { ModifierSpec[] array = BuildHudModifierSlots(visible, armoredReduction, enragedBonus, modifierIconLayout); hudIconState.Set(visible, armored, enraged, modifierIconLayout); bool flag = false; bool packEmptySlots = modifierIconLayout == CreatureManagerPlugin.ModifierIconLayout.RightPacked; for (int i = 0; i < array.Length; i++) { flag |= UpdateModifierSlot(row, i, array[i], packEmptySlots); } if (flag) { LayoutRebuilder.MarkLayoutForRebuild(row); } } } private static HudIconState GetHudIconState(RectTransform row) { return HudIconStates.GetValue(row, (RectTransform _) => new HudIconState()); } private static Image EnsureModifierSlot(RectTransform row, ModifierGroup group, int siblingIndex, int iconSize) { //IL_0067: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown string modifierSlotName = GetModifierSlotName(group); Transform val = ((Transform)row).Find(modifierSlotName); if ((Object)(object)val != (Object)null) { val.SetSiblingIndex(siblingIndex); ((Component)val).gameObject.SetActive(true); Image component = ((Component)val).GetComponent(); ConfigureModifierSlot(component, iconSize); return component; } GameObject val2 = new GameObject(modifierSlotName, new Type[3] { typeof(RectTransform), typeof(Image), typeof(LayoutElement) }); RectTransform val3 = (RectTransform)val2.transform; ((Transform)val3).SetParent((Transform)(object)row, false); Image component2 = val2.GetComponent(); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; ((Behaviour)component2).enabled = false; ((Transform)val3).SetSiblingIndex(siblingIndex); ConfigureModifierSlot(component2, iconSize); return component2; } private static void ConfigureModifierSlot(Image image, int size) { //IL_0015: 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_0023: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor((float)size, (float)size); RectTransform rectTransform = ((Graphic)image).rectTransform; bool flag = false; if (rectTransform.sizeDelta != val) { rectTransform.sizeDelta = val; flag = true; } LayoutElement component = ((Component)image).GetComponent(); if (!Mathf.Approximately(component.preferredWidth, (float)size) || !Mathf.Approximately(component.preferredHeight, (float)size) || !Mathf.Approximately(component.minWidth, (float)size) || !Mathf.Approximately(component.minHeight, (float)size)) { component.preferredWidth = size; component.preferredHeight = size; component.minWidth = size; component.minHeight = size; flag = true; } if (flag) { Transform parent = ((Transform)rectTransform).parent; RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val2 != null) { LayoutRebuilder.MarkLayoutForRebuild(val2); } } } private static ModifierSpec?[] BuildHudModifierSlots(ModifierMask visible, float armoredReduction, float enragedBonus, CreatureManagerPlugin.ModifierIconLayout layout) { ModifierSpec[] array = new ModifierSpec[4]; ModifierSpec[] modifierSpecs; if (layout == CreatureManagerPlugin.ModifierIconLayout.RightPacked) { int num = 0; modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec in modifierSpecs) { if (IsModifierIconVisible(modifierSpec, visible, armoredReduction, enragedBonus)) { array[num++] = modifierSpec; if (num >= array.Length) { break; } } } return array; } ModifierSpec[] array2 = new ModifierSpec[4]; int num2 = 0; modifierSpecs = ModifierSpecs; foreach (ModifierSpec modifierSpec2 in modifierSpecs) { if (IsModifierIconVisible(modifierSpec2, visible, armoredReduction, enragedBonus)) { int num3 = (int)modifierSpec2.Group; if (array[num3] == null) { array[num3] = modifierSpec2; } else if (num2 < array2.Length) { array2[num2++] = modifierSpec2; } } } int num4 = 0; for (int j = 0; j < array.Length; j++) { if (num4 >= num2) { break; } if (array[j] == null) { array[j] = array2[num4++]; } } return array; } private static bool IsModifierIconVisible(ModifierSpec spec, ModifierMask visible, float armoredReduction, float enragedBonus) { if (HasModifier(visible, spec.Mask) && (spec.Mask != ModifierMask.Armored || armoredReduction > 0f)) { if (spec.Mask == ModifierMask.Enraged) { return enragedBonus > 0f; } return true; } return false; } private static bool UpdateModifierSlot(RectTransform row, int slotIndex, ModifierSpec? spec, bool packEmptySlots) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) HudIconState hudIconState = GetHudIconState(row); ModifierGroup modifierGroup = ModifierGroupOrder[slotIndex]; Image val = hudIconState.Slots[slotIndex] ?? EnsureModifierSlot(row, modifierGroup, slotIndex, 17); if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } ((Behaviour)val).enabled = spec != null; val.sprite = spec?.Sprite(); ((Graphic)val).color = Color.white; LayoutElement component = ((Component)val).GetComponent(); bool flag = packEmptySlots && spec == null; if ((Object)(object)component == (Object)null || component.ignoreLayout == flag) { return false; } component.ignoreLayout = flag; return true; } private static string GetModifierSlotName(ModifierGroup group) { return group switch { ModifierGroup.Offense => "CreatureManager_ModifierSlot_Offense", ModifierGroup.Defense => "CreatureManager_ModifierSlot_Defense", ModifierGroup.Affliction => "CreatureManager_ModifierSlot_Affliction", ModifierGroup.Special => "CreatureManager_ModifierSlot_Special", _ => throw new ArgumentOutOfRangeException("group", group, null), }; } private static Sprite GetArmoredSprite() { return GetModifierIconSprite(ref ArmoredSprite, "armored"); } private static Sprite GetEnragedSprite() { return GetModifierIconSprite(ref EnragedSprite, "enraged"); } private static Sprite GetDeathwardSprite() { return GetModifierIconSprite(ref DeathwardSprite, "deathward"); } private static Sprite GetSwiftSprite() { return GetModifierIconSprite(ref SwiftSprite, "swift"); } private static Sprite GetRegeneratingSprite() { return GetModifierIconSprite(ref RegeneratingSprite, "regenerating"); } private static Sprite GetVampiricSprite() { return GetModifierIconSprite(ref VampiricSprite, "vampiric"); } private static Sprite GetFireSprite() { return GetModifierIconSprite(ref FireSprite, "fire"); } private static Sprite GetFrostSprite() { return GetModifierIconSprite(ref FrostSprite, "frost"); } private static Sprite GetLightningSprite() { return GetModifierIconSprite(ref LightningSprite, "lightning"); } private static Sprite GetSpiritSprite() { return GetModifierIconSprite(ref SpiritSprite, "spirit"); } private static Sprite GetToxicDeathSprite() { return GetModifierIconSprite(ref ToxicDeathSprite, "toxicDeath"); } private static Sprite GetArmorPiercingSprite() { return GetModifierIconSprite(ref ArmorPiercingSprite, "armorPiercing"); } private static Sprite GetStaggeringSprite() { return GetModifierIconSprite(ref StaggeringSprite, "staggering"); } private static Sprite GetUndodgeableSprite() { return GetModifierIconSprite(ref UndodgeableSprite, "undodgeable"); } private static Sprite GetAttackSpeedSprite() { return GetModifierIconSprite(ref AttackSpeedSprite, "attackSpeed"); } private static Sprite GetExposedSprite() { return GetModifierIconSprite(ref ExposedSprite, "exposed"); } private static Sprite GetWeakenedSprite() { return GetModifierIconSprite(ref WeakenedSprite, "weakened"); } private static Sprite GetWitheredSprite() { return GetModifierIconSprite(ref WitheredSprite, "withered"); } private static Sprite GetReflectionSprite() { return GetModifierIconSprite(ref ReflectionSprite, "reflection"); } private static Sprite GetVortexSprite() { return GetModifierIconSprite(ref VortexSprite, "vortex"); } private static Sprite GetCripplingSprite() { return GetModifierIconSprite(ref CripplingSprite, "crippling"); } private static Sprite GetDisruptiveSprite() { return GetModifierIconSprite(ref DisruptiveSprite, "disruptive"); } private static Sprite GetAdrenalineDrainSprite() { return GetModifierIconSprite(ref AdrenalineDrainSprite, "adrenalineDrain"); } private static Sprite GetCorrosiveSprite() { return GetModifierIconSprite(ref CorrosiveSprite, "corrosive"); } private static Sprite GetAdaptiveSprite() { return GetModifierIconSprite(ref AdaptiveSprite, "adaptive"); } private static Sprite GetUnflinchingSprite() { return GetModifierIconSprite(ref UnflinchingSprite, "unflinching"); } private static Sprite GetChameleonSprite() { return GetModifierIconSprite(ref ChameleonSprite, "chameleon"); } private static Sprite GetOmenSprite() { return GetModifierIconSprite(ref OmenSprite, "omen"); } private static Sprite GetReapingSprite() { return GetModifierIconSprite(ref ReapingSprite, "reaping"); } private static Sprite GetBlinkSprite() { return GetModifierIconSprite(ref BlinkSprite, "blink"); } private static Sprite GetKnockbackSprite() { return GetModifierIconSprite(ref KnockbackSprite, "juggernaut"); } private static Sprite GetBlamerSprite() { return GetModifierIconSprite(ref BlamerSprite, "blamer"); } private static Sprite GetModifierIconSprite(ref Sprite? cached, string key) { if (cached == null) { cached = CreateModifierIconSprite(key); } return cached; } private static Sprite CreateModifierIconSprite(string key) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0083: Unknown result type (might be due to invalid IL or missing references) ModifierIconSpec modifierIconSpec = ModifierIconSource.Get(key); Texture2D val = CreateIconTexture("CreatureManager_" + char.ToUpperInvariant(key[0]) + key.Substring(1) + "Icon"); Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); for (int i = 0; i < ((Texture)val).height; i++) { for (int j = 0; j < ((Texture)val).width; j++) { ModifierIconTone tone = modifierIconSpec.GetTone(j, i); Color val3 = ((tone == ModifierIconTone.Clear) ? val2 : ToUnityColor(modifierIconSpec.GetColor(tone))); val.SetPixel(j, i, val3); } } val.Apply(false, true); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), (float)((Texture)val).width); } private static Color ToUnityColor(ModifierIconColor color) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return Color32.op_Implicit(new Color32(color.Red, color.Green, color.Blue, byte.MaxValue)); } private static Sprite GetFallbackStarSprite() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)FallbackStarSprite != (Object)null) { return FallbackStarSprite; } Texture2D val = CreateIconTexture("CreatureManager_FallbackStarSprite"); Color val2 = default(Color); ((Color)(ref val2))..ctor(1f, 0.78f, 0.22f, 1f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0f, 0f, 0f, 0f); for (int i = 0; i < ((Texture)val).height; i++) { for (int j = 0; j < ((Texture)val).width; j++) { bool flag = IsFallbackStarPixel(j, i); val.SetPixel(j, i, flag ? val2 : val3); } } val.Apply(false, true); FallbackStarSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), (float)((Texture)val).width); return FallbackStarSprite; } private static Texture2D CreateIconTexture(string name) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown return new Texture2D(64, 64, (TextureFormat)4, false) { name = name, filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; } private static bool IsFivePointStarPixel(Vector2 point, Vector2 center, float outerRadius, float innerRadius) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_001b: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) bool flag = false; Vector2 val = GetFivePointStarVertex(center, outerRadius, innerRadius, 9); for (int i = 0; i < 10; i++) { Vector2 fivePointStarVertex = GetFivePointStarVertex(center, outerRadius, innerRadius, i); if (fivePointStarVertex.y > point.y != val.y > point.y && point.x < (val.x - fivePointStarVertex.x) * (point.y - fivePointStarVertex.y) / (val.y - fivePointStarVertex.y) + fivePointStarVertex.x) { flag = !flag; } val = fivePointStarVertex; } return flag; } private static bool IsFallbackStarPixel(int x, int y) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return IsFivePointStarPixel(new Vector2((float)x + 0.5f, (float)y + 0.5f), new Vector2(32f, 32f), 27f, 12f); } private static Vector2 GetFivePointStarVertex(Vector2 center, float outerRadius, float innerRadius, int index) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) float num = ((index % 2 == 0) ? outerRadius : innerRadius); float num2 = (float)Math.PI / 2f + (float)index * (float)Math.PI / 5f; return center + new Vector2(Mathf.Cos(num2), Mathf.Sin(num2)) * num; } } internal enum ModifierIconTone : byte { Clear, Tone1, Tone2, Tone3 } internal readonly struct ModifierIconColor { internal byte Red { get; } internal byte Green { get; } internal byte Blue { get; } internal ModifierIconColor(byte red, byte green, byte blue) { Red = red; Green = green; Blue = blue; } } internal sealed class ModifierIconSpec { private readonly byte[] _tones; private readonly ModifierIconColor[] _palette; internal string Key { get; } internal ModifierIconSpec(string key, string packedToneRuns, params ModifierIconColor[] palette) { int num = palette.Length; if ((num < 1 || num > 3) ? true : false) { throw new ArgumentOutOfRangeException("palette"); } Key = key; _palette = palette; _tones = UnpackTones(packedToneRuns); } internal ModifierIconTone GetTone(int x, int y) { if ((uint)x >= 64u || (uint)y >= 64u) { throw new ArgumentOutOfRangeException(); } return (ModifierIconTone)_tones[y * 64 + x]; } internal ModifierIconColor GetColor(ModifierIconTone tone) { int num = (int)(tone - 1); if ((uint)num >= (uint)_palette.Length) { throw new ArgumentOutOfRangeException("tone"); } return _palette[num]; } private static byte[] UnpackTones(string packedToneRuns) { byte[] array = Convert.FromBase64String(packedToneRuns); byte[] array2 = new byte[4096]; int num = 0; byte[] array3 = array; foreach (byte num2 in array3) { byte b = (byte)(num2 >> 6); int num3 = (num2 & 0x3F) + 1; if (b > 3 || num + num3 > array2.Length) { throw new InvalidOperationException("Invalid modifier icon tone data."); } for (int j = 0; j < num3; j++) { array2[num++] = b; } } if (num != array2.Length) { throw new InvalidOperationException("Incomplete modifier icon tone data."); } return array2; } } internal static class ModifierIconSource { internal const int Size = 64; private static readonly ModifierIconSpec[] Icons = new ModifierIconSpec[32] { Icon("enraged", P(C(byte.MaxValue, 59, 31)), "Pz8/B0E8QzpFDEAqRwpCKUgIRClIBkYpSARGK0gCRi1IAEYvTjFMM0s0SzRLMk0wTy5RLFMqVShGAU4mRgNOJkQFTiZCB04mQAlOMU4xTjFOMU4xTjFOMU4xTjFOMU4xTjFOMU4xTjFOMU0yTDNMM0s0SjVJNkk4RjxCPz8/Pz8/Pz8/Pz8/Pwo="), Icon("fire", P(C(byte.MaxValue, 51, 5), C(byte.MaxValue, 209, 20)), "Pz8/Px5FNEwwUStVKFclWiNcIU6BTR9NhEweTIdMHUuJTBtLi0wZTIxLGEyNSxhLj0oYS49LFkuQSxZLkEsWS5FKFUyRShVMkUsUTY9ME06PTBNOj0sUTo5MFE6OSxVPjUsWT4tMFlCKSxdQiUwXUYdMGFKFTRlRhUwaUoNMG1OBTRtiHWAeYB9eIVwiXCNaJFolWSZXKFYpVSpTLFItUC9OMU0zSjZHOEY6Qz1APz8/HQ=="), Icon("frost", P(C(89, 217, byte.MaxValue)), "Pz8/Pz8/Pz8/PxxFOUU5RTlFLUAKRQpAIEIJRQlCHkQIRQhEHEYHRQdGHEYGRQZGHkYFRQVGIEYERQRGIkYDRQNGJEYCRQJGJkYBRQFGKEYARQBGKlMsUS5PME0iaxNrE2sTaxNrE2siTTBPLlEsUypGAEUARihGAUUBRiZGAkUCRiRGA0UDRiJGBEUERiBGBUUFRh5GBkUGRhxGB0UHRhxECEUIRB5CCUUJQiBACkUKQC1FOUU5RTlFPz8/Pz8/Pz8/Pxw="), Icon("lightning", P(C(byte.MaxValue, 230, 20)), "Pz8/Pz8/PxdAP0A+QT1CPEI8QzxDO0Q6RDpFOUY5RjhHN0c3SDdINkk1STVKNEs0SzNMJVkmWSZZJVolWSZZJVolWiVaJUszSzRKNUk1STZIN0c3RzhGOUU5RjlFOkQ7QztDPEI9QT1BPkA/Pz8/Pz8/Gw=="), Icon("spirit", P(C(byte.MaxValue, 173, 13), C(217, 38, 20)), "Pz8/Pz8bRzdHN0c3RzdHN0c3RzdHN0c3RzdHN0c3RzdHN0c3RzdHN0csXSFMg0whS4VLIUuFSyFLhUshS4VLIUyDTCFdKksyTTBPL08uRgNGLUUFRS1FBUUsRQdFK0UHRStFB0UrRQdFK0UHRStFB0UrRQdFK0UHRSxFBUUtRQVFLUYDRi5PL08wTTJLNEk3RT8/Pz8/Pz8/Pxw="), Icon("armorPiercing", P(C(byte.MaxValue, 133, 20), C(122, 191, byte.MaxValue), C(51, 64, 140)), "Pz8/PwNBPUY5STVODIEhTwiFIE4HiB9NBosfSwSPHkoDkh1KApUbSwCXG0qZGkqaGUQBRJoYQwRCmxdCBYJAh8GRF0AFjMKRHYbAg8SQHIfBgsWDwIoch8KAx4DDihqI0YkZitGJGIrSiBiKyEHHiReJyEPEixeHyUXCjBaHyUePFYfISY4ViMZLjRWKxEyMFYvETIsVjMRMihWLxkyJFYrITIgUispMhxSLykyGFI7ITIYTkMdMhROQxoFMhBOPxoNMgxSOxYVMghSOxIdMgRSPwolMgBSQwItMFZ1MFJ5ME59ME59MEqBMF5tLIpFJL4UARzhFOkM8QT8/Pz8/CQ=="), Icon("staggering", P(C(byte.MaxValue, 140, 5), C(byte.MaxValue, 242, 71), C(byte.MaxValue, 219, 31)), "Pz8/Pz8/Pz8/Pz8/EUgyUilYJF0gYB1VgUsaSgpAgU0XSA6BAE4VRg+DAkwURRCDBUsRRhCDB0oQRRCFCEkPRRCFCkgORRCFC0gNRQ+HDEcMRg6HDUcMRQ6HDkcLRQ2JDkYMRQyJD0YLRgmNDkUMRgWTDEULRwGZCUUMRZ8GRgxBpQRFDUClBEUOQp8HRQ9EmQpFEEaTDEYRSI0PRRRIiRBGEcEBR4kOSBHBA0aHQQpKEcIFRIdWEsMGQodVE8MIQYVVE8UJhVMUxwiFUBTMB4MESBbRBIMk0QSDJswIgSrHCoErxQuBLMM7wzvCPcE9wT8/Pz8w"), Icon("undodgeable", P(C(byte.MaxValue, 245, 230), C(byte.MaxValue, 31, 20)), "Pz8/Pz8/PwdOEU4PThFOD04RTgxREVEJURFRCVERUQlFKUUJRSlFCUUpRQlFKUUJRSlFCUUpRQlFE4AURQlFEoITRQlFEYQSRQlFEIYRRQlFD4gQRQlFDooPRR2MMYYAhi+GAoYthgSGK4YBQwCGKYYBRQCGJ4YCRQGGJoYCRQGGJ4YBRQCGKYYBQwCGK4YEhi2GAoYvhgCGMYweRQ6KD0UJRQ+IEEUJRRCGEUUJRRGEEkUJRRKCE0UJRROAFEUJRSlFCUUpRQlFKUUJRSlFCUUpRQlFKUUJURFRCVERUQlREVEMThFOD04RTg9OEU4/Pz8/Pz8/Bw=="), Icon("armored", P(C(20, 71, 173)), "Pz8/Pz8eQTpHNE0uUyhZIl8eYR1hHWEcYhxjG2MbYxtjG2MbYxpkGmUZZRllGWUZZRllGGYYZxdnF2cXZxZpFGsSbBJtEG8QbRJrFWcYZRllGmMcYR1hHWEdYR1hHWEdYR1hHUwHTB1KC0ocSg1KGkoPShlJEUkYShFKF0kTSRtFE0QlQBNAPz8/FA=="), Icon("deathward", P(C(46, 10, 82), C(92, 31, 158), C(240, 217, byte.MaxValue)), "Pz8/Pz8/PwlrE2sTaxNrE2sTaxNrGKEdoR2hHaEdoR2hHaEdoR2hHYzHjB2Mx4wdjMeMHYzHjB2Mx4wdjMeMHYzHjB2Mx4wdjMeMHYzHjB2H0Ycdh9GHHYfRhx2H0Ycdh9GHHYfRhx2H0Ycdh9GHHYzHjB2Mx4wdjMeMHYzHjB2Mx4wdjMeMHovHix+fH58gnSGdIpskmSWZJpcpkyyRL400hz8/Pz8b"), Icon("regenerating", P(C(51, 242, 64)), "Pz8/Pz8/Pz8/GE0xTTFNMU0xTTFNMU0xTTFNMU0xTTFNMU0xTTFNMU0hbhBuEG4QbhBuEG4QbhBuEG4QbhBuEG4QbhBuIE0xTTFNMU0xTTFNMU0xTTFNMU0xTTFNMU0xTTFNMU0xTT8/Pz8/Pz8/GA=="), Icon("reflection", P(C(82, 173, byte.MaxValue), C(143, 214, 235), C(245, byte.MaxValue, byte.MaxValue)), "Pz8/Pz8FQARAOEMAQjhHN0g2SzNMM0oUgB5LEoIdTA+FHE0MiBxNCoobTgiMGkMBSQaOGUIDSQWPGUAFSQORIEkCkiBJAJMhSZQhSZMiSZIjRMBDhcCLI0TBQ4HBjCRDwkPCjCVCyY0lQcmNJkHHjiaAQMePJYDJjiSAy40jz4si0YoiRcWQIUfDkR9Jw5EeSoDBkh1KgcGSG0uCwZIaSpkYSwGYF0sCmBZKBZcUSwaXE0oIlxJKCpYQSwuWD0oOkxBKD48VSBGKGkUThh5EFYEjQj8/Pz8/Pz8/NA=="), Icon("vortex", P(C(92, 140, 173), C(148, 184, 209)), "Pz8/Pz8/EkU3hUUyiEQxikQvi0QMgx2NQwqFHYxECIZAHI1DCIZAHY1DBohAHotDBohAH4tCBohAIYlCBYlAI4dCBYhBJIZCBYhBJoRCBYhBJ4RABohBGUMKgkEFiEIWSQiBQQWIQhRMB4FABodDE04HgEAFiEITRIlBBoAGh0MSQ41ADIdDE0KQC4VFEkGQDIVFEkGODoNHE0GMEEkVQIwwQIwxQIsyiglBgCeJCUGBBIAhiAhCgQVAgguCEIYIQoIFQJQOggpChAVAlBpDhAVBlBlChQVBlBhDhQVCkxhDhQZCkhhChwVEjkAZQocGRItBGkKIBkeEQxtCiAdNHUKJCEkfQokLQiRCiDNCiTNBiTNCiDRBiDVBhzZBhjdBhTlAgz8/Pz8/IA=="), Icon("adaptive", P(C(178, byte.MaxValue, 71), C(235, byte.MaxValue, 148)), "Pz8/Pz8/PxpIMlAuUixUKlYpVihADEk3SAJBF0EZRwBCFUMaShREG0kSRhxIEUcbSQ9JGkoOShxIEEgdRxFHDoAPRRFHDIQPQxFHCogPQRFEAUAJiiFFCo4fRQqOH0UKjh9FCo4fRQqOH0UKjh9FCo4fRQqOCkIRRQqOCkUPRAyKDEQQRA2IDUQQRQ6EDkUQRRCAEEURRSBFEkUgRRNFHkUURhxGFUYaRhZHGEcXRxZHGUUOQQVHG0MNQwNIHUENRAFJH0ALUSxRK1ErUS9NM0c5RTtDPUE/Pz8/Pxs="), Icon("unflinching", P(C(byte.MaxValue, 158, 28), C(235, 209, 140)), "Pz8/Pz8/Pz8/Pz8/PwtACUA0QQVBkCRCA0KTIUmWHkmYHEmEBo4YgkeAEIsVgkkTiROCSxSIEYFPE4gPgVEUhw2HRxqHDIYCQx2GDIYCQx2GEYAEQR+AHEE9QT8/Pz8/PzeAJoARhiSGDIYkhgyHGUAHQIYNhxhCA0KFD4gWQwFDhBGIFkeEE4kUR4MVixCAR4IYjgaESRyXSx2UTR+QTyGQAkM7QzxBPUE9QT8/Pz8/Pz8/Pz8R"), Icon("chameleon", P(C(26, 199, 158), C(209, byte.MaxValue, 46)), "Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8WQTtDNYdCEUEeikERQxuNQA9FGY5ADkUaj0AMRhmGA4YMRRqEBoVACkUbgwdAhEMGRRyDBkGFTxOEDkGFTgFFDIQOQYVYCoQOQYVZCYUNQYVaCYUMQYVaCYULQYVcCIYKQIZcCYcIhl0KiASIVoFECpUFUINDC5MLSoVCDZARRYVBD40TRoNCEYkWRoFCNEk3RT8/Pz8/Pz8/Pz8/Pz8/Pz8/Cw=="), Icon("exposed", P(C(107, 209, 250), C(184, 245, byte.MaxValue)), "Pz8/Pz8/Pz8bQD1COkQHgDBGBoEtSQaBK0oEhClLBIUnSwaFJUsHhiNLB4ghTAeJH0wHix5LB40cTAeNHEsHjxtLBpAaTQSRGk0EkhlMBpEYTQeQGE4HkBdPB48WUAePFlEHjxVSB44VUwaOFVQEjxVTBY8VUgeOFVEIjhVRB48VUAeQFU8HkRVOB5IVTQiSFU0HkxVOBZQVTgSVFU0GlBVNB5MVTgaTFU4HkhVPB5EVUAaRFVAHkD8/Pz8/Pz8/Pz8/Cg=="), Icon("weakened", P(C(245, 173, 61), C(209, 214, 217), C(82, 28, 38)), "Pz8/Pz8/Pz8/Px1BPEM7QjtDgDpCggOAM0KEAYIxQ4kxQosvQ4wuQo0uQowuQ4svQowvQosvQosEQCpCiwVAKEKLB0AnQooIgEAmQooIgUEjQooKgUEiQooKgUIhQYoLgkIfQokNgkIeQokMhEIRwQlBiQyFQhLFA0AAiQuIQhLGBIgKikETxwWECotCFMcGgAyLQRbHE4tBF8YTikEYxhKLQBfIEotAFcoSikAVyxKKQBPNEooSygDDEolAEMoDwhSGEMoFwRaFQA3KB8EYgg7JCcAbgA7IM0PGMkfDM0cAwTNJNUk1STVJNkc3RzlDPz8y"), Icon("withered", P(C(168, 199, 56)), "Pz8/Pz8/PxxAOkQ6RBFAJ0QPQidEDkQmRQtGJ0QJSSZEB0smRAVMJ0UCTCpEAUssRAFJLkQCRjBFAUQyRQJCNEQCQDZEOkQ6RTpEOkU5RjhINko1SjRMMk0wUCxCAFApQwBRJkUAUyNGAFUfSAFVHUgCVRxJAlMcSwJSHEwCUBxNBE0dTgRLHVAESR5RBEgfUAZFJE0GRCdLBkQpSQZFK0YHRC1DCEQwQAhEOkQ6QD8/Pz8/Pz8b"), Icon("crippling", P(C(199, 115, byte.MaxValue)), "Pz8/Pz8/PxJdIV0hXSFdIV0hXTNGNkguQQNLLFIsUC1PLU8tTi5PL1AvUS1GAkgtQgZILEAJRjlEKkoFSyJICEohSAxIIEcORx9HEEceRxBHHkYSRh5GEkYeRhJGHkYSRh5GEkYeRhJGHkYSRh5GEkYeRxBHHkcQRx9HDkcgSAxIIUgKSCJKBkojWiVYJ1YpVCxQMEw0SD8/Pz8/Pz8/Pxs="), Icon("disruptive", P(C(36, byte.MaxValue, 219)), "Pz8/Pz8/Pz8/Pz8/Pz8/GkQWQCBHFEEfSRJCHksQQx1FAkQORA9ADEMGQwxEEEEKQwhDCkQRQghDCkMIRBJDBUQMQwZEE0UBRQ5EAkUVSxBLF0kSSRlHFEccRBZEHkQWRBxHFEcZSRJJF0sQSxVFAUUORAJFFEMFRAxDBkQTQghDCkMIRBJBCkMIQwpEEUAMQwZDDEQeRQJEDkQeSxBDH0kSQiBHFEEiRBZAPz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Bw=="), Icon("adrenalineDrain", P(C(byte.MaxValue, 89, 107)), "Pz8/Pz8/Pz8/Pz8vQTxDOkU4RzdHNkk0SzJNME8vTy5RLFMqVShXMEU5RStBC0UnRQtFJ0YKRSdGCkUmSAlFJkkIRSZJCEUlSwdFJUQARQdFJUQBRQZFEUsGRQJREUsGRANREUsAQQNEBFARTgJFBEEATRFPAUQITRFPAUQITRxLEEUdSRFFHUkRRR5IEUUfRhJFH0YSRSBFEkUgQRZFOUU/Pz8/Pz8/Pz8/Pz8L"), Icon("corrosive", P(C(184, 199, 207), C(242, 82, 15), C(87, 110, 122)), "Pz8/Pz8/Pz8/PxFNMFiCI1aEIlaFIVaGIVaGIFaHIFaHwB5Xg8OEGk0HQYPDhBlMC4PDhRdLD4HDhRdKEYoWShOKFUkViRRKFYoTSReEQIMSShdFgRNKGUQVShlEFEsZQxVLGUMWShlDFkoZQxZKGUMXSRmCQBdKF4UWSheFF0oVg8OAFUoVg8OBFEsThMOBFUsRhcOAFkwPihdOC4wYTweNGVeFw4MZV4XDHleEwh9XhMIfWIYgWIUgWYQgW4IhXD8/Pz8/Pz8/Pz8U"), Icon("toxicDeath", P(C(115, byte.MaxValue, 46)), "Pz8/Pz8/Pz8/PxRVKVUpVSlVKVUpVSlVKVUpVShWJ1glWiNcIU4ATh9OAk4dTgROHE0GTRtNCE0aTQhNGk4GThlQBFAYUQJRGFIAUhhmGGYYSQNKA0kYSAVIBUgYRwdGB0cYRwdGB0cYRwdGB0cYRwdGB0cYSAVIBUgZSANKA0gaZBpkG2IcYh1gH14hXCNaJVgnVipSLk4zSD8/Pz8/Pz8/Gw=="), Icon("swift", P(C(64, 242, 140)), "Pz8/Pz8/Pz8/Pz8/DEAQQCtCDkIpRAxEJ0cJRyVICEgjSgZKIU0DTR5PAU8eTwFPHk8BTx5QAFAeTwFPHk8BTx5QAFAeTwFPHk8BTx5PAU8eTQNNIUoGSiNICEgkSAhII0oGSiBNA00eTwFPHE8BTxxPAU8bUABQG08BTxxPAU8bUABQG08BTxxPAU8cTwFPHk0DTSBKBkojSAhIJEcJRyZEDEQpQg5CK0AQQD8/Pz8/Pz8/Pz8/Px8="), Icon("attackSpeed", P(C(byte.MaxValue, 158, 20)), "Pz8/Pz8/Pz8/EVsjWyNbI1sjWyNbJEUNRSRGDUYiSAtIIkgJSCRHCEglSAdHJ0gFSChHBEgqRwJIK0gBRy1HAEguTy9OMUwzSzNKNEo0SjRKNEsyTDFOME8uRwBILEgBRyxHAkgqRwRIKEgFSCZIB0cmRwhIJEgJSCJIC0giRg1GJEUNRSRbI1sjWyNbI1sjWz8/Pz8/Pz8/PxE="), Icon("vampiric", P(C(250, 8, 18), C(245, 247, byte.MaxValue), C(235, 240, byte.MaxValue)), "Pz8/Pz8eQTxDOkU4RzdHN0c2STVJK4AISQuAHoAISQuBHIEJRwuCHIIIRwuCHIIJRQyCG4MJRAyDG4MKQwyEGYQLQQyFGYQLQQyFGIUahRiGGIYYhhiHFocYhxaHGIcWhwTDBcMEiBWIA8UDxQOIFYgDxQPFA4kUiQHHAccCiROKAccBxwKJE4oBxwHHAYoTigHHAccBihKLAccBxwGLEYsBxwHHAYsRiwHHAccBixGLAccBxwGLEYsBxwHHAYsQjAHHAccBjA+NAMcBxwCND40AxwHHAI0PjQDHAccAjQ+NAMcBxwCND40AxwHHAI0PjQDHAccAjQ6OAMcBxwCODY0BxwHHAY0NjALHAccCjD8/Pz8/Pz8/Pz8/Pz8/PwY="), Icon("reaping", P(C(122, 71, 33), C(92, 20, 41), C(209, 224, 237)), "Pz8/Pz8/LUEhmkQgmEYfl0YhlUYilEYkkkYlkUYmkEYojkYpjkUrjEUsi0UuiUWALohFgS2IRYIth0WELIZFhSuGRYcqhUWIKoRGhyqERogqg0aILIFGiS1GiixGiixGiytGjCpGjSpFjyhFhgGIJ0SFBYclRYQHhiRFhAmFJEQAhAmEJEUAhAmEI0UBhAmEI0QChAmDI0UDhAeEIsFDBIUFhSHDQQWHAYYhxEEGjyDGCI0hxgmLI8YJiSTHCYcmyAiFEcISywaDC8cVzQSBB8sW5hnjG+Ee3yHbKNEyxT8/Hw=="), Icon("blink", P(C(148, 92, byte.MaxValue), C(71, 224, byte.MaxValue)), "Pz8/Pz8/Pz8/GkkyTy1TKVclWyJdIEsHSx5JDUkcSBFIG0cTSBpFF0YaRBlGGUQZRhlDC4AORhhCDIEORRhCDIINRhdBDYQMRRdBDYULRRdBDYYKRhCFQJYKRRCFQJgIRRCFQJkHRRCFQJoGRRCFQJoGRRCFQJkHRRCFQJgIRRCFQJYKRRZBDYYKRhZBDYULRRdBDYQMRRdCDIINRhdCDIEORRhDC4AORhhEGUYZRBlGGUUXRhpHE0gaSBFIHEkNSR5LB0sgXSJbJVcpUy1PMkk/Pz8/Pz8/Pz8a"), Icon("omen", P(C(199, 125, byte.MaxValue), C(byte.MaxValue, 48, 40)), "Pz8/Pz8/Pz8/Pz8/Px5APUI6RjdINEwxTi5SK1QoSgJKJUkGSSJJAoQCSR9IAogCSBxIAowCSBlIA4wDSBZIBI4ESBNHBo4GRxBHB5AHRw1GCZAJRgpHCpAKRwpGCZAJRg1HB5AHRxBHBo4GRxNIBI4ESBZIA4wDSBlIAowCSBxIAogCSB9JAoQCSSJJBkklSgJKKFQrUi5OMUw0SDdGOkI9QD8/Pz8/Pz8/Pz8/Pz8/Hw=="), Icon("juggernaut", P(C(209, 184, 122)), "Pz8ZQABFLkADRABLJ1cBQCNYAEMgXx5iG2QZZxZQBlEUTQ9ME0oVSQFAEEYbRwBCD0IhSzJMMUQASC9PLlEsUypKAEgpSgJIJ0oDSCZKBUclSgZFAUAjSghHFEAMSglHE0IKSgpHEkQISgtIEEYGSg1HD0gESg5HDkoCSg9HD0oAShBHEFQRRxFSEkgRUBRHEk4VRxNMFkYVShdFFUwWRxJOFEgLVhNHClkSRwhcEUkFUgBKD0kGUQJKDkgGRQVFBEoORgdEB0QFSA9GBkQJRAVGD0YHRAlEBkQQRQhECUQHQhFECUQJRAhAEkMKRAlEG0MLRAlEG0INRAdEHEINRQVFHEEPTx1AEE8cQBJNM0k3RT8/Py8="), Icon("blamer", P(C(87, 56, 18), C(199, 133, 38), C(byte.MaxValue, 199, 56)), "Pz8/Pz8/Px5BO0U4RzdHN0c3RzdHKmEdYR1hHIBhgBujFcAFoQXADsEFoQXBDcEFoQXBDMIEwACfAMAEwgrDBMABnQHABMMJwwPBAZ0BwQPDCMQCwgKbAsICxAfEAsICmwLCAsQHwwLDA5kDwwLDBsQCwgSZBMICxAXEAcMEmQTDAcQFwwLCBpcGwgLDBcMCwgaXBsICwwTEAcMGlwbDAcQDxAHDBpcGwwHEA8QBwwaXBsMBxAPEAcMGlwbDAcQDxAHDBpcGwwHEA8QBwwaXBsMBxAPEAsIGlwbCAsQEwwLCBpcGwgLDBcMCwwWXBcMCwwXEAcMFlwXDAcQFxALDBJcEwwLEBsQBwwSXBMMBxAfEAsIFlQXCAsQIwwLCBZUFwgLDCcMDwQWVBcEDwwrCBMAGkwbABMILwg2RDcIMwQ6PDsEOwA+ND8AjhzmDO4M7gzuDO4M/Pz8/Pz8/HQ==") }; private static readonly Dictionary IconsByKey = BuildIconsByKey(); internal static IReadOnlyList All => Icons; internal static ModifierIconSpec Get(string key) { if (!IconsByKey.TryGetValue(key, out ModifierIconSpec value)) { throw new ArgumentOutOfRangeException("key", key, "Unknown modifier icon."); } return value; } private static ModifierIconSpec Icon(string key, ModifierIconColor[] palette, string packedToneRuns) { return new ModifierIconSpec(key, packedToneRuns, palette); } private static ModifierIconColor[] P(params ModifierIconColor[] palette) { return palette; } private static ModifierIconColor C(byte red, byte green, byte blue) { return new ModifierIconColor(red, green, blue); } private static Dictionary BuildIconsByKey() { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); ModifierIconSpec[] icons = Icons; foreach (ModifierIconSpec modifierIconSpec in icons) { dictionary.Add(modifierIconSpec.Key, modifierIconSpec); } return dictionary; } } [Flags] internal enum CreaturePrefabBaselineGroup : ulong { None = 0uL, AttackDamage = 1uL, AttackTuple = 2uL, AttackStatusEffect = 4uL, AttackProjectile = 8uL, AttackAi = 0x10uL, CharacterIdentity = 0x20uL, CharacterBoss = 0x40uL, CharacterGlobalKey = 0x80uL, CharacterHealth = 0x100uL, CharacterDamageModifiers = 0x200uL, CharacterSpeed = 0x400uL, CharacterJump = 0x800uL, CharacterSwim = 0x1000uL, CharacterFlight = 0x2000uL, BaseAiSenses = 0x4000uL, BaseAiIdleSound = 0x8000uL, BaseAiMovement = 0x10000uL, BaseAiSerpent = 0x20000uL, BaseAiRandomMove = 0x40000uL, BaseAiFlight = 0x80000uL, BaseAiAvoid = 0x100000uL, BaseAiFlee = 0x200000uL, BaseAiAggressive = 0x400000uL, BaseAiMessages = 0x800000uL, MonsterAiAlertRange = 0x1000000uL, MonsterAiHunt = 0x2000000uL, MonsterAiChase = 0x4000000uL, MonsterAiCircle = 0x8000000uL, MonsterAiHurtFlee = 0x10000000uL, MonsterAiCharge = 0x20000000uL, MonsterAiSleep = 0x40000000uL, MonsterAiAvoidLand = 0x80000000uL, HumanoidDefaultItems = 0x100000000uL, HumanoidRandomWeapon = 0x200000000uL, HumanoidRandomArmor = 0x400000000uL, HumanoidRandomShield = 0x800000000uL, HumanoidRandomItems = 0x1000000000uL, HumanoidRandomSets = 0x2000000000uL, VisualScale = 0x4000000000uL, RagdollReferences = 0x8000000000uL, Appearance = 0x10000000000uL, ProjectileSpawnOnHit = 0x20000000000uL, SpawnAbilitySpawnPrefabs = 0x40000000000uL, AttackAll = 0x1FuL, CharacterAll = 0x3FE0uL, BaseAiAll = 0xFFC000uL, MonsterAiAll = 0xFF000000uL, HumanoidAll = 0x3F00000000uL, VisualAll = 0x1C000000000uL, ProjectileAll = 0x60000000000uL, All = 0x7FFFFFFFFFFuL } internal static class CreaturePrefabBaseline { private sealed class Entry { internal readonly GameObject Prefab; internal readonly AttackState Attack = new AttackState(); internal readonly CharacterState Character = new CharacterState(); internal readonly BaseAiState BaseAi = new BaseAiState(); internal readonly MonsterAiState MonsterAi = new MonsterAiState(); internal readonly HumanoidState Humanoid = new HumanoidState(); internal readonly ProjectileState Projectile = new ProjectileState(); internal readonly RagdollState Ragdoll = new RagdollState(); internal readonly AppearanceState Appearance = new AppearanceState(); internal CreaturePrefabBaselineGroup CapturedGroups; internal CreaturePrefabBaselineGroup AppliedGroups; internal Vector3 Scale; internal Entry(GameObject prefab) { Prefab = prefab; } } private sealed class AttackState { private DamageTypes _damages; private float _attackForce; private int _toolTier; private AttackType _attackType; private string _attackAnimation = ""; private StatusEffect? _statusEffect; private float _statusEffectChance; private GameObject? _projectile; private float _projectileVelocity; private float _projectileAccuracy; private int _projectileCount; private float _aiInterval; private float _aiMinRange; private float _aiRange; private float _aiMaxAngle; private bool _attackReferenceCaptured; private bool _attackWasOriginallyNull; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) SharedData val = prefab.GetComponent()?.m_itemData?.m_shared; if (val == null) { return false; } if (!_attackReferenceCaptured) { _attackWasOriginallyNull = val.m_attack == null; _attackReferenceCaptured = true; } switch (group) { case CreaturePrefabBaselineGroup.AttackDamage: _damages = val.m_damages; _attackForce = val.m_attackForce; _toolTier = val.m_toolTier; return true; case CreaturePrefabBaselineGroup.AttackStatusEffect: _statusEffect = val.m_attackStatusEffect; _statusEffectChance = val.m_attackStatusEffectChance; return true; case CreaturePrefabBaselineGroup.AttackAi: _aiInterval = val.m_aiAttackInterval; _aiMinRange = val.m_aiAttackRangeMin; _aiRange = val.m_aiAttackRange; _aiMaxAngle = val.m_aiAttackMaxAngle; return true; default: { Attack attack = val.m_attack; if (attack == null) { return true; } switch (group) { case CreaturePrefabBaselineGroup.AttackTuple: _attackType = attack.m_attackType; _attackAnimation = attack.m_attackAnimation; return true; case CreaturePrefabBaselineGroup.AttackProjectile: _projectile = attack.m_attackProjectile; _projectileVelocity = attack.m_projectileVel; _projectileAccuracy = attack.m_projectileAccuracy; _projectileCount = attack.m_projectiles; return true; default: return false; } } } } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { //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_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) SharedData val = prefab.GetComponent()?.m_itemData?.m_shared; if (val == null) { return; } if (_attackReferenceCaptured && _attackWasOriginallyNull) { val.m_attack = null; } switch (group) { case CreaturePrefabBaselineGroup.AttackDamage: val.m_damages = _damages; val.m_attackForce = _attackForce; val.m_toolTier = _toolTier; return; case CreaturePrefabBaselineGroup.AttackStatusEffect: val.m_attackStatusEffect = _statusEffect; val.m_attackStatusEffectChance = _statusEffectChance; return; case CreaturePrefabBaselineGroup.AttackAi: val.m_aiAttackInterval = _aiInterval; val.m_aiAttackRangeMin = _aiMinRange; val.m_aiAttackRange = _aiRange; val.m_aiAttackMaxAngle = _aiMaxAngle; return; } if (val.m_attack != null) { switch (group) { case CreaturePrefabBaselineGroup.AttackTuple: val.m_attack.m_attackType = _attackType; val.m_attack.m_attackAnimation = _attackAnimation; break; case CreaturePrefabBaselineGroup.AttackProjectile: val.m_attack.m_attackProjectile = _projectile; val.m_attack.m_projectileVel = _projectileVelocity; val.m_attack.m_projectileAccuracy = _projectileAccuracy; val.m_attack.m_projectiles = _projectileCount; break; } } } } private sealed class ProjectileState { private GameObject? _spawnOnHit; private GameObject[]? _spawnPrefabs; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { switch (group) { case CreaturePrefabBaselineGroup.ProjectileSpawnOnHit: { Projectile component2 = prefab.GetComponent(); if ((Object)(object)component2 == (Object)null) { return false; } _spawnOnHit = component2.m_spawnOnHit; return true; } case CreaturePrefabBaselineGroup.SpawnAbilitySpawnPrefabs: { SpawnAbility component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } _spawnPrefabs = ((component.m_spawnPrefab == null) ? null : ((GameObject[])component.m_spawnPrefab.Clone())); return true; } default: return false; } } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { switch (group) { case CreaturePrefabBaselineGroup.ProjectileSpawnOnHit: { Projectile component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.m_spawnOnHit = _spawnOnHit; } break; } case CreaturePrefabBaselineGroup.SpawnAbilitySpawnPrefabs: { SpawnAbility component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_spawnPrefab = ((_spawnPrefabs == null) ? null : ((GameObject[])_spawnPrefabs.Clone())); } break; } } } } private sealed class CharacterState { private string _name = ""; private Faction _faction; private bool _boss; private bool _dontHideBossHud; private string _bossEvent = ""; private string _globalKey = ""; private float _health; private float _regenAllHpTime; private DamageModifiers _damageModifiers; private readonly float[] _speed = new float[7]; private readonly float[] _jump = new float[5]; private bool _canSwim; private readonly float[] _swim = new float[4]; private bool _flying; private readonly float[] _flight = new float[3]; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) Character component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } switch (group) { case CreaturePrefabBaselineGroup.CharacterIdentity: _name = component.m_name; _faction = component.m_faction; break; case CreaturePrefabBaselineGroup.CharacterBoss: _boss = component.m_boss; _dontHideBossHud = component.m_dontHideBossHud; _bossEvent = component.m_bossEvent; break; case CreaturePrefabBaselineGroup.CharacterGlobalKey: _globalKey = component.m_defeatSetGlobalKey; break; case CreaturePrefabBaselineGroup.CharacterHealth: _health = component.m_health; _regenAllHpTime = component.m_regenAllHPTime; break; case CreaturePrefabBaselineGroup.CharacterDamageModifiers: _damageModifiers = component.m_damageModifiers; break; case CreaturePrefabBaselineGroup.CharacterSpeed: _speed[0] = component.m_crouchSpeed; _speed[1] = component.m_walkSpeed; _speed[2] = component.m_speed; _speed[3] = component.m_turnSpeed; _speed[4] = component.m_runSpeed; _speed[5] = component.m_runTurnSpeed; _speed[6] = component.m_acceleration; break; case CreaturePrefabBaselineGroup.CharacterJump: _jump[0] = component.m_jumpForce; _jump[1] = component.m_jumpForceForward; _jump[2] = component.m_jumpForceTiredFactor; _jump[3] = component.m_airControl; _jump[4] = component.m_jumpStaminaUsage; break; case CreaturePrefabBaselineGroup.CharacterSwim: _canSwim = component.m_canSwim; _swim[0] = component.m_swimDepth; _swim[1] = component.m_swimSpeed; _swim[2] = component.m_swimTurnSpeed; _swim[3] = component.m_swimAcceleration; break; case CreaturePrefabBaselineGroup.CharacterFlight: _flying = component.m_flying; _flight[0] = component.m_flySlowSpeed; _flight[1] = component.m_flyFastSpeed; _flight[2] = component.m_flyTurnSpeed; break; default: return false; } return true; } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { //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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) Character component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { switch (group) { case CreaturePrefabBaselineGroup.CharacterIdentity: component.m_name = _name; component.m_faction = _faction; break; case CreaturePrefabBaselineGroup.CharacterBoss: component.m_boss = _boss; component.m_dontHideBossHud = _dontHideBossHud; component.m_bossEvent = _bossEvent; break; case CreaturePrefabBaselineGroup.CharacterGlobalKey: component.m_defeatSetGlobalKey = _globalKey; break; case CreaturePrefabBaselineGroup.CharacterHealth: component.m_health = _health; component.m_regenAllHPTime = _regenAllHpTime; break; case CreaturePrefabBaselineGroup.CharacterDamageModifiers: component.m_damageModifiers = _damageModifiers; break; case CreaturePrefabBaselineGroup.CharacterSpeed: component.m_crouchSpeed = _speed[0]; component.m_walkSpeed = _speed[1]; component.m_speed = _speed[2]; component.m_turnSpeed = _speed[3]; component.m_runSpeed = _speed[4]; component.m_runTurnSpeed = _speed[5]; component.m_acceleration = _speed[6]; break; case CreaturePrefabBaselineGroup.CharacterJump: component.m_jumpForce = _jump[0]; component.m_jumpForceForward = _jump[1]; component.m_jumpForceTiredFactor = _jump[2]; component.m_airControl = _jump[3]; component.m_jumpStaminaUsage = _jump[4]; break; case CreaturePrefabBaselineGroup.CharacterSwim: component.m_canSwim = _canSwim; component.m_swimDepth = _swim[0]; component.m_swimSpeed = _swim[1]; component.m_swimTurnSpeed = _swim[2]; component.m_swimAcceleration = _swim[3]; break; case CreaturePrefabBaselineGroup.CharacterFlight: component.m_flying = _flying; component.m_flySlowSpeed = _flight[0]; component.m_flyFastSpeed = _flight[1]; component.m_flyTurnSpeed = _flight[2]; break; } } } } private sealed class BaseAiState { private float _viewRange; private float _viewAngle; private float _hearRange; private bool _mistVision; private float _idleSoundInterval; private float _idleSoundChance; private bool _patrol; private AgentType _pathAgentType; private float _moveMinAngle; private bool _smoothMovement; private bool _serpentMovement; private float _serpentTurnRadius; private readonly float[] _randomMove = new float[4]; private bool _randomFly; private readonly float[] _flight = new float[9]; private readonly bool[] _avoid = new bool[6]; private readonly float[] _flee = new float[3]; private bool _aggravatable; private bool _passiveAggressive; private readonly string[] _messages = new string[3]; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) BaseAI component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } switch (group) { case CreaturePrefabBaselineGroup.BaseAiSenses: _viewRange = component.m_viewRange; _viewAngle = component.m_viewAngle; _hearRange = component.m_hearRange; _mistVision = component.m_mistVision; break; case CreaturePrefabBaselineGroup.BaseAiIdleSound: _idleSoundInterval = component.m_idleSoundInterval; _idleSoundChance = component.m_idleSoundChance; break; case CreaturePrefabBaselineGroup.BaseAiMovement: _patrol = component.m_patrol; _pathAgentType = component.m_pathAgentType; _moveMinAngle = component.m_moveMinAngle; _smoothMovement = component.m_smoothMovement; break; case CreaturePrefabBaselineGroup.BaseAiSerpent: _serpentMovement = component.m_serpentMovement; _serpentTurnRadius = component.m_serpentTurnRadius; break; case CreaturePrefabBaselineGroup.BaseAiRandomMove: _randomMove[0] = component.m_jumpInterval; _randomMove[1] = component.m_randomCircleInterval; _randomMove[2] = component.m_randomMoveInterval; _randomMove[3] = component.m_randomMoveRange; break; case CreaturePrefabBaselineGroup.BaseAiFlight: _randomFly = component.m_randomFly; _flight[0] = component.m_chanceToTakeoff; _flight[1] = component.m_chanceToLand; _flight[2] = component.m_groundDuration; _flight[3] = component.m_airDuration; _flight[4] = component.m_maxLandAltitude; _flight[5] = component.m_takeoffTime; _flight[6] = component.m_flyAltitudeMin; _flight[7] = component.m_flyAltitudeMax; _flight[8] = component.m_flyAbsMinAltitude; break; case CreaturePrefabBaselineGroup.BaseAiAvoid: _avoid[0] = component.m_avoidFire; _avoid[1] = component.m_afraidOfFire; _avoid[2] = component.m_avoidWater; _avoid[3] = component.m_avoidLava; _avoid[4] = component.m_skipLavaTargets; _avoid[5] = component.m_avoidLavaFlee; break; case CreaturePrefabBaselineGroup.BaseAiFlee: _flee[0] = component.m_fleeRange; _flee[1] = component.m_fleeAngle; _flee[2] = component.m_fleeInterval; break; case CreaturePrefabBaselineGroup.BaseAiAggressive: _aggravatable = component.m_aggravatable; _passiveAggressive = component.m_passiveAggresive; break; case CreaturePrefabBaselineGroup.BaseAiMessages: _messages[0] = component.m_spawnMessage; _messages[1] = component.m_deathMessage; _messages[2] = component.m_alertedMessage; break; default: return false; } return true; } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { //IL_00fd: 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) BaseAI component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { switch (group) { case CreaturePrefabBaselineGroup.BaseAiSenses: component.m_viewRange = _viewRange; component.m_viewAngle = _viewAngle; component.m_hearRange = _hearRange; component.m_mistVision = _mistVision; break; case CreaturePrefabBaselineGroup.BaseAiIdleSound: component.m_idleSoundInterval = _idleSoundInterval; component.m_idleSoundChance = _idleSoundChance; break; case CreaturePrefabBaselineGroup.BaseAiMovement: component.m_patrol = _patrol; component.m_pathAgentType = _pathAgentType; component.m_moveMinAngle = _moveMinAngle; component.m_smoothMovement = _smoothMovement; break; case CreaturePrefabBaselineGroup.BaseAiSerpent: component.m_serpentMovement = _serpentMovement; component.m_serpentTurnRadius = _serpentTurnRadius; break; case CreaturePrefabBaselineGroup.BaseAiRandomMove: component.m_jumpInterval = _randomMove[0]; component.m_randomCircleInterval = _randomMove[1]; component.m_randomMoveInterval = _randomMove[2]; component.m_randomMoveRange = _randomMove[3]; break; case CreaturePrefabBaselineGroup.BaseAiFlight: component.m_randomFly = _randomFly; component.m_chanceToTakeoff = _flight[0]; component.m_chanceToLand = _flight[1]; component.m_groundDuration = _flight[2]; component.m_airDuration = _flight[3]; component.m_maxLandAltitude = _flight[4]; component.m_takeoffTime = _flight[5]; component.m_flyAltitudeMin = _flight[6]; component.m_flyAltitudeMax = _flight[7]; component.m_flyAbsMinAltitude = _flight[8]; break; case CreaturePrefabBaselineGroup.BaseAiAvoid: component.m_avoidFire = _avoid[0]; component.m_afraidOfFire = _avoid[1]; component.m_avoidWater = _avoid[2]; component.m_avoidLava = _avoid[3]; component.m_skipLavaTargets = _avoid[4]; component.m_avoidLavaFlee = _avoid[5]; break; case CreaturePrefabBaselineGroup.BaseAiFlee: component.m_fleeRange = _flee[0]; component.m_fleeAngle = _flee[1]; component.m_fleeInterval = _flee[2]; break; case CreaturePrefabBaselineGroup.BaseAiAggressive: component.m_aggravatable = _aggravatable; component.m_passiveAggresive = _passiveAggressive; break; case CreaturePrefabBaselineGroup.BaseAiMessages: component.m_spawnMessage = _messages[0]; component.m_deathMessage = _messages[1]; component.m_alertedMessage = _messages[2]; break; } } } } private sealed class MonsterAiState { private float _alertRange; private bool _enableHuntPlayer; private bool _attackPlayerObjects; private int _privateAreaTriggerThreshold; private readonly float[] _chase = new float[4]; private readonly float[] _circle = new float[3]; private bool _fleeIfHurt; private float _fleeUnreachableSinceAttacking; private float _fleeUnreachableSinceHurt; private bool _fleeIfNotAlerted; private float _fleeIfLowHealth; private float _fleeTimeSinceHurt; private bool _fleeInLava; private bool _circulateWhileCharging; private bool _circulateWhileChargingFlying; private bool _sleeping; private float _wakeupRange; private bool _noiseWakeup; private float _maxNoiseWakeupRange; private float _wakeUpDelayMin; private float _wakeUpDelayMax; private float _fallAsleepDistance; private bool _avoidLand; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { MonsterAI component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } switch (group) { case CreaturePrefabBaselineGroup.MonsterAiAlertRange: _alertRange = component.m_alertRange; break; case CreaturePrefabBaselineGroup.MonsterAiHunt: _enableHuntPlayer = component.m_enableHuntPlayer; _attackPlayerObjects = component.m_attackPlayerObjects; _privateAreaTriggerThreshold = component.m_privateAreaTriggerTreshold; break; case CreaturePrefabBaselineGroup.MonsterAiChase: _chase[0] = component.m_interceptTimeMin; _chase[1] = component.m_interceptTimeMax; _chase[2] = component.m_maxChaseDistance; _chase[3] = component.m_minAttackInterval; break; case CreaturePrefabBaselineGroup.MonsterAiCircle: _circle[0] = component.m_circleTargetInterval; _circle[1] = component.m_circleTargetDuration; _circle[2] = component.m_circleTargetDistance; break; case CreaturePrefabBaselineGroup.MonsterAiHurtFlee: _fleeIfHurt = component.m_fleeIfHurtWhenTargetCantBeReached; _fleeUnreachableSinceAttacking = component.m_fleeUnreachableSinceAttacking; _fleeUnreachableSinceHurt = component.m_fleeUnreachableSinceHurt; _fleeIfNotAlerted = component.m_fleeIfNotAlerted; _fleeIfLowHealth = component.m_fleeIfLowHealth; _fleeTimeSinceHurt = component.m_fleeTimeSinceHurt; _fleeInLava = component.m_fleeInLava; break; case CreaturePrefabBaselineGroup.MonsterAiCharge: _circulateWhileCharging = component.m_circulateWhileCharging; _circulateWhileChargingFlying = component.m_circulateWhileChargingFlying; break; case CreaturePrefabBaselineGroup.MonsterAiSleep: _sleeping = component.m_sleeping; _wakeupRange = component.m_wakeupRange; _noiseWakeup = component.m_noiseWakeup; _maxNoiseWakeupRange = component.m_maxNoiseWakeupRange; _wakeUpDelayMin = component.m_wakeUpDelayMin; _wakeUpDelayMax = component.m_wakeUpDelayMax; _fallAsleepDistance = component.m_fallAsleepDistance; break; case CreaturePrefabBaselineGroup.MonsterAiAvoidLand: _avoidLand = component.m_avoidLand; break; default: return false; } return true; } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { MonsterAI component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { switch (group) { case CreaturePrefabBaselineGroup.MonsterAiAlertRange: component.m_alertRange = _alertRange; break; case CreaturePrefabBaselineGroup.MonsterAiHunt: component.m_enableHuntPlayer = _enableHuntPlayer; component.m_attackPlayerObjects = _attackPlayerObjects; component.m_privateAreaTriggerTreshold = _privateAreaTriggerThreshold; break; case CreaturePrefabBaselineGroup.MonsterAiChase: component.m_interceptTimeMin = _chase[0]; component.m_interceptTimeMax = _chase[1]; component.m_maxChaseDistance = _chase[2]; component.m_minAttackInterval = _chase[3]; break; case CreaturePrefabBaselineGroup.MonsterAiCircle: component.m_circleTargetInterval = _circle[0]; component.m_circleTargetDuration = _circle[1]; component.m_circleTargetDistance = _circle[2]; break; case CreaturePrefabBaselineGroup.MonsterAiHurtFlee: component.m_fleeIfHurtWhenTargetCantBeReached = _fleeIfHurt; component.m_fleeUnreachableSinceAttacking = _fleeUnreachableSinceAttacking; component.m_fleeUnreachableSinceHurt = _fleeUnreachableSinceHurt; component.m_fleeIfNotAlerted = _fleeIfNotAlerted; component.m_fleeIfLowHealth = _fleeIfLowHealth; component.m_fleeTimeSinceHurt = _fleeTimeSinceHurt; component.m_fleeInLava = _fleeInLava; break; case CreaturePrefabBaselineGroup.MonsterAiCharge: component.m_circulateWhileCharging = _circulateWhileCharging; component.m_circulateWhileChargingFlying = _circulateWhileChargingFlying; break; case CreaturePrefabBaselineGroup.MonsterAiSleep: component.m_sleeping = _sleeping; component.m_wakeupRange = _wakeupRange; component.m_noiseWakeup = _noiseWakeup; component.m_maxNoiseWakeupRange = _maxNoiseWakeupRange; component.m_wakeUpDelayMin = _wakeUpDelayMin; component.m_wakeUpDelayMax = _wakeUpDelayMax; component.m_fallAsleepDistance = _fallAsleepDistance; break; case CreaturePrefabBaselineGroup.MonsterAiAvoidLand: component.m_avoidLand = _avoidLand; break; } } } } private sealed class HumanoidState { private GameObject[]? _defaultItems; private GameObject[]? _randomWeapon; private GameObject[]? _randomArmor; private GameObject[]? _randomShield; private RandomItem[]? _randomItems; private ItemSet[]? _randomSets; internal bool Capture(GameObject prefab, CreaturePrefabBaselineGroup group) { Humanoid component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } switch (group) { case CreaturePrefabBaselineGroup.HumanoidDefaultItems: _defaultItems = CloneArray(component.m_defaultItems); break; case CreaturePrefabBaselineGroup.HumanoidRandomWeapon: _randomWeapon = CloneArray(component.m_randomWeapon); break; case CreaturePrefabBaselineGroup.HumanoidRandomArmor: _randomArmor = CloneArray(component.m_randomArmor); break; case CreaturePrefabBaselineGroup.HumanoidRandomShield: _randomShield = CloneArray(component.m_randomShield); break; case CreaturePrefabBaselineGroup.HumanoidRandomItems: _randomItems = CloneRandomItems(component.m_randomItems); break; case CreaturePrefabBaselineGroup.HumanoidRandomSets: _randomSets = CloneItemSets(component.m_randomSets); break; default: return false; } return true; } internal void Restore(GameObject prefab, CreaturePrefabBaselineGroup group) { Humanoid component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { switch (group) { case CreaturePrefabBaselineGroup.HumanoidDefaultItems: component.m_defaultItems = CloneArray(_defaultItems); break; case CreaturePrefabBaselineGroup.HumanoidRandomWeapon: component.m_randomWeapon = CloneArray(_randomWeapon); break; case CreaturePrefabBaselineGroup.HumanoidRandomArmor: component.m_randomArmor = CloneArray(_randomArmor); break; case CreaturePrefabBaselineGroup.HumanoidRandomShield: component.m_randomShield = CloneArray(_randomShield); break; case CreaturePrefabBaselineGroup.HumanoidRandomItems: component.m_randomItems = CloneRandomItems(_randomItems); break; case CreaturePrefabBaselineGroup.HumanoidRandomSets: component.m_randomSets = CloneItemSets(_randomSets); break; } } } private static GameObject[]? CloneArray(GameObject[]? values) { if (values != null) { return (GameObject[])values.Clone(); } return null; } private static RandomItem[]? CloneRandomItems(RandomItem[]? values) { return values?.Select((Func)((RandomItem value) => (value != null) ? new RandomItem { m_prefab = value.m_prefab, m_chance = value.m_chance } : ((RandomItem)null))).ToArray(); } private static ItemSet[]? CloneItemSets(ItemSet[]? values) { return values?.Select((Func)((ItemSet value) => (value != null) ? new ItemSet { m_name = value.m_name, m_items = CloneArray(value.m_items) } : ((ItemSet)null))).ToArray(); } } private sealed class RagdollState { private readonly List _effects = new List(); private readonly List _prefabs = new List(); internal bool Capture(GameObject prefab) { EffectData[] array = prefab.GetComponent()?.m_deathEffects?.m_effectPrefabs; if (array == null) { return false; } _effects.Clear(); _prefabs.Clear(); EffectData[] array2 = array; foreach (EffectData val in array2) { if (val != null && !((Object)(object)val.m_prefab == (Object)null) && !((Object)(object)val.m_prefab.GetComponent() == (Object)null)) { _effects.Add(val); _prefabs.Add(val.m_prefab); } } return _effects.Count > 0; } internal void Restore() { for (int i = 0; i < _effects.Count && i < _prefabs.Count; i++) { EffectData val = _effects[i]; if (val != null) { val.m_prefab = _prefabs[i]; } } } } private sealed class AppearanceState { private bool _hasVisEquipment; private int _modelIndex; private Vector3 _skinColor; private Vector3 _hairColor; private string _visHair = ""; private string _visBeard = ""; private bool _hasHumanoid; private string _humanoidHair = ""; private string _humanoidBeard = ""; internal bool Capture(GameObject prefab) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0062: Unknown result type (might be due to invalid IL or missing references) VisEquipment component = prefab.GetComponent(); Humanoid component2 = prefab.GetComponent(); _hasVisEquipment = (Object)(object)component != (Object)null; _hasHumanoid = (Object)(object)component2 != (Object)null; if (!_hasVisEquipment && !_hasHumanoid) { return false; } if ((Object)(object)component != (Object)null) { _modelIndex = component.m_modelIndex; _skinColor = component.m_skinColor; _hairColor = component.m_hairColor; _visHair = component.m_hairItem; _visBeard = component.m_beardItem; } if ((Object)(object)component2 != (Object)null) { _humanoidHair = component2.m_hairItem; _humanoidBeard = component2.m_beardItem; } return true; } internal void Restore(GameObject prefab) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (_hasVisEquipment) { VisEquipment component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_modelIndex = _modelIndex; component.m_skinColor = _skinColor; component.m_hairColor = _hairColor; component.m_hairItem = _visHair; component.m_beardItem = _visBeard; } } if (_hasHumanoid) { Humanoid component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.m_hairItem = _humanoidHair; component2.m_beardItem = _humanoidBeard; } } } } private static readonly CreaturePrefabBaselineGroup[] IndividualGroups = new CreaturePrefabBaselineGroup[43] { CreaturePrefabBaselineGroup.AttackDamage, CreaturePrefabBaselineGroup.AttackTuple, CreaturePrefabBaselineGroup.AttackStatusEffect, CreaturePrefabBaselineGroup.AttackProjectile, CreaturePrefabBaselineGroup.AttackAi, CreaturePrefabBaselineGroup.ProjectileSpawnOnHit, CreaturePrefabBaselineGroup.SpawnAbilitySpawnPrefabs, CreaturePrefabBaselineGroup.CharacterIdentity, CreaturePrefabBaselineGroup.CharacterBoss, CreaturePrefabBaselineGroup.CharacterGlobalKey, CreaturePrefabBaselineGroup.CharacterHealth, CreaturePrefabBaselineGroup.CharacterDamageModifiers, CreaturePrefabBaselineGroup.CharacterSpeed, CreaturePrefabBaselineGroup.CharacterJump, CreaturePrefabBaselineGroup.CharacterSwim, CreaturePrefabBaselineGroup.CharacterFlight, CreaturePrefabBaselineGroup.BaseAiSenses, CreaturePrefabBaselineGroup.BaseAiIdleSound, CreaturePrefabBaselineGroup.BaseAiMovement, CreaturePrefabBaselineGroup.BaseAiSerpent, CreaturePrefabBaselineGroup.BaseAiRandomMove, CreaturePrefabBaselineGroup.BaseAiFlight, CreaturePrefabBaselineGroup.BaseAiAvoid, CreaturePrefabBaselineGroup.BaseAiFlee, CreaturePrefabBaselineGroup.BaseAiAggressive, CreaturePrefabBaselineGroup.BaseAiMessages, CreaturePrefabBaselineGroup.MonsterAiAlertRange, CreaturePrefabBaselineGroup.MonsterAiHunt, CreaturePrefabBaselineGroup.MonsterAiChase, CreaturePrefabBaselineGroup.MonsterAiCircle, CreaturePrefabBaselineGroup.MonsterAiHurtFlee, CreaturePrefabBaselineGroup.MonsterAiCharge, CreaturePrefabBaselineGroup.MonsterAiSleep, CreaturePrefabBaselineGroup.MonsterAiAvoidLand, CreaturePrefabBaselineGroup.HumanoidDefaultItems, CreaturePrefabBaselineGroup.HumanoidRandomWeapon, CreaturePrefabBaselineGroup.HumanoidRandomArmor, CreaturePrefabBaselineGroup.HumanoidRandomShield, CreaturePrefabBaselineGroup.HumanoidRandomItems, CreaturePrefabBaselineGroup.HumanoidRandomSets, CreaturePrefabBaselineGroup.VisualScale, CreaturePrefabBaselineGroup.RagdollReferences, CreaturePrefabBaselineGroup.Appearance }; private static readonly Dictionary Entries = new Dictionary(); internal static void BeginApplyPass() { KeyValuePair[] array = Entries.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; int key = keyValuePair.Key; Entry value = keyValuePair.Value; if ((Object)(object)value.Prefab == (Object)null) { Entries.Remove(key); continue; } RestoreGroups(value, value.AppliedGroups); value.AppliedGroups = CreaturePrefabBaselineGroup.None; } } internal static bool HasAppliedGroups(GameObject prefab) { if ((Object)(object)prefab != (Object)null && Entries.TryGetValue(((Object)prefab).GetInstanceID(), out Entry value) && value.Prefab == prefab) { return value.AppliedGroups != CreaturePrefabBaselineGroup.None; } return false; } internal static void Capture(GameObject prefab, CreaturePrefabBaselineGroup groups) { if ((Object)(object)prefab == (Object)null || groups == CreaturePrefabBaselineGroup.None) { return; } Entry orCreateEntry = GetOrCreateEntry(prefab); CreaturePrefabBaselineGroup[] individualGroups = IndividualGroups; foreach (CreaturePrefabBaselineGroup creaturePrefabBaselineGroup in individualGroups) { if ((groups & creaturePrefabBaselineGroup) != CreaturePrefabBaselineGroup.None && ((orCreateEntry.CapturedGroups & creaturePrefabBaselineGroup) != CreaturePrefabBaselineGroup.None || CaptureGroup(orCreateEntry, creaturePrefabBaselineGroup))) { orCreateEntry.CapturedGroups |= creaturePrefabBaselineGroup; orCreateEntry.AppliedGroups |= creaturePrefabBaselineGroup; } } } internal static void Restore(GameObject prefab, CreaturePrefabBaselineGroup groups) { if (!((Object)(object)prefab == (Object)null) && Entries.TryGetValue(((Object)prefab).GetInstanceID(), out Entry value) && value.Prefab == prefab) { CreaturePrefabBaselineGroup creaturePrefabBaselineGroup = groups & value.AppliedGroups; RestoreGroups(value, creaturePrefabBaselineGroup); value.AppliedGroups &= ~creaturePrefabBaselineGroup; } } internal static void RestoreAndForget(GameObject prefab) { if (!((Object)(object)prefab == (Object)null) && Entries.TryGetValue(((Object)prefab).GetInstanceID(), out Entry value) && value.Prefab == prefab) { RestoreGroups(value, value.AppliedGroups); Entries.Remove(((Object)prefab).GetInstanceID()); } } internal static void Forget(GameObject prefab) { if (prefab != null) { Entries.Remove(((Object)prefab).GetInstanceID()); } } internal static void RestoreAllAndClear() { foreach (Entry value in Entries.Values) { if ((Object)(object)value.Prefab != (Object)null) { RestoreGroups(value, value.AppliedGroups); } } Entries.Clear(); } private static Entry GetOrCreateEntry(GameObject prefab) { int instanceID = ((Object)prefab).GetInstanceID(); if (Entries.TryGetValue(instanceID, out Entry value) && value.Prefab == prefab) { return value; } value = new Entry(prefab); Entries[instanceID] = value; return value; } private static bool CaptureGroup(Entry entry, CreaturePrefabBaselineGroup group) { //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) GameObject prefab = entry.Prefab; if ((group & CreaturePrefabBaselineGroup.AttackAll) != CreaturePrefabBaselineGroup.None) { return entry.Attack.Capture(prefab, group); } if ((group & CreaturePrefabBaselineGroup.CharacterAll) != CreaturePrefabBaselineGroup.None) { return entry.Character.Capture(prefab, group); } if ((group & CreaturePrefabBaselineGroup.BaseAiAll) != CreaturePrefabBaselineGroup.None) { return entry.BaseAi.Capture(prefab, group); } if ((group & CreaturePrefabBaselineGroup.MonsterAiAll) != CreaturePrefabBaselineGroup.None) { return entry.MonsterAi.Capture(prefab, group); } if ((group & CreaturePrefabBaselineGroup.HumanoidAll) != CreaturePrefabBaselineGroup.None) { return entry.Humanoid.Capture(prefab, group); } if ((group & CreaturePrefabBaselineGroup.ProjectileAll) != CreaturePrefabBaselineGroup.None) { return entry.Projectile.Capture(prefab, group); } switch (group) { case CreaturePrefabBaselineGroup.VisualScale: entry.Scale = prefab.transform.localScale; return true; case CreaturePrefabBaselineGroup.RagdollReferences: return entry.Ragdoll.Capture(prefab); case CreaturePrefabBaselineGroup.Appearance: return entry.Appearance.Capture(prefab); default: return false; } } private static void RestoreGroups(Entry entry, CreaturePrefabBaselineGroup groups) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) groups &= entry.CapturedGroups; if (groups == CreaturePrefabBaselineGroup.None || (Object)(object)entry.Prefab == (Object)null) { return; } CreaturePrefabBaselineGroup[] individualGroups = IndividualGroups; foreach (CreaturePrefabBaselineGroup creaturePrefabBaselineGroup in individualGroups) { if ((groups & creaturePrefabBaselineGroup) == CreaturePrefabBaselineGroup.None) { continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.AttackAll) != CreaturePrefabBaselineGroup.None) { entry.Attack.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.CharacterAll) != CreaturePrefabBaselineGroup.None) { entry.Character.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.BaseAiAll) != CreaturePrefabBaselineGroup.None) { entry.BaseAi.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.MonsterAiAll) != CreaturePrefabBaselineGroup.None) { entry.MonsterAi.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.HumanoidAll) != CreaturePrefabBaselineGroup.None) { entry.Humanoid.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } if ((creaturePrefabBaselineGroup & CreaturePrefabBaselineGroup.ProjectileAll) != CreaturePrefabBaselineGroup.None) { entry.Projectile.Restore(entry.Prefab, creaturePrefabBaselineGroup); continue; } switch (creaturePrefabBaselineGroup) { case CreaturePrefabBaselineGroup.VisualScale: entry.Prefab.transform.localScale = entry.Scale; break; case CreaturePrefabBaselineGroup.RagdollReferences: entry.Ragdoll.Restore(); break; case CreaturePrefabBaselineGroup.Appearance: entry.Appearance.Restore(entry.Prefab); break; } } } } internal static class CreaturePrefabRegistry { private sealed class CloneRecord { internal readonly GameObject Prefab; internal readonly string SourceName; internal CloneRecord(GameObject prefab, string sourceName) { Prefab = prefab; SourceName = sourceName; } } private static readonly Dictionary ClonedPrefabs = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ResourceCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly List PrefabsToRegister = new List(); private static readonly List RetiredClones = new List(); private static readonly HashSet ClonesPresentAtApplyStart = new HashSet(StringComparer.OrdinalIgnoreCase); private static GameObject? Root; private static ZNetScene? FejdZNetScene; private static ObjectDB? FejdObjectDb; private static bool CloneApplyInProgress; internal static void CacheFejdStartup(FejdStartup fejdStartup) { if (!((Object)(object)fejdStartup.m_objectDBPrefab == (Object)null)) { FejdZNetScene = fejdStartup.m_objectDBPrefab.GetComponent(); FejdObjectDb = fejdStartup.m_objectDBPrefab.GetComponent(); } } internal static GameObject? GetPrefab(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return null; } if (ClonedPrefabs.TryGetValue(prefabName, out CloneRecord value) && (Object)(object)value.Prefab != (Object)null) { return value.Prefab; } if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab != (Object)null) { return prefab; } } if ((Object)(object)FejdZNetScene != (Object)null) { GameObject val = ((IEnumerable)FejdZNetScene.m_prefabs).FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && ((Object)candidate).name == prefabName)); if ((Object)(object)val != (Object)null) { return val; } } if ((Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); if ((Object)(object)itemPrefab != (Object)null) { return itemPrefab; } } if ((Object)(object)FejdObjectDb != (Object)null) { GameObject val2 = ((IEnumerable)FejdObjectDb.m_items).FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && ((Object)candidate).name == prefabName)); if ((Object)(object)val2 != (Object)null) { return val2; } } CacheResourcesIfNeeded(); if (!ResourceCache.TryGetValue(prefabName, out GameObject value2)) { return null; } return value2; } internal static List GetCreaturePrefabs() { IEnumerable enumerable = Enumerable.Empty(); if ((Object)(object)ZNetScene.instance != (Object)null) { enumerable = enumerable.Concat(ZNetScene.instance.m_prefabs); } if ((Object)(object)FejdZNetScene != (Object)null) { enumerable = enumerable.Concat(FejdZNetScene.m_prefabs); } return (from @group in enumerable.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !IsClonedPrefab(prefab) && (Object)(object)prefab.GetComponent() != (Object)null && !IsPlayerPrefab(prefab)).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First()).OrderBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); } internal static List GetAttackPrefabs() { HashSet creatureAttackItemNames = GetCreatureAttackItemNames(); IEnumerable enumerable = Enumerable.Empty(); if ((Object)(object)ObjectDB.instance != (Object)null) { enumerable = enumerable.Concat(ObjectDB.instance.m_items); } if ((Object)(object)FejdObjectDb != (Object)null) { enumerable = enumerable.Concat(FejdObjectDb.m_items); } if ((Object)(object)ZNetScene.instance != (Object)null) { enumerable = enumerable.Concat(ZNetScene.instance.m_prefabs); } if ((Object)(object)FejdZNetScene != (Object)null) { enumerable = enumerable.Concat(FejdZNetScene.m_prefabs); } return (from @group in enumerable.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !IsClonedPrefab(prefab) && creatureAttackItemNames.Contains(((Object)prefab).name) && IsAttackItem(prefab)).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First()).OrderBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); } internal static List GetItemPrefabs() { IEnumerable enumerable = Enumerable.Empty(); if ((Object)(object)ObjectDB.instance != (Object)null) { enumerable = enumerable.Concat(ObjectDB.instance.m_items); } if ((Object)(object)FejdObjectDb != (Object)null) { enumerable = enumerable.Concat(FejdObjectDb.m_items); } if ((Object)(object)ZNetScene.instance != (Object)null) { enumerable = enumerable.Concat(ZNetScene.instance.m_prefabs); } if ((Object)(object)FejdZNetScene != (Object)null) { enumerable = enumerable.Concat(FejdZNetScene.m_prefabs); } return (from @group in enumerable.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !IsClonedPrefab(prefab) && (Object)(object)prefab.GetComponent() != (Object)null).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First()).OrderBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); } internal static List GetProjectilePrefabs() { IEnumerable enumerable = Enumerable.Empty(); if ((Object)(object)ZNetScene.instance != (Object)null) { enumerable = enumerable.Concat(ZNetScene.instance.m_prefabs); } if ((Object)(object)FejdZNetScene != (Object)null) { enumerable = enumerable.Concat(FejdZNetScene.m_prefabs); } if ((Object)(object)ObjectDB.instance != (Object)null) { enumerable = enumerable.Concat(ObjectDB.instance.m_items); } if ((Object)(object)FejdObjectDb != (Object)null) { enumerable = enumerable.Concat(FejdObjectDb.m_items); } return (from @group in enumerable.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !IsClonedPrefab(prefab) && HasProjectileLikeComponent(prefab)).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First()).OrderBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); } internal static bool IsCreatureManagerClone(GameObject prefab) { return IsClonedPrefab(prefab); } internal static bool TryGetCloneSource(string cloneName, out string sourceName) { if (ClonedPrefabs.TryGetValue(cloneName, out CloneRecord value) && (Object)(object)value.Prefab != (Object)null) { sourceName = value.SourceName; return true; } sourceName = ""; return false; } internal static bool IsPlayerPrefab(GameObject prefab) { return (Object)(object)prefab.GetComponent() != (Object)null; } internal static bool TryValidateCloneRegistration(GameObject source, string cloneName, out string error) { error = ""; if ((Object)(object)source == (Object)null || string.IsNullOrWhiteSpace(cloneName)) { error = "Cannot create a CreatureManager prefab clone without a source and clone name."; return false; } cloneName = cloneName.Trim(); if ((Object)(object)FindExternalPrefabByName(cloneName) != (Object)null) { error = "CreatureManager clone '" + cloneName + "' was not created because an external prefab with that name is already registered."; return false; } if (TryFindExternalHashOwner(source, cloneName, out GameObject owner)) { error = "CreatureManager clone '" + cloneName + "' was not created because its stable hash collides with external prefab '" + ((Object)owner).name + "'."; return false; } return true; } internal static GameObject? ClonePrefab(GameObject source, string cloneName) { if ((Object)(object)source == (Object)null || string.IsNullOrWhiteSpace(cloneName)) { CreatureManagerPlugin.Log.LogError((object)"Cannot create a CreatureManager prefab clone without a source and clone name."); return null; } cloneName = cloneName.Trim(); string name = ((Object)source).name; GameObject val = null; if (ClonedPrefabs.TryGetValue(cloneName, out CloneRecord value)) { if ((Object)(object)value.Prefab == (Object)null) { RemoveOwnedClone(cloneName); } else { if (!string.Equals(value.SourceName, name, StringComparison.OrdinalIgnoreCase)) { CreatureManagerPlugin.Log.LogError((object)("CreatureManager clone '" + cloneName + "' is already based on '" + value.SourceName + "' and cannot be changed to '" + name + "' at runtime. Restart the game after changing clonedFrom.")); return null; } val = value.Prefab; } } if (!TryValidateCloneRegistration(source, cloneName, out string error)) { CreatureManagerPlugin.Log.LogError((object)error); return null; } if ((Object)(object)val != (Object)null) { return val; } Transform rootTransform = GetRootTransform(); GameObject val2 = Object.Instantiate(source, rootTransform, false); ((Object)val2).name = cloneName; ClonedPrefabs[cloneName] = new CloneRecord(val2, name); if (!RegisterPrefab(val2)) { RemoveOwnedClone(cloneName); return null; } return val2; } internal static void BeginCloneApplyPass() { CloneApplyInProgress = true; ClonesPresentAtApplyStart.Clear(); ClonesPresentAtApplyStart.UnionWith(ClonedPrefabs.Keys); } internal static void CompleteCloneApplyPass() { if (CloneApplyInProgress) { CloneApplyInProgress = false; ClonesPresentAtApplyStart.Clear(); } } internal static void CancelCloneApplyPass() { if (CloneApplyInProgress) { CloneApplyInProgress = false; string[] array = ClonedPrefabs.Keys.Where((string name) => !ClonesPresentAtApplyStart.Contains(name)).ToArray(); ClonesPresentAtApplyStart.Clear(); string[] array2 = array; for (int num = 0; num < array2.Length; num++) { RemoveOwnedClone(array2[num]); } } } internal static void ResetOwnedClones() { CloneApplyInProgress = false; ClonesPresentAtApplyStart.Clear(); string[] array = ClonedPrefabs.Keys.ToArray(); for (int i = 0; i < array.Length; i++) { RemoveOwnedClone(array[i]); } ClonedPrefabs.Clear(); PrefabsToRegister.Clear(); ResourceCache.Clear(); if ((Object)(object)Root != (Object)null) { Object.Destroy((Object)(object)Root); Root = null; } } private static bool RegisterPrefab(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return false; } if (!PrefabsToRegister.Contains(prefab)) { PrefabsToRegister.Add(prefab); } return RegisterWithZNetScene(ZNetScene.instance, prefab) & RegisterWithZNetScene(FejdZNetScene, prefab) & RegisterWithObjectDb(ObjectDB.instance, prefab) & RegisterWithObjectDb(FejdObjectDb, prefab); } internal static void RegisterPendingPrefabs(ZNetScene scene) { PrefabsToRegister.RemoveAll((GameObject val) => (Object)(object)val == (Object)null); GameObject[] array = PrefabsToRegister.ToArray(); foreach (GameObject prefab in array) { RegisterWithZNetScene(scene, prefab); } } internal static void RegisterPendingPrefabs(ObjectDB objectDb) { PrefabsToRegister.RemoveAll((GameObject val) => (Object)(object)val == (Object)null); GameObject[] array = PrefabsToRegister.ToArray(); foreach (GameObject prefab in array) { RegisterWithObjectDb(objectDb, prefab); } } private static Transform GetRootTransform() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if ((Object)(object)Root != (Object)null) { return Root.transform; } Root = new GameObject("CreatureManager_prefab_root"); Object.DontDestroyOnLoad((Object)(object)Root); Root.SetActive(false); return Root.transform; } private static bool IsClonedPrefab(GameObject prefab) { return ClonedPrefabs.Values.Any((CloneRecord record) => (Object)(object)record.Prefab == (Object)(object)prefab); } internal static bool IsAttackItem(GameObject prefab) { ItemDrop component = prefab.GetComponent(); if (component?.m_itemData?.m_shared?.m_attack != null) { return !string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_attack.m_attackAnimation); } return false; } private static bool HasProjectileLikeComponent(GameObject prefab) { if (!((Object)(object)prefab.GetComponent("Projectile") != (Object)null) && !((Object)(object)prefab.GetComponent("Aoe") != (Object)null) && !((Object)(object)prefab.GetComponent("SpawnAbility") != (Object)null)) { return (Object)(object)prefab.GetComponent("TriggerSpawnAbility") != (Object)null; } return true; } private static HashSet GetCreatureAttackItemNames() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (GameObject creaturePrefab in GetCreaturePrefabs()) { Humanoid component = creaturePrefab.GetComponent(); if ((Object)(object)component == (Object)null) { continue; } AddAttackItems(hashSet, component.m_defaultItems); AddAttackItems(hashSet, component.m_randomWeapon); if (component.m_randomItems != null) { RandomItem[] randomItems = component.m_randomItems; for (int i = 0; i < randomItems.Length; i++) { AddAttackItem(hashSet, randomItems[i]?.m_prefab); } } if (component.m_randomSets != null) { ItemSet[] randomSets = component.m_randomSets; for (int i = 0; i < randomSets.Length; i++) { AddAttackItems(hashSet, randomSets[i]?.m_items); } } } return hashSet; } private static void AddAttackItems(HashSet names, IEnumerable? items) { if (items == null) { return; } foreach (GameObject item in items) { AddAttackItem(names, item); } } private static void AddAttackItem(HashSet names, GameObject? item) { if ((Object)(object)item != (Object)null && IsAttackItem(item)) { names.Add(((Object)item).name); } } private static bool RegisterWithZNetScene(ZNetScene? scene, GameObject prefab) { if ((Object)(object)scene == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null) { return true; } int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); if (scene.m_prefabs.Any((GameObject candidate) => (Object)(object)candidate != (Object)null && (Object)(object)candidate != (Object)(object)prefab && string.Equals(((Object)candidate).name, ((Object)prefab).name, StringComparison.OrdinalIgnoreCase)) || (scene.m_namedPrefabs != null && scene.m_namedPrefabs.TryGetValue(stableHashCode, out var value) && (Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)prefab)) { CreatureManagerPlugin.Log.LogError((object)("CreatureManager prefab '" + ((Object)prefab).name + "' was not registered in ZNetScene because its name or stable hash is already owned by another prefab.")); return false; } if (!scene.m_prefabs.Contains(prefab)) { scene.m_prefabs.Add(prefab); } if (scene.m_namedPrefabs != null) { scene.m_namedPrefabs[stableHashCode] = prefab; } return true; } private static bool RegisterWithObjectDb(ObjectDB? objectDb, GameObject prefab) { ItemDrop component = prefab.GetComponent(); if ((Object)(object)objectDb == (Object)null || (Object)(object)component == (Object)null) { return true; } int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); SharedData shared = component.m_itemData.m_shared; if (objectDb.m_items.Any((GameObject candidate) => (Object)(object)candidate != (Object)null && (Object)(object)candidate != (Object)(object)prefab && string.Equals(((Object)candidate).name, ((Object)prefab).name, StringComparison.OrdinalIgnoreCase)) || (objectDb.m_itemByHash != null && objectDb.m_itemByHash.TryGetValue(stableHashCode, out var value) && (Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)prefab) || (objectDb.m_itemByData != null && objectDb.m_itemByData.TryGetValue(shared, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)value2 != (Object)(object)prefab)) { CreatureManagerPlugin.Log.LogError((object)("CreatureManager prefab '" + ((Object)prefab).name + "' was not registered in ObjectDB because its name or stable hash is already owned by another prefab.")); return false; } if (!objectDb.m_items.Contains(prefab)) { objectDb.m_items.Add(prefab); } if (objectDb.m_itemByHash != null) { objectDb.m_itemByHash[stableHashCode] = prefab; } if (objectDb.m_itemByData != null) { objectDb.m_itemByData[shared] = prefab; } return true; } private static void RemoveOwnedClone(string cloneName) { if (ClonedPrefabs.TryGetValue(cloneName, out CloneRecord value)) { ClonedPrefabs.Remove(cloneName); GameObject prefab = value.Prefab; CreaturePrefabBaseline.Forget(prefab); RetiredClones.Add(prefab); PrefabsToRegister.RemoveAll((GameObject candidate) => candidate == prefab); RemoveFromZNetScene(ZNetScene.instance, prefab); RemoveFromZNetScene(FejdZNetScene, prefab); RemoveFromObjectDb(ObjectDB.instance, prefab); RemoveFromObjectDb(FejdObjectDb, prefab); string[] array = (from pair in ResourceCache where pair.Value == prefab select pair.Key).ToArray(); foreach (string key in array) { ResourceCache.Remove(key); } if ((Object)(object)prefab != (Object)null) { Object.Destroy((Object)(object)prefab); } } } private static void RemoveFromZNetScene(ZNetScene? scene, GameObject prefab) { if ((Object)(object)scene == (Object)null) { return; } scene.m_prefabs.RemoveAll((GameObject candidate) => candidate == prefab); if (scene.m_namedPrefabs == null) { return; } int[] array = (from pair in scene.m_namedPrefabs where pair.Value == prefab select pair.Key).ToArray(); foreach (int hash in array) { scene.m_namedPrefabs.Remove(hash); GameObject val = ((IEnumerable)scene.m_prefabs).FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && !IsClonedPrefab(candidate) && StringExtensionMethods.GetStableHashCode(((Object)candidate).name) == hash)); if ((Object)(object)val != (Object)null) { scene.m_namedPrefabs[hash] = val; } } } private static void RemoveFromObjectDb(ObjectDB? objectDb, GameObject prefab) { if ((Object)(object)objectDb == (Object)null) { return; } objectDb.m_items.RemoveAll((GameObject candidate) => candidate == prefab); if (objectDb.m_itemByHash != null) { int[] array = (from pair in objectDb.m_itemByHash where pair.Value == prefab select pair.Key).ToArray(); foreach (int hash in array) { objectDb.m_itemByHash.Remove(hash); GameObject val = ((IEnumerable)objectDb.m_items).FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && !IsClonedPrefab(candidate) && StringExtensionMethods.GetStableHashCode(((Object)candidate).name) == hash)); if ((Object)(object)val != (Object)null) { objectDb.m_itemByHash[hash] = val; } } } if (objectDb.m_itemByData == null) { return; } SharedData[] array2 = (from pair in objectDb.m_itemByData where pair.Value == prefab select pair.Key).ToArray(); foreach (SharedData shared in array2) { objectDb.m_itemByData.Remove(shared); GameObject val2 = ((IEnumerable)objectDb.m_items).FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && !IsClonedPrefab(candidate) && candidate.GetComponent()?.m_itemData?.m_shared == shared)); if ((Object)(object)val2 != (Object)null) { objectDb.m_itemByData[shared] = val2; } } } private static GameObject? FindExternalPrefabByName(string prefabName) { IEnumerable enumerable = Enumerable.Empty(); if ((Object)(object)ZNetScene.instance != (Object)null) { enumerable = enumerable.Concat(ZNetScene.instance.m_prefabs); } if ((Object)(object)FejdZNetScene != (Object)null) { enumerable = enumerable.Concat(FejdZNetScene.m_prefabs); } if ((Object)(object)ObjectDB.instance != (Object)null) { enumerable = enumerable.Concat(ObjectDB.instance.m_items); } if ((Object)(object)FejdObjectDb != (Object)null) { enumerable = enumerable.Concat(FejdObjectDb.m_items); } GameObject val = enumerable.FirstOrDefault((Func)((GameObject candidate) => (Object)(object)candidate != (Object)null && !IsClonedPrefab(candidate) && string.Equals(((Object)candidate).name, prefabName, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val != (Object)null) { return val; } CacheResourcesIfNeeded(); if (!ResourceCache.TryGetValue(prefabName, out GameObject value) || !((Object)(object)value != (Object)null) || IsClonedPrefab(value)) { return null; } return value; } private static bool TryFindExternalHashOwner(GameObject source, string cloneName, out GameObject? owner) { int stableHashCode = StringExtensionMethods.GetStableHashCode(cloneName); if ((Object)(object)source.GetComponent() != (Object)null && (TryGetExternalHashOwner(ZNetScene.instance?.m_namedPrefabs, stableHashCode, out owner) || TryGetExternalHashOwner(FejdZNetScene?.m_namedPrefabs, stableHashCode, out owner))) { return true; } if ((Object)(object)source.GetComponent() != (Object)null && (TryGetExternalHashOwner(ObjectDB.instance?.m_itemByHash, stableHashCode, out owner) || TryGetExternalHashOwner(FejdObjectDb?.m_itemByHash, stableHashCode, out owner))) { return true; } owner = null; return false; } private static bool TryGetExternalHashOwner(Dictionary? registry, int hash, out GameObject? owner) { if (registry != null && registry.TryGetValue(hash, out GameObject value) && (Object)(object)value != (Object)null && !IsClonedPrefab(value)) { owner = value; return true; } owner = null; return false; } private static void CacheResourcesIfNeeded() { RetiredClones.RemoveAll((GameObject val) => (Object)(object)val == (Object)null); if (ResourceCache.Count > 0) { return; } GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject prefab in array) { if (!((Object)(object)prefab == (Object)null) && ((Object)prefab).GetInstanceID() >= 0 && !RetiredClones.Any((GameObject retired) => retired == prefab) && !ResourceCache.ContainsKey(((Object)prefab).name)) { ResourceCache[((Object)prefab).name] = prefab; } } } } internal static class CreatureReferenceSections { private sealed class GroupedPrefab { public GameObject Prefab { get; set; } public string PrefabName { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; } internal const string VanillaOwnerName = "Valheim"; internal const string UnknownOwnerName = "Unknown / Untracked"; internal static void AppendPrefabSections(StringBuilder builder, IEnumerable prefabs, Action appendEntry) { List> list = (from entry in prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null).Select(delegate(GameObject prefab) { string prefabName = (((Object)prefab).name ?? "").Trim(); string ownerName = CreaturePrefabOwnerResolver.GetOwnerName(prefabName); return new GroupedPrefab { Prefab = prefab, PrefabName = prefabName, OwnerName = (string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()) }; }) orderby GetOwnerSortBucket(entry.OwnerName) select entry).ThenBy((GroupedPrefab entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((GroupedPrefab entry) => entry.PrefabName, StringComparer.OrdinalIgnoreCase).GroupBy((GroupedPrefab entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase) .ToList(); bool flag = false; foreach (IGrouping item in list) { if (flag) { builder.AppendLine(); } AppendSectionHeaderComment(builder, item.Key); bool flag2 = false; foreach (GroupedPrefab item2 in item) { if (flag2) { builder.AppendLine(); } appendEntry(builder, item2.Prefab); flag2 = true; } flag = true; } } private static void AppendSectionHeaderComment(StringBuilder builder, string ownerName) { builder.Append("# ===== "); builder.Append(string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()); builder.AppendLine(" ====="); } internal static int GetOwnerSortBucket(string ownerName) { if (string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase)) { return 0; } if (!string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase)) { return 1; } return 2; } } internal static class CreaturePrefabOwnerResolver { internal static string GetOwnerName(string? prefabName) { string text = NormalizeName(prefabName); if (text.Length == 0) { return "Unknown / Untracked"; } foreach (string item in EnumerateLookupCandidates(text)) { if (CreatureVanillaAssetCatalog.IsVanillaPrefab(item)) { return "Valheim"; } } return CreatureAssetOwnerCatalog.GetOwnerName(text); } private static IEnumerable EnumerateLookupCandidates(string normalizedName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); AddIfNew(normalizedName); int num = normalizedName.IndexOf(':'); if (num > 0) { AddIfNew(normalizedName.Substring(0, num)); } foreach (string item in seen) { yield return item; } void AddIfNew(string candidate) { string text = NormalizeName(candidate); if (text.Length > 0) { seen.Add(text); } } } private static string NormalizeName(string? name) { return (name ?? "").Replace("(Clone)", "").Trim(); } } internal static class CreatureTextureOwnerResolver { internal static string GetOwnerName(string? textureName) { string text = NormalizeName(textureName); if (text.Length == 0) { return "Unknown / Untracked"; } foreach (string item in EnumerateLookupCandidates(text)) { if (CreatureVanillaAssetCatalog.IsVanillaTexture(item)) { return "Valheim"; } } return CreatureAssetOwnerCatalog.GetOwnerName(text); } private static IEnumerable EnumerateLookupCandidates(string normalizedName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); AddIfNew(normalizedName); int num = normalizedName.IndexOf(':'); if (num > 0) { AddIfNew(normalizedName.Substring(0, num)); } foreach (string item in seen) { yield return item; } void AddIfNew(string candidate) { string text = NormalizeName(candidate); if (text.Length > 0) { seen.Add(text); } } } private static string NormalizeName(string? name) { return (name ?? "").Replace("(Clone)", "").Replace("(Instance)", "").Trim(); } } internal static class CreatureVanillaAssetCatalog { private enum CatalogState { Uninitialized, Loaded, Unavailable } private static readonly object Sync = new object(); private static readonly HashSet PrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet TextureNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static CatalogState State; internal static bool IsVanillaPrefab(string prefabName) { EnsureLoaded(); if (State == CatalogState.Loaded && !string.IsNullOrWhiteSpace(prefabName)) { return PrefabNames.Contains(prefabName); } return false; } internal static bool IsVanillaTexture(string textureName) { EnsureLoaded(); if (State == CatalogState.Loaded && !string.IsNullOrWhiteSpace(textureName)) { return TextureNames.Contains(textureName); } return false; } private static void EnsureLoaded() { if (State != CatalogState.Uninitialized) { return; } lock (Sync) { if (State != CatalogState.Uninitialized) { return; } string text = Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef", "manifest_extended"); if (!File.Exists(text)) { State = CatalogState.Unavailable; CreatureManagerPlugin.Log.LogWarning((object)("Vanilla asset manifest was not found at '" + text + "'. Reference owner sections may place vanilla assets under 'Unknown / Untracked'.")); return; } foreach (string item in File.ReadLines(text)) { int num = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase); if (num < 0) { continue; } string text2 = item.Substring(num + "path in bundle:".Length).Trim(); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { PrefabNames.Add(fileNameWithoutExtension); } else if (IsTextureAssetPath(text2)) { TextureNames.Add(fileNameWithoutExtension); } } } State = CatalogState.Loaded; CreatureManagerPlugin.Log.LogDebug((object)$"Loaded {PrefabNames.Count} vanilla prefab and {TextureNames.Count} texture names for reference owner sections."); } } private static bool IsTextureAssetPath(string assetPath) { if (!assetPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tga", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tif", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tiff", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".psd", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".exr", StringComparison.OrdinalIgnoreCase)) { return assetPath.EndsWith(".hdr", StringComparison.OrdinalIgnoreCase); } return true; } } internal static class CreatureAssetOwnerCatalog { private sealed class PluginResourceSnapshot { public string OwnerName { get; set; } = ""; public string PluginName { get; set; } = ""; public string PluginGuid { get; set; } = ""; public string AssemblyName { get; set; } = ""; public string[] ResourceNames { get; set; } = Array.Empty(); } private static readonly object Sync = new object(); private static readonly Dictionary AssetOwners = new Dictionary(StringComparer.OrdinalIgnoreCase); private static string? LoadedSignature; private static bool MappingsPrepared; internal static string GetOwnerName(string assetName) { PrepareMappings(); foreach (string item in EnumerateLookupCandidates(assetName)) { if (AssetOwners.TryGetValue(item, out string value) && !string.IsNullOrWhiteSpace(value)) { return value; } } return "Unknown / Untracked"; } internal static void InvalidateMappings() { lock (Sync) { MappingsPrepared = false; LoadedSignature = null; } } internal static void PrepareMappings() { if (!MappingsPrepared) { RefreshMappings(); } } internal static void RefreshMappings() { string text = BuildSignature(); if (MappingsPrepared && string.Equals(text, LoadedSignature, StringComparison.Ordinal)) { return; } lock (Sync) { if (MappingsPrepared && string.Equals(text, LoadedSignature, StringComparison.Ordinal)) { return; } if (string.Equals(text, LoadedSignature, StringComparison.Ordinal)) { MappingsPrepared = true; return; } AssetOwners.Clear(); List pluginResources = GetPluginResources(); foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { string text2 = ((Object)allLoadedAssetBundle).name ?? ""; if (text2.Length == 0) { continue; } string value = ResolveOwnerName(text2, pluginResources); if (string.IsNullOrWhiteSpace(value)) { continue; } string[] allAssetNames = allLoadedAssetBundle.GetAllAssetNames(); foreach (string text3 in allAssetNames) { if (IsTrackedAssetPath(text3)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text3); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { AssetOwners[fileNameWithoutExtension] = value; } } } } LoadedSignature = text; MappingsPrepared = true; CreatureManagerPlugin.Log.LogDebug((object)$"Tracked {AssetOwners.Count} asset owner mapping(s) for reference sections."); } } private static bool IsTrackedAssetPath(string assetPath) { if (!assetPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".asset", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tga", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tif", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".tiff", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".psd", StringComparison.OrdinalIgnoreCase) && !assetPath.EndsWith(".exr", StringComparison.OrdinalIgnoreCase)) { return assetPath.EndsWith(".hdr", StringComparison.OrdinalIgnoreCase); } return true; } private static IEnumerable EnumerateLookupCandidates(string assetName) { string normalizedName = (assetName ?? "").Replace("(Clone)", "").Trim(); if (normalizedName.Length != 0) { yield return normalizedName; int num = normalizedName.IndexOf(':'); if (num > 0) { yield return normalizedName.Substring(0, num); } } } private static List GetPluginResources() { return (from plugin in Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo) { string text = (pluginInfo.Metadata.Name ?? "").Trim(); string text2 = (pluginInfo.Metadata.GUID ?? "").Trim(); string assemblyName = ""; string[] resourceNames = Array.Empty(); try { assemblyName = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? ""; resourceNames = ((object)pluginInfo.Instance)?.GetType().Assembly.GetManifestResourceNames() ?? Array.Empty(); } catch { } return new PluginResourceSnapshot { OwnerName = ((text.Length > 0) ? text : text2), PluginName = text, PluginGuid = text2, AssemblyName = assemblyName, ResourceNames = resourceNames }; }) where plugin.OwnerName.Length > 0 select plugin).ToList(); } private static string ResolveOwnerName(string bundleName, List plugins) { PluginResourceSnapshot pluginResourceSnapshot = plugins.FirstOrDefault((PluginResourceSnapshot plugin) => plugin.ResourceNames.Any((string resourceName) => resourceName.EndsWith(bundleName, StringComparison.OrdinalIgnoreCase))); if (pluginResourceSnapshot != null) { return pluginResourceSnapshot.OwnerName; } string normalizedBundleName = NormalizeToken(Path.GetFileNameWithoutExtension(bundleName)); if (normalizedBundleName.Length == 0) { return ""; } return plugins.FirstOrDefault(delegate(PluginResourceSnapshot plugin) { string pluginToken = NormalizeToken(plugin.PluginName); string pluginToken2 = NormalizeToken(plugin.PluginGuid); string pluginToken3 = NormalizeToken(plugin.AssemblyName); return IsTokenMatch(normalizedBundleName, pluginToken) || IsTokenMatch(normalizedBundleName, pluginToken2) || IsTokenMatch(normalizedBundleName, pluginToken3); })?.OwnerName ?? ""; } private static bool IsTokenMatch(string bundleName, string pluginToken) { if (pluginToken.Length > 0) { if (bundleName.IndexOf(pluginToken, StringComparison.OrdinalIgnoreCase) < 0) { return pluginToken.IndexOf(bundleName, StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static string NormalizeToken(string value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (char c in value) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static string BuildSignature() { IEnumerable values = (from bundle in AssetBundle.GetAllLoadedAssetBundles() select ((Object)bundle).name ?? "" into name where name.Length > 0 select name).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase); IEnumerable values2 = Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo) { string text = pluginInfo.Metadata.Name ?? ""; string text2 = pluginInfo.Metadata.GUID ?? ""; string text3 = ""; try { text3 = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? ""; } catch { } return text2 + ":" + text + ":" + text3; }).OrderBy((string token) => token, StringComparer.OrdinalIgnoreCase); return string.Join("|", values) + "||" + string.Join("|", values2); } } internal static class CreatureReferenceWriter { private sealed class CreatureLoadoutUsage { public Dictionary> UsedBy { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public void Add(string creatureName, string slot) { if (!UsedBy.TryGetValue(creatureName, out HashSet value)) { value = new HashSet(StringComparer.OrdinalIgnoreCase); UsedBy[creatureName] = value; } value.Add(slot); } public List FormatUsedBy() { return (from entry in UsedBy.OrderBy>, string>((KeyValuePair> entry) => entry.Key, StringComparer.OrdinalIgnoreCase) select entry.Key + "(" + string.Join(", ", entry.Value.OrderBy((string slot) => slot, StringComparer.OrdinalIgnoreCase)) + ")").ToList(); } } private sealed class CreatureLoadoutReferenceEntry { public string Name { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; public string AttackAnimation { get; set; } = ""; public List UsedBy { get; set; } = new List(); } private sealed class ProjectileReferenceUsage { public GameObject? Prefab { get; set; } public HashSet UsedByAttacks { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); } private sealed class ProjectileReferenceEntry { public string Name { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; public List UsedByAttacks { get; set; } = new List(); public bool HasProjectile { get; set; } public string? ProjectileSpawnOnHit { get; set; } public bool HasSpawnAbility { get; set; } public List SpawnAbilityPrefabs { get; set; } = new List(); } private const string MainTextureProperty = "_MainTex"; internal static string BuildReferenceYaml() { return BuildSectionedPrefabYaml(CreaturePrefabRegistry.GetCreaturePrefabs(), "compact creature reference", AppendReferenceEntry); } internal static string BuildFullScaffoldYaml() { HashSet attackAnimationNames = GetKnownAttackAnimationNames(); return BuildSectionedPrefabYaml(CreaturePrefabRegistry.GetCreaturePrefabs(), "full creature scaffold", delegate(StringBuilder builder, GameObject prefab) { AppendFullScaffoldEntry(builder, prefab, attackAnimationNames); }); } internal static string BuildAttackReferenceYaml() { return BuildSectionedPrefabYaml(CreaturePrefabRegistry.GetAttackPrefabs(), "compact attack reference", AppendAttackReferenceEntry); } internal static string BuildAiReferenceYaml() { return BuildSectionedPrefabYaml(GetAiPrefabs(), "compact AI reference", AppendAiReferenceEntry); } internal static string BuildLevelVisualReferenceYaml() { return BuildSectionedPrefabYaml(GetLevelVisualPrefabs(), "level visual reference", AppendLevelVisualReferenceEntry); } internal static string BuildCreatureLoadoutReferenceText() { CreatureAssetOwnerCatalog.PrepareMappings(); List creatureLoadoutReferenceEntries = GetCreatureLoadoutReferenceEntries(); if (creatureLoadoutReferenceEntries.Count == 0) { return "[]\n"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Generated by CreatureManager (creature loadout reference)."); stringBuilder.AppendLine("# Lists likely creature/NPC equipment ItemDrop prefabs for humanoid.defaultItems and random* loadout fields."); stringBuilder.AppendLine("# Pure attack holder prefabs such as attack_*, *_shoot, *_shot, and *_melee are omitted."); stringBuilder.AppendLine("# ItemDrop prefabs with attack animations but no visible renderer are omitted as likely hidden attack holders."); stringBuilder.AppendLine("# Craftable recipe items are omitted as likely player items."); stringBuilder.AppendLine("# usedBy is gathered from non-player creature Humanoid loadout fields."); stringBuilder.AppendLine("# Owner sections are best-effort guesses from the Valheim manifest and loaded asset bundles."); stringBuilder.AppendLine(); bool flag = false; foreach (IGrouping item in creatureLoadoutReferenceEntries.OrderBy((CreatureLoadoutReferenceEntry entry) => CreatureReferenceSections.GetOwnerSortBucket(entry.OwnerName)).ThenBy((CreatureLoadoutReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((CreatureLoadoutReferenceEntry entry) => entry.Name, StringComparer.OrdinalIgnoreCase) .GroupBy((CreatureLoadoutReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase)) { if (flag) { stringBuilder.AppendLine(); } stringBuilder.Append("# ===== "); stringBuilder.Append(item.Key); stringBuilder.AppendLine(" ====="); bool flag2 = false; foreach (CreatureLoadoutReferenceEntry item2 in item) { if (flag2) { stringBuilder.AppendLine(); } stringBuilder.AppendLine(item2.Name); stringBuilder.AppendLine(" usedBy: " + FormatInlineList(item2.UsedBy)); if (!string.IsNullOrWhiteSpace(item2.AttackAnimation)) { stringBuilder.AppendLine(" attackAnimation: " + FormatYamlString(item2.AttackAnimation)); } flag2 = true; } flag = true; } return stringBuilder.ToString(); } internal static string BuildProjectileReferenceYaml() { CreatureAssetOwnerCatalog.PrepareMappings(); List projectileReferenceEntries = GetProjectileReferenceEntries(); if (projectileReferenceEntries.Count == 0) { return "[]\n"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Generated by CreatureManager (projectile reference)."); stringBuilder.AppendLine("# Lists projectile-like prefabs that can be inspected or copied into projectile.yml."); stringBuilder.AppendLine("# Includes prefabs with root Projectile, Aoe, TriggerSpawnAbility, or SpawnAbility components, plus prefabs referenced by ItemDrop attack projectile fields."); stringBuilder.AppendLine("# usedByAttacks is reference-only metadata gathered from loaded ItemDrop primary and secondary attacks; projectile.yml ignores it."); stringBuilder.AppendLine("# projectile and spawnAbility blocks are emitted only when those components exist on the prefab root."); stringBuilder.AppendLine("# Repeated spawnAbility.spawnPrefabs entries are compacted as Prefab:weight; an omitted weight means 1."); stringBuilder.AppendLine("# Owner sections are best-effort guesses from the Valheim manifest and loaded asset bundles."); stringBuilder.AppendLine(); bool flag = false; foreach (IGrouping item in projectileReferenceEntries.OrderBy((ProjectileReferenceEntry entry) => CreatureReferenceSections.GetOwnerSortBucket(entry.OwnerName)).ThenBy((ProjectileReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((ProjectileReferenceEntry entry) => entry.Name, StringComparer.OrdinalIgnoreCase) .GroupBy((ProjectileReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase)) { if (flag) { stringBuilder.AppendLine(); } stringBuilder.Append("# ===== "); stringBuilder.Append(item.Key); stringBuilder.AppendLine(" ====="); bool flag2 = false; foreach (ProjectileReferenceEntry item2 in item) { if (flag2) { stringBuilder.AppendLine(); } AppendListEntry(stringBuilder, 0, "prefab", item2.Name); AppendLine(stringBuilder, 1, "usedByAttacks: " + FormatInlineList(item2.UsedByAttacks)); if (item2.HasProjectile) { AppendLine(stringBuilder, 1, "projectile:"); AppendLine(stringBuilder, 2, "spawnOnHit: " + ((item2.ProjectileSpawnOnHit == null) ? "null" : FormatYamlString(item2.ProjectileSpawnOnHit))); } if (item2.HasSpawnAbility) { AppendLine(stringBuilder, 1, "spawnAbility:"); AppendLine(stringBuilder, 2, "spawnPrefabs: " + FormatWeightedPrefabInlineList(item2.SpawnAbilityPrefabs)); } flag2 = true; } flag = true; } return stringBuilder.ToString(); } private static List GetCreatureLoadoutReferenceEntries() { Dictionary dictionary = CollectCreatureLoadoutUsage(); HashSet craftableRecipeItemNames = GetCraftableRecipeItemNames(); List list = new List(); foreach (GameObject itemPrefab in CreaturePrefabRegistry.GetItemPrefabs()) { if ((Object)(object)itemPrefab == (Object)null) { continue; } string text = ((Object)itemPrefab).name ?? ""; if (text.Length == 0) { continue; } ItemDrop component = itemPrefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { SharedData shared = component.m_itemData.m_shared; bool hasRecipe = craftableRecipeItemNames.Contains(text); dictionary.TryGetValue(text, out var value); if (ShouldIncludeCreatureLoadoutItem(itemPrefab, shared, value, hasRecipe)) { list.Add(new CreatureLoadoutReferenceEntry { Name = text, OwnerName = CreaturePrefabOwnerResolver.GetOwnerName(text), AttackAnimation = (GetAttackAnimation(itemPrefab) ?? ""), UsedBy = (value?.FormatUsedBy() ?? new List()) }); } } } return list; } private static List GetProjectileReferenceEntries() { Dictionary usageByProjectile = CollectProjectileUsage(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (GameObject projectilePrefab in CreaturePrefabRegistry.GetProjectilePrefabs()) { if ((Object)(object)projectilePrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)projectilePrefab).name)) { dictionary[((Object)projectilePrefab).name] = projectilePrefab; } } SpawnAbility[] array = Resources.FindObjectsOfTypeAll(); foreach (SpawnAbility obj in array) { GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if (!((Object)(object)val == (Object)null) && !string.IsNullOrWhiteSpace(((Object)val).name)) { GameObject prefab = CreaturePrefabRegistry.GetPrefab(((Object)val).name); if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { dictionary[((Object)prefab).name] = prefab; } } } foreach (KeyValuePair item in usageByProjectile) { if ((Object)(object)item.Value.Prefab != (Object)null) { dictionary[item.Key] = item.Value.Prefab; continue; } GameObject prefab2 = CreaturePrefabRegistry.GetPrefab(item.Key); if ((Object)(object)prefab2 != (Object)null) { dictionary[item.Key] = prefab2; } } return dictionary.OrderBy, string>((KeyValuePair entry) => entry.Key, StringComparer.OrdinalIgnoreCase).Select(delegate(KeyValuePair entry) { usageByProjectile.TryGetValue(entry.Key, out ProjectileReferenceUsage value); Projectile component = entry.Value.GetComponent(); SpawnAbility component2 = entry.Value.GetComponent(); return new ProjectileReferenceEntry { Name = entry.Key, OwnerName = CreaturePrefabOwnerResolver.GetOwnerName(entry.Key), UsedByAttacks = (value?.UsedByAttacks.OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList() ?? new List()), HasProjectile = ((Object)(object)component != (Object)null), ProjectileSpawnOnHit = GetPrefabNameOrNull(component?.m_spawnOnHit), HasSpawnAbility = ((Object)(object)component2 != (Object)null), SpawnAbilityPrefabs = ((from val2 in component2?.m_spawnPrefab?.Where((GameObject val2) => (Object)(object)val2 != (Object)null && !string.IsNullOrWhiteSpace(((Object)val2).name)) select ((Object)val2).name).ToList() ?? new List()) }; }).ToList(); } private static Dictionary CollectCreatureLoadoutUsage() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (GameObject creaturePrefab in CreaturePrefabRegistry.GetCreaturePrefabs()) { Humanoid component = creaturePrefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { AddLoadoutItems(dictionary, ((Object)creaturePrefab).name, "defaultItems", component.m_defaultItems); AddLoadoutItems(dictionary, ((Object)creaturePrefab).name, "randomWeapon", component.m_randomWeapon); GetRandomArmorAndHairForOutput(creaturePrefab, component, out GameObject[] randomArmor, out GameObject[] randomHair); AddLoadoutItems(dictionary, ((Object)creaturePrefab).name, "randomArmor", randomArmor); AddLoadoutItems(dictionary, ((Object)creaturePrefab).name, "randomHair", randomHair); AddLoadoutItems(dictionary, ((Object)creaturePrefab).name, "randomShield", component.m_randomShield); AddRandomItems(dictionary, ((Object)creaturePrefab).name, component.m_randomItems); AddRandomSets(dictionary, ((Object)creaturePrefab).name, component.m_randomSets); } } return dictionary; } private static Dictionary CollectProjectileUsage() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (GameObject itemPrefab in CreaturePrefabRegistry.GetItemPrefabs()) { SharedData val = itemPrefab.GetComponent()?.m_itemData?.m_shared; if (val != null) { AddAttackProjectileUsage(dictionary, ((Object)itemPrefab).name, val.m_attack); AddAttackProjectileUsage(dictionary, ((Object)itemPrefab).name + ":secondary", val.m_secondaryAttack); } } return dictionary; } private static void AddAttackProjectileUsage(Dictionary usageByProjectile, string attackName, Attack? attack) { GameObject val = attack?.m_attackProjectile; string text = ((val != null) ? ((Object)val).name : null) ?? ""; if (text.Length != 0) { if (!usageByProjectile.TryGetValue(text, out ProjectileReferenceUsage value)) { value = (usageByProjectile[text] = new ProjectileReferenceUsage()); } ProjectileReferenceUsage projectileReferenceUsage2 = value; if (projectileReferenceUsage2.Prefab == null) { GameObject val2 = (projectileReferenceUsage2.Prefab = val); } value.UsedByAttacks.Add(attackName); } } private static string? GetPrefabNameOrNull(GameObject? prefab) { string text = ((prefab != null) ? ((Object)prefab).name : null) ?? ""; if (!string.IsNullOrWhiteSpace(text)) { return text; } return null; } private static void AddLoadoutItems(Dictionary usageByItem, string creatureName, string slot, IEnumerable? items) { if (items == null) { return; } foreach (GameObject item in items) { AddLoadoutItem(usageByItem, creatureName, slot, item); } } private static void AddRandomItems(Dictionary usageByItem, string creatureName, RandomItem[]? items) { if (items != null) { for (int i = 0; i < items.Length; i++) { AddLoadoutItem(usageByItem, creatureName, "randomItems", items[i]?.m_prefab); } } } private static void AddRandomSets(Dictionary usageByItem, string creatureName, ItemSet[]? sets) { if (sets != null) { foreach (ItemSet val in sets) { AddLoadoutItems(usageByItem, creatureName, "randomSets:" + val?.m_name, val?.m_items); } } } private static void AddLoadoutItem(Dictionary usageByItem, string creatureName, string slot, GameObject? item) { if ((Object)(object)item == (Object)null || (Object)(object)item.GetComponent() == (Object)null) { return; } string text = ((Object)item).name ?? ""; if (text.Length != 0) { if (!usageByItem.TryGetValue(text, out CreatureLoadoutUsage value)) { value = (usageByItem[text] = new CreatureLoadoutUsage()); } value.Add(creatureName, slot); } } private static HashSet GetCraftableRecipeItemNames() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (ObjectDB.instance?.m_recipes == null) { return hashSet; } foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (!((Object)(object)recipe?.m_item == (Object)null)) { GameObject gameObject = ((Component)recipe.m_item).gameObject; string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? ""; if (text.Length > 0) { hashSet.Add(text); } } } return hashSet; } private static bool ShouldIncludeCreatureLoadoutItem(GameObject itemPrefab, SharedData shared, CreatureLoadoutUsage? usage, bool hasRecipe) { if (hasRecipe) { return false; } if (IsLikelyAttackOnlyItem(itemPrefab, shared)) { return false; } if (!IsCreatureLoadoutItemType(((object)Unsafe.As(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString())) { return usage != null; } return true; } private static bool IsCreatureLoadoutItemType(string itemType) { switch (itemType) { case "OneHandedWeapon": case "TwoHandedWeapon": case "Helmet": case "Shield": case "Chest": case "Torch": case "Legs": case "Tool": case "TwoHandedWeaponLeft": case "Bow": case "Shoulder": case "Utility": case "Attach_Atgeir": return true; default: return false; } } private static bool IsLikelyAttackOnlyItem(GameObject itemPrefab, SharedData shared) { string text = (((Object)itemPrefab).name ?? "").ToLowerInvariant(); if (text.StartsWith("attack_", StringComparison.Ordinal) || text.Contains("_attack") || text.EndsWith("_shoot", StringComparison.Ordinal) || text.EndsWith("_shot", StringComparison.Ordinal) || text.EndsWith("_melee", StringComparison.Ordinal) || text.Contains("projectile")) { return true; } string? obj = GetAttackAnimation(itemPrefab) ?? ""; string text2 = ((object)Unsafe.As(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString(); if (obj.Length == 0) { return false; } if (!HasVisibleRenderer(itemPrefab)) { return true; } if (string.Equals(text2, "None", StringComparison.OrdinalIgnoreCase)) { return true; } if (!IsCreatureLoadoutItemType(text2) && !HasItemIcon(shared)) { return !HasVisibleRenderer(itemPrefab); } return false; } private static bool HasItemIcon(SharedData shared) { if (shared.m_icons != null) { return shared.m_icons.Any((Sprite icon) => (Object)(object)icon != (Object)null); } return false; } private static bool HasVisibleRenderer(GameObject prefab) { return prefab.GetComponentsInChildren(true).Any((Renderer renderer) => (Object)(object)renderer != (Object)null && !IsEffectRenderer(renderer)); } private static List GetAiPrefabs() { return (from prefab in CreaturePrefabRegistry.GetCreaturePrefabs() where (Object)(object)prefab.GetComponent() != (Object)null || (Object)(object)prefab.GetComponent() != (Object)null select prefab).ToList(); } private static List GetLevelVisualPrefabs() { return CreaturePrefabRegistry.GetCreaturePrefabs().Where(delegate(GameObject prefab) { LevelEffects componentInChildren = prefab.GetComponentInChildren(true); return componentInChildren?.m_levelSetups != null && componentInChildren.m_levelSetups.Count > 0; }).ToList(); } private static HashSet GetKnownAttackAnimationNames() { return (from animation in CreaturePrefabRegistry.GetAttackPrefabs().Select(GetAttackAnimation) where !string.IsNullOrWhiteSpace(animation) select (animation)).ToHashSet(StringComparer.OrdinalIgnoreCase); } private static List GetAvailableAttackAnimations(GameObject prefab, HashSet knownAttackAnimationNames) { List animationParameters = GetAnimationParameters(prefab); if (animationParameters.Count == 0 || knownAttackAnimationNames.Count == 0) { return GetConfiguredAttackAnimations(prefab); } List list = animationParameters.Where(knownAttackAnimationNames.Contains).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase) .ToList(); if (list.Count <= 0) { return GetConfiguredAttackAnimations(prefab); } return list; } private static List GetConfiguredAttackAnimations(GameObject prefab) { return (from animation in prefab.GetComponent()?.m_defaultItems?.Where((GameObject item) => (Object)(object)item != (Object)null).Select(GetAttackAnimation) where !string.IsNullOrWhiteSpace(animation) select (animation)).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string animation) => animation, StringComparer.OrdinalIgnoreCase).ToList() ?? new List(); } private static List GetAnimationParameters(GameObject prefab) { List animatorParameterNames = GetAnimatorParameterNames(prefab.GetComponent()?.m_animator ?? prefab.GetComponentInChildren(true)); if (animatorParameterNames.Count > 0) { return animatorParameterNames; } return GetInstantiatedAnimatorParameterNames(prefab); } private static List GetAnimatorParameterNames(Animator? animator) { if ((Object)(object)animator == (Object)null) { return new List(); } return (from parameter in animator.parameters select parameter.name into name where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); } private static List GetInstantiatedAnimatorParameterNames(GameObject prefab) { if ((Object)(object)prefab.GetComponentInChildren(true) == (Object)null) { return new List(); } GameObject val = null; bool forceDisableInit = ZNetView.m_forceDisableInit; try { ZNetView.m_forceDisableInit = true; val = Object.Instantiate(prefab); return GetAnimatorParameterNames(val.GetComponentInChildren(true)); } catch (Exception ex) { CreatureManagerPlugin.Log.LogDebug((object)("Failed to inspect animation parameters for '" + ((Object)prefab).name + "': " + ex.Message)); return new List(); } finally { ZNetView.m_forceDisableInit = forceDisableInit; if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } } } private static string BuildSectionedPrefabYaml(List prefabs, string artifactKind, Action appendEntry) { CreatureAssetOwnerCatalog.PrepareMappings(); if (prefabs.Count == 0) { return "[]\n"; } StringBuilder stringBuilder = new StringBuilder(); AppendGeneratedHeader(stringBuilder, artifactKind); CreatureReferenceSections.AppendPrefabSections(stringBuilder, prefabs, appendEntry); return stringBuilder.ToString(); } private static void AppendReferenceEntry(StringBuilder builder, GameObject prefab) { //IL_0049: 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) Character component = prefab.GetComponent(); AppendListEntry(builder, 0, "prefab", ((Object)prefab).name); AppendLine(builder, 1, "character:"); AppendLine(builder, 2, "name: " + FormatYamlString(component.m_name)); AppendLine(builder, 2, "faction: " + CreatureFactionManager.GetDisplayName(component.m_faction)); if (component.m_boss) { AppendLine(builder, 2, "boss: " + FormatBossTuple(component)); } AppendLine(builder, 2, "health: " + FormatFloat(component.m_health)); AppendReferenceDamageModifiers(builder, component.m_damageModifiers, 2); } private static void AppendFullScaffoldEntry(StringBuilder builder, GameObject prefab, HashSet attackAnimationNames) { //IL_0049: 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_024b: Unknown result type (might be due to invalid IL or missing references) Character component = prefab.GetComponent(); AppendListEntry(builder, 0, "prefab", ((Object)prefab).name); AppendLine(builder, 1, "character:"); AppendLine(builder, 2, "name: " + FormatYamlString(component.m_name)); AppendLine(builder, 2, "faction: " + CreatureFactionManager.GetDisplayName(component.m_faction)); AppendLine(builder, 2, "boss: " + FormatBossTuple(component)); AppendLine(builder, 2, "defeatSetGlobalKey: " + FormatYamlString(component.m_defeatSetGlobalKey)); AppendLine(builder, 2, "health: " + FormatTuple(component.m_health, component.m_regenAllHPTime)); AppendLine(builder, 2, "damageModifiers:"); AppendDamageModifiers(builder, component.m_damageModifiers, 3); AppendLine(builder, 2, "speed: " + FormatTuple(component.m_crouchSpeed, component.m_walkSpeed, component.m_speed, component.m_turnSpeed, component.m_runSpeed, component.m_runTurnSpeed, component.m_acceleration)); AppendLine(builder, 2, "jump: " + FormatTuple(component.m_jumpForce, component.m_jumpForceForward, component.m_jumpForceTiredFactor, component.m_airControl, component.m_jumpStaminaUsage)); AppendLine(builder, 2, "swim: " + FormatTuple(component.m_canSwim, component.m_swimDepth, component.m_swimSpeed, component.m_swimTurnSpeed, component.m_swimAcceleration)); AppendLine(builder, 2, "flight: " + FormatTuple(component.m_flying, component.m_flySlowSpeed, component.m_flyFastSpeed, component.m_flyTurnSpeed)); if ((Object)(object)prefab.GetComponent() != (Object)null || (Object)(object)prefab.GetComponent() != (Object)null) { AppendLine(builder, 1, "ai: " + FormatYamlString(((Object)prefab).name)); } AppendHumanoidBlock(builder, prefab, 1); AppendLine(builder, 1, "scale: " + FormatFloat(prefab.transform.localScale.x)); AppendLine(builder, 1, "textures: " + FormatInlineList(GetPrimaryTextureSlots(prefab))); AppendAppearance(builder, prefab, 1); AppendLine(builder, 1, "availableAttackAnimations: " + FormatInlineList(GetAvailableAttackAnimations(prefab, attackAnimationNames))); } private static void AppendAttackReferenceEntry(StringBuilder builder, GameObject prefab) { SharedData shared = prefab.GetComponent().m_itemData.m_shared; Attack attack = shared.m_attack; AppendListEntry(builder, 0, "prefab", ((Object)prefab).name); AppendAttackDamage(builder, 1, shared); AppendLine(builder, 1, "attack: " + FormatInlineList(new string[2] { ((object)Unsafe.As(ref attack.m_attackType)/*cast due to .constrained prefix*/).ToString(), attack.m_attackAnimation })); if ((Object)(object)shared.m_attackStatusEffect != (Object)null) { AppendLine(builder, 1, "statusEffect: " + FormatInlineList(new string[2] { ((Object)shared.m_attackStatusEffect).name, FormatFloat(shared.m_attackStatusEffectChance) })); } if ((Object)(object)attack.m_attackProjectile != (Object)null) { AppendLine(builder, 1, "projectile: " + FormatInlineList(new string[4] { ((Object)attack.m_attackProjectile).name, FormatFloat(attack.m_projectileVel), FormatFloat(attack.m_projectileAccuracy), attack.m_projectiles.ToString(CultureInfo.InvariantCulture) })); } AppendLine(builder, 1, "ai: " + FormatInlineList(new string[4] { FormatFloat(shared.m_aiAttackInterval), FormatFloat(shared.m_aiAttackRangeMin), FormatFloat(shared.m_aiAttackRange), FormatFloat(shared.m_aiAttackMaxAngle) })); } private static void AppendAiReferenceEntry(StringBuilder builder, GameObject prefab) { MonsterAI component = prefab.GetComponent(); AnimalAI component2 = prefab.GetComponent(); BaseAI val = (BaseAI)(((Object)(object)component != (Object)null) ? ((object)component) : ((object)component2)); if (!((Object)(object)val == (Object)null)) { AppendListEntry(builder, 0, "ai", ((Object)prefab).name); AppendLine(builder, 1, "baseAI:"); AppendLine(builder, 2, "senses: " + FormatInlineRawList(FormatFloat(val.m_viewRange), FormatFloat(val.m_viewAngle), FormatFloat(val.m_hearRange), FormatBool(val.m_mistVision))); AppendLine(builder, 2, "idleSound: " + FormatInlineRawList(FormatFloat(val.m_idleSoundInterval), FormatFloat(val.m_idleSoundChance))); AppendLine(builder, 2, "movement: " + FormatInlineRawList(FormatBool(val.m_patrol), ((object)Unsafe.As(ref val.m_pathAgentType)/*cast due to .constrained prefix*/).ToString(), FormatFloat(val.m_moveMinAngle), FormatBool(val.m_smoothMovement))); if (val.m_serpentMovement) { AppendLine(builder, 2, "serpent: " + FormatInlineRawList(FormatBool(val.m_serpentMovement), FormatFloat(val.m_serpentTurnRadius))); } AppendLine(builder, 2, "randomMove: " + FormatInlineRawList(FormatFloat(val.m_jumpInterval), FormatFloat(val.m_randomCircleInterval), FormatFloat(val.m_randomMoveInterval), FormatFloat(val.m_randomMoveRange))); if (val.m_randomFly) { AppendLine(builder, 2, "flight: " + FormatInlineRawList(FormatBool(val.m_randomFly), FormatFloat(val.m_chanceToTakeoff), FormatFloat(val.m_chanceToLand), FormatFloat(val.m_groundDuration), FormatFloat(val.m_airDuration), FormatFloat(val.m_maxLandAltitude), FormatFloat(val.m_takeoffTime), FormatFloat(val.m_flyAltitudeMin), FormatFloat(val.m_flyAltitudeMax), FormatFloat(val.m_flyAbsMinAltitude))); } AppendLine(builder, 2, "avoid: " + FormatInlineRawList(FormatBool(val.m_avoidFire), FormatBool(val.m_afraidOfFire), FormatBool(val.m_avoidWater), FormatBool(val.m_avoidLava), FormatBool(val.m_skipLavaTargets), FormatBool(val.m_avoidLavaFlee))); AppendLine(builder, 2, "flee: " + FormatInlineRawList(FormatFloat(val.m_fleeRange), FormatFloat(val.m_fleeAngle), FormatFloat(val.m_fleeInterval))); AppendLine(builder, 2, "aggressive: " + FormatInlineRawList(FormatBool(val.m_aggravatable), FormatBool(val.m_passiveAggresive))); if (HasAnyMessage(val)) { AppendLine(builder, 2, "messages: " + FormatInlineList(new string[3] { val.m_spawnMessage, val.m_deathMessage, val.m_alertedMessage })); } if ((Object)(object)component != (Object)null) { AppendLine(builder, 1, "monsterAI:"); AppendLine(builder, 2, "alertRange: " + FormatFloat(component.m_alertRange)); AppendLine(builder, 2, "hunt: " + FormatInlineRawList(FormatBool(component.m_enableHuntPlayer), FormatBool(component.m_attackPlayerObjects), component.m_privateAreaTriggerTreshold.ToString(CultureInfo.InvariantCulture))); AppendLine(builder, 2, "chase: " + FormatInlineRawList(FormatFloat(component.m_interceptTimeMin), FormatFloat(component.m_interceptTimeMax), FormatFloat(component.m_maxChaseDistance), FormatFloat(component.m_minAttackInterval))); AppendLine(builder, 2, "circle: " + FormatInlineRawList(FormatFloat(component.m_circleTargetInterval), FormatFloat(component.m_circleTargetDuration), FormatFloat(component.m_circleTargetDistance))); AppendLine(builder, 2, "hurtFlee: " + FormatInlineRawList(FormatBool(component.m_fleeIfHurtWhenTargetCantBeReached), FormatFloat(component.m_fleeUnreachableSinceAttacking), FormatFloat(component.m_fleeUnreachableSinceHurt), FormatBool(component.m_fleeIfNotAlerted), FormatFloat(component.m_fleeIfLowHealth), FormatFloat(component.m_fleeTimeSinceHurt), FormatBool(component.m_fleeInLava))); AppendLine(builder, 2, "charge: " + FormatInlineRawList(FormatBool(component.m_circulateWhileCharging), FormatBool(component.m_circulateWhileChargingFlying))); AppendLine(builder, 2, "sleep: " + FormatInlineRawList(FormatBool(component.m_sleeping), FormatFloat(component.m_wakeupRange), FormatBool(component.m_noiseWakeup), FormatFloat(component.m_maxNoiseWakeupRange), FormatFloat(component.m_wakeUpDelayMin), FormatFloat(component.m_wakeUpDelayMax), FormatFloat(component.m_fallAsleepDistance))); AppendLine(builder, 2, "avoidLand: " + FormatBool(component.m_avoidLand)); } } } private static void AppendLevelVisualReferenceEntry(StringBuilder builder, GameObject prefab) { //IL_0184: Unknown result type (might be due to invalid IL or missing references) LevelEffects componentInChildren = prefab.GetComponentInChildren(true); if (componentInChildren?.m_levelSetups == null || componentInChildren.m_levelSetups.Count == 0) { return; } AppendListEntry(builder, 0, "prefab", ((Object)prefab).name); AppendLine(builder, 1, "levelEffects:"); AppendLine(builder, 2, "component: " + FormatYamlString(GetObjectPath(prefab.transform, ((Component)componentInChildren).transform))); AppendLine(builder, 2, "mainRenderer: " + FormatYamlString(GetObjectPath(prefab.transform, ((Object)(object)componentInChildren.m_mainRender != (Object)null) ? ((Component)componentInChildren.m_mainRender).transform : null))); AppendLine(builder, 2, "baseEnableObject: " + FormatYamlString(GetObjectPath(prefab.transform, ((Object)(object)componentInChildren.m_baseEnableObject != (Object)null) ? componentInChildren.m_baseEnableObject.transform : null))); AppendLine(builder, 2, "levels:"); for (int i = 0; i < componentInChildren.m_levelSetups.Count; i++) { LevelSetup val = componentInChildren.m_levelSetups[i]; if (val != null) { List list = new List(); list.Add("scale: " + FormatFloat(val.m_scale)); list.Add("hsv: " + FormatInlineRawList(FormatFloat(val.m_hue), FormatFloat(val.m_saturation), FormatFloat(val.m_value))); List list2 = list; if (val.m_setEmissiveColor) { list2.Add("emissive: " + FormatColorTuple(val.m_emissiveColor)); } string objectPath = GetObjectPath(prefab.transform, ((Object)(object)val.m_enableObject != (Object)null) ? val.m_enableObject.transform : null); if (objectPath.Length > 0) { list2.Add("enableObject: " + FormatYamlString(objectPath)); } AppendLine(builder, 3, string.Format("{0}: {{ {1} }}", i + 2, string.Join(", ", list2))); } } } private static bool HasAnyMessage(BaseAI ai) { if (string.IsNullOrWhiteSpace(ai.m_spawnMessage) && string.IsNullOrWhiteSpace(ai.m_deathMessage)) { return !string.IsNullOrWhiteSpace(ai.m_alertedMessage); } return true; } private static void AppendAttackDamage(StringBuilder builder, int indent, SharedData shared) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) DamageTypes damages = shared.m_damages; List list = new List(); AddDamage(list, "damage", damages.m_damage); AddDamage(list, "blunt", damages.m_blunt); AddDamage(list, "slash", damages.m_slash); AddDamage(list, "pierce", damages.m_pierce); AddDamage(list, "chop", damages.m_chop); AddDamage(list, "pickaxe", damages.m_pickaxe); AddDamage(list, "fire", damages.m_fire); AddDamage(list, "frost", damages.m_frost); AddDamage(list, "lightning", damages.m_lightning); AddDamage(list, "poison", damages.m_poison); AddDamage(list, "spirit", damages.m_spirit); AddDamage(list, "attackForce", shared.m_attackForce); if (shared.m_toolTier != 0) { list.Add("toolTier: " + shared.m_toolTier.ToString(CultureInfo.InvariantCulture)); } if (list.Count != 0) { AppendLine(builder, indent, "damage: { " + string.Join(", ", list) + " }"); } } private static void AddDamage(List values, string key, float value) { if (!Mathf.Approximately(value, 0f)) { values.Add(key + ": " + FormatFloat(value)); } } private static void AppendDamageModifiers(StringBuilder builder, DamageModifiers modifiers, int indent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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) foreach (var (arg, val) in EnumerateDamageModifiers(modifiers)) { AppendLine(builder, indent, $"{arg}: {val}"); } } private static void AppendReferenceDamageModifiers(StringBuilder builder, DamageModifiers modifiers, int indent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) List<(string, DamageModifier)> list = (from entry in EnumerateDamageModifiers(modifiers) where !IsReferenceDefaultDamageModifier(entry.Key, entry.Value) select entry).ToList(); if (list.Count == 0) { return; } AppendLine(builder, indent, "damageModifiers:"); foreach (var (arg, val) in list) { AppendLine(builder, indent + 1, $"{arg}: {val}"); } } private static bool IsReferenceDefaultDamageModifier(string key, DamageModifier value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if ((int)value == 0) { return true; } if (key == "chop" || key == "pickaxe") { return (int)value == 4; } return false; } private static IEnumerable<(string Key, DamageModifier Value)> EnumerateDamageModifiers(DamageModifiers modifiers) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) yield return (Key: "blunt", Value: modifiers.m_blunt); yield return (Key: "slash", Value: modifiers.m_slash); yield return (Key: "pierce", Value: modifiers.m_pierce); yield return (Key: "chop", Value: modifiers.m_chop); yield return (Key: "pickaxe", Value: modifiers.m_pickaxe); yield return (Key: "fire", Value: modifiers.m_fire); yield return (Key: "frost", Value: modifiers.m_frost); yield return (Key: "lightning", Value: modifiers.m_lightning); yield return (Key: "poison", Value: modifiers.m_poison); yield return (Key: "spirit", Value: modifiers.m_spirit); } private static void AppendHumanoidBlock(StringBuilder builder, GameObject prefab, int indent) { Humanoid component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { AppendLine(builder, indent, "humanoid:"); AppendDefaultItems(builder, indent + 1, component.m_defaultItems); AppendItemList(builder, indent + 1, "randomWeapon", component.m_randomWeapon); GetRandomArmorAndHairForOutput(prefab, component, out GameObject[] randomArmor, out GameObject[] randomHair); AppendItemList(builder, indent + 1, "randomArmor", randomArmor); AppendItemList(builder, indent + 1, "randomHair", randomHair); AppendItemList(builder, indent + 1, "randomShield", component.m_randomShield); AppendRandomItems(builder, indent + 1, component.m_randomItems); AppendRandomSets(builder, indent + 1, component.m_randomSets); } } private static void GetRandomArmorAndHairForOutput(GameObject creaturePrefab, Humanoid humanoid, out GameObject[] randomArmor, out GameObject[] randomHair) { GameObject[] array = humanoid.m_randomArmor ?? Array.Empty(); if (CreatureDomainManager.TryGetRandomHairPrefabs(creaturePrefab, out IReadOnlyList prefabs)) { randomArmor = array; randomHair = prefabs.Where((GameObject item) => (Object)(object)item != (Object)null).ToArray(); } else if (CreatureDomainManager.IsConfirmedHairOnly(array)) { randomArmor = Array.Empty(); randomHair = array; } else { randomArmor = array; randomHair = Array.Empty(); } } private static void AppendDefaultItems(StringBuilder builder, int indent, GameObject[]? items) { List list = (from @group in items?.Where((GameObject item) => (Object)(object)item != (Object)null).GroupBy((GameObject item) => ((Object)item).name, StringComparer.OrdinalIgnoreCase) select @group.First()).ToList() ?? new List(); if (list.Count == 0) { AppendLine(builder, indent, "defaultItems: []"); return; } List list2 = (from item in list.OrderByDescending(CreaturePrefabRegistry.IsAttackItem).ThenBy((GameObject item) => ((Object)item).name, StringComparer.OrdinalIgnoreCase) select ((Object)item).name).ToList(); AppendLine(builder, indent, "defaultItems:"); foreach (string item in list2) { AppendLine(builder, indent + 1, "- " + FormatYamlString(item)); } } private static void AppendItemList(StringBuilder builder, int indent, string key, GameObject[]? items) { List list = (from item in items?.Where((GameObject item) => (Object)(object)item != (Object)null) select ((Object)item).name).ToList() ?? new List(); if (list.Count == 0) { AppendLine(builder, indent, key + ": []"); return; } AppendLine(builder, indent, key + ":"); foreach (string item in list) { AppendLine(builder, indent + 1, "- " + FormatYamlString(item)); } } private static void AppendRandomItems(StringBuilder builder, int indent, RandomItem[]? randomItems) { List list = (from item in randomItems?.Where((RandomItem item) => (Object)(object)item?.m_prefab != (Object)null) select ((Object)item.m_prefab).name + ", " + FormatFloat(item.m_chance)).ToList() ?? new List(); if (list.Count == 0) { AppendLine(builder, indent, "randomItems: []"); return; } AppendLine(builder, indent, "randomItems:"); foreach (string item in list) { AppendLine(builder, indent + 1, "- " + FormatYamlString(item)); } } private static void AppendRandomSets(StringBuilder builder, int indent, ItemSet[]? randomSets) { List list = (from set in randomSets?.Where((ItemSet set) => set?.m_items != null) select (set.m_name + ", " + string.Join(", ", from item in set.m_items where (Object)(object)item != (Object)null select ((Object)item).name)).TrimEnd(' ', ',') into line where line.IndexOf(',') >= 0 select line).ToList() ?? new List(); if (list.Count == 0) { AppendLine(builder, indent, "randomSets: []"); return; } AppendLine(builder, indent, "randomSets:"); foreach (string item in list) { AppendLine(builder, indent + 1, "- " + FormatYamlString(item)); } } private static void AppendAppearance(StringBuilder builder, GameObject prefab, int indent) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) VisEquipment component = prefab.GetComponent(); Humanoid component2 = prefab.GetComponent(); if ((Object)(object)component == (Object)null && (Object)(object)component2 == (Object)null) { return; } int num = component?.m_modelIndex ?? 0; Vector3 color = component?.m_skinColor ?? Vector3.one; Vector3 color2 = component?.m_hairColor ?? Vector3.one; string text = FirstNonEmpty(component2?.m_hairItem, component?.m_hairItem); string text2 = FirstNonEmpty(component2?.m_beardItem, component?.m_beardItem); bool flag = !IsWhite(color2); bool flag2 = !IsWhite(color); if (text.Length != 0 || text2.Length != 0 || flag || flag2 || num != 0) { AppendLine(builder, indent, "appearance:"); if (text.Length > 0) { AppendLine(builder, indent + 1, "hair: " + FormatYamlString(text)); } if (text2.Length > 0) { AppendLine(builder, indent + 1, "beard: " + FormatYamlString(text2)); } if (flag) { AppendLine(builder, indent + 1, "hairColor: " + FormatYamlString(FormatColor(color2))); } if (flag2) { AppendLine(builder, indent + 1, "skinColor: " + FormatYamlString(FormatColor(color))); } if (num != 0) { AppendLine(builder, indent + 1, "modelIndex: " + num.ToString(CultureInfo.InvariantCulture)); } } } private static string FirstNonEmpty(string? primary, string? fallback) { if (string.IsNullOrWhiteSpace(primary)) { if (string.IsNullOrWhiteSpace(fallback)) { return ""; } return fallback; } return primary; } private static List GetPrimaryTextureSlots(GameObject prefab) { Renderer val = FindPrimaryRenderer(prefab); if ((Object)(object)val == (Object)null) { return new List(); } List list = new List(); Material[] sharedMaterials = val.sharedMaterials; for (int i = 0; i < sharedMaterials.Length; i++) { if (TryGetMainTexture(sharedMaterials[i], out Texture texture)) { list.Add($"{((Object)val).name}:{i}:{((Object)texture).name}"); } } return list; } private static Renderer? FindPrimaryRenderer(GameObject prefab) { return (from entry in (from renderer in prefab.GetComponentsInChildren(true) where (Object)(object)renderer != (Object)null && HasTextureMaterial(renderer) && !IsEffectRenderer(renderer) select new { Renderer = renderer, Score = GetPrimaryRendererScore(prefab, renderer) } into entry where entry.Score > -1073741824 orderby entry.Score descending select entry).ThenBy(entry => GetTransformPath(prefab.transform, ((Component)entry.Renderer).transform), StringComparer.OrdinalIgnoreCase) select entry.Renderer).FirstOrDefault(); } private static bool HasTextureMaterial(Renderer renderer) { Texture texture; return renderer.sharedMaterials.Any((Material material) => TryGetMainTexture(material, out texture)); } private static bool IsEffectRenderer(Renderer renderer) { string name = ((object)renderer).GetType().Name; if (string.Equals(name, "ParticleSystemRenderer", StringComparison.Ordinal) || string.Equals(name, "TrailRenderer", StringComparison.Ordinal) || string.Equals(name, "LineRenderer", StringComparison.Ordinal)) { return true; } string text = (((Object)renderer).name + "/" + GetTransformPath(null, ((Component)renderer).transform)).ToLowerInvariant(); return new string[17] { "fx", "vfx", "sfx", "particle", "particles", "smoke", "mist", "splash", "splsh", "flies", "ooz", "wet", "water", "dust", "fire", "flame", "glow" }.Any((string token) => text.Contains(token)); } private static int GetPrimaryRendererScore(GameObject prefab, Renderer renderer) { string obj = ((Object)renderer).name ?? ""; string transformPath = GetTransformPath(prefab.transform, ((Component)renderer).transform); string text = (obj + "/" + transformPath).ToLowerInvariant(); string text2 = ((Object)prefab).name ?? ""; int num = 0; if (renderer is SkinnedMeshRenderer) { num += 100; } if (string.Equals(obj, text2, StringComparison.OrdinalIgnoreCase)) { num += 120; } else if (text2.Length > 0 && text.Contains(text2.ToLowerInvariant())) { num += 80; } if (ContainsAny(text, "body", "character", "model", "mesh", "main", "root")) { num += 50; } if (ContainsAny(text, "lod", "ragdoll", "collider", "shadow")) { num -= 40; } return num; } private static bool ContainsAny(string value, params string[] tokens) { return tokens.Any((string token) => value.Contains(token)); } private static string GetTransformPath(Transform? root, Transform transform) { Stack stack = new Stack(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { stack.Push(((Object)val).name); val = val.parent; } return string.Join("/", stack); } private static string GetObjectPath(Transform root, Transform? transform) { if ((Object)(object)transform == (Object)null) { return ""; } string transformPath = GetTransformPath(root, transform); if (!string.IsNullOrWhiteSpace(transformPath)) { return transformPath; } return "."; } private static bool TryGetMainTexture(Material? material, out Texture texture) { texture = null; if ((Object)(object)material == (Object)null || !material.HasProperty("_MainTex")) { return false; } Texture texture2 = material.GetTexture("_MainTex"); if ((Object)(object)texture2 == (Object)null) { return false; } texture = texture2; return true; } private static string? GetAttackAnimation(GameObject item) { return item.GetComponent()?.m_itemData?.m_shared?.m_attack?.m_attackAnimation; } private static void AppendGeneratedHeader(StringBuilder builder, string artifactKind) { builder.AppendLine("# Generated by CreatureManager (" + artifactKind + ")."); if (artifactKind.IndexOf("AI", StringComparison.OrdinalIgnoreCase) >= 0) { builder.AppendLine("# Use 'ai: prefabName' in creatures.yml to copy an existing creature prefab's AI directly."); builder.AppendLine("# Copy entries into ai.yml or ai_*.yml only when creating a custom reusable preset."); builder.AppendLine("# See ai.yml for schema notes."); } else if (artifactKind.IndexOf("attack", StringComparison.OrdinalIgnoreCase) >= 0) { builder.AppendLine("# Copy entries into attacks.yml or attacks_*.yml before editing."); builder.AppendLine("# See attacks.yml for schema notes."); } else if (artifactKind.IndexOf("level visual", StringComparison.OrdinalIgnoreCase) >= 0) { builder.AppendLine("# Lists loaded creature prefabs with LevelEffects setup data, grouped by best-effort owner."); builder.AppendLine("# Vanilla level 2 uses the first setup, level 3 uses the second setup, and so on."); builder.AppendLine("# hsv is [hue, saturation, value]. enableObject is the optional object toggled on for that level."); } else { builder.AppendLine("# Copy entries into creatures.yml or creatures_*.yml before editing."); builder.AppendLine((artifactKind.IndexOf("full", StringComparison.OrdinalIgnoreCase) >= 0) ? "# Full scaffold includes humanoid loadout fields and body textures entries." : "# Compact reference omits humanoid loadouts and texture entries; use cm:full creature for those."); if (artifactKind.IndexOf("full", StringComparison.OrdinalIgnoreCase) >= 0) { builder.AppendLine("# A randomArmor list is emitted as randomHair only when every entry is a confirmed attachable hair item."); } builder.AppendLine("# See creatures.yml for schema notes."); builder.AppendLine("# Configure attack prefab details in attacks.yml."); builder.AppendLine("# Configure reusable MonsterAI presets in ai.yml."); builder.AppendLine("# Drop, spawn, and detailed attack fields are intentionally not scaffolded here."); } builder.AppendLine(); } private static void AppendListEntry(StringBuilder builder, int indent, string key, string value) { AppendLine(builder, indent, "- " + key + ": " + FormatYamlString(value)); } private static void AppendLine(StringBuilder builder, int indent, string line) { builder.Append(' ', indent * 2); builder.AppendLine(line); } private static string FormatBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatTuple(params float[] values) { return string.Join(", ", values.Select(FormatFloat)); } private static string FormatTuple(bool first, params float[] values) { return FormatBool(first) + ", " + FormatTuple(values); } private static string FormatBossTuple(Character character) { return FormatBool(character.m_boss) + ", " + FormatBool(character.m_dontHideBossHud) + ", " + FormatYamlString(character.m_bossEvent); } private static string FormatColor(Vector3 color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.RoundToInt(Mathf.Clamp01(color.x) * 255f); int num2 = Mathf.RoundToInt(Mathf.Clamp01(color.y) * 255f); int num3 = Mathf.RoundToInt(Mathf.Clamp01(color.z) * 255f); return $"#{num:X2}{num2:X2}{num3:X2}"; } private static string FormatColorTuple(Color color) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) return FormatInlineRawList(FormatFloat(color.r), FormatFloat(color.g), FormatFloat(color.b), FormatFloat(color.a)); } private static bool IsWhite(Vector3 color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Approximately(color.x, 1f) && Mathf.Approximately(color.y, 1f)) { return Mathf.Approximately(color.z, 1f); } return false; } private static string FormatInlineList(IEnumerable values) { string[] value = values.Select(FormatYamlString).ToArray(); return "[" + string.Join(", ", value) + "]"; } private static string FormatWeightedPrefabInlineList(IEnumerable prefabNames) { List list = new List(); Dictionary countsByName = new Dictionary(StringComparer.Ordinal); foreach (string prefabName in prefabNames) { if (countsByName.TryGetValue(prefabName, out var value)) { countsByName[prefabName] = value + 1; continue; } list.Add(prefabName); countsByName[prefabName] = 1; } return FormatInlineList(list.Select((string prefabName) => (countsByName[prefabName] != 1 || prefabName.IndexOf(':') >= 0) ? $"{prefabName}:{countsByName[prefabName]}" : prefabName)); } private static string FormatInlineRawList(params string[] values) { return "[" + string.Join(", ", values) + "]"; } private static string FormatYamlString(string? value) { if (value == null) { value = ""; } if (value.Length == 0) { return "''"; } if (!char.IsWhiteSpace(value[0]) && !char.IsWhiteSpace(value[value.Length - 1]) && value.IndexOfAny(new char[17] { ':', '#', '{', '}', '[', ']', ',', '\'', '"', '&', '*', '!', '|', '>', '%', '@', '`' }) < 0 && value[0] != '-' && value[0] != '?' && !string.Equals(value, "null", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) { return value; } return "'" + value.Replace("'", "''") + "'"; } } internal static class CreatureServerLocalization { private readonly struct OriginalTranslation { internal bool Existed { get; } internal string? Value { get; } internal OriginalTranslation(bool existed, string? value) { Existed = existed; Value = value; } } private const string SyncedPayloadKey = "LocalizationBundle"; private const long ReloadDebounceTicks = 2500000L; private const long SyncedApplyDebounceTicks = 1000000L; private const int MaxSyncedPayloadBytes = 2097152; private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private static readonly object Sync = new object(); private static readonly HashSet KnownValheimLanguages = new HashSet(StringComparer.OrdinalIgnoreCase) { "Chinese", "Chinese_Trad", "Czech", "Danish", "Dutch", "English", "Finnish", "French", "German", "Greek", "Hungarian", "Italian", "Japanese", "Korean", "Norwegian", "Polish", "Portuguese_European", "Portuguese_Brazilian", "Russian", "Slovak", "Spanish", "Swedish", "Turkish" }; private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).DisableAliases().Build(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).WithDuplicateKeyChecking().Build(); private static ConfigSync? ConfigSync; private static CustomSyncedValue? SyncedPayload; private static FileSystemWatcher? Watcher; private static DateTime PendingDiskReloadTime = DateTime.MaxValue; private static DateTime PendingSyncedApplyTime = DateTime.MaxValue; private static bool DiskReloadPending; private static bool SyncedApplyPending; private static bool SuppressSyncedApply; private static bool AwaitingRemotePayload; private static bool WatcherResetPending; private static bool TemplatesEnsuredThisSession; private static ServerLocalizationPayload ActivePayload = CreateEmptyPayload(); private static Localization? AppliedLocalization; private static string AppliedLanguage = ""; private static readonly Dictionary OriginalTranslations = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary LastAppliedTranslations = new Dictionary(StringComparer.Ordinal); internal static string LocalizationDirectoryPath => Path.Combine(CreatureDomainManager.ConfigDirectoryPath, "localization"); internal static void Initialize(ConfigSync configSync) { ConfigSync = configSync; SyncedPayload = new CustomSyncedValue(configSync, "LocalizationBundle", "", 100); SyncedPayload.ValueChanged += OnSyncedPayloadChanged; configSync.SourceOfTruthChanged += OnSourceOfTruthChanged; if (configSync.IsSourceOfTruth) { ReloadFromDiskAndSync(); SetupWatcher(); } else if (!string.IsNullOrWhiteSpace(SyncedPayload.Value)) { RequestSyncedPayloadApply(); } } internal static void Dispose() { RestoreAppliedTranslations(); Watcher?.Dispose(); Watcher = null; if (SyncedPayload != null) { SyncedPayload.ValueChanged -= OnSyncedPayloadChanged; SyncedPayload = null; } if (ConfigSync != null) { ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged; ConfigSync = null; } DiskReloadPending = false; SyncedApplyPending = false; SuppressSyncedApply = false; AwaitingRemotePayload = false; WatcherResetPending = false; TemplatesEnsuredThisSession = false; PendingDiskReloadTime = DateTime.MaxValue; PendingSyncedApplyTime = DateTime.MaxValue; lock (Sync) { ActivePayload = CreateEmptyPayload(); } } internal static void Update() { DateTime utcNow = DateTime.UtcNow; if (DiskReloadPending && utcNow >= PendingDiskReloadTime) { DiskReloadPending = false; PendingDiskReloadTime = DateTime.MaxValue; ConfigSync? configSync = ConfigSync; if (configSync != null && configSync.IsSourceOfTruth) { ReloadFromDiskAndSync(); if (WatcherResetPending) { WatcherResetPending = false; SetupWatcher(); } } } if (SyncedApplyPending && utcNow >= PendingSyncedApplyTime) { SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; ConfigSync? configSync2 = ConfigSync; if (configSync2 != null && !configSync2.IsSourceOfTruth && !AwaitingRemotePayload) { ApplySyncedPayload(); } } } internal static void BeforeLanguageSetup(Localization localization) { if (IsLiveLocalization(localization)) { RestoreAppliedTranslations(localization); } } internal static void ApplyCurrentLocalization() { Localization instance = Localization.m_instance; if (instance != null) { ApplyCurrentLocalization(instance, instance.GetSelectedLanguage()); } } internal static void ApplyCurrentLocalization(Localization localization, string? language) { if (!IsLiveLocalization(localization)) { return; } string text = language?.Trim() ?? ""; if (text.Length == 0) { text = "English"; } if (AppliedLocalization != localization || !AppliedLanguage.Equals(text, StringComparison.OrdinalIgnoreCase)) { RestoreAppliedTranslations(); AppliedLocalization = localization; AppliedLanguage = text; } Dictionary dictionary; lock (Sync) { dictionary = CreatureYaml.BuildLocalizationForLanguage(ActivePayload, text); } bool flag = RestoreRemovedTranslations(localization, dictionary.Keys); foreach (KeyValuePair item in dictionary) { flag |= ApplyTranslation(localization, item.Key, item.Value); } if (flag || dictionary.Count > 0) { localization.m_cache.EvictAll(); } } internal static void OnWorldShutdown() { ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { DiskReloadPending = false; SyncedApplyPending = false; AwaitingRemotePayload = true; PendingDiskReloadTime = DateTime.MaxValue; PendingSyncedApplyTime = DateTime.MaxValue; ClearActivePayloadAndRestore(); } } private static bool IsLiveLocalization(Localization localization) { Localization instance = Localization.m_instance; if (instance != null) { return localization == instance; } return false; } private static void ReloadFromDiskAndSync() { ConfigSync? configSync = ConfigSync; if (configSync == null || !configSync.IsSourceOfTruth) { return; } if (!TryLoadPayloadFromDisk(out ServerLocalizationPayload payload) || !TrySerializeAndVerifyPayload(payload, out string serialized, out ServerLocalizationPayload verified)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the last-known-good server localization because at least one localization file is invalid."); return; } if (SyncedPayload == null) { CreatureManagerPlugin.Log.LogError((object)"Failed to publish server localization because ServerSync is not initialized."); return; } if (!string.Equals(SyncedPayload.Value, serialized, StringComparison.Ordinal)) { SuppressSyncedApply = true; try { SyncedPayload.AssignLocalValue(serialized); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to publish server localization; keeping the last-known-good synchronized value: " + ex.Message)); return; } finally { SuppressSyncedApply = false; } } lock (Sync) { ActivePayload = verified; } ApplyCurrentLocalization(); NotifyLocalizationChanged(); int num = verified.Languages?.Count ?? 0; int num2 = verified.Languages?.Values.Sum((Dictionary map) => map.Count) ?? 0; CreatureManagerPlugin.Log.LogInfo((object)$"Loaded and synchronized {num2} localization token(s) across {num} language file(s)."); } private static bool TryLoadPayloadFromDisk(out ServerLocalizationPayload payload) { payload = CreateEmptyPayload(); try { EnsureLocalizationDirectoryAndTemplates(); Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (string item in Directory.EnumerateFiles(LocalizationDirectoryPath, "*.*", SearchOption.TopDirectoryOnly).Where(IsLocalizationFile).OrderBy((string path) => path, StringComparer.OrdinalIgnoreCase)) { if (new FileInfo(item).Length > 2097152) { throw new InvalidDataException($"Localization file '{item}' exceeds the {2097152}-byte payload safety limit."); } string text = Path.GetFileNameWithoutExtension(item).Trim(); if (text.Length == 0 || text.Length > 64) { throw new InvalidDataException("Localization file '" + item + "' must use a Valheim language name from 1 to 64 characters."); } if (!KnownValheimLanguages.Contains(text)) { CreatureManagerPlugin.Log.LogWarning((object)("Localization file '" + item + "' uses unrecognized language name '" + text + "'. It will synchronize, but only clients selecting that exact language name can use it.")); } if (dictionary.ContainsKey(text)) { throw new InvalidDataException("More than one localization file resolves to language '" + text + "'. Keep only one .yml or .yaml file per language."); } if (!CreatureYaml.TryReadLocalizationMap(File.ReadAllText(item, Encoding.UTF8), item, out Dictionary translations)) { return false; } dictionary[text] = translations; } payload = new ServerLocalizationPayload { Version = 1, Languages = dictionary }; WarnAboutMissingEnglishFallback(payload); return CreatureYaml.TryNormalizeLocalizationPayload(payload, "local server localization", out payload); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to load server localization files: " + ex.Message)); payload = CreateEmptyPayload(); return false; } } private static void WarnAboutMissingEnglishFallback(ServerLocalizationPayload payload) { Dictionary> dictionary = payload.Languages ?? new Dictionary>(StringComparer.OrdinalIgnoreCase); Dictionary value; HashSet englishTokens = (dictionary.TryGetValue("English", out value) ? new HashSet(value.Keys, StringComparer.Ordinal) : new HashSet(StringComparer.Ordinal)); int num = dictionary.Where((KeyValuePair> entry) => !entry.Key.Equals("English", StringComparison.OrdinalIgnoreCase)).SelectMany((KeyValuePair> entry) => entry.Value.Keys).Distinct(StringComparer.Ordinal) .Count((string token) => !englishTokens.Contains(token)); if (num > 0) { CreatureManagerPlugin.Log.LogWarning((object)$"Server localization has {num} token(s) without an English fallback. Clients using an unconfigured language may see raw [token] text."); } } private static bool TrySerializeAndVerifyPayload(ServerLocalizationPayload payload, out string serialized, out ServerLocalizationPayload verified) { serialized = ""; verified = CreateEmptyPayload(); try { serialized = Serializer.Serialize(payload); if (Encoding.UTF8.GetByteCount(serialized) > 2097152) { CreatureManagerPlugin.Log.LogError((object)$"Server localization payload exceeds the {2097152}-byte safety limit."); return false; } } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to serialize server localization: " + ex.Message)); return false; } return TryDeserializePayload(serialized, "local server localization round-trip", out verified); } private static bool TryDeserializePayload(string serialized, string source, out ServerLocalizationPayload payload) { payload = CreateEmptyPayload(); if (string.IsNullOrWhiteSpace(serialized)) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": localization payload is empty.")); return false; } if (Encoding.UTF8.GetByteCount(serialized) > 2097152) { CreatureManagerPlugin.Log.LogError((object)$"Failed to read {source}: localization payload exceeds the {2097152}-byte safety limit."); return false; } try { return CreatureYaml.TryNormalizeLocalizationPayload(Deserializer.Deserialize(serialized), source, out payload); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read " + source + ": " + ex.Message)); return false; } } private static void ApplySyncedPayload() { if (SyncedPayload == null) { return; } ConfigSync? configSync = ConfigSync; if (configSync == null || configSync.IsSourceOfTruth) { return; } if (!TryDeserializePayload(SyncedPayload.Value, "server synchronized localization", out ServerLocalizationPayload payload)) { CreatureManagerPlugin.Log.LogWarning((object)"Keeping the last-known-good server localization because the synchronized payload is invalid."); return; } lock (Sync) { ActivePayload = payload; } ApplyCurrentLocalization(); NotifyLocalizationChanged(); } private static void OnSyncedPayloadChanged() { if (!SuppressSyncedApply) { ConfigSync? configSync = ConfigSync; if (configSync != null && !configSync.IsSourceOfTruth) { AwaitingRemotePayload = false; RequestSyncedPayloadApply(); } } } private static void RequestSyncedPayloadApply() { SyncedApplyPending = true; PendingSyncedApplyTime = DateTime.UtcNow.AddTicks(1000000L); } private static void OnSourceOfTruthChanged(bool isSourceOfTruth) { if (isSourceOfTruth) { AwaitingRemotePayload = false; SyncedApplyPending = false; PendingSyncedApplyTime = DateTime.MaxValue; ClearActivePayloadAndRestore(); NotifyLocalizationChanged(); ReloadFromDiskAndSync(); SetupWatcher(); } else { DiskReloadPending = false; SyncedApplyPending = false; AwaitingRemotePayload = true; PendingDiskReloadTime = DateTime.MaxValue; PendingSyncedApplyTime = DateTime.MaxValue; Watcher?.Dispose(); Watcher = null; ClearActivePayloadAndRestore(); NotifyLocalizationChanged(); } } private static void SetupWatcher() { Watcher?.Dispose(); Watcher = null; ConfigSync? configSync = ConfigSync; if (configSync == null || !configSync.IsSourceOfTruth) { return; } WatcherResetPending = false; try { EnsureLocalizationDirectoryAndTemplates(); Watcher = new FileSystemWatcher(LocalizationDirectoryPath) { IncludeSubdirectories = false, Filter = "*.*", SynchronizingObject = ThreadingHelper.SynchronizingObject }; Watcher.Changed += OnLocalizationFileChanged; Watcher.Created += OnLocalizationFileChanged; Watcher.Deleted += OnLocalizationFileChanged; Watcher.Renamed += OnLocalizationFileChanged; Watcher.Error += OnLocalizationWatcherError; Watcher.EnableRaisingEvents = true; } catch (Exception ex) { Watcher?.Dispose(); Watcher = null; CreatureManagerPlugin.Log.LogError((object)("Failed to watch server localization files: " + ex.Message)); } } private static void OnLocalizationFileChanged(object sender, FileSystemEventArgs args) { if (IsLocalizationFile(args.FullPath) || (args is RenamedEventArgs e && IsLocalizationFile(e.OldFullPath))) { ConfigSync? configSync = ConfigSync; if (configSync != null && configSync.IsSourceOfTruth) { DiskReloadPending = true; PendingDiskReloadTime = DateTime.UtcNow.AddTicks(2500000L); } } } private static bool IsLocalizationFile(string path) { string extension = Path.GetExtension(path); if (!extension.Equals(".yml", StringComparison.OrdinalIgnoreCase)) { return extension.Equals(".yaml", StringComparison.OrdinalIgnoreCase); } return true; } private static void OnLocalizationWatcherError(object sender, ErrorEventArgs args) { ConfigSync? configSync = ConfigSync; if (configSync != null && configSync.IsSourceOfTruth) { CreatureManagerPlugin.Log.LogWarning((object)("Server localization watcher lost file events and will be rebuilt: " + args.GetException().Message)); WatcherResetPending = true; DiskReloadPending = true; PendingDiskReloadTime = DateTime.UtcNow.AddTicks(2500000L); } } private static void EnsureLocalizationDirectoryAndTemplates() { Directory.CreateDirectory(LocalizationDirectoryPath); if (!TemplatesEnsuredThisSession) { string path = Path.Combine(LocalizationDirectoryPath, "English.yml"); if (!File.Exists(path)) { File.WriteAllText(path, BuildTemplate("$rootwitch: Root Witch\n$vincent: Vincent\n$bonebeard: Bonebeard\n$vitrfell: Vitrfell"), Utf8WithoutBom); } string path2 = Path.Combine(LocalizationDirectoryPath, "Korean.yml"); if (!File.Exists(path2)) { File.WriteAllText(path2, BuildTemplate("$rootwitch: 뿌리 마녀\n$vincent: 빈센트\n$bonebeard: 본비어드\n$vitrfell: 비트르펠"), Utf8WithoutBom); } TemplatesEnsuredThisSession = true; } } private static string BuildTemplate(string translations) { return "# CreatureManager server-authoritative localization. The file name must be a Valheim language name.\n# English is the fallback; the selected client language overrides matching English tokens.\n# Keys may have one leading '$'; reference them as $token in creatures.yml or other localized fields.\n# Remote clients ignore their local files while connected. Redefining an existing token changes every use of it.\n# Localization is a global live rule; synchronized edits refresh the current language and UI immediately.\n# These default names are used by creatures.sample.yml.\n" + translations + "\n"; } private static bool ApplyTranslation(Localization localization, string token, string text) { string value; bool flag = localization.m_translations.TryGetValue(token, out value); if (!OriginalTranslations.ContainsKey(token) || (LastAppliedTranslations.TryGetValue(token, out string value2) && (!flag || !string.Equals(value, value2, StringComparison.Ordinal)))) { OriginalTranslations[token] = new OriginalTranslation(flag, value); } bool result = !flag || !string.Equals(value, text, StringComparison.Ordinal); localization.m_translations[token] = text; LastAppliedTranslations[token] = text; return result; } private static bool RestoreRemovedTranslations(Localization localization, IEnumerable currentTokens) { HashSet current = new HashSet(currentTokens, StringComparer.Ordinal); bool flag = false; string[] array = LastAppliedTranslations.Keys.Where((string token) => !current.Contains(token)).ToArray(); foreach (string text in array) { flag |= RestoreTranslationIfOwned(localization, text); LastAppliedTranslations.Remove(text); OriginalTranslations.Remove(text); } return flag; } private static void RestoreAppliedTranslations() { RestoreAppliedTranslations(AppliedLocalization); } private static void RestoreAppliedTranslations(Localization? localization) { if (localization == null) { ClearAppliedTranslationState(); return; } bool flag = false; string[] array = LastAppliedTranslations.Keys.ToArray(); foreach (string token in array) { flag |= RestoreTranslationIfOwned(localization, token); } if (flag) { localization.m_cache.EvictAll(); } ClearAppliedTranslationState(); } private static bool RestoreTranslationIfOwned(Localization localization, string token) { if (!LastAppliedTranslations.TryGetValue(token, out string value) || !localization.m_translations.TryGetValue(token, out var value2) || !string.Equals(value2, value, StringComparison.Ordinal)) { return false; } if (OriginalTranslations.TryGetValue(token, out var value3) && value3.Existed) { localization.m_translations[token] = value3.Value ?? ""; } else { localization.m_translations.Remove(token); } return true; } private static void ClearAppliedTranslationState() { AppliedLocalization = null; AppliedLanguage = ""; OriginalTranslations.Clear(); LastAppliedTranslations.Clear(); } private static void ClearActivePayloadAndRestore() { lock (Sync) { ActivePayload = CreateEmptyPayload(); } RestoreAppliedTranslations(); } private static void NotifyLocalizationChanged() { if (Localization.m_instance == null || Localization.OnLanguageChange == null) { return; } Delegate[] invocationList = Localization.OnLanguageChange.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("A localized UI subscriber failed after a server localization update: " + ex.Message)); } } } private static ServerLocalizationPayload CreateEmptyPayload() { return new ServerLocalizationPayload { Version = 1, Languages = new Dictionary>(StringComparer.OrdinalIgnoreCase) }; } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class CreatureManagerServerLocalizationShutdownPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { try { CreatureServerLocalization.OnWorldShutdown(); } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to clear server localization during world shutdown: " + ex.Message)); } } } [HarmonyPatch] internal static class CreatureManagerSwiftMovementPatch { private static readonly IReadOnlyDictionary> ExpectedFieldsByMethod = new Dictionary>(StringComparer.Ordinal) { ["UpdateWalking"] = new HashSet(StringComparer.Ordinal) { "m_walkSpeed", "m_speed", "m_runSpeed", "m_acceleration", "m_turnSpeed", "m_runTurnSpeed" }, ["UpdateFlying"] = new HashSet(StringComparer.Ordinal) { "m_flySlowSpeed", "m_flyFastSpeed", "m_acceleration", "m_flyTurnSpeed" }, ["UpdateSwimming"] = new HashSet(StringComparer.Ordinal) { "m_swimSpeed", "m_swimAcceleration", "m_swimTurnSpeed" } }; private static readonly MethodInfo ApplyMovementFactorMethod = AccessTools.Method(typeof(CreatureManagerSwiftMovementPatch), "ApplyMovementFactor", (Type[])null, (Type[])null); private static IReadOnlyList? _targetMethods; [ThreadStatic] private static float _activeMovementFactor; private static bool Prepare() { return GetTargetMethods().Count > 0; } private static IEnumerable TargetMethods() { return GetTargetMethods(); } private static IReadOnlyList GetTargetMethods() { if (_targetMethods != null) { return _targetMethods; } List list = new List(); foreach (string key in ExpectedFieldsByMethod.Keys) { MethodInfo methodInfo = FindTargetMethod(key); if (methodInfo != null) { list.Add(methodInfo); } else { CreatureManagerPlugin.Log.LogWarning((object)("Swift could not patch Character." + key + "(float); that movement mode will not receive its speed bonus.")); } } if (list.Count == 0) { CreatureManagerPlugin.Log.LogWarning((object)"Swift could not find Valheim's character movement methods; movement bonuses are disabled for this game version."); } _targetMethods = list; return _targetMethods; } private static MethodInfo? FindTargetMethod(string methodName) { return AccessTools.DeclaredMethod(typeof(Character), methodName, new Type[1] { typeof(float) }, (Type[])null); } [HarmonyPriority(800)] private static void Prefix(Character __instance, out float __state) { __state = _activeMovementFactor; _activeMovementFactor = (CreatureModifierManager.TryGetSwiftMovementFactor(__instance, out var factor) ? factor : 1f); } [HarmonyPriority(0)] private static Exception? Finalizer(Exception? __exception, float __state) { _activeMovementFactor = __state; return __exception; } [HarmonyPriority(0)] private static IEnumerable Transpiler(IEnumerable instructions, MethodBase __originalMethod) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if (!ExpectedFieldsByMethod.TryGetValue(__originalMethod.Name, out HashSet value)) { return instructions; } List list = instructions.ToList(); List list2 = new List(); HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (CodeInstruction item in list) { list2.Add(item); if (!(item.opcode != OpCodes.Ldfld) && item.operand is FieldInfo fieldInfo && !(fieldInfo.DeclaringType != typeof(Character)) && !(fieldInfo.FieldType != typeof(float)) && value.Contains(fieldInfo.Name)) { list2.Add(new CodeInstruction(OpCodes.Call, (object)ApplyMovementFactorMethod)); hashSet.Add(fieldInfo.Name); } } string[] array = value.Except(hashSet).OrderBy((string name) => name, StringComparer.Ordinal).ToArray(); string[] array2 = array; foreach (string text in array2) { CreatureManagerPlugin.Log.LogWarning((object)("Swift could not patch Character." + __originalMethod.Name + "'s " + text + " read; that movement mode's Swift bonus has been disabled for this game version.")); } if (array.Length != 0) { return list; } return list2; } private static float ApplyMovementFactor(float value) { if (!(_activeMovementFactor > 0f)) { return value; } return value * _activeMovementFactor; } } internal static class CreatureTextureRegistry { private sealed class FileTextureEntry { internal DateTime LastWriteTimeUtc; internal long Length; internal Texture2D Texture; } private sealed class TextureReferenceEntry { public string Name { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; } private static readonly Dictionary FileTextureCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ResourceTextureCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly MethodInfo? LoadImageMethod = AccessTools.Method(typeof(ImageConversion), "LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }, (Type[])null); internal static void Dispose() { foreach (FileTextureEntry value in FileTextureCache.Values) { if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } } FileTextureCache.Clear(); } internal static string BuildTextureReferenceText() { CreatureAssetOwnerCatalog.PrepareMappings(); List availableTextureEntries = GetAvailableTextureEntries(); if (availableTextureEntries.Count == 0) { return "[]\n"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Generated by CreatureManager (texture reference)."); stringBuilder.AppendLine("# Use these names as the textureName value in visual.textures entries."); stringBuilder.AppendLine("# PNG files in BepInEx/config/CreatureManager/textures can also be used by file name."); stringBuilder.AppendLine("# Owner sections are best-effort guesses from the Valheim manifest and loaded asset bundles."); stringBuilder.AppendLine(); bool flag = false; foreach (IGrouping item in availableTextureEntries.OrderBy((TextureReferenceEntry entry) => CreatureReferenceSections.GetOwnerSortBucket(entry.OwnerName)).ThenBy((TextureReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((TextureReferenceEntry entry) => entry.Name, StringComparer.OrdinalIgnoreCase) .GroupBy((TextureReferenceEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase)) { if (flag) { stringBuilder.AppendLine(); } stringBuilder.Append("# ===== "); stringBuilder.Append(item.Key); stringBuilder.AppendLine(" ====="); foreach (TextureReferenceEntry item2 in item) { stringBuilder.AppendLine(item2.Name); } flag = true; } return stringBuilder.ToString(); } internal static Texture? GetTexture(string textureName) { if (string.IsNullOrWhiteSpace(textureName)) { return null; } string text = textureName; if (!Path.IsPathRooted(text)) { text = Path.Combine(CreatureDomainManager.TextureDirectoryPath, textureName); } if (!Path.HasExtension(text)) { text += ".png"; } if (File.Exists(text)) { return (Texture?)(object)LoadTextureFile(text); } CacheResourceTexturesIfNeeded(); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(textureName); if (!ResourceTextureCache.TryGetValue(fileNameWithoutExtension, out Texture2D value)) { return null; } return (Texture?)(object)value; } private static Texture2D? LoadTextureFile(string path) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) path = Path.GetFullPath(path); FileTextureCache.TryGetValue(path, out FileTextureEntry value); try { FileInfo fileInfo = new FileInfo(path); if ((Object)(object)value?.Texture != (Object)null && value.LastWriteTimeUtc == fileInfo.LastWriteTimeUtc && value.Length == fileInfo.Length) { return value.Texture; } byte[] bytes = File.ReadAllBytes(path); bool flag = (Object)(object)value?.Texture == (Object)null; Texture2D val = (Texture2D)((!flag) ? ((object)value.Texture) : ((object)new Texture2D(2, 2, (TextureFormat)4, false))); if (!TryLoadImage(val, bytes)) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to load texture file " + path + ".")); if (flag) { Object.Destroy((Object)(object)val); } return value?.Texture; } ((Object)val).name = Path.GetFileNameWithoutExtension(path); if (value != null) { value.LastWriteTimeUtc = fileInfo.LastWriteTimeUtc; value.Length = fileInfo.Length; return val; } FileTextureCache[path] = new FileTextureEntry { LastWriteTimeUtc = fileInfo.LastWriteTimeUtc, Length = fileInfo.Length, Texture = val }; return val; } catch (Exception ex) { CreatureManagerPlugin.Log.LogWarning((object)("Failed to load texture file " + path + ": " + ex.Message)); return value?.Texture; } } private static bool TryLoadImage(Texture2D texture, byte[] bytes) { if (LoadImageMethod == null) { CreatureManagerPlugin.Log.LogWarning((object)"UnityEngine.ImageConversion.LoadImage was not found."); return false; } object obj = LoadImageMethod.Invoke(null, new object[2] { texture, bytes }); if (obj is bool) { return (bool)obj; } return false; } private static void CacheResourceTexturesIfNeeded() { if (ResourceTextureCache.Count <= 0) { RefreshResourceTextureCache(); } } private static void RefreshResourceTextureCache() { foreach (Texture2D item in from texture in Resources.FindObjectsOfTypeAll() where (Object)(object)texture != (Object)null && ((Object)texture).GetInstanceID() >= 0 select texture) { if (!ResourceTextureCache.ContainsKey(((Object)item).name)) { ResourceTextureCache[((Object)item).name] = item; } } } private static List GetAvailableTextureEntries() { RefreshResourceTextureCache(); return (from name in ResourceTextureCache.Keys.Where((string name) => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase) select new TextureReferenceEntry { Name = name, OwnerName = CreatureTextureOwnerResolver.GetOwnerName(name) }).ToList(); } } internal sealed class ServerLocalizationPayload { public int Version { get; set; } public Dictionary>? Languages { get; set; } } internal static class CreatureYaml { internal enum ModifierYamlContext { Level, Karma } internal const int MaxSpawnPrefabWeight = 1000; internal const int MaxExpandedSpawnPrefabCount = 4096; internal const int ServerLocalizationPayloadVersion = 1; internal const int MaxLocalizationLanguageCount = 32; internal const int MaxLocalizationTokensPerLanguage = 8192; internal const int MaxLocalizationTokenLength = 128; internal const int MaxLocalizationTextLength = 4096; private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly HashSet LevelFieldNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "level", "damage", "damagePerLevel", "health", "healthPerLevel", "scalePerLevel", "distanceScaling", "modifiers", "modifierDistanceScaling" }; internal static bool TryReadDefinitions(string yaml, string source, out List definitions) { definitions = new List(); if (string.IsNullOrWhiteSpace(yaml) || IsCommentOnlyYaml(yaml)) { return true; } try { ValidateDefinitionDocument(yaml, source); definitions = Deserializer.Deserialize>(yaml) ?? new List(); return ValidateDefinitions(definitions, source); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read YAML from " + source + ": " + ex.Message)); return false; } } internal static bool ValidateDefinitions(IReadOnlyList definitions, string source) { for (int i = 0; i < definitions.Count; i++) { object obj = definitions[i]; string path = $"{source}[{i + 1}]"; if (obj == null) { return Invalid(path, "definition must be a mapping, not null."); } if (!((obj is FactionDefinition definition) ? ValidateFactionDefinition(definition, path) : ((obj is LevelDefinition rule) ? ValidateNormalizedLevelDefinition(rule, path) : ((obj is AiDefinition definition2) ? ValidateAiDefinition(definition2, path) : ((obj is AttackDefinition definition3) ? ValidateAttackDefinition(definition3, path) : ((obj is ProjectileDefinition definition4) ? ValidateProjectileDefinition(definition4, path) : (!(obj is CreatureDefinition definition5) || ValidateCreatureDefinition(definition5, path)))))))) { return false; } } return true; } private static bool ValidateFactionDefinition(FactionDefinition definition, string path) { if (string.IsNullOrWhiteSpace(definition.Faction)) { return Invalid(path, "faction must be a non-empty name."); } if (ValidateNameList(definition.Friendly, path, "friendly") && ValidateNameList(definition.AggravatedFriendly, path, "aggravatedFriendly")) { return ValidateNameList(definition.AlertedFriendly, path, "alertedFriendly"); } return false; } private static bool ValidateAiDefinition(AiDefinition definition, string path) { if (string.IsNullOrWhiteSpace(definition.Ai)) { return Invalid(path, "ai must be a non-empty name."); } BaseAiDefinition baseAI = definition.BaseAI; if (baseAI != null && (!ValidateStringTuple(baseAI.Senses, 4, path, "baseAI.senses", "float", "float", "float", "bool") || !ValidateFloatTuple(baseAI.IdleSound, 2, path, "baseAI.idleSound") || !ValidateStringTuple(baseAI.Movement, 4, path, "baseAI.movement", "bool", "name", "float", "bool") || !ValidateStringTuple(baseAI.Serpent, 2, path, "baseAI.serpent", "bool", "float") || !ValidateFloatTuple(baseAI.RandomMove, 4, path, "baseAI.randomMove") || !ValidateStringTuple(baseAI.Flight, 10, path, "baseAI.flight", "bool", "float", "float", "float", "float", "float", "float", "float", "float", "float") || !ValidateStringTuple(baseAI.Avoid, 6, path, "baseAI.avoid", "bool", "bool", "bool", "bool", "bool", "bool") || !ValidateFloatTuple(baseAI.Flee, 3, path, "baseAI.flee") || !ValidateStringTuple(baseAI.Aggressive, 2, path, "baseAI.aggressive", "bool", "bool") || !ValidateStringTuple(baseAI.Messages, 3, path, "baseAI.messages", "text", "text", "text"))) { return false; } MonsterAiDefinition monsterAI = definition.MonsterAI; if (monsterAI != null) { if (IsFinite(monsterAI.AlertRange, path, "monsterAI.alertRange") && ValidateStringTuple(monsterAI.Hunt, 3, path, "monsterAI.hunt", "bool", "bool", "int") && ValidateFloatTuple(monsterAI.Chase, 4, path, "monsterAI.chase") && ValidateFloatTuple(monsterAI.Circle, 3, path, "monsterAI.circle") && ValidateStringTuple(monsterAI.HurtFlee, 7, path, "monsterAI.hurtFlee", "bool", "float", "float", "bool", "float", "float", "bool") && ValidateStringTuple(monsterAI.Charge, 2, path, "monsterAI.charge", "bool", "bool")) { return ValidateStringTuple(monsterAI.Sleep, 7, path, "monsterAI.sleep", "bool", "float", "bool", "float", "float", "float", "float"); } return false; } return true; } private static bool ValidateAttackDefinition(AttackDefinition definition, string path) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(definition.Prefab)) { return Invalid(path, "prefab must be a non-empty name."); } AttackDamageDefinition damage = definition.Damage; if (damage != null && !AllFinite(path, "damage", damage.Damage, damage.Blunt, damage.Slash, damage.Pierce, damage.Chop, damage.Pickaxe, damage.Fire, damage.Frost, damage.Lightning, damage.Poison, damage.Spirit, damage.AttackForce)) { return false; } if (definition.Attack != null) { string[] array = CleanStringTuple(definition.Attack); if (array.Length != 2 || array.Any((string value) => value.Length == 0) || !Enum.TryParse(array[0], ignoreCase: true, out AttackType result) || !Enum.IsDefined(typeof(AttackType), result)) { return Invalid(path, "attack must be [type, animation] with a valid attack type and two non-empty values."); } } float parsed; if (definition.StatusEffect != null) { string[] array2 = CleanStringTuple(definition.StatusEffect); if (array2.Length != 2 || array2[0].Length == 0 || !TryParseFiniteFloat(array2[1], out parsed)) { return Invalid(path, "statusEffect must be [effect, chance] with a finite numeric chance."); } } if (definition.Projectile != null) { string[] array3 = CleanStringTuple(definition.Projectile); int num = array3.Length; bool flag = ((num < 1 || num > 4) ? true : false); if (flag || array3[0].Length == 0 || (array3.Length >= 2 && !TryParseFiniteFloat(array3[1], out parsed)) || (array3.Length >= 3 && !TryParseFiniteFloat(array3[2], out parsed)) || (array3.Length >= 4 && !int.TryParse(array3[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))) { return Invalid(path, "projectile must be [prefab[, velocity[, accuracy[, count]]]] with valid numeric values."); } } return ValidateFloatTuple(definition.Ai, 4, path, "ai"); } private static bool ValidateProjectileDefinition(ProjectileDefinition definition, string path) { if (string.IsNullOrWhiteSpace(definition.Prefab)) { return Invalid(path, "prefab must be a non-empty name."); } ProjectileComponentDefinition projectile = definition.Projectile; if (projectile != null) { if (!projectile.HasSpecifiedFields) { definition.Projectile = null; } else if (projectile.SpawnOnHit != null && string.IsNullOrWhiteSpace(projectile.SpawnOnHit)) { return Invalid(path, "projectile.spawnOnHit must be a non-empty prefab name or null."); } } SpawnAbilityDefinition spawnAbility = definition.SpawnAbility; if (spawnAbility != null && spawnAbility.SpawnPrefabsSpecified && !TryParseSpawnPrefabEntries(spawnAbility.SpawnPrefabs, out List<(string, int)> _, out string error)) { return Invalid(path, "spawnAbility.spawnPrefabs is invalid: " + error); } return true; } private static bool ValidateCreatureDefinition(CreatureDefinition definition, string path) { if (string.IsNullOrWhiteSpace(definition.Prefab)) { return Invalid(path, "prefab must be a non-empty name."); } if (!IsFinite(definition.Scale, path, "scale") || !ValidateNameList(definition.AvailableAttackAnimations, path, "availableAttackAnimations")) { return false; } CharacterDefinition character = definition.Character; if (character != null) { if (character.Boss != null && !TryParseBossTuple(character.Boss, out bool _, out bool dontHideBossHud, out string _, out string error)) { return Invalid(path, "character.boss is invalid: " + error); } if (character.Health != null && !TryParseHealthTuple(character.Health, out float _, out float? _, out string error2)) { return Invalid(path, "character.health is invalid: " + error2); } if (character.Speed != null && !TryParseFloatTuple(character.Speed, 7, "speed", out float[] values, out string error3)) { return Invalid(path, "character.speed is invalid: " + error3); } if (character.Jump != null && !TryParseFloatTuple(character.Jump, 5, "jump", out values, out string error4)) { return Invalid(path, "character.jump is invalid: " + error4); } if (character.Swim != null && !TryParseBoolFloatTuple(character.Swim, 4, "swim", out dontHideBossHud, out values, out string error5)) { return Invalid(path, "character.swim is invalid: " + error5); } if (character.Flight != null && !TryParseBoolFloatTuple(character.Flight, 3, "flight", out dontHideBossHud, out values, out string error6)) { return Invalid(path, "character.flight is invalid: " + error6); } if (!ValidateDamageModifiers(character.DamageModifiers, path)) { return false; } } HumanoidDefinition humanoid = definition.Humanoid; if (humanoid != null && (!ValidateNameList(humanoid.DefaultItems, path, "humanoid.defaultItems") || !ValidateNameList(humanoid.RandomWeapon, path, "humanoid.randomWeapon") || !ValidateNameList(humanoid.RandomArmor, path, "humanoid.randomArmor") || !ValidateNameList(humanoid.RandomHair, path, "humanoid.randomHair") || !ValidateNameList(humanoid.RandomShield, path, "humanoid.randomShield") || !ValidateRandomItems(humanoid.RandomItems, path) || !ValidateRandomSets(humanoid.RandomSets, path))) { return false; } AppearanceDefinition appearance = definition.Appearance; if (appearance != null) { if (!appearance.HasSpecifiedFields) { definition.Appearance = null; } else { if (appearance.HairColor != null && !TryParseAppearanceColor(appearance.HairColor, out var color)) { return Invalid(path, "appearance.hairColor must use #RRGGBB format."); } if (appearance.SkinColor != null && !TryParseAppearanceColor(appearance.SkinColor, out color)) { return Invalid(path, "appearance.skinColor must use #RRGGBB format."); } int? modelIndex = appearance.ModelIndex; if (modelIndex.HasValue && modelIndex.GetValueOrDefault() < 0) { return Invalid(path, "appearance.modelIndex must be 0 or greater."); } } } return ValidateTextureOverrides(definition.Textures, path); } private static bool ValidateDamageModifiers(DamageModifiersDefinition? definition, string path) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (definition == null) { return true; } string[] array = new string[10] { definition.Blunt, definition.Slash, definition.Pierce, definition.Chop, definition.Pickaxe, definition.Fire, definition.Frost, definition.Lightning, definition.Poison, definition.Spirit }; foreach (string text in array) { if (text != null && (!Enum.TryParse(text, ignoreCase: true, out DamageModifier result) || !Enum.IsDefined(typeof(DamageModifier), result))) { return Invalid(path, "character.damageModifiers has unknown value '" + text + "'."); } } return true; } private static bool ValidateRandomItems(List? values, string path) { if (values == null) { return true; } foreach (string value in values) { if (!TryParseRandomItemTuple(value, out string _, out float _, out string error)) { return Invalid(path, "humanoid.randomItems is invalid: " + error); } } return true; } private static bool ValidateRandomSets(List? values, string path) { if (values == null) { return true; } foreach (string value in values) { if (!TryParseRandomSetTuple(value, out string _, out string[] _, out string error)) { return Invalid(path, "humanoid.randomSets is invalid: " + error); } } return true; } private static bool ValidateTextureOverrides(List? values, string path) { if (values == null) { return true; } foreach (string value in values) { string[] array = (value ?? "").Split(new char[1] { ':' }); if (array.Length != 3 || string.IsNullOrWhiteSpace(array[0]) || string.IsNullOrWhiteSpace(array[2]) || !int.TryParse(array[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { return Invalid(path, "textures entry '" + value + "' must be rendererName:materialIndex:textureName."); } } return true; } private static bool ValidateNameList(List? values, string path, string field) { if (values == null) { return true; } if (!values.All((string value) => !string.IsNullOrWhiteSpace(value))) { return Invalid(path, field + " must contain only non-empty names."); } return true; } private static bool ValidateStringTuple(List? values, int count, string path, string field, params string[] types) { if (values == null) { return true; } if (values.Count != count || types.Length != count) { return Invalid(path, $"{field} must contain exactly {count} values."); } for (int i = 0; i < count; i++) { string text = values[i]?.Trim() ?? ""; if (types[i] switch { "bool" => TryParseFlexibleBool(text, out var _) ? 1 : 0, "float" => TryParseFiniteFloat(text, out var _) ? 1 : 0, "int" => int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _) ? 1 : 0, "name" => (text.Length > 0) ? 1 : 0, "text" => (values[i] != null) ? 1 : 0, _ => 0, } == 0) { return Invalid(path, $"{field}[{i + 1}] must be a valid {types[i]} value."); } } return true; } private static bool ValidateFloatTuple(List? values, int count, string path, string field) { if (values == null) { return true; } if (values.Count != count) { return Invalid(path, $"{field} must contain exactly {count} values."); } if (!values.All(IsFinite)) { return Invalid(path, field + " must contain only finite numbers."); } return true; } private static bool AllFinite(string path, string field, params float?[] values) { if (!values.All((float? value) => !value.HasValue || IsFinite(value.Value))) { return Invalid(path, field + " must contain only finite numbers."); } return true; } private static bool IsFinite(float? value, string path, string field) { if (value.HasValue && !IsFinite(value.Value)) { return Invalid(path, field + " must be a finite number."); } return true; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static bool TryParseFiniteFloat(string value, out float parsed) { if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out parsed)) { return IsFinite(parsed); } return false; } private static bool TryParseFlexibleBool(string value, out bool parsed) { if (bool.TryParse(value, out parsed)) { return true; } string text = value.Trim().ToLowerInvariant(); if (text != null) { switch (text.Length) { case 1: { char c = text[0]; if ((uint)c <= 49u) { if (c != '0') { if (c != '1') { break; } goto IL_00c6; } } else if (c != 'n') { if (c != 'y') { break; } goto IL_00c6; } goto IL_00cb; } case 3: { char c = text[0]; if (c != 'o') { if (c != 'y' || !(text == "yes")) { break; } goto IL_00c6; } if (!(text == "off")) { break; } goto IL_00cb; } case 2: { char c = text[0]; if (c != 'n') { if (c != 'o' || !(text == "on")) { break; } goto IL_00c6; } if (!(text == "no")) { break; } goto IL_00cb; } IL_00cb: parsed = false; return true; IL_00c6: parsed = true; return true; } } parsed = false; return false; } private static string[] CleanStringTuple(IEnumerable values) { return values.Select((string value) => value?.Trim() ?? "").ToArray(); } private static bool Invalid(string path, string message) { CreatureManagerPlugin.Log.LogError((object)("Failed to read YAML from " + path + ": " + message)); return false; } internal static bool TryReadLevelDefinitions(string yaml, string source, out List definitions) { definitions = new List(); if (string.IsNullOrWhiteSpace(yaml) || IsCommentOnlyYaml(yaml)) { return true; } try { YamlStream yamlStream = new YamlStream(); using StringReader input = new StringReader(yaml); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return true; } if (yamlStream.Documents.Count != 1) { throw new FormatException("Level YAML from " + source + " must contain exactly one YAML document."); } YamlNode rootNode = yamlStream.Documents[0].RootNode; if (rootNode is YamlSequenceNode yamlSequenceNode && yamlSequenceNode.Children.Count == 0) { return true; } if (!(rootNode is YamlMappingNode yamlMappingNode)) { CreatureManagerPlugin.Log.LogError((object)("Failed to read level YAML from " + source + ": expected a top-level mapping or an empty sequence.")); return false; } ValidateUniqueMappingKeys(yamlMappingNode, source, "root"); Dictionary> groups = ReadLevelGroups(yamlMappingNode, source); HashSet targetsWithNestedDefinitions; List collection = ReadNestedLevelDefinitions(yamlMappingNode, groups, source, out targetsWithNestedDefinitions); definitions = ReadTopLevelLevelDefinitions(yamlMappingNode, groups, source, targetsWithNestedDefinitions); definitions.AddRange(collection); return ValidateDefinitions(definitions, source); } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read level YAML from " + source + ": " + ex.Message)); definitions.Clear(); return false; } } private static List ReadTopLevelLevelDefinitions(YamlMappingNode root, Dictionary> groups, string source, HashSet targetsWithNestedDefinitions) { List list = new List(); foreach (KeyValuePair child in root.Children) { string yamlScalar = GetYamlScalar(child.Key); if (yamlScalar.Length == 0) { throw new FormatException("Level YAML from " + source + " has an empty top-level block name."); } if (!string.Equals(yamlScalar, "groups", StringComparison.OrdinalIgnoreCase)) { if (IsUnsupportedLevelTarget(yamlScalar)) { throw new FormatException("Level YAML from " + source + " uses unsupported top-level block '" + yamlScalar + "'. Use 'Boss'."); } if (!(child.Value is YamlMappingNode node)) { throw new FormatException("Level YAML from " + source + " '" + yamlScalar + "' must be a mapping."); } LevelDefinition levelDefinition = ReadLevelDefinition(node, source, yamlScalar, allowNestedDefinitions: true); levelDefinition.Target = yamlScalar; if (groups.TryGetValue(yamlScalar, out List value) && value != null) { levelDefinition.Prefabs = value; } bool warnNoEffect = !targetsWithNestedDefinitions.Contains(yamlScalar); if (ValidateLevelDefinition(levelDefinition, source, yamlScalar, warnNoEffect)) { list.Add(levelDefinition); } } } return list; } private static Dictionary> ReadLevelGroups(YamlMappingNode root, string source) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); YamlMappingNode yamlMappingNode = null; foreach (KeyValuePair child in root.Children) { if (string.Equals(GetYamlScalar(child.Key), "groups", StringComparison.OrdinalIgnoreCase)) { yamlMappingNode = child.Value as YamlMappingNode; if (yamlMappingNode == null) { throw new FormatException("Level YAML from " + source + " 'groups' must be a mapping."); } break; } } if (yamlMappingNode == null) { return dictionary; } foreach (KeyValuePair child2 in yamlMappingNode.Children) { string yamlScalar = GetYamlScalar(child2.Key); if (yamlScalar.Length == 0) { throw new FormatException("Level YAML from " + source + " has an empty group name."); } if (!(child2.Value is YamlSequenceNode yamlSequenceNode)) { throw new FormatException("Level YAML from " + source + " group '" + yamlScalar + "' must be a prefab list."); } List list = new List(); foreach (YamlNode child3 in yamlSequenceNode.Children) { string yamlScalar2 = GetYamlScalar(child3); if (!(child3 is YamlScalarNode) || string.IsNullOrWhiteSpace(yamlScalar2)) { throw new FormatException("Level YAML from " + source + " group '" + yamlScalar + "' must contain only non-empty prefab names."); } if (!list.Contains(yamlScalar2, StringComparer.OrdinalIgnoreCase)) { list.Add(yamlScalar2); } } if (list.Count == 0) { throw new FormatException("Level YAML from " + source + " group '" + yamlScalar + "' has no prefab names."); } dictionary[yamlScalar] = list; } return dictionary; } private static List ReadNestedLevelDefinitions(YamlMappingNode root, Dictionary> groups, string source, out HashSet targetsWithNestedDefinitions) { List list = new List(); targetsWithNestedDefinitions = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair child in root.Children) { string yamlScalar = GetYamlScalar(child.Key); if (yamlScalar.Length == 0 || string.Equals(yamlScalar, "groups", StringComparison.OrdinalIgnoreCase) || IsUnsupportedLevelTarget(yamlScalar) || !(child.Value is YamlMappingNode yamlMappingNode)) { continue; } foreach (KeyValuePair child2 in yamlMappingNode.Children) { string yamlScalar2 = GetYamlScalar(child2.Key); if (yamlScalar2.Length != 0 && !LevelFieldNames.Contains(yamlScalar2) && child2.Value is YamlMappingNode node) { targetsWithNestedDefinitions.Add(yamlScalar); string label = yamlScalar + "." + yamlScalar2; LevelDefinition levelDefinition = ReadLevelDefinition(node, source, label); levelDefinition.Target = yamlScalar; levelDefinition.Biome = yamlScalar2; if (groups.TryGetValue(yamlScalar, out List value) && value != null) { levelDefinition.Prefabs = value; } if (ValidateLevelDefinition(levelDefinition, source, label, warnNoEffect: true)) { list.Add(levelDefinition); } } } } return list; } private static bool IsUnsupportedLevelTarget(string target) { return string.Equals(target, "Bosses", StringComparison.OrdinalIgnoreCase); } internal static void ValidateUniqueMappingKeys(YamlNode node, string source, string path) { if (node is YamlMappingNode yamlMappingNode) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); { foreach (KeyValuePair child in yamlMappingNode.Children) { string yamlScalar = GetYamlScalar(child.Key); if (!hashSet.Add(yamlScalar)) { throw new FormatException("YAML from " + source + " " + path + " has duplicate key '" + yamlScalar + "' when compared case-insensitively."); } ValidateUniqueMappingKeys(child.Value, source, path + "." + yamlScalar); } return; } } if (node is YamlSequenceNode yamlSequenceNode) { for (int i = 0; i < yamlSequenceNode.Children.Count; i++) { ValidateUniqueMappingKeys(yamlSequenceNode.Children[i], source, $"{path}[{i + 1}]"); } } } internal static void ValidateUniqueMappingKeysInDocument(string yaml, string source) { ValidateUniqueMappingKeys(LoadSingleDocumentRoot(yaml, source), source, "root"); } private static void ValidateDefinitionDocument(string yaml, string source) { YamlNode yamlNode = LoadSingleDocumentRoot(yaml, source); if (!(yamlNode is YamlSequenceNode)) { throw new FormatException("YAML from " + source + " must have a sequence root. Use [] for an intentionally empty definition file."); } ValidateUniqueMappingKeys(yamlNode, source, "root"); } private static YamlNode LoadSingleDocumentRoot(string yaml, string source) { YamlStream yamlStream = new YamlStream(); using StringReader input = new StringReader(yaml); yamlStream.Load(input); if (yamlStream.Documents.Count != 1) { throw new FormatException("YAML from " + source + " must contain exactly one YAML document."); } return yamlStream.Documents[0].RootNode; } private static LevelDefinition ReadLevelDefinition(YamlMappingNode node, string source, string label, bool allowNestedDefinitions = false) { LevelDefinition levelDefinition = new LevelDefinition(); foreach (KeyValuePair child in node.Children) { string yamlScalar = GetYamlScalar(child.Key); switch (yamlScalar.ToLowerInvariant()) { case "level": levelDefinition.Level = ReadFloatList(child.Value, source, label, "level"); break; case "damage": levelDefinition.Damage = ReadFloat(child.Value, source, label, "damage"); break; case "damageperlevel": levelDefinition.DamagePerLevel = ReadFloat(child.Value, source, label, "damagePerLevel"); break; case "health": levelDefinition.Health = ReadFloat(child.Value, source, label, "health"); break; case "healthperlevel": levelDefinition.HealthPerLevel = ReadFloat(child.Value, source, label, "healthPerLevel"); break; case "scaleperlevel": levelDefinition.ScalePerLevel = ReadFloat(child.Value, source, label, "scalePerLevel"); break; case "distancescaling": levelDefinition.DistanceScaling = ReadFloatList(child.Value, source, label, "distanceScaling"); break; case "modifierdistancescaling": levelDefinition.ModifierDistanceScaling = ReadFloatList(child.Value, source, label, "modifierDistanceScaling"); break; case "modifiers": { if (!TryReadModifierBlock(child.Value, source, label, ModifierYamlContext.Level, out Dictionary modifiers, out bool modifiersCleared)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.modifiers is invalid."); } levelDefinition.Modifiers = modifiers; levelDefinition.ModifiersCleared = modifiersCleared; break; } default: if (!allowNestedDefinitions || !(child.Value is YamlMappingNode)) { throw new FormatException("Level YAML from " + source + " '" + label + "' has unknown field '" + yamlScalar + "'."); } break; } } return levelDefinition; } private static List? ReadFloatList(YamlNode node, string source, string label, string field) { if (!(node is YamlSequenceNode yamlSequenceNode)) { throw new FormatException("Level YAML from " + source + " '" + label + "'." + field + " must be a YAML list."); } List list = new List(); foreach (YamlNode child in yamlSequenceNode.Children) { float? num = ReadFloat(child, source, label, field); if (!num.HasValue) { throw new FormatException("Level YAML from " + source + " '" + label + "'." + field + " contains an invalid number."); } list.Add(num.Value); } return list; } private static float? ReadFloat(YamlNode node, string source, string label, string field) { string yamlScalar = GetYamlScalar(node); if (TryParseFiniteFloat(yamlScalar, out var parsed)) { return parsed; } throw new FormatException("Level YAML from " + source + " '" + label + "'." + field + " has invalid number '" + yamlScalar + "'."); } internal static bool TryReadModifierBlock(YamlNode node, string source, string label, ModifierYamlContext context, out Dictionary? modifiers, out bool modifiersCleared) { modifiers = null; modifiersCleared = false; string modifierBlockPath = GetModifierBlockPath(source, label, context); if (node is YamlSequenceNode yamlSequenceNode) { if (yamlSequenceNode.Children.Count == 0) { modifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); modifiersCleared = true; return true; } CreatureManagerPlugin.Log.LogError((object)(modifierBlockPath + " must be a mapping ({} keeps fallback) or an empty list [] to block all lower modifier fallback.")); return false; } if (!(node is YamlMappingNode yamlMappingNode)) { CreatureManagerPlugin.Log.LogError((object)(modifierBlockPath + " must be a mapping ({} keeps fallback) or an empty list [] to block all lower modifier fallback.")); return false; } modifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair child in yamlMappingNode.Children) { string text = GetYamlScalar(child.Key); if (context == ModifierYamlContext.Karma) { text = text.ToLowerInvariant(); } if (text.Length == 0) { CreatureManagerPlugin.Log.LogError((object)((context == ModifierYamlContext.Level) ? (modifierBlockPath + " has an empty modifier name.") : (modifierBlockPath + " has unknown modifier ''."))); return false; } if (!CreatureModifierCatalog.IsKnown(text)) { CreatureManagerPlugin.Log.LogError((object)(modifierBlockPath + " has unknown modifier '" + text + "'.")); return false; } if (!TryReadModifierDefinition(child.Value, source, label, text, context, out ModifierDefinition definition)) { return false; } modifiers[text] = definition; } return true; } private static bool TryReadModifierDefinition(YamlNode node, string source, string label, string modifier, ModifierYamlContext context, out ModifierDefinition definition) { definition = null; bool flag = modifier.Equals("blink", StringComparison.OrdinalIgnoreCase); bool flag2 = modifier.Equals("juggernaut", StringComparison.OrdinalIgnoreCase); bool flag3 = modifier.Equals("unflinching", StringComparison.OrdinalIgnoreCase); bool flag4 = modifier.Equals("undodgeable", StringComparison.OrdinalIgnoreCase); bool flag5 = modifier.Equals("chameleon", StringComparison.OrdinalIgnoreCase); bool flag6 = modifier.Equals("blamer", StringComparison.OrdinalIgnoreCase); bool flag7 = modifier.Equals("deathward", StringComparison.OrdinalIgnoreCase); bool flag8 = modifier.Equals("reflection", StringComparison.OrdinalIgnoreCase); bool flag9 = modifier.Equals("reaping", StringComparison.OrdinalIgnoreCase); bool flag10 = modifier.Equals("exposed", StringComparison.OrdinalIgnoreCase) || modifier.Equals("weakened", StringComparison.OrdinalIgnoreCase) || modifier.Equals("withered", StringComparison.OrdinalIgnoreCase) || modifier.Equals("corrosive", StringComparison.OrdinalIgnoreCase); bool flag11 = modifier.Equals("crippling", StringComparison.OrdinalIgnoreCase) || modifier.Equals("disruptive", StringComparison.OrdinalIgnoreCase); bool flag12 = modifier.Equals("adrenalinedrain", StringComparison.OrdinalIgnoreCase); bool flag13 = modifier.Equals("toxicdeath", StringComparison.OrdinalIgnoreCase); string modifierTupleFormat = GetModifierTupleFormat(flag, flag2, flag3, flag4, flag5, flag6, flag7, flag8, flag9, flag10, flag11, flag12, flag13); string text = GetModifierBlockPath(source, label, context) + "." + modifier; string yamlScalar = GetYamlScalar(node); if (yamlScalar.Length == 0) { CreatureManagerPlugin.Log.LogWarning((object)((context == ModifierYamlContext.Level) ? (text + " must be '" + modifierTupleFormat + "'.") : (text + " must be " + modifierTupleFormat + "."))); return false; } string[] array = SplitTuple(yamlScalar); int num = (flag ? 4 : 2); int num2 = (flag4 ? 2 : (flag3 ? 1 : (flag2 ? 3 : (flag7 ? 4 : (flag5 ? 2 : (flag6 ? 4 : (flag8 ? 3 : (flag9 ? 9 : (flag10 ? 4 : (flag11 ? 5 : (flag12 ? 5 : (flag13 ? 4 : 0)))))))))))); bool num3; if (num2 <= 0) { if (array.Length < 1) { goto IL_01fd; } num3 = array.Length > num; } else { num3 = array.Length != num2; } if (!num3) { int num4 = ((flag || flag13 || flag7) ? Math.Min(array.Length, 3) : array.Length); float[] array2 = new float[num4]; for (int i = 0; i < num4; i++) { if (!TryParseFiniteFloat(array[i], out array2[i])) { CreatureManagerPlugin.Log.LogError((object)$"{text} has invalid number '{array[i]}' at position {i + 1}."); return false; } } if (flag4 && (float.IsNaN(array2[1]) || float.IsInfinity(array2[1]))) { CreatureManagerPlugin.Log.LogWarning((object)(text + " damageReduction must be a finite number.")); return false; } int result = 0; if (flag7 && (!int.TryParse(array[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result) || result < 1)) { CreatureManagerPlugin.Log.LogWarning((object)(text + " maxActivations must be an integer of 1 or greater.")); return false; } int result2 = 0; if (flag9 && (!int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) || result2 < 1)) { CreatureManagerPlugin.Log.LogWarning((object)(text + " healMaxActivations must be an integer of 1 or greater.")); return false; } definition = new ModifierDefinition { Chance = array2[0] }; if (array2.Length >= 2) { if (flag) { definition.Cooldown = array2[1]; } else { definition.Power = array2[1]; } } if (flag7) { definition.Cooldown = array2[2]; definition.MaxActivations = result; } if (flag2) { definition.Cooldown = array2[2]; } if (flag6) { definition.MaxKarmaGain = array2[2]; definition.FleeHealthRatio = Mathf.Clamp01(array2[3]); } if (flag && array2.Length >= 3) { definition.MaxRange = array2[2]; } if (flag && array.Length >= 4) { definition.StartEffect = array[3]; } if (flag9) { definition.ReapingHealMaxActivations = result2; definition.ReapingMaxHealthPerKill = array2[3]; definition.ReapingMaxHealthCap = array2[4]; definition.ReapingDamagePerKill = array2[5]; definition.ReapingDamageCap = array2[6]; definition.ReapingScalePerKill = array2[7]; definition.ReapingScaleCap = array2[8]; } if (flag10) { definition.ProcChance = array2[2]; definition.Duration = array2[3]; } if (flag11 || flag12) { definition.SecondaryPower = array2[2]; definition.ProcChance = array2[3]; definition.Duration = array2[4]; } if (flag8) { definition.ProcChance = array2[2]; } if (flag13) { definition.Radius = array2[2]; definition.TriggerEffect = array[3]; } if (context == ModifierYamlContext.Level) { ValidateLevelModifier(definition, modifier, text); } else { ValidateAndNormalizeKarmaModifier(definition, modifier, text); } return true; } goto IL_01fd; IL_01fd: if (context == ModifierYamlContext.Level) { string arg = ((num2 > 0) ? num2.ToString(CultureInfo.InvariantCulture) : (flag ? "1 to 4" : "1 or 2")); CreatureManagerPlugin.Log.LogWarning((object)$"{text} expected {arg} tuple values but got {array.Length}. Use modifierDistanceScaling for distance-based chance scaling."); } else { CreatureManagerPlugin.Log.LogWarning((object)(text + " must be " + modifierTupleFormat + ".")); } return false; } private static string GetModifierBlockPath(string source, string label, ModifierYamlContext context) { if (context != ModifierYamlContext.Level) { return "Karma YAML from " + source + " " + label; } return "Level YAML from " + source + " '" + label + "'.modifiers"; } private static string GetModifierTupleFormat(bool isBlink, bool isKnockback, bool isChanceOnly, bool isUndodgeable, bool isChameleon, bool isBlamer, bool isDeathward, bool isReflection, bool isReaping, bool isStandardAffliction, bool isSplitAffliction, bool isAdrenalineDrain, bool isToxicDeath) { if (isBlink) { return "chance[, cooldown[, maxRange[, startEffect]]]"; } if (isUndodgeable) { return "chance, damageReduction"; } if (isChanceOnly) { return "chance"; } if (isKnockback) { return "chance, minimumPushForce, cooldown"; } if (isDeathward) { return "chance, restoreHealth, cooldown, maxActivations"; } if (isChameleon) { return "chance, immunitySwitchSeconds"; } if (isBlamer) { return "chance, karmaPerSecond, maxKarmaGain, fleeHealthRatio"; } if (isReaping) { return "chance, healPerKill, healMaxActivations, maxHealthPerKill, maxHealthCap, damagePerKill, damageCap, scalePerKill, scaleCap"; } if (isStandardAffliction) { return "chance, power, procChance, duration"; } if (isSplitAffliction) { return "chance, primaryPower, secondaryPower, procChance, duration"; } if (isAdrenalineDrain) { return "chance, currentAdrenalineRemoved, adrenalineGainReduction, procChance, duration"; } if (isReflection) { return "chance, reflectedDamage, procChance"; } if (isToxicDeath) { return "chance, maxHealthDamage, radius, triggerEffect"; } return "chance[, power]"; } private static void ValidateAndNormalizeKarmaModifier(ModifierDefinition definition, string modifier, string modifierPath) { bool flag = modifier.Equals("undodgeable", StringComparison.OrdinalIgnoreCase); bool flag2; if (flag) { float? power = definition.Power; if (power.HasValue) { float valueOrDefault = power.GetValueOrDefault(); if (valueOrDefault < 0f || valueOrDefault > 1f) { flag2 = true; goto IL_003e; } } flag2 = false; goto IL_003e; } goto IL_0040; IL_0040: if (flag) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " damageReduction must be from 0 to 1 and will be clamped at runtime.")); } definition.Chance = Mathf.Clamp(definition.Chance.Value, 0f, 100f); if (definition.Cooldown.HasValue) { definition.Cooldown = Mathf.Max(0f, definition.Cooldown.Value); } if (definition.MaxRange.HasValue) { definition.MaxRange = Mathf.Max(0f, definition.MaxRange.Value); } if (definition.ProcChance.HasValue) { definition.ProcChance = Mathf.Clamp01(definition.ProcChance.Value); } if (definition.Duration.HasValue) { definition.Duration = Mathf.Max(0.1f, definition.Duration.Value); } if (definition.SecondaryPower.HasValue) { definition.SecondaryPower = Mathf.Clamp01(definition.SecondaryPower.Value); } if (definition.Radius.HasValue) { definition.Radius = Mathf.Max(0f, definition.Radius.Value); } if (definition.MaxKarmaGain.HasValue) { definition.MaxKarmaGain = Mathf.Max(0f, definition.MaxKarmaGain.Value); } if (definition.FleeHealthRatio.HasValue) { definition.FleeHealthRatio = Mathf.Clamp01(definition.FleeHealthRatio.Value); } if (definition.ReapingMaxHealthPerKill.HasValue) { definition.ReapingMaxHealthPerKill = Mathf.Max(0f, definition.ReapingMaxHealthPerKill.Value); } if (definition.ReapingMaxHealthCap.HasValue) { definition.ReapingMaxHealthCap = Mathf.Max(0f, definition.ReapingMaxHealthCap.Value); } if (definition.ReapingDamagePerKill.HasValue) { definition.ReapingDamagePerKill = Mathf.Max(0f, definition.ReapingDamagePerKill.Value); } if (definition.ReapingDamageCap.HasValue) { definition.ReapingDamageCap = Mathf.Max(0f, definition.ReapingDamageCap.Value); } if (definition.ReapingScalePerKill.HasValue) { definition.ReapingScalePerKill = Mathf.Max(0f, definition.ReapingScalePerKill.Value); } if (definition.ReapingScaleCap.HasValue) { definition.ReapingScaleCap = Mathf.Max(0f, definition.ReapingScaleCap.Value); } return; IL_003e: flag = flag2; goto IL_0040; } private static void ValidateLevelModifier(ModifierDefinition definition, string modifier, string modifierPath) { float? chance = definition.Chance; bool flag; if (chance.HasValue) { float valueOrDefault = chance.GetValueOrDefault(); if (valueOrDefault < 0f || valueOrDefault > 100f) { flag = true; goto IL_002e; } } flag = false; goto IL_002e; IL_002e: if (flag) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " chance must be from 0 to 100 and will be clamped at runtime.")); } flag = definition.Power.HasValue && !modifier.Equals("juggernaut", StringComparison.OrdinalIgnoreCase) && !modifier.Equals("chameleon", StringComparison.OrdinalIgnoreCase); if (flag) { float valueOrDefault = definition.Power.Value; bool flag2 = ((valueOrDefault < 0f || valueOrDefault > 1f) ? true : false); flag = flag2; } if (flag) { string text = (modifier.Equals("undodgeable", StringComparison.OrdinalIgnoreCase) ? "damageReduction" : "power"); CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " " + text + " must be from 0 to 1 and will be clamped at runtime.")); } if (modifier.Equals("chameleon", StringComparison.OrdinalIgnoreCase) && definition.Power.HasValue) { float value = definition.Power.Value; string canonicalModifierPath = GetCanonicalModifierPath(modifierPath, modifier, "chameleon"); if (float.IsNaN(value) || float.IsInfinity(value)) { CreatureManagerPlugin.Log.LogWarning((object)(canonicalModifierPath + " immunitySwitchSeconds must be a finite number and will use the default interval.")); } else if (value <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)(canonicalModifierPath + " immunitySwitchSeconds must be greater than 0 and will use at least 0.1 seconds.")); } } if (modifier.Equals("juggernaut", StringComparison.OrdinalIgnoreCase) && definition.Power < 0f) { string canonicalModifierPath2 = GetCanonicalModifierPath(modifierPath, modifier, "juggernaut"); CreatureManagerPlugin.Log.LogWarning((object)(canonicalModifierPath2 + " minimumPushForce must be 0 or greater and will be clamped at runtime.")); } flag = definition.SecondaryPower.HasValue; if (flag) { float valueOrDefault = definition.SecondaryPower.Value; bool flag2 = ((valueOrDefault < 0f || valueOrDefault > 1f) ? true : false); flag = flag2; } if (flag) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " secondary power must be from 0 to 1 and will be clamped at runtime.")); } flag = definition.ProcChance.HasValue; if (flag) { float valueOrDefault = definition.ProcChance.Value; bool flag2 = ((valueOrDefault < 0f || valueOrDefault > 1f) ? true : false); flag = flag2; } if (flag) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " procChance must be from 0 to 1 and will be clamped at runtime.")); } if (definition.Duration.HasValue && definition.Duration.Value <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " duration must be greater than 0 and will use at least 0.1 seconds.")); } if (definition.Radius.HasValue && definition.Radius.Value < 0f) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " radius must be 0 or greater and will be clamped at runtime.")); } if (definition.Cooldown.HasValue && definition.Cooldown.Value < 0f) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " cooldown must be 0 or greater and will be clamped at runtime.")); } if (definition.MaxActivations.HasValue && definition.MaxActivations.Value < 1) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " maxActivations must be 1 or greater.")); } if (definition.MaxRange.HasValue && definition.MaxRange.Value <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)(modifierPath + " maxRange must be greater than 0 and will disable Blink AI range expansion.")); } if (modifier.Equals("reaping", StringComparison.OrdinalIgnoreCase) && (definition.ReapingMaxHealthPerKill < 0f || definition.ReapingMaxHealthCap < 0f || definition.ReapingDamagePerKill < 0f || definition.ReapingDamageCap < 0f || definition.ReapingScalePerKill < 0f || definition.ReapingScaleCap < 0f)) { string canonicalModifierPath3 = GetCanonicalModifierPath(modifierPath, modifier, "reaping"); CreatureManagerPlugin.Log.LogWarning((object)(canonicalModifierPath3 + " gain and cap values must be 0 or greater and will be clamped at runtime.")); } } private static string GetCanonicalModifierPath(string modifierPath, string modifier, string canonicalModifier) { return modifierPath.Substring(0, modifierPath.Length - modifier.Length) + canonicalModifier; } private static string GetYamlScalar(YamlNode node) { if (!(node is YamlScalarNode yamlScalarNode)) { return ""; } return (yamlScalarNode.Value ?? "").Trim(); } private static bool ValidateNormalizedLevelDefinition(LevelDefinition rule, string path) { if (string.IsNullOrWhiteSpace(rule.Target)) { return Invalid(path, "target must be a non-empty name."); } if (rule.Prefabs != null && !ValidateNameList(rule.Prefabs, path, "prefabs")) { return false; } if (rule.ModifiersCleared) { Dictionary modifiers = rule.Modifiers; if (modifiers != null && modifiers.Count > 0) { return Invalid(path, "modifiersCleared cannot be combined with modifier entries."); } } if (rule.Modifiers != null) { foreach (KeyValuePair modifier in rule.Modifiers) { if (!CreatureModifierCatalog.IsKnown(modifier.Key)) { return Invalid(path, "modifiers has unknown modifier '" + modifier.Key + "'."); } if (!ValidateNormalizedModifier(modifier.Key, modifier.Value, path)) { return false; } } } try { if (!ValidateLevelDefinition(rule, path, rule.Target, warnNoEffect: true)) { return Invalid(path, "level definition has no effect fields."); } } catch (Exception ex) { return Invalid(path, ex.Message); } return true; } private static bool ValidateNormalizedModifier(string modifier, ModifierDefinition? definition, string path) { if (definition == null || !definition.Chance.HasValue || !IsFinite(definition.Chance.Value)) { return Invalid(path, "modifiers." + modifier + ".chance must be a finite number."); } if (new float?[15] { definition.Power, definition.Cooldown, definition.MaxRange, definition.ProcChance, definition.Duration, definition.SecondaryPower, definition.Radius, definition.MaxKarmaGain, definition.FleeHealthRatio, definition.ReapingMaxHealthPerKill, definition.ReapingMaxHealthCap, definition.ReapingDamagePerKill, definition.ReapingDamageCap, definition.ReapingScalePerKill, definition.ReapingScaleCap }.Any((float? value) => value.HasValue && !IsFinite(value.Value))) { return Invalid(path, "modifiers." + modifier + " must contain only finite numbers."); } int? maxActivations = definition.MaxActivations; if (!maxActivations.HasValue || maxActivations.GetValueOrDefault() >= 1) { maxActivations = definition.ReapingHealMaxActivations; if (!maxActivations.HasValue || maxActivations.GetValueOrDefault() >= 1) { ValidateLevelModifier(definition, modifier, path + ".modifiers." + modifier); return true; } } return Invalid(path, "modifiers." + modifier + " activation counts must be integers of 1 or greater."); } private static bool ValidateLevelDefinition(LevelDefinition rule, string source, string label, bool warnNoEffect) { List level = rule.Level; if (level != null && level.Count == 0) { throw new FormatException("Level YAML from " + source + " '" + label + "'.level must not be empty."); } if (rule.Level != null && rule.Level.Any((float value) => !IsFinite(value))) { throw new FormatException("Level YAML from " + source + " '" + label + "'.level must contain only finite numbers."); } if (rule.Health.HasValue && (!IsFinite(rule.Health.Value) || rule.Health.Value <= 0f)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.health must be a finite number greater than 0."); } if (rule.Damage.HasValue && (!IsFinite(rule.Damage.Value) || rule.Damage.Value < 0f)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.damage must be a finite number of 0 or greater."); } if (rule.DamagePerLevel.HasValue && (!IsFinite(rule.DamagePerLevel.Value) || rule.DamagePerLevel.Value < 0f)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.damagePerLevel must be a finite number of 0 or greater."); } if (rule.HealthPerLevel.HasValue && (!IsFinite(rule.HealthPerLevel.Value) || rule.HealthPerLevel.Value < 0f)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.healthPerLevel must be a finite number of 0 or greater."); } if (rule.ScalePerLevel.HasValue && (!IsFinite(rule.ScalePerLevel.Value) || rule.ScalePerLevel.Value < 0f)) { throw new FormatException("Level YAML from " + source + " '" + label + "'.scalePerLevel must be a finite number of 0 or greater."); } ValidateDistanceScaling(rule.DistanceScaling, source, label); ValidateModifierDistanceScaling(rule.ModifierDistanceScaling, source, label); if (rule.Level == null && !rule.Health.HasValue && !rule.HealthPerLevel.HasValue && !rule.Damage.HasValue && !rule.DamagePerLevel.HasValue && !rule.ScalePerLevel.HasValue && !HasDistanceScalingEffect(rule.DistanceScaling) && !HasModifierDistanceScalingValue(rule.ModifierDistanceScaling) && !rule.ModifiersCleared && (rule.Modifiers == null || rule.Modifiers.Count == 0)) { if (warnNoEffect && rule.Modifiers == null) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "' has no effect fields and will be ignored.")); } return false; } return true; } private static bool HasDistanceScalingEffect(List? scaling) { if (scaling != null && scaling.Count >= 2) { if (!(scaling[0] > 0f)) { return scaling[1] > 0f; } return true; } return false; } private static void ValidateDistanceScaling(List? scaling, string source, string label) { if (scaling != null) { int count = scaling.Count; if ((count < 2 || count > 4) ? true : false) { throw new FormatException("Level YAML from " + source + " '" + label + "'.distanceScaling must be [damage, health[, interval[, maxSteps]]]."); } if (scaling.Any((float value) => !IsFinite(value))) { throw new FormatException("Level YAML from " + source + " '" + label + "'.distanceScaling must contain only finite numbers."); } if (scaling[0] < 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.distanceScaling damage must be 0 or greater.")); scaling[0] = 0f; } if (scaling[1] < 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.distanceScaling health must be 0 or greater.")); scaling[1] = 0f; } if (scaling.Count >= 3 && scaling[2] <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.distanceScaling interval must be greater than 0 and will use 1000.")); scaling[2] = 1000f; } if (scaling.Count >= 4 && scaling[3] < 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.distanceScaling maxSteps must be 0 or greater and will be ignored.")); scaling[3] = 0f; } } } private static bool HasModifierDistanceScalingValue(List? scaling) { if (scaling != null) { return scaling.Count == 3; } return false; } private static void ValidateModifierDistanceScaling(List? scaling, string source, string label) { if (scaling != null) { if (scaling.Count != 3) { throw new FormatException("Level YAML from " + source + " '" + label + "'.modifierDistanceScaling must be [chancePerStep, stepDistance, maxSteps]."); } if (scaling.Any((float value) => !IsFinite(value))) { throw new FormatException("Level YAML from " + source + " '" + label + "'.modifierDistanceScaling must contain only finite numbers."); } if (scaling[0] < 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.modifierDistanceScaling chancePerStep must be 0 or greater.")); scaling[0] = 0f; } if (scaling[1] <= 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.modifierDistanceScaling stepDistance must be greater than 0 and will use 1000.")); scaling[1] = 1000f; } if (scaling[2] < 0f) { CreatureManagerPlugin.Log.LogWarning((object)("Level YAML from " + source + " '" + label + "'.modifierDistanceScaling maxSteps must be 0 or greater and will be ignored.")); scaling[2] = 0f; } } } private static bool IsCommentOnlyYaml(string yaml) { string[] array = yaml.Split(new string[2] { "\r\n", "\n" }, StringSplitOptions.None); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0 && !text.StartsWith("#", StringComparison.Ordinal)) { return false; } } return true; } internal static bool TryReadLocalizationMap(string yaml, string source, out Dictionary translations) { translations = new Dictionary(StringComparer.Ordinal); if (string.IsNullOrWhiteSpace(yaml) || IsCommentOnlyYaml(yaml)) { return true; } try { YamlStream yamlStream = new YamlStream(); using StringReader input = new StringReader(yaml); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return true; } if (yamlStream.Documents.Count != 1) { throw new FormatException("Localization YAML from " + source + " must contain exactly one YAML document."); } if (!(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { throw new FormatException("Localization YAML from " + source + " must be a flat token-to-text mapping."); } ValidateUniqueMappingKeys(yamlMappingNode, source, "root"); if (yamlMappingNode.Children.Count > 8192) { throw new FormatException($"Localization YAML from {source} contains {yamlMappingNode.Children.Count} tokens; the limit is {8192}."); } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair child in yamlMappingNode.Children) { if (!TryNormalizeLocalizationToken(((child.Key as YamlScalarNode) ?? throw new FormatException("Localization YAML from " + source + " has a non-scalar token key.")).Value, out string token, out string error)) { throw new FormatException("Localization YAML from " + source + " has an invalid token: " + error); } if (!hashSet.Add(token)) { throw new FormatException("Localization YAML from " + source + " has duplicate normalized token '" + token + "' when compared case-insensitively."); } if (!(child.Value is YamlScalarNode yamlScalarNode) || string.IsNullOrEmpty(yamlScalarNode.Value)) { throw new FormatException("Localization YAML from " + source + " token '" + token + "' must have a non-empty scalar string value; null, mappings, and lists are not supported."); } string value = yamlScalarNode.Value; if (value.Length > 4096) { throw new FormatException($"Localization YAML from {source} token '{token}' exceeds the {4096}-character text limit."); } translations[token] = value; } return true; } catch (Exception ex) { CreatureManagerPlugin.Log.LogError((object)("Failed to read localization YAML from " + source + ": " + ex.Message)); translations.Clear(); return false; } } internal static bool TryNormalizeLocalizationPayload(ServerLocalizationPayload? payload, string source, out ServerLocalizationPayload normalized) { normalized = new ServerLocalizationPayload { Version = 1, Languages = new Dictionary>(StringComparer.OrdinalIgnoreCase) }; if (payload == null || payload.Version != 1 || payload.Languages == null) { string arg = ((payload == null) ? "missing" : payload.Version.ToString(CultureInfo.InvariantCulture)); return Invalid(source, $"localization payload must have version {1} and a languages mapping; received version {arg}."); } if (payload.Languages.Count > 32) { return Invalid(source, $"localization payload contains {payload.Languages.Count} languages; the limit is {32}."); } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> language in payload.Languages) { string text = language.Key?.Trim() ?? ""; if (text.Length == 0 || text.Length > 64) { return Invalid(source, "localization language names must contain from 1 to 64 characters."); } if (!hashSet.Add(text)) { return Invalid(source, "localization payload has duplicate language '" + text + "' when compared case-insensitively."); } Dictionary value = language.Value; if (value == null || value.Count > 8192) { string arg2 = ((value == null) ? "null" : value.Count.ToString(CultureInfo.InvariantCulture)); return Invalid(source, $"localization language '{text}' has {arg2} tokens; it must be a mapping with at most {8192} entries."); } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in value) { if (!TryNormalizeLocalizationToken(item.Key, out string token, out string error)) { return Invalid(source, "localization language '" + text + "' has an invalid token: " + error); } if (!hashSet2.Add(token)) { return Invalid(source, "localization language '" + text + "' has duplicate normalized token '" + token + "' when compared case-insensitively."); } if (string.IsNullOrEmpty(item.Value) || item.Value.Length > 4096) { return Invalid(source, $"localization language '{text}' token '{token}' must be non-empty and no longer than {4096} characters."); } dictionary[token] = item.Value; } normalized.Languages[text] = dictionary; } return true; } internal static Dictionary BuildLocalizationForLanguage(ServerLocalizationPayload payload, string? language) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); Dictionary> dictionary2 = payload.Languages ?? new Dictionary>(StringComparer.OrdinalIgnoreCase); string text = language?.Trim() ?? ""; if (text.Length == 0) { text = "English"; } if (dictionary2.TryGetValue("English", out var value)) { foreach (KeyValuePair item in value) { dictionary[item.Key] = item.Value; } } if (!text.Equals("English", StringComparison.OrdinalIgnoreCase) && dictionary2.TryGetValue(text, out var value2)) { foreach (KeyValuePair item2 in value2) { dictionary[item2.Key] = item2.Value; } } return dictionary; } internal static bool TryNormalizeLocalizationToken(string? value, out string token, out string error) { token = value?.Trim() ?? ""; error = ""; if (token.StartsWith("$", StringComparison.Ordinal)) { token = token.Substring(1); } if (token.Length == 0 || token.Length > 128) { error = $"token names must contain from 1 to {128} characters after an optional leading '$'."; return false; } if (token.IndexOf('$') >= 0 || token.Any((char character) => char.IsControl(character) || " (){}[]+-!?/\\&%,.:-=<>\r\n\t".IndexOf(character) >= 0)) { error = "token '" + token + "' contains a character that terminates Valheim localization tokens."; return false; } return true; } internal static bool TryParseFloatTuple(string line, int expectedCount, string label, out float[] values, out string error) { values = Array.Empty(); error = ""; string[] array = SplitTuple(line); if (array.Length != expectedCount) { error = $"{label} tuple expected {expectedCount} values but got {array.Length}."; return false; } float[] array2 = new float[expectedCount]; for (int i = 0; i < array.Length; i++) { if (!TryParseFiniteFloat(array[i], out array2[i])) { error = $"{label} tuple has invalid number '{array[i]}' at position {i + 1}."; return false; } } values = array2; return true; } internal static bool TryParseBoolFloatTuple(string line, int floatCount, string label, out bool boolValue, out float[] values, out string error) { boolValue = false; values = Array.Empty(); error = ""; string[] array = SplitTuple(line); if (array.Length != floatCount + 1) { error = $"{label} tuple expected {floatCount + 1} values but got {array.Length}."; return false; } if (!TryParseBool(array[0], out boolValue)) { error = label + " tuple has invalid boolean '" + array[0] + "' at position 1."; return false; } float[] array2 = new float[floatCount]; for (int i = 0; i < floatCount; i++) { string text = array[i + 1]; if (!TryParseFiniteFloat(text, out array2[i])) { error = $"{label} tuple has invalid number '{text}' at position {i + 2}."; return false; } } values = array2; return true; } internal static bool TryParseBossTuple(string line, out bool boss, out bool dontHideBossHud, out string? bossEvent, out string error) { boss = false; dontHideBossHud = false; bossEvent = null; error = ""; string[] array = SplitTuplePreserveEmpty(line); int num = array.Length; if ((num < 1 || num > 3) ? true : false) { error = $"boss tuple expected 'boss[, dontHideBossHud[, bossEvent]]' but got {array.Length} values."; return false; } if (!TryParseBool(array[0], out boss)) { error = "boss tuple has invalid boolean '" + array[0] + "' at position 1."; return false; } if (array.Length >= 2 && !TryParseBool(array[1], out dontHideBossHud)) { error = "boss tuple has invalid boolean '" + array[1] + "' at position 2."; return false; } if (array.Length >= 3) { bossEvent = array[2]; } return true; } internal static bool TryParseHealthTuple(string line, out float health, out float? regenAllHPTime, out string error) { health = 0f; regenAllHPTime = null; error = ""; string[] array = SplitTuple(line); int num = array.Length; if ((uint)(num - 1) > 1u) { error = $"health tuple expected 'health[, regenAllHPTime]' but got {array.Length} values."; return false; } if (!TryParseFiniteFloat(array[0], out health)) { error = "health tuple has invalid health '" + array[0] + "'."; return false; } if (array.Length == 2) { if (!TryParseFiniteFloat(array[1], out var parsed)) { error = "health tuple has invalid regenAllHPTime '" + array[1] + "'."; return false; } regenAllHPTime = parsed; } return true; } internal static bool TryParseRandomItemTuple(string line, out string prefabName, out float chance, out string error) { prefabName = ""; chance = 0.5f; error = ""; string[] array = SplitTuple(line); int num = array.Length; if ((uint)(num - 1) > 1u) { error = $"randomItems tuple expected 'prefab[, chance]' but got {array.Length} values."; return false; } prefabName = array[0]; if (array.Length == 1) { return true; } if (!TryParseFiniteFloat(array[1], out chance)) { error = "randomItems tuple has invalid chance '" + array[1] + "'."; return false; } chance = Math.Max(0f, Math.Min(1f, chance)); return true; } internal static bool TryParseRandomSetTuple(string line, out string setName, out string[] itemNames, out string error) { setName = ""; itemNames = Array.Empty(); error = ""; string[] array = SplitTuple(line); if (array.Length < 2) { error = $"randomSets tuple expected 'setName, itemPrefab...' but got {array.Length} values."; return false; } setName = array[0]; itemNames = array.Skip(1).ToArray(); return true; } internal static bool TryParseSpawnPrefabEntry(string? value, out string prefabName, out int weight, out string error) { prefabName = ""; weight = 1; error = ""; string text = value?.Trim() ?? ""; if (text.Length == 0) { error = $"entry must use Prefab[:weight] with a non-empty prefab name; omitted weight defaults to 1 and weight must be from 1 to {1000}."; return false; } int num = text.LastIndexOf(':'); if (num < 0) { prefabName = text; return true; } prefabName = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (prefabName.Length == 0) { error = $"entry '{text}' must use Prefab[:weight] with a non-empty prefab name; omitted weight defaults to 1 and weight must be from 1 to {1000}."; return false; } bool flag = !int.TryParse(s, NumberStyles.None, CultureInfo.InvariantCulture, out weight); if (!flag) { int num2 = weight; bool flag2 = ((num2 < 1 || num2 > 1000) ? true : false); flag = flag2; } if (flag) { error = $"entry '{text}' must use Prefab[:weight], where omitted weight defaults to 1 and an explicit weight is an integer from 1 to {1000}."; return false; } return true; } internal static bool TryParseSpawnPrefabEntries(IReadOnlyList? values, out List<(string PrefabName, int Weight)> entries, out string error) { entries = new List<(string, int)>(); error = ""; if (values == null || values.Count <= 0) { error = $"value must be a non-empty Prefab[:weight] list; omitted weight defaults to 1 and weight must be from 1 to {1000}."; return false; } int num = 0; foreach (string value in values) { if (!TryParseSpawnPrefabEntry(value, out string prefabName, out int weight, out error)) { entries.Clear(); return false; } if (num > 4096 - weight) { entries.Clear(); error = $"expanded Prefab[:weight] total must not exceed {4096}; each omitted weight defaults to 1 and each explicit weight must be from 1 to {1000}."; return false; } num += weight; entries.Add((prefabName, weight)); } return true; } private static string[] SplitTuple(string line) { return (from token in line.Split(new char[1] { ',' }).Select(CleanTupleToken) where token.Length > 0 select token).ToArray(); } private static string[] SplitTuplePreserveEmpty(string line) { return line.Split(new char[1] { ',' }).Select(CleanTupleToken).ToArray(); } private static string CleanTupleToken(string token) { string text = token.Trim(); if (text.Length >= 2 && text[0] == '\'' && text[text.Length - 1] == '\'') { return text.Substring(1, text.Length - 2).Replace("''", "'"); } if (text.Length >= 2 && text[0] == '"' && text[text.Length - 1] == '"') { return text.Substring(1, text.Length - 2).Replace("\\\"", "\""); } return text; } internal static bool TryParseAppearanceColor(string value, out Vector3 color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) color = Vector3.one; if (value == null || value.Length != 7 || value[0] != '#' || !int.TryParse(value.Substring(1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) || !int.TryParse(value.Substring(3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2) || !int.TryParse(value.Substring(5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result3)) { return false; } color = new Vector3((float)result / 255f, (float)result2 / 255f, (float)result3 / 255f); return true; } private static bool TryParseBool(string token, out bool value) { return bool.TryParse(token, out value); } } [BepInPlugin("sighsorry.CreatureManager", "CreatureManager", "1.0.7")] [BepInIncompatibility("org.bepinex.plugins.creaturelevelcontrol")] [BepInIncompatibility("MidnightsFX.StarLevelSystem")] [BepInIncompatibility("RustyMods.MonsterDB")] [BepInIncompatibility("warpalicious.MonsterModifiers")] public class CreatureManagerPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } public enum LevelBiomePreset { Easy, Normal, Hard, VeryHard } public enum KarmaSystemMode { Off, KarmaLevelAndEnforcer, KarmaLevelOnly, EnforcerOnly } public enum ModifierIconLayout { FixedCategorySlots, RightPacked } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; } internal const string ModName = "CreatureManager"; internal const string ModVersion = "1.0.7"; internal const string Author = "sighsorry"; internal const string ModGUID = "sighsorry.CreatureManager"; private static readonly string ConfigFileName = "sighsorry.CreatureManager.cfg"; private static readonly string ConfigFileFullPath; private readonly Harmony _harmony = new Harmony("sighsorry.CreatureManager"); internal static readonly ConfigSync ConfigSync; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private const long ReloadDelayTicks = 10000000L; private const float RuntimeMaintenanceInterval = 0.5f; private const float ConfigWatcherRetryInterval = 5f; private float _nextRuntimeMaintenanceTime; private float _nextConfigWatcherRetryTime; private bool _configWatcherRetryPending; private bool _localizerLoadStarted; private bool _configHandlersSubscribed; private bool _harmonyTouched; private bool _feedPatchStarted; private bool _serverLocalizationInitializationStarted; private bool _domainInitializationStarted; private bool _runtimeCleanupPending; private bool _awakeCompleted; private static ConfigEntry _serverConfigLocked; internal static ConfigEntry NormalCreatureNameplateRange; internal static ConfigEntry ShowSneakHoverResistances; internal static ConfigEntry ModifierHudIconLayout; internal static ConfigEntry GenerateSampleTextures; internal static ConfigEntry EnableLevelSystem; internal static ConfigEntry ApplyLevelScaleToSaddleableCreatures; internal static ConfigEntry BiomeLevelPreset; internal static ConfigEntry BossesFollowBiomeLevelPreset; internal static ConfigEntry EnableGlobalModifiers; internal static ConfigEntry EnableBossModifiers; internal static ConfigEntry EnableEnforcerModifiers; internal static ConfigEntry KarmaMode; internal static ConfigEntry MaximumEnforcersPerSector; internal static ConfigEntry BlockEnforcerWhileBossActive; internal static ConfigEntry BlockKarmaGainWhileBossActive; internal static ConfigEntry BlockKarmaGainWhileEnforcerActive; internal static ConfigEntry BlockOmenEnforcerDuringCooldown; internal static ConfigEntry ShowKarmaValueOnMinimap; internal static ConfigEntry MultiplayerHealthIncreasePerPlayer; internal static ConfigEntry MultiplayerDamageIncreasePerPlayer; internal static ConfigEntry MultiplayerMaximumPlayerCount; internal static ConfigEntry BlinkAlertGracePeriod; public static ManualLogSource Log { get; private set; } public void Awake() { Log = ((BaseUnityPlugin)this).Logger; _runtimeCleanupPending = true; bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { _localizerLoadStarted = true; _harmonyTouched = true; Localizer.Load(_harmony); _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, Ordered("If on, the configuration is locked and can be changed by server admins only.", 100)); NormalCreatureNameplateRange = config("1 - General", "Normal Creature Nameplate Range", 30f, Ordered("Distance in meters for normal creature nameplates and health bars. Vanilla is 10. Boss HUD range is not changed by this option.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange(10f, 50f)), synchronizedSetting: false); ShowSneakHoverResistances = config("1 - General", "Show Sneak Hover Resistances", Toggle.On, Ordered("If on, sneaking while hovering a non-tamed creature shows non-Normal and non-Ignore damage modifiers under its nameplate. Uses Normal Creature Nameplate Range.", 80), synchronizedSetting: false); ModifierHudIconLayout = config("1 - General", "Modifier HUD Icon Layout", ModifierIconLayout.FixedCategorySlots, Ordered("FixedCategorySlots keeps the first Offense, Defense, Affliction, and Special icon in its category slot; forced same-category extras fill unused slots so none are hidden. RightPacked removes category gaps and packs every visible icon against the right edge of creature and boss HUDs.", 70), synchronizedSetting: false); GenerateSampleTextures = config("1 - General", "Generate Sample Textures", Toggle.On, Ordered("If on, bundled sample PNGs are created in CreatureManager/textures when missing. Existing files are never overwritten or deleted; Off stops automatic creation but does not disable existing textures.", 60), synchronizedSetting: false); EnableLevelSystem = config("2 - Levels", "Enable Level System", Toggle.On, Ordered("Master switch for CreatureManager level rules, level damage/health scaling, distance scaling, modifiers, and level visuals.", 100)); BiomeLevelPreset = config("2 - Levels", "Biome Level Preset", LevelBiomePreset.Easy, Ordered("Built-in level weights for vanilla biome names when Enable Level System is On. levels.yml lists the biome distributions for every preset; copy or uncomment the desired preset's biome blocks to tune its difficulty as explicit overrides. Explicit biome, group, and prefab rules override this preset; Global remains the fallback for non-boss creatures and Enforcers.", 90)); BossesFollowBiomeLevelPreset = config("2 - Levels", "Bosses Follow Biome Level Preset", Toggle.On, Ordered("If on, regular bosses can use the built-in Biome Level Preset as a level fallback when no Boss, group, or prefab level rule applies. Other omitted boss fields never fall back to Global.", 85)); ApplyLevelScaleToSaddleableCreatures = config("2 - Levels", "Apply Level Scale To Saddle-able Creatures", Toggle.On, Ordered("If off, levels.yml scalePerLevel is not applied to creatures that can use a saddle.", 70)); EnableGlobalModifiers = config("2 - Levels", "Global Modifiers", Toggle.On, Ordered("Master switch for modifier rolls and effects on non-boss creatures. Karma Enforcers are controlled separately by Enforcer Modifiers.", 69)); EnableBossModifiers = config("2 - Levels", "Boss Modifiers", Toggle.On, Ordered("Master switch for modifier rolls and effects on regular boss creatures. Karma Enforcers are controlled separately by Enforcer Modifiers.", 68)); EnableEnforcerModifiers = config("2 - Levels", "Enforcer Modifiers", Toggle.On, Ordered("Master switch for modifier rolls, effects, and modifier HUD icons on Karma Enforcers. This does not disable Enforcer summoning, level bonuses, or loot settings.", 67)); KarmaMode = config("3 - Karma", "Karma System Mode", KarmaSystemMode.KarmaLevelAndEnforcer, Ordered("Off disables Karma runtime processing without deleting stored values. KarmaLevelAndEnforcer enables both features, KarmaLevelOnly disables Enforcer summons, and EnforcerOnly tracks Karma for summons without adding Karma levels to normal spawns.", 100)); MaximumEnforcersPerSector = config("3 - Karma", "Maximum Enforcers Per Sector", 1, Ordered("Maximum active Enforcers allowed in the same fixed 3x3 Karma neighborhood.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange(1, 20))); BlockEnforcerWhileBossActive = config("3 - Karma", "Block Enforcer While Boss Is Active", Toggle.On, Ordered("If on, Enforcer summons are blocked while a non-Enforcer boss is active in the same fixed 3x3 Karma neighborhood.", 80)); BlockKarmaGainWhileBossActive = config("3 - Karma", "Block Karma Gain While Boss Is Active", Toggle.On, Ordered("If on, creature kills do not add Karma while a non-Enforcer boss is alive in the same fixed 3x3 Karma neighborhood. Killing the last active boss can still award Karma.", 75)); BlockKarmaGainWhileEnforcerActive = config("3 - Karma", "Block Karma Gain While Enforcer Is Active", Toggle.On, Ordered("If on, creature kills do not add Karma while an Enforcer is alive in the same fixed 3x3 Karma neighborhood.", 74)); BlockOmenEnforcerDuringCooldown = config("3 - Karma", "Block Omen Enforcer During Cooldown", Toggle.On, Ordered("If on, Omen cannot summon an Enforcer while the target Karma region's Enforcer cooldown is active. Omen still bypasses the normal summon chance and required Karma.", 73)); ShowKarmaValueOnMinimap = config("3 - Karma", "Show Karma Value On Minimap", Toggle.On, Ordered("If on, the minimap Karma label includes the current Karma value, for example 'Karma Lv. 2 (137)'.", 70), synchronizedSetting: false); MultiplayerHealthIncreasePerPlayer = config("4 - Multiplayer Difficulty", "HP Increase Per Player In Multiplayer (%)", 30f, Ordered("Extra creature effective health per nearby player after the first. Vanilla is 30%. This does not increase max health directly; vanilla applies it by reducing damage taken, while floating damage text generally shows the pre-scaling damage.", 100, (AcceptableValueBase?)(object)new AcceptableValueRange(0f, 200f))); MultiplayerDamageIncreasePerPlayer = config("4 - Multiplayer Difficulty", "DMG Increase Per Player In Multiplayer (%)", 4f, Ordered("Extra creature damage per nearby player after the first. Vanilla is 4%.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange(0f, 200f))); MultiplayerMaximumPlayerCount = config("4 - Multiplayer Difficulty", "Maximum Player Count For Multiplayer Scaling", 5, Ordered("Maximum nearby player count used by vanilla multiplayer difficulty scaling. Vanilla is 5.", 80, (AcceptableValueBase?)(object)new AcceptableValueRange(1, 25))); BlinkAlertGracePeriod = config("5 - Modifiers", "Blink Alert Grace Period (s)", 3f, Ordered("Seconds after a creature becomes alerted during which Blink and its extended attack range are disabled. The timer expires even when no attack can start. Set to 0 for immediate Blink behavior.", 100, (AcceptableValueBase?)(object)new AcceptableValueRange(0f, 10f))); _configHandlersSubscribed = true; EnableLevelSystem.SettingChanged += ReloadLevelConfiguration; BiomeLevelPreset.SettingChanged += ReloadLevelConfiguration; NormalCreatureNameplateRange.SettingChanged += ApplyRuntimeConfigValues; GenerateSampleTextures.SettingChanged += ApplySampleTextureSetting; MultiplayerHealthIncreasePerPlayer.SettingChanged += ApplyRuntimeConfigValues; MultiplayerDamageIncreasePerPlayer.SettingChanged += ApplyRuntimeConfigValues; MultiplayerMaximumPlayerCount.SettingChanged += ApplyRuntimeConfigValues; ConfigSync.AddLockingConfigEntry(_serverConfigLocked); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); _feedPatchStarted = true; CreatureManagerFeedLikeGrandmaPokeballReleasePatch.ApplyIfAvailable(_harmony); _serverLocalizationInitializationStarted = true; CreatureServerLocalization.Initialize(ConfigSync); _domainInitializationStarted = true; CreatureDomainManager.Initialize(ConfigSync); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); CreatureLevelManager.RegisterRpcs(); CreatureModifierManager.RegisterRpcs(); _awakeCompleted = true; } catch { CleanupRuntime(saveConfig: false); throw; } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private static void ApplyRuntimeConfigValues(object sender, EventArgs args) { CreatureGameSettings.ApplyAll(); } private static void ApplySampleTextureSetting(object sender, EventArgs args) { CreatureDomainManager.EnsureDefaultTextures(); } private static void ReloadLevelConfiguration(object sender, EventArgs args) { CreatureDomainManager.RequestConfigurationReload(); } private void Update() { CreatureServerLocalization.Update(); CreatureDomainManager.Update(); TryRecoverConfigWatcher(); if (!(Time.time < _nextRuntimeMaintenanceTime)) { _nextRuntimeMaintenanceTime = Time.time + 0.5f; CreatureManagerFeedLikeGrandmaPokeballReleasePatch.ApplyIfAvailable(_harmony, lateAttempt: true); CreatureLevelManager.RegisterRpcs(); CreatureKarmaManager.RegisterRpcs(); CreatureModifierManager.RegisterRpcs(); CreatureLevelManager.UpdatePendingApplications(); CreatureModifierManager.PruneServerNetworkState(); CreatureKarmaManager.UpdateSummons(); } } private void OnDestroy() { CleanupRuntime(_awakeCompleted); _awakeCompleted = false; } private void CleanupRuntime(bool saveConfig) { if (_configHandlersSubscribed) { _configHandlersSubscribed = false; TryCleanup("unsubscribe configuration handlers", UnsubscribeConfigHandlers); } FileSystemWatcher watcher = _watcher; _watcher = null; _configWatcherRetryPending = false; _nextConfigWatcherRetryTime = 0f; if (watcher != null) { TryCleanup("dispose the configuration watcher", watcher.Dispose); } if (saveConfig) { TryCleanup("save the configuration", delegate { SaveWithRespectToConfigSet(); }); } if (_domainInitializationStarted) { _domainInitializationStarted = false; TryCleanup("dispose the YAML domain manager", CreatureDomainManager.Dispose); } if (_serverLocalizationInitializationStarted) { _serverLocalizationInitializationStarted = false; TryCleanup("dispose server localization", CreatureServerLocalization.Dispose); } if (_runtimeCleanupPending) { _runtimeCleanupPending = false; TryCleanup("remove the Karma minimap HUD", CreatureKarmaMinimapHud.Clear); TryCleanup("reset spawn lifecycle state", CreatureManagerSpawnLifecycle.ResetRuntimeState); TryCleanup("reset Karma runtime state", CreatureKarmaManager.ResetRuntimeState); TryCleanup("reset modifier runtime state", CreatureModifierManager.ResetRuntimeState); TryCleanup("reset level runtime state", CreatureLevelManager.ResetRuntimeState); } if (_feedPatchStarted) { _feedPatchStarted = false; TryCleanup("reset FeedLikeGrandma compatibility state", CreatureManagerFeedLikeGrandmaPokeballReleasePatch.Reset); } if (_harmonyTouched) { _harmonyTouched = false; TryCleanup("remove Harmony patches", (Action)_harmony.UnpatchSelf); } if (_localizerLoadStarted) { _localizerLoadStarted = false; TryCleanup("unload localization", Localizer.Unload); } } private static void UnsubscribeConfigHandlers() { if (EnableLevelSystem != null) { EnableLevelSystem.SettingChanged -= ReloadLevelConfiguration; } if (BiomeLevelPreset != null) { BiomeLevelPreset.SettingChanged -= ReloadLevelConfiguration; } if (NormalCreatureNameplateRange != null) { NormalCreatureNameplateRange.SettingChanged -= ApplyRuntimeConfigValues; } if (GenerateSampleTextures != null) { GenerateSampleTextures.SettingChanged -= ApplySampleTextureSetting; } if (MultiplayerHealthIncreasePerPlayer != null) { MultiplayerHealthIncreasePerPlayer.SettingChanged -= ApplyRuntimeConfigValues; } if (MultiplayerDamageIncreasePerPlayer != null) { MultiplayerDamageIncreasePerPlayer.SettingChanged -= ApplyRuntimeConfigValues; } if (MultiplayerMaximumPlayerCount != null) { MultiplayerMaximumPlayerCount.SettingChanged -= ApplyRuntimeConfigValues; } } private void TryCleanup(string operation, Action cleanup) { try { cleanup(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to {operation} during CreatureManager shutdown: {arg}"); } } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = null; try { fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName) { IncludeSubdirectories = true, SynchronizingObject = ThreadingHelper.SynchronizingObject }; fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.Error += OnConfigWatcherError; fileSystemWatcher.EnableRaisingEvents = true; FileSystemWatcher? watcher = _watcher; _watcher = fileSystemWatcher; fileSystemWatcher = null; watcher?.Dispose(); _configWatcherRetryPending = false; _nextConfigWatcherRetryTime = 0f; } catch (Exception ex) { fileSystemWatcher?.Dispose(); _configWatcherRetryPending = true; _nextConfigWatcherRetryTime = Time.unscaledTime + 5f; Log.LogError((object)$"Failed to create the configuration watcher; retrying in {5f:0} seconds: {ex.Message}"); } } private void OnConfigWatcherError(object sender, ErrorEventArgs args) { if (_runtimeCleanupPending && sender == _watcher) { Log.LogWarning((object)("Configuration watcher lost file events and will be rebuilt: " + args.GetException().Message)); FileSystemWatcher watcher = _watcher; _watcher = null; try { watcher?.Dispose(); } catch (Exception ex) { Log.LogWarning((object)("Failed to dispose the broken configuration watcher: " + ex.Message)); } SetupWatcher(); if (_watcher != null) { ReloadConfigValues(force: true); } } } private void TryRecoverConfigWatcher() { if (_configWatcherRetryPending && _watcher == null && !(Time.unscaledTime < _nextConfigWatcherRetryTime)) { SetupWatcher(); if (_watcher != null) { ReloadConfigValues(force: true); } } } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (_runtimeCleanupPending && sender == _watcher) { ReloadConfigValues(force: false); } } private void ReloadConfigValues(bool force) { DateTime now = DateTime.Now; long num = now.Ticks - _lastConfigReloadTime.Ticks; if (!force && num < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { Log.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { Log.LogDebug((object)"Reloading configuration..."); SaveWithRespectToConfigSet(reload: true); Log.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { Log.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private static ConfigDescription Ordered(string description, int order, AcceptableValueBase? acceptableValues = null) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private ConfigEntry config(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind(group, name, value, val); ConfigSync.AddConfigEntry(val2).SynchronizedConfig = synchronizedSetting; return val2; } static CreatureManagerPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; Log = null; ConfigSync = new ConfigSync("sighsorry.CreatureManager") { DisplayName = "CreatureManager", CurrentVersion = "1.0.7", MinimumRequiredVersion = "1.0.7", ModRequired = true }; _serverConfigLocked = null; NormalCreatureNameplateRange = null; ShowSneakHoverResistances = null; ModifierHudIconLayout = null; GenerateSampleTextures = null; EnableLevelSystem = null; ApplyLevelScaleToSaddleableCreatures = null; BiomeLevelPreset = null; BossesFollowBiomeLevelPreset = null; EnableGlobalModifiers = null; EnableBossModifiers = null; EnableEnforcerModifiers = null; KarmaMode = null; MaximumEnforcersPerSector = null; BlockEnforcerWhileBossActive = null; BlockKarmaGainWhileBossActive = null; BlockKarmaGainWhileEnforcerActive = null; BlockOmenEnforcerDuringCooldown = null; ShowKarmaValueOnMinimap = null; MultiplayerHealthIncreasePerPlayer = null; MultiplayerDamageIncreasePerPlayer = null; MultiplayerMaximumPlayerCount = null; BlinkAlertGracePeriod = null; } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry(ConfigEntry sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry SourceConfig = sourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register(configSync2.Name + " ConfigSync", (Action)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } static void SendAdmin(List peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List CurrentList = new List(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List(adminList.GetList()); List adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary configValues = new Dictionary(); public readonly Dictionary customValues = new Dictionary(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List Package = new List(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List entries = new List(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet configSyncs; private readonly HashSet allConfigs = new HashSet(); private HashSet allCustomValues = new HashSet(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary> configValueCache = new Dictionary>(); private readonly List> cacheExpirations = new List>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0052: result = 0; goto IL_0053; IL_0053: return (byte)result != 0; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; public event Action? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry AddConfigEntry(ConfigEntry configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry syncedEntry = ownConfigEntryBase as SyncedConfigEntry; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty()).Concat(new SyncedConfigEntry[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry AddLockingConfigEntry(ConfigEntry lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(delegate(KeyValuePair kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary value)) { value = new SortedDictionary(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); ConfigFile val2 = null; bool saveOnConfigSet = false; foreach (KeyValuePair configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val2 == null) { val2 = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val2.SaveOnConfigSet; val2.SaveOnConfigSet = false; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } if (val2 != null) { val2.SaveOnConfigSet = saveOnConfigSet; val2.Save(); } foreach (KeyValuePair customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int num2 = 0; num2 < num; num2++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null || (!configSync.IsLocked && (config != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private IEnumerator distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + (data.LongLength - 1) / 250000); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer.m_socket.IsConnected()) { ZPackage fragmentedPackage = new ZPackage(); fragmentedPackage.Write((byte)2); fragmentedPackage.Write(packageIdentifier); fragmentedPackage.Write(fragment); fragmentedPackage.Write(fragments); fragmentedPackage.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(fragmentedPackage); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer.m_server ? 0 : peer.m_uid, text, new object[1] { pkg }); } } IEnumerable waitForQueue() { float timeout = Time.time + 30f; while (peer.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer.m_uid} after 30 seconds config sending timeout"); peer.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty().GetEnumerator(); } List list = (List)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List peers, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] rawData = package.GetArray(); if (rawData != null && rawData.LongLength > 10000) { ZPackage compressedPackage = new ZPackage(); compressedPackage.Write((byte)4); MemoryStream output = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(output, CompressionLevel.Optimal)) { deflateStream.Write(rawData, 0, rawData.Length); } compressedPackage.Write(output.ToArray()); package = compressedPackage; } List> writers = (from p in peers where p.IsReady() select distributeConfigToPeers(p, package)).ToList(); writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType().SingleOrDefault(); } public static SyncedConfigEntry? ConfigData(ConfigEntry config) { return ((ConfigEntryBase)config).Description.Tags?.OfType>().SingleOrDefault(); } private static T configAttribute(ConfigEntryBase config) { return config.Description.Tags.OfType().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage(IEnumerable? configs = null, IEnumerable? customValues = null, IEnumerable? packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List(); List list2 = customValues?.ToList() ?? new List(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List source = new List(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [PublicAPI] [HarmonyPatch] internal class VersionCheck { private static readonly HashSet versionChecks; private static readonly Dictionary notProcessedNames; public string Name; private string? displayName; private string? currentVersion; private string? minimumRequiredVersion; public bool ModRequired = true; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; private readonly List ValidatedClients = new List(); private ConfigSync? ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet(); notProcessedNames = new Dictionary(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion); bool flag2 = new System.Version(ReceivedCurrentVersion) >= new System.Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; foreach (VersionCheck versionCheck in array2) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action action = (Action)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register("ServerSync VersionCheck", (Action)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register("ServerSync VersionCheck", (Action)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair item in notProcessedNames.OrderBy, string>((KeyValuePair kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } namespace YamlDotNet { internal sealed class CultureInfoAdapter : CultureInfo { private readonly IFormatProvider provider; public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) : base(baseCulture.Name) { this.provider = provider; } public override object? GetFormat(Type formatType) { return provider.GetFormat(formatType); } } internal static class Polyfills { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool Contains(this string source, char c) { return source.IndexOf(c) != -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EndsWith(this string source, char c) { if (source.Length > 0) { return source[source.Length - 1] == c; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool StartsWith(this string source, char c) { if (source.Length > 0) { return source[0] == c; } return false; } } internal static class PropertyInfoExtensions { public static object? ReadValue(this PropertyInfo property, object target) { return property.GetValue(target, null); } } internal static class ReflectionExtensions { private static readonly Func IsInstance = (PropertyInfo property) => !(property.GetMethod ?? property.SetMethod).IsStatic; private static readonly Func IsInstancePublic = (PropertyInfo property) => IsInstance(property) && (property.GetMethod ?? property.SetMethod).IsPublic; public static Type? BaseType(this Type type) { return type.GetTypeInfo().BaseType; } public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; } public static bool IsGenericType(this Type type) { return type.GetTypeInfo().IsGenericType; } public static bool IsGenericTypeDefinition(this Type type) { return type.GetTypeInfo().IsGenericTypeDefinition; } public static Type? GetImplementationOfOpenGenericInterface(this Type type, Type openGenericType) { if (!openGenericType.IsGenericType || !openGenericType.IsInterface) { throw new ArgumentException("The type must be a generic type definition and an interface", "openGenericType"); } if (IsGenericDefinitionOfType(type, openGenericType)) { return type; } return type.FindInterfaces((Type t, object context) => IsGenericDefinitionOfType(t, context), openGenericType).FirstOrDefault(); static bool IsGenericDefinitionOfType(Type t, object? context) { if (t.IsGenericType) { return t.GetGenericTypeDefinition() == (Type)context; } return false; } } public static bool IsInterface(this Type type) { return type.GetTypeInfo().IsInterface; } public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; } public static bool IsRequired(this MemberInfo member) { return member.GetCustomAttributes(inherit: true).Any((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute"); } public static bool HasDefaultConstructor(this Type type, bool allowPrivateConstructors) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (allowPrivateConstructors) { bindingFlags |= BindingFlags.NonPublic; } if (!type.IsValueType) { return type.GetConstructor(bindingFlags, null, Type.EmptyTypes, null) != null; } return true; } public static bool IsAssignableFrom(this Type type, Type source) { return type.IsAssignableFrom(source.GetTypeInfo()); } public static bool IsAssignableFrom(this Type type, TypeInfo source) { return type.GetTypeInfo().IsAssignableFrom(source); } public static TypeCode GetTypeCode(this Type type) { if (type.IsEnum()) { type = Enum.GetUnderlyingType(type); } if (type == typeof(bool)) { return TypeCode.Boolean; } if (type == typeof(char)) { return TypeCode.Char; } if (type == typeof(sbyte)) { return TypeCode.SByte; } if (type == typeof(byte)) { return TypeCode.Byte; } if (type == typeof(short)) { return TypeCode.Int16; } if (type == typeof(ushort)) { return TypeCode.UInt16; } if (type == typeof(int)) { return TypeCode.Int32; } if (type == typeof(uint)) { return TypeCode.UInt32; } if (type == typeof(long)) { return TypeCode.Int64; } if (type == typeof(ulong)) { return TypeCode.UInt64; } if (type == typeof(float)) { return TypeCode.Single; } if (type == typeof(double)) { return TypeCode.Double; } if (type == typeof(decimal)) { return TypeCode.Decimal; } if (type == typeof(DateTime)) { return TypeCode.DateTime; } if (type == typeof(string)) { return TypeCode.String; } return TypeCode.Object; } public static bool IsDbNull(this object value) { return value?.GetType()?.FullName == "System.DBNull"; } public static Type[] GetGenericArguments(this Type type) { return type.GetTypeInfo().GenericTypeArguments; } public static PropertyInfo? GetPublicProperty(this Type type, string name) { return type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public).FirstOrDefault((PropertyInfo p) => p.Name == name); } public static FieldInfo? GetPublicStaticField(this Type type, string name) { return type.GetRuntimeField(name); } public static IEnumerable GetProperties(this Type type, bool includeNonPublic) { Func predicate = (includeNonPublic ? IsInstance : IsInstancePublic); if (!type.IsInterface()) { return type.GetRuntimeProperties().Where(predicate); } return new Type[1] { type }.Concat(type.GetInterfaces()).SelectMany((Type i) => i.GetRuntimeProperties().Where(predicate)); } public static IEnumerable GetPublicProperties(this Type type) { return type.GetProperties(includeNonPublic: false); } public static IEnumerable GetPublicFields(this Type type) { return from f in type.GetRuntimeFields() where !f.IsStatic && f.IsPublic select f; } public static IEnumerable GetPublicStaticMethods(this Type type) { return from m in type.GetRuntimeMethods() where m.IsPublic && m.IsStatic select m; } public static MethodInfo GetPrivateStaticMethod(this Type type, string name) { return type.GetRuntimeMethods().FirstOrDefault((MethodInfo m) => !m.IsPublic && m.IsStatic && m.Name.Equals(name)) ?? throw new MissingMethodException("Expected to find a method named '" + name + "' in '" + type.FullName + "'."); } public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) { return type.GetRuntimeMethods().FirstOrDefault(delegate(MethodInfo m) { if (m.IsPublic && m.IsStatic && m.Name.Equals(name)) { ParameterInfo[] parameters = m.GetParameters(); if (parameters.Length == parameterTypes.Length) { return parameters.Zip(parameterTypes, (ParameterInfo pi, Type pt) => pi.ParameterType == pt).All((bool r) => r); } return false; } return false; }); } public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) { return type.GetRuntimeMethods().FirstOrDefault((MethodInfo m) => m.IsPublic && !m.IsStatic && m.Name.Equals(name)); } public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) { MethodInfo methodInfo = property.GetMethod; if (!nonPublic && !methodInfo.IsPublic) { methodInfo = null; } return methodInfo; } public static MethodInfo? GetSetMethod(this PropertyInfo property) { return property.SetMethod; } public static IEnumerable GetInterfaces(this Type type) { return type.GetTypeInfo().ImplementedInterfaces; } public static bool IsInstanceOf(this Type type, object o) { if (!(o.GetType() == type)) { return o.GetType().GetTypeInfo().IsSubclassOf(type); } return true; } public static Attribute[] GetAllCustomAttributes(this PropertyInfo member) { return Attribute.GetCustomAttributes(member, typeof(TAttribute), inherit: true); } public static bool AcceptsNull(this MemberInfo member) { object[] customAttributes = member.DeclaringType.GetCustomAttributes(inherit: true); object obj = customAttributes.FirstOrDefault((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute"); int num = 0; if (obj != null) { Type type = obj.GetType(); PropertyInfo property = type.GetProperty("Flag"); num = (byte)property.GetValue(obj); } object[] customAttributes2 = member.GetCustomAttributes(inherit: true); object obj2 = customAttributes2.FirstOrDefault((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute"); PropertyInfo propertyInfo = (obj2?.GetType())?.GetProperty("NullableFlags"); byte[] source = (byte[])propertyInfo.GetValue(obj2); return source.Any((byte x) => x == 2) || num == 2; } } internal static class StandardRegexOptions { public const RegexOptions Compiled = RegexOptions.Compiled; } } namespace YamlDotNet.Serialization { internal abstract class BuilderSkeleton where TBuilder : BuilderSkeleton { internal INamingConvention namingConvention = NullNamingConvention.Instance; internal INamingConvention enumNamingConvention = NullNamingConvention.Instance; internal ITypeResolver typeResolver; internal readonly YamlAttributeOverrides overrides; internal readonly LazyComponentRegistrationList typeConverterFactories; internal readonly LazyComponentRegistrationList typeInspectorFactories; internal bool ignoreFields; internal bool includeNonPublicProperties; internal Settings settings; internal YamlFormatter yamlFormatter = YamlFormatter.Default; protected abstract TBuilder Self { get; } internal BuilderSkeleton(ITypeResolver typeResolver) { overrides = new YamlAttributeOverrides(); typeConverterFactories = new LazyComponentRegistrationList { { typeof(YamlDotNet.Serialization.Converters.GuidConverter), (Nothing _) => new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: false) }, { typeof(SystemTypeConverter), (Nothing _) => new SystemTypeConverter() } }; typeInspectorFactories = new LazyComponentRegistrationList(); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); settings = new Settings(); } public TBuilder IgnoreFields() { ignoreFields = true; return Self; } public TBuilder IncludeNonPublicProperties() { includeNonPublicProperties = true; return Self; } public TBuilder EnablePrivateConstructors() { settings.AllowPrivateConstructors = true; return Self; } public TBuilder WithNamingConvention(INamingConvention namingConvention) { this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); return Self; } public TBuilder WithEnumNamingConvention(INamingConvention enumNamingConvention) { this.enumNamingConvention = enumNamingConvention; return Self; } public TBuilder WithTypeResolver(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); return Self; } public abstract TBuilder WithTagMapping(TagName tag, Type type); public TBuilder WithAttributeOverride(Expression> propertyAccessor, Attribute attribute) { overrides.Add(propertyAccessor, attribute); return Self; } public TBuilder WithAttributeOverride(Type type, string member, Attribute attribute) { overrides.Add(type, member, attribute); return Self; } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) { return WithTypeConverter(typeConverter, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter, Action> where) { if (typeConverter == null) { throw new ArgumentNullException("typeConverter"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), (Nothing _) => typeConverter)); return Self; } public TBuilder WithTypeConverter(WrapperFactory typeConverterFactory, Action> where) where TYamlTypeConverter : IYamlTypeConverter { if (typeConverterFactory == null) { throw new ArgumentNullException("typeConverterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (IYamlTypeConverter wrapped, Nothing _) => typeConverterFactory(wrapped))); return Self; } public TBuilder WithoutTypeConverter() where TYamlTypeConverter : IYamlTypeConverter { return WithoutTypeConverter(typeof(TYamlTypeConverter)); } public TBuilder WithoutTypeConverter(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } typeConverterFactories.Remove(converterType); return Self; } public TBuilder WithTypeInspector(Func typeInspectorFactory) where TTypeInspector : ITypeInspector { return WithTypeInspector(typeInspectorFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeInspector(Func typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector inner) => typeInspectorFactory(inner))); return Self; } public TBuilder WithTypeInspector(WrapperFactory typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector wrapped, ITypeInspector inner) => typeInspectorFactory(wrapped, inner))); return Self; } public TBuilder WithoutTypeInspector() where TTypeInspector : ITypeInspector { return WithoutTypeInspector(typeof(TTypeInspector)); } public TBuilder WithoutTypeInspector(Type inspectorType) { if (inspectorType == null) { throw new ArgumentNullException("inspectorType"); } typeInspectorFactories.Remove(inspectorType); return Self; } public TBuilder WithYamlFormatter(YamlFormatter formatter) { yamlFormatter = formatter ?? throw new ArgumentNullException("formatter"); return Self; } protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); } } internal delegate TComponent WrapperFactory(TComponentBase wrapped) where TComponent : TComponentBase; internal delegate TComponent WrapperFactory(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase; [Flags] internal enum DefaultValuesHandling { Preserve = 0, OmitNull = 1, OmitDefaults = 2, OmitEmptyCollections = 4 } internal sealed class Deserializer : IDeserializer { private readonly IValueDeserializer valueDeserializer; public Deserializer() : this(new DeserializerBuilder().BuildValueDeserializer()) { } private Deserializer(IValueDeserializer valueDeserializer) { this.valueDeserializer = valueDeserializer ?? throw new ArgumentNullException("valueDeserializer"); } public static Deserializer FromValueDeserializer(IValueDeserializer valueDeserializer) { return new Deserializer(valueDeserializer); } public T Deserialize(string input) { using StringReader input2 = new StringReader(input); return Deserialize(input2); } public T Deserialize(TextReader input) { return Deserialize(new Parser(input)); } public T Deserialize(IParser parser) { return (T)Deserialize(parser, typeof(T)); } public object? Deserialize(string input) { return Deserialize(input); } public object? Deserialize(TextReader input) { return Deserialize(input); } public object? Deserialize(IParser parser) { return Deserialize(parser); } public object? Deserialize(string input, Type type) { using StringReader input2 = new StringReader(input); return Deserialize(input2, type); } public object? Deserialize(TextReader input, Type type) { return Deserialize(new Parser(input), type); } public object? Deserialize(IParser parser, Type type) { if (parser == null) { throw new ArgumentNullException("parser"); } if (type == null) { throw new ArgumentNullException("type"); } YamlDotNet.Core.Events.StreamStart @event; bool flag = parser.TryConsume(out @event); YamlDotNet.Core.Events.DocumentStart event2; bool flag2 = parser.TryConsume(out event2); object result = null; if (!parser.Accept(out var _) && !parser.Accept(out var _)) { using SerializerState serializerState = new SerializerState(); result = valueDeserializer.DeserializeValue(parser, type, serializerState, valueDeserializer); serializerState.OnDeserialization(); } if (flag2) { parser.Consume(); } if (flag) { parser.Consume(); } return result; } } internal sealed class DeserializerBuilder : BuilderSkeleton { private Lazy objectFactory; private readonly LazyComponentRegistrationList nodeDeserializerFactories; private readonly LazyComponentRegistrationList nodeTypeResolverFactories; private readonly Dictionary tagMappings; private readonly Dictionary typeMappings; private readonly ITypeConverter typeConverter; private bool ignoreUnmatched; private bool duplicateKeyChecking; private bool attemptUnknownTypeDeserialization; private bool enforceNullability; private bool caseInsensitivePropertyMatching; private bool enforceRequiredProperties; protected override DeserializerBuilder Self => this; public DeserializerBuilder() : base((ITypeResolver)new StaticTypeResolver()) { typeMappings = new Dictionary(); objectFactory = new Lazy(() => new DefaultObjectFactory(typeMappings, settings), isThreadSafe: true); tagMappings = new Dictionary { { FailsafeSchema.Tags.Map, typeof(Dictionary) }, { FailsafeSchema.Tags.Str, typeof(string) }, { JsonSchema.Tags.Bool, typeof(bool) }, { JsonSchema.Tags.Float, typeof(double) }, { JsonSchema.Tags.Int, typeof(int) }, { DefaultSchema.Tags.Timestamp, typeof(DateTime) } }; typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), (ITypeInspector inner) => (overrides == null) ? inner : new YamlAttributeOverridesInspector(inner, overrides.Clone())); typeInspectorFactories.Add(typeof(ReadableAndWritablePropertiesTypeInspector), (ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner)); nodeDeserializerFactories = new LazyComponentRegistrationList { { typeof(YamlConvertibleNodeDeserializer), (Nothing _) => new YamlConvertibleNodeDeserializer(objectFactory.Value) }, { typeof(YamlSerializableNodeDeserializer), (Nothing _) => new YamlSerializableNodeDeserializer(objectFactory.Value) }, { typeof(TypeConverterNodeDeserializer), (Nothing _) => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), (Nothing _) => new NullNodeDeserializer() }, { typeof(ScalarNodeDeserializer), (Nothing _) => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, BuildTypeInspector(), yamlFormatter, enumNamingConvention) }, { typeof(ArrayNodeDeserializer), (Nothing _) => new ArrayNodeDeserializer(enumNamingConvention, BuildTypeInspector()) }, { typeof(DictionaryNodeDeserializer), (Nothing _) => new DictionaryNodeDeserializer(objectFactory.Value, duplicateKeyChecking) }, { typeof(CollectionNodeDeserializer), (Nothing _) => new CollectionNodeDeserializer(objectFactory.Value, enumNamingConvention, BuildTypeInspector()) }, { typeof(EnumerableNodeDeserializer), (Nothing _) => new EnumerableNodeDeserializer() }, { typeof(ObjectNodeDeserializer), (Nothing _) => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking, typeConverter, enumNamingConvention, enforceNullability, caseInsensitivePropertyMatching, enforceRequiredProperties, BuildTypeConverters()) }, { typeof(FsharpListNodeDeserializer), (Nothing _) => new FsharpListNodeDeserializer(BuildTypeInspector(), enumNamingConvention) } }; nodeTypeResolverFactories = new LazyComponentRegistrationList { { typeof(MappingNodeTypeResolver), (Nothing _) => new MappingNodeTypeResolver(typeMappings) }, { typeof(YamlConvertibleTypeResolver), (Nothing _) => new YamlConvertibleTypeResolver() }, { typeof(YamlSerializableTypeResolver), (Nothing _) => new YamlSerializableTypeResolver() }, { typeof(TagNodeTypeResolver), (Nothing _) => new TagNodeTypeResolver(tagMappings) }, { typeof(PreventUnknownTagsNodeTypeResolver), (Nothing _) => new PreventUnknownTagsNodeTypeResolver() }, { typeof(DefaultContainersNodeTypeResolver), (Nothing _) => new DefaultContainersNodeTypeResolver() } }; typeConverter = new ReflectionTypeConverter(); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = new WritablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); if (!ignoreFields) { typeInspector = new CompositeTypeInspector(new ReadableFieldsTypeInspector(typeResolver), typeInspector); } return typeInspectorFactories.BuildComponentChain(typeInspector); } public DeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() { attemptUnknownTypeDeserialization = true; return this; } public DeserializerBuilder WithObjectFactory(IObjectFactory objectFactory) { if (objectFactory == null) { throw new ArgumentNullException("objectFactory"); } this.objectFactory = new Lazy(() => objectFactory, isThreadSafe: true); return this; } public DeserializerBuilder WithObjectFactory(Func objectFactory) { if (objectFactory == null) { throw new ArgumentNullException("objectFactory"); } return WithObjectFactory(new LambdaObjectFactory(objectFactory)); } public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) { return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer, Action> where) { if (nodeDeserializer == null) { throw new ArgumentNullException("nodeDeserializer"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), (Nothing _) => nodeDeserializer)); return this; } public DeserializerBuilder WithNodeDeserializer(WrapperFactory nodeDeserializerFactory, Action> where) where TNodeDeserializer : INodeDeserializer { if (nodeDeserializerFactory == null) { throw new ArgumentNullException("nodeDeserializerFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (INodeDeserializer wrapped, Nothing _) => nodeDeserializerFactory(wrapped))); return this; } public DeserializerBuilder WithoutNodeDeserializer() where TNodeDeserializer : INodeDeserializer { return WithoutNodeDeserializer(typeof(TNodeDeserializer)); } public DeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) { if (nodeDeserializerType == null) { throw new ArgumentNullException("nodeDeserializerType"); } nodeDeserializerFactories.Remove(nodeDeserializerType); return this; } public DeserializerBuilder WithTypeDiscriminatingNodeDeserializer(Action configureTypeDiscriminatingNodeDeserializerOptions, int maxDepth = -1, int maxLength = -1) { TypeDiscriminatingNodeDeserializerOptions typeDiscriminatingNodeDeserializerOptions = new TypeDiscriminatingNodeDeserializerOptions(); configureTypeDiscriminatingNodeDeserializerOptions(typeDiscriminatingNodeDeserializerOptions); TypeDiscriminatingNodeDeserializer nodeDeserializer = new TypeDiscriminatingNodeDeserializer(nodeDeserializerFactories.BuildComponentList(), typeDiscriminatingNodeDeserializerOptions.discriminators, maxDepth, maxLength); return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax s) { s.Before(); }); } public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) { return WithNodeTypeResolver(nodeTypeResolver, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver, Action> where) { if (nodeTypeResolver == null) { throw new ArgumentNullException("nodeTypeResolver"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), (Nothing _) => nodeTypeResolver)); return this; } public DeserializerBuilder WithNodeTypeResolver(WrapperFactory nodeTypeResolverFactory, Action> where) where TNodeTypeResolver : INodeTypeResolver { if (nodeTypeResolverFactory == null) { throw new ArgumentNullException("nodeTypeResolverFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (INodeTypeResolver wrapped, Nothing _) => nodeTypeResolverFactory(wrapped))); return this; } public DeserializerBuilder WithCaseInsensitivePropertyMatching() { caseInsensitivePropertyMatching = true; return this; } public DeserializerBuilder WithEnforceNullability() { enforceNullability = true; return this; } public DeserializerBuilder WithEnforceRequiredMembers() { enforceRequiredProperties = true; return this; } public DeserializerBuilder WithoutNodeTypeResolver() where TNodeTypeResolver : INodeTypeResolver { return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); } public DeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) { if (nodeTypeResolverType == null) { throw new ArgumentNullException("nodeTypeResolverType"); } nodeTypeResolverFactories.Remove(nodeTypeResolverType); return this; } public override DeserializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(tag, out Type value)) { throw new ArgumentException($"Type already has a registered type '{value.FullName}' for tag '{tag}'", "tag"); } tagMappings.Add(tag, type); return this; } public DeserializerBuilder WithTypeMapping() where TConcrete : TInterface { Type typeFromHandle = typeof(TInterface); Type typeFromHandle2 = typeof(TConcrete); if (!typeFromHandle.IsAssignableFrom(typeFromHandle2)) { throw new InvalidOperationException("The type '" + typeFromHandle2.Name + "' does not implement interface '" + typeFromHandle.Name + "'."); } if (!DictionaryExtensions.TryAdd(typeMappings, typeFromHandle, typeFromHandle2)) { typeMappings[typeFromHandle] = typeFromHandle2; } return this; } public DeserializerBuilder WithoutTagMapping(TagName tag) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (!tagMappings.Remove(tag)) { throw new KeyNotFoundException($"Tag '{tag}' is not registered"); } return this; } public DeserializerBuilder IgnoreUnmatchedProperties() { ignoreUnmatched = true; return this; } public DeserializerBuilder WithDuplicateKeyChecking() { duplicateKeyChecking = true; return this; } public IDeserializer Build() { if (FsharpHelper.Instance == null) { FsharpHelper.Instance = new DefaultFsharpHelper(); } return Deserializer.FromValueDeserializer(BuildValueDeserializer()); } public IValueDeserializer BuildValueDeserializer() { return new AliasValueDeserializer(new NodeValueDeserializer(nodeDeserializerFactories.BuildComponentList(), nodeTypeResolverFactories.BuildComponentList(), typeConverter, enumNamingConvention, BuildTypeInspector())); } } internal sealed class EmissionPhaseObjectGraphVisitorArgs { private readonly IEnumerable> preProcessingPhaseVisitors; public IObjectGraphVisitor InnerVisitor { get; private set; } public IEventEmitter EventEmitter { get; private set; } public ObjectSerializer NestedObjectSerializer { get; private set; } public IEnumerable TypeConverters { get; private set; } public EmissionPhaseObjectGraphVisitorArgs(IObjectGraphVisitor innerVisitor, IEventEmitter eventEmitter, IEnumerable> preProcessingPhaseVisitors, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) { InnerVisitor = innerVisitor ?? throw new ArgumentNullException("innerVisitor"); EventEmitter = eventEmitter ?? throw new ArgumentNullException("eventEmitter"); this.preProcessingPhaseVisitors = preProcessingPhaseVisitors ?? throw new ArgumentNullException("preProcessingPhaseVisitors"); TypeConverters = typeConverters ?? throw new ArgumentNullException("typeConverters"); NestedObjectSerializer = nestedObjectSerializer ?? throw new ArgumentNullException("nestedObjectSerializer"); } public T GetPreProcessingPhaseObjectGraphVisitor() where T : IObjectGraphVisitor { return preProcessingPhaseVisitors.OfType().Single(); } } internal abstract class EventInfo { public IObjectDescriptor Source { get; } protected EventInfo(IObjectDescriptor source) { Source = source ?? throw new ArgumentNullException("source"); } } internal class AliasEventInfo : EventInfo { public AnchorName Alias { get; } public bool NeedsExpansion { get; set; } public AliasEventInfo(IObjectDescriptor source, AnchorName alias) : base(source) { if (alias.IsEmpty) { throw new ArgumentNullException("alias"); } Alias = alias; } } internal class ObjectEventInfo : EventInfo { public AnchorName Anchor { get; set; } public TagName Tag { get; set; } protected ObjectEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class ScalarEventInfo : ObjectEventInfo { public string RenderedValue { get; set; } public ScalarStyle Style { get; set; } public bool IsPlainImplicit { get; set; } public bool IsQuotedImplicit { get; set; } public ScalarEventInfo(IObjectDescriptor source) : base(source) { Style = source.ScalarStyle; RenderedValue = string.Empty; } } internal sealed class MappingStartEventInfo : ObjectEventInfo { public bool IsImplicit { get; set; } public MappingStyle Style { get; set; } public MappingStartEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class MappingEndEventInfo : EventInfo { public MappingEndEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class SequenceStartEventInfo : ObjectEventInfo { public bool IsImplicit { get; set; } public SequenceStyle Style { get; set; } public SequenceStartEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class SequenceEndEventInfo : EventInfo { public SequenceEndEventInfo(IObjectDescriptor source) : base(source) { } } internal interface IAliasProvider { AnchorName GetAlias(object target); } internal interface IDeserializer { T Deserialize(string input); T Deserialize(TextReader input); T Deserialize(IParser parser); object? Deserialize(string input); object? Deserialize(TextReader input); object? Deserialize(IParser parser); object? Deserialize(string input, Type type); object? Deserialize(TextReader input, Type type); object? Deserialize(IParser parser, Type type); } internal interface IEventEmitter { void Emit(AliasEventInfo eventInfo, IEmitter emitter); void Emit(ScalarEventInfo eventInfo, IEmitter emitter); void Emit(MappingStartEventInfo eventInfo, IEmitter emitter); void Emit(MappingEndEventInfo eventInfo, IEmitter emitter); void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter); void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter); } internal interface INamingConvention { string Apply(string value); string Reverse(string value); } internal interface INodeDeserializer { bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer); } internal interface INodeTypeResolver { bool Resolve(NodeEvent? nodeEvent, ref Type currentType); } internal interface IObjectAccessor { void Set(string name, object target, object value); object? Read(string name, object target); } internal interface IObjectDescriptor { object? Value { get; } Type Type { get; } Type StaticType { get; } ScalarStyle ScalarStyle { get; } } internal static class ObjectDescriptorExtensions { public static object NonNullValue(this IObjectDescriptor objectDescriptor) { return objectDescriptor.Value ?? throw new InvalidOperationException("Attempted to use a IObjectDescriptor of type '" + objectDescriptor.Type.FullName + "' whose Value is null at a point whete it is invalid to do so. This may indicate a bug in YamlDotNet."); } } internal interface IObjectFactory { object Create(Type type); object? CreatePrimitive(Type type); bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments); Type GetValueType(Type type); void ExecuteOnDeserializing(object value); void ExecuteOnDeserialized(object value); void ExecuteOnSerializing(object value); void ExecuteOnSerialized(object value); } internal interface IObjectGraphTraversalStrategy { void Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context, ObjectSerializer serializer); } internal interface IObjectGraphVisitor { bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, TContext context, ObjectSerializer serializer); bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, TContext context, ObjectSerializer serializer); bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, TContext context, ObjectSerializer serializer); void VisitScalar(IObjectDescriptor scalar, TContext context, ObjectSerializer serializer); void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, TContext context, ObjectSerializer serializer); void VisitMappingEnd(IObjectDescriptor mapping, TContext context, ObjectSerializer serializer); void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, TContext context, ObjectSerializer serializer); void VisitSequenceEnd(IObjectDescriptor sequence, TContext context, ObjectSerializer serializer); } internal interface IPropertyDescriptor { string Name { get; } bool AllowNulls { get; } bool CanWrite { get; } Type Type { get; } Type? TypeOverride { get; set; } int Order { get; set; } ScalarStyle ScalarStyle { get; set; } bool Required { get; } Type? ConverterType { get; } T? GetCustomAttribute() where T : Attribute; IObjectDescriptor Read(object target); void Write(object target, object? value); } internal interface IRegistrationLocationSelectionSyntax { void InsteadOf() where TRegistrationType : TBaseRegistrationType; void Before() where TRegistrationType : TBaseRegistrationType; void After() where TRegistrationType : TBaseRegistrationType; void OnTop(); void OnBottom(); } internal interface ITrackingRegistrationLocationSelectionSyntax { void InsteadOf() where TRegistrationType : TBaseRegistrationType; } internal interface ISerializer { string Serialize(object? graph); string Serialize(object? graph, Type type); void Serialize(TextWriter writer, object? graph); void Serialize(TextWriter writer, object? graph, Type type); void Serialize(IEmitter emitter, object? graph); void Serialize(IEmitter emitter, object? graph, Type type); } internal interface ITypeInspector { IEnumerable GetProperties(Type type, object? container); IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched, bool caseInsensitivePropertyMatching); string GetEnumName(Type enumType, string name); string GetEnumValue(object enumValue); } internal interface ITypeResolver { Type Resolve(Type staticType, object? actualValue); } internal interface IValueDeserializer { object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer); } internal interface IValuePromise { event Action ValueAvailable; } internal interface IValueSerializer { void SerializeValue(IEmitter emitter, object? value, Type? type); } internal interface IYamlConvertible { void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer); void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer); } internal delegate object? ObjectDeserializer(Type type); internal delegate void ObjectSerializer(object? value, Type? type = null); [Obsolete("Please use IYamlConvertible instead")] internal interface IYamlSerializable { void ReadYaml(IParser parser); void WriteYaml(IEmitter emitter); } internal interface IYamlTypeConverter { bool Accepts(Type type); object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer); void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer); } internal sealed class LazyComponentRegistrationList : IEnumerable>, IEnumerable { public sealed class LazyComponentRegistration { public readonly Type ComponentType; public readonly Func Factory; public LazyComponentRegistration(Type componentType, Func factory) { ComponentType = componentType; Factory = factory; } } public sealed class TrackingLazyComponentRegistration { public readonly Type ComponentType; public readonly Func Factory; public TrackingLazyComponentRegistration(Type componentType, Func factory) { ComponentType = componentType; Factory = factory; } } private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax { private readonly LazyComponentRegistrationList registrations; private readonly LazyComponentRegistration newRegistration; public RegistrationLocationSelector(LazyComponentRegistrationList registrations, LazyComponentRegistration newRegistration) { this.registrations = registrations; this.newRegistration = newRegistration; } void IRegistrationLocationSelectionSyntax.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } int index = registrations.EnsureRegistrationExists(); registrations.entries[index] = newRegistration; } void IRegistrationLocationSelectionSyntax.After() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); int num = registrations.EnsureRegistrationExists(); registrations.entries.Insert(num + 1, newRegistration); } void IRegistrationLocationSelectionSyntax.Before() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); int index = registrations.EnsureRegistrationExists(); registrations.entries.Insert(index, newRegistration); } void IRegistrationLocationSelectionSyntax.OnBottom() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Add(newRegistration); } void IRegistrationLocationSelectionSyntax.OnTop() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Insert(0, newRegistration); } } private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax { private readonly LazyComponentRegistrationList registrations; private readonly TrackingLazyComponentRegistration newRegistration; public TrackingRegistrationLocationSelector(LazyComponentRegistrationList registrations, TrackingLazyComponentRegistration newRegistration) { this.registrations = registrations; this.newRegistration = newRegistration; } void ITrackingRegistrationLocationSelectionSyntax.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } int index = registrations.EnsureRegistrationExists(); Func innerComponentFactory = registrations.entries[index].Factory; registrations.entries[index] = new LazyComponentRegistration(newRegistration.ComponentType, (TArgument arg) => newRegistration.Factory(innerComponentFactory(arg), arg)); } } private readonly List entries = new List(); public int Count => entries.Count; public IEnumerable> InReverseOrder { get { int i = entries.Count - 1; while (i >= 0) { yield return entries[i].Factory; int num = i - 1; i = num; } } } public LazyComponentRegistrationList Clone() { LazyComponentRegistrationList lazyComponentRegistrationList = new LazyComponentRegistrationList(); foreach (LazyComponentRegistration entry in entries) { lazyComponentRegistrationList.entries.Add(entry); } return lazyComponentRegistrationList; } public void Clear() { entries.Clear(); } public void Add(Type componentType, Func factory) { entries.Add(new LazyComponentRegistration(componentType, factory)); } public void Remove(Type componentType) { for (int i = 0; i < entries.Count; i++) { if (entries[i].ComponentType == componentType) { entries.RemoveAt(i); return; } } throw new KeyNotFoundException("A component registration of type '" + componentType.FullName + "' was not found."); } public IRegistrationLocationSelectionSyntax CreateRegistrationLocationSelector(Type componentType, Func factory) { return new RegistrationLocationSelector(this, new LazyComponentRegistration(componentType, factory)); } public ITrackingRegistrationLocationSelectionSyntax CreateTrackingRegistrationLocationSelector(Type componentType, Func factory) { return new TrackingRegistrationLocationSelector(this, new TrackingLazyComponentRegistration(componentType, factory)); } public IEnumerator> GetEnumerator() { return entries.Select((LazyComponentRegistration e) => e.Factory).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private int IndexOfRegistration(Type registrationType) { for (int i = 0; i < entries.Count; i++) { if (registrationType == entries[i].ComponentType) { return i; } } return -1; } private void EnsureNoDuplicateRegistrationType(Type componentType) { if (IndexOfRegistration(componentType) != -1) { throw new InvalidOperationException("A component of type '" + componentType.FullName + "' has already been registered."); } } private int EnsureRegistrationExists() { int num = IndexOfRegistration(typeof(TRegistrationType)); if (num == -1) { throw new InvalidOperationException("A component of type '" + typeof(TRegistrationType).FullName + "' has not been registered."); } return num; } } internal static class LazyComponentRegistrationListExtensions { public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent) { return registrations.InReverseOrder.Aggregate(innerComponent, (TComponent inner, Func factory) => factory(inner)); } public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent, Func argumentBuilder) { return registrations.InReverseOrder.Aggregate(innerComponent, (TComponent inner, Func factory) => factory(argumentBuilder(inner))); } public static List BuildComponentList(this LazyComponentRegistrationList registrations) { return registrations.Select((Func factory) => factory(default(Nothing))).ToList(); } public static List BuildComponentList(this LazyComponentRegistrationList registrations, TArgument argument) { return registrations.Select((Func factory) => factory(argument)).ToList(); } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct Nothing { } internal sealed class ObjectDescriptor : IObjectDescriptor { public object? Value { get; private set; } public Type Type { get; private set; } public Type StaticType { get; private set; } public ScalarStyle ScalarStyle { get; private set; } public ObjectDescriptor(object? value, Type type, Type staticType) : this(value, type, staticType, ScalarStyle.Any) { } public ObjectDescriptor(object? value, Type type, Type staticType, ScalarStyle scalarStyle) { Value = value; Type = type ?? throw new ArgumentNullException("type"); StaticType = staticType ?? throw new ArgumentNullException("staticType"); ScalarStyle = scalarStyle; } } internal delegate IObjectGraphTraversalStrategy ObjectGraphTraversalStrategyFactory(ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion); internal sealed class PropertyDescriptor : IPropertyDescriptor { private readonly IPropertyDescriptor baseDescriptor; public bool AllowNulls => baseDescriptor.AllowNulls; public string Name { get; set; } public bool Required => baseDescriptor.Required; public Type Type => baseDescriptor.Type; public Type? TypeOverride { get { return baseDescriptor.TypeOverride; } set { baseDescriptor.TypeOverride = value; } } public Type? ConverterType => baseDescriptor.ConverterType; public int Order { get; set; } public ScalarStyle ScalarStyle { get { return baseDescriptor.ScalarStyle; } set { baseDescriptor.ScalarStyle = value; } } public bool CanWrite => baseDescriptor.CanWrite; public PropertyDescriptor(IPropertyDescriptor baseDescriptor) { this.baseDescriptor = baseDescriptor; Name = baseDescriptor.Name; } public void Write(object target, object? value) { baseDescriptor.Write(target, value); } public T? GetCustomAttribute() where T : Attribute { return baseDescriptor.GetCustomAttribute(); } public IObjectDescriptor Read(object target) { return baseDescriptor.Read(target); } } internal sealed class Serializer : ISerializer { private readonly IValueSerializer valueSerializer; private readonly EmitterSettings emitterSettings; public Serializer() : this(new SerializerBuilder().BuildValueSerializer(), EmitterSettings.Default) { } private Serializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) { this.valueSerializer = valueSerializer ?? throw new ArgumentNullException("valueSerializer"); this.emitterSettings = emitterSettings ?? throw new ArgumentNullException("emitterSettings"); } public static Serializer FromValueSerializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) { return new Serializer(valueSerializer, emitterSettings); } public string Serialize(object? graph) { using StringWriter stringWriter = new StringWriter(); Serialize(stringWriter, graph); return stringWriter.ToString(); } public string Serialize(object? graph, Type type) { using StringWriter stringWriter = new StringWriter(); Serialize(stringWriter, graph, type); return stringWriter.ToString(); } public void Serialize(TextWriter writer, object? graph) { Serialize(new Emitter(writer, emitterSettings), graph); } public void Serialize(TextWriter writer, object? graph, Type type) { Serialize(new Emitter(writer, emitterSettings), graph, type); } public void Serialize(IEmitter emitter, object? graph) { if (emitter == null) { throw new ArgumentNullException("emitter"); } EmitDocument(emitter, graph, null); } public void Serialize(IEmitter emitter, object? graph, Type type) { if (emitter == null) { throw new ArgumentNullException("emitter"); } if (type == null) { throw new ArgumentNullException("type"); } EmitDocument(emitter, graph, type); } private void EmitDocument(IEmitter emitter, object? graph, Type? type) { emitter.Emit(new YamlDotNet.Core.Events.StreamStart()); emitter.Emit(new YamlDotNet.Core.Events.DocumentStart()); valueSerializer.SerializeValue(emitter, graph, type); emitter.Emit(new YamlDotNet.Core.Events.DocumentEnd(isImplicit: true)); emitter.Emit(new YamlDotNet.Core.Events.StreamEnd()); } } internal sealed class SerializerBuilder : BuilderSkeleton { private class ValueSerializer : IValueSerializer { private readonly IObjectGraphTraversalStrategy traversalStrategy; private readonly IEventEmitter eventEmitter; private readonly IEnumerable typeConverters; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; public ValueSerializer(IObjectGraphTraversalStrategy traversalStrategy, IEventEmitter eventEmitter, IEnumerable typeConverters, LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories) { this.traversalStrategy = traversalStrategy; this.eventEmitter = eventEmitter; this.typeConverters = typeConverters; this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; } public void SerializeValue(IEmitter emitter, object? value, Type? type) { Type type2 = type ?? ((value != null) ? value.GetType() : typeof(object)); Type staticType = type ?? typeof(object); ObjectDescriptor graph = new ObjectDescriptor(value, type2, staticType); List> preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); IObjectGraphVisitor visitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain>(new EmittingObjectGraphVisitor(eventEmitter), (IObjectGraphVisitor inner) => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer)); foreach (IObjectGraphVisitor item in preProcessingPhaseObjectGraphVisitors) { traversalStrategy.Traverse(graph, item, default(Nothing), NestedObjectSerializer); } traversalStrategy.Traverse(graph, visitor, emitter, NestedObjectSerializer); void NestedObjectSerializer(object? v, Type? t) { SerializeValue(emitter, v, t); } } } private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList eventEmitterFactories; private readonly Dictionary tagMappings = new Dictionary(); private readonly IObjectFactory objectFactory; private int maximumRecursion = 50; private EmitterSettings emitterSettings = EmitterSettings.Default; private DefaultValuesHandling defaultValuesHandlingConfiguration; private ScalarStyle defaultScalarStyle; private bool quoteNecessaryStrings; private bool quoteYaml1_1Strings; protected override SerializerBuilder Self => this; public SerializerBuilder() : base((ITypeResolver)new DynamicTypeResolver()) { typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), (ITypeInspector inner) => (overrides == null) ? inner : new YamlAttributeOverridesInspector(inner, overrides.Clone())); preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> { { typeof(AnchorAssigner), (IEnumerable typeConverters) => new AnchorAssigner(typeConverters) } }; emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> { { typeof(CustomSerializationObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) }, { typeof(AnchorAssigningObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) }, { typeof(DefaultValuesObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor, new DefaultObjectFactory()) }, { typeof(CommentsObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CommentsObjectGraphVisitor(args.InnerVisitor) } }; eventEmitterFactories = new LazyComponentRegistrationList { { typeof(TypeAssigningEventEmitter), (IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()) } }; objectFactory = new DefaultObjectFactory(); objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention, objectFactory); } public SerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false) { quoteNecessaryStrings = true; this.quoteYaml1_1Strings = quoteYaml1_1Strings; return this; } public SerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { defaultScalarStyle = style; return this; } public SerializerBuilder WithMaximumRecursion(int maximumRecursion) { if (maximumRecursion <= 0) { throw new ArgumentOutOfRangeException("maximumRecursion", $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); } this.maximumRecursion = maximumRecursion; return this; } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { return WithEventEmitter((IEventEmitter e, ITypeInspector _) => eventEmitterFactory(e), where); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter inner) => eventEmitterFactory(inner, BuildTypeInspector()))); return Self; } public SerializerBuilder WithEventEmitter(WrapperFactory eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter wrapped, IEventEmitter inner) => eventEmitterFactory(wrapped, inner))); return Self; } public SerializerBuilder WithoutEventEmitter() where TEventEmitter : IEventEmitter { return WithoutEventEmitter(typeof(TEventEmitter)); } public SerializerBuilder WithoutEventEmitter(Type eventEmitterType) { if (eventEmitterType == null) { throw new ArgumentNullException("eventEmitterType"); } eventEmitterFactories.Remove(eventEmitterType); return this; } public override SerializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(type, out var value)) { throw new ArgumentException($"Type already has a registered tag '{value}' for type '{type.FullName}'", "type"); } tagMappings.Add(type, tag); return this; } public SerializerBuilder WithoutTagMapping(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (!tagMappings.Remove(type)) { throw new KeyNotFoundException("Tag for type '" + type.FullName + "' is not registered"); } return this; } public SerializerBuilder EnsureRoundtrip() { objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new RoundtripObjectGraphTraversalStrategy(typeConverters, typeInspector, typeResolver, maximumRecursion, namingConvention, settings, objectFactory); WithEventEmitter((IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); return WithTypeInspector((ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner), delegate(IRegistrationLocationSelectionSyntax loc) { loc.OnBottom(); }); } public SerializerBuilder DisableAliases() { preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); return this; } [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", true)] public SerializerBuilder EmitDefaults() { return ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); } public SerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) { defaultValuesHandlingConfiguration = configuration; return this; } public SerializerBuilder JsonCompatible() { emitterSettings = emitterSettings.WithMaxSimpleKeyLength(int.MaxValue).WithoutAnchorName().WithUtf16SurrogatePairs(); return WithTypeConverter(new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: true), delegate(IRegistrationLocationSelectionSyntax w) { w.InsteadOf(); }).WithTypeConverter(new DateTime8601Converter(ScalarStyle.DoubleQuoted)).WithEventEmitter((IEventEmitter inner) => new JsonEventEmitter(inner, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); } public SerializerBuilder WithNewLine(string newLine) { emitterSettings = emitterSettings.WithNewLine(newLine); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitor == null) { throw new ArgumentNullException("objectGraphVisitor"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable _) => objectGraphVisitor)); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable typeConverters) => objectGraphVisitorFactory(typeConverters))); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable _) => objectGraphVisitorFactory(wrapped))); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, IObjectGraphVisitor, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable typeConverters) => objectGraphVisitorFactory(wrapped, typeConverters))); return this; } public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public SerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) { this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; return this; } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(args))); return this; } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(wrapped, args))); return this; } public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public SerializerBuilder WithIndentedSequences() { emitterSettings = emitterSettings.WithIndentedSequences(); return this; } public ISerializer Build() { if (FsharpHelper.Instance == null) { FsharpHelper.Instance = new DefaultFsharpHelper(); } return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); } public IValueSerializer BuildValueSerializer() { IEnumerable typeConverters = BuildTypeConverters(); ITypeInspector typeInspector = BuildTypeInspector(); IObjectGraphTraversalStrategy traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); IEventEmitter eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); return new ValueSerializer(traversalStrategy, eventEmitter, typeConverters, preProcessingPhaseObjectGraphVisitorFactories.Clone(), emissionPhaseObjectGraphVisitorFactories.Clone()); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = new ReadablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); if (!ignoreFields) { typeInspector = new CompositeTypeInspector(new ReadableFieldsTypeInspector(typeResolver), typeInspector); } return typeInspectorFactories.BuildComponentChain(typeInspector); } } internal class Settings { public bool AllowPrivateConstructors { get; set; } } internal abstract class StaticBuilderSkeleton where TBuilder : StaticBuilderSkeleton { internal INamingConvention namingConvention = NullNamingConvention.Instance; internal INamingConvention enumNamingConvention = NullNamingConvention.Instance; internal ITypeResolver typeResolver; internal readonly LazyComponentRegistrationList typeConverterFactories; internal readonly LazyComponentRegistrationList typeInspectorFactories; internal bool includeNonPublicProperties; internal Settings settings; internal YamlFormatter yamlFormatter = YamlFormatter.Default; protected abstract TBuilder Self { get; } internal StaticBuilderSkeleton(ITypeResolver typeResolver) { typeConverterFactories = new LazyComponentRegistrationList { { typeof(YamlDotNet.Serialization.Converters.GuidConverter), (Nothing _) => new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: false) } }; typeInspectorFactories = new LazyComponentRegistrationList(); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); settings = new Settings(); } public TBuilder WithNamingConvention(INamingConvention namingConvention) { this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); return Self; } public TBuilder WithEnumNamingConvention(INamingConvention enumNamingConvention) { this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); return Self; } public TBuilder WithTypeResolver(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); return Self; } public abstract TBuilder WithTagMapping(TagName tag, Type type); public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) { return WithTypeConverter(typeConverter, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter, Action> where) { if (typeConverter == null) { throw new ArgumentNullException("typeConverter"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), (Nothing _) => typeConverter)); return Self; } public TBuilder WithTypeConverter(WrapperFactory typeConverterFactory, Action> where) where TYamlTypeConverter : IYamlTypeConverter { if (typeConverterFactory == null) { throw new ArgumentNullException("typeConverterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (IYamlTypeConverter wrapped, Nothing _) => typeConverterFactory(wrapped))); return Self; } public TBuilder WithoutTypeConverter() where TYamlTypeConverter : IYamlTypeConverter { return WithoutTypeConverter(typeof(TYamlTypeConverter)); } public TBuilder WithoutTypeConverter(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } typeConverterFactories.Remove(converterType); return Self; } public TBuilder WithTypeInspector(Func typeInspectorFactory) where TTypeInspector : ITypeInspector { return WithTypeInspector(typeInspectorFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeInspector(Func typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector inner) => typeInspectorFactory(inner))); return Self; } public TBuilder WithTypeInspector(WrapperFactory typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector wrapped, ITypeInspector inner) => typeInspectorFactory(wrapped, inner))); return Self; } public TBuilder WithoutTypeInspector() where TTypeInspector : ITypeInspector { return WithoutTypeInspector(typeof(TTypeInspector)); } public TBuilder WithoutTypeInspector(Type inspectorType) { if (inspectorType == null) { throw new ArgumentNullException("inspectorType"); } typeInspectorFactories.Remove(inspectorType); return Self; } public TBuilder WithYamlFormatter(YamlFormatter formatter) { yamlFormatter = formatter ?? throw new ArgumentNullException("formatter"); return Self; } protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); } } internal abstract class StaticContext { public virtual bool IsKnownType(Type type) { throw new NotImplementedException(); } public virtual ITypeResolver GetTypeResolver() { throw new NotImplementedException(); } public virtual StaticObjectFactory GetFactory() { throw new NotImplementedException(); } public virtual ITypeInspector GetTypeInspector() { throw new NotImplementedException(); } } internal sealed class StaticDeserializerBuilder : StaticBuilderSkeleton { private readonly StaticContext context; private readonly StaticObjectFactory factory; private readonly LazyComponentRegistrationList nodeDeserializerFactories; private readonly LazyComponentRegistrationList nodeTypeResolverFactories; private readonly Dictionary tagMappings; private readonly ITypeConverter typeConverter; private readonly Dictionary typeMappings; private bool ignoreUnmatched; private bool duplicateKeyChecking; private bool attemptUnknownTypeDeserialization; private bool enforceNullability; private bool caseInsensitivePropertyMatching; protected override StaticDeserializerBuilder Self => this; public StaticDeserializerBuilder(StaticContext context) : base(context.GetTypeResolver()) { this.context = context; factory = context.GetFactory(); typeMappings = new Dictionary(); tagMappings = new Dictionary { { FailsafeSchema.Tags.Map, typeof(Dictionary) }, { FailsafeSchema.Tags.Str, typeof(string) }, { JsonSchema.Tags.Bool, typeof(bool) }, { JsonSchema.Tags.Float, typeof(double) }, { JsonSchema.Tags.Int, typeof(int) }, { DefaultSchema.Tags.Timestamp, typeof(DateTime) } }; typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); nodeDeserializerFactories = new LazyComponentRegistrationList { { typeof(YamlConvertibleNodeDeserializer), (Nothing _) => new YamlConvertibleNodeDeserializer(factory) }, { typeof(YamlSerializableNodeDeserializer), (Nothing _) => new YamlSerializableNodeDeserializer(factory) }, { typeof(TypeConverterNodeDeserializer), (Nothing _) => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), (Nothing _) => new NullNodeDeserializer() }, { typeof(ScalarNodeDeserializer), (Nothing _) => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, BuildTypeInspector(), yamlFormatter, enumNamingConvention) }, { typeof(StaticArrayNodeDeserializer), (Nothing _) => new StaticArrayNodeDeserializer(factory) }, { typeof(StaticDictionaryNodeDeserializer), (Nothing _) => new StaticDictionaryNodeDeserializer(factory, duplicateKeyChecking) }, { typeof(StaticCollectionNodeDeserializer), (Nothing _) => new StaticCollectionNodeDeserializer(factory) }, { typeof(ObjectNodeDeserializer), (Nothing _) => new ObjectNodeDeserializer(factory, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking, typeConverter, enumNamingConvention, enforceNullability, caseInsensitivePropertyMatching, enforceRequiredProperties: false, BuildTypeConverters()) } }; nodeTypeResolverFactories = new LazyComponentRegistrationList { { typeof(MappingNodeTypeResolver), (Nothing _) => new MappingNodeTypeResolver(typeMappings) }, { typeof(YamlConvertibleTypeResolver), (Nothing _) => new YamlConvertibleTypeResolver() }, { typeof(YamlSerializableTypeResolver), (Nothing _) => new YamlSerializableTypeResolver() }, { typeof(TagNodeTypeResolver), (Nothing _) => new TagNodeTypeResolver(tagMappings) }, { typeof(PreventUnknownTagsNodeTypeResolver), (Nothing _) => new PreventUnknownTagsNodeTypeResolver() }, { typeof(DefaultContainersNodeTypeResolver), (Nothing _) => new DefaultContainersNodeTypeResolver() } }; typeConverter = new NullTypeConverter(); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = context.GetTypeInspector(); return typeInspectorFactories.BuildComponentChain(typeInspector); } public StaticDeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() { attemptUnknownTypeDeserialization = true; return this; } public StaticDeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) { return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticDeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer, Action> where) { if (nodeDeserializer == null) { throw new ArgumentNullException("nodeDeserializer"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), (Nothing _) => nodeDeserializer)); return this; } public StaticDeserializerBuilder WithNodeDeserializer(WrapperFactory nodeDeserializerFactory, Action> where) where TNodeDeserializer : INodeDeserializer { if (nodeDeserializerFactory == null) { throw new ArgumentNullException("nodeDeserializerFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (INodeDeserializer wrapped, Nothing _) => nodeDeserializerFactory(wrapped))); return this; } public StaticDeserializerBuilder WithCaseInsensitivePropertyMatching() { caseInsensitivePropertyMatching = true; return this; } public StaticDeserializerBuilder WithEnforceNullability() { enforceNullability = true; return this; } public StaticDeserializerBuilder WithoutNodeDeserializer() where TNodeDeserializer : INodeDeserializer { return WithoutNodeDeserializer(typeof(TNodeDeserializer)); } public StaticDeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) { if (nodeDeserializerType == null) { throw new ArgumentNullException("nodeDeserializerType"); } nodeDeserializerFactories.Remove(nodeDeserializerType); return this; } public StaticDeserializerBuilder WithTypeDiscriminatingNodeDeserializer(Action configureTypeDiscriminatingNodeDeserializerOptions, int maxDepth = -1, int maxLength = -1) { TypeDiscriminatingNodeDeserializerOptions typeDiscriminatingNodeDeserializerOptions = new TypeDiscriminatingNodeDeserializerOptions(); configureTypeDiscriminatingNodeDeserializerOptions(typeDiscriminatingNodeDeserializerOptions); TypeDiscriminatingNodeDeserializer nodeDeserializer = new TypeDiscriminatingNodeDeserializer(nodeDeserializerFactories.BuildComponentList(), typeDiscriminatingNodeDeserializerOptions.discriminators, maxDepth, maxLength); return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax s) { s.Before(); }); } public StaticDeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) { return WithNodeTypeResolver(nodeTypeResolver, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticDeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver, Action> where) { if (nodeTypeResolver == null) { throw new ArgumentNullException("nodeTypeResolver"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), (Nothing _) => nodeTypeResolver)); return this; } public StaticDeserializerBuilder WithNodeTypeResolver(WrapperFactory nodeTypeResolverFactory, Action> where) where TNodeTypeResolver : INodeTypeResolver { if (nodeTypeResolverFactory == null) { throw new ArgumentNullException("nodeTypeResolverFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (INodeTypeResolver wrapped, Nothing _) => nodeTypeResolverFactory(wrapped))); return this; } public StaticDeserializerBuilder WithoutNodeTypeResolver() where TNodeTypeResolver : INodeTypeResolver { return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); } public StaticDeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) { if (nodeTypeResolverType == null) { throw new ArgumentNullException("nodeTypeResolverType"); } nodeTypeResolverFactories.Remove(nodeTypeResolverType); return this; } public override StaticDeserializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(tag, out Type value)) { throw new ArgumentException($"Type already has a registered type '{value.FullName}' for tag '{tag}'", "tag"); } tagMappings.Add(tag, type); return this; } public StaticDeserializerBuilder WithTypeMapping() where TConcrete : TInterface { Type typeFromHandle = typeof(TInterface); Type typeFromHandle2 = typeof(TConcrete); if (!typeFromHandle.IsAssignableFrom(typeFromHandle2)) { throw new InvalidOperationException("The type '" + typeFromHandle2.Name + "' does not implement interface '" + typeFromHandle.Name + "'."); } typeMappings[typeFromHandle] = typeFromHandle2; return this; } public StaticDeserializerBuilder WithoutTagMapping(TagName tag) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (!tagMappings.Remove(tag)) { throw new KeyNotFoundException($"Tag '{tag}' is not registered"); } return this; } public StaticDeserializerBuilder IgnoreUnmatchedProperties() { ignoreUnmatched = true; return this; } public StaticDeserializerBuilder WithDuplicateKeyChecking() { duplicateKeyChecking = true; return this; } public IDeserializer Build() { return Deserializer.FromValueDeserializer(BuildValueDeserializer()); } public IValueDeserializer BuildValueDeserializer() { return new AliasValueDeserializer(new NodeValueDeserializer(nodeDeserializerFactories.BuildComponentList(), nodeTypeResolverFactories.BuildComponentList(), typeConverter, enumNamingConvention, BuildTypeInspector())); } } internal sealed class StaticSerializerBuilder : StaticBuilderSkeleton { private class ValueSerializer : IValueSerializer { private readonly IObjectGraphTraversalStrategy traversalStrategy; private readonly IEventEmitter eventEmitter; private readonly IEnumerable typeConverters; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; public ValueSerializer(IObjectGraphTraversalStrategy traversalStrategy, IEventEmitter eventEmitter, IEnumerable typeConverters, LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories) { this.traversalStrategy = traversalStrategy; this.eventEmitter = eventEmitter; this.typeConverters = typeConverters; this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; } public void SerializeValue(IEmitter emitter, object? value, Type? type) { Type type2 = type ?? ((value != null) ? value.GetType() : typeof(object)); Type staticType = type ?? typeof(object); ObjectDescriptor graph = new ObjectDescriptor(value, type2, staticType); List> preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); foreach (IObjectGraphVisitor item in preProcessingPhaseObjectGraphVisitors) { traversalStrategy.Traverse(graph, item, default(Nothing), NestedObjectSerializer); } IObjectGraphVisitor visitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain>(new EmittingObjectGraphVisitor(eventEmitter), (IObjectGraphVisitor inner) => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer)); traversalStrategy.Traverse(graph, visitor, emitter, NestedObjectSerializer); void NestedObjectSerializer(object? v, Type? t) { SerializeValue(emitter, v, t); } } } private readonly StaticContext context; private readonly StaticObjectFactory factory; private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList eventEmitterFactories; private readonly Dictionary tagMappings = new Dictionary(); private int maximumRecursion = 50; private EmitterSettings emitterSettings = EmitterSettings.Default; private DefaultValuesHandling defaultValuesHandlingConfiguration; private bool quoteNecessaryStrings; private bool quoteYaml1_1Strings; private ScalarStyle defaultScalarStyle; protected override StaticSerializerBuilder Self => this; public StaticSerializerBuilder(StaticContext context) : base((ITypeResolver)new DynamicTypeResolver()) { this.context = context; factory = context.GetFactory(); typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> { { typeof(AnchorAssigner), (IEnumerable typeConverters) => new AnchorAssigner(typeConverters) } }; emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> { { typeof(CustomSerializationObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) }, { typeof(AnchorAssigningObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) }, { typeof(DefaultValuesObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor, factory) }, { typeof(CommentsObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CommentsObjectGraphVisitor(args.InnerVisitor) } }; eventEmitterFactories = new LazyComponentRegistrationList { { typeof(TypeAssigningEventEmitter), (IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()) } }; objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention, factory); } public StaticSerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false) { quoteNecessaryStrings = true; this.quoteYaml1_1Strings = quoteYaml1_1Strings; return this; } public StaticSerializerBuilder WithQuotingNecessaryStrings() { quoteNecessaryStrings = true; return this; } public StaticSerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { defaultScalarStyle = style; return this; } public StaticSerializerBuilder WithMaximumRecursion(int maximumRecursion) { if (maximumRecursion <= 0) { throw new ArgumentOutOfRangeException("maximumRecursion", $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); } this.maximumRecursion = maximumRecursion; return this; } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { return WithEventEmitter((IEventEmitter e, ITypeInspector _) => eventEmitterFactory(e), where); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter inner) => eventEmitterFactory(inner, BuildTypeInspector()))); return Self; } public StaticSerializerBuilder WithEventEmitter(WrapperFactory eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter wrapped, IEventEmitter inner) => eventEmitterFactory(wrapped, inner))); return Self; } public StaticSerializerBuilder WithoutEventEmitter() where TEventEmitter : IEventEmitter { return WithoutEventEmitter(typeof(TEventEmitter)); } public StaticSerializerBuilder WithoutEventEmitter(Type eventEmitterType) { if (eventEmitterType == null) { throw new ArgumentNullException("eventEmitterType"); } eventEmitterFactories.Remove(eventEmitterType); return this; } public override StaticSerializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(type, out var value)) { throw new ArgumentException($"Type already has a registered tag '{value}' for type '{type.FullName}'", "type"); } tagMappings.Add(type, tag); return this; } public StaticSerializerBuilder WithoutTagMapping(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (!tagMappings.Remove(type)) { throw new KeyNotFoundException("Tag for type '" + type.FullName + "' is not registered"); } return this; } public StaticSerializerBuilder EnsureRoundtrip() { objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new RoundtripObjectGraphTraversalStrategy(typeConverters, typeInspector, typeResolver, maximumRecursion, namingConvention, settings, factory); WithEventEmitter((IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings: false, ScalarStyle.Plain, YamlFormatter.Default, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); return WithTypeInspector((ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner), delegate(IRegistrationLocationSelectionSyntax loc) { loc.OnBottom(); }); } public StaticSerializerBuilder DisableAliases() { preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); return this; } [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", true)] public StaticSerializerBuilder EmitDefaults() { return ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); } public StaticSerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) { defaultValuesHandlingConfiguration = configuration; return this; } public StaticSerializerBuilder JsonCompatible() { emitterSettings = emitterSettings.WithMaxSimpleKeyLength(int.MaxValue).WithoutAnchorName().WithUtf16SurrogatePairs(); return WithTypeConverter(new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: true), delegate(IRegistrationLocationSelectionSyntax w) { w.InsteadOf(); }).WithTypeConverter(new DateTime8601Converter(ScalarStyle.DoubleQuoted)).WithEventEmitter((IEventEmitter inner) => new JsonEventEmitter(inner, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); } public StaticSerializerBuilder WithNewLine(string newLine) { emitterSettings = emitterSettings.WithNewLine(newLine); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitor == null) { throw new ArgumentNullException("objectGraphVisitor"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable _) => objectGraphVisitor)); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable typeConverters) => objectGraphVisitorFactory(typeConverters))); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable _) => objectGraphVisitorFactory(wrapped))); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, IObjectGraphVisitor, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable typeConverters) => objectGraphVisitorFactory(wrapped, typeConverters))); return this; } public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public StaticSerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) { this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; return this; } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(args))); return this; } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(wrapped, args))); return this; } public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public StaticSerializerBuilder WithIndentedSequences() { emitterSettings = emitterSettings.WithIndentedSequences(); return this; } public ISerializer Build() { return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); } public IValueSerializer BuildValueSerializer() { IEnumerable typeConverters = BuildTypeConverters(); ITypeInspector typeInspector = BuildTypeInspector(); IObjectGraphTraversalStrategy traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); IEventEmitter eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); return new ValueSerializer(traversalStrategy, eventEmitter, typeConverters, preProcessingPhaseObjectGraphVisitorFactories.Clone(), emissionPhaseObjectGraphVisitorFactories.Clone()); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = context.GetTypeInspector(); return typeInspectorFactories.BuildComponentChain(typeInspector); } } internal sealed class StreamFragment : IYamlConvertible { private readonly List events = new List(); public IList Events => events; void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { events.Clear(); int num = 0; do { if (!parser.MoveNext()) { throw new InvalidOperationException("The parser has reached the end before deserialization completed."); } ParsingEvent current = parser.Current; events.Add(current); num += current.NestingIncrease; } while (num > 0); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { foreach (ParsingEvent @event in events) { emitter.Emit(@event); } } } internal sealed class TagMappings { private readonly Dictionary mappings; public TagMappings() { mappings = new Dictionary(); } public TagMappings(IDictionary mappings) { this.mappings = new Dictionary(mappings); } public void Add(string tag, Type mapping) { mappings.Add(tag, mapping); } internal Type? GetMapping(string tag) { if (!mappings.TryGetValue(tag, out Type value)) { return null; } return value; } } internal sealed class YamlAttributeOverrides { private readonly struct AttributeKey { public readonly Type AttributeType; public readonly string PropertyName; public AttributeKey(Type attributeType, string propertyName) { AttributeType = attributeType; PropertyName = propertyName; } public override bool Equals(object? obj) { if (obj is AttributeKey attributeKey && AttributeType.Equals(attributeKey.AttributeType)) { return PropertyName.Equals(attributeKey.PropertyName); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(AttributeType.GetHashCode(), PropertyName.GetHashCode()); } } private sealed class AttributeMapping { public readonly Type RegisteredType; public readonly Attribute Attribute; public AttributeMapping(Type registeredType, Attribute attribute) { RegisteredType = registeredType; Attribute = attribute; } public override bool Equals(object? obj) { if (obj is AttributeMapping attributeMapping && RegisteredType.Equals(attributeMapping.RegisteredType)) { return Attribute.Equals(attributeMapping.Attribute); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(RegisteredType.GetHashCode(), Attribute.GetHashCode()); } public int Matches(Type matchType) { int num = 0; Type type = matchType; while (type != null) { num++; if (type == RegisteredType) { return num; } type = type.BaseType(); } if (matchType.GetInterfaces().Contains(RegisteredType)) { return num; } return 0; } } private readonly Dictionary> overrides = new Dictionary>(); [return: MaybeNull] public T GetAttribute(Type type, string member) where T : Attribute { if (overrides.TryGetValue(new AttributeKey(typeof(T), member), out List value)) { int num = 0; AttributeMapping attributeMapping = null; foreach (AttributeMapping item in value) { int num2 = item.Matches(type); if (num2 > num) { num = num2; attributeMapping = item; } } if (num > 0) { return (T)attributeMapping.Attribute; } } return null; } public void Add(Type type, string member, Attribute attribute) { AttributeMapping item = new AttributeMapping(type, attribute); AttributeKey key = new AttributeKey(attribute.GetType(), member); if (!overrides.TryGetValue(key, out List value)) { value = new List(); overrides.Add(key, value); } else if (value.Contains(item)) { throw new InvalidOperationException($"Attribute ({attribute}) already set for Type {type.FullName}, Member {member}"); } value.Add(item); } public YamlAttributeOverrides Clone() { YamlAttributeOverrides yamlAttributeOverrides = new YamlAttributeOverrides(); foreach (KeyValuePair> @override in overrides) { foreach (AttributeMapping item in @override.Value) { yamlAttributeOverrides.Add(item.RegisteredType, @override.Key.PropertyName, item.Attribute); } } return yamlAttributeOverrides; } public void Add(Expression> propertyAccessor, Attribute attribute) { PropertyInfo propertyInfo = propertyAccessor.AsProperty(); Add(typeof(TClass), propertyInfo.Name, attribute); } } internal sealed class YamlAttributeOverridesInspector : ReflectionTypeInspector { public sealed class OverridePropertyDescriptor : IPropertyDescriptor { private readonly IPropertyDescriptor baseDescriptor; private readonly YamlAttributeOverrides overrides; private readonly Type classType; public string Name => baseDescriptor.Name; public bool Required => baseDescriptor.Required; public bool AllowNulls => baseDescriptor.AllowNulls; public bool CanWrite => baseDescriptor.CanWrite; public Type Type => baseDescriptor.Type; public Type? TypeOverride { get { return baseDescriptor.TypeOverride; } set { baseDescriptor.TypeOverride = value; } } public Type? ConverterType => GetCustomAttribute()?.ConverterType ?? baseDescriptor.ConverterType; public int Order { get { return baseDescriptor.Order; } set { baseDescriptor.Order = value; } } public ScalarStyle ScalarStyle { get { return baseDescriptor.ScalarStyle; } set { baseDescriptor.ScalarStyle = value; } } public OverridePropertyDescriptor(IPropertyDescriptor baseDescriptor, YamlAttributeOverrides overrides, Type classType) { this.baseDescriptor = baseDescriptor; this.overrides = overrides; this.classType = classType; } public void Write(object target, object? value) { baseDescriptor.Write(target, value); } public T? GetCustomAttribute() where T : Attribute { T attribute = overrides.GetAttribute(classType, Name); return attribute ?? baseDescriptor.GetCustomAttribute(); } public IObjectDescriptor Read(object target) { return baseDescriptor.Read(target); } } private readonly ITypeInspector innerTypeDescriptor; private readonly YamlAttributeOverrides overrides; public YamlAttributeOverridesInspector(ITypeInspector innerTypeDescriptor, YamlAttributeOverrides overrides) { this.innerTypeDescriptor = innerTypeDescriptor; this.overrides = overrides; } public override IEnumerable GetProperties(Type type, object? container) { IEnumerable enumerable = innerTypeDescriptor.GetProperties(type, container); if (overrides != null) { enumerable = enumerable.Select((Func)((IPropertyDescriptor p) => new OverridePropertyDescriptor(p, overrides, type))); } return enumerable; } } internal sealed class YamlAttributesTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; public YamlAttributesTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor; } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return from p in (from p in innerTypeDescriptor.GetProperties(type, container) where p.GetCustomAttribute() == null select p).Select((Func)delegate(IPropertyDescriptor p) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(p); YamlMemberAttribute customAttribute = p.GetCustomAttribute(); if (customAttribute != null) { if (customAttribute.SerializeAs != null) { propertyDescriptor.TypeOverride = customAttribute.SerializeAs; } propertyDescriptor.Order = customAttribute.Order; propertyDescriptor.ScalarStyle = customAttribute.ScalarStyle; if (customAttribute.Alias != null) { propertyDescriptor.Name = customAttribute.Alias; } } return propertyDescriptor; }) orderby p.Order select p; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class YamlConverterAttribute : Attribute { public Type ConverterType { get; } public YamlConverterAttribute(Type converterType) { ConverterType = converterType; } } internal class YamlFormatter { public static YamlFormatter Default { get; } = new YamlFormatter(); public NumberFormatInfo NumberFormat { get; set; } = new NumberFormatInfo { CurrencyDecimalSeparator = ".", CurrencyGroupSeparator = "_", CurrencyGroupSizes = new int[1] { 3 }, CurrencySymbol = string.Empty, CurrencyDecimalDigits = 99, NumberDecimalSeparator = ".", NumberGroupSeparator = "_", NumberGroupSizes = new int[1] { 3 }, NumberDecimalDigits = 99, NaNSymbol = ".nan", PositiveInfinitySymbol = ".inf", NegativeInfinitySymbol = "-.inf" }; public virtual Func FormatEnum { get; set; } = delegate(object value, ITypeInspector typeInspector, INamingConvention enumNamingConvention) { string empty = string.Empty; empty = ((value != null) ? typeInspector.GetEnumValue(value) : string.Empty); return enumNamingConvention.Apply(empty); }; public virtual Func PotentiallyQuoteEnums { get; set; } = (object _) => true; public string FormatNumber(object number) { return Convert.ToString(number, NumberFormat); } public string FormatNumber(double number) { return number.ToString("G", NumberFormat); } public string FormatNumber(float number) { return number.ToString("G", NumberFormat); } public string FormatBoolean(object boolean) { if (!boolean.Equals(true)) { return "false"; } return "true"; } public string FormatDateTime(object dateTime) { return ((DateTime)dateTime).ToString("o", CultureInfo.InvariantCulture); } public string FormatTimeSpan(object timeSpan) { return ((TimeSpan)timeSpan/*cast due to .constrained prefix*/).ToString(); } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class YamlIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class YamlMemberAttribute : Attribute { private DefaultValuesHandling? defaultValuesHandling; public string? Description { get; set; } public Type? SerializeAs { get; set; } public int Order { get; set; } public string? Alias { get; set; } public bool ApplyNamingConventions { get; set; } public ScalarStyle ScalarStyle { get; set; } public DefaultValuesHandling DefaultValuesHandling { get { return defaultValuesHandling.GetValueOrDefault(); } set { defaultValuesHandling = value; } } public bool IsDefaultValuesHandlingSpecified => defaultValuesHandling.HasValue; public YamlMemberAttribute() { ScalarStyle = ScalarStyle.Any; ApplyNamingConventions = true; } public YamlMemberAttribute(Type serializeAs) : this() { SerializeAs = serializeAs ?? throw new ArgumentNullException("serializeAs"); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, Inherited = false, AllowMultiple = true)] internal sealed class YamlSerializableAttribute : Attribute { public YamlSerializableAttribute() { } public YamlSerializableAttribute(Type serializableType) { } } [AttributeUsage(AttributeTargets.Class)] internal sealed class YamlStaticContextAttribute : Attribute { } } namespace YamlDotNet.Serialization.ValueDeserializers { internal sealed class AliasValueDeserializer : IValueDeserializer { private sealed class AliasState : Dictionary, IPostDeserializationCallback { public void OnDeserialization() { foreach (ValuePromise value in base.Values) { if (!value.HasValue) { YamlDotNet.Core.Events.AnchorAlias alias = value.Alias; throw new AnchorNotFoundException(alias.Start, alias.End, $"Anchor '{alias.Value}' not found"); } } } } private sealed class ValuePromise : IValuePromise { private object? value; public readonly YamlDotNet.Core.Events.AnchorAlias? Alias; public bool HasValue { get; private set; } public object? Value { get { if (!HasValue) { throw new InvalidOperationException("Value not set"); } return value; } set { if (HasValue) { throw new InvalidOperationException("Value already set"); } HasValue = true; this.value = value; this.ValueAvailable?.Invoke(value); } } public event Action? ValueAvailable; public ValuePromise(YamlDotNet.Core.Events.AnchorAlias alias) { Alias = alias; } public ValuePromise(object? value) { HasValue = true; this.value = value; } } private readonly IValueDeserializer innerDeserializer; public AliasValueDeserializer(IValueDeserializer innerDeserializer) { this.innerDeserializer = innerDeserializer ?? throw new ArgumentNullException("innerDeserializer"); } public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) { if (parser.TryConsume(out var @event)) { AliasState aliasState = state.Get(); if (!aliasState.TryGetValue(@event.Value, out ValuePromise value)) { throw new AnchorNotFoundException(@event.Start, @event.End, $"Alias ${@event.Value} cannot precede anchor declaration"); } if (!value.HasValue) { return value; } return value.Value; } AnchorName anchorName = AnchorName.Empty; if (parser.Accept(out var event2) && !event2.Anchor.IsEmpty) { anchorName = event2.Anchor; AliasState aliasState2 = state.Get(); if (!aliasState2.ContainsKey(anchorName)) { aliasState2[anchorName] = new ValuePromise(new YamlDotNet.Core.Events.AnchorAlias(anchorName)); } } object obj = innerDeserializer.DeserializeValue(parser, expectedType, state, nestedObjectDeserializer); if (!anchorName.IsEmpty) { AliasState aliasState3 = state.Get(); if (!aliasState3.TryGetValue(anchorName, out ValuePromise value2)) { aliasState3.Add(anchorName, new ValuePromise(obj)); } else if (!value2.HasValue) { value2.Value = obj; } else { aliasState3[anchorName] = new ValuePromise(obj); } } return obj; } } internal sealed class NodeValueDeserializer : IValueDeserializer { private readonly IList deserializers; private readonly IList typeResolvers; private readonly ITypeConverter typeConverter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public NodeValueDeserializer(IList deserializers, IList typeResolvers, ITypeConverter typeConverter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.deserializers = deserializers ?? throw new ArgumentNullException("deserializers"); this.typeResolvers = typeResolvers ?? throw new ArgumentNullException("typeResolvers"); this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); this.typeInspector = typeInspector; } public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) { parser.Accept(out var @event); Type typeFromEvent = GetTypeFromEvent(@event, expectedType); ObjectDeserializer rootDeserializer = (Type x) => DeserializeValue(parser, x, state, nestedObjectDeserializer); try { foreach (INodeDeserializer deserializer in deserializers) { if (deserializer.Deserialize(parser, typeFromEvent, (IParser r, Type t) => nestedObjectDeserializer.DeserializeValue(r, t, state, nestedObjectDeserializer), out object value, rootDeserializer)) { return typeConverter.ChangeType(value, expectedType, enumNamingConvention, typeInspector); } } } catch (YamlException) { throw; } catch (Exception innerException) { throw new YamlException(@event?.Start ?? Mark.Empty, @event?.End ?? Mark.Empty, "Exception during deserialization", innerException); } throw new YamlException(@event?.Start ?? Mark.Empty, @event?.End ?? Mark.Empty, "No node deserializer was able to deserialize the node into type " + expectedType.AssemblyQualifiedName); } private Type GetTypeFromEvent(NodeEvent? nodeEvent, Type currentType) { foreach (INodeTypeResolver typeResolver in typeResolvers) { if (typeResolver.Resolve(nodeEvent, ref currentType)) { break; } } return currentType; } } } namespace YamlDotNet.Serialization.Utilities { internal interface IPostDeserializationCallback { void OnDeserialization(); } internal interface ITypeConverter { object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector); } internal class NullTypeConverter : ITypeConverter { public object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return value; } } internal sealed class ObjectAnchorCollection { private readonly Dictionary objectsByAnchor = new Dictionary(); private readonly Dictionary anchorsByObject = new Dictionary(); public object this[string anchor] { get { if (objectsByAnchor.TryGetValue(anchor, out object value)) { return value; } throw new AnchorNotFoundException("The anchor '" + anchor + "' does not exists"); } } public void Add(string anchor, object @object) { objectsByAnchor.Add(anchor, @object); if (@object != null) { anchorsByObject.Add(@object, anchor); } } public bool TryGetAnchor(object @object, [MaybeNullWhen(false)] out string? anchor) { return anchorsByObject.TryGetValue(@object, out anchor); } } internal class ReflectionTypeConverter : ITypeConverter { public object? ChangeType(object? value, Type expectedType, ITypeInspector typeInspector) { return ChangeType(value, expectedType, NullNamingConvention.Instance, typeInspector); } public object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return TypeConverter.ChangeType(value, expectedType, enumNamingConvention, typeInspector); } } internal sealed class SerializerState : IDisposable { private readonly Dictionary items = new Dictionary(); public T Get() where T : class, new() { if (!items.TryGetValue(typeof(T), out object value)) { value = new T(); items.Add(typeof(T), value); } return (T)value; } public void OnDeserialization() { foreach (IPostDeserializationCallback item in items.Values.OfType()) { item.OnDeserialization(); } } public void Dispose() { foreach (IDisposable item in items.Values.OfType()) { item.Dispose(); } } } internal static class StringExtensions { private static string ToCamelOrPascalCase(string str, Func firstLetterTransform) { string text = Regex.Replace(str, "([_\\-])(?[a-z])", (Match match) => match.Groups["char"].Value.ToUpperInvariant(), RegexOptions.IgnoreCase); return firstLetterTransform(text[0]) + text.Substring(1); } public static string ToCamelCase(this string str) { return ToCamelOrPascalCase(str, char.ToLowerInvariant); } public static string ToPascalCase(this string str) { return ToCamelOrPascalCase(str, char.ToUpperInvariant); } public static string FromCamelCase(this string str, string separator) { str = char.ToLower(str[0], CultureInfo.InvariantCulture) + str.Substring(1); str = Regex.Replace(str.ToCamelCase(), "(?[A-Z])", (Match match) => separator + match.Groups["char"].Value.ToLowerInvariant()); return str; } } internal static class TypeConverter { public static T ChangeType(object? value, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return (T)ChangeType(value, typeof(T), enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return ChangeType(value, destinationType, CultureInfo.InvariantCulture, enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, IFormatProvider provider, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return ChangeType(value, destinationType, new CultureInfoAdapter(CultureInfo.CurrentCulture, provider), enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, CultureInfo culture, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { if (value == null || value.IsDbNull()) { if (!destinationType.IsValueType()) { return null; } return Activator.CreateInstance(destinationType); } Type type = value.GetType(); if (destinationType == type || destinationType.IsAssignableFrom(type)) { return value; } if (destinationType.IsGenericType()) { Type genericTypeDefinition = destinationType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Nullable<>) || FsharpHelper.IsOptionType(genericTypeDefinition)) { Type destinationType2 = destinationType.GetGenericArguments()[0]; object obj = ChangeType(value, destinationType2, culture, enumNamingConvention, typeInspector); return Activator.CreateInstance(destinationType, obj); } } if (destinationType.IsEnum()) { object result = value; if (value is string value2) { string name = enumNamingConvention.Reverse(value2); name = typeInspector.GetEnumName(destinationType, name); result = Enum.Parse(destinationType, name, ignoreCase: true); } return result; } if (destinationType == typeof(bool)) { if ("0".Equals(value)) { return false; } if ("1".Equals(value)) { return true; } } System.ComponentModel.TypeConverter converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.CanConvertTo(destinationType)) { return converter.ConvertTo(null, culture, value, destinationType); } System.ComponentModel.TypeConverter converter2 = TypeDescriptor.GetConverter(destinationType); if (converter2 != null && converter2.CanConvertFrom(type)) { return converter2.ConvertFrom(null, culture, value); } Type[] array = new Type[2] { type, destinationType }; foreach (Type type2 in array) { foreach (MethodInfo publicStaticMethod2 in type2.GetPublicStaticMethods()) { if (!publicStaticMethod2.IsSpecialName || (!(publicStaticMethod2.Name == "op_Implicit") && !(publicStaticMethod2.Name == "op_Explicit")) || !destinationType.IsAssignableFrom(publicStaticMethod2.ReturnParameter.ParameterType)) { continue; } ParameterInfo[] parameters = publicStaticMethod2.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(type)) { try { return publicStaticMethod2.Invoke(null, new object[1] { value }); } catch (TargetInvocationException ex) { throw ex.InnerException; } } } } if (type == typeof(string)) { try { MethodInfo publicStaticMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); if (publicStaticMethod != null) { return publicStaticMethod.Invoke(null, new object[2] { value, culture }); } publicStaticMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string)); if (publicStaticMethod != null) { return publicStaticMethod.Invoke(null, new object[1] { value }); } } catch (TargetInvocationException ex2) { throw ex2.InnerException; } } if (destinationType == typeof(TimeSpan)) { return TimeSpan.Parse((string)ChangeType(value, typeof(string), CultureInfo.InvariantCulture, enumNamingConvention, typeInspector), CultureInfo.InvariantCulture); } return Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); } public static void RegisterTypeConverter() where TConverter : System.ComponentModel.TypeConverter { if (!TypeDescriptor.GetAttributes(typeof(TConvertible)).OfType().Any((TypeConverterAttribute a) => a.ConverterTypeName == typeof(TConverter).AssemblyQualifiedName)) { TypeDescriptor.AddAttributes(typeof(TConvertible), new TypeConverterAttribute(typeof(TConverter))); } } } internal sealed class TypeConverterCache { private readonly IYamlTypeConverter[] typeConverters; private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); public TypeConverterCache(IEnumerable? typeConverters) : this(typeConverters?.ToArray() ?? Array.Empty()) { } public TypeConverterCache(IYamlTypeConverter[] typeConverters) { this.typeConverters = typeConverters; } public bool TryGetConverterForType(Type type, [NotNullWhen(true)] out IYamlTypeConverter? typeConverter) { (bool, IYamlTypeConverter) orAdd = DictionaryExtensions.GetOrAdd(cache, type, (Type t, IYamlTypeConverter[] tc) => LookupTypeConverter(t, tc), typeConverters); typeConverter = orAdd.Item2; return orAdd.Item1; } public IYamlTypeConverter GetConverterByType(Type converter) { IYamlTypeConverter[] array = typeConverters; foreach (IYamlTypeConverter yamlTypeConverter in array) { if (yamlTypeConverter.GetType() == converter) { return yamlTypeConverter; } } throw new ArgumentException("IYamlTypeConverter of type " + converter.FullName + " not found", "converter"); } private static (bool HasMatch, IYamlTypeConverter? TypeConverter) LookupTypeConverter(Type type, IYamlTypeConverter[] typeConverters) { foreach (IYamlTypeConverter yamlTypeConverter in typeConverters) { if (yamlTypeConverter.Accepts(type)) { return (HasMatch: true, TypeConverter: yamlTypeConverter); } } return (HasMatch: false, TypeConverter: null); } } } namespace YamlDotNet.Serialization.TypeResolvers { internal sealed class DynamicTypeResolver : ITypeResolver { public Type Resolve(Type staticType, object? actualValue) { if (actualValue == null) { return staticType; } return actualValue.GetType(); } } internal class StaticTypeResolver : ITypeResolver { public virtual Type Resolve(Type staticType, object? actualValue) { if (actualValue != null) { if (actualValue.GetType().IsEnum) { return staticType; } switch (actualValue.GetType().GetTypeCode()) { case TypeCode.Boolean: return typeof(bool); case TypeCode.Char: return typeof(char); case TypeCode.SByte: return typeof(sbyte); case TypeCode.Byte: return typeof(byte); case TypeCode.Int16: return typeof(short); case TypeCode.UInt16: return typeof(ushort); case TypeCode.Int32: return typeof(int); case TypeCode.UInt32: return typeof(uint); case TypeCode.Int64: return typeof(long); case TypeCode.UInt64: return typeof(ulong); case TypeCode.Single: return typeof(float); case TypeCode.Double: return typeof(double); case TypeCode.Decimal: return typeof(decimal); case TypeCode.String: return typeof(string); case TypeCode.DateTime: return typeof(DateTime); } } return staticType; } } } namespace YamlDotNet.Serialization.TypeInspectors { internal class CachedTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; private readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary> enumNameCache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary enumValueCache = new ConcurrentDictionary(); public CachedTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); } public override string GetEnumName(Type enumType, string name) { ConcurrentDictionary orAdd = enumNameCache.GetOrAdd(enumType, (Type _) => new ConcurrentDictionary()); return DictionaryExtensions.GetOrAdd(orAdd, name, delegate(string n, (Type enumType, ITypeInspector innerTypeDescriptor) context) { var (enumType2, typeInspector) = context; return typeInspector.GetEnumName(enumType2, n); }, (enumType, innerTypeDescriptor)); } public override string GetEnumValue(object enumValue) { return DictionaryExtensions.GetOrAdd(enumValueCache, enumValue, delegate(object _, (object enumValue, ITypeInspector innerTypeDescriptor) context) { var (enumValue2, typeInspector) = context; return typeInspector.GetEnumValue(enumValue2); }, (enumValue, innerTypeDescriptor)); } public override IEnumerable GetProperties(Type type, object? container) { return DictionaryExtensions.GetOrAdd(cache, type, delegate(Type t, (object container, ITypeInspector innerTypeDescriptor) context) { var (container2, typeInspector) = context; return typeInspector.GetProperties(t, container2).ToList(); }, (container, innerTypeDescriptor)); } } internal class CompositeTypeInspector : TypeInspectorSkeleton { private readonly IEnumerable typeInspectors; public CompositeTypeInspector(params ITypeInspector[] typeInspectors) : this((IEnumerable)typeInspectors) { } public CompositeTypeInspector(IEnumerable typeInspectors) { this.typeInspectors = typeInspectors?.ToList() ?? throw new ArgumentNullException("typeInspectors"); } public override string GetEnumName(Type enumType, string name) { foreach (ITypeInspector typeInspector in typeInspectors) { try { return typeInspector.GetEnumName(enumType, name); } catch { } } throw new ArgumentOutOfRangeException("enumType,name", "Name not found on enum type"); } public override string GetEnumValue(object enumValue) { if (enumValue == null) { throw new ArgumentNullException("enumValue"); } foreach (ITypeInspector typeInspector in typeInspectors) { try { return typeInspector.GetEnumValue(enumValue); } catch { } } throw new ArgumentOutOfRangeException("enumValue", $"Value not found for ({enumValue})"); } public override IEnumerable GetProperties(Type type, object? container) { return typeInspectors.SelectMany((ITypeInspector i) => i.GetProperties(type, container)); } } internal class NamingConventionTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; private readonly INamingConvention namingConvention; public NamingConventionTypeInspector(ITypeInspector innerTypeDescriptor, INamingConvention namingConvention) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return innerTypeDescriptor.GetProperties(type, container).Select(delegate(IPropertyDescriptor p) { YamlMemberAttribute customAttribute = p.GetCustomAttribute(); return (customAttribute != null && !customAttribute.ApplyNamingConventions) ? p : new PropertyDescriptor(p) { Name = namingConvention.Apply(p.Name) }; }); } } internal class ReadableAndWritablePropertiesTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; public ReadableAndWritablePropertiesTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return from p in innerTypeDescriptor.GetProperties(type, container) where p.CanWrite select p; } } internal class ReadableFieldsTypeInspector : ReflectionTypeInspector { protected class ReflectionFieldDescriptor : IPropertyDescriptor { private readonly FieldInfo fieldInfo; private readonly ITypeResolver typeResolver; public string Name => fieldInfo.Name; public bool Required => fieldInfo.IsRequired(); public Type Type => fieldInfo.FieldType; public Type? ConverterType { get; } public Type? TypeOverride { get; set; } public bool AllowNulls => fieldInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => !fieldInfo.IsInitOnly; public ScalarStyle ScalarStyle { get; set; } public ReflectionFieldDescriptor(FieldInfo fieldInfo, ITypeResolver typeResolver) { this.fieldInfo = fieldInfo; this.typeResolver = typeResolver; YamlConverterAttribute customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } ScalarStyle = ScalarStyle.Any; } public void Write(object target, object? value) { fieldInfo.SetValue(target, value); } public T? GetCustomAttribute() where T : Attribute { object[] customAttributes = fieldInfo.GetCustomAttributes(typeof(T), inherit: true); return (T)customAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object value = fieldInfo.GetValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, value); return new ObjectDescriptor(value, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; public ReadableFieldsTypeInspector(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); } public override IEnumerable GetProperties(Type type, object? container) { return type.GetPublicFields().Select((Func)((FieldInfo p) => new ReflectionFieldDescriptor(p, typeResolver))); } } internal class ReadablePropertiesTypeInspector : ReflectionTypeInspector { protected class ReflectionPropertyDescriptor : IPropertyDescriptor { private readonly PropertyInfo propertyInfo; private readonly ITypeResolver typeResolver; public string Name => propertyInfo.Name; public bool Required => propertyInfo.IsRequired(); public Type Type => propertyInfo.PropertyType; public Type? TypeOverride { get; set; } public Type? ConverterType { get; set; } public bool AllowNulls => propertyInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => propertyInfo.CanWrite; public ScalarStyle ScalarStyle { get; set; } public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) { this.propertyInfo = propertyInfo ?? throw new ArgumentNullException("propertyInfo"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); ScalarStyle = ScalarStyle.Any; YamlConverterAttribute customAttribute = propertyInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } } public void Write(object target, object? value) { propertyInfo.SetValue(target, value, null); } public T? GetCustomAttribute() where T : Attribute { Attribute[] allCustomAttributes = propertyInfo.GetAllCustomAttributes(); return (T)allCustomAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object obj = propertyInfo.ReadValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, obj); return new ObjectDescriptor(obj, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; private readonly bool includeNonPublicProperties; public ReadablePropertiesTypeInspector(ITypeResolver typeResolver) : this(typeResolver, includeNonPublicProperties: false) { } public ReadablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.includeNonPublicProperties = includeNonPublicProperties; } private static bool IsValidProperty(PropertyInfo property) { if (property.CanRead) { return property.GetGetMethod(nonPublic: true).GetParameters().Length == 0; } return false; } public override IEnumerable GetProperties(Type type, object? container) { return type.GetProperties(includeNonPublicProperties).Where(IsValidProperty).Select((Func)((PropertyInfo p) => new ReflectionPropertyDescriptor(p, typeResolver))); } } internal abstract class ReflectionTypeInspector : TypeInspectorSkeleton { public override string GetEnumName(Type enumType, string name) { return name; } public override string GetEnumValue(object enumValue) { if (enumValue == null) { return string.Empty; } return enumValue.ToString(); } } internal abstract class TypeInspectorSkeleton : ITypeInspector { public abstract string GetEnumName(Type enumType, string name); public abstract string GetEnumValue(object enumValue); public abstract IEnumerable GetProperties(Type type, object? container); public IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched, bool caseInsensitivePropertyMatching) { IEnumerable enumerable = ((!caseInsensitivePropertyMatching) ? (from p in GetProperties(type, container) where p.Name == name select p) : (from p in GetProperties(type, container) where p.Name.Equals(name, StringComparison.OrdinalIgnoreCase) select p)); using IEnumerator enumerator = enumerable.GetEnumerator(); if (!enumerator.MoveNext()) { if (ignoreUnmatched) { return null; } throw new SerializationException("Property '" + name + "' not found on type '" + type.FullName + "'."); } IPropertyDescriptor current = enumerator.Current; if (enumerator.MoveNext()) { throw new SerializationException("Multiple properties with the name/alias '" + name + "' already exists on type '" + type.FullName + "', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: " + string.Join(", ", enumerable.Select((IPropertyDescriptor p) => p.Name).ToArray())); } return current; } } internal class WritablePropertiesTypeInspector : ReflectionTypeInspector { protected class ReflectionPropertyDescriptor : IPropertyDescriptor { private readonly PropertyInfo propertyInfo; private readonly ITypeResolver typeResolver; public string Name => propertyInfo.Name; public bool Required => propertyInfo.IsRequired(); public Type Type => propertyInfo.PropertyType; public Type? TypeOverride { get; set; } public Type? ConverterType { get; set; } public bool AllowNulls => propertyInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => propertyInfo.CanWrite; public ScalarStyle ScalarStyle { get; set; } public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) { this.propertyInfo = propertyInfo ?? throw new ArgumentNullException("propertyInfo"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); ScalarStyle = ScalarStyle.Any; YamlConverterAttribute customAttribute = propertyInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } } public void Write(object target, object? value) { propertyInfo.SetValue(target, value, null); } public T? GetCustomAttribute() where T : Attribute { Attribute[] allCustomAttributes = propertyInfo.GetAllCustomAttributes(); return (T)allCustomAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object obj = propertyInfo.ReadValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, obj); return new ObjectDescriptor(obj, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; private readonly bool includeNonPublicProperties; public WritablePropertiesTypeInspector(ITypeResolver typeResolver) : this(typeResolver, includeNonPublicProperties: false) { } public WritablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.includeNonPublicProperties = includeNonPublicProperties; } private static bool IsValidProperty(PropertyInfo property) { if (property.CanWrite) { return property.GetSetMethod(nonPublic: true).GetParameters().Length == 1; } return false; } public override IEnumerable GetProperties(Type type, object? container) { return type.GetProperties(includeNonPublicProperties).Where(IsValidProperty).Select((Func)((PropertyInfo p) => new ReflectionPropertyDescriptor(p, typeResolver))) .ToArray(); } } } namespace YamlDotNet.Serialization.Schemas { internal sealed class FailsafeSchema { public static class Tags { public static readonly TagName Map = new TagName("tag:yaml.org,2002:map"); public static readonly TagName Seq = new TagName("tag:yaml.org,2002:seq"); public static readonly TagName Str = new TagName("tag:yaml.org,2002:str"); } } internal sealed class JsonSchema { public static class Tags { public static readonly TagName Null = new TagName("tag:yaml.org,2002:null"); public static readonly TagName Bool = new TagName("tag:yaml.org,2002:bool"); public static readonly TagName Int = new TagName("tag:yaml.org,2002:int"); public static readonly TagName Float = new TagName("tag:yaml.org,2002:float"); } } internal sealed class CoreSchema { public static class Tags { } } internal sealed class DefaultSchema { public static class Tags { public static readonly TagName Timestamp = new TagName("tag:yaml.org,2002:timestamp"); } } } namespace YamlDotNet.Serialization.ObjectGraphVisitors { internal sealed class AnchorAssigner : PreProcessingPhaseObjectGraphVisitorSkeleton, IAliasProvider { private class AnchorAssignment { public AnchorName Anchor; } private readonly Dictionary assignments = new Dictionary(); private uint nextId; public AnchorAssigner(IEnumerable typeConverters) : base(typeConverters) { } protected override bool Enter(IObjectDescriptor value, ObjectSerializer serializer) { if (value.Value != null && assignments.TryGetValue(value.Value, out AnchorAssignment value2)) { if (value2.Anchor.IsEmpty) { value2.Anchor = new AnchorName("o" + nextId.ToString(CultureInfo.InvariantCulture)); nextId++; } return false; } return true; } protected override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, ObjectSerializer serializer) { return true; } protected override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, ObjectSerializer serializer) { return true; } protected override void VisitScalar(IObjectDescriptor scalar, ObjectSerializer serializer) { } protected override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, ObjectSerializer serializer) { VisitObject(mapping); } protected override void VisitMappingEnd(IObjectDescriptor mapping, ObjectSerializer serializer) { } protected override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, ObjectSerializer serializer) { VisitObject(sequence); } protected override void VisitSequenceEnd(IObjectDescriptor sequence, ObjectSerializer serializer) { } private void VisitObject(IObjectDescriptor value) { if (value.Value != null) { assignments.Add(value.Value, new AnchorAssignment()); } } AnchorName IAliasProvider.GetAlias(object target) { if (target != null && assignments.TryGetValue(target, out AnchorAssignment value)) { return value.Anchor; } return AnchorName.Empty; } } internal sealed class AnchorAssigningObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly IEventEmitter eventEmitter; private readonly IAliasProvider aliasProvider; private readonly HashSet emittedAliases = new HashSet(); public AnchorAssigningObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEventEmitter eventEmitter, IAliasProvider aliasProvider) : base(nextVisitor) { this.eventEmitter = eventEmitter; this.aliasProvider = aliasProvider; } public override bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (value.Value != null) { AnchorName alias = aliasProvider.GetAlias(value.Value); if (!alias.IsEmpty && !emittedAliases.Add(alias)) { AliasEventInfo aliasEventInfo = new AliasEventInfo(value, alias); eventEmitter.Emit(aliasEventInfo, context); return aliasEventInfo.NeedsExpansion; } } return base.Enter(propertyDescriptor, value, context, serializer); } public override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { AnchorName alias = aliasProvider.GetAlias(mapping.NonNullValue()); eventEmitter.Emit(new MappingStartEventInfo(mapping) { Anchor = alias }, context); } public override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { AnchorName alias = aliasProvider.GetAlias(sequence.NonNullValue()); eventEmitter.Emit(new SequenceStartEventInfo(sequence) { Anchor = alias }, context); } public override void VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { ScalarEventInfo scalarEventInfo = new ScalarEventInfo(scalar); if (scalar.Value != null) { scalarEventInfo.Anchor = aliasProvider.GetAlias(scalar.Value); } eventEmitter.Emit(scalarEventInfo, context); } } internal abstract class ChainedObjectGraphVisitor : IObjectGraphVisitor { private readonly IObjectGraphVisitor nextVisitor; protected ChainedObjectGraphVisitor(IObjectGraphVisitor nextVisitor) { this.nextVisitor = nextVisitor; } public virtual bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.Enter(propertyDescriptor, value, context, serializer); } public virtual bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.EnterMapping(key, value, context, serializer); } public virtual bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.EnterMapping(key, value, context, serializer); } public virtual void VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitScalar(scalar, context, serializer); } public virtual void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitMappingStart(mapping, keyType, valueType, context, serializer); } public virtual void VisitMappingEnd(IObjectDescriptor mapping, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitMappingEnd(mapping, context, serializer); } public virtual void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitSequenceStart(sequence, elementType, context, serializer); } public virtual void VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitSequenceEnd(sequence, context, serializer); } } internal sealed class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor { public CommentsObjectGraphVisitor(IObjectGraphVisitor nextVisitor) : base(nextVisitor) { } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { YamlMemberAttribute customAttribute = key.GetCustomAttribute(); if (customAttribute != null && customAttribute.Description != null) { context.Emit(new YamlDotNet.Core.Events.Comment(customAttribute.Description, isInline: false)); } return base.EnterMapping(key, value, context, serializer); } } internal sealed class CustomSerializationObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly TypeConverterCache typeConverters; private readonly ObjectSerializer nestedObjectSerializer; public CustomSerializationObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) : base(nextVisitor) { this.typeConverters = new TypeConverterCache(typeConverters); this.nestedObjectSerializer = nestedObjectSerializer; } public override bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (propertyDescriptor?.ConverterType != null) { IYamlTypeConverter converterByType = typeConverters.GetConverterByType(propertyDescriptor.ConverterType); converterByType.WriteYaml(context, value.Value, value.Type, serializer); return false; } if (typeConverters.TryGetConverterForType(value.Type, out IYamlTypeConverter typeConverter)) { typeConverter.WriteYaml(context, value.Value, value.Type, serializer); return false; } if (value.Value is IYamlConvertible yamlConvertible) { yamlConvertible.Write(context, nestedObjectSerializer); return false; } if (value.Value is IYamlSerializable yamlSerializable) { yamlSerializable.WriteYaml(context); return false; } return base.Enter(propertyDescriptor, value, context, serializer); } } internal sealed class DefaultExclusiveObjectGraphVisitor : ChainedObjectGraphVisitor { public DefaultExclusiveObjectGraphVisitor(IObjectGraphVisitor nextVisitor) : base(nextVisitor) { } private static object? GetDefault(Type type) { if (!type.IsValueType()) { return null; } return Activator.CreateInstance(type); } public override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (!object.Equals(value.Value, GetDefault(value.Type))) { return base.EnterMapping(key, value, context, serializer); } return false; } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { DefaultValueAttribute customAttribute = key.GetCustomAttribute(); object objB = ((customAttribute != null) ? customAttribute.Value : GetDefault(key.Type)); if (!object.Equals(value.Value, objB)) { return base.EnterMapping(key, value, context, serializer); } return false; } } internal sealed class DefaultValuesObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly DefaultValuesHandling handling; private readonly IObjectFactory factory; public DefaultValuesObjectGraphVisitor(DefaultValuesHandling handling, IObjectGraphVisitor nextVisitor, IObjectFactory factory) : base(nextVisitor) { this.handling = handling; this.factory = factory; } private object? GetDefault(Type type) { return factory.CreatePrimitive(type); } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { DefaultValuesHandling defaultValuesHandling = handling; YamlMemberAttribute customAttribute = key.GetCustomAttribute(); if (customAttribute != null && customAttribute.IsDefaultValuesHandlingSpecified) { defaultValuesHandling = customAttribute.DefaultValuesHandling; } if ((defaultValuesHandling & DefaultValuesHandling.OmitNull) != DefaultValuesHandling.Preserve && value.Value == null) { return false; } if ((defaultValuesHandling & DefaultValuesHandling.OmitEmptyCollections) != DefaultValuesHandling.Preserve && value.Value is IEnumerable enumerable) { IEnumerator enumerator = enumerable.GetEnumerator(); bool flag = enumerator.MoveNext(); if (enumerator is IDisposable disposable) { disposable.Dispose(); } if (!flag) { return false; } } if ((defaultValuesHandling & DefaultValuesHandling.OmitDefaults) != DefaultValuesHandling.Preserve) { object objB = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); if (object.Equals(value.Value, objB)) { return false; } } return base.EnterMapping(key, value, context, serializer); } } internal sealed class EmittingObjectGraphVisitor : IObjectGraphVisitor { private readonly IEventEmitter eventEmitter; public EmittingObjectGraphVisitor(IEventEmitter eventEmitter) { this.eventEmitter = eventEmitter; } bool IObjectGraphVisitor.Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new ScalarEventInfo(scalar), context); } void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new MappingStartEventInfo(mapping), context); } void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new MappingEndEventInfo(mapping), context); } void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new SequenceStartEventInfo(sequence), context); } void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new SequenceEndEventInfo(sequence), context); } } internal abstract class PreProcessingPhaseObjectGraphVisitorSkeleton : IObjectGraphVisitor { protected readonly IEnumerable typeConverters; private readonly TypeConverterCache typeConverterCache; public PreProcessingPhaseObjectGraphVisitorSkeleton(IEnumerable typeConverters) { typeConverterCache = new TypeConverterCache((IYamlTypeConverter[])(this.typeConverters = typeConverters?.ToArray() ?? Array.Empty())); } bool IObjectGraphVisitor.Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { if (typeConverterCache.TryGetConverterForType(value.Type, out IYamlTypeConverter _)) { return false; } if (value.Value is IYamlConvertible) { return false; } if (value.Value is IYamlSerializable) { return false; } return Enter(value, serializer); } bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { return EnterMapping(key, value, serializer); } bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { return EnterMapping(key, value, serializer); } void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, Nothing context, ObjectSerializer serializer) { VisitMappingEnd(mapping, serializer); } void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, Nothing context, ObjectSerializer serializer) { VisitMappingStart(mapping, keyType, valueType, serializer); } void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, Nothing context, ObjectSerializer serializer) { VisitScalar(scalar, serializer); } void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, Nothing context, ObjectSerializer serializer) { VisitSequenceEnd(sequence, serializer); } void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, Nothing context, ObjectSerializer serializer) { VisitSequenceStart(sequence, elementType, serializer); } protected abstract bool Enter(IObjectDescriptor value, ObjectSerializer serializer); protected abstract bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, ObjectSerializer serializer); protected abstract bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, ObjectSerializer serializer); protected abstract void VisitMappingEnd(IObjectDescriptor mapping, ObjectSerializer serializer); protected abstract void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, ObjectSerializer serializer); protected abstract void VisitScalar(IObjectDescriptor scalar, ObjectSerializer serializer); protected abstract void VisitSequenceEnd(IObjectDescriptor sequence, ObjectSerializer serializer); protected abstract void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, ObjectSerializer serializer); } } namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies { internal class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy { protected readonly struct ObjectPathSegment { public readonly object Name; public readonly IObjectDescriptor Value; public ObjectPathSegment(object name, IObjectDescriptor value) { Name = name; Value = value; } } private readonly int maxRecursion; private readonly ITypeInspector typeDescriptor; private readonly ITypeResolver typeResolver; private readonly INamingConvention namingConvention; private readonly IObjectFactory objectFactory; public FullObjectGraphTraversalStrategy(ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention, IObjectFactory objectFactory) { if (maxRecursion <= 0) { throw new ArgumentOutOfRangeException("maxRecursion", maxRecursion, "maxRecursion must be greater than 1"); } this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException("typeDescriptor"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.maxRecursion = maxRecursion; this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } void IObjectGraphTraversalStrategy.Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context, ObjectSerializer serializer) { Traverse(null, "", graph, visitor, context, new Stack(maxRecursion), serializer); } protected virtual void Traverse(IPropertyDescriptor? propertyDescriptor, object name, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (path.Count >= maxRecursion) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Too much recursion when traversing the object graph."); stringBuilder.AppendLine("The path to reach this recursion was:"); Stack> stack = new Stack>(path.Count); int num = 0; foreach (ObjectPathSegment item in path) { string text = item.Name?.ToString() ?? string.Empty; num = Math.Max(num, text.Length); stack.Push(new KeyValuePair(text, item.Value.Type.FullName)); } foreach (KeyValuePair item2 in stack) { stringBuilder.Append(" -> ").Append(item2.Key.PadRight(num)).Append(" [") .Append(item2.Value) .AppendLine("]"); } throw new MaximumRecursionLevelReachedException(stringBuilder.ToString()); } if (!visitor.Enter(propertyDescriptor, value, context, serializer)) { return; } path.Push(new ObjectPathSegment(name, value)); try { TypeCode typeCode = value.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: case TypeCode.DateTime: case TypeCode.String: visitor.VisitScalar(value, context, serializer); return; case TypeCode.Empty: throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } if (value.IsDbNull()) { visitor.VisitScalar(new ObjectDescriptor(null, typeof(object), typeof(object)), context, serializer); } if (value.Value == null || value.Type == typeof(TimeSpan)) { visitor.VisitScalar(value, context, serializer); return; } Type underlyingType = Nullable.GetUnderlyingType(value.Type); Type type = underlyingType ?? FsharpHelper.GetOptionUnderlyingType(value.Type); object obj = ((type != null) ? FsharpHelper.GetValue(value) : null); if (underlyingType != null) { Traverse(propertyDescriptor, "Value", new ObjectDescriptor(value.Value, underlyingType, value.Type, value.ScalarStyle), visitor, context, path, serializer); } else if (type != null && obj != null) { Traverse(propertyDescriptor, "Value", new ObjectDescriptor(FsharpHelper.GetValue(value), type, value.Type, value.ScalarStyle), visitor, context, path, serializer); } else { TraverseObject(propertyDescriptor, value, visitor, context, path, serializer); } } finally { path.Pop(); } } protected virtual void TraverseObject(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { IDictionary dictionary; Type[] genericArguments; if (typeof(IDictionary).IsAssignableFrom(value.Type)) { TraverseDictionary(propertyDescriptor, value, visitor, typeof(object), typeof(object), context, path, serializer); } else if (objectFactory.GetDictionary(value, out dictionary, out genericArguments)) { TraverseDictionary(propertyDescriptor, new ObjectDescriptor(dictionary, value.Type, value.StaticType, value.ScalarStyle), visitor, genericArguments[0], genericArguments[1], context, path, serializer); } else if (typeof(IEnumerable).IsAssignableFrom(value.Type)) { TraverseList(propertyDescriptor, value, visitor, context, path, serializer); } else { TraverseProperties(value, visitor, context, path, serializer); } } protected virtual void TraverseDictionary(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor dictionary, IObjectGraphVisitor visitor, Type keyType, Type valueType, TContext context, Stack path, ObjectSerializer serializer) { visitor.VisitMappingStart(dictionary, keyType, valueType, context, serializer); bool flag = dictionary.Type.FullName.Equals("System.Dynamic.ExpandoObject"); foreach (DictionaryEntry? item in (IDictionary)dictionary.NonNullValue()) { DictionaryEntry value = item.Value; object obj = (flag ? namingConvention.Apply(value.Key.ToString()) : value.Key); ObjectDescriptor objectDescriptor = GetObjectDescriptor(obj, keyType); ObjectDescriptor objectDescriptor2 = GetObjectDescriptor(value.Value, valueType); if (visitor.EnterMapping(objectDescriptor, objectDescriptor2, context, serializer)) { Traverse(propertyDescriptor, obj, objectDescriptor, visitor, context, path, serializer); Traverse(propertyDescriptor, obj, objectDescriptor2, visitor, context, path, serializer); } } visitor.VisitMappingEnd(dictionary, context, serializer); } private void TraverseList(IPropertyDescriptor propertyDescriptor, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { Type valueType = objectFactory.GetValueType(value.Type); visitor.VisitSequenceStart(value, valueType, context, serializer); int num = 0; foreach (object item in (IEnumerable)value.NonNullValue()) { Traverse(propertyDescriptor, num, GetObjectDescriptor(item, valueType), visitor, context, path, serializer); num++; } visitor.VisitSequenceEnd(value, context, serializer); } protected virtual void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (context.GetType() != typeof(Nothing)) { objectFactory.ExecuteOnSerializing(value.Value); } visitor.VisitMappingStart(value, typeof(string), typeof(object), context, serializer); object obj = value.NonNullValue(); foreach (IPropertyDescriptor property in typeDescriptor.GetProperties(value.Type, obj)) { IObjectDescriptor value2 = property.Read(obj); if (visitor.EnterMapping(property, value2, context, serializer)) { Traverse(null, property.Name, new ObjectDescriptor(property.Name, typeof(string), typeof(string), ScalarStyle.Plain), visitor, context, path, serializer); Traverse(property, property.Name, value2, visitor, context, path, serializer); } } visitor.VisitMappingEnd(value, context, serializer); if (context.GetType() != typeof(Nothing)) { objectFactory.ExecuteOnSerialized(value.Value); } } private ObjectDescriptor GetObjectDescriptor(object? value, Type staticType) { return new ObjectDescriptor(value, typeResolver.Resolve(staticType, value), staticType); } } internal class RoundtripObjectGraphTraversalStrategy : FullObjectGraphTraversalStrategy { private readonly TypeConverterCache converters; private readonly Settings settings; public RoundtripObjectGraphTraversalStrategy(IEnumerable converters, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention, Settings settings, IObjectFactory factory) : base(typeDescriptor, typeResolver, maxRecursion, namingConvention, factory) { this.converters = new TypeConverterCache(converters); this.settings = settings; } protected override void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (!value.Type.HasDefaultConstructor(settings.AllowPrivateConstructors) && !converters.TryGetConverterForType(value.Type, out IYamlTypeConverter _)) { throw new InvalidOperationException($"Type '{value.Type}' cannot be deserialized because it does not have a default constructor or a type converter."); } base.TraverseProperties(value, visitor, context, path, serializer); } } } namespace YamlDotNet.Serialization.ObjectFactories { internal class DefaultObjectFactory : ObjectFactoryBase { private readonly Dictionary> stateMethods = new Dictionary> { { typeof(YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnSerializedAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnSerializingAttribute), new ConcurrentDictionary() } }; private readonly Dictionary defaultGenericInterfaceImplementations = new Dictionary { { typeof(IEnumerable<>), typeof(List<>) }, { typeof(ICollection<>), typeof(List<>) }, { typeof(IList<>), typeof(List<>) }, { typeof(IDictionary<, >), typeof(Dictionary<, >) } }; private readonly Dictionary defaultNonGenericInterfaceImplementations = new Dictionary { { typeof(IEnumerable), typeof(List) }, { typeof(ICollection), typeof(List) }, { typeof(IList), typeof(List) }, { typeof(IDictionary), typeof(Dictionary) } }; private readonly Settings settings; public DefaultObjectFactory() : this(new Dictionary(), new Settings()) { } public DefaultObjectFactory(IDictionary mappings) : this(mappings, new Settings()) { } public DefaultObjectFactory(IDictionary mappings, Settings settings) { foreach (KeyValuePair mapping in mappings) { if (!mapping.Key.IsAssignableFrom(mapping.Value)) { throw new InvalidOperationException($"Type '{mapping.Value}' does not implement type '{mapping.Key}'."); } defaultNonGenericInterfaceImplementations.Add(mapping.Key, mapping.Value); } this.settings = settings; } public override object Create(Type type) { if (type.IsInterface()) { Type value2; if (type.IsGenericType()) { if (defaultGenericInterfaceImplementations.TryGetValue(type.GetGenericTypeDefinition(), out Type value)) { type = value.MakeGenericType(type.GetGenericArguments()); } } else if (defaultNonGenericInterfaceImplementations.TryGetValue(type, out value2)) { type = value2; } } try { return Activator.CreateInstance(type, settings.AllowPrivateConstructors); } catch (Exception innerException) { string message = "Failed to create an instance of type '" + type.FullName + "'."; throw new InvalidOperationException(message, innerException); } } public override void ExecuteOnDeserialized(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute), value); } public override void ExecuteOnDeserializing(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute), value); } public override void ExecuteOnSerialized(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnSerializedAttribute), value); } public override void ExecuteOnSerializing(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnSerializingAttribute), value); } private void ExecuteState(Type attributeType, object value) { if (value != null) { Type type = value.GetType(); MethodInfo[] array = GetStateMethods(attributeType, type); MethodInfo[] array2 = array; foreach (MethodInfo methodInfo in array2) { methodInfo.Invoke(value, null); } } } private MethodInfo[] GetStateMethods(Type attributeType, Type valueType) { ConcurrentDictionary concurrentDictionary = stateMethods[attributeType]; return concurrentDictionary.GetOrAdd(valueType, delegate(Type type) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return methods.Where((MethodInfo x) => x.GetCustomAttributes(attributeType, inherit: true).Length != 0).ToArray(); }); } } internal sealed class LambdaObjectFactory : ObjectFactoryBase { private readonly Func factory; public LambdaObjectFactory(Func factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public override object Create(Type type) { return factory(type); } } internal abstract class ObjectFactoryBase : IObjectFactory { public abstract object Create(Type type); public virtual object? CreatePrimitive(Type type) { if (!type.IsValueType()) { return null; } return Activator.CreateInstance(type); } public virtual void ExecuteOnDeserialized(object value) { } public virtual void ExecuteOnDeserializing(object value) { } public virtual void ExecuteOnSerialized(object value) { } public virtual void ExecuteOnSerializing(object value) { } public virtual bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments) { Type implementationOfOpenGenericInterface = descriptor.Type.GetImplementationOfOpenGenericInterface(typeof(IDictionary<, >)); if (implementationOfOpenGenericInterface != null) { genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); object obj = Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<, >).MakeGenericType(genericArguments), descriptor.Value); dictionary = obj as IDictionary; return true; } genericArguments = null; dictionary = null; return false; } public virtual Type GetValueType(Type type) { Type implementationOfOpenGenericInterface = type.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); return (implementationOfOpenGenericInterface != null) ? implementationOfOpenGenericInterface.GetGenericArguments()[0] : typeof(object); } } internal abstract class StaticObjectFactory : IObjectFactory { public abstract object Create(Type type); public abstract Array CreateArray(Type type, int count); public abstract bool IsDictionary(Type type); public abstract bool IsArray(Type type); public abstract bool IsList(Type type); public abstract Type GetKeyType(Type type); public abstract Type GetValueType(Type type); public virtual object? CreatePrimitive(Type type) { return Type.GetTypeCode(type) switch { TypeCode.Boolean => false, TypeCode.Byte => (byte)0, TypeCode.Int16 => (short)0, TypeCode.Int32 => 0, TypeCode.Int64 => 0L, TypeCode.SByte => (sbyte)0, TypeCode.UInt16 => (ushort)0, TypeCode.UInt32 => 0u, TypeCode.UInt64 => 0uL, TypeCode.Single => 0f, TypeCode.Double => 0.0, TypeCode.Decimal => 0m, TypeCode.Char => '\0', TypeCode.DateTime => default(DateTime), _ => null, }; } public bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments) { dictionary = null; genericArguments = null; return false; } public abstract void ExecuteOnDeserializing(object value); public abstract void ExecuteOnDeserialized(object value); public abstract void ExecuteOnSerializing(object value); public abstract void ExecuteOnSerialized(object value); } } namespace YamlDotNet.Serialization.NodeTypeResolvers { internal sealed class DefaultContainersNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (currentType == typeof(object)) { if (nodeEvent is SequenceStart) { currentType = typeof(List); return true; } if (nodeEvent is MappingStart) { currentType = typeof(Dictionary); return true; } } return false; } } internal class MappingNodeTypeResolver : INodeTypeResolver { private readonly IDictionary mappings; public MappingNodeTypeResolver(IDictionary mappings) { if (mappings == null) { throw new ArgumentNullException("mappings"); } foreach (KeyValuePair mapping in mappings) { if (!mapping.Key.IsAssignableFrom(mapping.Value)) { throw new InvalidOperationException($"Type '{mapping.Value}' does not implement type '{mapping.Key}'."); } } this.mappings = mappings; } public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (mappings.TryGetValue(currentType, out Type value)) { currentType = value; return true; } return false; } } internal class PreventUnknownTagsNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) { throw new YamlException(nodeEvent.Start, nodeEvent.End, $"Encountered an unresolved tag '{nodeEvent.Tag}'"); } return false; } } internal sealed class TagNodeTypeResolver : INodeTypeResolver { private readonly IDictionary tagMappings; public TagNodeTypeResolver(IDictionary tagMappings) { this.tagMappings = tagMappings ?? throw new ArgumentNullException("tagMappings"); } bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty && tagMappings.TryGetValue(nodeEvent.Tag, out Type value)) { currentType = value; return true; } return false; } } [Obsolete("The mechanism that this class uses to specify type names is non-standard. Register the tags explicitly instead of using this convention.")] internal sealed class TypeNameInTagNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) { Type type = Type.GetType(nodeEvent.Tag.Value.Substring(1), throwOnError: false); if (type != null) { currentType = type; return true; } } return false; } } internal sealed class YamlConvertibleTypeResolver : INodeTypeResolver { public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { return typeof(IYamlConvertible).IsAssignableFrom(currentType); } } internal sealed class YamlSerializableTypeResolver : INodeTypeResolver { public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { return typeof(IYamlSerializable).IsAssignableFrom(currentType); } } } namespace YamlDotNet.Serialization.NodeDeserializers { internal sealed class ArrayNodeDeserializer : INodeDeserializer { private sealed class ArrayList : IList, ICollection, IEnumerable { private object?[] data; public bool IsFixedSize => false; public bool IsReadOnly => false; public object? this[int index] { get { return data[index]; } set { data[index] = value; } } public int Count { get; private set; } public bool IsSynchronized => false; public object SyncRoot => data; public ArrayList() { Clear(); } public int Add(object? value) { if (Count == data.Length) { Array.Resize(ref data, data.Length * 2); } data[Count] = value; return Count++; } public void Clear() { data = new object[10]; Count = 0; } bool IList.Contains(object? value) { throw new NotSupportedException(); } int IList.IndexOf(object? value) { throw new NotSupportedException(); } void IList.Insert(int index, object? value) { throw new NotSupportedException(); } void IList.Remove(object? value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { Array.Copy(data, 0, array, index, Count); } public IEnumerator GetEnumerator() { int i = 0; while (i < Count) { yield return data[i]; int num = i + 1; i = num; } } } private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public ArrayNodeDeserializer(INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!expectedType.IsArray) { value = false; return false; } Type itemType = expectedType.GetElementType(); ArrayList arrayList = new ArrayList(); Array array = null; CollectionNodeDeserializer.DeserializeHelper(itemType, parser, nestedObjectDeserializer, arrayList, canUpdate: true, enumNamingConvention, typeInspector, PromiseResolvedHandler); array = Array.CreateInstance(itemType, arrayList.Count); arrayList.CopyTo(array, 0); value = array; return true; void PromiseResolvedHandler(int index, object? value2) { if (array == null) { throw new InvalidOperationException("Destination array is still null"); } array.SetValue(YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(value2, itemType, enumNamingConvention, typeInspector), index); } } } internal abstract class CollectionDeserializer { protected static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate, IObjectFactory objectFactory) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { if (!canUpdate) { throw new ForwardAnchorNotSupportedException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Forward alias references are not allowed because this type does not implement IList<>"); } int index = result.Add(objectFactory.CreatePrimitive(tItem)); valuePromise.ValueAvailable += delegate(object? v) { result[index] = v; }; } else { result.Add(obj); } } } } internal sealed class CollectionNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public CollectionNodeDeserializer(IObjectFactory objectFactory, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { bool canUpdate = true; Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(ICollection<>)); Type type; IList list; if (implementationOfOpenGenericInterface != null) { Type[] genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); type = genericArguments[0]; value = objectFactory.Create(expectedType); list = value as IList; if (list == null) { Type implementationOfOpenGenericInterface2 = expectedType.GetImplementationOfOpenGenericInterface(typeof(IList<>)); canUpdate = implementationOfOpenGenericInterface2 != null; list = (IList)Activator.CreateInstance(typeof(GenericCollectionToNonGenericAdapter<>).MakeGenericType(type), value); } } else { if (!typeof(IList).IsAssignableFrom(expectedType)) { value = null; return false; } type = typeof(object); value = objectFactory.Create(expectedType); list = (IList)value; } DeserializeHelper(type, parser, nestedObjectDeserializer, list, canUpdate, enumNamingConvention, typeInspector); return true; } internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate, INamingConvention enumNamingConvention, ITypeInspector typeInspector, Action? promiseResolvedHandler = null) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { if (!canUpdate) { throw new ForwardAnchorNotSupportedException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Forward alias references are not allowed because this type does not implement IList<>"); } int index = result.Add(tItem.IsValueType() ? Activator.CreateInstance(tItem) : null); if (promiseResolvedHandler != null) { valuePromise.ValueAvailable += delegate(object? v) { promiseResolvedHandler(index, v); }; } else { valuePromise.ValueAvailable += delegate(object? v) { result[index] = YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(v, tItem, enumNamingConvention, typeInspector); }; } } else { result.Add(YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(obj, tItem, enumNamingConvention, typeInspector)); } } } } internal abstract class DictionaryDeserializer { private readonly bool duplicateKeyChecking; public DictionaryDeserializer(bool duplicateKeyChecking) { this.duplicateKeyChecking = duplicateKeyChecking; } private void TryAssign(IDictionary result, object key, object value, MappingStart propertyName) { if (duplicateKeyChecking && result.Contains(key)) { throw new YamlException(propertyName.Start, propertyName.End, $"Encountered duplicate key {key}"); } result[key] = value; } protected virtual void Deserialize(Type tKey, Type tValue, IParser parser, Func nestedObjectDeserializer, IDictionary result, ObjectDeserializer rootDeserializer) { MappingStart property = parser.Consume(); MappingEnd @event; while (!parser.TryConsume(out @event)) { object key = nestedObjectDeserializer(parser, tKey); object value = nestedObjectDeserializer(parser, tValue); IValuePromise valuePromise = value as IValuePromise; if (key is IValuePromise valuePromise2) { if (valuePromise == null) { valuePromise2.ValueAvailable += delegate(object? v) { result[v] = value; }; continue; } bool hasFirstPart = false; valuePromise2.ValueAvailable += delegate(object? v) { if (hasFirstPart) { TryAssign(result, v, value, property); } else { key = v; hasFirstPart = true; } }; valuePromise.ValueAvailable += delegate(object? v) { if (hasFirstPart) { TryAssign(result, key, v, property); } else { value = v; hasFirstPart = true; } }; continue; } if (key == null) { throw new ArgumentException("Empty key names are not supported yet.", "tKey"); } if (valuePromise == null) { TryAssign(result, key, value, property); continue; } valuePromise.ValueAvailable += delegate(object? v) { result[key] = v; }; } } } internal class DictionaryNodeDeserializer : DictionaryDeserializer, INodeDeserializer { private readonly IObjectFactory objectFactory; public DictionaryNodeDeserializer(IObjectFactory objectFactory, bool duplicateKeyChecking) : base(duplicateKeyChecking) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(IDictionary<, >)); Type type; Type type2; IDictionary dictionary; if (implementationOfOpenGenericInterface != null) { Type[] genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); type = genericArguments[0]; type2 = genericArguments[1]; value = objectFactory.Create(expectedType); dictionary = value as IDictionary; if (dictionary == null) { dictionary = (IDictionary)Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<, >).MakeGenericType(type, type2), value); } } else { if (!typeof(IDictionary).IsAssignableFrom(expectedType)) { value = null; return false; } type = typeof(object); type2 = typeof(object); value = objectFactory.Create(expectedType); dictionary = (IDictionary)value; } Deserialize(type, type2, parser, nestedObjectDeserializer, dictionary, rootDeserializer); return true; } } internal sealed class EnumerableNodeDeserializer : INodeDeserializer { public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { Type type; if (expectedType == typeof(IEnumerable)) { type = typeof(object); } else { Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); if (implementationOfOpenGenericInterface != expectedType) { value = null; return false; } type = implementationOfOpenGenericInterface.GetGenericArguments()[0]; } Type arg = typeof(List<>).MakeGenericType(type); value = nestedObjectDeserializer(parser, arg); return true; } } internal sealed class FsharpListNodeDeserializer : INodeDeserializer { private readonly ITypeInspector typeInspector; private readonly INamingConvention enumNamingConvention; public FsharpListNodeDeserializer(ITypeInspector typeInspector, INamingConvention enumNamingConvention) { this.typeInspector = typeInspector; this.enumNamingConvention = enumNamingConvention; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!FsharpHelper.IsFsharpListType(expectedType)) { value = false; return false; } Type type = expectedType.GetGenericArguments()[0]; Type t = expectedType.GetGenericTypeDefinition().MakeGenericType(type); ArrayList arrayList = new ArrayList(); CollectionNodeDeserializer.DeserializeHelper(type, parser, nestedObjectDeserializer, arrayList, canUpdate: true, enumNamingConvention, typeInspector); Array array = Array.CreateInstance(type, arrayList.Count); arrayList.CopyTo(array, 0); object obj = FsharpHelper.CreateFsharpListFromArray(t, type, array); value = obj; return true; } } internal sealed class NullNodeDeserializer : INodeDeserializer { public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { value = null; if (parser.Accept(out var @event) && NodeIsNull(@event)) { parser.SkipThisAndNestedEvents(); return true; } return false; } private static bool NodeIsNull(NodeEvent nodeEvent) { if (nodeEvent.Tag == "tag:yaml.org,2002:null") { return true; } if (nodeEvent is YamlDotNet.Core.Events.Scalar { Style: ScalarStyle.Plain, IsKey: false } scalar) { string value = scalar.Value; switch (value) { default: return value == "NULL"; case "": case "~": case "null": case "Null": return true; } } return false; } } internal sealed class ObjectNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; private readonly ITypeInspector typeInspector; private readonly bool ignoreUnmatched; private readonly bool duplicateKeyChecking; private readonly ITypeConverter typeConverter; private readonly INamingConvention enumNamingConvention; private readonly bool enforceNullability; private readonly bool caseInsensitivePropertyMatching; private readonly bool enforceRequiredProperties; private readonly TypeConverterCache typeConverters; public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeInspector, bool ignoreUnmatched, bool duplicateKeyChecking, ITypeConverter typeConverter, INamingConvention enumNamingConvention, bool enforceNullability, bool caseInsensitivePropertyMatching, bool enforceRequiredProperties, IEnumerable typeConverters) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); this.typeInspector = typeInspector ?? throw new ArgumentNullException("typeInspector"); this.ignoreUnmatched = ignoreUnmatched; this.duplicateKeyChecking = duplicateKeyChecking; this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); this.enforceNullability = enforceNullability; this.caseInsensitivePropertyMatching = caseInsensitivePropertyMatching; this.enforceRequiredProperties = enforceRequiredProperties; this.typeConverters = new TypeConverterCache(typeConverters); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!parser.TryConsume(out var _)) { value = null; return false; } Type type = Nullable.GetUnderlyingType(expectedType) ?? FsharpHelper.GetOptionUnderlyingType(expectedType) ?? expectedType; value = objectFactory.Create(type); objectFactory.ExecuteOnDeserializing(value); HashSet hashSet = new HashSet(StringComparer.Ordinal); HashSet hashSet2 = new HashSet(StringComparer.Ordinal); Mark start = Mark.Empty; MappingEnd event2; while (!parser.TryConsume(out event2)) { YamlDotNet.Core.Events.Scalar propertyName = parser.Consume(); if (duplicateKeyChecking && !hashSet.Add(propertyName.Value)) { throw new YamlException(propertyName.Start, propertyName.End, "Encountered duplicate key " + propertyName.Value); } try { IPropertyDescriptor property = typeInspector.GetProperty(type, null, propertyName.Value, ignoreUnmatched, caseInsensitivePropertyMatching); if (property == null) { parser.SkipThisAndNestedEvents(); continue; } hashSet2.Add(property.Name); object obj; if (property.ConverterType != null) { IYamlTypeConverter converterByType = typeConverters.GetConverterByType(property.ConverterType); obj = converterByType.ReadYaml(parser, property.Type, rootDeserializer); } else { obj = nestedObjectDeserializer(parser, property.Type); } if (obj is IValuePromise valuePromise) { object valueRef = value; valuePromise.ValueAvailable += delegate(object? v) { object value3 = typeConverter.ChangeType(v, property.Type, enumNamingConvention, typeInspector); NullCheck(value3, property, propertyName); property.Write(valueRef, value3); }; } else { object value2 = typeConverter.ChangeType(obj, property.Type, enumNamingConvention, typeInspector); NullCheck(value2, property, propertyName); property.Write(value, value2); } } catch (SerializationException ex) { throw new YamlException(propertyName.Start, propertyName.End, ex.Message); } catch (YamlException) { throw; } catch (Exception innerException) { throw new YamlException(propertyName.Start, propertyName.End, "Exception during deserialization", innerException); } start = propertyName.End; } if (enforceRequiredProperties) { IEnumerable properties = typeInspector.GetProperties(type, value); List list = new List(); foreach (IPropertyDescriptor item in properties) { if (item.Required && !hashSet2.Contains(item.Name)) { list.Add(item.Name); } } if (list.Count > 0) { string text = string.Join(",", list); throw new YamlException(in start, in start, "Missing properties, '" + text + "' in source yaml."); } } objectFactory.ExecuteOnDeserialized(value); return true; } public void NullCheck(object value, IPropertyDescriptor property, YamlDotNet.Core.Events.Scalar propertyName) { if (enforceNullability && value == null && !property.AllowNulls) { throw new YamlException(propertyName.Start, propertyName.End, "Strict nullability enforcement error.", new NullReferenceException("Yaml value is null when target property requires non null values.")); } } } internal sealed class ScalarNodeDeserializer : INodeDeserializer { private const string BooleanTruePattern = "^(true|y|yes|on)$"; private const string BooleanFalsePattern = "^(false|n|no|off)$"; private readonly bool attemptUnknownTypeDeserialization; private readonly ITypeConverter typeConverter; private readonly ITypeInspector typeInspector; private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization, ITypeConverter typeConverter, ITypeInspector typeInspector, YamlFormatter formatter, INamingConvention enumNamingConvention) { this.attemptUnknownTypeDeserialization = attemptUnknownTypeDeserialization; this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.typeInspector = typeInspector; this.formatter = formatter; this.enumNamingConvention = enumNamingConvention; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!parser.TryConsume(out var @event)) { value = null; return false; } Type type = Nullable.GetUnderlyingType(expectedType) ?? FsharpHelper.GetOptionUnderlyingType(expectedType) ?? expectedType; if (type.IsEnum()) { string name = enumNamingConvention.Reverse(@event.Value); name = typeInspector.GetEnumName(type, name); value = Enum.Parse(type, name, ignoreCase: true); return true; } TypeCode typeCode = type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: value = DeserializeBooleanHelper(@event.Value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: value = DeserializeIntegerHelper(typeCode, @event.Value); break; case TypeCode.Single: value = float.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.Double: value = double.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.Decimal: value = decimal.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.String: value = @event.Value; break; case TypeCode.Char: value = @event.Value[0]; break; case TypeCode.DateTime: value = DateTime.Parse(@event.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); break; default: if (expectedType == typeof(object)) { if (!@event.IsKey && attemptUnknownTypeDeserialization) { value = AttemptUnknownTypeDeserialization(@event); } else { value = @event.Value; } } else { value = typeConverter.ChangeType(@event.Value, expectedType, enumNamingConvention, typeInspector); } break; } return true; } private static bool DeserializeBooleanHelper(string value) { if (Regex.IsMatch(value, "^(true|y|yes|on)$", RegexOptions.IgnoreCase)) { return true; } if (Regex.IsMatch(value, "^(false|n|no|off)$", RegexOptions.IgnoreCase)) { return false; } throw new FormatException("The value \"" + value + "\" is not a valid YAML Boolean"); } private object DeserializeIntegerHelper(TypeCode typeCode, string value) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; int i = 0; bool flag = false; ulong num = 0uL; if (value[0] == '-') { i++; flag = true; } else if (value[0] == '+') { i++; } if (value[i] == '0') { int num2; if (i == value.Length - 1) { num2 = 10; num = 0uL; } else { i++; if (value[i] == 'b') { num2 = 2; i++; } else if (value[i] == 'x') { num2 = 16; i++; } else { num2 = 8; } } for (; i < value.Length; i++) { if (value[i] != '_') { builder.Append(value[i]); } } switch (num2) { case 2: case 8: num = Convert.ToUInt64(builder.ToString(), num2); break; case 16: num = ulong.Parse(builder.ToString(), NumberStyles.HexNumber, formatter.NumberFormat); break; } } else { string[] array = value.Substring(i).Split(new char[1] { ':' }); num = 0uL; for (int j = 0; j < array.Length; j++) { num *= 60; num += ulong.Parse(array[j].Replace("_", ""), CultureInfo.InvariantCulture); } } if (!flag) { return CastInteger(num, typeCode); } long number = ((num != 9223372036854775808uL) ? checked(-(long)num) : long.MinValue); return CastInteger(number, typeCode); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private static object CastInteger(long number, TypeCode typeCode) { return checked(typeCode switch { TypeCode.Byte => (byte)number, TypeCode.Int16 => (short)number, TypeCode.Int32 => (int)number, TypeCode.Int64 => number, TypeCode.SByte => (sbyte)number, TypeCode.UInt16 => (ushort)number, TypeCode.UInt32 => (uint)number, TypeCode.UInt64 => (ulong)number, _ => number, }); } private static object CastInteger(ulong number, TypeCode typeCode) { return checked(typeCode switch { TypeCode.Byte => (byte)number, TypeCode.Int16 => (short)number, TypeCode.Int32 => (int)number, TypeCode.Int64 => (long)number, TypeCode.SByte => (sbyte)number, TypeCode.UInt16 => (ushort)number, TypeCode.UInt32 => (uint)number, TypeCode.UInt64 => number, _ => number, }); } private object? AttemptUnknownTypeDeserialization(YamlDotNet.Core.Events.Scalar value) { if (value.Style == ScalarStyle.SingleQuoted || value.Style == ScalarStyle.DoubleQuoted || value.Style == ScalarStyle.Folded) { return value.Value; } string v = value.Value; switch (v) { case "null": case "Null": case "NULL": case "~": case "": return null; case "true": case "True": case "TRUE": return true; case "False": case "FALSE": case "false": return false; default: if (Regex.IsMatch(v, "^0x[0-9a-fA-F]+$")) { v = v.Substring(2); if (byte.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result)) { return result; } if (short.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result2)) { return result2; } if (int.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result3)) { return result3; } if (long.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result4)) { return result4; } if (ulong.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result5)) { return result5; } return v; } if (Regex.IsMatch(v, "^0o[0-9a-fA-F]+$")) { if (!TryAndSwallow(() => Convert.ToByte(v, 8), out object value2) && !TryAndSwallow(() => Convert.ToInt16(v, 8), out value2) && !TryAndSwallow(() => Convert.ToInt32(v, 8), out value2) && !TryAndSwallow(() => Convert.ToInt64(v, 8), out value2) && !TryAndSwallow(() => Convert.ToUInt64(v, 8), out value2)) { return v; } return value2; } if (Regex.IsMatch(v, "^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$")) { if (byte.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result6)) { return result6; } if (short.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result7)) { return result7; } if (int.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result8)) { return result8; } if (long.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result9)) { return result9; } if (ulong.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result10)) { return result10; } if (float.TryParse(v, NumberStyles.Float, formatter.NumberFormat, out var result11)) { return result11; } if (double.TryParse(v, NumberStyles.Float, formatter.NumberFormat, out var result12)) { return result12; } return v; } if (Regex.IsMatch(v, "^[-+]?(\\.inf|\\.Inf|\\.INF)$")) { if (Polyfills.StartsWith(v, '-')) { return float.NegativeInfinity; } return float.PositiveInfinity; } if (Regex.IsMatch(v, "^(\\.nan|\\.NaN|\\.NAN)$")) { return float.NaN; } return v; } } private static bool TryAndSwallow(Func attempt, out object? value) { try { value = attempt(); return true; } catch { value = null; return false; } } } internal sealed class StaticArrayNodeDeserializer : INodeDeserializer { private sealed class ArrayList : IList, ICollection, IEnumerable { private object?[] data; public bool IsFixedSize => false; public bool IsReadOnly => false; public object? this[int index] { get { return data[index]; } set { data[index] = value; } } public int Count { get; private set; } public bool IsSynchronized => false; public object SyncRoot => data; public ArrayList() { Clear(); } public int Add(object? value) { if (Count == data.Length) { Array.Resize(ref data, data.Length * 2); } data[Count] = value; return Count++; } public void Clear() { data = new object[10]; Count = 0; } bool IList.Contains(object? value) { throw new NotSupportedException(); } int IList.IndexOf(object? value) { throw new NotSupportedException(); } void IList.Insert(int index, object? value) { throw new NotSupportedException(); } void IList.Remove(object? value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { Array.Copy(data, 0, array, index, Count); } public IEnumerator GetEnumerator() { int i = 0; while (i < Count) { yield return data[i]; int num = i + 1; i = num; } } } private readonly StaticObjectFactory factory; public StaticArrayNodeDeserializer(StaticObjectFactory factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!factory.IsArray(expectedType)) { value = false; return false; } Type valueType = factory.GetValueType(expectedType); ArrayList arrayList = new ArrayList(); StaticCollectionNodeDeserializer.DeserializeHelper(valueType, parser, nestedObjectDeserializer, arrayList, factory); Array array = factory.CreateArray(expectedType, arrayList.Count); arrayList.CopyTo(array, 0); value = array; return true; } } internal sealed class StaticCollectionNodeDeserializer : INodeDeserializer { private readonly StaticObjectFactory factory; public StaticCollectionNodeDeserializer(StaticObjectFactory factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!factory.IsList(expectedType)) { value = null; return false; } DeserializeHelper(result: (IList)(value = factory.Create(expectedType) as IList), tItem: factory.GetValueType(expectedType), parser: parser, nestedObjectDeserializer: nestedObjectDeserializer, factory: factory); return true; } internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, IObjectFactory factory) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { int index = result.Add(factory.CreatePrimitive(tItem)); valuePromise.ValueAvailable += delegate(object? v) { result[index] = v; }; } else { result.Add(obj); } } } } internal class StaticDictionaryNodeDeserializer : DictionaryDeserializer, INodeDeserializer { private readonly StaticObjectFactory objectFactory; public StaticDictionaryNodeDeserializer(StaticObjectFactory objectFactory, bool duplicateKeyChecking) : base(duplicateKeyChecking) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (objectFactory.IsDictionary(expectedType)) { if (!(objectFactory.Create(expectedType) is IDictionary dictionary)) { value = null; return false; } Type keyType = objectFactory.GetKeyType(expectedType); Type valueType = objectFactory.GetValueType(expectedType); value = dictionary; base.Deserialize(keyType, valueType, reader, nestedObjectDeserializer, dictionary, rootDeserializer); return true; } value = null; return false; } } internal sealed class TypeConverterNodeDeserializer : INodeDeserializer { private readonly TypeConverterCache converters; public TypeConverterNodeDeserializer(IEnumerable converters) { this.converters = new TypeConverterCache(converters); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!converters.TryGetConverterForType(expectedType, out IYamlTypeConverter typeConverter)) { value = null; return false; } value = typeConverter.ReadYaml(parser, expectedType, rootDeserializer); return true; } } internal sealed class YamlConvertibleNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; public YamlConvertibleNodeDeserializer(IObjectFactory objectFactory) { this.objectFactory = objectFactory; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (typeof(IYamlConvertible).IsAssignableFrom(expectedType)) { IYamlConvertible yamlConvertible = (IYamlConvertible)objectFactory.Create(expectedType); yamlConvertible.Read(parser, expectedType, (Type type) => nestedObjectDeserializer(parser, type)); value = yamlConvertible; return true; } value = null; return false; } } internal sealed class YamlSerializableNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; public YamlSerializableNodeDeserializer(IObjectFactory objectFactory) { this.objectFactory = objectFactory; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (typeof(IYamlSerializable).IsAssignableFrom(expectedType)) { IYamlSerializable yamlSerializable = (IYamlSerializable)objectFactory.Create(expectedType); yamlSerializable.ReadYaml(parser); value = yamlSerializable; return true; } value = null; return false; } } } namespace YamlDotNet.Serialization.NamingConventions { internal sealed class CamelCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new CamelCaseNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public CamelCaseNamingConvention() { } public string Apply(string value) { return value.ToCamelCase(); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class HyphenatedNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new HyphenatedNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public HyphenatedNamingConvention() { } public string Apply(string value) { return value.FromCamelCase("-"); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class LowerCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new LowerCaseNamingConvention(); private LowerCaseNamingConvention() { } public string Apply(string value) { return value.ToCamelCase().ToLower(CultureInfo.InvariantCulture); } public string Reverse(string value) { if (string.IsNullOrEmpty(value)) { return value; } return char.ToUpperInvariant(value[0]) + value.Substring(1); } } internal sealed class NullNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new NullNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public NullNamingConvention() { } public string Apply(string value) { return value; } public string Reverse(string value) { return value; } } internal sealed class PascalCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new PascalCaseNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public PascalCaseNamingConvention() { } public string Apply(string value) { return value.ToPascalCase(); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class UnderscoredNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new UnderscoredNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public UnderscoredNamingConvention() { } public string Apply(string value) { return value.FromCamelCase("_"); } public string Reverse(string value) { return value.ToPascalCase(); } } } namespace YamlDotNet.Serialization.EventEmitters { internal abstract class ChainedEventEmitter : IEventEmitter { protected readonly IEventEmitter nextEmitter; protected ChainedEventEmitter(IEventEmitter nextEmitter) { this.nextEmitter = nextEmitter ?? throw new ArgumentNullException("nextEmitter"); } public virtual void Emit(AliasEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(MappingEndEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } } internal sealed class JsonEventEmitter : ChainedEventEmitter { private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public JsonEventEmitter(IEventEmitter nextEmitter, YamlFormatter formatter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) : base(nextEmitter) { this.formatter = formatter; this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public override void Emit(AliasEventInfo eventInfo, IEmitter emitter) { eventInfo.NeedsExpansion = true; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { eventInfo.IsPlainImplicit = true; eventInfo.Style = ScalarStyle.Plain; object value = eventInfo.Source.Value; if (value == null) { eventInfo.RenderedValue = "null"; } else { TypeCode typeCode = eventInfo.Source.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: if (eventInfo.Source.Type.IsEnum()) { eventInfo.RenderedValue = formatter.FormatEnum(value, typeInspector, enumNamingConvention); eventInfo.Style = ((!formatter.PotentiallyQuoteEnums(value)) ? ScalarStyle.Plain : ScalarStyle.DoubleQuoted); } else { eventInfo.RenderedValue = formatter.FormatNumber(value); } break; case TypeCode.Single: { float f = (float)value; eventInfo.RenderedValue = f.ToString("G", CultureInfo.InvariantCulture); if (float.IsNaN(f) || float.IsInfinity(f)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } break; } case TypeCode.Double: { double d = (double)value; eventInfo.RenderedValue = d.ToString("G", CultureInfo.InvariantCulture); if (double.IsNaN(d) || double.IsInfinity(d)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } break; } case TypeCode.Decimal: eventInfo.RenderedValue = ((decimal)value).ToString(CultureInfo.InvariantCulture); break; case TypeCode.Char: case TypeCode.String: eventInfo.RenderedValue = value.ToString(); eventInfo.Style = ScalarStyle.DoubleQuoted; break; case TypeCode.DateTime: eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: eventInfo.RenderedValue = "null"; break; default: if (eventInfo.Source.Type == typeof(TimeSpan)) { eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } } base.Emit(eventInfo, emitter); } public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { eventInfo.Style = MappingStyle.Flow; base.Emit(eventInfo, emitter); } public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { eventInfo.Style = SequenceStyle.Flow; base.Emit(eventInfo, emitter); } } internal sealed class TypeAssigningEventEmitter : ChainedEventEmitter { private readonly IDictionary tagMappings; private readonly bool quoteNecessaryStrings; private readonly Regex? isSpecialStringValue_Regex; private static readonly string SpecialStrings_Pattern = "^(null|Null|NULL|\\~|true|True|TRUE|false|False|FALSE|[-+]?[0-9]+|0o[0-7]+|0x[0-9a-fA-F]+|[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?|[-+]?(\\.inf|\\.Inf|\\.INF)|\\.nan|\\.NaN|\\.NAN|\\s.*)$"; private static readonly string CombinedYaml1_1SpecialStrings_Pattern = "^(null|Null|NULL|\\~|true|True|TRUE|false|False|FALSE|y|Y|yes|Yes|YES|n|N|no|No|NO|on|On|ON|off|Off|OFF|[-+]?0b[0-1_]+|[-+]?0o?[0-7_]+|[-+]?(0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+|[-+]?([0-9][0-9_]*)?\\.[0-9_]*([eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(inf|Inf|INF)|\\.(nan|NaN|NAN))$"; private readonly ScalarStyle defaultScalarStyle; private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public TypeAssigningEventEmitter(IEventEmitter nextEmitter, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings, ScalarStyle defaultScalarStyle, YamlFormatter formatter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) : base(nextEmitter) { this.defaultScalarStyle = defaultScalarStyle; this.formatter = formatter; this.tagMappings = tagMappings; this.quoteNecessaryStrings = quoteNecessaryStrings; isSpecialStringValue_Regex = new Regex(quoteYaml1_1Strings ? CombinedYaml1_1SpecialStrings_Pattern : SpecialStrings_Pattern, RegexOptions.Compiled); this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { ScalarStyle style = ScalarStyle.Plain; object value = eventInfo.Source.Value; if (value == null) { eventInfo.Tag = JsonSchema.Tags.Null; eventInfo.RenderedValue = ""; } else { TypeCode typeCode = eventInfo.Source.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: eventInfo.Tag = JsonSchema.Tags.Bool; eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: if (eventInfo.Source.Type.IsEnum) { eventInfo.Tag = FailsafeSchema.Tags.Str; eventInfo.RenderedValue = formatter.FormatEnum(value, typeInspector, enumNamingConvention); style = ((!quoteNecessaryStrings || !IsSpecialStringValue(eventInfo.RenderedValue) || !formatter.PotentiallyQuoteEnums(value)) ? defaultScalarStyle : ScalarStyle.DoubleQuoted); } else { eventInfo.Tag = JsonSchema.Tags.Int; eventInfo.RenderedValue = formatter.FormatNumber(value); } break; case TypeCode.Single: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber((float)value); break; case TypeCode.Double: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber((double)value); break; case TypeCode.Decimal: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.Char: case TypeCode.String: eventInfo.Tag = FailsafeSchema.Tags.Str; eventInfo.RenderedValue = value.ToString(); style = ((!quoteNecessaryStrings || !IsSpecialStringValue(eventInfo.RenderedValue)) ? defaultScalarStyle : ScalarStyle.DoubleQuoted); break; case TypeCode.DateTime: eventInfo.Tag = DefaultSchema.Tags.Timestamp; eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: eventInfo.Tag = JsonSchema.Tags.Null; eventInfo.RenderedValue = ""; break; default: if (eventInfo.Source.Type == typeof(TimeSpan)) { eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } } eventInfo.IsPlainImplicit = true; if (eventInfo.Style == ScalarStyle.Any) { eventInfo.Style = style; } base.Emit(eventInfo, emitter); } public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { AssignTypeIfNeeded(eventInfo); base.Emit(eventInfo, emitter); } public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { AssignTypeIfNeeded(eventInfo); base.Emit(eventInfo, emitter); } private void AssignTypeIfNeeded(ObjectEventInfo eventInfo) { if (tagMappings.TryGetValue(eventInfo.Source.Type, out var value)) { eventInfo.Tag = value; } } private bool IsSpecialStringValue(string value) { if (value.Trim() == string.Empty) { return true; } return isSpecialStringValue_Regex?.IsMatch(value) ?? false; } } internal sealed class WriterEventEmitter : IEventEmitter { void IEventEmitter.Emit(AliasEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new YamlDotNet.Core.Events.AnchorAlias(eventInfo.Alias)); } void IEventEmitter.Emit(ScalarEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(eventInfo.Anchor, eventInfo.Tag, eventInfo.RenderedValue, eventInfo.Style, eventInfo.IsPlainImplicit, eventInfo.IsQuotedImplicit)); } void IEventEmitter.Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new MappingStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); } void IEventEmitter.Emit(MappingEndEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new MappingEnd()); } void IEventEmitter.Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new SequenceStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); } void IEventEmitter.Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new SequenceEnd()); } } } namespace YamlDotNet.Serialization.Converters { internal class DateTime8601Converter : IYamlTypeConverter { private readonly ScalarStyle scalarStyle; public DateTime8601Converter() : this(ScalarStyle.Any) { } public DateTime8601Converter(ScalarStyle scalarStyle) { this.scalarStyle = scalarStyle; } public bool Accepts(Type type) { return type == typeof(DateTime); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTime dateTime = DateTime.ParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); return dateTime; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { string value2 = ((DateTime)value).ToString("O", CultureInfo.InvariantCulture); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, scalarStyle, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class DateTimeConverter : IYamlTypeConverter { private readonly DateTimeKind kind; private readonly IFormatProvider provider; private readonly bool doubleQuotes; private readonly string[] formats; public DateTimeConverter(DateTimeKind kind = DateTimeKind.Utc, IFormatProvider? provider = null, bool doubleQuotes = false, params string[] formats) { this.kind = ((kind == DateTimeKind.Unspecified) ? DateTimeKind.Utc : kind); this.provider = provider ?? CultureInfo.InvariantCulture; this.doubleQuotes = doubleQuotes; this.formats = formats.DefaultIfEmpty("G").ToArray(); } public bool Accepts(Type type) { return type == typeof(DateTime); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTimeStyles style = ((kind == DateTimeKind.Local) ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal); DateTime dt = DateTime.ParseExact(value, formats, provider, style); dt = EnsureDateTimeKind(dt, kind); return dt; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { DateTime dateTime = (DateTime)value; string value2 = ((kind == DateTimeKind.Local) ? dateTime.ToLocalTime() : dateTime.ToUniversalTime()).ToString(formats.First(), provider); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } private static DateTime EnsureDateTimeKind(DateTime dt, DateTimeKind kind) { if (dt.Kind == DateTimeKind.Local && kind == DateTimeKind.Utc) { return dt.ToUniversalTime(); } if (dt.Kind == DateTimeKind.Utc && kind == DateTimeKind.Local) { return dt.ToLocalTime(); } return dt; } } internal class DateTimeOffsetConverter : IYamlTypeConverter { private readonly IFormatProvider provider; private readonly ScalarStyle style; private readonly DateTimeStyles dateStyle; private readonly string[] formats; public DateTimeOffsetConverter(IFormatProvider? provider = null, ScalarStyle style = ScalarStyle.Any, DateTimeStyles dateStyle = DateTimeStyles.None, params string[] formats) { this.provider = provider ?? CultureInfo.InvariantCulture; this.style = style; this.dateStyle = dateStyle; this.formats = formats.DefaultIfEmpty("O").ToArray(); } public bool Accepts(Type type) { return type == typeof(DateTimeOffset); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTimeOffset dateTimeOffset = DateTimeOffset.ParseExact(value, formats, provider, dateStyle); return dateTimeOffset; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { string value2 = ((DateTimeOffset)value).ToString(formats.First(), provider); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, style, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class GuidConverter : IYamlTypeConverter { private readonly bool jsonCompatible; public GuidConverter(bool jsonCompatible) { this.jsonCompatible = jsonCompatible; } public bool Accepts(Type type) { return type == typeof(Guid); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; return new Guid(value); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { Guid guid = (Guid)value; emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, guid.ToString("D"), jsonCompatible ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class SystemTypeConverter : IYamlTypeConverter { public bool Accepts(Type type) { return typeof(Type).IsAssignableFrom(type); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; return Type.GetType(value, throwOnError: true); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { Type type2 = (Type)value; emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, type2.AssemblyQualifiedName, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } } } namespace YamlDotNet.Serialization.Callbacks { [AttributeUsage(AttributeTargets.Method)] internal sealed class OnDeserializedAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnDeserializingAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnSerializedAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnSerializingAttribute : Attribute { } } namespace YamlDotNet.Serialization.BufferedDeserialization { internal interface ITypeDiscriminatingNodeDeserializerOptions { void AddTypeDiscriminator(ITypeDiscriminator discriminator); void AddKeyValueTypeDiscriminator(string discriminatorKey, IDictionary valueTypeMapping); void AddUniqueKeyTypeDiscriminator(IDictionary uniqueKeyTypeMapping); } internal class ParserBuffer : IParser { private readonly LinkedList buffer; private LinkedListNode? current; public ParsingEvent? Current => current?.Value; public ParserBuffer(IParser parserToBuffer, int maxDepth, int maxLength) { buffer = new LinkedList(); buffer.AddLast(parserToBuffer.Consume()); int num = 0; do { ParsingEvent parsingEvent = parserToBuffer.Consume(); num += parsingEvent.NestingIncrease; buffer.AddLast(parsingEvent); if (maxDepth > -1 && num > maxDepth) { throw new ArgumentOutOfRangeException("parserToBuffer", "Parser buffer exceeded max depth"); } if (maxLength > -1 && buffer.Count > maxLength) { throw new ArgumentOutOfRangeException("parserToBuffer", "Parser buffer exceeded max length"); } } while (num >= 0); current = buffer.First; } public bool MoveNext() { current = current?.Next; return current != null; } public void Reset() { current = buffer.First; } } internal class TypeDiscriminatingNodeDeserializer : INodeDeserializer { private readonly IList innerDeserializers; private readonly IList typeDiscriminators; private readonly int maxDepthToBuffer; private readonly int maxLengthToBuffer; public TypeDiscriminatingNodeDeserializer(IList innerDeserializers, IList typeDiscriminators, int maxDepthToBuffer, int maxLengthToBuffer) { this.innerDeserializers = innerDeserializers; this.typeDiscriminators = typeDiscriminators; this.maxDepthToBuffer = maxDepthToBuffer; this.maxLengthToBuffer = maxLengthToBuffer; } public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!reader.Accept(out var _)) { value = null; return false; } IEnumerable enumerable = typeDiscriminators.Where((ITypeDiscriminator t) => t.BaseType.IsAssignableFrom(expectedType)); if (!enumerable.Any()) { value = null; return false; } Mark start = reader.Current.Start; Type expectedType2 = expectedType; ParserBuffer parserBuffer; try { parserBuffer = new ParserBuffer(reader, maxDepthToBuffer, maxLengthToBuffer); } catch (Exception innerException) { throw new YamlException(in start, reader.Current.End, "Failed to buffer yaml node", innerException); } try { foreach (ITypeDiscriminator item in enumerable) { parserBuffer.Reset(); if (item.TryDiscriminate(parserBuffer, out Type suggestedType)) { expectedType2 = suggestedType; break; } } } catch (Exception innerException2) { throw new YamlException(in start, reader.Current.End, "Failed to discriminate type", innerException2); } parserBuffer.Reset(); foreach (INodeDeserializer innerDeserializer in innerDeserializers) { if (innerDeserializer.Deserialize(parserBuffer, expectedType2, nestedObjectDeserializer, out value, rootDeserializer)) { return true; } } value = null; return false; } } internal class TypeDiscriminatingNodeDeserializerOptions : ITypeDiscriminatingNodeDeserializerOptions { internal readonly List discriminators = new List(); public void AddTypeDiscriminator(ITypeDiscriminator discriminator) { discriminators.Add(discriminator); } public void AddKeyValueTypeDiscriminator(string discriminatorKey, IDictionary valueTypeMapping) { discriminators.Add(new KeyValueTypeDiscriminator(typeof(T), discriminatorKey, valueTypeMapping)); } public void AddUniqueKeyTypeDiscriminator(IDictionary uniqueKeyTypeMapping) { discriminators.Add(new UniqueKeyTypeDiscriminator(typeof(T), uniqueKeyTypeMapping)); } } } namespace YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators { internal interface ITypeDiscriminator { Type BaseType { get; } bool TryDiscriminate(IParser buffer, out Type? suggestedType); } internal class KeyValueTypeDiscriminator : ITypeDiscriminator { private readonly string targetKey; private readonly IDictionary typeMapping; public Type BaseType { get; private set; } public KeyValueTypeDiscriminator(Type baseType, string targetKey, IDictionary typeMapping) { foreach (KeyValuePair item in typeMapping) { if (!baseType.IsAssignableFrom(item.Value)) { throw new ArgumentOutOfRangeException("typeMapping", $"{item.Value} is not a assignable to {baseType}"); } } BaseType = baseType; this.targetKey = targetKey; this.typeMapping = typeMapping; } public bool TryDiscriminate(IParser parser, out Type? suggestedType) { if (parser.TryFindMappingEntry((YamlDotNet.Core.Events.Scalar scalar2) => targetKey == scalar2.Value, out YamlDotNet.Core.Events.Scalar _, out ParsingEvent value) && value is YamlDotNet.Core.Events.Scalar scalar && typeMapping.TryGetValue(scalar.Value, out Type value2)) { suggestedType = value2; return true; } suggestedType = null; return false; } } internal class UniqueKeyTypeDiscriminator : ITypeDiscriminator { private readonly IDictionary typeMapping; public Type BaseType { get; private set; } public UniqueKeyTypeDiscriminator(Type baseType, IDictionary typeMapping) { foreach (KeyValuePair item in typeMapping) { if (!baseType.IsAssignableFrom(item.Value)) { throw new ArgumentOutOfRangeException("typeMapping", $"{item.Value} is not a assignable to {baseType}"); } } BaseType = baseType; this.typeMapping = typeMapping; } public bool TryDiscriminate(IParser parser, out Type? suggestedType) { if (parser.TryFindMappingEntry((YamlDotNet.Core.Events.Scalar scalar) => typeMapping.ContainsKey(scalar.Value), out YamlDotNet.Core.Events.Scalar key, out ParsingEvent _)) { suggestedType = typeMapping[key.Value]; return true; } suggestedType = null; return false; } } } namespace YamlDotNet.RepresentationModel { internal class DocumentLoadingState { private readonly Dictionary anchors = new Dictionary(); private readonly List nodesWithUnresolvedAliases = new List(); public void AddAnchor(YamlNode node) { if (node.Anchor.IsEmpty) { throw new ArgumentException("The specified node does not have an anchor"); } anchors[node.Anchor] = node; } public YamlNode GetNode(AnchorName anchor, Mark start, Mark end) { if (anchors.TryGetValue(anchor, out YamlNode value)) { return value; } throw new AnchorNotFoundException(in start, in end, $"The anchor '{anchor}' does not exists"); } public bool TryGetNode(AnchorName anchor, [NotNullWhen(true)] out YamlNode? node) { return anchors.TryGetValue(anchor, out node); } public void AddNodeWithUnresolvedAliases(YamlNode node) { nodesWithUnresolvedAliases.Add(node); } public void ResolveAliases() { foreach (YamlNode nodesWithUnresolvedAlias in nodesWithUnresolvedAliases) { nodesWithUnresolvedAlias.ResolveAliases(this); } } } internal class EmitterState { public HashSet EmittedAnchors { get; } = new HashSet(); } internal interface IYamlVisitor { void Visit(YamlStream stream); void Visit(YamlDocument document); void Visit(YamlScalarNode scalar); void Visit(YamlSequenceNode sequence); void Visit(YamlMappingNode mapping); } internal class LibYamlEventStream { private readonly IParser parser; public LibYamlEventStream(IParser iParser) { parser = iParser ?? throw new ArgumentNullException("iParser"); } public void WriteTo(TextWriter textWriter) { while (parser.MoveNext()) { ParsingEvent current = parser.Current; if (!(current is YamlDotNet.Core.Events.AnchorAlias anchorAlias)) { if (!(current is YamlDotNet.Core.Events.DocumentEnd documentEnd)) { if (!(current is YamlDotNet.Core.Events.DocumentStart documentStart)) { if (!(current is MappingEnd)) { if (!(current is MappingStart nodeEvent)) { if (!(current is YamlDotNet.Core.Events.Scalar scalar)) { if (!(current is SequenceEnd)) { if (!(current is SequenceStart nodeEvent2)) { if (!(current is YamlDotNet.Core.Events.StreamEnd)) { if (current is YamlDotNet.Core.Events.StreamStart) { textWriter.Write("+STR"); } } else { textWriter.Write("-STR"); } } else { textWriter.Write("+SEQ"); WriteAnchorAndTag(textWriter, nodeEvent2); } } else { textWriter.Write("-SEQ"); } } else { textWriter.Write("=VAL"); WriteAnchorAndTag(textWriter, scalar); switch (scalar.Style) { case ScalarStyle.DoubleQuoted: textWriter.Write(" \""); break; case ScalarStyle.SingleQuoted: textWriter.Write(" '"); break; case ScalarStyle.Folded: textWriter.Write(" >"); break; case ScalarStyle.Literal: textWriter.Write(" |"); break; default: textWriter.Write(" :"); break; } string value = scalar.Value; foreach (char c in value) { switch (c) { case '\b': textWriter.Write("\\b"); break; case '\t': textWriter.Write("\\t"); break; case '\n': textWriter.Write("\\n"); break; case '\r': textWriter.Write("\\r"); break; case '\\': textWriter.Write("\\\\"); break; default: textWriter.Write(c); break; } } } } else { textWriter.Write("+MAP"); WriteAnchorAndTag(textWriter, nodeEvent); } } else { textWriter.Write("-MAP"); } } else { textWriter.Write("+DOC"); if (!documentStart.IsImplicit) { textWriter.Write(" ---"); } } } else { textWriter.Write("-DOC"); if (!documentEnd.IsImplicit) { textWriter.Write(" ..."); } } } else { textWriter.Write("=ALI *"); textWriter.Write(anchorAlias.Value); } textWriter.WriteLine(); } } private static void WriteAnchorAndTag(TextWriter textWriter, NodeEvent nodeEvent) { if (!nodeEvent.Anchor.IsEmpty) { textWriter.Write(" &"); textWriter.Write(nodeEvent.Anchor); } if (!nodeEvent.Tag.IsEmpty) { textWriter.Write(" <"); textWriter.Write(nodeEvent.Tag.Value); textWriter.Write(">"); } } } internal class YamlAliasNode : YamlNode { public override YamlNodeType NodeType => YamlNodeType.Alias; internal YamlAliasNode(AnchorName anchor) { base.Anchor = anchor; } internal override void ResolveAliases(DocumentLoadingState state) { throw new NotSupportedException("Resolving an alias on an alias node does not make sense"); } internal override void Emit(IEmitter emitter, EmitterState state) { throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be saved."); } public override void Accept(IYamlVisitor visitor) { throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be visited."); } public override bool Equals(object? obj) { if (obj is YamlAliasNode yamlAliasNode && Equals(yamlAliasNode)) { return object.Equals(base.Anchor, yamlAliasNode.Anchor); } return false; } public override int GetHashCode() { return base.GetHashCode(); } internal override string ToString(RecursionLevel level) { return "*" + base.Anchor; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { yield return this; } } internal class YamlDocument { private class AnchorAssigningVisitor : YamlVisitorBase { private readonly HashSet existingAnchors = new HashSet(); private readonly Dictionary visitedNodes = new Dictionary(); public void AssignAnchors(YamlDocument document) { existingAnchors.Clear(); visitedNodes.Clear(); document.Accept(this); Random random = new Random(); foreach (KeyValuePair visitedNode in visitedNodes) { if (!visitedNode.Value) { continue; } AnchorName anchorName; if (!visitedNode.Key.Anchor.IsEmpty && !existingAnchors.Contains(visitedNode.Key.Anchor)) { anchorName = visitedNode.Key.Anchor; } else { do { anchorName = new AnchorName(random.Next().ToString(CultureInfo.InvariantCulture)); } while (existingAnchors.Contains(anchorName)); } existingAnchors.Add(anchorName); visitedNode.Key.Anchor = anchorName; } } private bool VisitNodeAndFindDuplicates(YamlNode node) { if (visitedNodes.TryGetValue(node, out var value)) { if (!value) { visitedNodes[node] = true; } return !value; } visitedNodes.Add(node, value: false); return false; } public override void Visit(YamlScalarNode scalar) { VisitNodeAndFindDuplicates(scalar); } public override void Visit(YamlMappingNode mapping) { if (!VisitNodeAndFindDuplicates(mapping)) { base.Visit(mapping); } } public override void Visit(YamlSequenceNode sequence) { if (!VisitNodeAndFindDuplicates(sequence)) { base.Visit(sequence); } } } public YamlNode RootNode { get; private set; } public IEnumerable AllNodes => RootNode.AllNodes; public YamlDocument(YamlNode rootNode) { RootNode = rootNode; } public YamlDocument(string rootNode) { RootNode = new YamlScalarNode(rootNode); } internal YamlDocument(IParser parser) { DocumentLoadingState documentLoadingState = new DocumentLoadingState(); parser.Consume(); YamlDotNet.Core.Events.DocumentEnd @event; while (!parser.TryConsume(out @event)) { RootNode = YamlNode.ParseNode(parser, documentLoadingState); if (RootNode is YamlAliasNode) { throw new YamlException("A document cannot contain only an alias"); } } documentLoadingState.ResolveAliases(); if (RootNode == null) { throw new ArgumentException("Atempted to parse an empty document"); } } private void AssignAnchors() { AnchorAssigningVisitor anchorAssigningVisitor = new AnchorAssigningVisitor(); anchorAssigningVisitor.AssignAnchors(this); } internal void Save(IEmitter emitter, bool assignAnchors = true) { if (assignAnchors) { AssignAnchors(); } emitter.Emit(new YamlDotNet.Core.Events.DocumentStart()); RootNode.Save(emitter, new EmitterState()); emitter.Emit(new YamlDotNet.Core.Events.DocumentEnd(isImplicit: false)); } public void Accept(IYamlVisitor visitor) { visitor.Visit(this); } } internal sealed class YamlMappingNode : YamlNode, IEnumerable>, IEnumerable, IYamlConvertible { private readonly OrderedDictionary children = new OrderedDictionary(); public IOrderedDictionary Children => children; public MappingStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Mapping; internal YamlMappingNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } private void Load(IParser parser, DocumentLoadingState state) { MappingStart mappingStart = parser.Consume(); Load(mappingStart, state); Style = mappingStart.Style; bool flag = false; MappingEnd @event; while (!parser.TryConsume(out @event)) { YamlNode yamlNode = YamlNode.ParseNode(parser, state); YamlNode yamlNode2 = YamlNode.ParseNode(parser, state); if (!children.TryAdd(yamlNode, yamlNode2)) { throw new YamlException(yamlNode.Start, yamlNode.End, $"Duplicate key {yamlNode}"); } flag = flag || yamlNode is YamlAliasNode || yamlNode2 is YamlAliasNode; } if (flag) { state.AddNodeWithUnresolvedAliases(this); } } public YamlMappingNode() { } public YamlMappingNode(params KeyValuePair[] children) : this((IEnumerable>)children) { } public YamlMappingNode(IEnumerable> children) { foreach (KeyValuePair child in children) { this.children.Add(child); } } public YamlMappingNode(params YamlNode[] children) : this((IEnumerable)children) { } public YamlMappingNode(IEnumerable children) { using IEnumerator enumerator = children.GetEnumerator(); while (enumerator.MoveNext()) { YamlNode current = enumerator.Current; if (!enumerator.MoveNext()) { throw new ArgumentException("When constructing a mapping node with a sequence, the number of elements of the sequence must be even."); } Add(current, enumerator.Current); } } public void Add(YamlNode key, YamlNode value) { children.Add(key, value); } public void Add(string key, YamlNode value) { children.Add(new YamlScalarNode(key), value); } public void Add(YamlNode key, string value) { children.Add(key, new YamlScalarNode(value)); } public void Add(string key, string value) { children.Add(new YamlScalarNode(key), new YamlScalarNode(value)); } internal override void ResolveAliases(DocumentLoadingState state) { Dictionary dictionary = null; Dictionary dictionary2 = null; foreach (KeyValuePair child in children) { if (child.Key is YamlAliasNode) { if (dictionary == null) { dictionary = new Dictionary(); } dictionary.Add(child.Key, state.GetNode(child.Key.Anchor, child.Key.Start, child.Key.End)); } if (child.Value is YamlAliasNode) { if (dictionary2 == null) { dictionary2 = new Dictionary(); } dictionary2.Add(child.Key, state.GetNode(child.Value.Anchor, child.Value.Start, child.Value.End)); } } if (dictionary2 != null) { foreach (KeyValuePair item in dictionary2) { children[item.Key] = item.Value; } } if (dictionary == null) { return; } foreach (KeyValuePair item2 in dictionary) { YamlNode value = children[item2.Key]; children.Remove(item2.Key); children.Add(item2.Value, value); } } internal override void Emit(IEmitter emitter, EmitterState state) { emitter.Emit(new MappingStart(base.Anchor, base.Tag, isImplicit: true, Style)); foreach (KeyValuePair child in children) { child.Key.Save(emitter, state); child.Value.Save(emitter, state); } emitter.Emit(new MappingEnd()); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (!(obj is YamlMappingNode yamlMappingNode) || !object.Equals(base.Tag, yamlMappingNode.Tag) || children.Count != yamlMappingNode.children.Count) { return false; } foreach (KeyValuePair child in children) { if (!yamlMappingNode.children.TryGetValue(child.Key, out YamlNode value) || !object.Equals(child.Value, value)) { return false; } } return true; } public override int GetHashCode() { int num = base.GetHashCode(); foreach (KeyValuePair child in children) { num = YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Key); num = (child.Value.Anchor.IsEmpty ? YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Value) : YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Value.Anchor)); } return num; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { level.Increment(); yield return this; foreach (KeyValuePair child in children) { foreach (YamlNode item in child.Key.SafeAllNodes(level)) { yield return item; } foreach (YamlNode item2 in child.Value.SafeAllNodes(level)) { yield return item2; } } level.Decrement(); } internal override string ToString(RecursionLevel level) { if (!level.TryIncrement()) { return "WARNING! INFINITE RECURSION!"; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append("{ "); foreach (KeyValuePair child in children) { if (builder.Length > 2) { builder.Append(", "); } builder.Append("{ ").Append(child.Key.ToString(level)).Append(", ") .Append(child.Value.ToString(level)) .Append(" }"); } builder.Append(" }"); level.Decrement(); return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } public IEnumerator> GetEnumerator() { return children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } public static YamlMappingNode FromObject(object mapping) { if (mapping == null) { throw new ArgumentNullException("mapping"); } YamlMappingNode yamlMappingNode = new YamlMappingNode(); foreach (PropertyInfo publicProperty in mapping.GetType().GetPublicProperties()) { if (publicProperty.CanRead && publicProperty.GetGetMethod(nonPublic: false).GetParameters().Length == 0) { object value = publicProperty.GetValue(mapping, null); YamlNode yamlNode = value as YamlNode; if (yamlNode == null) { string text = Convert.ToString(value, CultureInfo.InvariantCulture); yamlNode = text ?? string.Empty; } yamlMappingNode.Add(publicProperty.Name, yamlNode); } } return yamlMappingNode; } } internal abstract class YamlNode { private const int MaximumRecursionLevel = 1000; internal const string MaximumRecursionLevelReachedToStringValue = "WARNING! INFINITE RECURSION!"; public AnchorName Anchor { get; set; } public TagName Tag { get; set; } public Mark Start { get; private set; } = Mark.Empty; public Mark End { get; private set; } = Mark.Empty; public IEnumerable AllNodes { get { RecursionLevel level = new RecursionLevel(1000); return SafeAllNodes(level); } } public abstract YamlNodeType NodeType { get; } public YamlNode this[int index] { get { if (!(this is YamlSequenceNode yamlSequenceNode)) { throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {index}. Only Sequences can be indexed by number."); } return yamlSequenceNode.Children[index]; } } public YamlNode this[YamlNode key] { get { if (!(this is YamlMappingNode yamlMappingNode)) { throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {key}. Only Mappings can be indexed by key."); } return yamlMappingNode.Children[key]; } } internal void Load(NodeEvent yamlEvent, DocumentLoadingState state) { Tag = yamlEvent.Tag; if (!yamlEvent.Anchor.IsEmpty) { Anchor = yamlEvent.Anchor; state.AddAnchor(this); } Start = yamlEvent.Start; End = yamlEvent.End; } internal static YamlNode ParseNode(IParser parser, DocumentLoadingState state) { if (parser.Accept(out var _)) { return new YamlScalarNode(parser, state); } if (parser.Accept(out var _)) { return new YamlSequenceNode(parser, state); } if (parser.Accept(out var _)) { return new YamlMappingNode(parser, state); } if (parser.TryConsume(out var event4)) { if (!state.TryGetNode(event4.Value, out YamlNode node)) { return new YamlAliasNode(event4.Value); } return node; } throw new ArgumentException("The current event is of an unsupported type.", "parser"); } internal abstract void ResolveAliases(DocumentLoadingState state); internal void Save(IEmitter emitter, EmitterState state) { if (!Anchor.IsEmpty && !state.EmittedAnchors.Add(Anchor)) { emitter.Emit(new YamlDotNet.Core.Events.AnchorAlias(Anchor)); } else { Emit(emitter, state); } } internal abstract void Emit(IEmitter emitter, EmitterState state); public abstract void Accept(IYamlVisitor visitor); public override string ToString() { RecursionLevel recursionLevel = new RecursionLevel(1000); return ToString(recursionLevel); } internal abstract string ToString(RecursionLevel level); internal abstract IEnumerable SafeAllNodes(RecursionLevel level); public static implicit operator YamlNode(string value) { return new YamlScalarNode(value); } public static implicit operator YamlNode(string[] sequence) { return new YamlSequenceNode(((IEnumerable)sequence).Select((Func)((string i) => i))); } public static explicit operator string?(YamlNode node) { if (!(node is YamlScalarNode yamlScalarNode)) { throw new ArgumentException($"Attempted to convert a '{node.NodeType}' to string. This conversion is valid only for Scalars."); } return yamlScalarNode.Value; } } internal sealed class YamlNodeIdentityEqualityComparer : IEqualityComparer { public bool Equals([AllowNull] YamlNode x, [AllowNull] YamlNode y) { return x == y; } public int GetHashCode(YamlNode obj) { return obj.GetHashCode(); } } internal enum YamlNodeType { Alias, Mapping, Scalar, Sequence } [DebuggerDisplay("{Value}")] internal sealed class YamlScalarNode : YamlNode, IYamlConvertible { private bool forceImplicitPlain; private string? value; public string? Value { get { return value; } set { if (value == null) { forceImplicitPlain = true; } else { forceImplicitPlain = false; } this.value = value; } } public ScalarStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Scalar; internal YamlScalarNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Load(IParser parser, DocumentLoadingState state) { YamlDotNet.Core.Events.Scalar scalar = parser.Consume(); Load(scalar, state); string text = scalar.Value; if (scalar.Style == ScalarStyle.Plain && base.Tag.IsEmpty) { forceImplicitPlain = text.Length switch { 0 => true, 1 => text == "~", 4 => text == "null" || text == "Null" || text == "NULL", _ => false, }; } value = text; Style = scalar.Style; } public YamlScalarNode() { } public YamlScalarNode(string? value) { Value = value; } internal override void ResolveAliases(DocumentLoadingState state) { throw new NotSupportedException("Resolving an alias on a scalar node does not make sense"); } internal override void Emit(IEmitter emitter, EmitterState state) { TagName tag = base.Tag; bool isPlainImplicit = tag.IsEmpty; if (forceImplicitPlain && Style == ScalarStyle.Plain && (Value == null || Value == "")) { tag = JsonSchema.Tags.Null; isPlainImplicit = true; } else if (tag.IsEmpty && Value == null && (Style == ScalarStyle.Plain || Style == ScalarStyle.Any)) { tag = JsonSchema.Tags.Null; isPlainImplicit = true; } emitter.Emit(new YamlDotNet.Core.Events.Scalar(base.Anchor, tag, Value ?? string.Empty, Style, isPlainImplicit, isQuotedImplicit: false)); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (obj is YamlScalarNode yamlScalarNode && object.Equals(base.Tag, yamlScalarNode.Tag)) { return object.Equals(Value, yamlScalarNode.Value); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(base.Tag.GetHashCode(), Value); } public static explicit operator string?(YamlScalarNode value) { return value.Value; } internal override string ToString(RecursionLevel level) { return Value ?? string.Empty; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { yield return this; } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } } [DebuggerDisplay("Count = {children.Count}")] internal sealed class YamlSequenceNode : YamlNode, IEnumerable, IEnumerable, IYamlConvertible { private readonly List children = new List(); public IList Children => children; public SequenceStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Sequence; internal YamlSequenceNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } private void Load(IParser parser, DocumentLoadingState state) { SequenceStart sequenceStart = parser.Consume(); Load(sequenceStart, state); Style = sequenceStart.Style; bool flag = false; SequenceEnd @event; while (!parser.TryConsume(out @event)) { YamlNode yamlNode = YamlNode.ParseNode(parser, state); children.Add(yamlNode); flag = flag || yamlNode is YamlAliasNode; } if (flag) { state.AddNodeWithUnresolvedAliases(this); } } public YamlSequenceNode() { } public YamlSequenceNode(params YamlNode[] children) : this((IEnumerable)children) { } public YamlSequenceNode(IEnumerable children) { foreach (YamlNode child in children) { this.children.Add(child); } } public void Add(YamlNode child) { children.Add(child); } public void Add(string child) { children.Add(new YamlScalarNode(child)); } internal override void ResolveAliases(DocumentLoadingState state) { for (int i = 0; i < children.Count; i++) { if (children[i] is YamlAliasNode) { children[i] = state.GetNode(children[i].Anchor, children[i].Start, children[i].End); } } } internal override void Emit(IEmitter emitter, EmitterState state) { emitter.Emit(new SequenceStart(base.Anchor, base.Tag, base.Tag.IsEmpty, Style)); foreach (YamlNode child in children) { child.Save(emitter, state); } emitter.Emit(new SequenceEnd()); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (!(obj is YamlSequenceNode yamlSequenceNode) || !object.Equals(base.Tag, yamlSequenceNode.Tag) || children.Count != yamlSequenceNode.children.Count) { return false; } for (int i = 0; i < children.Count; i++) { if (!object.Equals(children[i], yamlSequenceNode.children[i])) { return false; } } return true; } public override int GetHashCode() { int h = 0; foreach (YamlNode child in children) { h = YamlDotNet.Core.HashCode.CombineHashCodes(h, child); } return YamlDotNet.Core.HashCode.CombineHashCodes(h, base.Tag); } internal override IEnumerable SafeAllNodes(RecursionLevel level) { level.Increment(); yield return this; foreach (YamlNode child in children) { foreach (YamlNode item in child.SafeAllNodes(level)) { yield return item; } } level.Decrement(); } internal override string ToString(RecursionLevel level) { if (!level.TryIncrement()) { return "WARNING! INFINITE RECURSION!"; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append("[ "); foreach (YamlNode child in children) { if (builder.Length > 2) { builder.Append(", "); } builder.Append(child.ToString(level)); } builder.Append(" ]"); level.Decrement(); return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } public IEnumerator GetEnumerator() { return Children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } } internal class YamlStream : IEnumerable, IEnumerable { private readonly List documents = new List(); public IList Documents => documents; public YamlStream() { } public YamlStream(params YamlDocument[] documents) : this((IEnumerable)documents) { } public YamlStream(IEnumerable documents) { foreach (YamlDocument document in documents) { this.documents.Add(document); } } public void Add(YamlDocument document) { documents.Add(document); } public void Load(TextReader input) { Load(new Parser(input)); } public void Load(IParser parser) { documents.Clear(); parser.Consume(); YamlDotNet.Core.Events.StreamEnd @event; while (!parser.TryConsume(out @event)) { YamlDocument item = new YamlDocument(parser); documents.Add(item); } } public void Save(TextWriter output) { Save(output, assignAnchors: true); } public void Save(TextWriter output, bool assignAnchors) { Save(new Emitter(output), assignAnchors); } public void Save(IEmitter emitter, bool assignAnchors) { emitter.Emit(new YamlDotNet.Core.Events.StreamStart()); foreach (YamlDocument document in documents) { document.Save(emitter, assignAnchors); } emitter.Emit(new YamlDotNet.Core.Events.StreamEnd()); } public void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public IEnumerator GetEnumerator() { return documents.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } [Obsolete("Use YamlVisitorBase")] internal abstract class YamlVisitor : IYamlVisitor { protected virtual void Visit(YamlStream stream) { } protected virtual void Visited(YamlStream stream) { } protected virtual void Visit(YamlDocument document) { } protected virtual void Visited(YamlDocument document) { } protected virtual void Visit(YamlScalarNode scalar) { } protected virtual void Visited(YamlScalarNode scalar) { } protected virtual void Visit(YamlSequenceNode sequence) { } protected virtual void Visited(YamlSequenceNode sequence) { } protected virtual void Visit(YamlMappingNode mapping) { } protected virtual void Visited(YamlMappingNode mapping) { } protected virtual void VisitChildren(YamlStream stream) { foreach (YamlDocument document in stream.Documents) { document.Accept(this); } } protected virtual void VisitChildren(YamlDocument document) { if (document.RootNode != null) { document.RootNode.Accept(this); } } protected virtual void VisitChildren(YamlSequenceNode sequence) { foreach (YamlNode child in sequence.Children) { child.Accept(this); } } protected virtual void VisitChildren(YamlMappingNode mapping) { foreach (KeyValuePair child in mapping.Children) { child.Key.Accept(this); child.Value.Accept(this); } } void IYamlVisitor.Visit(YamlStream stream) { Visit(stream); VisitChildren(stream); Visited(stream); } void IYamlVisitor.Visit(YamlDocument document) { Visit(document); VisitChildren(document); Visited(document); } void IYamlVisitor.Visit(YamlScalarNode scalar) { Visit(scalar); Visited(scalar); } void IYamlVisitor.Visit(YamlSequenceNode sequence) { Visit(sequence); VisitChildren(sequence); Visited(sequence); } void IYamlVisitor.Visit(YamlMappingNode mapping) { Visit(mapping); VisitChildren(mapping); Visited(mapping); } } internal abstract class YamlVisitorBase : IYamlVisitor { public virtual void Visit(YamlStream stream) { VisitChildren(stream); } public virtual void Visit(YamlDocument document) { VisitChildren(document); } public virtual void Visit(YamlScalarNode scalar) { } public virtual void Visit(YamlSequenceNode sequence) { VisitChildren(sequence); } public virtual void Visit(YamlMappingNode mapping) { VisitChildren(mapping); } protected virtual void VisitPair(YamlNode key, YamlNode value) { key.Accept(this); value.Accept(this); } protected virtual void VisitChildren(YamlStream stream) { foreach (YamlDocument document in stream.Documents) { document.Accept(this); } } protected virtual void VisitChildren(YamlDocument document) { if (document.RootNode != null) { document.RootNode.Accept(this); } } protected virtual void VisitChildren(YamlSequenceNode sequence) { foreach (YamlNode child in sequence.Children) { child.Accept(this); } } protected virtual void VisitChildren(YamlMappingNode mapping) { foreach (KeyValuePair child in mapping.Children) { VisitPair(child.Key, child.Value); } } } } namespace YamlDotNet.Helpers { internal class DefaultFsharpHelper : IFsharpHelper { private static bool IsFsharpCore(Type t) { return t.Namespace == "Microsoft.FSharp.Core"; } public bool IsOptionType(Type t) { if (IsFsharpCore(t)) { return t.Name == "FSharpOption`1"; } return false; } public Type? GetOptionUnderlyingType(Type t) { if (!t.IsGenericType || !IsOptionType(t)) { return null; } return t.GenericTypeArguments[0]; } public object? GetValue(IObjectDescriptor objectDescriptor) { if (!IsOptionType(objectDescriptor.Type)) { throw new InvalidOperationException("Should not be called on non-Option<> type"); } if (objectDescriptor.Value == null) { return null; } return objectDescriptor.Type.GetProperty("Value").GetValue(objectDescriptor.Value); } public bool IsFsharpListType(Type t) { if (t.Namespace == "Microsoft.FSharp.Collections") { return t.Name == "FSharpList`1"; } return false; } public object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { if (!IsFsharpListType(t)) { return null; } return t.Assembly.GetType("Microsoft.FSharp.Collections.ListModule").GetMethod("OfArray").MakeGenericMethod(itemsType) .Invoke(null, new object[1] { arr }); } } internal static class DictionaryExtensions { public static bool TryAdd(this Dictionary dictionary, T key, V value) { if (dictionary.ContainsKey(key)) { return false; } dictionary.Add(key, value); return true; } public static TValue GetOrAdd(this ConcurrentDictionary dictionary, TKey key, Func valueFactory, TArg arg) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } if (key == null) { throw new ArgumentNullException("key"); } if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } TValue value; do { if (dictionary.TryGetValue(key, out value)) { return value; } value = valueFactory(key, arg); } while (!dictionary.TryAdd(key, value)); return value; } } internal static class ExpressionExtensions { public static PropertyInfo AsProperty(this LambdaExpression propertyAccessor) { PropertyInfo propertyInfo = TryGetMemberExpression(propertyAccessor); if (propertyInfo == null) { throw new ArgumentException("Expected a lambda expression in the form: x => x.SomeProperty", "propertyAccessor"); } return propertyInfo; } [return: MaybeNull] private static TMemberInfo TryGetMemberExpression(LambdaExpression lambdaExpression) where TMemberInfo : MemberInfo { if (lambdaExpression.Parameters.Count != 1) { return null; } Expression expression = lambdaExpression.Body; if (expression is UnaryExpression unaryExpression) { if (unaryExpression.NodeType != ExpressionType.Convert) { return null; } expression = unaryExpression.Operand; } if (expression is MemberExpression memberExpression) { if (memberExpression.Expression != lambdaExpression.Parameters[0]) { return null; } return memberExpression.Member as TMemberInfo; } return null; } } internal static class FsharpHelper { public static IFsharpHelper? Instance { get; set; } public static bool IsOptionType(Type t) { return Instance?.IsOptionType(t) ?? false; } public static Type? GetOptionUnderlyingType(Type t) { return Instance?.GetOptionUnderlyingType(t); } public static object? GetValue(IObjectDescriptor objectDescriptor) { return Instance?.GetValue(objectDescriptor); } public static bool IsFsharpListType(Type t) { return Instance?.IsFsharpListType(t) ?? false; } public static object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { return Instance?.CreateFsharpListFromArray(t, itemsType, arr); } } internal sealed class GenericCollectionToNonGenericAdapter : IList, ICollection, IEnumerable { private readonly ICollection genericCollection; public bool IsFixedSize { get { throw new NotSupportedException(); } } public bool IsReadOnly { get { throw new NotSupportedException(); } } public object? this[int index] { get { throw new NotSupportedException(); } set { ((IList)genericCollection)[index] = (T)value; } } public int Count { get { throw new NotSupportedException(); } } public bool IsSynchronized { get { throw new NotSupportedException(); } } public object SyncRoot { get { throw new NotSupportedException(); } } public GenericCollectionToNonGenericAdapter(ICollection genericCollection) { this.genericCollection = genericCollection ?? throw new ArgumentNullException("genericCollection"); } public int Add(object? value) { int count = genericCollection.Count; genericCollection.Add((T)value); return count; } public void Clear() { genericCollection.Clear(); } public bool Contains(object? value) { throw new NotSupportedException(); } public int IndexOf(object? value) { throw new NotSupportedException(); } public void Insert(int index, object? value) { throw new NotSupportedException(); } public void Remove(object? value) { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } public IEnumerator GetEnumerator() { return genericCollection.GetEnumerator(); } } internal sealed class GenericDictionaryToNonGenericAdapter : IDictionary, ICollection, IEnumerable where TKey : notnull { private class DictionaryEnumerator : IDictionaryEnumerator, IEnumerator { private readonly IEnumerator> enumerator; public DictionaryEntry Entry => new DictionaryEntry(Key, Value); public object Key => enumerator.Current.Key; public object? Value => enumerator.Current.Value; public object Current => Entry; public DictionaryEnumerator(IEnumerator> enumerator) { this.enumerator = enumerator; } public bool MoveNext() { return enumerator.MoveNext(); } public void Reset() { enumerator.Reset(); } } private readonly IDictionary genericDictionary; public bool IsFixedSize { get { throw new NotSupportedException(); } } public bool IsReadOnly { get { throw new NotSupportedException(); } } public ICollection Keys { get { throw new NotSupportedException(); } } public ICollection Values { get { throw new NotSupportedException(); } } public object? this[object key] { get { throw new NotSupportedException(); } set { genericDictionary[(TKey)key] = (TValue)value; } } public int Count { get { throw new NotSupportedException(); } } public bool IsSynchronized { get { throw new NotSupportedException(); } } public object SyncRoot { get { throw new NotSupportedException(); } } public GenericDictionaryToNonGenericAdapter(IDictionary genericDictionary) { this.genericDictionary = genericDictionary ?? throw new ArgumentNullException("genericDictionary"); } public void Add(object key, object? value) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(object key) { throw new NotSupportedException(); } public IDictionaryEnumerator GetEnumerator() { return new DictionaryEnumerator(genericDictionary.GetEnumerator()); } public void Remove(object key) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal interface IFsharpHelper { bool IsOptionType(Type t); Type? GetOptionUnderlyingType(Type t); object? GetValue(IObjectDescriptor objectDescriptor); bool IsFsharpListType(Type t); object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr); } internal interface IOrderedDictionary : IDictionary, ICollection>, IEnumerable>, IEnumerable where TKey : notnull { KeyValuePair this[int index] { get; set; } void Insert(int index, TKey key, TValue value); void RemoveAt(int index); } internal class NullFsharpHelper : IFsharpHelper { public object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { return null; } public Type? GetOptionUnderlyingType(Type t) { return null; } public object? GetValue(IObjectDescriptor objectDescriptor) { return null; } public bool IsFsharpListType(Type t) { return false; } public bool IsOptionType(Type t) { return false; } } internal static class NumberExtensions { public static bool IsPowerOfTwo(this int value) { return (value & (value - 1)) == 0; } } [Serializable] internal sealed class OrderedDictionary : IOrderedDictionary, IDictionary, ICollection>, IEnumerable>, IEnumerable where TKey : notnull { private class KeyCollection : ICollection, IEnumerable, IEnumerable { private readonly OrderedDictionary orderedDictionary; public int Count => orderedDictionary.list.Count; public bool IsReadOnly => true; public void Add(TKey item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TKey item) { return orderedDictionary.dictionary.ContainsKey(item); } public KeyCollection(OrderedDictionary orderedDictionary) { this.orderedDictionary = orderedDictionary; } public void CopyTo(TKey[] array, int arrayIndex) { for (int i = 0; i < orderedDictionary.list.Count; i++) { array[i] = orderedDictionary.list[i + arrayIndex].Key; } } public IEnumerator GetEnumerator() { return orderedDictionary.list.Select((KeyValuePair kvp) => kvp.Key).GetEnumerator(); } public bool Remove(TKey item) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private class ValueCollection : ICollection, IEnumerable, IEnumerable { private readonly OrderedDictionary orderedDictionary; public int Count => orderedDictionary.list.Count; public bool IsReadOnly => true; public void Add(TValue item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TValue item) { return orderedDictionary.dictionary.ContainsValue(item); } public ValueCollection(OrderedDictionary orderedDictionary) { this.orderedDictionary = orderedDictionary; } public void CopyTo(TValue[] array, int arrayIndex) { for (int i = 0; i < orderedDictionary.list.Count; i++) { array[i] = orderedDictionary.list[i + arrayIndex].Value; } } public IEnumerator GetEnumerator() { return orderedDictionary.list.Select((KeyValuePair kvp) => kvp.Value).GetEnumerator(); } public bool Remove(TValue item) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } [NonSerialized] private Dictionary dictionary; private readonly List> list; private readonly IEqualityComparer comparer; public TValue this[TKey key] { get { return dictionary[key]; } set { if (dictionary.ContainsKey(key)) { int index = list.FindIndex((KeyValuePair kvp) => comparer.Equals(kvp.Key, key)); dictionary[key] = value; list[index] = new KeyValuePair(key, value); } else { Add(key, value); } } } public ICollection Keys => new KeyCollection(this); public ICollection Values => new ValueCollection(this); public int Count => dictionary.Count; public bool IsReadOnly => false; public KeyValuePair this[int index] { get { return list[index]; } set { list[index] = value; } } public OrderedDictionary() : this((IEqualityComparer)EqualityComparer.Default) { } public OrderedDictionary(IEqualityComparer comparer) { list = new List>(); dictionary = new Dictionary(comparer); this.comparer = comparer; } public void Add(KeyValuePair item) { if (!TryAdd(item)) { ThrowDuplicateKeyException(item.Key); } } public void Add(TKey key, TValue value) { if (!TryAdd(key, value)) { ThrowDuplicateKeyException(key); } } private static void ThrowDuplicateKeyException(TKey key) { throw new ArgumentException($"An item with the same key {key} has already been added."); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAdd(TKey key, TValue value) { if (DictionaryExtensions.TryAdd(dictionary, key, value)) { list.Add(new KeyValuePair(key, value)); return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAdd(KeyValuePair item) { if (DictionaryExtensions.TryAdd(dictionary, item.Key, item.Value)) { list.Add(item); return true; } return false; } public void Clear() { dictionary.Clear(); list.Clear(); } public bool Contains(KeyValuePair item) { return dictionary.Contains(item); } public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } public IEnumerator> GetEnumerator() { return list.GetEnumerator(); } public void Insert(int index, TKey key, TValue value) { dictionary.Add(key, value); list.Insert(index, new KeyValuePair(key, value)); } public bool Remove(TKey key) { if (dictionary.ContainsKey(key)) { int index = list.FindIndex((KeyValuePair kvp) => comparer.Equals(kvp.Key, key)); list.RemoveAt(index); if (!dictionary.Remove(key)) { throw new InvalidOperationException(); } return true; } return false; } public bool Remove(KeyValuePair item) { return Remove(item.Key); } public void RemoveAt(int index) { TKey key = list[index].Key; dictionary.Remove(key); list.RemoveAt(index); } public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { return dictionary.TryGetValue(key, out value); } IEnumerator IEnumerable.GetEnumerator() { return list.GetEnumerator(); } [System.Runtime.Serialization.OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { dictionary = new Dictionary(); foreach (KeyValuePair item in list) { dictionary[item.Key] = item.Value; } } } internal static class ReadOnlyCollectionExtensions { public static IReadOnlyList AsReadonlyList(this List list) { return list; } public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull { return dictionary; } } internal static class ThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeException(string paramName, string message) { throw new ArgumentOutOfRangeException(paramName, message); } } } namespace YamlDotNet.Core { internal readonly struct AnchorName : IEquatable { public static readonly AnchorName Empty; private static readonly Regex AnchorPattern = new Regex("^[^\\[\\]\\{\\},]+$", RegexOptions.Compiled); private readonly string? value; public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of an empty anchor"); public bool IsEmpty => value == null; public AnchorName(string value) { this.value = value ?? throw new ArgumentNullException("value"); if (!AnchorPattern.IsMatch(value)) { throw new ArgumentException("Anchor cannot be empty or contain disallowed characters: []{},\nThe value was '" + value + "'.", "value"); } } public override string ToString() { return value ?? "[empty]"; } public bool Equals(AnchorName other) { return object.Equals(value, other.value); } public override bool Equals(object? obj) { if (obj is AnchorName other) { return Equals(other); } return false; } public override int GetHashCode() { return value?.GetHashCode() ?? 0; } public static bool operator ==(AnchorName left, AnchorName right) { return left.Equals(right); } public static bool operator !=(AnchorName left, AnchorName right) { return !(left == right); } public static implicit operator AnchorName(string? value) { if (value != null) { return new AnchorName(value); } return Empty; } } internal class AnchorNotFoundException : YamlException { public AnchorNotFoundException(string message) : base(message) { } public AnchorNotFoundException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public AnchorNotFoundException(string message, Exception inner) : base(message, inner) { } } [DebuggerStepThrough] internal readonly struct CharacterAnalyzer where TBuffer : ILookAheadBuffer { public TBuffer Buffer { get; } public bool EndOfInput => Buffer.EndOfInput; public CharacterAnalyzer(TBuffer buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } Buffer = buffer; } public char Peek(int offset) { return Buffer.Peek(offset); } public void Skip(int length) { Buffer.Skip(length); } public bool IsAlphaNumericDashOrUnderscore(int offset = 0) { char c = Buffer.Peek(offset); if ((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && c != '_') { return c == '-'; } return true; } public bool IsAscii(int offset = 0) { return Buffer.Peek(offset) <= '\u007f'; } public bool IsPrintable(int offset = 0) { char c = Buffer.Peek(offset); switch (c) { default: if (c != '\u0085' && (c < '\u00a0' || c > '\ud7ff')) { if (c >= '\ue000') { return c <= '\ufffd'; } return false; } break; case '\t': case '\n': case '\r': case ' ': case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': break; } return true; } public bool IsDigit(int offset = 0) { char c = Buffer.Peek(offset); if (c >= '0') { return c <= '9'; } return false; } public int AsDigit(int offset = 0) { return Buffer.Peek(offset) - 48; } public bool IsHex(int offset) { char c = Buffer.Peek(offset); if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { if (c >= 'a') { return c <= 'f'; } return false; } return true; } public int AsHex(int offset) { char c = Buffer.Peek(offset); if (c <= '9') { return c - 48; } if (c <= 'F') { return c - 65 + 10; } return c - 97 + 10; } public bool IsSpace(int offset = 0) { return Check(' ', offset); } public bool IsZero(int offset = 0) { return Check('\0', offset); } public bool IsTab(int offset = 0) { return Check('\t', offset); } public bool IsWhite(int offset = 0) { if (!IsSpace(offset)) { return IsTab(offset); } return true; } public bool IsBreak(int offset = 0) { return Check("\r\n\u0085\u2028\u2029", offset); } public bool IsCrLf(int offset = 0) { if (Check('\r', offset)) { return Check('\n', offset + 1); } return false; } public bool IsBreakOrZero(int offset = 0) { if (!IsBreak(offset)) { return IsZero(offset); } return true; } public bool IsWhiteBreakOrZero(int offset = 0) { if (!IsWhite(offset)) { return IsBreakOrZero(offset); } return true; } public bool Check(char expected, int offset = 0) { return Buffer.Peek(offset) == expected; } public bool Check(string expectedCharacters, int offset = 0) { char c = Buffer.Peek(offset); return Polyfills.Contains(expectedCharacters, c); } } internal static class Constants { public static readonly TagDirective[] DefaultTagDirectives = new TagDirective[2] { new TagDirective("!", "!"), new TagDirective("!!", "tag:yaml.org,2002:") }; public const int MajorVersion = 1; public const int MinorVersion = 3; } [DebuggerStepThrough] internal sealed class Cursor { public long Index { get; private set; } public long Line { get; private set; } public long LineOffset { get; private set; } public Cursor() { Line = 1L; } public Cursor(Cursor cursor) { Index = cursor.Index; Line = cursor.Line; LineOffset = cursor.LineOffset; } public Mark Mark() { return new Mark(Index, Line, LineOffset + 1); } public void Skip() { Index++; LineOffset++; } public void SkipLineByOffset(int offset) { Index += offset; Line++; LineOffset = 0L; } public void ForceSkipLineAfterNonBreak() { if (LineOffset != 0L) { Line++; LineOffset = 0L; } } } internal class Emitter : IEmitter { private class AnchorData { public AnchorName Anchor; public bool IsAlias; } private class TagData { public string? Handle; public string? Suffix; } private class ScalarData { public string Value = string.Empty; public bool IsMultiline; public bool IsFlowPlainAllowed; public bool IsBlockPlainAllowed; public bool IsSingleQuotedAllowed; public bool IsBlockAllowed; public bool HasSingleQuotes; public ScalarStyle Style; } private static readonly Regex UriReplacer = new Regex("[^0-9A-Za-z_\\-;?@=$~\\\\\\)\\]/:&+,\\.\\*\\(\\[!]", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly string[] NewLineSeparators = new string[3] { "\r\n", "\r", "\n" }; private readonly TextWriter output; private readonly bool outputUsesUnicodeEncoding; private readonly int maxSimpleKeyLength; private readonly bool isCanonical; private readonly bool skipAnchorName; private readonly int bestIndent; private readonly int bestWidth; private EmitterState state; private readonly Stack states = new Stack(); private readonly Queue events = new Queue(); private readonly Stack indents = new Stack(); private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); private int indent; private int flowLevel; private bool isMappingContext; private bool isSimpleKeyContext; private int column; private bool isWhitespace; private bool isIndentation; private readonly bool forceIndentLess; private readonly bool useUtf16SurrogatePair; private bool isDocumentEndWritten; private readonly AnchorData anchorData = new AnchorData(); private readonly TagData tagData = new TagData(); private readonly ScalarData scalarData = new ScalarData(); public Emitter(TextWriter output) : this(output, EmitterSettings.Default) { } public Emitter(TextWriter output, int bestIndent) : this(output, bestIndent, int.MaxValue) { } public Emitter(TextWriter output, int bestIndent, int bestWidth) : this(output, bestIndent, bestWidth, isCanonical: false) { } public Emitter(TextWriter output, int bestIndent, int bestWidth, bool isCanonical) : this(output, new EmitterSettings(bestIndent, bestWidth, isCanonical, 1024)) { } public Emitter(TextWriter output, EmitterSettings settings) { bestIndent = settings.BestIndent; bestWidth = settings.BestWidth; isCanonical = settings.IsCanonical; maxSimpleKeyLength = settings.MaxSimpleKeyLength; skipAnchorName = settings.SkipAnchorName; forceIndentLess = !settings.IndentSequences; useUtf16SurrogatePair = settings.UseUtf16SurrogatePairs; this.output = output; this.output.NewLine = settings.NewLine; outputUsesUnicodeEncoding = IsUnicode(output.Encoding); } public void Emit(ParsingEvent @event) { events.Enqueue(@event); while (!NeedMoreEvents()) { ParsingEvent evt = events.Peek(); try { AnalyzeEvent(evt); StateMachine(evt); } finally { events.Dequeue(); } } } private bool NeedMoreEvents() { if (events.Count == 0) { return true; } int num; switch (events.Peek().Type) { case EventType.DocumentStart: num = 1; break; case EventType.SequenceStart: num = 2; break; case EventType.MappingStart: num = 3; break; default: return false; } if (events.Count > num) { return false; } int num2 = 0; foreach (ParsingEvent @event in events) { switch (@event.Type) { case EventType.DocumentStart: case EventType.SequenceStart: case EventType.MappingStart: num2++; break; case EventType.DocumentEnd: case EventType.SequenceEnd: case EventType.MappingEnd: num2--; break; } if (num2 == 0) { return false; } } return true; } private void AnalyzeEvent(ParsingEvent evt) { anchorData.Anchor = AnchorName.Empty; tagData.Handle = null; tagData.Suffix = null; if (evt is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { AnalyzeAnchor(anchorAlias.Value, isAlias: true); } else if (evt is NodeEvent nodeEvent) { if (evt is YamlDotNet.Core.Events.Scalar scalar) { AnalyzeScalar(scalar); } AnalyzeAnchor(nodeEvent.Anchor, isAlias: false); if (!nodeEvent.Tag.IsEmpty && (isCanonical || nodeEvent.IsCanonical)) { AnalyzeTag(nodeEvent.Tag); } } } private void AnalyzeAnchor(AnchorName anchor, bool isAlias) { anchorData.Anchor = anchor; anchorData.IsAlias = isAlias; } private void AnalyzeScalar(YamlDotNet.Core.Events.Scalar scalar) { string value = scalar.Value; scalarData.Value = value; if (value.Length == 0) { if (scalar.Tag == "tag:yaml.org,2002:null") { scalarData.IsMultiline = false; scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = true; scalarData.IsSingleQuotedAllowed = false; scalarData.IsBlockAllowed = false; } else { scalarData.IsMultiline = false; scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = true; scalarData.IsBlockAllowed = false; } return; } bool flag = false; bool flag2 = false; if (value.StartsWith("---", StringComparison.Ordinal) || value.StartsWith("...", StringComparison.Ordinal)) { flag = true; flag2 = true; } StringLookAheadBufferPool.BufferWrapper bufferWrapper = StringLookAheadBufferPool.Rent(value); try { CharacterAnalyzer characterAnalyzer = new CharacterAnalyzer(bufferWrapper.Buffer); bool flag3 = true; bool flag4 = characterAnalyzer.IsWhiteBreakOrZero(1); bool flag5 = false; bool flag6 = false; bool flag7 = false; bool flag8 = false; bool flag9 = false; bool flag10 = false; bool flag11 = false; bool flag12 = false; bool flag13 = false; bool flag14 = false; bool flag15 = false; bool flag16 = !ValueIsRepresentableInOutputEncoding(value); bool flag17 = false; bool flag18 = false; bool flag19 = true; while (!characterAnalyzer.EndOfInput) { if (flag19) { if (characterAnalyzer.Check("#,[]{}&*!|>\"%@`'")) { flag = true; flag2 = true; flag9 = characterAnalyzer.Check('\''); flag17 |= characterAnalyzer.Check('\''); } if (characterAnalyzer.Check("?:")) { flag = true; if (flag4) { flag2 = true; } } if (characterAnalyzer.Check('-') && flag4) { flag = true; flag2 = true; } } else { if (characterAnalyzer.Check(",?[]{}")) { flag = true; } if (characterAnalyzer.Check(':')) { flag = true; if (flag4) { flag2 = true; } } if (characterAnalyzer.Check('#') && flag3) { flag = true; flag2 = true; } flag17 |= characterAnalyzer.Check('\''); } if (!flag16 && !characterAnalyzer.IsPrintable()) { flag16 = true; } if (characterAnalyzer.IsBreak()) { flag15 = true; } if (characterAnalyzer.IsSpace()) { if (flag19) { flag5 = true; } if (characterAnalyzer.Buffer.Position >= characterAnalyzer.Buffer.Length - 1) { flag7 = true; } if (flag13) { flag10 = true; flag14 = true; } flag12 = true; flag13 = false; } else if (characterAnalyzer.IsBreak()) { if (flag19) { flag6 = true; } if (characterAnalyzer.Buffer.Position >= characterAnalyzer.Buffer.Length - 1) { flag8 = true; } if (flag12) { flag11 = true; } if (flag14) { flag18 = true; } flag12 = false; flag13 = true; } else { flag12 = false; flag13 = false; flag14 = false; } flag3 = characterAnalyzer.IsWhiteBreakOrZero(); characterAnalyzer.Skip(1); if (!characterAnalyzer.EndOfInput) { flag4 = characterAnalyzer.IsWhiteBreakOrZero(1); } flag19 = false; } scalarData.IsFlowPlainAllowed = true; scalarData.IsBlockPlainAllowed = true; scalarData.IsSingleQuotedAllowed = true; scalarData.IsBlockAllowed = true; if (flag5 || flag6 || flag7 || flag8 || flag9) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; } if (flag7) { scalarData.IsBlockAllowed = false; } if (flag10) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = false; } if (flag11 || flag16) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = false; } if (flag18) { scalarData.IsBlockAllowed = false; } scalarData.IsMultiline = flag15; if (flag15) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; } if (flag) { scalarData.IsFlowPlainAllowed = false; } if (flag2) { scalarData.IsBlockPlainAllowed = false; } scalarData.HasSingleQuotes = flag17; } finally { ((IDisposable)bufferWrapper/*cast due to .constrained prefix*/).Dispose(); } } private bool ValueIsRepresentableInOutputEncoding(string value) { if (outputUsesUnicodeEncoding) { return true; } try { byte[] bytes = output.Encoding.GetBytes(value); string text = output.Encoding.GetString(bytes, 0, bytes.Length); return text.Equals(value); } catch (EncoderFallbackException) { return false; } catch (ArgumentOutOfRangeException) { return false; } } private static bool IsUnicode(Encoding encoding) { if (!(encoding is UTF8Encoding) && !(encoding is UnicodeEncoding)) { return encoding is UTF7Encoding; } return true; } private void AnalyzeTag(TagName tag) { tagData.Handle = tag.Value; foreach (TagDirective tagDirective in tagDirectives) { if (tag.Value.StartsWith(tagDirective.Prefix, StringComparison.Ordinal)) { tagData.Handle = tagDirective.Handle; tagData.Suffix = tag.Value.Substring(tagDirective.Prefix.Length); break; } } } private void StateMachine(ParsingEvent evt) { if (evt is YamlDotNet.Core.Events.Comment comment) { EmitComment(comment); return; } switch (state) { case EmitterState.StreamStart: EmitStreamStart(evt); break; case EmitterState.FirstDocumentStart: EmitDocumentStart(evt, isFirst: true); break; case EmitterState.DocumentStart: EmitDocumentStart(evt, isFirst: false); break; case EmitterState.DocumentContent: EmitDocumentContent(evt); break; case EmitterState.DocumentEnd: EmitDocumentEnd(evt); break; case EmitterState.FlowSequenceFirstItem: EmitFlowSequenceItem(evt, isFirst: true); break; case EmitterState.FlowSequenceItem: EmitFlowSequenceItem(evt, isFirst: false); break; case EmitterState.FlowMappingFirstKey: EmitFlowMappingKey(evt, isFirst: true); break; case EmitterState.FlowMappingKey: EmitFlowMappingKey(evt, isFirst: false); break; case EmitterState.FlowMappingSimpleValue: EmitFlowMappingValue(evt, isSimple: true); break; case EmitterState.FlowMappingValue: EmitFlowMappingValue(evt, isSimple: false); break; case EmitterState.BlockSequenceFirstItem: EmitBlockSequenceItem(evt, isFirst: true); break; case EmitterState.BlockSequenceItem: EmitBlockSequenceItem(evt, isFirst: false); break; case EmitterState.BlockMappingFirstKey: EmitBlockMappingKey(evt, isFirst: true); break; case EmitterState.BlockMappingKey: EmitBlockMappingKey(evt, isFirst: false); break; case EmitterState.BlockMappingSimpleValue: EmitBlockMappingValue(evt, isSimple: true); break; case EmitterState.BlockMappingValue: EmitBlockMappingValue(evt, isSimple: false); break; case EmitterState.StreamEnd: throw new YamlException("Expected nothing after STREAM-END"); default: throw new InvalidOperationException(); } } private void EmitComment(YamlDotNet.Core.Events.Comment comment) { if (flowLevel > 0 || state == EmitterState.FlowMappingFirstKey || state == EmitterState.FlowSequenceFirstItem) { return; } string[] array = comment.Value.Split(NewLineSeparators, StringSplitOptions.None); if (comment.IsInline) { Write(" # "); Write(string.Join(" ", array)); } else { bool flag = state == EmitterState.BlockMappingFirstKey; if (flag) { IncreaseIndent(isFlow: false, isIndentless: false); } string[] array2 = array; foreach (string value in array2) { WriteIndent(); Write("# "); Write(value); WriteBreak(); } if (flag) { indent = indents.Pop(); } } isIndentation = true; } private void EmitStreamStart(ParsingEvent evt) { if (!(evt is YamlDotNet.Core.Events.StreamStart)) { throw new ArgumentException("Expected STREAM-START.", "evt"); } indent = -1; column = 0; isWhitespace = true; isIndentation = true; state = EmitterState.FirstDocumentStart; } private void EmitDocumentStart(ParsingEvent evt, bool isFirst) { if (evt is YamlDotNet.Core.Events.DocumentStart documentStart) { bool flag = documentStart.IsImplicit && isFirst && !isCanonical; TagDirectiveCollection tagDirectiveCollection = NonDefaultTagsAmong(documentStart.Tags); if (!isFirst && !isDocumentEndWritten && (documentStart.Version != null || tagDirectiveCollection.Count > 0)) { isDocumentEndWritten = false; WriteIndicator("...", needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); } if (documentStart.Version != null) { AnalyzeVersionDirective(documentStart.Version); Version version = documentStart.Version.Version; flag = false; WriteIndicator("%YAML", needWhitespace: true, whitespace: false, indentation: false); WriteIndicator(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor), needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); } foreach (TagDirective item in tagDirectiveCollection) { AppendTagDirectiveTo(item, allowDuplicates: false, tagDirectives); } TagDirective[] defaultTagDirectives = Constants.DefaultTagDirectives; foreach (TagDirective value in defaultTagDirectives) { AppendTagDirectiveTo(value, allowDuplicates: true, tagDirectives); } if (tagDirectiveCollection.Count > 0) { flag = false; TagDirective[] defaultTagDirectives2 = Constants.DefaultTagDirectives; foreach (TagDirective value2 in defaultTagDirectives2) { AppendTagDirectiveTo(value2, allowDuplicates: true, tagDirectiveCollection); } foreach (TagDirective item2 in tagDirectiveCollection) { WriteIndicator("%TAG", needWhitespace: true, whitespace: false, indentation: false); WriteTagHandle(item2.Handle); WriteTagContent(item2.Prefix, needsWhitespace: true); WriteIndent(); } } if (CheckEmptyDocument()) { flag = false; } if (!flag) { WriteIndent(); WriteIndicator("---", needWhitespace: true, whitespace: false, indentation: false); if (isCanonical) { WriteIndent(); } } state = EmitterState.DocumentContent; } else { if (!(evt is YamlDotNet.Core.Events.StreamEnd)) { throw new YamlException("Expected DOCUMENT-START or STREAM-END"); } state = EmitterState.StreamEnd; } } private static TagDirectiveCollection NonDefaultTagsAmong(IEnumerable? tagCollection) { TagDirectiveCollection tagDirectiveCollection = new TagDirectiveCollection(); if (tagCollection == null) { return tagDirectiveCollection; } foreach (TagDirective item2 in tagCollection) { AppendTagDirectiveTo(item2, allowDuplicates: false, tagDirectiveCollection); } TagDirective[] defaultTagDirectives = Constants.DefaultTagDirectives; foreach (TagDirective item in defaultTagDirectives) { tagDirectiveCollection.Remove(item); } return tagDirectiveCollection; } private static void AnalyzeVersionDirective(VersionDirective versionDirective) { if (versionDirective.Version.Major != 1 || versionDirective.Version.Minor > 3) { throw new YamlException("Incompatible %YAML directive"); } } private static void AppendTagDirectiveTo(TagDirective value, bool allowDuplicates, TagDirectiveCollection tagDirectives) { if (tagDirectives.Contains(value)) { if (!allowDuplicates) { throw new YamlException("Duplicate %TAG directive."); } } else { tagDirectives.Add(value); } } private void EmitDocumentContent(ParsingEvent evt) { states.Push(EmitterState.DocumentEnd); EmitNode(evt, isMapping: false, isSimpleKey: false); } private void EmitNode(ParsingEvent evt, bool isMapping, bool isSimpleKey) { isMappingContext = isMapping; isSimpleKeyContext = isSimpleKey; switch (evt.Type) { case EventType.Alias: EmitAlias(); break; case EventType.Scalar: EmitScalar(evt); break; case EventType.SequenceStart: EmitSequenceStart(evt); break; case EventType.MappingStart: EmitMappingStart(evt); break; default: throw new YamlException($"Expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, got {evt.Type}"); } } private void EmitAlias() { ProcessAnchor(); state = states.Pop(); } private void EmitScalar(ParsingEvent evt) { SelectScalarStyle(evt); ProcessAnchor(); ProcessTag(); IncreaseIndent(isFlow: true, isIndentless: false); ProcessScalar(); indent = indents.Pop(); state = states.Pop(); } private void SelectScalarStyle(ParsingEvent evt) { YamlDotNet.Core.Events.Scalar scalar = (YamlDotNet.Core.Events.Scalar)evt; ScalarStyle scalarStyle = scalar.Style; bool flag = tagData.Handle == null && tagData.Suffix == null; if (flag && !scalar.IsPlainImplicit && !scalar.IsQuotedImplicit) { throw new YamlException("Neither tag nor isImplicit flags are specified."); } if (scalarStyle == ScalarStyle.Any) { scalarStyle = ((!scalarData.IsMultiline) ? ScalarStyle.Plain : ScalarStyle.Folded); } if (isCanonical) { scalarStyle = ScalarStyle.DoubleQuoted; } if (isSimpleKeyContext && scalarData.IsMultiline) { scalarStyle = ScalarStyle.DoubleQuoted; } if (scalarStyle == ScalarStyle.Plain) { if ((flowLevel != 0 && !scalarData.IsFlowPlainAllowed) || (flowLevel == 0 && !scalarData.IsBlockPlainAllowed)) { scalarStyle = ((scalarData.IsSingleQuotedAllowed && !scalarData.HasSingleQuotes) ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted); } if (string.IsNullOrEmpty(scalarData.Value) && (flowLevel != 0 || isSimpleKeyContext)) { scalarStyle = ScalarStyle.SingleQuoted; } if (flag && !scalar.IsPlainImplicit) { scalarStyle = ScalarStyle.SingleQuoted; } } if (scalarStyle == ScalarStyle.SingleQuoted && !scalarData.IsSingleQuotedAllowed) { scalarStyle = ScalarStyle.DoubleQuoted; } if ((scalarStyle == ScalarStyle.Literal || scalarStyle == ScalarStyle.Folded) && (!scalarData.IsBlockAllowed || flowLevel != 0 || isSimpleKeyContext)) { scalarStyle = ScalarStyle.DoubleQuoted; } if (scalarStyle == ScalarStyle.ForcePlain) { scalarStyle = ScalarStyle.Plain; } scalarData.Style = scalarStyle; } private void ProcessScalar() { switch (scalarData.Style) { case ScalarStyle.Plain: WritePlainScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.SingleQuoted: WriteSingleQuotedScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.DoubleQuoted: WriteDoubleQuotedScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.Literal: WriteLiteralScalar(scalarData.Value); break; case ScalarStyle.Folded: WriteFoldedScalar(scalarData.Value); break; default: throw new InvalidOperationException(); } } private void WritePlainScalar(string value, bool allowBreaks) { if (!isWhitespace) { Write(' '); } bool flag = false; bool flag2 = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsSpace(c)) { if (allowBreaks && !flag && column > bestWidth && i + 1 < value.Length && value[i + 1] != ' ') { WriteIndent(); } else { Write(c); } flag = true; continue; } if (IsBreak(c, out var breakChar)) { if (!flag2 && c == '\n') { WriteBreak(); } WriteBreak(breakChar); isIndentation = true; flag2 = true; continue; } if (flag2) { WriteIndent(); } Write(c); isIndentation = false; flag = false; flag2 = false; } isWhitespace = false; isIndentation = false; } private void WriteSingleQuotedScalar(string value, bool allowBreaks) { WriteIndicator("'", needWhitespace: true, whitespace: false, indentation: false); bool flag = false; bool flag2 = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c == ' ') { if (allowBreaks && !flag && column > bestWidth && i != 0 && i + 1 < value.Length && value[i + 1] != ' ') { WriteIndent(); } else { Write(c); } flag = true; continue; } if (IsBreak(c, out var breakChar)) { if (!flag2 && c == '\n') { WriteBreak(); } WriteBreak(breakChar); isIndentation = true; flag2 = true; continue; } if (flag2) { WriteIndent(); } if (c == '\'') { Write(c); } Write(c); isIndentation = false; flag = false; flag2 = false; } WriteIndicator("'", needWhitespace: false, whitespace: false, indentation: false); isWhitespace = false; isIndentation = false; } private void WriteDoubleQuotedScalar(string value, bool allowBreaks) { WriteIndicator("\"", needWhitespace: true, whitespace: false, indentation: false); bool flag = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsPrintable(c) && !IsBreak(c, out var _)) { switch (c) { case '"': case '\\': break; case ' ': if (allowBreaks && !flag && column > bestWidth && i > 0 && i + 1 < value.Length) { WriteIndent(); if (value[i + 1] == ' ') { Write('\\'); } } else { Write(c); } flag = true; continue; default: Write(c); flag = false; continue; } } Write('\\'); switch (c) { case '\0': Write('0'); break; case '\a': Write('a'); break; case '\b': Write('b'); break; case '\t': Write('t'); break; case '\n': Write('n'); break; case '\v': Write('v'); break; case '\f': Write('f'); break; case '\r': Write('r'); break; case '\u001b': Write('e'); break; case '"': Write('"'); break; case '\\': Write('\\'); break; case '\u0085': Write('N'); break; case '\u00a0': Write('_'); break; case '\u2028': Write('L'); break; case '\u2029': Write('P'); break; default: { ushort num = c; if (num <= 255) { Write('x'); Write(num.ToString("X02", CultureInfo.InvariantCulture)); } else if (IsHighSurrogate(c)) { if (i + 1 >= value.Length || !IsLowSurrogate(value[i + 1])) { throw new SyntaxErrorException("While writing a quoted scalar, found an orphaned high surrogate."); } if (useUtf16SurrogatePair) { Write('u'); Write(num.ToString("X04", CultureInfo.InvariantCulture)); Write('\\'); Write('u'); Write(((ushort)value[i + 1]).ToString("X04", CultureInfo.InvariantCulture)); } else { Write('U'); Write(char.ConvertToUtf32(c, value[i + 1]).ToString("X08", CultureInfo.InvariantCulture)); } i++; } else { Write('u'); Write(num.ToString("X04", CultureInfo.InvariantCulture)); } break; } } flag = false; } WriteIndicator("\"", needWhitespace: false, whitespace: false, indentation: false); isWhitespace = false; isIndentation = false; } private void WriteLiteralScalar(string value) { bool flag = true; WriteIndicator("|", needWhitespace: true, whitespace: false, indentation: false); WriteBlockScalarHints(value); WriteBreak(); isIndentation = true; isWhitespace = true; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c == '\r' && i + 1 < value.Length && value[i + 1] == '\n') { continue; } if (IsBreak(c, out var breakChar)) { WriteBreak(breakChar); isIndentation = true; flag = true; continue; } if (flag) { WriteIndent(); } Write(c); isIndentation = false; flag = false; } } private void WriteFoldedScalar(string value) { bool flag = true; bool flag2 = true; WriteIndicator(">", needWhitespace: true, whitespace: false, indentation: false); WriteBlockScalarHints(value); WriteBreak(); isIndentation = true; isWhitespace = true; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsBreak(c, out var breakChar)) { if (c == '\r' && i + 1 < value.Length && value[i + 1] == '\n') { continue; } if (!flag && !flag2 && breakChar == '\n') { int j; char breakChar2; for (j = 0; i + j < value.Length && IsBreak(value[i + j], out breakChar2); j++) { } if (i + j < value.Length && !IsBlank(value[i + j]) && !IsBreak(value[i + j], out breakChar2)) { WriteBreak(); } } WriteBreak(breakChar); isIndentation = true; flag = true; } else { if (flag) { WriteIndent(); flag2 = IsBlank(c); } if (!flag && c == ' ' && i + 1 < value.Length && value[i + 1] != ' ' && column > bestWidth) { WriteIndent(); } else { Write(c); } isIndentation = false; flag = false; } } } private static bool IsSpace(char character) { return character == ' '; } private static bool IsBreak(char character, out char breakChar) { switch (character) { case '\n': case '\r': case '\u0085': breakChar = '\n'; return true; case '\u2028': case '\u2029': breakChar = character; return true; default: breakChar = '\0'; return false; } } private static bool IsBlank(char character) { if (character != ' ') { return character == '\t'; } return true; } private static bool IsPrintable(char character) { switch (character) { default: if (character != '\u0085' && (character < '\u00a0' || character > '\ud7ff')) { if (character >= '\ue000') { return character <= '\ufffd'; } return false; } break; case '\t': case '\n': case '\r': case ' ': case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': break; } return true; } private static bool IsHighSurrogate(char c) { if ('\ud800' <= c) { return c <= '\udbff'; } return false; } private static bool IsLowSurrogate(char c) { if ('\udc00' <= c) { return c <= '\udfff'; } return false; } private void EmitSequenceStart(ParsingEvent evt) { ProcessAnchor(); ProcessTag(); SequenceStart sequenceStart = (SequenceStart)evt; if (flowLevel != 0 || isCanonical || sequenceStart.Style == SequenceStyle.Flow || CheckEmptySequence()) { state = EmitterState.FlowSequenceFirstItem; } else { state = EmitterState.BlockSequenceFirstItem; } } private void EmitMappingStart(ParsingEvent evt) { ProcessAnchor(); ProcessTag(); MappingStart mappingStart = (MappingStart)evt; if (flowLevel != 0 || isCanonical || mappingStart.Style == MappingStyle.Flow || CheckEmptyMapping()) { state = EmitterState.FlowMappingFirstKey; } else { state = EmitterState.BlockMappingFirstKey; } } private void ProcessAnchor() { if (!anchorData.Anchor.IsEmpty && !skipAnchorName) { WriteIndicator(anchorData.IsAlias ? "*" : "&", needWhitespace: true, whitespace: false, indentation: false); WriteAnchor(anchorData.Anchor); } } private void ProcessTag() { if (tagData.Handle == null && tagData.Suffix == null) { return; } if (tagData.Handle != null) { WriteTagHandle(tagData.Handle); if (tagData.Suffix != null) { WriteTagContent(tagData.Suffix, needsWhitespace: false); } } else { WriteIndicator("!<", needWhitespace: true, whitespace: false, indentation: false); WriteTagContent(tagData.Suffix, needsWhitespace: false); WriteIndicator(">", needWhitespace: false, whitespace: false, indentation: false); } } private void EmitDocumentEnd(ParsingEvent evt) { if (evt is YamlDotNet.Core.Events.DocumentEnd documentEnd) { WriteIndent(); if (!documentEnd.IsImplicit) { WriteIndicator("...", needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); isDocumentEndWritten = true; } state = EmitterState.DocumentStart; tagDirectives.Clear(); return; } throw new YamlException("Expected DOCUMENT-END."); } private void EmitFlowSequenceItem(ParsingEvent evt, bool isFirst) { if (isFirst) { WriteIndicator("[", needWhitespace: true, whitespace: true, indentation: false); IncreaseIndent(isFlow: true, isIndentless: false); flowLevel++; } if (evt is SequenceEnd) { flowLevel--; indent = indents.Pop(); if (isCanonical && !isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); WriteIndent(); } WriteIndicator("]", needWhitespace: false, whitespace: false, indentation: false); state = states.Pop(); } else { if (!isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); } if (isCanonical || column > bestWidth) { WriteIndent(); } states.Push(EmitterState.FlowSequenceItem); EmitNode(evt, isMapping: false, isSimpleKey: false); } } private void EmitFlowMappingKey(ParsingEvent evt, bool isFirst) { if (isFirst) { WriteIndicator("{", needWhitespace: true, whitespace: true, indentation: false); IncreaseIndent(isFlow: true, isIndentless: false); flowLevel++; } if (evt is MappingEnd) { flowLevel--; indent = indents.Pop(); if (isCanonical && !isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); WriteIndent(); } WriteIndicator("}", needWhitespace: false, whitespace: false, indentation: false); state = states.Pop(); return; } if (!isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); } if (isCanonical || column > bestWidth) { WriteIndent(); } if (!isCanonical && CheckSimpleKey()) { states.Push(EmitterState.FlowMappingSimpleValue); EmitNode(evt, isMapping: true, isSimpleKey: true); } else { WriteIndicator("?", needWhitespace: true, whitespace: false, indentation: false); states.Push(EmitterState.FlowMappingValue); EmitNode(evt, isMapping: true, isSimpleKey: false); } } private void EmitFlowMappingValue(ParsingEvent evt, bool isSimple) { if (isSimple) { WriteIndicator(":", needWhitespace: false, whitespace: false, indentation: false); } else { if (isCanonical || column > bestWidth) { WriteIndent(); } WriteIndicator(":", needWhitespace: true, whitespace: false, indentation: false); } states.Push(EmitterState.FlowMappingKey); EmitNode(evt, isMapping: true, isSimpleKey: false); } private void EmitBlockSequenceItem(ParsingEvent evt, bool isFirst) { if (isFirst) { IncreaseIndent(isFlow: false, isMappingContext && !isIndentation); } if (evt is SequenceEnd) { indent = indents.Pop(); state = states.Pop(); return; } WriteIndent(); WriteIndicator("-", needWhitespace: true, whitespace: false, indentation: true); states.Push(EmitterState.BlockSequenceItem); EmitNode(evt, isMapping: false, isSimpleKey: false); } private void EmitBlockMappingKey(ParsingEvent evt, bool isFirst) { if (isFirst) { IncreaseIndent(isFlow: false, isIndentless: false); } if (evt is MappingEnd) { indent = indents.Pop(); state = states.Pop(); return; } WriteIndent(); if (CheckSimpleKey()) { states.Push(EmitterState.BlockMappingSimpleValue); EmitNode(evt, isMapping: true, isSimpleKey: true); WriteIndicator(":", needWhitespace: false, whitespace: false, indentation: false); } else { WriteIndicator("?", needWhitespace: true, whitespace: false, indentation: true); states.Push(EmitterState.BlockMappingValue); EmitNode(evt, isMapping: true, isSimpleKey: false); } } private void EmitBlockMappingValue(ParsingEvent evt, bool isSimple) { if (!isSimple) { WriteIndent(); WriteIndicator(":", needWhitespace: true, whitespace: false, indentation: true); } states.Push(EmitterState.BlockMappingKey); EmitNode(evt, isMapping: true, isSimpleKey: false); } private void IncreaseIndent(bool isFlow, bool isIndentless) { indents.Push(indent); if (indent < 0) { indent = (isFlow ? bestIndent : 0); } else if (!isIndentless || !forceIndentLess) { indent += bestIndent; } } private bool CheckEmptyDocument() { int num = 0; foreach (ParsingEvent @event in events) { num++; if (num == 2) { if (@event is YamlDotNet.Core.Events.Scalar scalar) { return string.IsNullOrEmpty(scalar.Value); } break; } } return false; } private bool CheckSimpleKey() { if (events.Count < 1) { return false; } int num; switch (events.Peek().Type) { case EventType.Alias: num = AnchorNameLength(anchorData.Anchor); break; case EventType.Scalar: if (scalarData.IsMultiline) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix) + SafeStringLength(scalarData.Value); break; case EventType.SequenceStart: if (!CheckEmptySequence()) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix); break; case EventType.MappingStart: if (!CheckEmptySequence()) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix); break; default: return false; } return num <= maxSimpleKeyLength; } private static int AnchorNameLength(AnchorName value) { if (!value.IsEmpty) { return value.Value.Length; } return 0; } private static int SafeStringLength(string? value) { return value?.Length ?? 0; } private bool CheckEmptySequence() { return CheckEmptyStructure(); } private bool CheckEmptyMapping() { return CheckEmptyStructure(); } private bool CheckEmptyStructure() where TStart : NodeEvent where TEnd : ParsingEvent { if (events.Count < 2) { return false; } using Queue.Enumerator enumerator = events.GetEnumerator(); return enumerator.MoveNext() && enumerator.Current is TStart && enumerator.MoveNext() && enumerator.Current is TEnd; } private void WriteBlockScalarHints(string value) { StringLookAheadBufferPool.BufferWrapper bufferWrapper = StringLookAheadBufferPool.Rent(value); try { CharacterAnalyzer characterAnalyzer = new CharacterAnalyzer(bufferWrapper.Buffer); if (characterAnalyzer.IsSpace() || characterAnalyzer.IsBreak()) { int num = bestIndent; string indicator = num.ToString(CultureInfo.InvariantCulture); WriteIndicator(indicator, needWhitespace: false, whitespace: false, indentation: false); } string text = null; if (value.Length == 0 || !characterAnalyzer.IsBreak(value.Length - 1)) { text = "-"; } else if (value.Length >= 2 && characterAnalyzer.IsBreak(value.Length - 2)) { text = "+"; } if (text != null) { WriteIndicator(text, needWhitespace: false, whitespace: false, indentation: false); } } finally { ((IDisposable)bufferWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void WriteIndicator(string indicator, bool needWhitespace, bool whitespace, bool indentation) { if (needWhitespace && !isWhitespace) { Write(' '); } Write(indicator); isWhitespace = whitespace; isIndentation &= indentation; } private void WriteIndent() { int num = Math.Max(indent, 0); if (!isIndentation || column > num || (column == num && !isWhitespace)) { WriteBreak(); } while (column < num) { Write(' '); } isWhitespace = true; isIndentation = true; } private void WriteAnchor(AnchorName value) { Write(value.Value); isWhitespace = false; isIndentation = false; } private void WriteTagHandle(string value) { if (!isWhitespace) { Write(' '); } Write(value); isWhitespace = false; isIndentation = false; } private void WriteTagContent(string value, bool needsWhitespace) { if (needsWhitespace && !isWhitespace) { Write(' '); } Write(UrlEncode(value)); isWhitespace = false; isIndentation = false; } private static string UrlEncode(string text) { return UriReplacer.Replace(text, delegate(Match match) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; byte[] bytes = Encoding.UTF8.GetBytes(match.Value); foreach (byte b in bytes) { builder.AppendFormat(CultureInfo.InvariantCulture, "%{0:X02}", b); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } }); } private void Write(char value) { output.Write(value); column++; } private void Write(string value) { output.Write(value); column += value.Length; } private void WriteBreak(char breakCharacter = '\n') { if (breakCharacter == '\n') { output.WriteLine(); } else { output.Write(breakCharacter); } column = 0; } } internal sealed class EmitterSettings { public static readonly EmitterSettings Default = new EmitterSettings(); public int BestIndent { get; } = 2; public int BestWidth { get; } = int.MaxValue; public string NewLine { get; } = Environment.NewLine; public bool IsCanonical { get; } public bool SkipAnchorName { get; private set; } public int MaxSimpleKeyLength { get; } = 1024; public bool IndentSequences { get; } public bool UseUtf16SurrogatePairs { get; } public EmitterSettings() { } public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength, bool skipAnchorName = false, bool indentSequences = false, string? newLine = null, bool useUtf16SurrogatePairs = false) { if (bestIndent < 2 || bestIndent > 9) { throw new ArgumentOutOfRangeException("bestIndent", "BestIndent must be between 2 and 9, inclusive"); } if (bestWidth <= bestIndent * 2) { throw new ArgumentOutOfRangeException("bestWidth", "BestWidth must be greater than BestIndent x 2."); } if (maxSimpleKeyLength < 0) { throw new ArgumentOutOfRangeException("maxSimpleKeyLength", "MaxSimpleKeyLength must be >= 0"); } BestIndent = bestIndent; BestWidth = bestWidth; IsCanonical = isCanonical; MaxSimpleKeyLength = maxSimpleKeyLength; SkipAnchorName = skipAnchorName; IndentSequences = indentSequences; NewLine = newLine ?? Environment.NewLine; UseUtf16SurrogatePairs = useUtf16SurrogatePairs; } public EmitterSettings WithBestIndent(int bestIndent) { return new EmitterSettings(bestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithBestWidth(int bestWidth) { return new EmitterSettings(BestIndent, bestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithMaxSimpleKeyLength(int maxSimpleKeyLength) { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, maxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithNewLine(string newLine) { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, newLine, UseUtf16SurrogatePairs); } public EmitterSettings Canonical() { return new EmitterSettings(BestIndent, BestWidth, isCanonical: true, MaxSimpleKeyLength, SkipAnchorName); } public EmitterSettings WithoutAnchorName() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, skipAnchorName: true, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithIndentedSequences() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, indentSequences: true, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithUtf16SurrogatePairs() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, useUtf16SurrogatePairs: true); } } internal enum EmitterState { StreamStart, StreamEnd, FirstDocumentStart, DocumentStart, DocumentContent, DocumentEnd, FlowSequenceFirstItem, FlowSequenceItem, FlowMappingFirstKey, FlowMappingKey, FlowMappingSimpleValue, FlowMappingValue, BlockSequenceFirstItem, BlockSequenceItem, BlockMappingFirstKey, BlockMappingKey, BlockMappingSimpleValue, BlockMappingValue } internal sealed class ForwardAnchorNotSupportedException : YamlException { public ForwardAnchorNotSupportedException(string message) : base(message) { } public ForwardAnchorNotSupportedException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public ForwardAnchorNotSupportedException(string message, Exception inner) : base(message, inner) { } } internal static class HashCode { public static int CombineHashCodes(int h1, int h2) { return ((h1 << 5) + h1) ^ h2; } public static int CombineHashCodes(int h1, object? o2) { return CombineHashCodes(h1, GetHashCode(o2)); } private static int GetHashCode(object? obj) { return obj?.GetHashCode() ?? 0; } } internal interface IEmitter { void Emit(ParsingEvent @event); } internal interface ILookAheadBuffer { bool EndOfInput { get; } char Peek(int offset); void Skip(int length); } internal sealed class InsertionQueue : IEnumerable, IEnumerable { private const int DefaultInitialCapacity = 128; private T[] items; private int readPtr; private int writePtr; private int mask; private int count; public int Count => count; public int Capacity => items.Length; public InsertionQueue(int initialCapacity = 128) { if (initialCapacity <= 0) { throw new ArgumentOutOfRangeException("initialCapacity", "The initial capacity must be a positive number."); } if (!initialCapacity.IsPowerOfTwo()) { throw new ArgumentException("The initial capacity must be a power of 2.", "initialCapacity"); } items = new T[initialCapacity]; readPtr = initialCapacity / 2; writePtr = initialCapacity / 2; mask = initialCapacity - 1; } public void Enqueue(T item) { ResizeIfNeeded(); items[writePtr] = item; writePtr = (writePtr - 1) & mask; count++; } public T Dequeue() { if (count == 0) { throw new InvalidOperationException("The queue is empty"); } T result = items[readPtr]; readPtr = (readPtr - 1) & mask; count--; return result; } public void Insert(int index, T item) { if (index > count) { throw new InvalidOperationException("Cannot insert outside of the bounds of the queue"); } ResizeIfNeeded(); CalculateInsertionParameters(mask, count, index, ref readPtr, ref writePtr, out var insertPtr, out var copyIndex, out var copyOffset, out var copyLength); if (copyLength != 0) { Array.Copy(items, copyIndex, items, copyIndex + copyOffset, copyLength); } items[insertPtr] = item; count++; } private void ResizeIfNeeded() { int num = items.Length; if (count == num) { T[] destinationArray = new T[num * 2]; int num2 = readPtr + 1; if (num2 > 0) { Array.Copy(items, 0, destinationArray, 0, num2); } writePtr += num; int num3 = num - num2; if (num3 > 0) { Array.Copy(items, readPtr + 1, destinationArray, writePtr + 1, num3); } items = destinationArray; mask = mask * 2 + 1; } } internal static void CalculateInsertionParameters(int mask, int count, int index, ref int readPtr, ref int writePtr, out int insertPtr, out int copyIndex, out int copyOffset, out int copyLength) { int num = (readPtr + 1) & mask; if (index == 0) { insertPtr = (readPtr = num); copyIndex = 0; copyOffset = 0; copyLength = 0; return; } insertPtr = (readPtr - index) & mask; if (index == count) { writePtr = (writePtr - 1) & mask; copyIndex = 0; copyOffset = 0; copyLength = 0; return; } int num2 = ((num >= insertPtr) ? (readPtr - insertPtr) : int.MaxValue); int num3 = ((writePtr <= insertPtr) ? (insertPtr - writePtr) : int.MaxValue); if (num2 <= num3) { insertPtr++; readPtr++; copyIndex = insertPtr; copyOffset = 1; copyLength = num2; } else { copyIndex = writePtr + 1; copyOffset = -1; copyLength = num3; writePtr = (writePtr - 1) & mask; } } public IEnumerator GetEnumerator() { int ptr = readPtr; for (int i = 0; i < Count; i++) { yield return items[ptr]; ptr = (ptr - 1) & mask; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal interface IParser { ParsingEvent? Current { get; } bool MoveNext(); } internal interface IScanner { Mark CurrentPosition { get; } Token? Current { get; } bool MoveNext(); bool MoveNextWithoutConsuming(); void ConsumeCurrent(); } [DebuggerStepThrough] internal sealed class LookAheadBuffer : ILookAheadBuffer { private readonly TextReader input; private readonly char[] buffer; private readonly int blockSize; private readonly int mask; private int firstIndex; private int writeOffset; private int count; private bool endOfInput; public bool EndOfInput { get { if (endOfInput) { return count == 0; } return false; } } public LookAheadBuffer(TextReader input, int capacity) { if (capacity < 1) { throw new ArgumentOutOfRangeException("capacity", "The capacity must be positive."); } if (!capacity.IsPowerOfTwo()) { throw new ArgumentException("The capacity must be a power of 2.", "capacity"); } this.input = input ?? throw new ArgumentNullException("input"); blockSize = capacity; buffer = new char[capacity * 2]; mask = capacity * 2 - 1; } private int GetIndexForOffset(int offset) { return (firstIndex + offset) & mask; } public char Peek(int offset) { if (offset >= count) { FillBuffer(); } if (offset < count) { return buffer[(firstIndex + offset) & mask]; } return '\0'; } public void Cache(int length) { if (length >= count) { FillBuffer(); } } private void FillBuffer() { if (endOfInput) { return; } int num = blockSize; do { int num2 = input.Read(buffer, writeOffset, num); if (num2 == 0) { endOfInput = true; return; } num -= num2; writeOffset += num2; count += num2; } while (num > 0); if (writeOffset == buffer.Length) { writeOffset = 0; } } public void Skip(int length) { if (length < 1 || length > blockSize) { throw new ArgumentOutOfRangeException("length", "The length must be between 1 and the number of characters in the buffer. Use the Peek() and / or Cache() methods to fill the buffer."); } firstIndex = GetIndexForOffset(length); count -= length; } } internal readonly struct Mark : IEquatable, IComparable, IComparable { public static readonly Mark Empty = new Mark(0L, 1L, 1L); public long Index { get; } public long Line { get; } public long Column { get; } public Mark(long index, long line, long column) { if (index < 0) { ThrowHelper.ThrowArgumentOutOfRangeException("index", "Index must be greater than or equal to zero."); } if (line < 1) { ThrowHelper.ThrowArgumentOutOfRangeException("line", "Line must be greater than or equal to 1."); } if (column < 1) { ThrowHelper.ThrowArgumentOutOfRangeException("column", "Column must be greater than or equal to 1."); } Index = index; Line = line; Column = column; } public override string ToString() { return $"Line: {Line}, Col: {Column}, Idx: {Index}"; } public override bool Equals(object? obj) { return Equals((Mark)(obj ?? ((object)Empty))); } public bool Equals(Mark other) { if (Index == other.Index && Line == other.Line) { return Column == other.Column; } return false; } public override int GetHashCode() { return HashCode.CombineHashCodes(Index.GetHashCode(), HashCode.CombineHashCodes(Line.GetHashCode(), Column.GetHashCode())); } public int CompareTo(object? obj) { return CompareTo((Mark)(obj ?? ((object)Empty))); } public int CompareTo(Mark other) { int num = Line.CompareTo(other.Line); if (num == 0) { num = Column.CompareTo(other.Column); } return num; } public static bool operator ==(Mark left, Mark right) { return left.Equals(right); } public static bool operator !=(Mark left, Mark right) { return !(left == right); } public static bool operator <(Mark left, Mark right) { return left.CompareTo(right) < 0; } public static bool operator <=(Mark left, Mark right) { return left.CompareTo(right) <= 0; } public static bool operator >(Mark left, Mark right) { return left.CompareTo(right) > 0; } public static bool operator >=(Mark left, Mark right) { return left.CompareTo(right) >= 0; } } internal sealed class MaximumRecursionLevelReachedException : YamlException { public MaximumRecursionLevelReachedException(string message) : base(message) { } public MaximumRecursionLevelReachedException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public MaximumRecursionLevelReachedException(string message, Exception inner) : base(message, inner) { } } internal sealed class MergingParser : IParser { private sealed class ParsingEventCollection : IEnumerable>, IEnumerable { private readonly LinkedList events; private readonly HashSet> deleted; private readonly Dictionary> references; public ParsingEventCollection() { events = new LinkedList(); deleted = new HashSet>(); references = new Dictionary>(); } public void AddAfter(LinkedListNode node, IEnumerable items) { foreach (ParsingEvent item in items) { node = events.AddAfter(node, item); } } public void Add(ParsingEvent item) { LinkedListNode node = events.AddLast(item); AddReference(item, node); } public void MarkDeleted(LinkedListNode node) { deleted.Add(node); } public bool IsDeleted(LinkedListNode node) { return deleted.Contains(node); } public void CleanMarked() { foreach (LinkedListNode item in deleted) { events.Remove(item); } } public IEnumerable> FromAnchor(AnchorName anchor) { LinkedListNode next = references[anchor].Next; return Enumerate(next); } public IEnumerator> GetEnumerator() { return Enumerate(events.First).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private static IEnumerable> Enumerate(LinkedListNode? node) { while (node != null) { yield return node; node = node.Next; } } private void AddReference(ParsingEvent item, LinkedListNode node) { if (item is MappingStart { Anchor: { IsEmpty: false } anchor }) { references[anchor] = node; } } } private sealed class ParsingEventCloner : IParsingEventVisitor { private ParsingEvent? clonedEvent; public ParsingEvent Clone(ParsingEvent e) { e.Accept(this); if (clonedEvent == null) { throw new InvalidOperationException($"Could not clone event of type '{e.Type}'"); } return clonedEvent; } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.AnchorAlias e) { clonedEvent = new YamlDotNet.Core.Events.AnchorAlias(e.Value, e.Start, e.End); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.StreamStart e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.StreamEnd e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.DocumentStart e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.DocumentEnd e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.Scalar e) { clonedEvent = new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, e.Tag, e.Value, e.Style, e.IsPlainImplicit, e.IsQuotedImplicit, e.Start, e.End); } void IParsingEventVisitor.Visit(SequenceStart e) { clonedEvent = new SequenceStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); } void IParsingEventVisitor.Visit(SequenceEnd e) { clonedEvent = new SequenceEnd(e.Start, e.End); } void IParsingEventVisitor.Visit(MappingStart e) { clonedEvent = new MappingStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); } void IParsingEventVisitor.Visit(MappingEnd e) { clonedEvent = new MappingEnd(e.Start, e.End); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.Comment e) { throw new NotSupportedException(); } } private readonly ParsingEventCollection events; private readonly IParser innerParser; private IEnumerator> iterator; private bool merged; public ParsingEvent? Current => iterator.Current?.Value; public MergingParser(IParser innerParser) { events = new ParsingEventCollection(); merged = false; iterator = events.GetEnumerator(); this.innerParser = innerParser; } public bool MoveNext() { if (!merged) { Merge(); events.CleanMarked(); iterator = events.GetEnumerator(); merged = true; } return iterator.MoveNext(); } private void Merge() { while (innerParser.MoveNext()) { events.Add(innerParser.Current); } foreach (LinkedListNode @event in events) { if (IsMergeToken(@event)) { events.MarkDeleted(@event); if (!HandleMerge(@event.Next)) { throw new SemanticErrorException(@event.Value.Start, @event.Value.End, "Unrecognized merge key pattern"); } } } } private bool HandleMerge(LinkedListNode? node) { if (node == null) { return false; } if (node.Value is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { return HandleAnchorAlias(node, node, anchorAlias); } if (node.Value is SequenceStart) { return HandleSequence(node); } return false; } private bool HandleMergeSequence(LinkedListNode sequenceStart, LinkedListNode? node) { if (node == null) { return false; } if (node.Value is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { return HandleAnchorAlias(sequenceStart, node, anchorAlias); } if (node.Value is SequenceStart) { return HandleSequence(node); } return false; } private static bool IsMergeToken(LinkedListNode node) { if (node.Value is YamlDotNet.Core.Events.Scalar scalar) { return scalar.Value == "<<"; } return false; } private bool HandleAnchorAlias(LinkedListNode node, LinkedListNode anchorNode, YamlDotNet.Core.Events.AnchorAlias anchorAlias) { IEnumerable mappingEvents = GetMappingEvents(anchorAlias.Value); events.AddAfter(node, mappingEvents); events.MarkDeleted(anchorNode); return true; } private bool HandleSequence(LinkedListNode node) { events.MarkDeleted(node); LinkedListNode linkedListNode = node; while (linkedListNode != null) { if (linkedListNode.Value is SequenceEnd) { events.MarkDeleted(linkedListNode); return true; } LinkedListNode next = linkedListNode.Next; HandleMergeSequence(node, next); linkedListNode = next; } return true; } private IEnumerable GetMappingEvents(AnchorName anchor) { ParsingEventCloner parsingEventCloner = new ParsingEventCloner(); int nesting = 0; return (from e in events.FromAnchor(anchor) where !events.IsDeleted(e) select e.Value).TakeWhile((ParsingEvent e) => (nesting += e.NestingIncrease) >= 0).Select(parsingEventCloner.Clone); } } internal class Parser : IParser { private class EventQueue { private readonly Queue highPriorityEvents = new Queue(); private readonly Queue normalPriorityEvents = new Queue(); public int Count => highPriorityEvents.Count + normalPriorityEvents.Count; public void Enqueue(ParsingEvent @event) { EventType type = @event.Type; if (type == EventType.StreamStart || type == EventType.DocumentStart) { highPriorityEvents.Enqueue(@event); } else { normalPriorityEvents.Enqueue(@event); } } public ParsingEvent Dequeue() { if (highPriorityEvents.Count <= 0) { return normalPriorityEvents.Dequeue(); } return highPriorityEvents.Dequeue(); } } private readonly Stack states = new Stack(); private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); private ParserState state; private readonly IScanner scanner; private Token? currentToken; private VersionDirective? version; private readonly EventQueue pendingEvents = new EventQueue(); public ParsingEvent? Current { get; private set; } private Token? GetCurrentToken() { if (currentToken == null) { while (scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (!(currentToken is YamlDotNet.Core.Tokens.Comment comment)) { break; } pendingEvents.Enqueue(new YamlDotNet.Core.Events.Comment(comment.Value, comment.IsInline, comment.Start, comment.End)); scanner.ConsumeCurrent(); } } return currentToken; } public Parser(TextReader input) : this(new Scanner(input)) { } public Parser(IScanner scanner) { this.scanner = scanner; } public bool MoveNext() { if (state == ParserState.StreamEnd) { Current = null; return false; } if (pendingEvents.Count == 0) { pendingEvents.Enqueue(StateMachine()); } Current = pendingEvents.Dequeue(); return true; } private ParsingEvent StateMachine() { return state switch { ParserState.StreamStart => ParseStreamStart(), ParserState.ImplicitDocumentStart => ParseDocumentStart(isImplicit: true), ParserState.DocumentStart => ParseDocumentStart(isImplicit: false), ParserState.DocumentContent => ParseDocumentContent(), ParserState.DocumentEnd => ParseDocumentEnd(), ParserState.BlockNode => ParseNode(isBlock: true, isIndentlessSequence: false), ParserState.BlockNodeOrIndentlessSequence => ParseNode(isBlock: true, isIndentlessSequence: true), ParserState.FlowNode => ParseNode(isBlock: false, isIndentlessSequence: false), ParserState.BlockSequenceFirstEntry => ParseBlockSequenceEntry(isFirst: true), ParserState.BlockSequenceEntry => ParseBlockSequenceEntry(isFirst: false), ParserState.IndentlessSequenceEntry => ParseIndentlessSequenceEntry(), ParserState.BlockMappingFirstKey => ParseBlockMappingKey(isFirst: true), ParserState.BlockMappingKey => ParseBlockMappingKey(isFirst: false), ParserState.BlockMappingValue => ParseBlockMappingValue(), ParserState.FlowSequenceFirstEntry => ParseFlowSequenceEntry(isFirst: true), ParserState.FlowSequenceEntry => ParseFlowSequenceEntry(isFirst: false), ParserState.FlowSequenceEntryMappingKey => ParseFlowSequenceEntryMappingKey(), ParserState.FlowSequenceEntryMappingValue => ParseFlowSequenceEntryMappingValue(), ParserState.FlowSequenceEntryMappingEnd => ParseFlowSequenceEntryMappingEnd(), ParserState.FlowMappingFirstKey => ParseFlowMappingKey(isFirst: true), ParserState.FlowMappingKey => ParseFlowMappingKey(isFirst: false), ParserState.FlowMappingValue => ParseFlowMappingValue(isEmpty: false), ParserState.FlowMappingEmptyValue => ParseFlowMappingValue(isEmpty: true), _ => throw new InvalidOperationException(), }; } private void Skip() { if (currentToken != null) { currentToken = null; scanner.ConsumeCurrent(); } } private YamlDotNet.Core.Events.StreamStart ParseStreamStart() { Token token = GetCurrentToken(); if (!(token is YamlDotNet.Core.Tokens.StreamStart streamStart)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "Did not find expected ."); } Skip(); state = ParserState.ImplicitDocumentStart; return new YamlDotNet.Core.Events.StreamStart(streamStart.Start, streamStart.End); } private ParsingEvent ParseDocumentStart(bool isImplicit) { if (currentToken is VersionDirective) { throw new SyntaxErrorException("While parsing a document start node, could not find document end marker before version directive."); } Token token = GetCurrentToken(); if (!isImplicit) { while (token is YamlDotNet.Core.Tokens.DocumentEnd) { Skip(); token = GetCurrentToken(); } } if (token == null) { throw new SyntaxErrorException("Reached the end of the stream while parsing a document start."); } if (token is YamlDotNet.Core.Tokens.Scalar && (state == ParserState.ImplicitDocumentStart || state == ParserState.DocumentStart)) { isImplicit = true; } if ((isImplicit && !(token is VersionDirective) && !(token is TagDirective) && !(token is YamlDotNet.Core.Tokens.DocumentStart) && !(token is YamlDotNet.Core.Tokens.StreamEnd) && !(token is YamlDotNet.Core.Tokens.DocumentEnd)) || token is BlockMappingStart) { TagDirectiveCollection tags = new TagDirectiveCollection(); ProcessDirectives(tags); states.Push(ParserState.DocumentEnd); state = ParserState.BlockNode; return new YamlDotNet.Core.Events.DocumentStart(null, tags, isImplicit: true, token.Start, token.End); } if (!(token is YamlDotNet.Core.Tokens.StreamEnd) && !(token is YamlDotNet.Core.Tokens.DocumentEnd)) { Mark start = token.Start; TagDirectiveCollection tags2 = new TagDirectiveCollection(); VersionDirective versionDirective = ProcessDirectives(tags2); token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); if (!(token is YamlDotNet.Core.Tokens.DocumentStart)) { throw new SemanticErrorException(token.Start, token.End, "Did not find expected ."); } states.Push(ParserState.DocumentEnd); state = ParserState.DocumentContent; Mark end = token.End; Skip(); return new YamlDotNet.Core.Events.DocumentStart(versionDirective, tags2, isImplicit: false, start, end); } if (token is YamlDotNet.Core.Tokens.DocumentEnd) { Skip(); } state = ParserState.StreamEnd; token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); YamlDotNet.Core.Events.StreamEnd result = new YamlDotNet.Core.Events.StreamEnd(token.Start, token.End); if (scanner.MoveNextWithoutConsuming()) { throw new InvalidOperationException("The scanner should contain no more tokens."); } return result; } private VersionDirective? ProcessDirectives(TagDirectiveCollection tags) { bool flag = false; VersionDirective result = null; while (true) { if (GetCurrentToken() is VersionDirective versionDirective) { if (version != null) { throw new SemanticErrorException(versionDirective.Start, versionDirective.End, "Found duplicate %YAML directive."); } if (versionDirective.Version.Major != 1 || versionDirective.Version.Minor > 3) { throw new SemanticErrorException(versionDirective.Start, versionDirective.End, "Found incompatible YAML document."); } result = (version = versionDirective); flag = true; } else { if (!(GetCurrentToken() is TagDirective tagDirective)) { break; } if (tags.Contains(tagDirective.Handle)) { throw new SemanticErrorException(tagDirective.Start, tagDirective.End, "Found duplicate %TAG directive."); } tags.Add(tagDirective); flag = true; } Skip(); } if (GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart && (version == null || (version.Version.Major == 1 && version.Version.Minor > 1))) { if (GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart && version == null) { version = new VersionDirective(new Version(1, 2)); } flag = true; } AddTagDirectives(tags, Constants.DefaultTagDirectives); if (flag) { tagDirectives.Clear(); } AddTagDirectives(tagDirectives, tags); return result; } private static void AddTagDirectives(TagDirectiveCollection directives, IEnumerable source) { foreach (TagDirective item in source) { if (!directives.Contains(item)) { directives.Add(item); } } } private ParsingEvent ParseDocumentContent() { if (GetCurrentToken() is VersionDirective || GetCurrentToken() is TagDirective || GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart || GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentEnd || GetCurrentToken() is YamlDotNet.Core.Tokens.StreamEnd) { state = states.Pop(); return ProcessEmptyScalar(scanner.CurrentPosition); } return ParseNode(isBlock: true, isIndentlessSequence: false); } private static YamlDotNet.Core.Events.Scalar ProcessEmptyScalar(in Mark position) { return new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, string.Empty, ScalarStyle.Plain, isPlainImplicit: true, isQuotedImplicit: false, position, position); } private ParsingEvent ParseNode(bool isBlock, bool isIndentlessSequence) { if (GetCurrentToken() is Error { Start: var start } error) { throw new SemanticErrorException(in start, error.End, error.Value); } Token token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); if (token is YamlDotNet.Core.Tokens.AnchorAlias anchorAlias) { state = states.Pop(); ParsingEvent result = new YamlDotNet.Core.Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); Skip(); return result; } Mark start2 = token.Start; AnchorName anchor = AnchorName.Empty; TagName tag = TagName.Empty; Anchor anchor2 = null; Tag tag2 = null; while (true) { if (anchor.IsEmpty && token is Anchor anchor3) { anchor2 = anchor3; anchor = anchor3.Value; Skip(); } else { if (!tag.IsEmpty || !(token is Tag tag3)) { if (token is Anchor { Start: var start3 } anchor4) { throw new SemanticErrorException(in start3, anchor4.End, "While parsing a node, found more than one anchor."); } if (token is YamlDotNet.Core.Tokens.AnchorAlias { Start: var start4 } anchorAlias2) { throw new SemanticErrorException(in start4, anchorAlias2.End, "While parsing a node, did not find expected token."); } if (!(token is Error error2)) { break; } if (tag2 != null && anchor2 != null && !anchor.IsEmpty) { return new YamlDotNet.Core.Events.Scalar(anchor, default(TagName), string.Empty, ScalarStyle.Any, isPlainImplicit: false, isQuotedImplicit: false, anchor2.Start, anchor2.End); } throw new SemanticErrorException(error2.Start, error2.End, error2.Value); } tag2 = tag3; if (string.IsNullOrEmpty(tag3.Handle)) { tag = new TagName(tag3.Suffix); } else { if (!tagDirectives.Contains(tag3.Handle)) { throw new SemanticErrorException(tag3.Start, tag3.End, "While parsing a node, found undefined tag handle."); } tag = new TagName(tagDirectives[tag3.Handle].Prefix + tag3.Suffix); } Skip(); } token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); } bool isEmpty = tag.IsEmpty; if (isIndentlessSequence && GetCurrentToken() is BlockEntry) { state = ParserState.IndentlessSequenceEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Block, start2, token.End); } if (token is YamlDotNet.Core.Tokens.Scalar scalar) { bool isPlainImplicit = false; bool isQuotedImplicit = false; if ((scalar.Style == ScalarStyle.Plain && tag.IsEmpty) || tag.IsNonSpecific) { isPlainImplicit = true; } else if (tag.IsEmpty) { isQuotedImplicit = true; } state = states.Pop(); Skip(); ParsingEvent result2 = new YamlDotNet.Core.Events.Scalar(anchor, tag, scalar.Value, scalar.Style, isPlainImplicit, isQuotedImplicit, start2, scalar.End, scalar.IsKey); if (!anchor.IsEmpty && scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (currentToken is Error) { Error error3 = currentToken as Error; throw new SemanticErrorException(error3.Start, error3.End, error3.Value); } } if (state == ParserState.FlowMappingKey && !(scanner.Current is FlowMappingEnd) && scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (currentToken != null && !(currentToken is FlowEntry) && !(currentToken is FlowMappingEnd)) { throw new SemanticErrorException(currentToken.Start, currentToken.End, "While parsing a flow mapping, did not find expected ',' or '}'."); } } return result2; } if (token is FlowSequenceStart flowSequenceStart) { state = ParserState.FlowSequenceFirstEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Flow, start2, flowSequenceStart.End); } if (token is FlowMappingStart flowMappingStart) { state = ParserState.FlowMappingFirstKey; return new MappingStart(anchor, tag, isEmpty, MappingStyle.Flow, start2, flowMappingStart.End); } if (isBlock) { if (token is BlockSequenceStart blockSequenceStart) { state = ParserState.BlockSequenceFirstEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Block, start2, blockSequenceStart.End); } if (token is BlockMappingStart blockMappingStart) { state = ParserState.BlockMappingFirstKey; return new MappingStart(anchor, tag, isEmpty, MappingStyle.Block, start2, blockMappingStart.End); } } if (!anchor.IsEmpty || !tag.IsEmpty) { state = states.Pop(); return new YamlDotNet.Core.Events.Scalar(anchor, tag, string.Empty, ScalarStyle.Plain, isEmpty, isQuotedImplicit: false, start2, token.End); } throw new SemanticErrorException(token.Start, token.End, "While parsing a node, did not find expected node content."); } private YamlDotNet.Core.Events.DocumentEnd ParseDocumentEnd() { Token token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document end"); bool isImplicit = true; Mark start = token.Start; Mark end = start; if (token is YamlDotNet.Core.Tokens.DocumentEnd) { end = token.End; Skip(); isImplicit = false; } else if (!(currentToken is YamlDotNet.Core.Tokens.StreamEnd) && !(currentToken is YamlDotNet.Core.Tokens.DocumentStart) && !(currentToken is FlowSequenceEnd) && !(currentToken is VersionDirective) && (!(Current is YamlDotNet.Core.Events.Scalar) || !(currentToken is Error))) { throw new SemanticErrorException(in start, in end, "Did not find expected ."); } if (version != null && version.Version.Major == 1 && version.Version.Minor > 1) { version = null; } state = ParserState.DocumentStart; return new YamlDotNet.Core.Events.DocumentEnd(isImplicit, start, end); } private ParsingEvent ParseBlockSequenceEntry(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (token is BlockEntry { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is BlockEntry) && !(token is BlockEnd)) { states.Push(ParserState.BlockSequenceEntry); return ParseNode(isBlock: true, isIndentlessSequence: false); } state = ParserState.BlockSequenceEntry; return ProcessEmptyScalar(in position); } if (token is BlockEnd blockEnd) { state = states.Pop(); ParsingEvent result = new SequenceEnd(blockEnd.Start, blockEnd.End); Skip(); return result; } throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a block collection, did not find expected '-' indicator."); } private ParsingEvent ParseIndentlessSequenceEntry() { Token token = GetCurrentToken(); if (token is BlockEntry { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is BlockEntry) && !(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.IndentlessSequenceEntry); return ParseNode(isBlock: true, isIndentlessSequence: false); } state = ParserState.IndentlessSequenceEntry; return ProcessEmptyScalar(in position); } state = states.Pop(); return new SequenceEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseBlockMappingKey(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (token is Key { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.BlockMappingValue); return ParseNode(isBlock: true, isIndentlessSequence: true); } state = ParserState.BlockMappingValue; return ProcessEmptyScalar(in position); } if (token is Value value) { Skip(); return ProcessEmptyScalar(value.End); } if (token is YamlDotNet.Core.Tokens.AnchorAlias anchorAlias) { Skip(); return new YamlDotNet.Core.Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); } if (token is BlockEnd blockEnd) { state = states.Pop(); ParsingEvent result = new MappingEnd(blockEnd.Start, blockEnd.End); Skip(); return result; } if (GetCurrentToken() is Error { Start: var start } error) { throw new SyntaxErrorException(in start, error.End, error.Value); } throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a block mapping, did not find expected key."); } private ParsingEvent ParseBlockMappingValue() { Token token = GetCurrentToken(); if (token is Value { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.BlockMappingKey); return ParseNode(isBlock: true, isIndentlessSequence: true); } state = ParserState.BlockMappingKey; return ProcessEmptyScalar(in position); } if (token is Error { Start: var start } error) { throw new SemanticErrorException(in start, error.End, error.Value); } state = ParserState.BlockMappingKey; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } private ParsingEvent ParseFlowSequenceEntry(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); ParsingEvent result; if (!(token is FlowSequenceEnd)) { if (!isFirst) { if (!(token is FlowEntry)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a flow sequence, did not find expected ',' or ']'."); } Skip(); token = GetCurrentToken(); } if (token is Key) { state = ParserState.FlowSequenceEntryMappingKey; result = new MappingStart(AnchorName.Empty, TagName.Empty, isImplicit: true, MappingStyle.Flow); Skip(); return result; } if (!(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntry); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = states.Pop(); result = new SequenceEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); Skip(); return result; } private ParsingEvent ParseFlowSequenceEntryMappingKey() { Token token = GetCurrentToken(); if (!(token is Value) && !(token is FlowEntry) && !(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntryMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } Mark position = token?.End ?? Mark.Empty; Skip(); state = ParserState.FlowSequenceEntryMappingValue; return ProcessEmptyScalar(in position); } private ParsingEvent ParseFlowSequenceEntryMappingValue() { Token token = GetCurrentToken(); if (token is Value) { Skip(); token = GetCurrentToken(); if (!(token is FlowEntry) && !(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntryMappingEnd); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = ParserState.FlowSequenceEntryMappingEnd; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } private MappingEnd ParseFlowSequenceEntryMappingEnd() { state = ParserState.FlowSequenceEntry; Token token = GetCurrentToken(); return new MappingEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseFlowMappingKey(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (!(token is FlowMappingEnd)) { if (!isFirst) { if (token is FlowEntry) { Skip(); token = GetCurrentToken(); } else if (!(token is YamlDotNet.Core.Tokens.Scalar)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a flow mapping, did not find expected ',' or '}'."); } } if (token is Key) { Skip(); token = GetCurrentToken(); if (!(token is Value) && !(token is FlowEntry) && !(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } state = ParserState.FlowMappingValue; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } if (token is YamlDotNet.Core.Tokens.Scalar) { states.Push(ParserState.FlowMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } if (!(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingEmptyValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = states.Pop(); Skip(); return new MappingEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseFlowMappingValue(bool isEmpty) { Token token = GetCurrentToken(); if (!isEmpty && token is Value) { Skip(); token = GetCurrentToken(); if (!(token is FlowEntry) && !(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingKey); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = ParserState.FlowMappingKey; if (!isEmpty && token is YamlDotNet.Core.Tokens.Scalar scalar) { Skip(); return new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, scalar.Value, scalar.Style, isPlainImplicit: false, isQuotedImplicit: false, token.Start, scalar.End); } return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } } internal static class ParserExtensions { public static T Consume(this IParser parser) where T : ParsingEvent { T result = parser.Require(); parser.MoveNext(); return result; } public static bool TryConsume(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent { if (parser.Accept(out @event)) { parser.MoveNext(); return true; } return false; } public static T Require(this IParser parser) where T : ParsingEvent { if (!parser.Accept(out var @event)) { ParsingEvent current = parser.Current; if (current == null) { throw new YamlException("Expected '" + typeof(T).Name + "', got nothing."); } throw new YamlException(current.Start, current.End, $"Expected '{typeof(T).Name}', got '{current.GetType().Name}' (at {current.Start})."); } return @event; } public static bool Accept(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent { if (parser.Current == null && !parser.MoveNext()) { throw new EndOfStreamException(); } if (parser.Current is T val) { @event = val; return true; } @event = null; return false; } public static void SkipThisAndNestedEvents(this IParser parser) { int num = 0; do { ParsingEvent parsingEvent = parser.Consume(); num += parsingEvent.NestingIncrease; } while (num > 0); } [Obsolete("Please use Consume() instead")] public static T Expect(this IParser parser) where T : ParsingEvent { return parser.Consume(); } [Obsolete("Please use TryConsume(out var evt) instead")] [return: MaybeNull] public static T? Allow(this IParser parser) where T : ParsingEvent { if (!parser.TryConsume(out var @event)) { return null; } return @event; } [Obsolete("Please use Accept(out var evt) instead")] [return: MaybeNull] public static T? Peek(this IParser parser) where T : ParsingEvent { if (!parser.Accept(out var @event)) { return null; } return @event; } [Obsolete("Please use TryConsume(out var evt) or Accept(out var evt) instead")] public static bool Accept(this IParser parser) where T : ParsingEvent { T @event; return parser.Accept(out @event); } public static bool TryFindMappingEntry(this IParser parser, Func selector, [MaybeNullWhen(false)] out YamlDotNet.Core.Events.Scalar? key, [MaybeNullWhen(false)] out ParsingEvent? value) { if (parser.TryConsume(out var _)) { while (parser.Current != null) { ParsingEvent current = parser.Current; if (!(current is YamlDotNet.Core.Events.Scalar scalar)) { if (current is MappingStart || current is SequenceStart) { parser.SkipThisAndNestedEvents(); } else { parser.MoveNext(); } continue; } bool flag = selector(scalar); parser.MoveNext(); if (flag) { value = parser.Current; key = scalar; return true; } parser.SkipThisAndNestedEvents(); } } key = null; value = null; return false; } } internal enum ParserState { StreamStart, StreamEnd, ImplicitDocumentStart, DocumentStart, DocumentContent, DocumentEnd, BlockNode, BlockNodeOrIndentlessSequence, FlowNode, BlockSequenceFirstEntry, BlockSequenceEntry, IndentlessSequenceEntry, BlockMappingFirstKey, BlockMappingKey, BlockMappingValue, FlowSequenceFirstEntry, FlowSequenceEntry, FlowSequenceEntryMappingKey, FlowSequenceEntryMappingValue, FlowSequenceEntryMappingEnd, FlowMappingFirstKey, FlowMappingKey, FlowMappingValue, FlowMappingEmptyValue } internal sealed class RecursionLevel { private int current; public int Maximum { get; } public RecursionLevel(int maximum) { Maximum = maximum; } public void Increment() { if (!TryIncrement()) { throw new MaximumRecursionLevelReachedException("Maximum level of recursion reached"); } } public bool TryIncrement() { if (current < Maximum) { current++; return true; } return false; } public void Decrement() { if (current == 0) { throw new InvalidOperationException("Attempted to decrement RecursionLevel to a negative value"); } current--; } } internal enum ScalarStyle { Any, Plain, SingleQuoted, DoubleQuoted, Literal, Folded, ForcePlain } internal class Scanner : IScanner { private const int MaxVersionNumberLength = 9; private static readonly SortedDictionary SimpleEscapeCodes = new SortedDictionary { { '0', '\0' }, { 'a', '\a' }, { 'b', '\b' }, { 't', '\t' }, { '\t', '\t' }, { 'n', '\n' }, { 'v', '\v' }, { 'f', '\f' }, { 'r', '\r' }, { 'e', '\u001b' }, { ' ', ' ' }, { '"', '"' }, { '\\', '\\' }, { '/', '/' }, { 'N', '\u0085' }, { '_', '\u00a0' }, { 'L', '\u2028' }, { 'P', '\u2029' } }; private readonly Stack indents = new Stack(); private readonly InsertionQueue tokens = new InsertionQueue(); private readonly Stack simpleKeys = new Stack(); private readonly CharacterAnalyzer analyzer; private readonly Cursor cursor; private bool streamStartProduced; private bool streamEndProduced; private bool plainScalarFollowedByComment; private bool flowCollectionFetched; private bool startFlowCollectionFetched; private long indent = -1L; private bool flowScalarFetched; private bool simpleKeyAllowed; private int flowLevel; private int tokensParsed; private bool tokenAvailable; private Token? previous; private Anchor? previousAnchor; private YamlDotNet.Core.Tokens.Scalar? lastScalar; private readonly int maxKeySize; private static readonly byte[] EmptyBytes = Array.Empty(); public bool SkipComments { get; private set; } public Token? Current { get; private set; } public Mark CurrentPosition => cursor.Mark(); private bool IsDocumentStart() { if (!analyzer.EndOfInput && cursor.LineOffset == 0L && analyzer.Check('-') && analyzer.Check('-', 1) && analyzer.Check('-', 2)) { return analyzer.IsWhiteBreakOrZero(3); } return false; } private bool IsDocumentEnd() { if (!analyzer.EndOfInput && cursor.LineOffset == 0L && analyzer.Check('.') && analyzer.Check('.', 1) && analyzer.Check('.', 2)) { return analyzer.IsWhiteBreakOrZero(3); } return false; } private bool IsDocumentIndicator() { if (!IsDocumentStart()) { return IsDocumentEnd(); } return true; } public Scanner(TextReader input, bool skipComments = true) : this(input, skipComments, 1024) { } public Scanner(TextReader input, bool skipComments, int maxKeySize) { analyzer = new CharacterAnalyzer(new LookAheadBuffer(input, 1024)); cursor = new Cursor(); SkipComments = skipComments; this.maxKeySize = maxKeySize; } public bool MoveNext() { if (Current != null) { ConsumeCurrent(); } return MoveNextWithoutConsuming(); } public bool MoveNextWithoutConsuming() { if (!tokenAvailable && !streamEndProduced) { FetchMoreTokens(); } if (tokens.Count > 0) { Current = tokens.Dequeue(); tokenAvailable = false; return true; } Current = null; return false; } public void ConsumeCurrent() { tokensParsed++; tokenAvailable = false; previous = Current; Current = null; } private char ReadCurrentCharacter() { char result = analyzer.Peek(0); Skip(); return result; } private char ReadLine() { if (analyzer.Check("\r\n\u0085")) { SkipLine(); return '\n'; } char result = analyzer.Peek(0); SkipLine(); return result; } private void FetchMoreTokens() { while (true) { bool flag = false; if (tokens.Count == 0) { flag = true; } else { foreach (SimpleKey simpleKey in simpleKeys) { if (simpleKey.IsPossible && simpleKey.TokenNumber == tokensParsed) { flag = true; break; } } } if (!flag) { break; } FetchNextToken(); } tokenAvailable = true; } private static bool StartsWith(StringBuilder what, char start) { if (what.Length > 0) { return what[0] == start; } return false; } private void StaleSimpleKeys() { foreach (SimpleKey simpleKey in simpleKeys) { if (simpleKey.IsPossible && (simpleKey.Line < cursor.Line || simpleKey.Index + maxKeySize < cursor.Index)) { if (simpleKey.IsRequired) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning a simple key, could not find expected ':'.", mark, mark)); } simpleKey.MarkAsImpossible(); } } } private void FetchNextToken() { if (!streamStartProduced) { FetchStreamStart(); return; } ScanToNextToken(); StaleSimpleKeys(); UnrollIndent(cursor.LineOffset); analyzer.Buffer.Cache(4); if (analyzer.Buffer.EndOfInput) { lastScalar = null; FetchStreamEnd(); } if (cursor.LineOffset == 0L && analyzer.Check('%')) { lastScalar = null; FetchDirective(); return; } if (IsDocumentStart()) { lastScalar = null; FetchDocumentIndicator(isStartToken: true); return; } if (IsDocumentEnd()) { lastScalar = null; FetchDocumentIndicator(isStartToken: false); return; } if (analyzer.Check('[')) { lastScalar = null; FetchFlowCollectionStart(isSequenceToken: true); return; } if (analyzer.Check('{')) { lastScalar = null; FetchFlowCollectionStart(isSequenceToken: false); return; } if (analyzer.Check(']')) { lastScalar = null; FetchFlowCollectionEnd(isSequenceToken: true); return; } if (analyzer.Check('}')) { lastScalar = null; FetchFlowCollectionEnd(isSequenceToken: false); return; } if (analyzer.Check(',')) { lastScalar = null; FetchFlowEntry(); return; } if (analyzer.Check('-')) { if (analyzer.IsWhiteBreakOrZero(1)) { FetchBlockEntry(); return; } if (flowLevel > 0 && analyzer.Check(",[]{}", 1)) { tokens.Enqueue(new Error("Invalid key indicator format.", cursor.Mark(), cursor.Mark())); } } if (analyzer.Check('?') && (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && analyzer.IsWhiteBreakOrZero(1)) { FetchKey(); } else if (analyzer.Check(':') && (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && (!simpleKeyAllowed || flowLevel <= 0) && (!flowScalarFetched || !analyzer.Check(':', 1)) && (analyzer.IsWhiteBreakOrZero(1) || analyzer.Check(',', 1) || flowScalarFetched || flowCollectionFetched || startFlowCollectionFetched)) { if (lastScalar != null) { lastScalar.IsKey = true; lastScalar = null; } FetchValue(); } else if (analyzer.Check('*')) { FetchAnchor(isAlias: true); } else if (analyzer.Check('&')) { FetchAnchor(isAlias: false); } else if (analyzer.Check('!')) { FetchTag(); } else if (analyzer.Check('|') && flowLevel == 0) { FetchBlockScalar(isLiteral: true); } else if (analyzer.Check('>') && flowLevel == 0) { FetchBlockScalar(isLiteral: false); } else if (analyzer.Check('\'')) { FetchQuotedScalar(isSingleQuoted: true); } else if (analyzer.Check('"')) { FetchQuotedScalar(isSingleQuoted: false); } else if ((!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("-?:,[]{}#&*!|>'\"%@`")) || (analyzer.Check('-') && !analyzer.IsWhite(1)) || (analyzer.Check("?:") && !analyzer.IsWhiteBreakOrZero(1)) || (simpleKeyAllowed && flowLevel > 0)) { if (plainScalarFollowedByComment) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning plain scalar, found a comment between adjacent scalars.", mark, mark)); } if ((flowScalarFetched || (flowCollectionFetched && !startFlowCollectionFetched)) && analyzer.Check(':')) { Skip(); } flowScalarFetched = false; flowCollectionFetched = false; startFlowCollectionFetched = false; plainScalarFollowedByComment = false; FetchPlainScalar(); } else { if (simpleKeyAllowed && indent >= cursor.LineOffset && analyzer.IsTab()) { throw new SyntaxErrorException("While scanning a mapping, found invalid tab as indentation."); } if (!analyzer.IsWhiteBreakOrZero()) { Mark start = cursor.Mark(); Skip(); throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning for the next token, found character that cannot start any token."); } Skip(); } } private bool CheckWhiteSpace() { if (!analyzer.Check(' ')) { if (flowLevel > 0 || !simpleKeyAllowed) { return analyzer.Check('\t'); } return false; } return true; } private void Skip() { cursor.Skip(); analyzer.Buffer.Skip(1); } private void SkipLine() { if (analyzer.IsCrLf()) { cursor.SkipLineByOffset(2); analyzer.Buffer.Skip(2); } else if (analyzer.IsBreak()) { cursor.SkipLineByOffset(1); analyzer.Buffer.Skip(1); } else if (!analyzer.IsZero()) { throw new InvalidOperationException("Not at a break."); } } private void ScanToNextToken() { while (true) { if (CheckWhiteSpace()) { Skip(); continue; } ProcessComment(); if (analyzer.IsBreak()) { SkipLine(); if (flowLevel == 0) { simpleKeyAllowed = true; } continue; } break; } } private void ProcessComment() { if (!analyzer.Check('#')) { return; } Mark start = cursor.Mark(); Skip(); while (analyzer.IsSpace()) { Skip(); } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (!analyzer.IsBreakOrZero()) { builder.Append(ReadCurrentCharacter()); } if (!SkipComments) { bool isInline = previous != null && previous.End.Line == start.Line && previous.End.Column != 1 && !(previous is YamlDotNet.Core.Tokens.StreamStart); tokens.Enqueue(new YamlDotNet.Core.Tokens.Comment(builder.ToString(), isInline, start, cursor.Mark())); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchStreamStart() { simpleKeys.Push(new SimpleKey()); simpleKeyAllowed = true; streamStartProduced = true; Mark start = cursor.Mark(); tokens.Enqueue(new YamlDotNet.Core.Tokens.StreamStart(in start, in start)); } private void UnrollIndent(long column) { if (flowLevel == 0) { while (indent > column) { Mark start = cursor.Mark(); tokens.Enqueue(new BlockEnd(in start, in start)); indent = indents.Pop(); } } } private void FetchStreamEnd() { cursor.ForceSkipLineAfterNonBreak(); UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; streamEndProduced = true; Mark start = cursor.Mark(); tokens.Enqueue(new YamlDotNet.Core.Tokens.StreamEnd(in start, in start)); } private void FetchDirective() { UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; Token token = ScanDirective(); if (token != null) { tokens.Enqueue(token); } } private Token? ScanDirective() { Mark start = cursor.Mark(); Skip(); string text = ScanDirectiveName(in start); Token result; if (!(text == "YAML")) { if (!(text == "TAG")) { while (!analyzer.EndOfInput && !analyzer.Check('#') && !analyzer.IsBreak()) { Skip(); } return null; } result = ScanTagDirectiveValue(in start); } else { if (!(previous is YamlDotNet.Core.Tokens.DocumentStart) && !(previous is YamlDotNet.Core.Tokens.StreamStart) && !(previous is YamlDotNet.Core.Tokens.DocumentEnd)) { throw new SemanticErrorException(in start, cursor.Mark(), "While scanning a version directive, did not find preceding ."); } result = ScanVersionDirectiveValue(in start); } while (analyzer.IsWhite()) { Skip(); } ProcessComment(); if (!analyzer.IsBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, did not find expected comment or line break."); } if (analyzer.IsBreak()) { SkipLine(); } return result; } private void FetchDocumentIndicator(bool isStartToken) { UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; Mark start = cursor.Mark(); Skip(); Skip(); Skip(); if (isStartToken) { tokens.Enqueue(new YamlDotNet.Core.Tokens.DocumentStart(in start, cursor.Mark())); return; } Token token = null; while (!analyzer.EndOfInput && !analyzer.IsBreak() && !analyzer.Check('#')) { if (!analyzer.IsWhite()) { token = new Error("While scanning a document end, found invalid content after '...' marker.", start, cursor.Mark()); break; } Skip(); } tokens.Enqueue(new YamlDotNet.Core.Tokens.DocumentEnd(in start, in start)); if (token != null) { tokens.Enqueue(token); } } private void FetchFlowCollectionStart(bool isSequenceToken) { SaveSimpleKey(); IncreaseFlowLevel(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); Token item = ((!isSequenceToken) ? ((Token)new FlowMappingStart(in start, in start)) : ((Token)new FlowSequenceStart(in start, in start))); tokens.Enqueue(item); startFlowCollectionFetched = true; } private void IncreaseFlowLevel() { simpleKeys.Push(new SimpleKey()); flowLevel++; } private void FetchFlowCollectionEnd(bool isSequenceToken) { RemoveSimpleKey(); DecreaseFlowLevel(); simpleKeyAllowed = false; Mark start = cursor.Mark(); Skip(); Token token = null; Token item; if (isSequenceToken) { if (analyzer.Check('#')) { token = new Error("While scanning a flow sequence end, found invalid comment after ']'.", start, start); } item = new FlowSequenceEnd(in start, in start); } else { item = new FlowMappingEnd(in start, in start); } tokens.Enqueue(item); if (token != null) { tokens.Enqueue(token); } flowCollectionFetched = true; } private void DecreaseFlowLevel() { if (flowLevel > 0) { flowLevel--; simpleKeys.Pop(); } } private void FetchFlowEntry() { RemoveSimpleKey(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); Mark end = cursor.Mark(); if (analyzer.Check('#')) { tokens.Enqueue(new Error("While scanning a flow entry, found invalid comment after comma.", start, end)); } else { tokens.Enqueue(new FlowEntry(in start, in end)); } } private void FetchBlockEntry() { if (flowLevel == 0) { if (!simpleKeyAllowed) { if (previousAnchor != null && previousAnchor.End.Line == cursor.Line) { throw new SemanticErrorException(previousAnchor.Start, previousAnchor.End, "Anchor before sequence entry on same line is not allowed."); } Mark mark = cursor.Mark(); tokens.Enqueue(new Error("Block sequence entries are not allowed in this context.", mark, mark)); } RollIndent(cursor.LineOffset, -1, isSequence: true, cursor.Mark()); } RemoveSimpleKey(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); tokens.Enqueue(new BlockEntry(in start, cursor.Mark())); } private void FetchKey() { if (flowLevel == 0) { if (!simpleKeyAllowed) { Mark start = cursor.Mark(); throw new SyntaxErrorException(in start, in start, "Mapping keys are not allowed in this context."); } RollIndent(cursor.LineOffset, -1, isSequence: false, cursor.Mark()); } RemoveSimpleKey(); simpleKeyAllowed = flowLevel == 0; Mark start2 = cursor.Mark(); Skip(); tokens.Enqueue(new Key(in start2, cursor.Mark())); } private void FetchValue() { SimpleKey simpleKey = simpleKeys.Peek(); if (simpleKey.IsPossible) { tokens.Insert(simpleKey.TokenNumber - tokensParsed, new Key(simpleKey.Mark, simpleKey.Mark)); RollIndent(simpleKey.LineOffset, simpleKey.TokenNumber, isSequence: false, simpleKey.Mark); simpleKey.MarkAsImpossible(); simpleKeyAllowed = false; } else { bool flag = flowLevel == 0; if (flag) { if (!simpleKeyAllowed) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("Mapping values are not allowed in this context.", mark, mark)); return; } RollIndent(cursor.LineOffset, -1, isSequence: false, cursor.Mark()); if (cursor.LineOffset == 0L && simpleKey.LineOffset == 0L) { tokens.Insert(tokens.Count, new Key(simpleKey.Mark, simpleKey.Mark)); flag = false; } } simpleKeyAllowed = flag; } Mark start = cursor.Mark(); Skip(); tokens.Enqueue(new Value(in start, cursor.Mark())); } private void RollIndent(long column, int number, bool isSequence, Mark position) { if (flowLevel <= 0 && indent < column) { indents.Push(indent); indent = column; Token item = ((!isSequence) ? ((Token)new BlockMappingStart(in position, in position)) : ((Token)new BlockSequenceStart(in position, in position))); if (number == -1) { tokens.Enqueue(item); } else { tokens.Insert(number - tokensParsed, item); } } } private void FetchAnchor(bool isAlias) { SaveSimpleKey(); simpleKeyAllowed = false; tokens.Enqueue(ScanAnchor(isAlias)); } private Token ScanAnchor(bool isAlias) { Mark start = cursor.Mark(); Skip(); bool flag = false; if (isAlias) { SimpleKey simpleKey = simpleKeys.Peek(); flag = simpleKey.IsRequired && simpleKey.IsPossible; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("[]{},") && (!flag || !analyzer.Check(':') || !analyzer.IsWhiteBreakOrZero(1))) { builder.Append(ReadCurrentCharacter()); } if (builder.Length == 0 || (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("?:,]}%@`"))) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning an anchor or alias, found value containing disallowed: []{},"); } AnchorName value = new AnchorName(builder.ToString()); if (isAlias) { return new YamlDotNet.Core.Tokens.AnchorAlias(value, start, cursor.Mark()); } return previousAnchor = new Anchor(value, start, cursor.Mark()); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchTag() { SaveSimpleKey(); simpleKeyAllowed = false; tokens.Enqueue(ScanTag()); } private Tag ScanTag() { Mark start = cursor.Mark(); string text; string text2; if (analyzer.Check('<', 1)) { text = string.Empty; Skip(); Skip(); text2 = ScanTagUri(null, start); if (!analyzer.Check('>')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find the expected '>'."); } Skip(); } else { string text3 = ScanTagHandle(isDirective: false, start); if (text3.Length > 1 && text3[0] == '!' && text3[text3.Length - 1] == '!') { text = text3; text2 = ScanTagUri(null, start); } else { text2 = ScanTagUri(text3, start); text = "!"; if (text2.Length == 0) { text2 = text; text = string.Empty; } } } if (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check(',')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find expected whitespace, comma or line break."); } return new Tag(text, text2, start, cursor.Mark()); } private void FetchBlockScalar(bool isLiteral) { SaveSimpleKey(); simpleKeyAllowed = true; tokens.Enqueue(ScanBlockScalar(isLiteral)); } private YamlDotNet.Core.Tokens.Scalar ScanBlockScalar(bool isLiteral) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; int num = 0; int num2 = 0; long currentIndent = 0L; bool flag = false; bool? isFirstLine = null; Mark start = cursor.Mark(); Skip(); if (analyzer.Check("+-")) { num = (analyzer.Check('+') ? 1 : (-1)); Skip(); if (analyzer.IsDigit()) { if (analyzer.Check('0')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); } num2 = analyzer.AsDigit(); Skip(); } } else if (analyzer.IsDigit()) { if (analyzer.Check('0')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); } num2 = analyzer.AsDigit(); Skip(); if (analyzer.Check("+-")) { num = (analyzer.Check('+') ? 1 : (-1)); Skip(); } } if (analyzer.Check('#')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found a comment without whtespace after '>' indicator."); } while (analyzer.IsWhite()) { Skip(); } ProcessComment(); if (!analyzer.IsBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, did not find expected comment or line break."); } if (analyzer.IsBreak()) { SkipLine(); if (!isFirstLine.HasValue) { isFirstLine = true; } else if (isFirstLine == true) { isFirstLine = false; } } Mark end = cursor.Mark(); if (num2 != 0) { currentIndent = ((indent >= 0) ? (indent + num2) : num2); } currentIndent = ScanBlockScalarBreaks(currentIndent, builder3, isLiteral, ref end, ref isFirstLine); isFirstLine = false; while (cursor.LineOffset == currentIndent && !analyzer.IsZero() && !IsDocumentEnd()) { bool flag2 = analyzer.IsWhite(); if (!isLiteral && StartsWith(builder2, '\n') && !flag && !flag2) { if (builder3.Length == 0) { builder.Append(' '); } builder2.Length = 0; } else { builder.Append((object?)builder2); builder2.Length = 0; } builder.Append((object?)builder3); builder3.Length = 0; flag = analyzer.IsWhite(); while (!analyzer.IsBreakOrZero()) { builder.Append(ReadCurrentCharacter()); } char c = ReadLine(); if (c != 0) { builder2.Append(c); } currentIndent = ScanBlockScalarBreaks(currentIndent, builder3, isLiteral, ref end, ref isFirstLine); } if (num != -1) { builder.Append((object?)builder2); } if (num == 1) { builder.Append((object?)builder3); } ScalarStyle style = (isLiteral ? ScalarStyle.Literal : ScalarStyle.Folded); return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), style, start, end); } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private long ScanBlockScalarBreaks(long currentIndent, StringBuilder breaks, bool isLiteral, ref Mark end, ref bool? isFirstLine) { long num = 0L; long num2 = -1L; end = cursor.Mark(); while (true) { if ((currentIndent == 0L || cursor.LineOffset < currentIndent) && analyzer.IsSpace()) { Skip(); continue; } if (cursor.LineOffset > num) { num = cursor.LineOffset; } if (!analyzer.IsBreak()) { break; } if (isFirstLine == true) { isFirstLine = false; num2 = cursor.LineOffset; } breaks.Append(ReadLine()); end = cursor.Mark(); } if (isLiteral && isFirstLine == true) { long num3 = cursor.LineOffset; int num4 = 0; while (!analyzer.IsBreak(num4) && analyzer.IsSpace(num4)) { num4++; num3++; } if (analyzer.IsBreak(num4) && num3 > cursor.LineOffset) { isFirstLine = false; num2 = num3; } } if (isLiteral && num2 > 1 && currentIndent < num2 - 1) { throw new SemanticErrorException(in end, cursor.Mark(), "While scanning a literal block scalar, found extra spaces in first line."); } if (!isLiteral && num > cursor.LineOffset && num2 > -1) { throw new SemanticErrorException(in end, cursor.Mark(), "While scanning a literal block scalar, found more spaces in lines above first content line."); } if (currentIndent == 0L && (cursor.LineOffset > 0 || indent > -1)) { currentIndent = Math.Max(num, Math.Max(indent + 1, 1L)); } return currentIndent; } private void FetchQuotedScalar(bool isSingleQuoted) { SaveSimpleKey(); simpleKeyAllowed = false; flowScalarFetched = flowLevel > 0; YamlDotNet.Core.Tokens.Scalar item = ScanFlowScalar(isSingleQuoted); tokens.Enqueue(item); lastScalar = item; if (!isSingleQuoted && analyzer.Check('#')) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning a flow sequence end, found invalid comment after double-quoted scalar.", mark, mark)); } } private YamlDotNet.Core.Tokens.Scalar ScanFlowScalar(bool isSingleQuoted) { Mark start = cursor.Mark(); Skip(); StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; StringBuilderPool.BuilderWrapper builderWrapper4 = StringBuilderPool.Rent(); try { StringBuilder builder4 = builderWrapper4.Builder; bool flag = false; while (true) { if (IsDocumentIndicator()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unexpected document indicator."); } if (analyzer.IsZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unexpected end of stream."); } if (flag && !isSingleQuoted && indent >= cursor.LineOffset) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a multi-line double-quoted scalar, found wrong indentation."); } flag = false; while (!analyzer.IsWhiteBreakOrZero()) { if (isSingleQuoted && analyzer.Check('\'') && analyzer.Check('\'', 1)) { builder.Append('\''); Skip(); Skip(); continue; } if (analyzer.Check(isSingleQuoted ? '\'' : '"')) { break; } if (!isSingleQuoted && analyzer.Check('\\') && analyzer.IsBreak(1)) { Skip(); SkipLine(); flag = true; break; } if (!isSingleQuoted && analyzer.Check('\\')) { int num = 0; char c = analyzer.Peek(1); switch (c) { case 'x': num = 2; break; case 'u': num = 4; break; case 'U': num = 8; break; default: { if (SimpleEscapeCodes.TryGetValue(c, out var value)) { builder.Append(value); break; } throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unknown escape character."); } } Skip(); Skip(); if (num <= 0) { continue; } int num2 = 0; for (int i = 0; i < num; i++) { if (!analyzer.IsHex(i)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); } num2 = (num2 << 4) + analyzer.AsHex(i); } if (num2 >= 55296 && num2 <= 57343) { for (int j = 0; j < num; j++) { Skip(); } if (analyzer.Peek(0) != '\\' || (analyzer.Peek(1) != 'u' && analyzer.Peek(1) != 'U')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode surrogates."); } Skip(); num = ((analyzer.Peek(0) != 'u') ? 8 : 4); Skip(); int num3 = 0; for (int k = 0; k < num; k++) { if (!analyzer.IsHex(0)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); } num3 = (num3 << 4) + analyzer.AsHex(k); } for (int l = 0; l < num; l++) { Skip(); } num2 = char.ConvertToUtf32((char)num2, (char)num3); } else { if (num2 > 1114111) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode character escape code."); } for (int m = 0; m < num; m++) { Skip(); } } builder.Append(char.ConvertFromUtf32(num2)); } else { builder.Append(ReadCurrentCharacter()); } } if (analyzer.Check(isSingleQuoted ? '\'' : '"')) { break; } while (analyzer.IsWhite() || analyzer.IsBreak()) { if (analyzer.IsWhite()) { if (!flag) { builder2.Append(ReadCurrentCharacter()); } else { Skip(); } } else if (!flag) { builder2.Length = 0; builder3.Append(ReadLine()); flag = true; } else { builder4.Append(ReadLine()); } } if (flag) { if (StartsWith(builder3, '\n')) { if (builder4.Length == 0) { builder.Append(' '); } else { builder.Append((object?)builder4); } } else { builder.Append((object?)builder3); builder.Append((object?)builder4); } builder3.Length = 0; builder4.Length = 0; } else { builder.Append((object?)builder2); builder2.Length = 0; } } Skip(); return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), isSingleQuoted ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted, start, cursor.Mark()); } finally { ((IDisposable)builderWrapper4/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchPlainScalar() { SaveSimpleKey(); simpleKeyAllowed = false; bool isMultiline = false; YamlDotNet.Core.Tokens.Scalar item = (lastScalar = ScanPlainScalar(ref isMultiline)); if (isMultiline && analyzer.Check(':') && flowLevel == 0 && indent < cursor.LineOffset) { tokens.Enqueue(new Error("While scanning a multiline plain scalar, found invalid mapping.", cursor.Mark(), cursor.Mark())); } tokens.Enqueue(item); } private YamlDotNet.Core.Tokens.Scalar ScanPlainScalar(ref bool isMultiline) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; StringBuilderPool.BuilderWrapper builderWrapper4 = StringBuilderPool.Rent(); try { StringBuilder builder4 = builderWrapper4.Builder; bool flag = false; long num = indent + 1; Mark start = cursor.Mark(); Mark end = start; SimpleKey simpleKey = simpleKeys.Peek(); while (!IsDocumentIndicator()) { if (analyzer.Check('#')) { if (indent < 0 && flowLevel == 0) { plainScalarFollowedByComment = true; } break; } bool flag2 = analyzer.Check('*') && (!simpleKey.IsPossible || !simpleKey.IsRequired); while (!analyzer.IsWhiteBreakOrZero()) { if ((analyzer.Check(':') && !flag2 && (analyzer.IsWhiteBreakOrZero(1) || (flowLevel > 0 && analyzer.Check(',', 1)))) || (flowLevel > 0 && analyzer.Check(",[]{}"))) { if (flowLevel == 0 && !simpleKey.IsPossible) { tokens.Enqueue(new Error("While scanning a plain scalar value, found invalid mapping.", cursor.Mark(), cursor.Mark())); } break; } if (flag || builder2.Length > 0) { if (flag) { if (StartsWith(builder3, '\n')) { if (builder4.Length == 0) { builder.Append(' '); } else { builder.Append((object?)builder4); } } else { builder.Append((object?)builder3); builder.Append((object?)builder4); } builder3.Length = 0; builder4.Length = 0; flag = false; } else { builder.Append((object?)builder2); builder2.Length = 0; } } if (flowLevel > 0 && cursor.LineOffset < num) { throw new InvalidOperationException(); } builder.Append(ReadCurrentCharacter()); end = cursor.Mark(); } if (!analyzer.IsWhite() && !analyzer.IsBreak()) { break; } while (analyzer.IsWhite() || analyzer.IsBreak()) { if (analyzer.IsWhite()) { if (flag && cursor.LineOffset < num && analyzer.IsTab()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a plain scalar, found a tab character that violate indentation."); } if (!flag) { builder2.Append(ReadCurrentCharacter()); } else { Skip(); } } else { isMultiline = true; if (!flag) { builder2.Length = 0; builder3.Append(ReadLine()); flag = true; } else { builder4.Append(ReadLine()); } } } if (flowLevel == 0 && cursor.LineOffset < num) { break; } } if (flag) { simpleKeyAllowed = true; } return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), ScalarStyle.Plain, start, end); } finally { ((IDisposable)builderWrapper4/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void RemoveSimpleKey() { SimpleKey simpleKey = simpleKeys.Peek(); if (simpleKey.IsPossible && simpleKey.IsRequired) { throw new SyntaxErrorException(simpleKey.Mark, simpleKey.Mark, "While scanning a simple key, could not find expected ':'."); } simpleKey.MarkAsImpossible(); } private string ScanDirectiveName(in Mark start) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (analyzer.IsAlphaNumericDashOrUnderscore()) { builder.Append(ReadCurrentCharacter()); } if (builder.Length == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, could not find expected directive name."); } if (analyzer.EndOfInput) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, found unexpected end of stream."); } if (!analyzer.IsWhiteBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, found unexpected non-alphabetical character."); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void SkipWhitespaces() { while (analyzer.IsWhite()) { Skip(); } } private VersionDirective ScanVersionDirectiveValue(in Mark start) { SkipWhitespaces(); int major = ScanVersionDirectiveNumber(in start); if (!analyzer.Check('.')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, did not find expected digit or '.' character."); } Skip(); int minor = ScanVersionDirectiveNumber(in start); return new VersionDirective(new Version(major, minor), start, start); } private TagDirective ScanTagDirectiveValue(in Mark start) { SkipWhitespaces(); string handle = ScanTagHandle(isDirective: true, start); if (!analyzer.IsWhite()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace."); } SkipWhitespaces(); string prefix = ScanTagUri(null, start); if (!analyzer.IsWhiteBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace or line break."); } return new TagDirective(handle, prefix, start, start); } private string ScanTagUri(string? head, Mark start) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; if (head != null && head.Length > 1) { builder.Append(head.Substring(1)); } while (analyzer.IsAlphaNumericDashOrUnderscore() || analyzer.Check(";/?:@&=+$.!~*'()[]%") || (analyzer.Check(',') && !analyzer.IsBreak(1))) { if (analyzer.Check('%')) { builder.Append(ScanUriEscapes(in start)); } else if (analyzer.Check('+')) { builder.Append(' '); Skip(); } else { builder.Append(ReadCurrentCharacter()); } } if (builder.Length == 0) { return string.Empty; } string text = builder.ToString(); if (Polyfills.EndsWith(text, ',')) { throw new SyntaxErrorException(cursor.Mark(), cursor.Mark(), "Unexpected comma at end of tag"); } return text; } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private string ScanUriEscapes(in Mark start) { byte[] array = EmptyBytes; int count = 0; int num = 0; do { if (!analyzer.Check('%') || !analyzer.IsHex(1) || !analyzer.IsHex(2)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find URI escaped octet."); } int num2 = (analyzer.AsHex(1) << 4) + analyzer.AsHex(2); if (num == 0) { num = (((num2 & 0x80) == 0) ? 1 : (((num2 & 0xE0) == 192) ? 2 : (((num2 & 0xF0) == 224) ? 3 : (((num2 & 0xF8) == 240) ? 4 : 0)))); if (num == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect leading UTF-8 octet."); } array = new byte[num]; } else if ((num2 & 0xC0) != 128) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect trailing UTF-8 octet."); } array[count++] = (byte)num2; Skip(); Skip(); Skip(); } while (--num > 0); string text = Encoding.UTF8.GetString(array, 0, count); if (text.Length == 0 || text.Length > 2) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect UTF-8 sequence."); } return text; } private string ScanTagHandle(bool isDirective, Mark start) { if (!analyzer.Check('!')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find expected '!'."); } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append(ReadCurrentCharacter()); while (analyzer.IsAlphaNumericDashOrUnderscore()) { builder.Append(ReadCurrentCharacter()); } if (analyzer.Check('!')) { builder.Append(ReadCurrentCharacter()); } else if (isDirective && (builder.Length != 1 || builder[0] != '!')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag directive, did not find expected '!'."); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private int ScanVersionDirectiveNumber(in Mark start) { int num = 0; int num2 = 0; while (analyzer.IsDigit()) { if (++num2 > 9) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, found extremely long version number."); } num = num * 10 + analyzer.AsDigit(); Skip(); } if (num2 == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, did not find expected version number."); } return num; } private void SaveSimpleKey() { bool isRequired = flowLevel == 0 && indent == cursor.LineOffset; if (simpleKeyAllowed) { SimpleKey item = new SimpleKey(isRequired, tokensParsed + tokens.Count, cursor); RemoveSimpleKey(); simpleKeys.Pop(); simpleKeys.Push(item); } } } internal class SemanticErrorException : YamlException { public SemanticErrorException(string message) : base(message) { } public SemanticErrorException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public SemanticErrorException(string message, Exception inner) : base(message, inner) { } } internal sealed class SimpleKey { private readonly Cursor cursor; public bool IsPossible { get; private set; } public bool IsRequired { get; } public int TokenNumber { get; } public long Index => cursor.Index; public long Line => cursor.Line; public long LineOffset => cursor.LineOffset; public Mark Mark => cursor.Mark(); public void MarkAsImpossible() { IsPossible = false; } public SimpleKey() { cursor = new Cursor(); } public SimpleKey(bool isRequired, int tokenNumber, Cursor cursor) { IsPossible = true; IsRequired = isRequired; TokenNumber = tokenNumber; this.cursor = new Cursor(cursor); } } internal sealed class StringLookAheadBuffer : ILookAheadBuffer, IResettable { public string Value { get; set; } = string.Empty; public int Position { get; private set; } public int Length => Value.Length; public bool EndOfInput => IsOutside(Position); public char Peek(int offset) { int index = Position + offset; if (!IsOutside(index)) { return Value[index]; } return '\0'; } private bool IsOutside(int index) { return index >= Value.Length; } public void Skip(int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length", "The length must be positive."); } Position += length; } public bool TryReset() { Position = 0; Value = string.Empty; return true; } } internal sealed class SyntaxErrorException : YamlException { public SyntaxErrorException(string message) : base(message) { } public SyntaxErrorException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public SyntaxErrorException(string message, Exception inner) : base(message, inner) { } } internal sealed class TagDirectiveCollection : KeyedCollection { public TagDirectiveCollection() { } public TagDirectiveCollection(IEnumerable tagDirectives) { foreach (TagDirective tagDirective in tagDirectives) { Add(tagDirective); } } protected override string GetKeyForItem(TagDirective item) { return item.Handle; } public new bool Contains(TagDirective directive) { return Contains(GetKeyForItem(directive)); } } internal readonly struct TagName : IEquatable { public static readonly TagName Empty; private readonly string? value; public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of a non-specific tag"); public bool IsEmpty => value == null; public bool IsNonSpecific { get { if (!IsEmpty) { if (!(value == "!")) { return value == "?"; } return true; } return false; } } public bool IsLocal { get { if (!IsEmpty) { return Value[0] == '!'; } return false; } } public bool IsGlobal { get { if (!IsEmpty) { return !IsLocal; } return false; } } public TagName(string value) { this.value = value ?? throw new ArgumentNullException("value"); if (value.Length == 0) { throw new ArgumentException("Tag value must not be empty.", "value"); } if (IsGlobal && !Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute)) { throw new ArgumentException("Global tags must be valid URIs.", "value"); } } public override string ToString() { return value ?? "?"; } public bool Equals(TagName other) { return object.Equals(value, other.value); } public override bool Equals(object? obj) { if (obj is TagName other) { return Equals(other); } return false; } public override int GetHashCode() { return value?.GetHashCode() ?? 0; } public static bool operator ==(TagName left, TagName right) { return left.Equals(right); } public static bool operator !=(TagName left, TagName right) { return !(left == right); } public static bool operator ==(TagName left, string right) { return object.Equals(left.value, right); } public static bool operator !=(TagName left, string right) { return !(left == right); } public static implicit operator TagName(string? value) { if (value != null) { return new TagName(value); } return Empty; } } internal sealed class Version { public int Major { get; } public int Minor { get; } public Version(int major, int minor) { if (major < 0) { throw new ArgumentOutOfRangeException("major", $"{major} should be >= 0"); } Major = major; if (minor < 0) { throw new ArgumentOutOfRangeException("minor", $"{minor} should be >= 0"); } Minor = minor; } public override bool Equals(object? obj) { if (obj is Version version && Major == version.Major) { return Minor == version.Minor; } return false; } public override int GetHashCode() { return HashCode.CombineHashCodes(Major.GetHashCode(), Minor.GetHashCode()); } } internal class YamlException : Exception { public Mark Start { get; } public Mark End { get; } public YamlException(string message) : this(in Mark.Empty, in Mark.Empty, message) { } public YamlException(in Mark start, in Mark end, string message) : this(in start, in end, message, null) { } public YamlException(in Mark start, in Mark end, string message, Exception? innerException) : base(message, innerException) { Start = start; End = end; } public YamlException(string message, Exception inner) : this(in Mark.Empty, in Mark.Empty, message, inner) { } public override string ToString() { return $"({Start}) - ({End}): {Message}"; } } } namespace YamlDotNet.Core.Tokens { internal class Anchor : Token { public AnchorName Value { get; } public Anchor(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public Anchor(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new ArgumentNullException("value"); } Value = value; } } internal sealed class AnchorAlias : Token { public AnchorName Value { get; } public AnchorAlias(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public AnchorAlias(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new ArgumentNullException("value"); } Value = value; } } internal sealed class BlockEnd : Token { public BlockEnd() : this(in Mark.Empty, in Mark.Empty) { } public BlockEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockEntry : Token { public BlockEntry() : this(in Mark.Empty, in Mark.Empty) { } public BlockEntry(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockMappingStart : Token { public BlockMappingStart() : this(in Mark.Empty, in Mark.Empty) { } public BlockMappingStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockSequenceStart : Token { public BlockSequenceStart() : this(in Mark.Empty, in Mark.Empty) { } public BlockSequenceStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Comment : Token { public string Value { get; } public bool IsInline { get; } public Comment(string value, bool isInline) : this(value, isInline, Mark.Empty, Mark.Empty) { } public Comment(string value, bool isInline, Mark start, Mark end) : base(in start, in end) { Value = value ?? throw new ArgumentNullException("value"); IsInline = isInline; } } internal sealed class DocumentEnd : Token { public DocumentEnd() : this(in Mark.Empty, in Mark.Empty) { } public DocumentEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class DocumentStart : Token { public DocumentStart() : this(in Mark.Empty, in Mark.Empty) { } public DocumentStart(in Mark start, in Mark end) : base(in start, in end) { } } internal class Error : Token { public string Value { get; } public Error(string value, Mark start, Mark end) : base(in start, in end) { Value = value; } } internal sealed class FlowEntry : Token { public FlowEntry() : this(in Mark.Empty, in Mark.Empty) { } public FlowEntry(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowMappingEnd : Token { public FlowMappingEnd() : this(in Mark.Empty, in Mark.Empty) { } public FlowMappingEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowMappingStart : Token { public FlowMappingStart() : this(in Mark.Empty, in Mark.Empty) { } public FlowMappingStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowSequenceEnd : Token { public FlowSequenceEnd() : this(in Mark.Empty, in Mark.Empty) { } public FlowSequenceEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowSequenceStart : Token { public FlowSequenceStart() : this(in Mark.Empty, in Mark.Empty) { } public FlowSequenceStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Key : Token { public Key() : this(in Mark.Empty, in Mark.Empty) { } public Key(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Scalar : Token { public bool IsKey { get; set; } public string Value { get; } public ScalarStyle Style { get; } public Scalar(string value) : this(value, ScalarStyle.Any) { } public Scalar(string value, ScalarStyle style) : this(value, style, Mark.Empty, Mark.Empty) { } public Scalar(string value, ScalarStyle style, Mark start, Mark end) : base(in start, in end) { Value = value ?? throw new ArgumentNullException("value"); Style = style; } } internal sealed class StreamEnd : Token { public StreamEnd() : this(in Mark.Empty, in Mark.Empty) { } public StreamEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class StreamStart : Token { public StreamStart() : this(in Mark.Empty, in Mark.Empty) { } public StreamStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Tag : Token { public string Handle { get; } public string Suffix { get; } public Tag(string handle, string suffix) : this(handle, suffix, Mark.Empty, Mark.Empty) { } public Tag(string handle, string suffix, Mark start, Mark end) : base(in start, in end) { Handle = handle ?? throw new ArgumentNullException("handle"); Suffix = suffix ?? throw new ArgumentNullException("suffix"); } } internal class TagDirective : Token { private static readonly Regex TagHandlePattern = new Regex("^!([0-9A-Za-z_\\-]*!)?$", RegexOptions.Compiled); public string Handle { get; } public string Prefix { get; } public TagDirective(string handle, string prefix) : this(handle, prefix, Mark.Empty, Mark.Empty) { } public TagDirective(string handle, string prefix, Mark start, Mark end) : base(in start, in end) { if (string.IsNullOrEmpty(handle)) { throw new ArgumentNullException("handle", "Tag handle must not be empty."); } if (!TagHandlePattern.IsMatch(handle)) { throw new ArgumentException("Tag handle must start and end with '!' and contain alphanumerical characters only.", "handle"); } Handle = handle; if (string.IsNullOrEmpty(prefix)) { throw new ArgumentNullException("prefix", "Tag prefix must not be empty."); } Prefix = prefix; } public override bool Equals(object? obj) { if (obj is TagDirective tagDirective && Handle.Equals(tagDirective.Handle)) { return Prefix.Equals(tagDirective.Prefix); } return false; } public override int GetHashCode() { return Handle.GetHashCode() ^ Prefix.GetHashCode(); } public override string ToString() { return Handle + " => " + Prefix; } } internal abstract class Token { public Mark Start { get; } public Mark End { get; } protected Token(in Mark start, in Mark end) { Start = start; End = end; } } internal sealed class Value : Token { public Value() : this(in Mark.Empty, in Mark.Empty) { } public Value(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class VersionDirective : Token { public Version Version { get; } public VersionDirective(Version version) : this(version, Mark.Empty, Mark.Empty) { } public VersionDirective(Version version, Mark start, Mark end) : base(in start, in end) { Version = version; } public override bool Equals(object? obj) { if (obj is VersionDirective versionDirective) { return Version.Equals(versionDirective.Version); } return false; } public override int GetHashCode() { return Version.GetHashCode(); } } } namespace YamlDotNet.Core.ObjectPool { internal class DefaultObjectPool : ObjectPool where T : class { private readonly Func createFunc; private readonly Func returnFunc; private readonly int maxCapacity; private int numItems; private protected readonly ConcurrentQueue items = new ConcurrentQueue(); private protected T? fastItem; public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) { } public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) { createFunc = policy.Create; returnFunc = policy.Return; maxCapacity = maximumRetained - 1; } public override T Get() { T result = fastItem; if (result == null || Interlocked.CompareExchange(ref fastItem, null, result) != result) { if (items.TryDequeue(out result)) { Interlocked.Decrement(ref numItems); return result; } return createFunc(); } return result; } public override void Return(T obj) { ReturnCore(obj); } private protected bool ReturnCore(T obj) { if (!returnFunc(obj)) { return false; } if (fastItem != null || Interlocked.CompareExchange(ref fastItem, obj, null) != null) { if (Interlocked.Increment(ref numItems) <= maxCapacity) { items.Enqueue(obj); return true; } Interlocked.Decrement(ref numItems); return false; } return true; } } internal class DefaultPooledObjectPolicy : IPooledObjectPolicy where T : class, new() { public T Create() { return new T(); } public bool Return(T obj) { if (obj is IResettable resettable) { return resettable.TryReset(); } return true; } } internal interface IPooledObjectPolicy where T : notnull { T Create(); bool Return(T obj); } internal interface IResettable { bool TryReset(); } internal abstract class ObjectPool where T : class { public abstract T Get(); public abstract void Return(T obj); } internal static class ObjectPool { public static ObjectPool Create(IPooledObjectPolicy? policy = null) where T : class, new() { return new DefaultObjectPool(policy ?? new DefaultPooledObjectPolicy()); } public static ObjectPool Create(int maximumRetained, IPooledObjectPolicy? policy = null) where T : class, new() { return new DefaultObjectPool(policy ?? new DefaultPooledObjectPolicy(), maximumRetained); } } [DebuggerStepThrough] internal static class StringBuilderPool { internal readonly struct BuilderWrapper : IDisposable { public readonly StringBuilder Builder; private readonly ObjectPool pool; public BuilderWrapper(StringBuilder builder, ObjectPool pool) { Builder = builder; this.pool = pool; } public override string ToString() { return Builder.ToString(); } public void Dispose() { pool.Return(Builder); } } private static readonly ObjectPool Pool = ObjectPool.Create(new StringBuilderPooledObjectPolicy { InitialCapacity = 16, MaximumRetainedCapacity = 1024 }); public static BuilderWrapper Rent() { StringBuilder builder = Pool.Get(); return new BuilderWrapper(builder, Pool); } } internal class StringBuilderPooledObjectPolicy : IPooledObjectPolicy { public int InitialCapacity { get; set; } = 100; public int MaximumRetainedCapacity { get; set; } = 4096; public StringBuilder Create() { return new StringBuilder(InitialCapacity); } public bool Return(StringBuilder obj) { if (obj.Capacity > MaximumRetainedCapacity) { return false; } obj.Clear(); return true; } } internal static class StringLookAheadBufferPool { internal readonly struct BufferWrapper : IDisposable { public readonly StringLookAheadBuffer Buffer; private readonly ObjectPool pool; public BufferWrapper(StringLookAheadBuffer buffer, ObjectPool pool) { Buffer = buffer; this.pool = pool; } public override string ToString() { return Buffer.ToString(); } public void Dispose() { pool.Return(Buffer); } } private static readonly ObjectPool Pool = ObjectPool.Create(new DefaultPooledObjectPolicy()); public static BufferWrapper Rent(string value) { StringLookAheadBuffer stringLookAheadBuffer = Pool.Get(); stringLookAheadBuffer.Value = value; return new BufferWrapper(stringLookAheadBuffer, Pool); } } } namespace YamlDotNet.Core.Events { internal sealed class AnchorAlias : ParsingEvent { internal override EventType Type => EventType.Alias; public AnchorName Value { get; } public AnchorAlias(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new YamlException(in start, in end, "Anchor value must not be empty."); } Value = value; } public AnchorAlias(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Alias [value = {Value}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class Comment : ParsingEvent { public string Value { get; } public bool IsInline { get; } internal override EventType Type => EventType.Comment; public Comment(string value, bool isInline) : this(value, isInline, Mark.Empty, Mark.Empty) { } public Comment(string value, bool isInline, Mark start, Mark end) : base(in start, in end) { Value = value; IsInline = isInline; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } public override string ToString() { return (IsInline ? "Inline" : "Block") + " Comment [" + Value + "]"; } } internal sealed class DocumentEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.DocumentEnd; public bool IsImplicit { get; } public DocumentEnd(bool isImplicit, Mark start, Mark end) : base(in start, in end) { IsImplicit = isImplicit; } public DocumentEnd(bool isImplicit) : this(isImplicit, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Document end [isImplicit = {IsImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class DocumentStart : ParsingEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.DocumentStart; public TagDirectiveCollection? Tags { get; } public VersionDirective? Version { get; } public bool IsImplicit { get; } public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit, Mark start, Mark end) : base(in start, in end) { Version = version; Tags = tags; IsImplicit = isImplicit; } public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit) : this(version, tags, isImplicit, Mark.Empty, Mark.Empty) { } public DocumentStart(in Mark start, in Mark end) : this(null, null, isImplicit: true, start, end) { } public DocumentStart() : this(null, null, isImplicit: true, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Document start [isImplicit = {IsImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum EventType { None, StreamStart, StreamEnd, DocumentStart, DocumentEnd, Alias, Scalar, SequenceStart, SequenceEnd, MappingStart, MappingEnd, Comment } internal interface IParsingEventVisitor { void Visit(AnchorAlias e); void Visit(StreamStart e); void Visit(StreamEnd e); void Visit(DocumentStart e); void Visit(DocumentEnd e); void Visit(Scalar e); void Visit(SequenceStart e); void Visit(SequenceEnd e); void Visit(MappingStart e); void Visit(MappingEnd e); void Visit(Comment e); } internal class MappingEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.MappingEnd; public MappingEnd(in Mark start, in Mark end) : base(in start, in end) { } public MappingEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Mapping end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class MappingStart : NodeEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.MappingStart; public bool IsImplicit { get; } public override bool IsCanonical => !IsImplicit; public MappingStyle Style { get; } public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style, Mark start, Mark end) : base(anchor, tag, start, end) { IsImplicit = isImplicit; Style = style; } public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style) : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) { } public MappingStart() : this(AnchorName.Empty, TagName.Empty, isImplicit: true, MappingStyle.Any, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Mapping start [anchor = {base.Anchor}, tag = {base.Tag}, isImplicit = {IsImplicit}, style = {Style}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum MappingStyle { Any, Block, Flow } internal abstract class NodeEvent : ParsingEvent { public AnchorName Anchor { get; } public TagName Tag { get; } public abstract bool IsCanonical { get; } protected NodeEvent(AnchorName anchor, TagName tag, Mark start, Mark end) : base(in start, in end) { Anchor = anchor; Tag = tag; } protected NodeEvent(AnchorName anchor, TagName tag) : this(anchor, tag, Mark.Empty, Mark.Empty) { } } internal abstract class ParsingEvent { public virtual int NestingIncrease => 0; internal abstract EventType Type { get; } public Mark Start { get; } public Mark End { get; } public abstract void Accept(IParsingEventVisitor visitor); internal ParsingEvent(in Mark start, in Mark end) { Start = start; End = end; } } internal sealed class Scalar : NodeEvent { internal override EventType Type => EventType.Scalar; public string Value { get; } public ScalarStyle Style { get; } public bool IsPlainImplicit { get; } public bool IsQuotedImplicit { get; } public override bool IsCanonical { get { if (!IsPlainImplicit) { return !IsQuotedImplicit; } return false; } } public bool IsKey { get; } public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit, Mark start, Mark end, bool isKey = false) : base(anchor, tag, start, end) { Value = value; Style = style; IsPlainImplicit = isPlainImplicit; IsQuotedImplicit = isQuotedImplicit; IsKey = isKey; } public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit) : this(anchor, tag, value, style, isPlainImplicit, isQuotedImplicit, Mark.Empty, Mark.Empty) { } public Scalar(string value) : this(AnchorName.Empty, TagName.Empty, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public Scalar(TagName tag, string value) : this(AnchorName.Empty, tag, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public Scalar(AnchorName anchor, TagName tag, string value) : this(anchor, tag, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Scalar [anchor = {base.Anchor}, tag = {base.Tag}, value = {Value}, style = {Style}, isPlainImplicit = {IsPlainImplicit}, isQuotedImplicit = {IsQuotedImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class SequenceEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.SequenceEnd; public SequenceEnd(in Mark start, in Mark end) : base(in start, in end) { } public SequenceEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Sequence end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class SequenceStart : NodeEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.SequenceStart; public bool IsImplicit { get; } public override bool IsCanonical => !IsImplicit; public SequenceStyle Style { get; } public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style, Mark start, Mark end) : base(anchor, tag, start, end) { IsImplicit = isImplicit; Style = style; } public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style) : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Sequence start [anchor = {base.Anchor}, tag = {base.Tag}, isImplicit = {IsImplicit}, style = {Style}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum SequenceStyle { Any, Block, Flow } internal sealed class StreamEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.StreamEnd; public StreamEnd(in Mark start, in Mark end) : base(in start, in end) { } public StreamEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Stream end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class StreamStart : ParsingEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.StreamStart; public StreamStart() : this(in Mark.Empty, in Mark.Empty) { } public StreamStart(in Mark start, in Mark end) : base(in start, in end) { } public override string ToString() { return "Stream start"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class AllowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DisallowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DoesNotReturnAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DoesNotReturnIfAttribute : Attribute { public bool ParameterValue { get; } public DoesNotReturnIfAttribute(bool parameterValue) { ParameterValue = parameterValue; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MaybeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MaybeNullWhenAttribute : Attribute { public bool ReturnValue { get; } public MaybeNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullIfNotNullAttribute : Attribute { public string ParameterName { get; } public NotNullIfNotNullAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public NotNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } }