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 System.Timers; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; 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("SecondaryAttacks")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("SecondaryAttacks")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.1.9")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.9.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [CompilerGenerated] internal sealed class <>z__ReadOnlyArray : IEnumerable, ICollection, IList, IEnumerable, IReadOnlyCollection, IReadOnlyList, ICollection, IList { int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection.Count => _items.Length; T IReadOnlyList.this[int index] => _items[index]; int ICollection.Count => _items.Length; bool ICollection.IsReadOnly => true; T IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } 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(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(T item) { return ((ICollection)_items).Contains(item); } void ICollection.CopyTo(T[] array, int arrayIndex) { ((ICollection)_items).CopyTo(array, arrayIndex); } bool ICollection.Remove(T item) { throw new NotSupportedException(); } int IList.IndexOf(T item) { return ((IList)_items).IndexOf(item); } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } } 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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { private readonly string k__BackingField; public string Name => k__BackingField; public ExtensionMarkerAttribute(string name) { k__BackingField = name; } } } namespace LocalizationManager { [PublicAPI] public class Localizer { private static readonly Dictionary>> PlaceholderProcessors; private static readonly Dictionary> loadedTexts; private static readonly ConditionalWeakTable localizationLanguage; private static readonly List> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List fileExtensions; 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; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder(string key, string placeholder, ConfigEntry config, Func? convertConfigValue = null) where T : notnull { if (convertConfigValue == null) { convertConfigValue = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key)) { PlaceholderProcessors[key] = new Dictionary>(); } config.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value); UpdatePlaceholderText(Localization.instance, key); } } public static void AddText(string key, string text) { List> list = new List>(); foreach (WeakReference localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } public static void LoadLocalizationLater() { if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference(__instance)); } localizationLanguage.Add(__instance, 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('.'); 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; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_008f: 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_00ca: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown PlaceholderProcessors = new Dictionary>>(); loadedTexts = new Dictionary>(); localizationLanguage = new ConditionalWeakTable(); localizationObjects = new List>(); fileExtensions = new List(2) { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } 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(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.0"; } } namespace SecondaryAttacks { internal static class AftershockSystem { internal sealed class AftershockController : MonoBehaviour { private int _nextWave; private float _nextWaveTime; private bool _durabilityDrained; private bool _finished; internal Attack Attack { get; private set; } internal AftershockDefinition Aftershock { get; private set; } internal Vector3 BaseOrigin { get; private set; } internal Vector3 Forward { get; private set; } internal Quaternion Rotation { get; private set; } internal void Initialize(Attack attack, AftershockDefinition aftershock) { //IL_0023: 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_003b: 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_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) //IL_0056: 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_006c: 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) Attack = attack; Aftershock = aftershock; Transform transform = ((Component)attack.m_character).transform; Transform val = ResolveAttackOrigin(attack); Forward = transform.forward; Rotation = transform.rotation; BaseOrigin = val.position + Vector3.up * attack.m_attackHeight + Forward * attack.m_attackRange + transform.right * attack.m_attackOffset; _nextWave = 0; _nextWaveTime = Time.time; ((Behaviour)this).enabled = true; } private void Update() { if ((Object)(object)Attack?.m_character == (Object)null || Attack.m_weapon == null || ((Character)Attack.m_character).IsDead() || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)Attack.m_character)) { Finish(); return; } while (_nextWave <= Aftershock.Waves && Time.time >= _nextWaveTime) { ApplyWave(this, _nextWave); _nextWave++; float num = Mathf.Max(0f, Aftershock.Interval); _nextWaveTime += num; if (num > 0f) { break; } } if (_nextWave > Aftershock.Waves) { Finish(); } } internal void DrainDurabilityOnce() { if (!_durabilityDrained) { SecondaryAttackManager.DrainAttackDurability(Attack, Aftershock.DurabilityFactor); _durabilityDrained = true; } } private void Finish() { if (!_finished) { _finished = true; Object.Destroy((Object)(object)this); } } } private static readonly Collider[] Hits = (Collider[])(object)new Collider[128]; private static readonly HashSet HitObjects = new HashSet(); private static int _attackMask; private static int _attackMaskTerrain; private static int _attackMaskCharacters; internal static bool CanHandle(Attack attack, SecondaryAttackDefinition definition) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 if (attack != null && definition?.Aftershock != null && (Object)(object)attack.m_character != (Object)null && attack.m_weapon != null && (int)attack.m_attackType == 4) { return attack.m_attackRayWidth > 0f; } return false; } internal static void Trigger(Attack attack, SecondaryAttackDefinition definition) { if (CanHandle(attack, definition) && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)attack.m_character)) { AftershockDefinition aftershock = definition.Aftershock; ApplyAttackTriggerSideEffects(attack); ((Component)attack.m_character).gameObject.AddComponent().Initialize(attack, aftershock); } } private static void ApplyAttackTriggerSideEffects(Attack attack) { //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) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown if (attack.m_toggleFlying) { if (((Character)attack.m_character).IsFlying()) { ((Character)attack.m_character).Land(); } else { ((Character)attack.m_character).TakeOff(); } } if (attack.m_recoilPushback != 0f) { ((Character)attack.m_character).ApplyPushback(-((Component)attack.m_character).transform.forward, attack.m_recoilPushback); } if ((float)attack.m_selfDamage > 0f) { HitData val = new HitData(); val.m_damage.m_damage = attack.m_selfDamage; ((Character)attack.m_character).Damage(val); } if (attack.m_consumeItem) { attack.ConsumeItem(); } if (attack.m_requiresReload) { attack.m_character.ResetLoadedWeapon(); } } private static void ApplyWave(AftershockController controller, int waveIndex) { //IL_0092: 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_00a3: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00e2: 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_00fd: 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_013a: 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_017c: 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_0180: 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_0190: 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_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01db: 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_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025a: 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_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) Attack attack = controller.Attack; AftershockDefinition aftershock = controller.Aftershock; Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; if ((Object)(object)character == (Object)null || weapon?.m_shared == null) { return; } float visualScale; float pushScale; float num; float damageScale; float volumeScale = (visualScale = (pushScale = (num = (damageScale = ((waveIndex <= 0) ? 1f : Mathf.Pow(1f - aftershock.WaveDecay, (float)waveIndex)))))); float num2 = Mathf.Max(0.01f, attack.m_attackRayWidth * num); float num3 = Mathf.Max(0.01f, (attack.m_attackRayWidth + attack.m_attackRayWidthCharExtra) * num); Vector3 val = controller.BaseOrigin + controller.Forward * aftershock.ForwardStep * (float)waveIndex; Transform parent = ResolveAttackOrigin(attack); CreateScaledEffects(weapon.m_shared.m_triggerEffect, val, controller.Rotation, parent, visualScale, volumeScale); CreateScaledEffects(attack.m_triggerEffect, val, controller.Rotation, parent, visualScale, volumeScale); int hitCount = 0; Vector3 averageHitPoint = Vector3.zero; float randomSkillFactor = character.GetRandomSkillFactor(weapon.m_shared.m_skillType); float maxAdrenalineMultiplier = 0f; HitObjects.Clear(); try { int num4 = (attack.m_hitTerrain ? GetAttackMaskTerrain() : GetAttackMask()); CheckHits(controller, val, Physics.OverlapSphereNonAlloc(val, num2, Hits, num4, (QueryTriggerInteraction)0), damageScale, pushScale, randomSkillFactor, ref hitCount, ref averageHitPoint, ref maxAdrenalineMultiplier); if (attack.m_attackRayWidthCharExtra > 0f || attack.m_attackHeightChar1 != 0f) { CheckHits(controller, val, Physics.OverlapSphereNonAlloc(val + Vector3.up * attack.m_attackHeightChar1, num3, Hits, GetAttackMaskCharacters(), (QueryTriggerInteraction)0), damageScale, pushScale, randomSkillFactor, ref hitCount, ref averageHitPoint, ref maxAdrenalineMultiplier); if (!Mathf.Approximately(attack.m_attackHeightChar2, attack.m_attackHeightChar1)) { CheckHits(controller, val, Physics.OverlapSphereNonAlloc(val + Vector3.up * attack.m_attackHeightChar2, num3, Hits, GetAttackMaskCharacters(), (QueryTriggerInteraction)0), damageScale, pushScale, randomSkillFactor, ref hitCount, ref averageHitPoint, ref maxAdrenalineMultiplier); } } } finally { HitObjects.Clear(); Array.Clear(Hits, 0, Hits.Length); } if (hitCount > 0) { averageHitPoint /= (float)hitCount; CreateScaledEffects(weapon.m_shared.m_hitEffect, averageHitPoint, Quaternion.identity, null, visualScale, volumeScale); CreateScaledEffects(attack.m_hitEffect, averageHitPoint, Quaternion.identity, null, visualScale, volumeScale); controller.DrainDurabilityOnce(); character.AddNoise(attack.m_attackHitNoise); if (maxAdrenalineMultiplier > 0f) { SecondaryAttackAdrenalineSystem.TryGrantOnce(attack, maxAdrenalineMultiplier, 1f, "aftershock"); } } if ((Object)(object)attack.m_spawnOnTrigger != (Object)null) { IProjectile component = Object.Instantiate(attack.m_spawnOnTrigger, val, Quaternion.identity).GetComponent(); if (component != null) { component.Setup(character, ((Component)character).transform.forward, -1f, (HitData)null, (ItemData)null, attack.m_lastUsedAmmo); } } } private static void CheckHits(AftershockController controller, Vector3 origin, int count, float damageScale, float pushScale, float skillDamageFactor, ref int hitCount, ref Vector3 averageHitPoint, ref float maxAdrenalineMultiplier) { //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_0093: 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_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_009d: 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) //IL_00b8: 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_00cf: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_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) //IL_00f4: 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_0104: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_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_024f: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) Attack attack = controller.Attack; Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; Transform transform = ((Component)character).transform; for (int i = 0; i < count; i++) { Collider val = Hits[i]; if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)(object)((Component)character).gameObject) { continue; } GameObject val2 = Projectile.FindHitObject(val); if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)((Component)character).gameObject || !HitObjects.Add(val2)) { continue; } Vector3 val3 = SecondaryAttackManager.ResolveSafeClosestPoint(val, origin); Vector3 val4 = val3 - origin; if (((Vector3)(ref val4)).sqrMagnitude < 0.0001f) { Bounds bounds = val.bounds; val3 = ((Bounds)(ref bounds)).center; } IDestructible component = val2.GetComponent(); if (component == null) { continue; } Vector3 val5 = val3 - origin; val5.y = 0f; Vector3 val6 = val3 - transform.position; if (Vector3.Dot(val6, val5) < 0f) { val5 = val6; } if (((Vector3)(ref val5)).sqrMagnitude < 0.0001f) { val5 = transform.forward; } ((Vector3)(ref val5)).Normalize(); HitData val7 = CreateHitData(attack, val, val3, val5, skillDamageFactor, damageScale, pushScale); Character val8 = (Character)(object)((component is Character) ? component : null); bool flag = false; if ((Object)(object)val8 != (Object)null) { flag = BaseAI.IsEnemy(character, val8) || ((Object)(object)val8.GetBaseAI() != (Object)null && val8.GetBaseAI().IsAggravatable() && character.IsPlayer()); if (((!attack.m_hitFriendly || character.IsTamed()) && !character.IsPlayer() && !flag) || (!weapon.m_shared.m_tamedOnly && character.IsPlayer() && !character.IsPVPEnabled() && !flag) || (weapon.m_shared.m_tamedOnly && !val8.IsTamed())) { continue; } if (flag && val8.m_enemyAdrenalineMultiplier > maxAdrenalineMultiplier) { maxAdrenalineMultiplier = val8.m_enemyAdrenalineMultiplier; } if (val7.m_dodgeable && val8.IsDodgeInvincible()) { if (val8.IsPlayer()) { Character obj = ((val8 is Player) ? val8 : null); if (obj != null) { ((Player)obj).HitWhileDodging(); } } continue; } } else if (weapon.m_shared.m_tamedOnly) { continue; } TrySpawnOnHit(attack, val2); character.GetSEMan().ModifyAttack(weapon.m_shared.m_skillType, ref val7); if (attack.m_attackHealthReturnHit > 0f && flag) { character.Heal(attack.m_attackHealthReturnHit, true); } component.Damage(val7); hitCount++; averageHitPoint += val3; } } private static HitData CreateHitData(Attack attack, Collider collider, Vector3 hitPoint, Vector3 hitDirection, float skillDamageFactor, float damageScale, float pushScale) { //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) return SecondaryAttackHitDataFactory.CreateMeleeHit(attack, collider, hitPoint, hitDirection, skillDamageFactor, damageScale, pushScale); } private static void TrySpawnOnHit(Attack attack, GameObject hitObject) { //IL_003f: 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_006f: Unknown result type (might be due to invalid IL or missing references) if (!(attack.m_spawnOnHitChance <= 0f) && !((Object)(object)attack.m_spawnOnHit == (Object)null) && !(Random.Range(0f, 1f) >= attack.m_spawnOnHitChance)) { IProjectile componentInChildren = Object.Instantiate(attack.m_spawnOnHit, hitObject.transform.position, hitObject.transform.rotation).GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.Setup((Character)(object)attack.m_character, ((Component)attack.m_character).transform.forward, -1f, (HitData)null, attack.m_weapon, attack.m_lastUsedAmmo); } } } private static Transform ResolveAttackOrigin(Attack attack) { if (!string.IsNullOrWhiteSpace(attack.m_attackOriginJoint)) { GameObject visual = ((Character)attack.m_character).GetVisual(); if ((Object)(object)visual != (Object)null) { Transform val = Utils.FindChild(visual.transform, attack.m_attackOriginJoint, (IterativeSearchType)0); if ((Object)(object)val != (Object)null) { return val; } } } return ((Component)attack.m_character).transform; } private static void CreateScaledEffects(EffectList effects, Vector3 position, Quaternion rotation, Transform? parent, float visualScale, float volumeScale) { //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) if (effects == null || !effects.HasEffects()) { return; } float num = Mathf.Max(0f, visualScale); GameObject[] array = effects.Create(position, rotation, parent, num, -1); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null) { continue; } ScaleParticleSystems(val, num); ZSFX[] componentsInChildren = val.GetComponentsInChildren(true); foreach (ZSFX obj in componentsInChildren) { obj.SetVolumeModifier(obj.GetVolumeModifier() * volumeScale); } AudioSource[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (AudioSource val2 in componentsInChildren2) { if ((Object)(object)((Component)val2).GetComponent() == (Object)null) { val2.volume *= volumeScale; } } } } private static void ScaleParticleSystems(GameObject instance, float visualScale) { //IL_0021: 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_0072: 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_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_0048: 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_005c: 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_0086: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_00be: 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_00d1: 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_00f6: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Approximately(visualScale, 1f)) { return; } ParticleSystem[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (ParticleSystem obj in componentsInChildren) { MainModule main = obj.main; if (((MainModule)(ref main)).startSize3D) { ((MainModule)(ref main)).startSizeX = ScaleCurve(((MainModule)(ref main)).startSizeX, visualScale); ((MainModule)(ref main)).startSizeY = ScaleCurve(((MainModule)(ref main)).startSizeY, visualScale); ((MainModule)(ref main)).startSizeZ = ScaleCurve(((MainModule)(ref main)).startSizeZ, visualScale); } else { ((MainModule)(ref main)).startSize = ScaleCurve(((MainModule)(ref main)).startSize, visualScale); } ((MainModule)(ref main)).startSpeed = ScaleCurve(((MainModule)(ref main)).startSpeed, visualScale); ShapeModule shape = obj.shape; if (((ShapeModule)(ref shape)).enabled) { ((ShapeModule)(ref shape)).radius = ((ShapeModule)(ref shape)).radius * visualScale; ((ShapeModule)(ref shape)).scale = ((ShapeModule)(ref shape)).scale * visualScale; ((ShapeModule)(ref shape)).position = ((ShapeModule)(ref shape)).position * visualScale; } TrailModule trails = obj.trails; if (((TrailModule)(ref trails)).enabled) { ((TrailModule)(ref trails)).widthOverTrail = ScaleCurve(((TrailModule)(ref trails)).widthOverTrail, visualScale); } ParticleSystemRenderer component = ((Component)obj).GetComponent(); if ((Object)(object)component != (Object)null) { component.lengthScale *= visualScale; component.velocityScale *= visualScale; } } } private static MinMaxCurve ScaleCurve(MinMaxCurve curve, float scale) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) ParticleSystemCurveMode mode = ((MinMaxCurve)(ref curve)).mode; switch ((int)mode) { case 0: ((MinMaxCurve)(ref curve)).constant = ((MinMaxCurve)(ref curve)).constant * scale; break; case 3: ((MinMaxCurve)(ref curve)).constantMin = ((MinMaxCurve)(ref curve)).constantMin * scale; ((MinMaxCurve)(ref curve)).constantMax = ((MinMaxCurve)(ref curve)).constantMax * scale; break; case 1: case 2: ((MinMaxCurve)(ref curve)).curveMultiplier = ((MinMaxCurve)(ref curve)).curveMultiplier * scale; break; } return curve; } private static int GetAttackMask() { if (_attackMask == 0) { _attackMask = LayerMask.GetMask(new string[11] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _attackMask; } private static int GetAttackMaskTerrain() { if (_attackMaskTerrain == 0) { _attackMaskTerrain = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _attackMaskTerrain; } private static int GetAttackMaskCharacters() { if (_attackMaskCharacters == 0) { _attackMaskCharacters = LayerMask.GetMask(new string[6] { "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _attackMaskCharacters; } } internal static class BackstabSkillGainSystem { internal readonly struct BackstabDamageState { internal static BackstabDamageState Inactive => new BackstabDamageState(active: false, 0f); internal bool Active { get; } internal float PreviousBackstabTime { get; } internal BackstabDamageState(bool active, float previousBackstabTime) { Active = active; PreviousBackstabTime = previousBackstabTime; } } internal const string GrantSneakSkillRpcName = "SecondaryAttacks_GrantBackstabSneakSkill"; private const float BackstabTimeDetectionWindow = 1f; private static readonly FieldRef? BackstabTimeField = TryCreateBackstabTimeFieldRef(); internal static BackstabDamageState CaptureBackstabState(Character target, HitData hit) { if ((Object)(object)target == (Object)null || hit == null || hit.m_backstabBonus <= 1f) { return BackstabDamageState.Inactive; } return new BackstabDamageState(active: true, GetBackstabTime(target)); } internal static void TryGrantForBackstab(Character target, HitData hit, BackstabDamageState state) { if (!state.Active || (Object)(object)target == (Object)null || hit == null) { return; } float raiseAmount = GetRaiseAmount(); if (!(raiseAmount <= 0f) && DidBackstabSucceed(target, state.PreviousBackstabTime)) { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null) { GrantToPlayer(val, raiseAmount); } } } internal static void GrantLocal(Player player, float amount) { if (!((Object)(object)player == (Object)null) && !(amount <= 0f)) { float num = Mathf.Min(Mathf.Max(0f, amount), GetRaiseAmount()); if (!(num <= 0f)) { ((Character)player).RaiseSkill((SkillType)101, num); } } } private static float GetRaiseAmount() { return Mathf.Max(0f, SecondaryAttacksPlugin.BackstabSneakSkillRaiseAmount?.Value ?? 0f); } private static bool DidBackstabSucceed(Character target, float previousBackstabTime) { float backstabTime = GetBackstabTime(target); if (backstabTime > previousBackstabTime + 0.001f && backstabTime >= Time.time - 1f) { return backstabTime <= Time.time + 0.001f; } return false; } private static void GrantToPlayer(Player player, float amount) { //IL_0001: 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: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown ZNetView nview; ZDO zdo; if ((Object)player == (Object)Player.m_localPlayer) { GrantLocal(player, amount); } else if (SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)player, out nview, out zdo)) { nview.InvokeRPC("SecondaryAttacks_GrantBackstabSneakSkill", new object[1] { amount }); } } internal static float GetBackstabTime(Character target) { if ((Object)(object)target == (Object)null || BackstabTimeField == null) { return 0f; } return BackstabTimeField.Invoke(target); } private static FieldRef? TryCreateBackstabTimeFieldRef() { try { return AccessTools.FieldRefAccess("m_backstabTime"); } catch (Exception) { return null; } } } internal static class BloodMagicSkillGainSystem { private static float GetHealthCostSkillRaiseFactor() { return Mathf.Max(0f, SecondaryAttacksPlugin.BloodMagicHealthCostSkillRaiseFactor?.Value ?? 0f); } internal static bool IsHealthCostSkillGainEnabled() { return GetHealthCostSkillRaiseFactor() > 0f; } internal static bool ShouldUseMaxHealthForPercentageCost() { ConfigEntry bloodMagicHealthCostUsesMaxHealth = SecondaryAttacksPlugin.BloodMagicHealthCostUsesMaxHealth; if (bloodMagicHealthCostUsesMaxHealth == null) { return false; } return bloodMagicHealthCostUsesMaxHealth.Value == SecondaryAttacksPlugin.Toggle.On; } internal static void ApplyMaxHealthPercentageCost(Attack attack, ref float healthCost) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 if (!ShouldUseMaxHealthForPercentageCost() || attack == null || (Object)(object)attack.m_character == (Object)null) { return; } ItemData weapon = attack.m_weapon; if (weapon != null && (int)weapon.m_shared.m_skillType == 10 && !(attack.m_attackHealthPercentage <= 0f)) { float num = Mathf.Max(0f, attack.m_attackHealth) + Mathf.Max(0f, ((Character)attack.m_character).GetMaxHealth()) * Mathf.Max(0f, attack.m_attackHealthPercentage) / 100f; if (num <= 0f) { healthCost = 0f; return; } float num2 = Mathf.Clamp01(((Character)attack.m_character).GetSkillFactor((SkillType)10)); healthCost = num - num * 0.33f * num2; } } internal static void TryGrantForHealthUse(Character character, float previousHealth) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 if (!IsHealthCostSkillGainEnabled()) { return; } Player val = (Player)(object)((character is Player) ? character : null); if (val == null) { return; } Humanoid val2 = (Humanoid)(object)((character is Humanoid) ? character : null); if (val2 == null) { return; } float num = previousHealth - character.GetHealth(); if (num <= 0.001f) { return; } Attack currentAttack = val2.m_currentAttack; if (currentAttack == null || (Object)(object)currentAttack.m_character != (Object)(object)val2) { return; } ItemData weapon = currentAttack.m_weapon; if (weapon != null && (int)weapon.m_shared.m_skillType == 10) { float healthCostSkillRaiseFactor = GetHealthCostSkillRaiseFactor(); float num2 = num * healthCostSkillRaiseFactor; if (!(num2 <= 0f)) { ((Character)val).RaiseSkill((SkillType)10, num2); } } } } internal static class BowSecondaryKeyHintSystem { private const int MissingBlockHintRefreshFrames = 30; private static KeyHints? _activeKeyHints; private static KeyHintCell? _bowSecondaryHint; private static KeyHintCell? _detonateBlockHint; private static GameObject? _bowSecondaryHintTemplate; private static KeyHints? _cachedTemplateHints; private static GameObject? _cachedCombatHintTemplate; private static KeyHints? _cachedBlockHintHints; private static GameObject? _cachedBlockHint; private static bool _showingBowSecondaryHint; private static bool _showingDetonateBlockHint; private static bool _cachedTemplateGamepadActive; private static bool _cachedBlockHintGamepadActive; private static bool _cachedBlockHintResolved; private static bool _lastGamepadActive; private static bool _lastDetonateBlockGamepadActive; private static bool _keyHintApplied; private static bool _detonateBlockHintApplied; private static int _cachedBlockHintFrame; private static string _lastButtonLabel = string.Empty; private static string _lastDetonateBlockButtonLabel = string.Empty; internal static void InitializeKeyHints(KeyHints hints) { _activeKeyHints = hints; DestroyBowSecondaryHint(); RestoreDetonateBlockHint(); _detonateBlockHint = null; ClearBlockHintCache(); _showingBowSecondaryHint = false; UpdateKeyHint(hints); } internal static void RefreshKeyHintUi() { if ((Object)(object)_activeKeyHints != (Object)null) { UpdateKeyHint(_activeKeyHints); } } internal static void UpdateKeyHint(KeyHints hints) { if ((Object)(object)hints == (Object)null) { return; } _activeKeyHints = hints; if (!ShouldAllowCustomCombatHints(hints)) { HideBowSecondaryHint(); RestoreDetonateBlockHint(); if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(false); } return; } UpdateDetonateBlockHint(hints); if (!ShouldShowBowSecondaryHint()) { HideBowSecondaryHint(); return; } bool flag = ZInput.IsGamepadActive(); EnsureBowSecondaryHint(hints, flag); KeyHintCell? bowSecondaryHint = _bowSecondaryHint; if (bowSecondaryHint != null && bowSecondaryHint.IsValid) { if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(true); } SetVanillaCombatHintActive(hints.m_secondaryAttackGP, active: false); SetVanillaCombatHintActive(hints.m_secondaryAttackKB, active: false); bool flag2 = !_showingBowSecondaryHint; string text = ResolveSecondaryAttackButtonLabel(flag); if (!_keyHintApplied || _lastGamepadActive != flag || !string.Equals(_lastButtonLabel, text, StringComparison.Ordinal)) { _bowSecondaryHint.SetKey(text); _lastButtonLabel = text; _lastGamepadActive = flag; _keyHintApplied = true; flag2 = true; } if ((Object)(object)_bowSecondaryHint.Root != (Object)null && _bowSecondaryHint.Root.transform.GetSiblingIndex() != 0) { _bowSecondaryHint.MoveToStart(); flag2 = true; } if (flag2) { _bowSecondaryHint.RebuildParentLayout(); } _showingBowSecondaryHint = true; } } private static void UpdateDetonateBlockHint(KeyHints hints) { Player localPlayer = Player.m_localPlayer; if (!ShouldShowCombatHints(localPlayer) || !StickyDetonatorSystem.ShouldShowDetonateBlockHint(localPlayer)) { RestoreDetonateBlockHint(); return; } bool flag = ZInput.IsGamepadActive(); GameObject val = ResolveCachedBlockHint(hints, flag); if ((Object)(object)val == (Object)null) { RestoreDetonateBlockHint(); return; } if (_detonateBlockHint == null || (Object)(object)_detonateBlockHint.Root != (Object)(object)val) { RestoreDetonateBlockHint(); _detonateBlockHint = KeyHintCell.FromGameObject(val); _detonateBlockHintApplied = false; } KeyHintCell? detonateBlockHint = _detonateBlockHint; if (detonateBlockHint != null && detonateBlockHint.IsValid) { if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(true); } string text = ResolveBlockButtonLabel(flag); if (!_detonateBlockHintApplied || _lastDetonateBlockGamepadActive != flag || !string.Equals(_lastDetonateBlockButtonLabel, text, StringComparison.Ordinal)) { ApplyDetonateBlockHint(_detonateBlockHint, text); _lastDetonateBlockButtonLabel = text; _lastDetonateBlockGamepadActive = flag; _detonateBlockHintApplied = true; _detonateBlockHint.RebuildParentLayout(); } _showingDetonateBlockHint = true; } } private static bool ShouldShowBowSecondaryHint() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 Player localPlayer = Player.m_localPlayer; if (!ShouldShowCombatHints(localPlayer)) { return false; } ItemData val = ResolveEquippedBow(localPlayer); if (val?.m_shared == null) { return false; } if ((int)val.m_shared.m_itemType != 4) { return false; } if (!val.m_shared.m_attack.m_bowDraw) { return false; } return HasVisibleSecondaryAttack(val); } private static bool HasVisibleSecondaryAttack(ItemData weapon) { if (weapon == null) { return false; } if (!SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return false; } if (definition.BehaviorType != SecondaryAttackBehaviorType.Projectile) { return false; } return true; } private static bool ShouldAllowCustomCombatHints(KeyHints hints) { if (hints.m_keyHintsEnabled && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus())) { if (!((Object)(object)InventoryGui.instance == (Object)null)) { if (!InventoryGui.instance.IsSkillsPanelOpen && !InventoryGui.instance.IsTrophisPanelOpen) { return !InventoryGui.instance.IsTextPanelOpen; } return false; } return true; } return false; } private static bool ShouldShowCombatHints(Player? player) { if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus()) && ((Object)(object)InventoryGui.instance == (Object)null || (!InventoryGui.instance.IsSkillsPanelOpen && !InventoryGui.instance.IsTrophisPanelOpen && !InventoryGui.instance.IsTextPanelOpen)) && !PlayerCustomizaton.IsBarberGuiVisible()) { return player.GetDoodadController() == null; } return false; } private static void SetVanillaCombatHintActive(GameObject? hint, bool active) { if ((Object)(object)hint != (Object)null && hint.activeSelf != active) { hint.SetActive(active); } } private static void EnsureBowSecondaryHint(KeyHints hints, bool gamepadActive) { if (_bowSecondaryHint != null && (Object)(object)_bowSecondaryHint.Root != (Object)null && (Object)(object)_bowSecondaryHintTemplate != (Object)null && (Object)(object)_bowSecondaryHintTemplate.transform.parent != (Object)null && (Object)(object)_cachedTemplateHints == (Object)(object)hints && _cachedTemplateGamepadActive == gamepadActive && (Object)(object)_bowSecondaryHint.Root.transform.parent == (Object)(object)_bowSecondaryHintTemplate.transform.parent) { return; } GameObject val = ResolveCachedCombatHintTemplate(hints, gamepadActive); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.transform.parent == (Object)null)) { if (_bowSecondaryHint != null && (Object)(object)_bowSecondaryHint.Root != (Object)null && (Object)(object)_bowSecondaryHint.Root.transform.parent == (Object)(object)val.transform.parent) { _bowSecondaryHintTemplate = val; return; } DestroyBowSecondaryHint(); _bowSecondaryHint = KeyHintCell.CloneFrom(val, "SecondaryAttacks_BowSecondaryHint", hideOnRestore: true); _bowSecondaryHintTemplate = val; _cachedTemplateHints = hints; _cachedTemplateGamepadActive = gamepadActive; _cachedCombatHintTemplate = val; _keyHintApplied = false; } } private static GameObject? ResolveCachedCombatHintTemplate(KeyHints hints, bool gamepadActive) { if ((Object)(object)_cachedTemplateHints == (Object)(object)hints && _cachedTemplateGamepadActive == gamepadActive && (Object)(object)_cachedCombatHintTemplate != (Object)null && (Object)(object)_cachedCombatHintTemplate.transform.parent != (Object)null) { return _cachedCombatHintTemplate; } _cachedTemplateHints = hints; _cachedTemplateGamepadActive = gamepadActive; _cachedCombatHintTemplate = ResolveCombatHintTemplate(hints, gamepadActive); return _cachedCombatHintTemplate; } private static string ResolveSecondaryAttackButtonLabel(bool gamepadActive) { string text = (gamepadActive ? "JoySecondaryAttack" : "SecondaryAttack"); ZInput instance = ZInput.instance; string text2 = ((instance != null) ? instance.GetBoundKeyString(text, true) : null) ?? ""; if (!string.IsNullOrWhiteSpace(text2)) { if (Localization.instance == null) { return text2; } return Localization.instance.Localize(text2); } if (!gamepadActive) { return "MMB"; } return "RB"; } private static string ResolveBlockButtonLabel(bool gamepadActive) { string text = (gamepadActive ? "JoyBlock" : "Block"); ZInput instance = ZInput.instance; string text2 = ((instance != null) ? instance.GetBoundKeyString(text, true) : null) ?? ""; if (!string.IsNullOrWhiteSpace(text2)) { if (Localization.instance == null) { return text2; } return Localization.instance.Localize(text2); } if (!gamepadActive) { return "Mouse-2"; } return "LT"; } private static void ApplyDetonateBlockHint(KeyHintCell hint, string buttonLabel) { string text = SecondaryAttackLocalization.Localize("$sa_hint_detonate", "Detonate"); if (hint.HasKeyTexts) { hint.Set(text, buttonLabel); } else { hint.SetText(text + " " + buttonLabel + ""); } } private static GameObject? ResolveCachedBlockHint(KeyHints hints, bool gamepadActive) { if (_cachedBlockHintResolved && (Object)(object)_cachedBlockHintHints == (Object)(object)hints && _cachedBlockHintGamepadActive == gamepadActive) { if ((Object)(object)_cachedBlockHint != (Object)null && (Object)(object)_cachedBlockHint.transform.parent != (Object)null) { return _cachedBlockHint; } if ((Object)(object)_cachedBlockHint == (Object)null && Time.frameCount - _cachedBlockHintFrame < 30) { return null; } } _cachedBlockHintHints = hints; _cachedBlockHintGamepadActive = gamepadActive; _cachedBlockHint = ResolveBlockHintUncached(hints, gamepadActive); _cachedBlockHintResolved = true; _cachedBlockHintFrame = Time.frameCount; return _cachedBlockHint; } private static GameObject? ResolveBlockHintUncached(KeyHints hints, bool gamepadActive) { GameObject val = FindBlockHint(ResolveCombatInputGroup(hints, gamepadActive)); if ((Object)(object)val != (Object)null) { return val; } return FindBlockHint(ResolveCombatInputGroup(hints, !gamepadActive)); } private static void ClearBlockHintCache() { _cachedBlockHintHints = null; _cachedBlockHint = null; _cachedBlockHintResolved = false; _cachedBlockHintFrame = 0; } private static Transform? ResolveCombatInputGroup(KeyHints hints, bool gamepadActive) { GameObject val = (gamepadActive ? hints.m_primaryAttackGP : hints.m_primaryAttackKB); if ((Object)(object)((val != null) ? val.transform.parent : null) != (Object)null) { return val.transform.parent; } val = (gamepadActive ? hints.m_secondaryAttackGP : hints.m_secondaryAttackKB); if (val == null) { return null; } return val.transform.parent; } private static GameObject? FindBlockHint(Transform? group) { if ((Object)(object)group == (Object)null) { return null; } for (int i = 0; i < group.childCount; i++) { GameObject gameObject = ((Component)group.GetChild(i)).gameObject; if ((string.Equals(((Object)gameObject).name, "Block", StringComparison.OrdinalIgnoreCase) || string.Equals(((Object)gameObject).name, "Text - Block", StringComparison.OrdinalIgnoreCase)) && KeyHintCell.IsUsableTemplate(gameObject)) { return gameObject; } } TMP_Text[] componentsInChildren = ((Component)group).GetComponentsInChildren(true); foreach (TMP_Text val in componentsInChildren) { if ((Object)(object)val == (Object)null || !ContainsBlockHintText(val.text)) { continue; } Transform val2 = val.transform; while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)group.parent) { GameObject gameObject2 = ((Component)val2).gameObject; if (KeyHintCell.IsUsableTemplate(gameObject2)) { return gameObject2; } val2 = val2.parent; } } return null; } private static bool ContainsBlockHintText(string? value) { if (string.IsNullOrWhiteSpace(value)) { return false; } if (value.IndexOf("$settings_block", StringComparison.OrdinalIgnoreCase) < 0) { return value.IndexOf("Block", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static ItemData? ResolveEquippedBow(Player? player) { if ((Object)(object)player == (Object)null) { return null; } ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (IsBowDrawWeapon(currentWeapon)) { return currentWeapon; } ItemData rightItem = ((Humanoid)player).GetRightItem(); if (IsBowDrawWeapon(rightItem)) { return rightItem; } ItemData leftItem = ((Humanoid)player).GetLeftItem(); if (!IsBowDrawWeapon(leftItem)) { return null; } return leftItem; } private static bool IsBowDrawWeapon(ItemData? weapon) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (weapon?.m_shared != null && (int)weapon.m_shared.m_itemType == 4) { return weapon.m_shared.m_attack.m_bowDraw; } return false; } private static GameObject? ResolveCombatHintTemplate(KeyHints hints, bool gamepadActive) { GameObject val = (gamepadActive ? hints.m_secondaryAttackGP : hints.m_secondaryAttackKB); if (KeyHintCell.IsUsableTemplate(val)) { return val; } GameObject val2 = (gamepadActive ? hints.m_secondaryAttackKB : hints.m_secondaryAttackGP); if (KeyHintCell.IsUsableTemplate(val2)) { return val2; } GameObject val3 = (gamepadActive ? hints.m_bowDrawGP : hints.m_bowDrawKB); if (KeyHintCell.IsUsableTemplate(val3)) { return val3; } GameObject val4 = (gamepadActive ? hints.m_bowDrawKB : hints.m_bowDrawGP); if (KeyHintCell.IsUsableTemplate(val4)) { return val4; } GameObject val5 = (gamepadActive ? hints.m_primaryAttackGP : hints.m_primaryAttackKB); if (KeyHintCell.IsUsableTemplate(val5)) { return val5; } GameObject val6 = (gamepadActive ? hints.m_primaryAttackKB : hints.m_primaryAttackGP); if (KeyHintCell.IsUsableTemplate(val6)) { return val6; } return null; } private static void HideBowSecondaryHint() { if (_showingBowSecondaryHint) { _bowSecondaryHint?.Restore(); _bowSecondaryHint?.RebuildParentLayout(); _showingBowSecondaryHint = false; _keyHintApplied = false; _lastButtonLabel = string.Empty; } } private static void RestoreDetonateBlockHint() { if (_showingDetonateBlockHint || _detonateBlockHintApplied) { _detonateBlockHint?.Restore(); _detonateBlockHint?.RebuildParentLayout(); _showingDetonateBlockHint = false; _detonateBlockHintApplied = false; _lastDetonateBlockButtonLabel = string.Empty; } } private static void DestroyBowSecondaryHint() { if ((Object)(object)_bowSecondaryHint?.Root != (Object)null) { Object.Destroy((Object)(object)_bowSecondaryHint.Root); } _bowSecondaryHint = null; _bowSecondaryHintTemplate = null; _cachedTemplateHints = null; _cachedCombatHintTemplate = null; _keyHintApplied = false; _lastButtonLabel = string.Empty; } } [HarmonyPatch(typeof(KeyHints), "Awake")] internal static class KeyHintsAwakeBowSecondaryPatch { private static void Postfix(KeyHints __instance) { BowSecondaryKeyHintSystem.InitializeKeyHints(__instance); } } [HarmonyPatch(typeof(KeyHints), "UpdateHints")] internal static class KeyHintsUpdateBowSecondaryPatch { private static void Postfix(KeyHints __instance) { BowSecondaryKeyHintSystem.UpdateKeyHint(__instance); } } internal static class CopiedThrowProjectileVisualSystem { internal readonly struct BurstScope { public Attack? Attack { get; } public bool Active => Attack != null; public BurstScope(Attack attack) { Attack = attack; } } internal readonly struct SpawnedProjectileVisualContext { public ItemData? Weapon { get; } public string VisualPrefabName { get; } public GameObject? AttachPrefab { get; } public EffectData[]? HitEffectPrefabs { get; } public ThrowProjectileVisualSpin.AxisMode SpinAxisMode { get; } public Vector3 VisualRotationOffset { get; } public bool SkipVisualSwap { get; } public bool Active { get { if ((Object)(object)Weapon?.m_dropPrefab != (Object)null) { return !string.IsNullOrEmpty(VisualPrefabName); } return false; } } public SpawnedProjectileVisualContext(ItemData weapon, string visualPrefabName, GameObject? attachPrefab, EffectData[]? hitEffectPrefabs, ThrowProjectileVisualSpin.AxisMode spinAxisMode, Vector3 visualRotationOffset, bool skipVisualSwap) { //IL_0026: 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) Weapon = weapon; VisualPrefabName = visualPrefabName; AttachPrefab = attachPrefab; HitEffectPrefabs = hitEffectPrefabs; SpinAxisMode = spinAxisMode; VisualRotationOffset = visualRotationOffset; SkipVisualSwap = skipVisualSwap; } } private sealed class CopiedThrowSpinState : MonoBehaviour { private ThrowProjectileVisualSpin.AxisMode _axisMode; private Vector3 _rotationOffset; private Vector3 _horizontalForward; private bool _configured; internal bool IsCurrent(ThrowProjectileVisualSpin.AxisMode axisMode, Vector3 rotationOffset, Vector3 horizontalForward) { //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_001d: 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_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_004e: Unknown result type (might be due to invalid IL or missing references) if (_configured && _axisMode == axisMode) { Vector3 val = _rotationOffset - rotationOffset; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { val = _horizontalForward - horizontalForward; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { return ThrowProjectileVisualSpin.IsConfigured(((Component)this).gameObject, axisMode, horizontalForward); } } } return false; } internal void Configure(ThrowProjectileVisualSpin.AxisMode axisMode, Vector3 rotationOffset, Vector3 horizontalForward) { //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) //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) _axisMode = axisMode; _rotationOffset = rotationOffset; _horizontalForward = horizontalForward; _configured = true; } } private const string CopiedThrowProjectileMarkerKey = "SecondaryAttacks_CopiedThrowProjectile"; private const string CopiedThrowProjectileSpinAxisKey = "SecondaryAttacks_CopiedThrowSpinAxis"; private const string CopiedThrowProjectileRotationOffsetKey = "SecondaryAttacks_CopiedThrowRotationOffset"; private const string CopiedThrowProjectileVisualRootName = "SecondaryAttacks_CopiedThrowVisualRoot"; private const string CopiedThrowProjectileSpinRootName = "SecondaryAttacks_CopiedThrowSpinRoot"; private const float SpinStateEpsilonSqr = 0.0001f; private static readonly List ActiveCopiedThrowBursts = new List(); private static readonly List RendererBuffer = new List(); internal static BurstScope BeginBurst(Attack attack) { if (!ShouldApplyCopiedThrowVisuals(attack)) { return default(BurstScope); } ActiveCopiedThrowBursts.Add(attack); return new BurstScope(attack); } internal static void EndBurst(BurstScope scope) { if (scope.Active) { int num = ActiveCopiedThrowBursts.Count - 1; if (num >= 0 && ActiveCopiedThrowBursts[num] == scope.Attack) { ActiveCopiedThrowBursts.RemoveAt(num); } else { ActiveCopiedThrowBursts.Remove(scope.Attack); } } } internal static void EndBurst(ref BurstScope scope) { EndBurst(scope); scope = default(BurstScope); } internal static void TryApplyToProjectileSetup(Projectile projectile, ItemData item) { if ((Object)(object)projectile == (Object)null || ActiveCopiedThrowBursts.Count == 0) { return; } Attack val = ActiveCopiedThrowBursts[ActiveCopiedThrowBursts.Count - 1]; if (val != null) { ItemData val2 = val.m_weapon ?? item; if (!((Object)(object)val2?.m_dropPrefab == (Object)null)) { MoveProjectileClearOfOwner(projectile, val); SecondaryAttackDefinition definition = ResolveCopiedThrowDefinition(val, val2, ((Object)val2.m_dropPrefab).name); SpawnedProjectileVisualContext context = CreateSpawnedProjectileVisualContext(val2, val.m_attackProjectile, definition, includeHitEffects: false); ApplyCurrentWeaponVisual(projectile, context); ApplyCurrentWeaponHitEffects(projectile, val2); ApplyCopiedThrowAttribution(projectile, val2); MeleeBoomerangProjectileSystem.TryApplyToProjectileSetup(projectile, val, val2); MeleeProjectileHitCascadeSystem.RegisterOnProjectileHitSource(projectile, val, val2); } } } internal static void PrepareProjectileIfNeeded(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return; } ZNetView component = ((Component)projectile).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.GetZDO() != null && component.GetZDO().GetBool("SecondaryAttacks_CopiedThrowProjectile", false)) { TryApplyHitEffectsFromSyncedVisual(projectile, component); if (!projectile.m_changedVisual) { PrepareProjectileForVisualSwap(projectile); } } } internal static void EnsureProjectileVisualSpinIfNeeded(Projectile projectile) { if (IsMarkedCopiedThrowProjectile(projectile)) { string visualPrefabName = TryResolveSyncedVisualPrefabName(projectile); ApplyCopiedThrowVisualSpin(projectile, TryResolveSyncedVisualItem(visualPrefabName)?.m_itemData, visualPrefabName); } } internal static void ApplyCurrentWeaponVisualForSpawnedProjectile(Projectile projectile, ItemData weapon) { ApplyCurrentWeaponVisualForSpawnedProjectile(projectile, CreateSpawnedProjectileVisualContext(weapon)); } internal static void ApplyCurrentWeaponVisualForSpawnedProjectile(Projectile projectile, SpawnedProjectileVisualContext context) { if (!((Object)(object)projectile == (Object)null) && context.Active) { if (!context.SkipVisualSwap) { ApplyCurrentWeaponVisual(projectile, context); } ApplyHitEffects(projectile, context.HitEffectPrefabs); } } internal static SpawnedProjectileVisualContext CreateSpawnedProjectileVisualContext(ItemData weapon) { return CreateSpawnedProjectileVisualContext(weapon, includeHitEffects: true); } internal static SpawnedProjectileVisualContext CreateSpawnedProjectileVisualContext(ItemData weapon, GameObject? sourceProjectilePrefab) { return CreateSpawnedProjectileVisualContext(weapon, sourceProjectilePrefab, includeHitEffects: true); } private static SpawnedProjectileVisualContext CreateSpawnedProjectileVisualContext(ItemData weapon, bool includeHitEffects) { return CreateSpawnedProjectileVisualContext(weapon, null, includeHitEffects); } private static SpawnedProjectileVisualContext CreateSpawnedProjectileVisualContext(ItemData weapon, GameObject? sourceProjectilePrefab, bool includeHitEffects) { return CreateSpawnedProjectileVisualContext(weapon, sourceProjectilePrefab, null, includeHitEffects); } private static SpawnedProjectileVisualContext CreateSpawnedProjectileVisualContext(ItemData weapon, GameObject? sourceProjectilePrefab, SecondaryAttackDefinition? definition, bool includeHitEffects) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)weapon?.m_dropPrefab == (Object)null) { return default(SpawnedProjectileVisualContext); } bool flag = UsesNativeProjectileVisual(weapon, sourceProjectilePrefab); GameObject attachPrefab = null; if (!flag) { attachPrefab = ResolveAttachGameObject(weapon.m_dropPrefab); } if (definition == null) { definition = ResolveCopiedThrowDefinition(weapon, ((Object)weapon.m_dropPrefab).name); } ThrowProjectileVisualSpin.AxisMode valueOrDefault = ResolveConfiguredCopiedThrowSpinAxisMode(definition).GetValueOrDefault(); return new SpawnedProjectileVisualContext(weapon, ((Object)weapon.m_dropPrefab).name, attachPrefab, includeHitEffects ? CopyHitEffectPrefabs(weapon.m_shared?.m_hitEffect) : null, valueOrDefault, ResolveConfiguredCopiedThrowVisualRotationOffset(definition), flag); } internal static bool UsesNativeProjectileVisual(ItemData weapon, GameObject? sourceProjectilePrefab) { if ((Object)(object)weapon?.m_dropPrefab == (Object)null || (Object)(object)sourceProjectilePrefab == (Object)null) { return false; } return IsSameProjectilePrefab(sourceProjectilePrefab, weapon.m_shared?.m_secondaryAttack?.m_attackProjectile); } private static bool IsSameProjectilePrefab(GameObject sourceProjectilePrefab, GameObject? weaponProjectilePrefab) { if ((Object)(object)weaponProjectilePrefab != (Object)null) { if (sourceProjectilePrefab != weaponProjectilePrefab) { return string.Equals(((Object)sourceProjectilePrefab).name, ((Object)weaponProjectilePrefab).name, StringComparison.OrdinalIgnoreCase); } return true; } return false; } private static GameObject? ResolveAttachGameObject(GameObject itemPrefab) { Transform val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.transform.Find("attach") : null); if ((Object)(object)val == (Object)null) { return null; } Transform val2 = val.Find("attachobj"); if (!((Object)(object)val2 != (Object)null)) { return ((Component)val).gameObject; } return ((Component)val2).gameObject; } private static bool ShouldApplyCopiedThrowVisuals(Attack attack) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 if ((Object)(object)attack?.m_weapon?.m_dropPrefab == (Object)null || (Object)(object)attack.m_attackProjectile == (Object)null || (int)attack.m_attackType != 2) { return false; } if (SecondaryAttackStartAttackDispatch.IsProjectilePresetOriginalCooldownFallback(attack)) { return false; } if (SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) && activeAttack?.Definition.Behavior is CopiedSecondaryBehavior) { return true; } if (!IsConfiguredCopiedSecondaryAttack(attack)) { return false; } return true; } private static bool IsConfiguredCopiedSecondaryAttack(Attack attack) { if (!SecondaryAttackRuntimeFacade.TryGetDefinition(attack.m_weapon, out SecondaryAttackDefinition definition) || !(definition.Behavior is CopiedSecondaryBehavior)) { return false; } Humanoid character = attack.m_character; if (character != null && character.m_currentAttack == attack) { return character.m_currentAttackIsSecondary; } return attack == attack.m_weapon.m_shared.m_secondaryAttack; } private static void MoveProjectileClearOfOwner(Projectile projectile, Attack attack) { //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_003f: 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_0044: 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_005a: 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_0091: 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_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_00a8: 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_00b0: 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_00ba: 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) Character val = (Character)(((object)ProjectileAccess.GetOwner(projectile)) ?? ((object)attack.m_character)); if ((Object)(object)val == (Object)null) { return; } Vector3 velocity = ProjectileAccess.GetVelocity(projectile); Vector3 val2 = ((((Vector3)(ref velocity)).sqrMagnitude > 0.001f) ? ((Vector3)(ref velocity)).normalized : ((Component)projectile).transform.forward); if (!(((Vector3)(ref val2)).sqrMagnitude < 0.001f)) { Vector3 centerPoint = val.GetCenterPoint(); float num = Mathf.Max(val.GetRadius() + Mathf.Max(0.1f, projectile.m_rayRadius) + 0.35f, 1.25f); Vector3 position = ((Component)projectile).transform.position; float num2 = Vector3.Dot(position - centerPoint, val2); if (!(num2 >= num)) { Vector3 position2 = position + val2 * (num - num2); ((Component)projectile).transform.position = position2; } } } private static void ApplyCopiedThrowAttribution(Projectile projectile, ItemData weapon) { if (SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) && definition.Behavior is CopiedSecondaryBehavior) { SecondaryAttackRuntimeFacade.SetProjectileAttackAttribution(projectile, definition.PrefabName, secondaryAttack: true, definition, disableCurrentAttackFallback: false); SecondaryAttackProjectileToolTierSystem.ApplyToHitData(ProjectileAccess.GetOriginalHitData(projectile), projectile, weapon); } } private static void ApplyCurrentWeaponVisual(Projectile projectile, ItemData weapon) { ApplyCurrentWeaponVisual(projectile, CreateSpawnedProjectileVisualContext(weapon, includeHitEffects: false)); } private static void ApplyCurrentWeaponVisual(Projectile projectile, SpawnedProjectileVisualContext context) { if (!context.Active || context.SkipVisualSwap) { return; } PrepareProjectileForVisualSwap(projectile); MarkCopiedThrowProjectile(projectile, context); ZNetView component = ((Component)projectile).GetComponent(); bool flag = (Object)(object)component != (Object)null && component.IsValid(); if (projectile.m_canChangeVisuals && (Object)(object)projectile.m_visual != (Object)null && flag) { if (component.IsOwner()) { component.GetZDO().Set(ZDOVars.s_visual, context.VisualPrefabName); } projectile.UpdateVisual(); ApplyCopiedThrowVisualSpin(projectile, context); } else { ApplyLocalFallbackVisual(projectile, context); } } private static void ApplyCurrentWeaponHitEffects(Projectile projectile, ItemData weapon) { ApplyHitEffects(projectile, weapon.m_shared?.m_hitEffect); } private static void TryApplyHitEffectsFromSyncedVisual(Projectile projectile, ZNetView nview) { ApplyHitEffects(projectile, TryResolveSyncedVisualItem(nview)?.m_itemData?.m_shared?.m_hitEffect); } private static void ApplyHitEffects(Projectile projectile, EffectList? hitEffect) { if (hitEffect != null && hitEffect.HasEffects()) { ApplyHitEffects(projectile, CopyHitEffectPrefabs(hitEffect)); } } private static void ApplyHitEffects(Projectile projectile, EffectData[]? hitEffectPrefabs) { //IL_0009: 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_001a: Expected O, but got Unknown if (hitEffectPrefabs != null && hitEffectPrefabs.Length != 0) { projectile.m_hitEffects = new EffectList { m_effectPrefabs = hitEffectPrefabs }; } } private static EffectData[]? CopyHitEffectPrefabs(EffectList? hitEffect) { if (hitEffect?.m_effectPrefabs == null || !hitEffect.HasEffects()) { return null; } return (EffectData[])hitEffect.m_effectPrefabs.Clone(); } private static void MarkCopiedThrowProjectile(Projectile projectile, SpawnedProjectileVisualContext context) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)projectile).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner() && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); zDO.Set("SecondaryAttacks_CopiedThrowProjectile", true); zDO.Set("SecondaryAttacks_CopiedThrowSpinAxis", ToProjectileSpinAxisString(context.SpinAxisMode)); zDO.Set("SecondaryAttacks_CopiedThrowRotationOffset", SerializeVector3(context.VisualRotationOffset)); } } private static bool IsMarkedCopiedThrowProjectile(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return false; } ZNetView component = ((Component)projectile).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { return component.GetZDO().GetBool("SecondaryAttacks_CopiedThrowProjectile", false); } return false; } private static void PrepareProjectileForVisualSwap(Projectile projectile) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Transform val = ((Component)projectile).transform.Find("SecondaryAttacks_CopiedThrowVisualRoot"); if ((Object)(object)val != (Object)null) { projectile.m_visual = ((Component)val).gameObject; projectile.m_canChangeVisuals = true; return; } HideSourcePresentation(projectile); GameObject val2 = new GameObject("SecondaryAttacks_CopiedThrowVisualRoot"); val2.transform.SetParent(((Component)projectile).transform, false); val2.layer = ((Component)projectile).gameObject.layer; projectile.m_visual = val2; projectile.m_canChangeVisuals = true; } private static void HideSourcePresentation(Projectile projectile) { RendererBuffer.Clear(); try { ((Component)projectile).GetComponentsInChildren(true, RendererBuffer); foreach (Renderer item in RendererBuffer) { if (!(item is TrailRenderer) && !(item is ParticleSystemRenderer)) { item.enabled = false; } } } finally { RendererBuffer.Clear(); } } private static void ApplyLocalFallbackVisual(Projectile projectile, ItemData weapon) { ApplyLocalFallbackVisual(projectile, CreateSpawnedProjectileVisualContext(weapon, includeHitEffects: false)); } private static void ApplyLocalFallbackVisual(Projectile projectile, SpawnedProjectileVisualContext context) { //IL_0056: 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 (context.Active && !((Object)(object)context.AttachPrefab == (Object)null)) { GameObject visual = projectile.m_visual; GameObject val = Object.Instantiate(context.AttachPrefab, ((Component)projectile).transform, false); ((Object)val).name = ((Object)context.AttachPrefab).name + "(ProjectileVisual)"; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; if ((Object)(object)visual != (Object)null && (Object)(object)visual != (Object)(object)val) { visual.SetActive(false); } IEquipmentVisual componentInChildren = val.GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.Setup(context.Weapon.m_variant); } projectile.m_visual = val; ApplyCopiedThrowVisualSpin(projectile, context); } } private static void ApplyCopiedThrowVisualSpin(Projectile projectile, ItemData? weapon, string? visualPrefabName = null) { //IL_0041: 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_0042: 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_0049: 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_005b: 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) if (!((Object)(object)projectile == (Object)null)) { SecondaryAttackDefinition definition = ResolveCopiedThrowDefinition(weapon, visualPrefabName); ThrowProjectileVisualSpin.AxisMode axisMode; Vector3 rotationOffset; bool num = TryResolveSyncedProjectileSpin(projectile, out axisMode, out rotationOffset); ThrowProjectileVisualSpin.AxisMode valueOrDefault = (num ? new ThrowProjectileVisualSpin.AxisMode?(axisMode) : ResolveConfiguredCopiedThrowSpinAxisMode(definition)).GetValueOrDefault(); Vector3 rotationOffset2 = (num ? rotationOffset : ResolveConfiguredCopiedThrowVisualRotationOffset(definition)); Vector3 horizontalForward = ((valueOrDefault == ThrowProjectileVisualSpin.AxisMode.HorizontalSide) ? ResolveOwnerHorizontalForward(projectile) : Vector3.zero); ApplyCopiedThrowSpinConfiguration(projectile, valueOrDefault, rotationOffset2, horizontalForward); } } private static void ApplyCopiedThrowVisualSpin(Projectile projectile, SpawnedProjectileVisualContext context) { //IL_0025: 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_002a: 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) if (!((Object)(object)projectile == (Object)null) && context.Active) { Vector3 horizontalForward = ((context.SpinAxisMode == ThrowProjectileVisualSpin.AxisMode.HorizontalSide) ? ResolveOwnerHorizontalForward(projectile) : Vector3.zero); ApplyCopiedThrowSpinConfiguration(projectile, context.SpinAxisMode, context.VisualRotationOffset, horizontalForward); } } private static void ApplyCopiedThrowSpinConfiguration(Projectile projectile, ThrowProjectileVisualSpin.AxisMode axisMode, Vector3 rotationOffset, Vector3 horizontalForward) { //IL_0042: 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_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_0031: 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) GameObject val = ResolveCopiedThrowSpinVisual(projectile.m_visual); if (!((Object)(object)val == (Object)null)) { CopiedThrowSpinState copiedThrowSpinState = val.GetComponent() ?? val.AddComponent(); if (!IsProjectileVisualSpinCleared(projectile) || !copiedThrowSpinState.IsCurrent(axisMode, rotationOffset, horizontalForward)) { ClearProjectileVisualSpin(projectile); ThrowProjectileVisualRotationOffset.Ensure(val, rotationOffset); ThrowProjectileVisualSpin.Ensure(val, axisMode, horizontalForward); copiedThrowSpinState.Configure(axisMode, rotationOffset, horizontalForward); } } } private static SecondaryAttackDefinition? ResolveCopiedThrowDefinition(Attack? attack, ItemData? weapon, string? visualPrefabName) { if (attack != null && SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) && activeAttack?.Definition != null) { return activeAttack.Definition; } return ResolveCopiedThrowDefinition(weapon, visualPrefabName); } private static SecondaryAttackDefinition? ResolveCopiedThrowDefinition(ItemData? weapon, string? visualPrefabName) { if (weapon != null && SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return definition; } string text = visualPrefabName?.Trim() ?? ""; if (text.Length > 0 && SecondaryAttackRuntimeFacade.TryGetDefinition(text, out SecondaryAttackDefinition definition2)) { return definition2; } return null; } private static ThrowProjectileVisualSpin.AxisMode? ResolveConfiguredCopiedThrowSpinAxisMode(SecondaryAttackDefinition? definition) { if (definition == null) { return null; } if (definition.Boomerang != null && ProjectileSpinAxis.TryResolveAxisMode(definition.Boomerang.ProjectileSpinAxis, out var axisMode)) { return axisMode; } if (definition.OnProjectileHit == null || !ProjectileSpinAxis.TryResolveAxisMode(definition.OnProjectileHit.ProjectileSpinAxis, out var axisMode2)) { return null; } return axisMode2; } private static Vector3 ResolveConfiguredCopiedThrowVisualRotationOffset(SecondaryAttackDefinition? definition) { //IL_0014: 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_0059: 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) if (definition?.Boomerang != null) { return definition.Boomerang.ProjectileVisualRotationOffset; } return (Vector3)(((??)definition?.OnProjectileHit?.ProjectileVisualRotationOffset) ?? Vector3.zero); } private static GameObject? ResolveCopiedThrowSpinVisual(GameObject? visual) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0055: 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_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)visual == (Object)null) { return null; } Transform transform = visual.transform; Transform val = transform.Find("SecondaryAttacks_CopiedThrowSpinRoot"); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject; } GameObject val2 = new GameObject("SecondaryAttacks_CopiedThrowSpinRoot"); val2.layer = visual.layer; Transform transform2 = val2.transform; transform2.SetParent(transform, false); transform2.localPosition = Vector3.zero; transform2.localRotation = Quaternion.identity; transform2.localScale = Vector3.one; bool flag = false; for (int num = transform.childCount - 1; num >= 0; num--) { Transform child = transform.GetChild(num); if (!((Object)(object)child == (Object)(object)transform2)) { child.SetParent(transform2, false); flag = true; } } if (flag) { return val2; } Object.Destroy((Object)(object)val2); return visual; } private static bool TryResolveSyncedProjectileSpin(Projectile projectile, out ThrowProjectileVisualSpin.AxisMode axisMode, out Vector3 rotationOffset) { //IL_0004: 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_0078: 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) axisMode = ThrowProjectileVisualSpin.AxisMode.None; rotationOffset = Vector3.zero; ZNetView val = (((Object)(object)projectile != (Object)null) ? ((Component)projectile).GetComponent() : null); string raw = default(string); if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null || !val.GetZDO().GetString("SecondaryAttacks_CopiedThrowSpinAxis", ref raw) || !ProjectileSpinAxis.TryResolveAxisMode(raw, out axisMode)) { return false; } string raw2 = default(string); if (val.GetZDO().GetString("SecondaryAttacks_CopiedThrowRotationOffset", ref raw2) && TryParseVector3(raw2, out var value)) { rotationOffset = value; } return true; } private static string ToProjectileSpinAxisString(ThrowProjectileVisualSpin.AxisMode axisMode) { return axisMode switch { ThrowProjectileVisualSpin.AxisMode.HorizontalSide => "horizontal", ThrowProjectileVisualSpin.AxisMode.WorldUp => "vertical", _ => "none", }; } private static string SerializeVector3(Vector3 value) { //IL_000a: 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_0020: Unknown result type (might be due to invalid IL or missing references) return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", value.x, value.y, value.z); } private static bool TryParseVector3(string raw, out Vector3 value) { //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_007e: 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) value = Vector3.zero; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } value = new Vector3(result, result2, result3); return true; } private static Vector3 ResolveOwnerHorizontalForward(Projectile projectile) { //IL_0023: 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_0028: 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_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_004b: 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) Character owner = ProjectileAccess.GetOwner(projectile); Vector3 val = (((Object)(object)owner != (Object)null) ? ((Component)owner).transform.forward : ((Component)projectile).transform.forward); val = Vector3.ProjectOnPlane(val, Vector3.up); if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.zero; } return ((Vector3)(ref val)).normalized; } private static void ClearProjectileVisualSpin(Projectile projectile) { projectile.m_rotateVisual = 0f; projectile.m_rotateVisualY = 0f; projectile.m_rotateVisualZ = 0f; } private static bool IsProjectileVisualSpinCleared(Projectile projectile) { if (Mathf.Approximately(projectile.m_rotateVisual, 0f) && Mathf.Approximately(projectile.m_rotateVisualY, 0f)) { return Mathf.Approximately(projectile.m_rotateVisualZ, 0f); } return false; } private static string? TryResolveSyncedVisualPrefabName(Projectile projectile) { ZNetView val = ((projectile != null) ? ((Component)projectile).GetComponent() : null); if (!((Object)(object)val == (Object)null)) { return TryResolveSyncedVisualPrefabName(val); } return null; } private static string? TryResolveSyncedVisualPrefabName(ZNetView nview) { string result = default(string); if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO() == null || !nview.GetZDO().GetString(ZDOVars.s_visual, ref result)) { return null; } return result; } private static ItemDrop? TryResolveSyncedVisualItem(ZNetView nview) { return TryResolveSyncedVisualItem(TryResolveSyncedVisualPrefabName(nview)); } private static ItemDrop? TryResolveSyncedVisualItem(string? visualPrefabName) { if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrWhiteSpace(visualPrefabName)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(visualPrefabName); if (itemPrefab == null) { return null; } return itemPrefab.GetComponent(); } } internal static class CleavingThrustSystem { private readonly struct CleavingThrustHitTarget { public IDestructible Destructible { get; } public Character? Character { get; } public Collider? Collider { get; } public Vector3 Point { get; } public float Distance { get; } public CleavingThrustHitTarget(IDestructible destructible, Character? character, Collider? collider, Vector3 point, float distance) { //IL_0016: 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) Destructible = destructible; Character = character; Collider = collider; Point = point; Distance = distance; } } private readonly struct CleavingThrustAttackShape { public float Range { get; } public float Angle { get; } public float RayWidth { get; } public float CharacterRayWidth { get; } public CleavingThrustAttackShape(float range, float angle, float rayWidth, float characterRayWidth) { Range = range; Angle = angle; RayWidth = rayWidth; CharacterRayWidth = characterRayWidth; } } private const float FanStepDegrees = 4f; private const float TrailRangeScaleFactor = 3f; private static readonly List HitTargets = new List(); private static int _environmentMask; private static int _destructibleMask; internal static bool CanHandle(Attack attack) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (attack != null && (Object)(object)attack.m_character != (Object)null && attack.m_weapon?.m_shared != null && (int)attack.m_attackType == 0 && (Object)(object)attack.m_attackProjectile == (Object)null && attack.m_attackRange > 0f && attack.m_attackRayWidth > 0f) { return string.Equals(attack.m_attackAnimation, "greatsword_secondary", StringComparison.OrdinalIgnoreCase); } return false; } internal static void Trigger(Attack attack, SecondaryAttackDefinition definition) { //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_002a: 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) if (!CanHandle(attack) || definition.CleavingThrust == null) { return; } CleavingThrustDefinition cleavingThrust = definition.CleavingThrust; Humanoid character = attack.m_character; Vector3 origin = ResolveOrigin(attack); Vector3 forward = ResolveForward((Character)(object)character); SecondaryAttackManager.PlayTriggeredAttackEffects(attack, definition.CleavingThrust?.DurabilityFactor ?? definition.DurabilityFactor); try { GatherTargets(attack, cleavingThrust, origin, forward); int count = HitTargets.Count; for (int i = 0; i < count; i++) { ApplyHit(attack, cleavingThrust, HitTargets[i], count); } } finally { HitTargets.Clear(); } } internal static float ResolveVisualRangeScale(Attack attack, SecondaryAttackDefinition definition) { if (!CanHandle(attack) || definition.CleavingThrust == null || attack.m_attackRange <= 0.01f) { return 1f; } float num = Mathf.Max(1f, definition.CleavingThrust.RangeFactor); return Mathf.Max(1f, 1f + (num - 1f) * 3f); } private static Vector3 ResolveOrigin(Attack attack) { //IL_000d: 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_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_0032: 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) Transform transform = ((Component)attack.m_character).transform; return transform.position + Vector3.up * Mathf.Max(0f, attack.m_attackHeight) + transform.right * attack.m_attackOffset; } private static Vector3 ResolveForward(Character attacker) { //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_0015: 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_002a: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(((Component)attacker).transform.forward, Vector3.up); if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return ((Component)attacker).transform.forward; } return ((Vector3)(ref val)).normalized; } private static void GatherTargets(Attack attack, CleavingThrustDefinition cleavingThrust, Vector3 origin, Vector3 forward) { //IL_0030: 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_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) HitTargets.Clear(); _ = attack.m_character; CleavingThrustAttackShape shape = ResolveAttackShape(attack, cleavingThrust); foreach (Character allCharacter in Character.GetAllCharacters()) { if (TryResolveTarget(attack, allCharacter, origin, forward, shape, hitThroughWalls: false, out var target)) { HitTargets.Add(target); } } GatherDestructibleTargets(attack, origin, forward, shape, hitThroughWalls: false); HitTargets.Sort((CleavingThrustHitTarget left, CleavingThrustHitTarget right) => left.Distance.CompareTo(right.Distance)); } private static CleavingThrustAttackShape ResolveAttackShape(Attack attack, CleavingThrustDefinition cleavingThrust) { float num = Mathf.Max(0.01f, attack.m_attackRayWidth); float characterRayWidth = Mathf.Max(num, num + Mathf.Max(0f, attack.m_attackRayWidthCharExtra)); return new CleavingThrustAttackShape(Mathf.Max(0.1f, attack.m_attackRange * cleavingThrust.RangeFactor), Mathf.Clamp(attack.m_attackAngle, 1f, 360f), num, characterRayWidth); } private static void GatherDestructibleTargets(Attack attack, Vector3 origin, Vector3 forward, CleavingThrustAttackShape shape, bool hitThroughWalls) { //IL_0042: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_00da: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); foreach (CleavingThrustHitTarget hitTarget in HitTargets) { hashSet.Add(hitTarget.Destructible); } Collider[] array = Physics.OverlapSphere(origin, shape.Range + shape.RayWidth, GetDestructibleMask(attack), (QueryTriggerInteraction)1); foreach (Collider val in array) { if ((Object)(object)val == (Object)null) { continue; } IDestructible val2 = ResolveDestructible(val); if (val2 == null || val2 is Character || !hashSet.Add(val2) || !IsValidDestructibleTarget(val2)) { continue; } MonoBehaviour val3 = (MonoBehaviour)(object)((val2 is MonoBehaviour) ? val2 : null); if (val3 != null) { Vector3 val4 = ResolveDestructiblePoint(val, ((Component)val3).transform.position, origin); if (TryResolveAttackShapePoint(origin, forward, val4, shape, useCharacterWidth: false, out var distance) && (hitThroughWalls || !IsBlockedByEnvironment(origin, val4, val2))) { HitTargets.Add(new CleavingThrustHitTarget(val2, null, val, val4, distance)); } } } } private static bool TryResolveTarget(Attack attack, Character? candidate, Vector3 origin, Vector3 forward, CleavingThrustAttackShape shape, bool hitThroughWalls, out CleavingThrustHitTarget target) { //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_003d: 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_003f: 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_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) target = default(CleavingThrustHitTarget); Character character = (Character)(object)attack.m_character; if ((Object)(object)candidate == (Object)null || (Object)(object)candidate == (Object)(object)character || candidate.IsDead()) { return false; } if (!IsValidTarget(attack, candidate)) { return false; } Vector3 centerPoint = candidate.GetCenterPoint(); if (!TryResolveAttackShapePoint(origin, forward, centerPoint, shape, useCharacterWidth: true, out var distance)) { return false; } if (!hitThroughWalls && IsBlockedByEnvironment(origin, centerPoint, (IDestructible?)(object)candidate)) { return false; } Collider componentInChildren = ((Component)candidate).GetComponentInChildren(); target = new CleavingThrustHitTarget((IDestructible)(object)candidate, candidate, componentInChildren, centerPoint, distance); return true; } private static bool TryResolveAttackShapePoint(Vector3 origin, Vector3 forward, Vector3 point, CleavingThrustAttackShape shape, bool useCharacterWidth, out float distance) { //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) //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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_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_00bb: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00cc: 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_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) //IL_0104: 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_0107: 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) distance = 0f; Vector3 val = point - origin; Vector3 val2 = Vector3.ProjectOnPlane(val, Vector3.up); float num = (useCharacterWidth ? shape.CharacterRayWidth : shape.RayWidth); float num2 = shape.Range + num; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude > num2 * num2) { return false; } distance = Mathf.Sqrt(sqrMagnitude); if (((Vector3)(ref val)).sqrMagnitude <= num * num) { return true; } float num3 = num * num; int num4 = Mathf.Max(1, Mathf.CeilToInt(shape.Angle / 4f)); float num5 = shape.Angle * 0.5f; for (int i = 0; i <= num4; i++) { float num6 = (float)i / (float)num4; Vector3 val3 = Quaternion.AngleAxis(Mathf.Lerp(0f - num5, num5, num6), Vector3.up) * forward; Vector3 normalized = ((Vector3)(ref val3)).normalized; float num7 = Vector3.Dot(val2, normalized); if (!(num7 < 0f - num) && !(num7 > shape.Range + num)) { Vector3 val4 = normalized * Mathf.Clamp(num7, 0f, shape.Range); val3 = val - val4; if (((Vector3)(ref val3)).sqrMagnitude <= num3) { return true; } } } return false; } private static bool IsValidTarget(Attack attack, Character target) { Character character = (Character)(object)attack.m_character; bool flag = BaseAI.IsEnemy(character, target) || ((Object)(object)target.GetBaseAI() != (Object)null && target.GetBaseAI().IsAggravatable() && character.IsPlayer()); if (((!attack.m_hitFriendly || character.IsTamed()) && !character.IsPlayer() && !flag) || (!attack.m_weapon.m_shared.m_tamedOnly && character.IsPlayer() && !character.IsPVPEnabled() && !flag) || (attack.m_weapon.m_shared.m_tamedOnly && !target.IsTamed())) { return false; } if (attack.m_weapon.m_shared.m_dodgeable && target.IsDodgeInvincible()) { if (target.IsPlayer()) { Character obj = ((target is Player) ? target : null); if (obj != null) { ((Player)obj).HitWhileDodging(); } } return false; } return true; } private static bool IsValidDestructibleTarget(IDestructible destructible) { //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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 DestructibleType destructibleType = destructible.GetDestructibleType(); if ((int)destructibleType != 0) { return (int)destructibleType != 4; } return false; } private static bool IsBlockedByEnvironment(Vector3 origin, Vector3 targetPoint, IDestructible? allowedTarget) { //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_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_0061: 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_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) if (_environmentMask == 0) { _environmentMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain" }); } Vector3 val = targetPoint - origin; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.01f) { return false; } RaycastHit[] array = Physics.RaycastAll(origin, val / magnitude, magnitude, _environmentMask, (QueryTriggerInteraction)1); 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 val2 = array2[num]; if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && (allowedTarget == null || ResolveDestructible(((RaycastHit)(ref val2)).collider) != allowedTarget)) { return true; } } return false; } private static int GetDestructibleMask(Attack attack) { if (Attack.m_attackMask != 0) { return Attack.m_attackMask; } if (_destructibleMask == 0) { _destructibleMask = LayerMask.GetMask(new string[7] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "vehicle" }); } return _destructibleMask; } private static IDestructible? ResolveDestructible(Collider collider) { GameObject val = Projectile.FindHitObject(collider); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent(); } private static Vector3 ResolveDestructiblePoint(Collider collider, Vector3 fallbackPoint, Vector3 origin) { //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_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) //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) //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_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_003d: 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) Vector3 val = SecondaryAttackManager.ResolveSafeClosestPoint(collider, origin); Vector3 val2 = val - origin; if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { Bounds bounds = collider.bounds; val = ((Bounds)(ref bounds)).center; } if (!(((Vector3)(ref val)).sqrMagnitude > 0f)) { return fallbackPoint; } return val; } private static void ApplyHit(Attack attack, CleavingThrustDefinition cleavingThrust, CleavingThrustHitTarget target, int hitCount) { //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_001b: 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_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_007d: 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_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) Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; SkillType skillType = weapon.m_shared.m_skillType; float num = character.GetRandomSkillFactor(skillType); if (attack.m_multiHit && attack.m_lowerDamagePerHit && hitCount > 1) { num /= (float)hitCount * 0.75f; } HitData val = SecondaryAttackHitDataFactory.CreateMeleeHit(attack, target.Collider, target.Point, ResolveHitDirection(attack, target.Point), num, cleavingThrust.DamageFactor, cleavingThrust.PushFactor, attack.m_raiseSkillAmount); character.GetSEMan().ModifyAttack(skillType, ref val); weapon.m_shared.m_hitEffect.Create(target.Point, Quaternion.identity, (Transform)null, 1f, -1); attack.m_hitEffect.Create(target.Point, Quaternion.identity, (Transform)null, 1f, -1); if ((Object)(object)target.Character != (Object)null) { TrySpawnOnHit(attack, target.Character); } target.Destructible.Damage(val); if ((Object)(object)target.Character != (Object)null && attack.m_attackHealthReturnHit > 0f) { character.Heal(attack.m_attackHealthReturnHit, true); } if ((Object)(object)target.Character != (Object)null) { SecondaryAttackAdrenalineSystem.TryGrantOnce(attack, target.Character, 1f, "cleavingThrust"); } } private static Vector3 ResolveHitDirection(Attack attack, Vector3 hitPoint) { //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_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_002e: 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) Vector3 val = hitPoint - ResolveOrigin(attack); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)attack.m_character).transform.forward; } return ((Vector3)(ref val)).normalized; } private static void TrySpawnOnHit(Attack attack, Character target) { //IL_003f: 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_006f: Unknown result type (might be due to invalid IL or missing references) if (!(attack.m_spawnOnHitChance <= 0f) && !((Object)(object)attack.m_spawnOnHit == (Object)null) && !(Random.Range(0f, 1f) >= attack.m_spawnOnHitChance)) { IProjectile componentInChildren = Object.Instantiate(attack.m_spawnOnHit, ((Component)target).transform.position, ((Component)target).transform.rotation).GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.Setup((Character)(object)attack.m_character, ((Component)attack.m_character).transform.forward, -1f, (HitData)null, attack.m_weapon, attack.m_lastUsedAmmo); } } } } internal static class DirectWeaponHitContextSystem { internal readonly struct Scope { internal ScopeKind Kind { get; } internal Scope(ScopeKind kind) { Kind = kind; } } internal enum ScopeKind { None, DirectHit, CharacterDamage } private static int _directHitDepth; private static int _characterDamageDepth; private static bool ShouldHandleDirectWeaponHit { get { if (_directHitDepth > 0 && _characterDamageDepth == 1 && !LaunchSlamSystem.IsApplyingLandingDamage && !KnockbackChainSystem.IsApplyingChainDamage) { return !MeleeProjectileHitCascadeSystem.IsApplyingImpactBurstDamage; } return false; } } internal static void ApplyDirectWeaponHitEffects(Character target, ref HitData hit) { //IL_000e: 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_0022: Expected O, but got Unknown //IL_0022: Expected O, but got Unknown if (ShouldHandleDirectWeaponHit && !((Object)hit.GetAttacker() != (Object)Player.m_localPlayer) && TryGetEffectiveAttackContext(out Player localPlayer, out bool secondaryAttack, out SecondaryAttackDefinition definition)) { if (definition?.SneakAmbush != null) { SneakAmbushSystem.TryTriggerForSecondaryHit(localPlayer, target, secondaryAttack, definition); } if (!KnockbackChainSystem.TryApplyForSecondaryHit(localPlayer, target, secondaryAttack, definition, ref hit)) { LaunchSlamSystem.TryApplyForSecondaryHit(localPlayer, target, secondaryAttack, definition, ref hit); } } } internal static Scope BeginAttackHit(Attack attack) { if ((Object)(object)attack?.m_character != (Object)(object)Player.m_localPlayer) { return default(Scope); } _directHitDepth++; return new Scope(ScopeKind.DirectHit); } internal static Scope BeginProjectileHit(Projectile projectile) { if ((Object)(object)projectile == (Object)null || (Object)(object)ProjectileAccess.GetOwner(projectile) != (Object)(object)Player.m_localPlayer || IsSecondaryAttackProjectile(projectile)) { return default(Scope); } _directHitDepth++; return new Scope(ScopeKind.DirectHit); } internal static Scope BeginCharacterDamage() { _characterDamageDepth++; return new Scope(ScopeKind.CharacterDamage); } internal static void End(Scope scope) { switch (scope.Kind) { case ScopeKind.DirectHit: if (_directHitDepth > 0) { _directHitDepth--; } break; case ScopeKind.CharacterDamage: if (_characterDamageDepth > 0) { _characterDamageDepth--; } break; } } internal static void End(ref Scope scope) { End(scope); scope = default(Scope); } private static bool IsSecondaryAttackProjectile(Projectile projectile) { ProjectileAttackAttribution attribution; bool flag = SecondaryAttackRuntimeContext.TryGetProjectileAttackAttribution(projectile, out attribution); if (flag) { bool flag2 = ((attribution != null && (attribution.SecondaryAttack || attribution.DisableCurrentAttackFallback)) ? true : false); flag = flag2; } return flag; } private static bool TryGetEffectiveAttackContext(out Player localPlayer, out bool secondaryAttack, out SecondaryAttackDefinition? definition) { localPlayer = Player.m_localPlayer; secondaryAttack = false; definition = null; if ((Object)(object)localPlayer == (Object)null) { return false; } if (SecondaryAttackRuntimeFacade.TryGetProjectileHitAttackContext(out string _, out secondaryAttack, out definition, out bool disableCurrentAttackFallback)) { return true; } if (disableCurrentAttackFallback) { return false; } Attack currentAttack = ((Humanoid)localPlayer).m_currentAttack; if ((Object)(object)currentAttack?.m_weapon?.m_dropPrefab == (Object)null) { return false; } secondaryAttack = ((Humanoid)localPlayer).m_currentAttackIsSecondary; SecondaryAttackRuntimeFacade.TryGetDefinition(currentAttack.m_weapon, out definition); return true; } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] internal static class AttackDoMeleeAttackDirectWeaponHitPatch { [HarmonyPriority(800)] private static void Prefix(Attack __instance, out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginAttackHit(__instance); } [HarmonyPriority(800)] private static void Postfix(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } private static void Finalizer(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } } [HarmonyPatch(typeof(Attack), "DoAreaAttack")] internal static class AttackDoAreaAttackDirectWeaponHitPatch { [HarmonyPriority(800)] private static void Prefix(Attack __instance, out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginAttackHit(__instance); } [HarmonyPriority(800)] private static void Postfix(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } private static void Finalizer(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamageDirectWeaponHitDepthPatch { [HarmonyPriority(800)] private static void Prefix(out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginCharacterDamage(); } [HarmonyPriority(0)] private static void Postfix(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } private static void Finalizer(ref DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(ref __state); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamageDirectWeaponHitEffectsPatch { [HarmonyPriority(400)] private static void Prefix(Character __instance, ref HitData hit) { DirectWeaponHitContextSystem.ApplyDirectWeaponHitEffects(__instance, ref hit); } } internal static class FractureLineSystem { private readonly struct FractureLineHitTarget { public IDestructible Destructible { get; } public Character? Character { get; } public Collider Collider { get; } public Vector3 Point { get; } public Vector3 HitDirection { get; } public float TargetFactor { get; } public bool IsEnemy { get; } public FractureLineHitTarget(IDestructible destructible, Character? character, Collider collider, Vector3 point, Vector3 hitDirection, float targetFactor, bool isEnemy) { //IL_0016: 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_001e: 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) Destructible = destructible; Character = character; Collider = collider; Point = point; HitDirection = hitDirection; TargetFactor = targetFactor; IsEnemy = isEnemy; } } internal sealed class FractureLineController : MonoBehaviour { private readonly List _visualObjects = new List(); private readonly List _visualLines = new List(); private float _endTime; private float _visualEndTime; private float _nextTickTime; private bool _durabilityDrained; private bool _finished; internal Attack Attack { get; private set; } internal FractureLineDefinition FractureLine { get; private set; } internal Vector3 Origin { get; private set; } internal Vector3 Forward { get; private set; } internal Vector3 DamageStart { get; private set; } internal Vector3 DamageEnd { get; private set; } internal Vector3 DamageForward { get; private set; } internal float DamageLength { get; private set; } internal Vector3 GetSurfacePoint(float t) { //IL_0001: 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_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) return SnapToGround(Vector3.Lerp(DamageStart, DamageEnd, Mathf.Clamp01(t))); } internal void Initialize(Attack attack, FractureLineDefinition fractureLine) { //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_002a: 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_003b: 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_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_0075: 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_0085: 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_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_00a8: 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_00bd: 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_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_00d8: 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_0106: 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_00ff: Unknown result type (might be due to invalid IL or missing references) Attack = attack; FractureLine = fractureLine; Transform transform = ((Component)attack.m_character).transform; Vector3 val = Vector3.ProjectOnPlane(transform.forward, Vector3.up); Forward = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : transform.forward); Origin = transform.position + Vector3.up * Mathf.Max(0f, attack.m_attackHeight) + transform.right * attack.m_attackOffset; DamageStart = SnapToGround(Origin); DamageEnd = SnapToGround(Origin + Forward * fractureLine.Range); Vector3 val2 = Vector3.ProjectOnPlane(DamageEnd - DamageStart, Vector3.up); DamageLength = ((Vector3)(ref val2)).magnitude; DamageForward = ((DamageLength > 0.001f) ? (val2 / DamageLength) : Forward); DamageLength = Mathf.Max(0.1f, DamageLength); _endTime = Time.time + fractureLine.Duration; _visualEndTime = _endTime + 1f; _nextTickTime = Time.time + fractureLine.TickInterval; CreateVisual(); ((Behaviour)this).enabled = true; } private void Update() { if ((Object)(object)Attack?.m_character == (Object)null || Attack.m_weapon == null || ((Character)Attack.m_character).IsDead() || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)Attack.m_character)) { Finish(); return; } if (_nextTickTime <= _endTime && Time.time >= _nextTickTime) { ApplyTick(this); _nextTickTime += FractureLine.TickInterval; } UpdateVisualFade(); float num = ((_visualLines.Count > 0) ? _visualEndTime : _endTime); if (Time.time >= num && _nextTickTime > _endTime) { Finish(); } } internal void DrainDurabilityOnce() { if (!_durabilityDrained) { SecondaryAttackManager.DrainAttackDurability(Attack, FractureLine.DurabilityFactor); _durabilityDrained = true; } } private void CreateVisual() { //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_0063: 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_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_0085: 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_008b: 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_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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00d1: 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_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) CreateLine(DamageStart, DamageEnd, 0.02f, 0.45f); int num = 7; for (int i = 0; i < num; i++) { float num2 = ((float)i + 1f) / ((float)num + 1f); float num3 = ((i % 2 == 0) ? 1f : (-1f)) * Mathf.Lerp(25f, 55f, (float)(i % 3) / 2f); Vector3 val = Origin + Forward * (FractureLine.Range * num2); Vector3 val2 = Quaternion.AngleAxis(num3, Vector3.up) * Forward; float num4 = FractureLine.Range * 0.5f * Mathf.Lerp(0.45f, 1f, 1f - num2); CreateLine(SnapToGround(val), SnapToGround(val + ((Vector3)(ref val2)).normalized * num4), 0.011f, 0.225f); } } private void CreateLine(Vector3 start, Vector3 end, float lineWidth, float zigzagAmplitude) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //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_005d: 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_00cb: 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_00e3: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SecondaryAttacks_FractureLine"); _visualObjects.Add(val); LineRenderer val2 = val.AddComponent(); Material fractureMaterial = GetFractureMaterial(); if ((Object)(object)fractureMaterial != (Object)null) { ((Renderer)val2).material = fractureMaterial; } val2.useWorldSpace = true; List list = CreateJaggedLinePoints(start, end, zigzagAmplitude); val2.positionCount = list.Count; for (int i = 0; i < list.Count; i++) { val2.SetPosition(i, list[i]); } val2.widthMultiplier = lineWidth; val2.widthCurve = CreatePointedLineWidthCurve(); val2.numCapVertices = 0; val2.numCornerVertices = 0; val2.startColor = new Color(0.12f, 0.075f, 0.055f, 1f); val2.endColor = new Color(0.07f, 0.045f, 0.035f, 0.95f); _visualLines.Add(new FractureLineVisualLine(val2, val2.startColor, val2.endColor)); } private void UpdateVisualFade() { //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_006e: 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_009c: Unknown result type (might be due to invalid IL or missing references) if (_visualLines.Count == 0) { return; } float num = ((Time.time <= _endTime) ? 1f : Mathf.Clamp01((_visualEndTime - Time.time) / 1f)); foreach (FractureLineVisualLine visualLine in _visualLines) { if (!((Object)(object)visualLine.LineRenderer == (Object)null)) { Color startColor = visualLine.StartColor; Color endColor = visualLine.EndColor; startColor.a *= num; endColor.a *= num; visualLine.LineRenderer.startColor = startColor; visualLine.LineRenderer.endColor = endColor; } } } private List CreateJaggedLinePoints(Vector3 start, Vector3 end, float zigzagAmplitude) { //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_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_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_003d: 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_0025: 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_005f: 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_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_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_00a5: 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_00a9: 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_013a: 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_011b: 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_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) Vector3 val = end - start; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.01f) { return new List { start, end }; } Vector3 val2 = Vector3.ProjectOnPlane(val / magnitude, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = Forward; } Vector3 val3 = Vector3.Cross(Vector3.up, ((Vector3)(ref val2)).normalized); Vector3 normalized = ((Vector3)(ref val3)).normalized; int num = Mathf.Clamp(Mathf.CeilToInt(magnitude / 0.55f) + 1, 7, 15); List list = new List(num); for (int i = 0; i < num; i++) { float num2 = (float)i / (float)(num - 1); Vector3 val4 = Vector3.Lerp(start, end, num2); if (i > 0 && i < num - 1) { float num3 = Mathf.Sin(num2 * (float)Math.PI); float num4 = Mathf.Pow(1f - num2, 0.85f); float num5 = 0.75f + 0.25f * Mathf.Sin((float)i * 1.79f + magnitude * 0.33f); float num6 = ((i % 2 == 0) ? (-1f) : 1f); val4 += normalized * (num6 * zigzagAmplitude * num3 * num4 * num5); } list.Add(SnapToGround(val4)); } return list; } private static AnimationCurve CreatePointedLineWidthCurve() { //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_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_003e: 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_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_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_007a: Expected O, but got Unknown return new AnimationCurve((Keyframe[])(object)new Keyframe[5] { new Keyframe(0f, 0f), new Keyframe(0.08f, 0.85f), new Keyframe(0.5f, 1f), new Keyframe(0.92f, 0.65f), new Keyframe(1f, 0f) }); } private void Finish() { if (!_finished) { _finished = true; DestroyVisuals(); Object.Destroy((Object)(object)this); } } private void DestroyVisuals() { foreach (GameObject visualObject in _visualObjects) { if ((Object)(object)visualObject != (Object)null) { Object.Destroy((Object)(object)visualObject); } } _visualObjects.Clear(); _visualLines.Clear(); } private void OnDestroy() { DestroyVisuals(); } } private readonly struct FractureLineVisualLine { public LineRenderer LineRenderer { get; } public Color StartColor { get; } public Color EndColor { get; } public FractureLineVisualLine(LineRenderer lineRenderer, Color startColor, Color endColor) { //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) //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) LineRenderer = lineRenderer; StartColor = startColor; EndColor = endColor; } } private const float SurfaceDepthTolerance = 0.35f; private const float SurfaceHeight = 1.2f; private const float PushFactor = 0.3f; private const float VisualLineWidth = 0.02f; private const float VisualZigzagAmplitude = 0.45f; private const int VisualBranchCount = 7; private const float VisualBranchLengthFactor = 0.5f; private const float VisualFadeSeconds = 1f; private static readonly Collider[] Hits = (Collider[])(object)new Collider[128]; private static readonly HashSet HitObjects = new HashSet(); private static readonly HashSet HitColliders = new HashSet(); private static readonly List HitTargets = new List(); private static Material? _fractureMaterial; private static int _attackMask; private static int _environmentMask; private static int _groundMask; internal static bool CanHandle(Attack attack, SecondaryAttackDefinition definition) { //IL_0034: 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_0042: Invalid comparison between Unknown and I4 if (attack != null && definition?.FractureLine != null && (Object)(object)attack.m_character != (Object)null && attack.m_weapon?.m_shared != null) { if ((int)attack.m_attackType != 0) { return (int)attack.m_attackType == 1; } return true; } return false; } internal static void Trigger(Attack attack, SecondaryAttackDefinition definition) { if (CanHandle(attack, definition) && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)attack.m_character)) { ((Component)attack.m_character).gameObject.AddComponent().Initialize(attack, definition.FractureLine); } } private static void ApplyTick(FractureLineController controller) { //IL_0057: 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) Attack attack = controller.Attack; FractureLineDefinition fractureLine = controller.FractureLine; Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; if ((Object)(object)character == (Object)null || weapon?.m_shared == null) { return; } HitObjects.Clear(); HitColliders.Clear(); HitTargets.Clear(); try { float randomSkillFactor = character.GetRandomSkillFactor(weapon.m_shared.m_skillType); float num = Mathf.Max(0.1f, fractureLine.HitSpacing); int num2 = Mathf.Max(2, Mathf.CeilToInt(controller.DamageLength / num) + 1); for (int i = 0; i < num2; i++) { float t = ((num2 <= 1) ? 0f : ((float)i / (float)(num2 - 1))); Vector3 surfacePoint = controller.GetSurfacePoint(t); CollectSamplePoint(controller, surfacePoint, num); } float skillFactor = randomSkillFactor * ResolveMultiTargetPenalty(HitTargets.Count); foreach (FractureLineHitTarget hitTarget in HitTargets) { ApplyCollectedHit(controller, hitTarget, skillFactor); } } finally { HitObjects.Clear(); HitColliders.Clear(); HitTargets.Clear(); Array.Clear(Hits, 0, Hits.Length); } } private static void CollectSamplePoint(FractureLineController controller, Vector3 samplePoint, float hitRadius) { //IL_0000: 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) int num = Physics.OverlapSphereNonAlloc(samplePoint, hitRadius, Hits, GetAttackMask(), (QueryTriggerInteraction)0); for (int i = 0; i < num; i++) { TryCollectSampleHit(controller, samplePoint, Hits[i]); } } private static void TryCollectSampleHit(FractureLineController controller, Vector3 samplePoint, Collider collider) { //IL_0090: 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_0096: 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_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) if ((Object)(object)collider == (Object)null) { return; } Attack attack = controller.Attack; FractureLineDefinition fractureLine = controller.FractureLine; Character character = (Character)(object)attack.m_character; if ((Object)(object)((Component)collider).gameObject == (Object)(object)((Component)character).gameObject) { return; } GameObject val = Projectile.FindHitObject(collider); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)((Component)character).gameObject) { return; } IDestructible component = val.GetComponent(); if (component == null) { return; } Character val2 = (Character)(object)((component is Character) ? component : null); bool isEnemy = false; if ((Object)(object)val2 != (Object)null) { if (!IsValidCharacterTarget(attack, val2, out isEnemy)) { return; } } else if (!IsValidDestructibleTarget(attack, component)) { return; } Vector3 val3 = ResolveTargetSurfacePoint(collider, component, samplePoint); if (!IsInsideSampleSurface(fractureLine, samplePoint, val3)) { return; } if ((Object)(object)val2 != (Object)null) { if (!HitObjects.Add(val)) { return; } } else if (!HitColliders.Add(collider)) { return; } HitTargets.Add(new FractureLineHitTarget(component, val2, collider, val3, ResolveHitDirection(controller, val3), 1f, isEnemy)); } private static void ApplyCollectedHit(FractureLineController controller, FractureLineHitTarget target, float skillFactor) { //IL_0037: 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_0063: Unknown result type (might be due to invalid IL or missing references) Attack attack = controller.Attack; Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; if (!((Object)(object)character == (Object)null) && weapon?.m_shared != null) { HitData val = CreateHitData(attack, target.Collider, target.Point, target.HitDirection, skillFactor, controller.FractureLine, target.TargetFactor); character.GetSEMan().ModifyAttack(weapon.m_shared.m_skillType, ref val); target.Destructible.Damage(val); if ((Object)(object)target.Character != (Object)null && attack.m_attackHealthReturnHit > 0f && target.IsEnemy) { character.Heal(attack.m_attackHealthReturnHit, true); } controller.DrainDurabilityOnce(); } } private static float ResolveMultiTargetPenalty(int hitCount) { if (hitCount <= 1) { return 1f; } return 1f / ((float)hitCount * 0.75f); } private static HitData CreateHitData(Attack attack, Collider collider, Vector3 hitPoint, Vector3 hitDirection, float skillFactor, FractureLineDefinition fractureLine, float targetFactor) { //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) return SecondaryAttackHitDataFactory.CreateMeleeHit(attack, collider, hitPoint, hitDirection, skillFactor, fractureLine.DamageFactor * targetFactor, 0.3f); } private static bool IsValidCharacterTarget(Attack attack, Character target, out bool isEnemy) { Character character = (Character)(object)attack.m_character; isEnemy = BaseAI.IsEnemy(character, target) || ((Object)(object)target.GetBaseAI() != (Object)null && target.GetBaseAI().IsAggravatable() && character.IsPlayer()); if (!isEnemy) { return false; } if (((!attack.m_hitFriendly || character.IsTamed()) && !character.IsPlayer() && !isEnemy) || (!attack.m_weapon.m_shared.m_tamedOnly && character.IsPlayer() && !character.IsPVPEnabled() && !isEnemy) || (attack.m_weapon.m_shared.m_tamedOnly && !target.IsTamed())) { return false; } if (attack.m_weapon.m_shared.m_dodgeable && target.IsDodgeInvincible()) { if (target.IsPlayer()) { Character obj = ((target is Player) ? target : null); if (obj != null) { ((Player)obj).HitWhileDodging(); } } return false; } return true; } private static bool IsValidDestructibleTarget(Attack attack, IDestructible destructible) { //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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 if (attack.m_weapon.m_shared.m_tamedOnly) { return false; } DestructibleType destructibleType = destructible.GetDestructibleType(); if ((int)destructibleType != 0) { return (int)destructibleType != 4; } return false; } private static Vector3 ResolveTargetSurfacePoint(Collider collider, IDestructible destructible, Vector3 samplePoint) { //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_0021: 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_0010: Unknown result type (might be due to invalid IL or missing references) Character val = (Character)(object)((destructible is Character) ? destructible : null); if (val != null) { return ((Component)val).transform.position; } Vector3 origin = samplePoint + Vector3.up * 0.25f; return SecondaryAttackManager.ResolveSafeClosestPoint(collider, origin); } private static bool IsInsideSampleSurface(FractureLineDefinition fractureLine, Vector3 samplePoint, Vector3 point) { //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_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_0031: 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_0045: 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) Vector3 val = Vector3.ProjectOnPlane(point - samplePoint, Vector3.up); float num = Mathf.Max(0.1f, fractureLine.HitSpacing); if (((Vector3)(ref val)).sqrMagnitude > num * num) { return false; } if (point.y >= samplePoint.y - 0.35f) { return point.y <= samplePoint.y + 1.2f; } return false; } private static Vector3 ResolveHitDirection(FractureLineController controller, Vector3 hitPoint) { //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_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_0016: 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_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) Vector3 val = Vector3.ProjectOnPlane(hitPoint - controller.Origin, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = controller.Forward; } return ((Vector3)(ref val)).normalized; } private static bool IsBlockedByEnvironment(Vector3 origin, Vector3 targetPoint, IDestructible allowedTarget) { //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_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_0061: 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_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) if (_environmentMask == 0) { _environmentMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain" }); } Vector3 val = targetPoint - origin; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.01f) { return false; } RaycastHit[] array = Physics.RaycastAll(origin, val / magnitude, magnitude, _environmentMask, (QueryTriggerInteraction)1); 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 val2 = array2[num]; if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null)) { GameObject val3 = Projectile.FindHitObject(((RaycastHit)(ref val2)).collider); if (!((Object)(object)val3 != (Object)null) || val3.GetComponent() != allowedTarget) { return true; } } } return false; } private static int GetAttackMask() { if (Attack.m_attackMask != 0) { return Attack.m_attackMask; } if (_attackMask == 0) { _attackMask = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _attackMask; } private static int GetGroundMask() { if (_groundMask == 0) { _groundMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain" }); } return _groundMask; } private static Vector3 SnapToGround(Vector3 point) { //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_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_0015: 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_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_002e: 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_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) RaycastHit val = default(RaycastHit); if (!Physics.Raycast(point + Vector3.up * 2f, Vector3.down, ref val, 8f, GetGroundMask(), (QueryTriggerInteraction)1)) { return point + Vector3.up * 0.04f; } return ((RaycastHit)(ref val)).point + Vector3.up * 0.04f; } private static Material? GetFractureMaterial() { //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_005b: Expected O, but got Unknown if ((Object)(object)_fractureMaterial != (Object)null) { return _fractureMaterial; } Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Legacy Shaders/Particles/Alpha Blended") ?? Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { return null; } _fractureMaterial = new Material(val) { color = Color.white }; return _fractureMaterial; } } internal static class HarvestSweepSystem { [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct SkillRaiseFactorScope : IDisposable { public void Dispose() { if (SkillRaiseFactors.Count > 0) { SkillRaiseFactors.RemoveAt(SkillRaiseFactors.Count - 1); } } } private const string PresetName = "harvestSweep"; private const float RepeatDelay = 0f; private const float RotationSpeedFactor = 1f; private const float DefaultHarvestRadius = 1.5f; private const float DefaultHarvestRadiusMaxLevel = 2.5f; private static readonly List SkillRaiseFactors = new List(); internal static bool TryStart(Attack attack, SecondaryAttackDefinition definition) { HarvestSweepDefinition harvestSweep = definition.HarvestSweep; Humanoid val = attack?.m_character; if (val == null || attack.m_weapon == null || harvestSweep == null || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)val)) { return false; } HarvestSweepController harvestSweepController = ((Component)val).GetComponent(); if ((Object)(object)harvestSweepController != (Object)null && harvestSweepController.IsActive) { if (!harvestSweepController.MatchesWeapon(attack.m_weapon)) { harvestSweepController.StopAfterCurrentAttack(); return false; } harvestSweepController.AttachAttack(attack, harvestSweep); SweepObserverVisualSystem.SendRefresh(val, harvestSweep: true, attack.m_attackAnimation, harvestSweep.LoopStart, harvestSweep.LoopEnd, harvestSweep.AnimationSpeed); return true; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)val, attack.m_weapon, "harvestSweep", harvestSweep.PresetCooldown)) { return false; } if ((Object)(object)harvestSweepController == (Object)null) { harvestSweepController = ((Component)val).gameObject.AddComponent(); } harvestSweepController.Begin(attack, definition, harvestSweep); SweepObserverVisualSystem.SendStart(val, harvestSweep: true, attack.m_attackAnimation, harvestSweep.LoopStart, harvestSweep.LoopEnd, harvestSweep.AnimationSpeed); return true; } internal static void UpdateInput(Player player, bool secondaryAttackHold, bool primaryAttackHold) { if (!((Object)(object)player == (Object)null)) { ((Component)player).GetComponent()?.UpdateInput(secondaryAttackHold, primaryAttackHold); } } internal static bool StartRepeatAttack(Humanoid humanoid) { return ((Character)humanoid).StartAttack((Character)null, true); } internal static float GetRepeatDelay() { return 0f; } internal static float GetRotationSpeedFactor() { return 1f; } internal static float ResolveHarvestRadius(ItemData? weapon, Player? player) { return Mathf.Lerp(ResolveHarvestRadius(weapon), ResolveHarvestRadiusMaxLevel(weapon), ((Object)(object)player != (Object)null) ? ((Character)player).GetSkillFactor((SkillType)106) : 0f); } internal static float ResolveHarvestRadius(ItemData? weapon) { Attack val = weapon?.m_shared?.m_attack; if (val == null || !(val.m_harvestRadius > 0f)) { return 1.5f; } return val.m_harvestRadius; } internal static float ResolveHarvestRadiusMaxLevel(ItemData? weapon) { Attack val = weapon?.m_shared?.m_attack; if (val == null || !(val.m_harvestRadiusMaxLevel > 0f)) { return 2.5f; } return val.m_harvestRadiusMaxLevel; } internal static SkillRaiseFactorScope BeginSkillRaiseFactor(float factor) { SkillRaiseFactors.Add(Mathf.Max(0f, factor)); return default(SkillRaiseFactorScope); } internal static void ApplySkillRaiseFactor(SkillType skillType, ref float factor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)skillType == 106 && SkillRaiseFactors.Count > 0) { factor *= SkillRaiseFactors[SkillRaiseFactors.Count - 1]; } } } internal sealed class HarvestSweepController : MonoBehaviour { private const int MaxHarvestHitBufferSize = 2048; private static readonly int AttackTagHash = ZSyncAnimation.GetHash("attack"); private static Collider[] Hits = (Collider[])(object)new Collider[256]; private static int _harvestMask; private static bool _reportedHarvestSearchSaturation; private readonly HashSet _seenThisTick = new HashSet(); private readonly HashSet _destroyedPlants = new HashSet(); private Humanoid? _humanoid; private ItemData? _weapon; private SecondaryAttackDefinition? _definition; private HarvestSweepDefinition? _harvestSweep; private Attack? _currentAttack; private Animator? _animator; private ZSyncAnimation? _zanim; private float _nextRepeatTime; private float _originalAnimatorSpeed = 1f; private int _loopStateHash; private int _lastLoopFrame = -1; private int _startedFrame; private bool _stopRequested; private bool _cancelArmed; private bool _primaryCancelArmed; private bool _lastSecondaryHold = true; private bool _lastPrimaryHold; private bool _hasOriginalAnimatorSpeed; private bool _speedApplied; private bool _initialLoopStartApplied; private bool _harvestedCurrentSweep; private bool _loopRearmed = true; internal bool IsActive { get { if (_harvestSweep != null) { return !_stopRequested; } return false; } } internal bool SuppressesHitStop => _harvestSweep != null; internal bool TryGetAnimationSpeed(out float speed) { speed = _harvestSweep?.AnimationSpeed ?? 1f; if (_harvestSweep != null) { return !Mathf.Approximately(speed, 1f); } return false; } internal void Begin(Attack attack, SecondaryAttackDefinition definition, HarvestSweepDefinition harvestSweep) { _humanoid = attack.m_character; _weapon = attack.m_weapon; _definition = definition; _harvestSweep = harvestSweep; _animator = (((Object)(object)_humanoid != (Object)null) ? ((Component)_humanoid).GetComponentInChildren() : null); Humanoid? humanoid = _humanoid; _zanim = ((humanoid != null) ? ((Character)humanoid).GetZAnim() : null); _destroyedPlants.Clear(); _nextRepeatTime = Time.time + HarvestSweepSystem.GetRepeatDelay(); _loopStateHash = 0; _lastLoopFrame = -1; _startedFrame = Time.frameCount; _stopRequested = false; _cancelArmed = false; _primaryCancelArmed = false; _lastSecondaryHold = true; _lastPrimaryHold = false; _initialLoopStartApplied = false; _loopRearmed = true; AttachAttack(attack, harvestSweep); ((Behaviour)this).enabled = true; } internal bool MatchesWeapon(ItemData weapon) { if (_weapon != weapon) { if ((Object)(object)_weapon?.m_dropPrefab != (Object)null && (Object)(object)weapon?.m_dropPrefab != (Object)null) { return ((Object)_weapon.m_dropPrefab).name == ((Object)weapon.m_dropPrefab).name; } return false; } return true; } internal void AttachAttack(Attack attack, HarvestSweepDefinition harvestSweep) { bool num = _currentAttack != attack; _currentAttack = attack; if (num) { _loopStateHash = 0; _lastLoopFrame = -1; _initialLoopStartApplied = false; _loopRearmed = true; } ApplyMovementFactors(attack, harvestSweep); ApplyAnimationSpeed(harvestSweep); _nextRepeatTime = Time.time + HarvestSweepSystem.GetRepeatDelay(); _harvestedCurrentSweep = false; } internal void UpdateInput(bool secondaryAttackHold, bool primaryAttackHold) { if (!IsActive) { return; } UpdatePrimaryCancelInput(primaryAttackHold); if (!IsActive) { return; } if (!secondaryAttackHold) { _cancelArmed = Time.frameCount > _startedFrame + 1; _lastSecondaryHold = false; return; } bool flag = !_lastSecondaryHold; _lastSecondaryHold = true; if (_cancelArmed && flag) { StopAfterCurrentAttack(); } } private void UpdatePrimaryCancelInput(bool primaryAttackHold) { if (!primaryAttackHold) { _primaryCancelArmed = Time.frameCount > _startedFrame + 1; _lastPrimaryHold = false; return; } bool flag = !_lastPrimaryHold; _lastPrimaryHold = true; if (_primaryCancelArmed && flag) { StopAfterCurrentAttack(); } } internal void StopAfterCurrentAttack() { _stopRequested = true; } private void Update() { if ((Object)(object)_humanoid == (Object)null || _weapon == null || _definition == null || _harvestSweep == null || ((Character)_humanoid).IsDead() || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { Object.Destroy((Object)(object)this); return; } Attack currentAttack = _humanoid.m_currentAttack; if (currentAttack != null && ((Character)_humanoid).InAttack()) { if (currentAttack == _currentAttack) { ApplyMovementFactors(currentAttack, _harvestSweep); if (ShouldKeepLooping()) { TryUpdateSeamlessLoop(currentAttack); } } return; } if (_currentAttack != null && !_harvestedCurrentSweep) { HarvestCurrentSweep(_currentAttack); } if (_stopRequested || !MatchesWeapon(_humanoid.GetCurrentWeapon())) { Object.Destroy((Object)(object)this); } else if (!(Time.time < _nextRepeatTime) && !((Character)_humanoid).IsStaggering() && !((Character)_humanoid).InAttack()) { if (!CanPayNextAttackCost(_weapon)) { Object.Destroy((Object)(object)this); } else if (!HarvestSweepSystem.StartRepeatAttack(_humanoid)) { _nextRepeatTime = Time.time + 0.05f; } } } private bool ShouldKeepLooping() { if (!_stopRequested && (Object)(object)_humanoid != (Object)null) { return MatchesWeapon(_humanoid.GetCurrentWeapon()); } return false; } private bool TryUpdateSeamlessLoop(Attack activeAttack) { //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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_0115: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_humanoid == (Object)null || _weapon == null || _harvestSweep == null) { return false; } if (_animator == null) { _animator = ((Component)_humanoid).GetComponentInChildren(); } if ((Object)(object)_animator == (Object)null) { return false; } AnimatorStateInfo attackAnimatorState = GetAttackAnimatorState(_animator); if (!IsAttackState(attackAnimatorState)) { return false; } if (_loopStateHash == 0 && ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash != 0) { _loopStateHash = ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash; } float loopStart = _harvestSweep.LoopStart; float loopEnd = _harvestSweep.LoopEnd; if (!_initialLoopStartApplied) { _initialLoopStartApplied = true; if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime < loopStart) { SeekToLoopStart(attackAnimatorState); _loopRearmed = false; return true; } } if (!TryRearmLoop(attackAnimatorState, loopStart, loopEnd)) { return true; } if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime < loopEnd || _lastLoopFrame == Time.frameCount) { return false; } HarvestCurrentSweep(activeAttack); if (!CanPayNextAttackCost(_weapon)) { _stopRequested = true; return false; } PayNextAttackCost(activeAttack); _lastLoopFrame = Time.frameCount; SeekToLoopStart(attackAnimatorState); _harvestedCurrentSweep = false; _loopRearmed = false; return true; } private bool TryRearmLoop(AnimatorStateInfo state, float loopStart, float loopEnd) { if (_loopRearmed) { return true; } if (((AnimatorStateInfo)(ref state)).normalizedTime < loopEnd) { _loopRearmed = true; return true; } return false; } private void SeekToLoopStart(AnimatorStateInfo state) { if ((Object)(object)_animator == (Object)null || _harvestSweep == null) { return; } int num = ((_loopStateHash != 0) ? _loopStateHash : ((AnimatorStateInfo)(ref state)).fullPathHash); if (num != 0) { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); _animator.Play(num, 0, _harvestSweep.LoopStart); if ((Object)(object)_humanoid != (Object)null && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { SweepObserverVisualSystem.SendRefresh(_humanoid, harvestSweep: true, _currentAttack?.m_attackAnimation ?? string.Empty, _harvestSweep.LoopStart, _harvestSweep.LoopEnd, _harvestSweep.AnimationSpeed); } } } private static AnimatorStateInfo GetAttackAnimatorState(Animator animator) { //IL_001d: 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_0019: Unknown result type (might be due to invalid IL or missing references) if (animator.IsInTransition(0)) { AnimatorStateInfo nextAnimatorStateInfo = animator.GetNextAnimatorStateInfo(0); if (IsAttackState(nextAnimatorStateInfo)) { return nextAnimatorStateInfo; } } return animator.GetCurrentAnimatorStateInfo(0); } private static bool IsAttackState(AnimatorStateInfo state) { if (((AnimatorStateInfo)(ref state)).fullPathHash != 0) { return ((AnimatorStateInfo)(ref state)).tagHash == AttackTagHash; } return false; } private bool CanPayNextAttackCost(ItemData weapon) { SharedData shared = weapon.m_shared; Attack val = shared?.m_secondaryAttack; if (val == null) { return false; } float num = Mathf.Max(0f, shared.m_useDurabilityDrain * (_definition?.DurabilityFactor ?? 1f)); if (num > 0f && weapon.m_durability + 0.001f < num) { return false; } float num2 = Mathf.Max(0f, val.m_attackStamina); if (num2 > 0f && !((Character)_humanoid).HaveStamina(num2)) { return false; } float num3 = Mathf.Max(0f, val.m_attackEitr); if (num3 > 0f && !((Character)_humanoid).HaveEitr(num3)) { return false; } float num4 = Mathf.Max(0f, val.m_attackHealth); if (num4 > 0f && !((Character)_humanoid).HaveHealth(num4) && val.m_attackHealthLowBlockUse) { return false; } return true; } private void PayNextAttackCost(Attack activeAttack) { //IL_00e1: 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_010c: 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) if ((Object)(object)_humanoid == (Object)null || _weapon?.m_shared == null) { return; } Attack secondaryAttack = _weapon.m_shared.m_secondaryAttack; if (secondaryAttack != null) { float num = Mathf.Max(0f, secondaryAttack.m_attackStamina); if (num > 0f) { ((Character)_humanoid).UseStamina(num); } float num2 = Mathf.Max(0f, secondaryAttack.m_attackEitr); if (num2 > 0f) { ((Character)_humanoid).UseEitr(num2); } float num3 = Mathf.Max(0f, secondaryAttack.m_attackHealth); if (num3 > 0f) { ((Character)_humanoid).UseHealth(Mathf.Max(0f, Mathf.Min(((Character)_humanoid).GetHealth() - 1f, num3))); } Transform attackOrigin = activeAttack.GetAttackOrigin(); _weapon.m_shared.m_startEffect.Create(attackOrigin.position, ((Component)_humanoid).transform.rotation, attackOrigin, 1f, -1); activeAttack.m_startEffect.Create(attackOrigin.position, ((Component)_humanoid).transform.rotation, attackOrigin, 1f, -1); ((Character)_humanoid).AddNoise(activeAttack.m_attackStartNoise); } } private void HarvestCurrentSweep(Attack activeAttack) { if (!_harvestedCurrentSweep) { _harvestedCurrentSweep = true; HarvestOnce(activeAttack); } } private void HarvestOnce(Attack activeAttack) { //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_0076: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)activeAttack?.m_character == (Object)null || _harvestSweep == null || _weapon == null) { return; } Humanoid character = activeAttack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if ((Object)(object)val == (Object)null) { return; } Attack harvestAttack = _weapon.m_shared?.m_attack; float radius = HarvestSweepSystem.ResolveHarvestRadius(_weapon, val); Vector3 center = ResolveHarvestCenter(activeAttack, harvestAttack); int num = 0; _seenThisTick.Clear(); try { num = QueryHarvestHits(center, radius); for (int i = 0; i < num; i++) { Collider val2 = Hits[i]; if ((Object)(object)val2 == (Object)null) { continue; } Pickable componentInParent = ((Component)val2).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { if (!_seenThisTick.Add(componentInParent) || !CanHarvest(componentInParent)) { continue; } using (GroundworkCompat.SuppressForagingRangePickup()) { using (HarvestSweepSystem.BeginSkillRaiseFactor(_harvestSweep.SkillRaiseFactor)) { componentInParent.Interact((Humanoid)(object)val, false, false); } } } else { DestroyUnhealthyPlantIfNeeded(val2); } } } finally { _seenThisTick.Clear(); Array.Clear(Hits, 0, Hits.Length); } } private void DestroyUnhealthyPlantIfNeeded(Collider hit) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) Plant componentInParent = ((Component)hit).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && (int)componentInParent.GetStatus() != 0) { Destructible componentInParent2 = ((Component)hit).GetComponentInParent(); if (!((Object)(object)componentInParent2 == (Object)null) && _destroyedPlants.Add(componentInParent2)) { componentInParent2.Destroy((HitData)null); } } } private static bool CanHarvest(Pickable pickable) { if (GroundworkCompat.CanScytheHarvest(pickable)) { return pickable.CanBePicked(); } return false; } private static Vector3 ResolveHarvestCenter(Attack activeAttack, Attack? harvestAttack) { //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_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_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_006d: 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_0082: 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_008a: Unknown result type (might be due to invalid IL or missing references) Attack val = harvestAttack ?? activeAttack; Character character = (Character)(object)activeAttack.m_character; Transform val2 = ResolveAttackOrigin(val, character); Vector3 val3 = ResolveMeleeAttackDirection(val, character, val2); float num = Mathf.Max(0f, val.m_attackRange); if (num <= 0f) { num = HarvestSweepSystem.ResolveHarvestRadius(null); } return val2.position + Vector3.up * Mathf.Max(0f, val.m_attackHeight) + ((Component)character).transform.right * val.m_attackOffset + val3 * num; } private static Transform ResolveAttackOrigin(Attack attack, Character character) { if (!string.IsNullOrEmpty(attack.m_attackOriginJoint)) { GameObject visual = character.GetVisual(); Transform val = ((visual != null) ? visual.transform : null); if ((Object)(object)val != (Object)null) { Transform val2 = Utils.FindChild(val, attack.m_attackOriginJoint, (IterativeSearchType)0); if ((Object)(object)val2 != (Object)null) { return val2; } } } return ((Component)character).transform; } private static Vector3 ResolveMeleeAttackDirection(Attack attack, Character character, Transform origin) { //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_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_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_0028: 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_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_006a: 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) Vector3 forward = ((Component)character).transform.forward; Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); Vector3 val2 = ((val != null) ? val.GetAimDir(origin.position) : forward); val2.x = forward.x; val2.z = forward.z; if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { return forward; } ((Vector3)(ref val2)).Normalize(); return Vector3.RotateTowards(forward, val2, (float)Math.PI / 180f * attack.m_maxYAngle, 10f); } private void ApplyAnimationSpeed(HarvestSweepDefinition harvestSweep) { if (Mathf.Approximately(harvestSweep.AnimationSpeed, 1f)) { return; } if (_animator == null) { _animator = (((Object)(object)_humanoid != (Object)null) ? ((Component)_humanoid).GetComponentInChildren() : null); } if (_zanim == null) { Humanoid? humanoid = _humanoid; _zanim = ((humanoid != null) ? ((Character)humanoid).GetZAnim() : null); } if (!((Object)(object)_animator == (Object)null)) { if (!_hasOriginalAnimatorSpeed) { _originalAnimatorSpeed = _animator.speed; _hasOriginalAnimatorSpeed = true; } ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(harvestSweep.AnimationSpeed); } _animator.speed = harvestSweep.AnimationSpeed; _speedApplied = true; } } private void RestoreAnimationSpeed() { if (_speedApplied && !((Object)(object)_animator == (Object)null)) { ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(_originalAnimatorSpeed); } _animator.speed = _originalAnimatorSpeed; _speedApplied = false; } } private static void ApplyMovementFactors(Attack attack, HarvestSweepDefinition harvestSweep) { attack.m_speedFactor = harvestSweep.MoveSpeedFactor; attack.m_speedFactorRotation = HarvestSweepSystem.GetRotationSpeedFactor(); } private void OnDestroy() { if ((Object)(object)_humanoid != (Object)null && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { SweepObserverVisualSystem.SendStop(_humanoid); } SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); RestoreAnimationSpeed(); } private static int GetHarvestMask() { if (_harvestMask == 0) { _harvestMask = LayerMask.GetMask(new string[5] { "piece", "piece_nonsolid", "item", "Default", "Default_small" }); } return _harvestMask; } private static int QueryHarvestHits(Vector3 center, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) int num; while (true) { num = Physics.OverlapSphereNonAlloc(center, radius, Hits, GetHarvestMask(), (QueryTriggerInteraction)0); if (num < Hits.Length) { return num; } if (Hits.Length >= 2048) { break; } Array.Resize(ref Hits, Math.Min(Hits.Length * 2, 2048)); } if (!_reportedHarvestSearchSaturation) { _reportedHarvestSearchSaturation = true; SecondaryAttacksPlugin.ModLogger.LogWarning((object)($"Harvest sweep search reached the {Hits.Length}-collider maximum buffer capacity. " + "Results may be truncated in this unusually dense area.")); } return num; } } internal static class GreatSwordSkillScalingSystem { private sealed class OriginalTrailTipState { internal Vector3 LocalPosition { get; } internal OriginalTrailTipState(Vector3 localPosition) { //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) LocalPosition = localPosition; } } private sealed class CleavingThrustObserverTrailScaleController : MonoBehaviour { private readonly List _registeredTrails = new List(); private Character? _character; private GameObject? _rightItemInstance; private uint _sequence; private bool _hasSequence; private float _rangeScale; private double _startedAt; private double _expiresAt; internal void Begin(Character character, uint sequence, float rangeScale, double startedAt, double expiresAt) { if (!_hasSequence || IsNewerSequence(sequence, _sequence)) { _character = character; _sequence = sequence; _hasSequence = true; _rangeScale = Mathf.Max(1f, rangeScale); _startedAt = startedAt; _expiresAt = expiresAt; RefreshRegistrations(); ((Behaviour)this).enabled = true; } } private static bool IsNewerSequence(uint candidate, uint current) { if (candidate != current) { return (int)(candidate - current) > 0; } return false; } private void Update() { if ((Object)(object)_character == (Object)null || _character.IsDead() || SecondaryAttackManager.HasCharacterAuthority(_character) || SecondaryAttackManager.GetNetworkTimeSeconds() >= _expiresAt) { Object.Destroy((Object)(object)this); return; } GameObject rightItemInstance = WeaponTrailAccess.GetRightItemInstance(_character); if ((Object)(object)_rightItemInstance != (Object)(object)rightItemInstance || _registeredTrails.Count == 0 || HasDestroyedRegisteredTrail()) { RefreshRegistrations(rightItemInstance); } } internal ObserverTrailSampleScope BeginTrailSample(MeleeWeaponTrail trail) { //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_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_0063: 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_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_008c: 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_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_00a5: Unknown result type (might be due to invalid IL or missing references) double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); if ((Object)(object)_character == (Object)null || networkTimeSeconds < _startedAt || networkTimeSeconds >= _expiresAt || SecondaryAttackManager.HasCharacterAuthority(_character)) { return default(ObserverTrailSampleScope); } if (!WeaponTrailAccess.TryGetEndpoints(trail, out Transform baseTransform, out Transform tipTransform)) { return default(ObserverTrailSampleScope); } Vector3 position = baseTransform.position; Vector3 val = tipTransform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= 1E-06f) { return default(ObserverTrailSampleScope); } Vector3 localPosition = tipTransform.localPosition; tipTransform.position = position + val * _rangeScale; return new ObserverTrailSampleScope(tipTransform, localPosition); } private void RefreshRegistrations(GameObject? rightItemInstance = null) { if ((Object)(object)_character == (Object)null) { return; } if (rightItemInstance == null) { rightItemInstance = WeaponTrailAccess.GetRightItemInstance(_character); } UnregisterTrails(); _rightItemInstance = rightItemInstance; if ((Object)(object)rightItemInstance == (Object)null) { return; } MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(rightItemInstance); foreach (MeleeWeaponTrail val in trails) { if (!((Object)(object)val == (Object)null)) { ObserverTrailScaleControllers[val] = this; _registeredTrails.Add(val); } } } private bool HasDestroyedRegisteredTrail() { for (int i = 0; i < _registeredTrails.Count; i++) { if ((Object)(object)_registeredTrails[i] == (Object)null) { return true; } } return false; } private void UnregisterTrails() { for (int i = 0; i < _registeredTrails.Count; i++) { MeleeWeaponTrail val = _registeredTrails[i]; if (val != null && ObserverTrailScaleControllers.TryGetValue(val, out CleavingThrustObserverTrailScaleController value) && value == this) { ObserverTrailScaleControllers.Remove(val); } } _registeredTrails.Clear(); _rightItemInstance = null; } private void OnDestroy() { UnregisterTrails(); } } internal readonly struct ObserverTrailSampleScope { private readonly Transform? _tipTransform; private readonly Vector3 _originalLocalPosition; internal ObserverTrailSampleScope(Transform tipTransform, Vector3 originalLocalPosition) { //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) _tipTransform = tipTransform; _originalLocalPosition = originalLocalPosition; } internal void Restore() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tipTransform != (Object)null) { _tipTransform.localPosition = _originalLocalPosition; } } } internal const string CleavingThrustVisualSessionRpcName = "SecondaryAttacks_CleavingThrustVisualSessionV2"; private const double CleavingThrustVisualSessionDuration = 1.25; private const float MaxObserverRangeScale = 64f; private const double MinVisualSessionDuration = 0.1; private const double MaxVisualSessionDuration = 3.0; private const double MaxVisualSessionFutureSkew = 0.5; private const double VisualSessionStaleGrace = 1.0; private static readonly ConditionalWeakTable OriginalTrailTipLocalPositions = new ConditionalWeakTable(); private static readonly ConditionalWeakTable> ScaledTrailTipsByAttack = new ConditionalWeakTable>(); private static readonly Dictionary ObserverTrailScaleControllers = new Dictionary(); private static readonly ConditionalWeakTable StartedVisualSessions = new ConditionalWeakTable(); private static uint _nextCleavingThrustVisualSequence; internal static void BeginCleavingThrustVisualSession(Attack attack, SecondaryAttackDefinition definition) { if (!((Object)(object)attack?.m_character == (Object)null) && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)attack.m_character) && TryGetReadyCleavingThrustTrailScale(attack, definition, out var rangeScale) && !StartedVisualSessions.TryGetValue(attack, out object _)) { StartedVisualSessions.Add(attack, new object()); ApplyTrailScale(attack, rangeScale); SendCleavingThrustVisualSession(attack, rangeScale); } } internal static void HandleCleavingThrustVisualSessionRpc(Character character, ZNetView? nview, long sender, uint sequence, float rangeScale, double startedAt, double expiresAt) { if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsDedicated()) { double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); ZDO val = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); double num = expiresAt - startedAt; double num2 = networkTimeSeconds - startedAt; if (!((Object)(object)character == (Object)null) && !((Object)(object)nview == (Object)null) && nview.IsValid() && !nview.IsOwner() && val != null && val.GetOwner() == sender && sequence != 0 && !(rangeScale <= 1.0001f) && !float.IsNaN(rangeScale) && !float.IsInfinity(rangeScale) && !double.IsNaN(startedAt) && !double.IsInfinity(startedAt) && !double.IsNaN(expiresAt) && !double.IsInfinity(expiresAt) && !(num < 0.1) && !(num > 3.0) && !(num2 < -0.5) && !(num2 > num + 1.0)) { rangeScale = Mathf.Clamp(rangeScale, 1f, 64f); double expiresAt2 = networkTimeSeconds + num; (((Component)character).GetComponent() ?? ((Component)character).gameObject.AddComponent()).Begin(character, sequence, rangeScale, networkTimeSeconds, expiresAt2); } } } private static void ApplyTrailScale(Attack attack, float rangeScale) { //IL_0037: 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_0075: 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_007a: 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_0083: 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_008a: 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_0090: 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_0098: Unknown result type (might be due to invalid IL or missing references) List list = new List(); MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(attack); for (int i = 0; i < trails.Length; i++) { if (WeaponTrailAccess.TryGetEndpoints(trails[i], out Transform baseTransform, out Transform tipTransform)) { if (!OriginalTrailTipLocalPositions.TryGetValue(tipTransform, out OriginalTrailTipState value)) { value = new OriginalTrailTipState(tipTransform.localPosition); OriginalTrailTipLocalPositions.Add(tipTransform, value); } Vector3 val = (((Object)(object)tipTransform.parent != (Object)null) ? tipTransform.parent.InverseTransformPoint(baseTransform.position) : baseTransform.position); Vector3 val2 = value.LocalPosition - val; tipTransform.localPosition = val + val2 * rangeScale; list.Add(tipTransform); } } if (list.Count > 0) { ScaledTrailTipsByAttack.Remove(attack); ScaledTrailTipsByAttack.Add(attack, list); } } internal static void RestoreTrailScaleForAttack(Attack attack) { StartedVisualSessions.Remove(attack); if (ScaledTrailTipsByAttack.TryGetValue(attack, out List value)) { ScaledTrailTipsByAttack.Remove(attack); RestoreTrailTips(value); } } private static bool TryGetReadyCleavingThrustTrailScale(Attack attack, SecondaryAttackDefinition definition, out float rangeScale) { rangeScale = 1f; CleavingThrustDefinition cleavingThrust = definition.CleavingThrust; if (cleavingThrust == null || !CleavingThrustSystem.CanHandle(attack) || !MeleePresetCooldownSystem.IsReady((Character)(object)attack.m_character, "cleavingThrust", cleavingThrust.PresetCooldown)) { return false; } rangeScale = CleavingThrustSystem.ResolveVisualRangeScale(attack, definition); return rangeScale > 1.0001f; } private static void SendCleavingThrustVisualSession(Attack attack, float rangeScale) { if (!(rangeScale <= 1.0001f) && !((Object)(object)attack?.m_character == (Object)null) && SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)attack.m_character, out ZNetView nview, out ZDO _) && ZRoutedRpc.instance != null) { uint num = NextCleavingThrustVisualSequence(); double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); double num2 = networkTimeSeconds + 1.25; nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_CleavingThrustVisualSessionV2", new object[4] { num, rangeScale, networkTimeSeconds, num2 }); } } private static uint NextCleavingThrustVisualSequence() { _nextCleavingThrustVisualSequence++; if (_nextCleavingThrustVisualSequence == 0) { _nextCleavingThrustVisualSequence = 1u; } return _nextCleavingThrustVisualSequence; } internal static ObserverTrailSampleScope BeginObserverTrailSample(MeleeWeaponTrail trail) { if ((Object)(object)trail == (Object)null || !ObserverTrailScaleControllers.TryGetValue(trail, out CleavingThrustObserverTrailScaleController value)) { return default(ObserverTrailSampleScope); } return value.BeginTrailSample(trail); } internal static void EndObserverTrailSample(ObserverTrailSampleScope scope) { scope.Restore(); } internal static void EndObserverTrailSample(ref ObserverTrailSampleScope scope) { EndObserverTrailSample(scope); scope = default(ObserverTrailSampleScope); } private static void RestoreTrailTips(List scaledTips) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) foreach (Transform scaledTip in scaledTips) { if ((Object)(object)scaledTip != (Object)null && OriginalTrailTipLocalPositions.TryGetValue(scaledTip, out OriginalTrailTipState value)) { scaledTip.localPosition = value.LocalPosition; } } } } [HarmonyPatch(typeof(Attack), "Stop")] internal static class AttackStopGreatSwordSkillScalingPatch { private static void Postfix(Attack __instance) { GreatSwordSkillScalingSystem.RestoreTrailScaleForAttack(__instance); } private static void Finalizer(Attack __instance) { GreatSwordSkillScalingSystem.RestoreTrailScaleForAttack(__instance); } } [HarmonyPatch(typeof(MeleeWeaponTrail), "CustomFixedUpdate")] internal static class MeleeWeaponTrailCleavingThrustObserverScalePatch { private static void Prefix(MeleeWeaponTrail __instance, out GreatSwordSkillScalingSystem.ObserverTrailSampleScope __state) { __state = GreatSwordSkillScalingSystem.BeginObserverTrailSample(__instance); } private static void Postfix(ref GreatSwordSkillScalingSystem.ObserverTrailSampleScope __state) { GreatSwordSkillScalingSystem.EndObserverTrailSample(ref __state); } private static void Finalizer(ref GreatSwordSkillScalingSystem.ObserverTrailSampleScope __state) { GreatSwordSkillScalingSystem.EndObserverTrailSample(ref __state); } } internal sealed class KeyHintCell { private readonly bool _hideOnRestore; private readonly List _keys = new List(); private readonly List _keyParents = new List(); private readonly List _extraTexts = new List(); private readonly List _originalKeyTexts = new List(); private readonly List _originalKeyStates = new List(); private readonly List _originalKeyParentStates = new List(); private readonly List _originalExtraTextStates = new List(); private TMP_Text? _label; private bool _capturedOriginals; private bool _originalLabelState; private bool _originalRootActive; private string _originalLabel = string.Empty; private float? _originalLabelPreferredWidth; internal GameObject Root { get; } internal bool IsValid { get { if ((Object)(object)Root != (Object)null) { if (!((Object)(object)_label != (Object)null) && _keys.Count <= 0) { return _extraTexts.Count > 0; } return true; } return false; } } internal bool HasKeyTexts => _keys.Count > 0; private KeyHintCell(GameObject root, bool hideOnRestore) { Root = root; _hideOnRestore = hideOnRestore; RefreshChildren(); } internal static KeyHintCell? CloneFrom(GameObject? template, string name, bool hideOnRestore) { if (!IsUsableTemplate(template) || (Object)(object)template.transform.parent == (Object)null) { return null; } GameObject obj = Object.Instantiate(template, template.transform.parent, false); ((Object)obj).name = name; obj.SetActive(false); return new KeyHintCell(obj, hideOnRestore); } internal static KeyHintCell? FromGameObject(GameObject? root, bool hideOnRestore = false) { if (!IsUsableTemplate(root)) { return null; } return new KeyHintCell(root, hideOnRestore); } internal static bool IsUsableTemplate(GameObject? template) { if ((Object)(object)template != (Object)null && (Object)(object)template.transform.parent != (Object)null && !((Object)template).name.StartsWith("SecondaryAttacks_")) { return template.GetComponentsInChildren(true).Length != 0; } return false; } internal void Set(string label, string key, float preferredTextWidth = 0f) { SetSingleKey(key, label, preferredTextWidth, updateLabel: true); } internal void SetKey(string key) { SetSingleKey(key, null, 0f, updateLabel: false); } private void SetSingleKey(string key, string? label, float preferredTextWidth, bool updateLabel) { if ((Object)(object)Root == (Object)null) { return; } CaptureOriginals(); Root.SetActive(true); if (updateLabel && (Object)(object)_label != (Object)null) { SetText(_label, label ?? string.Empty); LayoutElement val = default(LayoutElement); if (preferredTextWidth > 0f && ((Component)_label).TryGetComponent(ref val)) { val.preferredWidth = preferredTextWidth; } } TMP_Text val2 = _keys.FirstOrDefault(); SetKeyVisibility(val2, _label); if ((Object)(object)val2 != (Object)null) { SetText(val2, key); } foreach (TMP_Text extraText in _extraTexts) { if ((Object)(object)extraText != (Object)null) { ((Component)extraText).gameObject.SetActive(false); } } } internal void SetText(string value) { if ((Object)(object)Root == (Object)null) { return; } CaptureOriginals(); Root.SetActive(true); TMP_Text val = _label ?? _keys.FirstOrDefault() ?? _extraTexts.FirstOrDefault(); SetText(val, value); SetKeyVisibility(((Object)(object)val != (Object)null && _keys.Contains(val)) ? val : null, val); foreach (TMP_Text extraText in _extraTexts) { if ((Object)(object)extraText != (Object)null && (Object)(object)extraText != (Object)(object)val) { ((Component)extraText).gameObject.SetActive(false); } } } internal void Restore() { if ((Object)(object)Root == (Object)null) { return; } if (!_capturedOriginals) { if (_hideOnRestore) { Root.SetActive(false); } return; } Root.SetActive(!_hideOnRestore && _originalRootActive); if ((Object)(object)_label != (Object)null) { SetText(_label, _originalLabel); ((Component)_label).gameObject.SetActive(_originalLabelState); LayoutElement val = default(LayoutElement); if (_originalLabelPreferredWidth.HasValue && ((Component)_label).TryGetComponent(ref val)) { val.preferredWidth = _originalLabelPreferredWidth.Value; } } for (int i = 0; i < _keys.Count && i < _originalKeyTexts.Count; i++) { SetText(_keys[i], _originalKeyTexts[i]); } for (int j = 0; j < _keys.Count && j < _originalKeyStates.Count; j++) { if ((Object)(object)_keys[j] != (Object)null) { ((Component)_keys[j]).gameObject.SetActive(_originalKeyStates[j]); } } for (int k = 0; k < _keyParents.Count && k < _originalKeyParentStates.Count; k++) { if ((Object)(object)_keyParents[k] != (Object)null) { _keyParents[k].SetActive(_originalKeyParentStates[k]); } } for (int l = 0; l < _extraTexts.Count && l < _originalExtraTextStates.Count; l++) { if ((Object)(object)_extraTexts[l] != (Object)null) { ((Component)_extraTexts[l]).gameObject.SetActive(_originalExtraTextStates[l]); } } } internal void MoveToStart() { if ((Object)(object)Root != (Object)null) { Root.transform.SetAsFirstSibling(); } } internal void RebuildParentLayout() { if ((Object)(object)Root != (Object)null) { Transform parent = Root.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val != null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val); } } } private void CaptureOriginals() { if (_capturedOriginals || (Object)(object)Root == (Object)null) { return; } _capturedOriginals = true; _originalRootActive = Root.activeSelf; _originalLabel = (((Object)(object)_label != (Object)null) ? _label.text : string.Empty); _originalLabelState = (Object)(object)_label != (Object)null && ((Component)_label).gameObject.activeSelf; LayoutElement val = default(LayoutElement); _originalLabelPreferredWidth = (((Object)(object)_label != (Object)null && ((Component)_label).TryGetComponent(ref val)) ? new float?(val.preferredWidth) : ((float?)null)); _originalKeyTexts.Clear(); foreach (TMP_Text key in _keys) { _originalKeyTexts.Add(((Object)(object)key != (Object)null) ? key.text : string.Empty); } _originalKeyStates.Clear(); foreach (TMP_Text key2 in _keys) { _originalKeyStates.Add((Object)(object)key2 != (Object)null && ((Component)key2).gameObject.activeSelf); } _originalKeyParentStates.Clear(); foreach (GameObject keyParent in _keyParents) { _originalKeyParentStates.Add((Object)(object)keyParent != (Object)null && keyParent.activeSelf); } _originalExtraTextStates.Clear(); foreach (TMP_Text extraText in _extraTexts) { _originalExtraTextStates.Add((Object)(object)extraText != (Object)null && ((Component)extraText).gameObject.activeSelf); } } private void RefreshChildren() { _keys.Clear(); _keyParents.Clear(); _extraTexts.Clear(); _label = null; TMP_Text[] array = (from text in Root.GetComponentsInChildren(true) where (Object)(object)text != (Object)null select text).ToArray(); TMP_Text[] array2 = array; foreach (TMP_Text val in array2) { Localization instance = Localization.instance; if (instance != null) { instance.RemoveTextFromCache(val); } TextMeshProUGUI val2 = (TextMeshProUGUI)(object)((val is TextMeshProUGUI) ? val : null); if (val2 != null) { ((Graphic)val2).raycastTarget = false; } } _keys.AddRange(array.Where((TMP_Text text) => string.Equals(((Object)text).name, "Key", StringComparison.OrdinalIgnoreCase))); if (_keys.Count == 0) { TMP_Text val3 = ((IEnumerable)array).FirstOrDefault((Func)((TMP_Text text) => LooksLikeKeyBindingText(text.text))) ?? array.OrderBy((TMP_Text text) => text.transform.position.x).LastOrDefault(); if ((Object)(object)val3 != (Object)null && array.Length > 1) { _keys.Add(val3); } } _label = ((IEnumerable)array).FirstOrDefault((Func)((TMP_Text text) => string.Equals(((Object)text).name, "Text", StringComparison.OrdinalIgnoreCase) && !_keys.Contains(text))) ?? ((IEnumerable)array).FirstOrDefault((Func)((TMP_Text text) => !_keys.Contains(text) && !LooksLikeKeyBindingText(text.text))) ?? ((IEnumerable)array).FirstOrDefault((Func)((TMP_Text text) => !_keys.Contains(text))); foreach (TMP_Text key in _keys) { _keyParents.Add(((Object)(object)key.transform.parent != (Object)null) ? ((Component)key.transform.parent).gameObject : ((Component)key).gameObject); } _extraTexts.AddRange(array.Where((TMP_Text text) => (Object)(object)text != (Object)(object)_label && !_keys.Contains(text))); SortKeysBySiblingIndex(); } private void SortKeysBySiblingIndex() { if (_keys.Count <= 1) { return; } List list = (from i in Enumerable.Range(0, _keys.Count) orderby (!((Object)(object)_keyParents[i] != (Object)null)) ? i : _keyParents[i].transform.GetSiblingIndex() select i).ToList(); if (list.Count <= 1) { return; } List list2 = new List(); List list3 = new List(); foreach (int item in list) { list2.Add(_keys[item]); list3.Add(_keyParents[item]); } _keys.Clear(); _keys.AddRange(list2); _keyParents.Clear(); _keyParents.AddRange(list3); } private static void SetText(TMP_Text? text, string value) { if ((Object)(object)text == (Object)null) { return; } if (!((Component)text).gameObject.activeSelf) { ((Component)text).gameObject.SetActive(true); } if (!string.Equals(text.text, value, StringComparison.Ordinal)) { Localization instance = Localization.instance; if (instance != null) { instance.RemoveTextFromCache(text); } text.text = value; } } private void SetKeyVisibility(TMP_Text? visibleKey, TMP_Text? protectedText) { foreach (GameObject item in _keyParents.Distinct()) { if (!((Object)(object)item == (Object)null)) { bool flag = ContainsText(item, visibleKey); bool flag2 = (Object)(object)item == (Object)(object)Root || ContainsText(item, protectedText); item.SetActive(flag || flag2); } } foreach (TMP_Text key in _keys) { if ((Object)(object)key != (Object)null) { ((Component)key).gameObject.SetActive((Object)(object)key == (Object)(object)visibleKey); } } } private static bool ContainsText(GameObject container, TMP_Text? text) { if ((Object)(object)text != (Object)null) { if (!((Object)(object)container == (Object)(object)((Component)text).gameObject)) { return text.transform.IsChildOf(container.transform); } return true; } return false; } private static bool LooksLikeKeyBindingText(string? text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string text2 = new string(text.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); if (!text2.Contains("mouse") && !text2.Contains("ctrl") && !text2.Contains("shift") && !text2.Contains("alt") && !text2.Contains("button") && !text2.Contains("key") && !text2.Contains("sprite")) { return text2.Length <= 2; } return true; } } internal static class KnockbackChainSystem { private const int MaxOverlapHits = 64; private const float MinChainPower = 0.05f; private const float CollisionRadius = 2f; private const string InitialHitVfx = "vfx_archerytarget_bullseye"; private const string CollisionVfx = "vfx_HitSparks"; private const string CollisionSfx = "sfx_club_hit"; private static readonly Collider[] OverlapHits = (Collider[])(object)new Collider[64]; private static int _characterMask; internal static bool IsApplyingChainDamage { get; private set; } internal static bool TryApplyForSecondaryHit(Player attacker, Character target, bool secondaryAttack, SecondaryAttackDefinition? definition, ref HitData hit) { //IL_002e: 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_003e: Expected O, but got Unknown //IL_003e: Expected O, but got Unknown //IL_00ae: 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_00cb: 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_00d7: 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) KnockbackChainDefinition knockbackChainDefinition = definition?.KnockbackChain; if (!secondaryAttack || knockbackChainDefinition == null || (Object)(object)attacker == (Object)null || (Object)(object)target == (Object)null || target.IsDead() || (Object)target == (Object)attacker) { return false; } float pushForce = hit.m_pushForce; if (pushForce <= 0f || ((DamageTypes)(ref hit.m_damage)).GetTotalDamage() <= 0f) { return false; } float num = pushForce * Mathf.Max(0f, knockbackChainDefinition.PushFactor); if (num <= 0f) { return false; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)attacker, null, "knockbackChain", knockbackChainDefinition.PresetCooldown)) { return false; } hit.m_pushForce = num; SpawnVfx("vfx_archerytarget_bullseye", ResolveVfxPoint(hit, target), ((Component)target).transform.rotation, "knockback_chain_initial_hit_vfx_missing"); Vector3 direction = ResolveDirection(hit.m_dir, (Character)(object)attacker, target); KnockbackChainState state = new KnockbackChainState((Character)(object)attacker, hit, knockbackChainDefinition); AttachTracker(target, state, num, 1f, direction); return true; } private static void AttachTracker(Character target, KnockbackChainState state, float pushForce, float chainPower, Vector3 direction) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)target == (Object)null) && !target.IsDead() && !(chainPower < 0.05f) && SecondaryAttackManager.HasCharacterAuthority(target)) { KnockbackChainTracker knockbackChainTracker = ((Component)target).GetComponent(); if ((Object)(object)knockbackChainTracker == (Object)null) { knockbackChainTracker = ((Component)target).gameObject.AddComponent(); } knockbackChainTracker.Initialize(target, state, pushForce, chainPower, direction); } } internal static void TryHitNearbyTargets(Character source, KnockbackChainState state, float pushForce, float chainPower, Vector3 travelDirection) { //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_0019: 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_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_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_0081: 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_0099: 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) KnockbackChainDefinition config = state.Config; if (state.HasReachedTargetLimit) { return; } Vector3 centerPoint = source.GetCenterPoint(); int num = 0; try { num = Physics.OverlapSphereNonAlloc(centerPoint, 2f, OverlapHits, GetCharacterMask(), (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val = OverlapHits[i]; if ((Object)(object)val == (Object)null) { continue; } Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(val); if (!IsValidCollisionTarget(state.Attacker, source, hitCharacter) || !state.CanHit(hitCharacter)) { continue; } Vector3 val2 = ResolveCollisionDirection(centerPoint, hitCharacter.GetCenterPoint(), travelDirection); if (!(Vector3.Dot(val2, travelDirection) < -0.25f) && ApplyCollisionHit(source, hitCharacter, val, state, pushForce, chainPower, val2)) { float num2 = chainPower * config.ChainDecay; if (!state.HasReachedTargetLimit && num2 >= 0.05f) { AttachTracker(hitCharacter, state, pushForce, num2, val2); } if (state.HasReachedTargetLimit) { break; } } } } finally { Array.Clear(OverlapHits, 0, OverlapHits.Length); } } private static bool ApplyCollisionHit(Character source, Character target, Collider collider, KnockbackChainState state, float pushForce, float chainPower, Vector3 direction) { //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_0061: 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_008d: 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_0098: 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_00d5: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_00ed: 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_0103: Unknown result type (might be due to invalid IL or missing references) DamageTypes damage = ((DamageTypes)(ref state.SourceHit.m_damage)).Clone(); ((DamageTypes)(ref damage)).Modify(chainPower); if (((DamageTypes)(ref damage)).GetTotalDamage() <= 0f) { return false; } if (state.SourceHit.m_dodgeable && target.IsDodgeInvincible()) { Player val = (Player)(object)((target is Player) ? target : null); if (val != null) { val.HitWhileDodging(); } return false; } HitData val2 = state.SourceHit.Clone(); val2.m_damage = damage; val2.m_pushForce = pushForce * chainPower; val2.m_skillRaiseAmount = 0f; val2.m_blockable = false; val2.m_dodgeable = false; val2.m_point = target.GetCenterPoint(); val2.m_dir = direction; val2.m_hitCollider = collider; val2.SetAttacker(state.Attacker); state.MarkHit(target); bool isApplyingChainDamage = IsApplyingChainDamage; IsApplyingChainDamage = true; try { target.Damage(val2); } finally { IsApplyingChainDamage = isApplyingChainDamage; } Quaternion rotation = Quaternion.LookRotation(direction, Vector3.up); SpawnVfx("vfx_HitSparks", val2.m_point, rotation, "knockback_chain_collision_vfx_missing"); SpawnVfx("sfx_club_hit", val2.m_point, rotation, "knockback_chain_collision_sfx_missing"); return true; } private static Vector3 ResolveVfxPoint(HitData hit, Character target) { //IL_001a: 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) if (((Vector3)(ref hit.m_point)).sqrMagnitude > 0.001f) { return hit.m_point; } return target.GetCenterPoint(); } private static void SpawnVfx(string prefabName, Vector3 position, Quaternion rotation, string warningPrefix) { //IL_005b: 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) if (string.IsNullOrWhiteSpace(prefabName)) { return; } string text = prefabName.Trim(); ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(text) : null); if ((Object)(object)val == (Object)null) { if (SecondaryAttackWarningLog.TryMarkWarning(warningPrefix + "_" + text)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Knockback chain VFX prefab '" + text + "' was not found.")); } } else { Object.Destroy((Object)(object)Object.Instantiate(val, position, rotation), 6f); } } private static bool IsValidCollisionTarget(Character attacker, Character source, Character? target) { if ((Object)(object)attacker == (Object)null || (Object)(object)source == (Object)null || (Object)(object)target == (Object)null || target.IsDead() || (Object)(object)target == (Object)(object)attacker || (Object)(object)target == (Object)(object)source) { return false; } if (BaseAI.IsEnemy(attacker, target)) { return true; } if (attacker.IsPlayer() && (Object)(object)target.GetBaseAI() != (Object)null) { return target.GetBaseAI().IsAggravatable(); } return false; } private static Vector3 ResolveCollisionDirection(Vector3 sourcePoint, Vector3 targetPoint, Vector3 fallbackDirection) { //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_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_0020: 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_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_0042: 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) Vector3 val = Vector3.ProjectOnPlane(targetPoint - sourcePoint, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(fallbackDirection, Vector3.up); } if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref val)).normalized; } private static Vector3 ResolveDirection(Vector3 hitDirection, Character attacker, Character target) { //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_000b: 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_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_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_003f: 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) //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_007a: 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) Vector3 val = Vector3.ProjectOnPlane(hitDirection, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(((Component)target).transform.position - ((Component)attacker).transform.position, Vector3.up); } if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(((Component)attacker).transform.forward, Vector3.up); } if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref val)).normalized; } private static int GetCharacterMask() { if (_characterMask == 0) { _characterMask = LayerMask.GetMask(new string[5] { "character", "character_net", "character_ghost", "hitbox", "character_noenv" }); } return _characterMask; } } internal sealed class KnockbackChainState { private const int MaxChainTargets = 5; private const float HitCooldown = 0.2f; private readonly Dictionary _lastHitTimes = new Dictionary(); public Character Attacker { get; } public HitData SourceHit { get; } public KnockbackChainDefinition Config { get; } public int CollateralHits { get; private set; } public bool HasReachedTargetLimit => CollateralHits >= 5; public KnockbackChainState(Character attacker, HitData sourceHit, KnockbackChainDefinition config) { Attacker = attacker; SourceHit = sourceHit.Clone(); Config = config; } public bool CanHit(Character target) { if ((Object)(object)target == (Object)null || HasReachedTargetLimit) { return false; } if (_lastHitTimes.TryGetValue(target, out var value)) { return Time.time - value >= 0.2f; } return true; } public void MarkHit(Character target) { _lastHitTimes[target] = Time.time; CollateralHits++; } } internal sealed class KnockbackChainTracker : MonoBehaviour { private const float Duration = 1.5f; private const float MinSpeed = 0.5f; private Character? _target; private Rigidbody? _body; private KnockbackChainState? _state; private Vector3 _lastPosition; private Vector3 _travelDirection = Vector3.forward; private float _pushForce; private float _chainPower; private float _startTime; public void Initialize(Character target, KnockbackChainState state, float pushForce, float chainPower, Vector3 travelDirection) { //IL_0070: 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_0082: 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) if (_state == null || !(_chainPower * _pushForce > chainPower * pushForce)) { _target = target; _body = (((Object)(object)target.m_body != (Object)null) ? target.m_body : ((Component)target).GetComponent()); _state = state; _pushForce = Mathf.Max(0f, pushForce); _chainPower = Mathf.Max(0f, chainPower); _travelDirection = ResolveHorizontalDirection(travelDirection, ((Component)target).transform.forward); _lastPosition = ((Component)target).transform.position; _startTime = Time.time; ((Behaviour)this).enabled = true; } } private void Update() { //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_006f: 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_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_0085: 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_0088: 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_00ad: 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_009b: 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_00c9: 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_00e6: 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_0134: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_target == (Object)null || _state == null || _target.IsDead() || !SecondaryAttackManager.HasCharacterAuthority(_target)) { Object.Destroy((Object)(object)this); return; } if (Time.time - _startTime > 1.5f || _state.HasReachedTargetLimit) { Object.Destroy((Object)(object)this); return; } Vector3 position = ((Component)_target).transform.position; Vector3 val = Vector3.ProjectOnPlane(position - _lastPosition, Vector3.up); _lastPosition = position; Vector3 val2 = (((Object)(object)_body != (Object)null) ? Vector3.ProjectOnPlane(_body.linearVelocity, Vector3.up) : Vector3.zero); if (((Vector3)(ref val2)).sqrMagnitude > 0.01f) { _travelDirection = ((Vector3)(ref val2)).normalized; } else if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { _travelDirection = ((Vector3)(ref val)).normalized; } if (!(Mathf.Max(((Vector3)(ref val2)).magnitude, ((Vector3)(ref val)).magnitude / Mathf.Max(Time.deltaTime, 0.001f)) < 0.5f)) { KnockbackChainSystem.TryHitNearbyTargets(_target, _state, _pushForce, _chainPower, _travelDirection); } } private static Vector3 ResolveHorizontalDirection(Vector3 direction, Vector3 fallback) { //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_000b: 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_0025: 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_0034: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(direction, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(fallback, Vector3.up); } if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref val)).normalized; } } internal static class LaunchSlamSystem { private const float DefaultLandingAreaRadius = 0.75f; private const float LandingAreaRadiusFactor = 1.5f; private const float LandingAreaRadiusMax = 4f; private const string LandingVfx = "vfx_archerytarget_bullseye"; private const string LandingSfx = "sfx_sledge_hit"; private static readonly Vector3 LandingVfxRotationOffset = new Vector3(90f, 0f, 0f); internal static bool IsApplyingLandingDamage { get; private set; } public static void TryApplyForSecondaryHit(Player attacker, Character target, bool secondaryAttack, SecondaryAttackDefinition? definition, ref HitData hit) { //IL_002e: 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_003e: Expected O, but got Unknown //IL_003e: Expected O, but got Unknown //IL_008d: 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_00f1: Unknown result type (might be due to invalid IL or missing references) LaunchSlamDefinition launchSlamDefinition = definition?.LaunchSlam; if (!secondaryAttack || launchSlamDefinition == null || (Object)(object)attacker == (Object)null || (Object)(object)target == (Object)null || target.IsDead() || (Object)target == (Object)attacker || ((DamageTypes)(ref hit.m_damage)).GetTotalDamage() <= 0f || !MeleePresetCooldownSystem.TryConsume((Character)(object)attacker, null, "launchSlam", launchSlamDefinition.PresetCooldown)) { return; } float num = Mathf.Max(0f, launchSlamDefinition.LaunchHeight); if (num <= 0f) { return; } DamageTypes landingDamage = ((DamageTypes)(ref hit.m_damage)).Clone(); ((DamageTypes)(ref landingDamage)).Modify(Mathf.Max(0f, launchSlamDefinition.DamageFactor)); if (!(((DamageTypes)(ref landingDamage)).GetTotalDamage() <= 0f) && TryLaunchTarget(target, num)) { hit.m_pushForce = 0f; LaunchSlamLandingTracker launchSlamLandingTracker = ((Component)target).GetComponent(); if ((Object)(object)launchSlamLandingTracker == (Object)null) { launchSlamLandingTracker = ((Component)target).gameObject.AddComponent(); } launchSlamLandingTracker.Initialize(target, (Character)(object)attacker, hit, landingDamage, num); } } private static bool TryLaunchTarget(Character target, float liftHeight) { //IL_003f: 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_006c: 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_0085: Unknown result type (might be due to invalid IL or missing references) if (!SecondaryAttackManager.HasCharacterAuthority(target)) { return false; } Rigidbody val = (((Object)(object)target.m_body != (Object)null) ? target.m_body : ((Component)target).GetComponent()); if ((Object)(object)val == (Object)null || val.isKinematic) { return false; } float num = Mathf.Max(0.1f, Mathf.Abs(Physics.gravity.y)); float num2 = Mathf.Sqrt(2f * num * liftHeight); Vector3 linearVelocity = val.linearVelocity; linearVelocity.y = Mathf.Max(linearVelocity.y, num2); val.linearVelocity = linearVelocity; target.ForceJump(linearVelocity, false); return true; } internal static void ApplyLandingDamage(Character target, Character attacker, HitData sourceHit, DamageTypes landingDamage) { //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_003e: 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_0059: 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_0060: 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_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_0084: 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_00b1: 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) if ((Object)(object)target == (Object)null || (Object)(object)attacker == (Object)null || target.IsDead() || ((DamageTypes)(ref landingDamage)).GetTotalDamage() <= 0f) { return; } Vector3 position = ((Component)target).transform.position; float radius = ResolveLandingAreaRadius(target); List list = GatherLandingAreaTargets(target, attacker, position, radius); if (list.Count == 0) { return; } Quaternion val = SecondaryAttackNamedEffectSystem.RotationFromNormal(Vector3.up); Quaternion rotation = val * Quaternion.Euler(LandingVfxRotationOffset); SecondaryAttackNamedEffectSystem.Create("vfx_archerytarget_bullseye", position, rotation, "launch_slam_landing_vfx_missing"); SecondaryAttackNamedEffectSystem.Create("sfx_sledge_hit", position, val, "launch_slam_landing_sfx_missing"); bool isApplyingLandingDamage = IsApplyingLandingDamage; IsApplyingLandingDamage = true; try { foreach (Character item in list) { ApplyLandingHit(item, attacker, sourceHit, landingDamage, position); } } finally { IsApplyingLandingDamage = isApplyingLandingDamage; } } private static List GatherLandingAreaTargets(Character source, Character attacker, Vector3 impactOrigin, float radius) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet seen = new HashSet(); AddLandingAreaTarget(source, list, seen); if (radius <= 0f) { return list; } foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsValidLandingAreaTarget(source, attacker, allCharacter) && !(ResolveHorizontalDistanceToCharacter(allCharacter, impactOrigin) > radius)) { AddLandingAreaTarget(allCharacter, list, seen); } } return list; } private static bool IsValidLandingAreaTarget(Character source, Character attacker, Character? candidate) { //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_0022: Expected O, but got Unknown //IL_0022: Expected O, but got Unknown //IL_0025: 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_0035: Expected O, but got Unknown //IL_0035: Expected O, but got Unknown if ((Object)(object)candidate == (Object)null || candidate.IsDead() || (Object)candidate == (Object)source || (Object)candidate == (Object)attacker) { return false; } if (!BaseAI.IsEnemy(attacker, candidate)) { if (attacker.IsPlayer() && (Object)(object)candidate.GetBaseAI() != (Object)null) { return candidate.GetBaseAI().IsAggravatable(); } return false; } return true; } private static void AddLandingAreaTarget(Character target, List targets, HashSet seen) { if (!((Object)(object)target == (Object)null) && !target.IsDead() && seen.Add(target)) { targets.Add(target); } } private static void ApplyLandingHit(Character target, Character attacker, HitData sourceHit, DamageTypes landingDamage, Vector3 impactOrigin) { //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_0060: 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_006e: 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_0075: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)target == (Object)null) && !((Object)(object)attacker == (Object)null) && !target.IsDead()) { HitData val = sourceHit.Clone(); val.m_damage = ((DamageTypes)(ref landingDamage)).Clone(); val.m_pushForce = 0f; val.m_staggerMultiplier = 0f; val.m_skillRaiseAmount = 0f; val.m_blockable = false; val.m_dodgeable = false; val.m_point = ResolveLandingHitPoint(target, impactOrigin); val.m_dir = ResolveLandingHitDirection(target, impactOrigin); val.m_hitCollider = null; val.SetAttacker(attacker); target.Damage(val); } } private static float ResolveLandingAreaRadius(Character target) { float num = ResolveCharacterFootprintRadius(target); return Mathf.Min(4f, num * 1.5f); } private static float ResolveCharacterFootprintRadius(Character target) { //IL_0012: 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 (!TryResolveCharacterBounds(target, out var bounds)) { return 0.75f; } float num = Mathf.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z); if (!(num > 0.01f)) { return 0.75f; } return num; } private static float ResolveHorizontalDistanceToCharacter(Character target, Vector3 origin) { //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_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_0031: 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_0070: 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) float num = float.MaxValue; bool flag = false; Collider[] componentsInChildren = ((Component)target).GetComponentsInChildren(); foreach (Collider collider in componentsInChildren) { if (IsUsableFootprintCollider(collider)) { Vector3 val = SecondaryAttackManager.ResolveSafeClosestPoint(collider, origin) - origin; val.y = 0f; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; flag = true; } } } if (flag) { return Mathf.Sqrt(num); } Vector3 val2 = target.GetCenterPoint() - origin; val2.y = 0f; return ((Vector3)(ref val2)).magnitude; } private static Vector3 ResolveLandingHitPoint(Character target, Vector3 origin) { //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_0058: 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_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_0030: 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_004b: 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) float num = float.MaxValue; Vector3 result = target.GetCenterPoint(); Collider[] componentsInChildren = ((Component)target).GetComponentsInChildren(); foreach (Collider collider in componentsInChildren) { if (IsUsableFootprintCollider(collider)) { Vector3 val = SecondaryAttackManager.ResolveSafeClosestPoint(collider, origin); Vector3 val2 = val - origin; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = val; } } } return result; } private static Vector3 ResolveLandingHitDirection(Character target, Vector3 origin) { //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_000c: 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_0027: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target.GetCenterPoint() - origin; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { return Vector3.down; } return ((Vector3)(ref val)).normalized; } private static bool TryResolveCharacterBounds(Character target, out Bounds bounds) { //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) //IL_0026: 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_004f: 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_0045: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool flag = false; Collider[] componentsInChildren = ((Component)target).GetComponentsInChildren(); foreach (Collider val in componentsInChildren) { if (!IsUsableFootprintCollider(val)) { continue; } Bounds bounds2 = val.bounds; Vector3 size = ((Bounds)(ref bounds2)).size; if (!(((Vector3)(ref size)).sqrMagnitude <= 0.0001f)) { if (!flag) { bounds = bounds2; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(bounds2); } } } return flag; } private static bool IsUsableFootprintCollider(Collider collider) { if ((Object)(object)collider != (Object)null && collider.enabled) { return !collider.isTrigger; } return false; } } internal sealed class LaunchSlamLandingTracker : MonoBehaviour { private readonly struct CollisionIgnorePair { public Collider First { get; } public Collider Second { get; } public CollisionIgnorePair(Collider first, Collider second) { First = first; Second = second; } } private const float MinAirTime = 0.2f; private const float LandingTimeout = 3f; private Character? _target; private Character? _attacker; private Rigidbody? _body; private HitData? _sourceHit; private DamageTypes _landingDamage; private float _targetApexHeight; private float _startTime; private float _startHeight; private bool _hasBeenAirborne; private bool _ignoredCharacterCollisions; private readonly List _ignoredCollisionPairs = new List(); public void Initialize(Character target, Character attacker, HitData sourceHit, DamageTypes landingDamage, float launchHeight) { //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_0075: 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) float totalDamage = ((DamageTypes)(ref landingDamage)).GetTotalDamage(); if (_sourceHit == null || !(totalDamage <= ((DamageTypes)(ref _landingDamage)).GetTotalDamage())) { RestoreIgnoredCharacterCollisions(); _target = target; _attacker = attacker; _body = (((Object)(object)target.m_body != (Object)null) ? target.m_body : ((Component)target).GetComponent()); _sourceHit = sourceHit.Clone(); _landingDamage = ((DamageTypes)(ref landingDamage)).Clone(); _targetApexHeight = ((Component)target).transform.position.y + Mathf.Max(0f, launchHeight); _startTime = Time.time; _startHeight = ((Component)target).transform.position.y; _hasBeenAirborne = false; IgnoreOtherCharacterCollisions(target); ((Behaviour)this).enabled = true; } } private void Update() { //IL_006b: 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) if ((Object)(object)_target == (Object)null || (Object)(object)_attacker == (Object)null || _sourceHit == null || _target.IsDead()) { Object.Destroy((Object)(object)this); return; } float num = Time.time - _startTime; if (num > 3f) { Object.Destroy((Object)(object)this); return; } bool flag = _target.IsOnGround(); if (((Component)_target).transform.position.y > _startHeight + 0.15f) { _hasBeenAirborne = true; } if (_ignoredCharacterCollisions && HasAscentEnded()) { RestoreIgnoredCharacterCollisions(); } if (_hasBeenAirborne && !(num < 0.2f) && flag) { LaunchSlamSystem.ApplyLandingDamage(_target, _attacker, _sourceHit, _landingDamage); Object.Destroy((Object)(object)this); } } private bool HasAscentEnded() { //IL_001b: 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) if ((Object)(object)_target == (Object)null) { return true; } if (((Component)_target).transform.position.y >= _targetApexHeight - 0.05f) { return true; } if ((Object)(object)_body == (Object)null) { return false; } if (_hasBeenAirborne) { return _body.linearVelocity.y <= 0.05f; } return false; } private void IgnoreOtherCharacterCollisions(Character target) { Collider[] collisionColliders = GetCollisionColliders(target); if (collisionColliders.Length == 0) { return; } foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)target || allCharacter.IsDead()) { continue; } Collider[] collisionColliders2 = GetCollisionColliders(allCharacter); Collider[] array = collisionColliders; foreach (Collider val in array) { Collider[] array2 = collisionColliders2; foreach (Collider val2 in array2) { if (!((Object)(object)val == (Object)(object)val2) && !Physics.GetIgnoreCollision(val, val2)) { Physics.IgnoreCollision(val, val2, true); _ignoredCollisionPairs.Add(new CollisionIgnorePair(val, val2)); } } } } _ignoredCharacterCollisions = _ignoredCollisionPairs.Count > 0; } private void RestoreIgnoredCharacterCollisions() { foreach (CollisionIgnorePair ignoredCollisionPair in _ignoredCollisionPairs) { if ((Object)(object)ignoredCollisionPair.First != (Object)null && (Object)(object)ignoredCollisionPair.Second != (Object)null) { Physics.IgnoreCollision(ignoredCollisionPair.First, ignoredCollisionPair.Second, false); } } _ignoredCollisionPairs.Clear(); _ignoredCharacterCollisions = false; } private void OnDestroy() { RestoreIgnoredCharacterCollisions(); } private static Collider[] GetCollisionColliders(Character character) { List list = new List(); Collider[] componentsInChildren = ((Component)character).GetComponentsInChildren(); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null && val.enabled && !val.isTrigger) { list.Add(val); } } return list.ToArray(); } } internal static class GroundworkCompat { private sealed class NoopDisposable : IDisposable { public void Dispose() { } } private const string GroundworkAssemblyName = "Groundwork"; private const string FarmingSkillSystemTypeName = "Groundwork.FarmingSkillSystem"; private const string ScytheHarvestSystemTypeName = "Groundwork.ScytheHarvestSystem"; private static readonly IDisposable NoopScope = new NoopDisposable(); private static bool _resolved; private static MethodInfo? _suppressRangePickupMethod; private static MethodInfo? _isForagingTargetMethod; private static MethodInfo? _canScytheHarvestMethod; internal static IDisposable SuppressForagingRangePickup() { Resolve(); if (_suppressRangePickupMethod == null) { return NoopScope; } try { return (_suppressRangePickupMethod.Invoke(null, null) as IDisposable) ?? NoopScope; } catch { return NoopScope; } } internal static bool IsForagingTarget(Pickable? pickable) { if ((Object)(object)pickable == (Object)null) { return false; } Resolve(); if (_isForagingTargetMethod != null) { try { object obj = _isForagingTargetMethod.Invoke(null, new object[1] { pickable }); if (obj is bool) { return (bool)obj; } } catch { } } return IsForagingTargetFallback(pickable); } internal static bool CanScytheHarvest(Pickable? pickable) { if ((Object)(object)pickable == (Object)null) { return false; } Resolve(); if (_canScytheHarvestMethod != null) { try { object obj = _canScytheHarvestMethod.Invoke(null, new object[1] { pickable }); if (obj is bool) { return (bool)obj; } } catch { } } if (!pickable.m_harvestable) { return IsForagingTarget(pickable); } return true; } private static void Resolve() { if (!_resolved) { _resolved = true; Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly assembly2) => string.Equals(assembly2.GetName().Name, "Groundwork", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("Groundwork.FarmingSkillSystem"); Type? obj = assembly?.GetType("Groundwork.ScytheHarvestSystem"); _suppressRangePickupMethod = type?.GetMethod("SuppressRangePickup", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _isForagingTargetMethod = type?.GetMethod("IsForagingTarget", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _canScytheHarvestMethod = obj?.GetMethod("CanScytheHarvest", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } private static bool IsForagingTargetFallback(Pickable pickable) { if (pickable.m_respawnTimeMinutes > 0f) { return DropsEdibleItem(pickable); } return false; } private static bool DropsEdibleItem(Pickable pickable) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (IsEdibleItemPrefab(pickable.m_itemPrefab)) { return true; } if (pickable.m_extraDrops?.m_drops == null) { return false; } foreach (DropData drop in pickable.m_extraDrops.m_drops) { if (IsEdibleItemPrefab(drop.m_item)) { return true; } } return false; } private static bool IsEdibleItemPrefab(GameObject? itemPrefab) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null); if (val?.m_itemData?.m_shared == null) { return false; } SharedData shared = val.m_itemData.m_shared; if ((int)shared.m_itemType == 2) { if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f)) { return shared.m_foodEitr > 0f; } return true; } return false; } } internal static class MagicSummonQualityPresetSystem { private sealed class ObjectDbState { internal Dictionary OriginalMaxQualities { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } internal sealed class SpawnAbilityRuntimeState { private int MaxSpawned { get; } private bool SetMaxInstancesFromWeaponLevel { get; } private List? LevelUpSettings { get; } internal string GroupId { get; } internal int MaxInstances { get; } internal ZDOID OwnerId { get; } internal HashSet ExistingSummonInstanceIds { get; } private SpawnAbilityRuntimeState(int maxSpawned, bool setMaxInstancesFromWeaponLevel, List? levelUpSettings, string groupId, int maxInstances, ZDOID ownerId, HashSet existingSummonInstanceIds) { //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) MaxSpawned = maxSpawned; SetMaxInstancesFromWeaponLevel = setMaxInstancesFromWeaponLevel; LevelUpSettings = levelUpSettings; GroupId = groupId; MaxInstances = maxInstances; OwnerId = ownerId; ExistingSummonInstanceIds = existingSummonInstanceIds; } internal static SpawnAbilityRuntimeState Capture(SpawnAbility spawnAbility, string groupId, int maxInstances, ZDOID ownerId) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return new SpawnAbilityRuntimeState(spawnAbility.m_maxSpawned, spawnAbility.m_setMaxInstancesFromWeaponLevel, spawnAbility.m_levelUpSettings, groupId, maxInstances, ownerId, SnapshotSummonInstanceIds(groupId)); } internal void Restore(SpawnAbility spawnAbility) { spawnAbility.m_maxSpawned = MaxSpawned; spawnAbility.m_setMaxInstancesFromWeaponLevel = SetMaxInstancesFromWeaponLevel; spawnAbility.m_levelUpSettings = LevelUpSettings; } } private sealed class QualityRule { internal string ItemPrefabName { get; } internal MagicSummonQualityPreset Preset { get; } internal int MaxQuality { get; } internal string GroupId { get; } internal QualityRule(string itemPrefabName, MagicSummonQualityPreset preset, int maxQuality) { ItemPrefabName = itemPrefabName; Preset = preset; MaxQuality = maxQuality; GroupId = "SecondaryAttacks.MagicSummon." + SanitizeGroupKey(itemPrefabName); } internal int GetSummonLevel(int itemQuality) { if (Preset != MagicSummonQualityPreset.LevelByQuality) { return 1; } return Mathf.Clamp(itemQuality, 1, MaxQuality); } internal int GetMaxInstances(int itemQuality) { if (Preset != MagicSummonQualityPreset.CountByQuality) { return 1; } return Mathf.Clamp(itemQuality, 1, MaxQuality); } } private const int GlobalMaxQuality = 4; private const int GlobalFixedSummonLevel = 1; private const string SummonOwnerZdoKey = "SecondaryAttacks_SummonOwner"; private static readonly ConditionalWeakTable ObjectDbStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable PendingSpawnAbilityStates = new ConditionalWeakTable(); private static readonly MethodInfo? TameableUnSummonMethod = AccessTools.DeclaredMethod(typeof(Tameable), "UnSummon", (Type[])null, (Type[])null); private static readonly Dictionary RulesByItemPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RulesBySharedName = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RulesBySpawnAbilityPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ActiveGroupIds = new HashSet(StringComparer.OrdinalIgnoreCase); private static List _activeRules = new List(); internal static void RestoreObjectDb(ObjectDB objectDb) { if (!((Object)(object)objectDb == (Object)null)) { ObjectDbState value = ObjectDbStates.GetValue(objectDb, (ObjectDB _) => new ObjectDbState()); RestoreOriginalMaxQualities(objectDb, value); RulesBySharedName.Clear(); } } internal static void ApplyToObjectDb(ObjectDB objectDb, IReadOnlyDictionary summonConfigs) { if ((Object)(object)objectDb == (Object)null) { return; } ObjectDbState value = ObjectDbStates.GetValue(objectDb, (ObjectDB _) => new ObjectDbState()); RebuildCoreRules(summonConfigs, objectDb); RulesBySharedName.Clear(); if (_activeRules.Count == 0) { return; } foreach (QualityRule activeRule in _activeRules) { GameObject itemPrefab = objectDb.GetItemPrefab(activeRule.ItemPrefabName); SharedData val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null)?.m_itemData?.m_shared; if (!((Object)(object)itemPrefab == (Object)null) && val != null) { string name = ((Object)itemPrefab).name; if (!value.OriginalMaxQualities.ContainsKey(name)) { value.OriginalMaxQualities[name] = val.m_maxQuality; } val.m_maxQuality = activeRule.MaxQuality; RulesBySharedName[val.m_name] = activeRule; } } } internal static bool TryGetItemQualityPreset(ItemData? item, out MagicSummonQualityPreset preset) { preset = MagicSummonQualityPreset.None; if (!TryResolveItemRule(item, out QualityRule rule)) { return false; } preset = rule.Preset; return preset != MagicSummonQualityPreset.None; } internal static void ApplyToZNetScene(ZNetScene scene, IReadOnlyDictionary summonConfigs) { if ((Object)(object)scene == (Object)null) { return; } RebuildCoreRules(summonConfigs, ObjectDB.instance); RulesBySpawnAbilityPrefab.Clear(); if (_activeRules.Count == 0) { return; } foreach (QualityRule activeRule in _activeRules) { if (TryResolveTargetSpawnAbility(scene, activeRule, out SpawnAbility spawnAbility, out string targetName) && !((Object)(object)spawnAbility == (Object)null)) { RulesBySpawnAbilityPrefab[targetName] = activeRule; string prefabName = Utils.GetPrefabName(((Component)spawnAbility).gameObject); if (!string.IsNullOrWhiteSpace(prefabName)) { RulesBySpawnAbilityPrefab[prefabName] = activeRule; } } } } internal static SpawnAbilityRuntimeState? PrepareSpawnAbilityForRule(SpawnAbility spawnAbility, Character? owner, ItemData item) { //IL_0078: 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_007d: 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_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_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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown if ((Object)(object)spawnAbility == (Object)null || item == null) { return null; } if (!TryResolveRule(spawnAbility, item, out QualityRule rule)) { if (PendingSpawnAbilityStates.TryGetValue(spawnAbility, out SpawnAbilityRuntimeState value)) { value.Restore(spawnAbility); } PendingSpawnAbilityStates.Remove(spawnAbility); ResetSpawnPrefabTags(spawnAbility); return null; } int itemQuality = Mathf.Clamp(item.m_quality, 1, rule.MaxQuality); int summonLevel = rule.GetSummonLevel(itemQuality); int maxInstances = rule.GetMaxInstances(itemQuality); ZDOID ownerId = (((Object)(object)owner != (Object)null) ? owner.GetZDOID() : ZDOID.None); TagSpawnPrefabs(spawnAbility, rule, maxInstances, summonLevel); SpawnAbilityRuntimeState spawnAbilityRuntimeState = SpawnAbilityRuntimeState.Capture(spawnAbility, rule.GroupId, maxInstances, ownerId); spawnAbility.m_maxSpawned = 0; spawnAbility.m_setMaxInstancesFromWeaponLevel = false; spawnAbility.m_levelUpSettings = new List { new LevelUpSettings { m_skill = (SkillType)10, m_skillLevel = 0, m_setLevel = summonLevel, m_maxSpawns = maxInstances } }; PendingSpawnAbilityStates.Remove(spawnAbility); PendingSpawnAbilityStates.Add(spawnAbility, spawnAbilityRuntimeState); return spawnAbilityRuntimeState; } internal static bool TryConsumePendingSpawnAbilityState(SpawnAbility spawnAbility, out SpawnAbilityRuntimeState? state) { state = null; if ((Object)(object)spawnAbility == (Object)null || !PendingSpawnAbilityStates.TryGetValue(spawnAbility, out SpawnAbilityRuntimeState value)) { return false; } PendingSpawnAbilityStates.Remove(spawnAbility); state = value; return true; } internal static IEnumerator WrapSpawnWithRestore(SpawnAbility spawnAbility, IEnumerator? spawnEnumerator, SpawnAbilityRuntimeState state) { try { if (spawnEnumerator != null) { while (spawnEnumerator.MoveNext()) { yield return spawnEnumerator.Current; } } } finally { RegisterNewSummonsAndEnforceLimit(state); RestoreSpawnAbilityAfterSpawn(spawnAbility, state); } } private static void RestoreSpawnAbilityAfterSpawn(SpawnAbility spawnAbility, SpawnAbilityRuntimeState? state) { if (!((Object)(object)spawnAbility == (Object)null)) { state?.Restore(spawnAbility); } } internal static void EnforceSummonGroupLimit(Tameable summoned, ZDOID ownerId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)summoned == (Object)null)) { EnforceSummonGroupLimit(((Component)summoned).GetComponent(), ownerId); } } private static void EnforceSummonGroupLimit(Character? summoned, ZDOID ownerId) { //IL_005c: 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) if ((Object)(object)summoned == (Object)null) { return; } SummonQualityPresetTag component = ((Component)summoned).GetComponent(); if (!((Object)(object)component == (Object)null) && !string.IsNullOrWhiteSpace(component.GroupId) && ActiveGroupIds.Contains(component.GroupId)) { ZNetView component2 = ((Component)summoned).GetComponent(); if (!((Object)(object)component2 == (Object)null) && component2.IsValid() && component2.IsOwner()) { MarkSummonOwner(summoned, ownerId); EnforceSummonGroupLimit(component.GroupId, ownerId, component.MaxInstances, showMessage: true); } } } private static void RegisterNewSummonsAndEnforceLimit(SpawnAbilityRuntimeState state) { //IL_000e: 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_009f: 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) if (string.IsNullOrWhiteSpace(state.GroupId)) { return; } ZDOID ownerId = state.OwnerId; if (((ZDOID)(ref ownerId)).IsNone()) { return; } foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !state.ExistingSummonInstanceIds.Contains(((Object)allCharacter).GetInstanceID())) { SummonQualityPresetTag component = ((Component)allCharacter).GetComponent(); if (!((Object)(object)component == (Object)null) && string.Equals(component.GroupId, state.GroupId, StringComparison.OrdinalIgnoreCase)) { MarkSummonOwner(allCharacter, state.OwnerId); } } } EnforceSummonGroupLimit(state.GroupId, state.OwnerId, state.MaxInstances, showMessage: true); } private static void EnforceSummonGroupLimit(string groupId, ZDOID ownerId, int maxInstances, bool showMessage) { //IL_0012: 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) if (string.IsNullOrWhiteSpace(groupId) || ((ZDOID)(ref ownerId)).IsNone()) { return; } Player val = ResolveOwner(ownerId); List list = new List(); foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsDead() && IsMatchingSummonGroup(allCharacter, groupId) && IsMatchingSummonOwner(allCharacter, ownerId, val)) { list.Add(allCharacter); } } list.Sort(CompareOlderFirst); int num = list.Count - Mathf.Max(1, maxInstances); for (int i = 0; i < num && i < list.Count; i++) { RemoveSummon(list[i]); } if (showMessage && num > 0 && (Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { ((Character)val).Message((MessageType)2, "$hud_maxsummonsreached", 0, (Sprite)null); } } private static void RebuildCoreRules(IReadOnlyDictionary summonConfigs, ObjectDB? objectDb) { _activeRules = BuildQualityRules(summonConfigs); AppendGlobalQualityRules(_activeRules, objectDb, summonConfigs); RulesByItemPrefab.Clear(); ActiveGroupIds.Clear(); foreach (QualityRule activeRule in _activeRules) { ActiveGroupIds.Add(activeRule.GroupId); RulesByItemPrefab[activeRule.ItemPrefabName] = activeRule; } } private static List BuildQualityRules(IReadOnlyDictionary summonConfigs) { List list = new List(); foreach (NormalizedMagicSummonOverrideConfig value in summonConfigs.Values) { if (value != null && value.HasQualityPreset) { string itemPrefabName = TrimOrEmpty(value.EntryId); list.Add(new QualityRule(itemPrefabName, value.QualityPreset, Mathf.Clamp(value.MaxQuality, 1, 10))); } } return list; } private static void AppendGlobalQualityRules(List rules, ObjectDB? objectDb, IReadOnlyDictionary summonConfigs) { MagicSummonQualityPreset configuredGlobalPreset = GetConfiguredGlobalPreset(); if (configuredGlobalPreset == MagicSummonQualityPreset.None || objectDb?.m_items == null) { return; } HashSet hashSet = new HashSet(rules.Select((QualityRule rule) => rule.ItemPrefabName), StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(from config in summonConfigs.Values where config != null && config.Summons.Count > 0 select TrimOrEmpty(config.EntryId), StringComparer.OrdinalIgnoreCase); foreach (GameObject item in objectDb.m_items) { if (!((Object)(object)item == (Object)null) && !string.IsNullOrWhiteSpace(((Object)item).name) && !hashSet.Contains(((Object)item).name) && IsBloodMagicSummonItem(item.GetComponent(), hashSet2.Contains(((Object)item).name))) { rules.Add(new QualityRule(((Object)item).name, configuredGlobalPreset, 4)); hashSet.Add(((Object)item).name); } } } private static MagicSummonQualityPreset GetConfiguredGlobalPreset() { return SecondaryAttacksPlugin.MagicSummonQualityPreset?.Value switch { SecondaryAttacksPlugin.MagicSummonQualityPresetSelection.CountByQuality => MagicSummonQualityPreset.CountByQuality, SecondaryAttacksPlugin.MagicSummonQualityPresetSelection.LevelByQuality => MagicSummonQualityPreset.LevelByQuality, _ => MagicSummonQualityPreset.None, }; } private static bool IsBloodMagicSummonItem(ItemDrop? itemDrop, bool hasSummonOverride) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 SharedData val = itemDrop?.m_itemData?.m_shared; if (val == null || (int)val.m_skillType != 10) { return false; } if (SummonSpawnAbilityResolver.TryResolve(val, out SpawnAbility spawnAbility, out string _)) { if (!hasSummonOverride) { if (spawnAbility == null) { return false; } return spawnAbility.m_spawnPrefab?.Any((GameObject prefab) => (Object)(object)prefab != (Object)null) == true; } return true; } return false; } private static void RestoreOriginalMaxQualities(ObjectDB objectDb, ObjectDbState state) { foreach (KeyValuePair originalMaxQuality in state.OriginalMaxQualities) { originalMaxQuality.Deconstruct(out var key, out var value); string text = key; int maxQuality = value; GameObject itemPrefab = objectDb.GetItemPrefab(text); SharedData val = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent()?.m_itemData?.m_shared); if (val != null) { val.m_maxQuality = maxQuality; } } state.OriginalMaxQualities.Clear(); } private static bool TryResolveRule(SpawnAbility spawnAbility, ItemData item, out QualityRule rule) { if (TryResolveItemRule(item, out rule)) { return true; } string prefabName = Utils.GetPrefabName(((Component)spawnAbility).gameObject); if (!string.IsNullOrWhiteSpace(prefabName) && RulesBySpawnAbilityPrefab.TryGetValue(prefabName, out rule)) { return true; } string text = (((Object)(object)((Component)spawnAbility).gameObject != (Object)null) ? ((Object)((Component)spawnAbility).gameObject).name : ""); if (!string.IsNullOrWhiteSpace(text) && RulesBySpawnAbilityPrefab.TryGetValue(text, out rule)) { return true; } rule = null; return false; } private static bool TryResolveItemRule(ItemData? item, out QualityRule rule) { string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); if (!string.IsNullOrWhiteSpace(text) && RulesByItemPrefab.TryGetValue(text, out rule)) { return true; } string text2 = item?.m_shared?.m_name ?? ""; if (!string.IsNullOrWhiteSpace(text2) && RulesBySharedName.TryGetValue(text2, out rule)) { return true; } rule = null; return false; } private static bool TryResolveTargetSpawnAbility(ZNetScene scene, QualityRule rule, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; GameObject val = ResolveItemPrefab(scene, rule.ItemPrefabName); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (val2?.m_itemData?.m_shared == null) { return false; } return SummonSpawnAbilityResolver.TryResolve(val2, out spawnAbility, out targetName); } private static GameObject? ResolveItemPrefab(ZNetScene scene, string itemPrefabName) { if (string.IsNullOrWhiteSpace(itemPrefabName)) { return null; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemPrefabName) : null); if (!((Object)(object)val != (Object)null)) { return scene.GetPrefab(itemPrefabName); } return val; } private static void TagSpawnPrefabs(SpawnAbility spawnAbility, QualityRule rule, int maxInstances, int summonLevel) { GameObject[] array = spawnAbility.m_spawnPrefab ?? Array.Empty(); foreach (GameObject val in array) { if (!((Object)(object)val == (Object)null)) { SummonQualityPresetTag summonQualityPresetTag = val.GetComponent(); if ((Object)(object)summonQualityPresetTag == (Object)null) { summonQualityPresetTag = val.AddComponent(); } summonQualityPresetTag.GroupId = rule.GroupId; summonQualityPresetTag.MaxInstances = Mathf.Max(1, maxInstances); summonQualityPresetTag.UsesLevelByQuality = rule.Preset == MagicSummonQualityPreset.LevelByQuality; summonQualityPresetTag.SummonLevel = ((!summonQualityPresetTag.UsesLevelByQuality) ? 1 : Mathf.Max(1, summonLevel)); } } } private static void ResetSpawnPrefabTags(SpawnAbility spawnAbility) { //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) GameObject[] array = spawnAbility.m_spawnPrefab ?? Array.Empty(); foreach (GameObject val in array) { SummonQualityPresetTag summonQualityPresetTag = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (!((Object)(object)summonQualityPresetTag == (Object)null)) { summonQualityPresetTag.GroupId = ""; summonQualityPresetTag.MaxInstances = 1; summonQualityPresetTag.UsesLevelByQuality = false; summonQualityPresetTag.SummonLevel = 1; summonQualityPresetTag.OwnerId = ZDOID.None; } } } private static HashSet SnapshotSummonInstanceIds(string groupId) { HashSet hashSet = new HashSet(); foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter != (Object)null && IsMatchingSummonGroup(allCharacter, groupId)) { hashSet.Add(((Object)allCharacter).GetInstanceID()); } } return hashSet; } private static bool IsMatchingSummonGroup(Character character, string groupId) { SummonQualityPresetTag component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(component.GroupId)) { return string.Equals(component.GroupId, groupId, StringComparison.OrdinalIgnoreCase); } return false; } private static void MarkSummonOwner(Character summon, ZDOID ownerId) { //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_004c: Unknown result type (might be due to invalid IL or missing references) if (!((ZDOID)(ref ownerId)).IsNone()) { SummonQualityPresetTag component = ((Component)summon).GetComponent(); if ((Object)(object)component != (Object)null) { component.OwnerId = ownerId; } ZNetView component2 = ((Component)summon).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.IsValid() && component2.IsOwner()) { component2.GetZDO().Set("SecondaryAttacks_SummonOwner", ownerId); } } } private static bool IsMatchingSummonOwner(Character summon, ZDOID ownerId, Player? owner) { //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_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_0067: 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_007a: 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) SummonQualityPresetTag component = ((Component)summon).GetComponent(); if ((Object)(object)component != (Object)null && !((ZDOID)(ref component.OwnerId)).IsNone() && component.OwnerId == ownerId) { return true; } ZNetView component2 = ((Component)summon).GetComponent(); ZDO val = (((Object)(object)component2 != (Object)null && component2.IsValid()) ? component2.GetZDO() : null); if (val != null) { ZDOID zDOID = val.GetZDOID("SecondaryAttacks_SummonOwner"); if (!((ZDOID)(ref zDOID)).IsNone() && zDOID == ownerId) { if ((Object)(object)component != (Object)null) { component.OwnerId = zDOID; } return true; } } if ((Object)(object)owner != (Object)null) { return IsFollowingOwner(((Component)summon).gameObject, owner); } return false; } private static int CompareOlderFirst(Character left, Character right) { float timeSinceSpawned = GetTimeSinceSpawned(left); return GetTimeSinceSpawned(right).CompareTo(timeSinceSpawned); } private static float GetTimeSinceSpawned(Character character) { BaseAI component = ((Component)character).GetComponent(); if (!((Object)(object)component != (Object)null)) { return 0f; } return (float)component.GetTimeSinceSpawned().TotalSeconds; } private static void RemoveSummon(Character summon) { Tameable component = ((Component)summon).GetComponent(); if ((Object)(object)component != (Object)null) { InvokeUnSummon(component); return; } ZNetView component2 = ((Component)summon).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.IsValid() && component2.IsOwner()) { ZNetScene.instance.Destroy(((Component)summon).gameObject); } } private static Player? ResolveOwner(ZDOID ownerId) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(ownerId) : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent(); } private static bool IsFollowingOwner(GameObject summon, Player owner) { MonsterAI component = summon.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.GetFollowTarget() == (Object)(object)((Component)owner).gameObject) { return true; } ZNetView component2 = summon.GetComponent(); ZDO val = (((Object)(object)component2 != (Object)null && component2.IsValid()) ? component2.GetZDO() : null); if (val != null) { return val.GetString(ZDOVars.s_follow, "") == owner.GetPlayerName(); } return false; } private static void InvokeUnSummon(Tameable tameable) { if (!(TameableUnSummonMethod == null)) { TameableUnSummonMethod.Invoke(tameable, Array.Empty()); } } private static string TrimOrEmpty(string? value) { if (!string.IsNullOrWhiteSpace(value)) { return value.Trim(); } return string.Empty; } private static string SanitizeGroupKey(string value) { char[] array = value.ToCharArray(); for (int i = 0; i < array.Length; i++) { if (!char.IsLetterOrDigit(array[i])) { array[i] = '_'; } } string text = new string(array).Trim('_'); if (!string.IsNullOrWhiteSpace(text)) { return text; } return "Summon"; } } internal sealed class SummonQualityPresetTag : MonoBehaviour { [SerializeField] internal string GroupId = ""; [SerializeField] internal int MaxInstances = 1; [SerializeField] internal bool UsesLevelByQuality; [SerializeField] internal int SummonLevel = 1; [NonSerialized] internal ZDOID OwnerId = ZDOID.None; } [HarmonyPatch(typeof(SpawnAbility), "Setup")] internal static class SpawnAbilitySetupMagicSummonQualityPresetPatch { private static void Prefix(SpawnAbility __instance, Character owner, ItemData item) { MagicSummonQualityPresetSystem.PrepareSpawnAbilityForRule(__instance, owner, item); } } [HarmonyPatch(typeof(SpawnAbility), "Spawn")] internal static class SpawnAbilitySpawnMagicSummonQualityPresetPatch { private static void Postfix(SpawnAbility __instance, ref IEnumerator __result) { if (MagicSummonQualityPresetSystem.TryConsumePendingSpawnAbilityState(__instance, out MagicSummonQualityPresetSystem.SpawnAbilityRuntimeState state) && state != null) { __result = MagicSummonQualityPresetSystem.WrapSpawnWithRestore(__instance, __result, state); } } } [HarmonyPatch(typeof(Tameable), "RPC_Command")] internal static class TameableRpcCommandMagicSummonQualityLimitPatch { private static void Postfix(Tameable __instance, ZDOID characterID) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) MagicSummonQualityPresetSystem.EnforceSummonGroupLimit(__instance, characterID); } } internal static class MeleePresetCooldownSystem { internal static bool TryConsume(Character attacker, ItemData? weapon, string presetName, MeleePresetCooldownDefinition cooldown) { if ((Object)(object)attacker == (Object)null) { return true; } if (cooldown == null) { return true; } string cooldownGroup = SecondaryAttackCooldownGroupResolver.ResolveMeleeGroup(presetName, cooldown); if (weapon == null) { weapon = ResolveCurrentWeapon(attacker); } float num = Mathf.Max(0f, cooldown.Cooldown); float num2 = Mathf.Clamp01(ResolveCooldownSkillLevel(attacker, weapon, cooldown.CooldownSkill) / 100f) * Mathf.Clamp01(cooldown.CooldownReductionFactor); float duration = Mathf.Max(0f, num * (1f - num2)); Sprite icon = ResolveWeaponIcon(weapon) ?? ResolveRegisteredIcon(presetName); return SecondaryCooldownGroupSystem.TryConsume(attacker, cooldownGroup, duration, icon); } internal static bool IsReady(Character attacker, string presetName, MeleePresetCooldownDefinition cooldown) { if ((Object)(object)attacker == (Object)null) { return true; } if (cooldown == null) { return true; } string cooldownGroup = SecondaryAttackCooldownGroupResolver.ResolveMeleeGroup(presetName, cooldown); return SecondaryCooldownGroupSystem.IsReady(attacker, cooldownGroup); } internal static bool IsCooldownActive(Character attacker, string presetName, MeleePresetCooldownDefinition cooldown, out float remaining) { remaining = 0f; if ((Object)(object)attacker == (Object)null) { return false; } if (cooldown == null) { return false; } string cooldownGroup = SecondaryAttackCooldownGroupResolver.ResolveMeleeGroup(presetName, cooldown); return SecondaryCooldownGroupSystem.IsCooldownActive(attacker, cooldownGroup, out remaining); } private static Sprite? ResolveWeaponIcon(ItemData? weapon) { Sprite[] array = weapon?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveRegisteredIcon(string presetName) { return SecondaryAttackPresetCatalog.ResolveIcon(presetName); } private static ItemData? ResolveCurrentWeapon(Character attacker) { Humanoid val = (Humanoid)(object)((attacker is Humanoid) ? attacker : null); if (val == null) { return null; } return val.GetCurrentWeapon(); } private static float ResolveCooldownSkillLevel(Character attacker, ItemData? weapon, string configuredSkill) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveSkillType(configuredSkill, weapon, out var skillType)) { return 0f; } return Mathf.Clamp(attacker.GetSkillLevel(skillType), 0f, 100f); } private static bool TryResolveSkillType(string configuredSkill, ItemData? weapon, out SkillType skillType) { //IL_004a: 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_0063: Expected I4, but got Unknown string text = NormalizeSkillToken(configuredSkill); if (!string.IsNullOrEmpty(text)) { bool flag; string value; switch (text) { case "weapon": case "current": case "equipped": break; case "none": case "off": case "disabled": flag = true; goto IL_0092; default: { flag = false; goto IL_0092; } IL_0092: if (flag) { skillType = (SkillType)0; return false; } switch (text) { case "sword": case "swords": value = "Swords"; break; case "knife": case "knives": value = "Knives"; break; case "spear": case "spears": value = "Spears"; break; case "clubs": case "maces": case "club": case "mace": value = "Clubs"; break; case "fists": case "fist": case "unarmed": value = "Unarmed"; break; case "axes": case "axe": value = "Axes"; break; case "atgeir": case "polearm": case "atgeirs": case "polearms": value = "Polearms"; break; case "pickaxe": case "pickaxes": value = "Pickaxes"; break; case "bows": case "bow": value = "Bows"; break; case "crossbow": case "crossbows": value = "Crossbows"; break; case "sneak": case "sneaking": value = "Sneak"; break; case "block": case "blocking": value = "Blocking"; break; case "blood": case "bloodmagic": value = "BloodMagic"; break; case "elemental": case "elementalmagic": value = "ElementalMagic"; break; case "woodcut": case "woodcutting": value = "WoodCutting"; break; case "farming": value = "Farming"; break; case "fishing": value = "Fishing"; break; default: value = configuredSkill?.Trim() ?? ""; break; } if (Enum.TryParse(value, ignoreCase: true, out skillType)) { return (int)skillType != 0; } return false; } } if (weapon?.m_shared == null || (int)weapon.m_shared.m_skillType == 0) { skillType = (SkillType)0; return false; } skillType = (SkillType)(int)weapon.m_shared.m_skillType; return true; } private static string NormalizeSkillToken(string? value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } return value.Trim().Replace(" ", "").Replace("_", "") .Replace("-", "") .ToLowerInvariant(); } } internal static class MeleeProjectileHitCascadeSystem { private sealed class OnProjectileHitSourceState { public SecondaryAttackDefinition Definition { get; } public MeleeOnProjectileHitDefinition Config { get; } public GameObject ProjectilePrefab { get; } public Attack SourceAttack { get; } public Character Owner { get; } public ItemData Weapon { get; } public ItemData? Ammo { get; } public float HitNoise { get; } public HitData BaseHitData { get; } public float BaseAdrenaline { get; } public string WeaponPrefabName { get; } public bool Triggered { get; set; } public OnProjectileHitSourceState(SecondaryAttackDefinition definition, MeleeOnProjectileHitDefinition config, GameObject projectilePrefab, Attack sourceAttack, Character owner, ItemData weapon, ItemData? ammo, float hitNoise, HitData baseHitData, float baseAdrenaline) { Definition = definition; Config = config; ProjectilePrefab = projectilePrefab; SourceAttack = sourceAttack; Owner = owner; Weapon = weapon; Ammo = ammo; HitNoise = hitNoise; BaseHitData = baseHitData; BaseAdrenaline = Mathf.Max(0f, baseAdrenaline); WeaponPrefabName = (((Object)(object)weapon.m_dropPrefab != (Object)null) ? ((Object)weapon.m_dropPrefab).name : definition.PrefabName); } } private readonly struct ImpactBurstTarget { public IDestructible Destructible { get; } public Character? Character { get; } public Collider Collider { get; } public Vector3 Point { get; } public float Distance => Mathf.Sqrt(DistanceSqr); public float DistanceSqr { get; } public ImpactBurstTarget(IDestructible destructible, Character? character, Collider collider, Vector3 point, float distanceSqr) { //IL_0016: 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) Destructible = destructible; Character = character; Collider = collider; Point = point; DistanceSqr = distanceSqr; } } private sealed class SpearRainPendingState { public int Count; } private sealed class SpearRainFollowupProjectileState { } private const int MaxImpactBurstColliders = 96; private const float MinPositiveImpactBurstDamage = 0.1f; private const string SpearRainPresetName = "spearRain"; private const float SpearRainVelocityLeadFactor = 0.75f; private const float SpearRainMaxVelocityLeadDistance = 3f; private static readonly ConditionalWeakTable OnProjectileHitSources = new ConditionalWeakTable(); private static readonly ConditionalWeakTable SpearRainFollowupProjectiles = new ConditionalWeakTable(); private static readonly ConditionalWeakTable PendingSpearRainByOwner = new ConditionalWeakTable(); private static readonly Collider[] ImpactBurstColliders = (Collider[])(object)new Collider[96]; private static readonly List ImpactBurstTargets = new List(); private static readonly HashSet ImpactBurstTargetIds = new HashSet(); private static int _characterMask; private static int _impactBurstMask; internal static bool IsApplyingImpactBurstDamage { get; private set; } internal static void RegisterOnProjectileHitSource(Projectile projectile, Attack attack, ItemData weapon) { if (!((Object)(object)projectile == (Object)null) && attack != null && !((Object)(object)weapon?.m_dropPrefab == (Object)null) && !((Object)(object)attack.m_attackProjectile == (Object)null)) { float num = projectile.m_adrenaline; if (num <= 0f) { num = ((attack.m_attackAdrenaline > 0f) ? attack.m_attackAdrenaline : attack.m_attackUseAdrenaline); } if (SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) && definition.Behavior is CopiedSecondaryBehavior && definition.OnProjectileHit != null) { RegisterOnProjectileHitSource(projectile, definition, definition.OnProjectileHit, attack.m_attackProjectile, attack, (Character)(object)attack.m_character, weapon, attack.m_lastUsedAmmo, projectile.m_hitNoise, num); } } } private static void RegisterOnProjectileHitSource(Projectile projectile, SecondaryAttackDefinition definition, MeleeOnProjectileHitDefinition config, GameObject projectilePrefab, Attack sourceAttack, Character owner, ItemData weapon, ItemData? ammo, float hitNoise, float baseAdrenaline) { HitData originalHitData = projectile.m_originalHitData; HitData val = ((originalHitData != null) ? originalHitData.Clone() : null); if (val != null) { SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, projectile, weapon); OnProjectileHitSources.Remove(projectile); OnProjectileHitSources.Add(projectile, new OnProjectileHitSourceState(definition, config, projectilePrefab, sourceAttack, owner, weapon, ammo, hitNoise, val, baseAdrenaline)); projectile.m_adrenaline = 0f; if (IsSpearRainPreset(config.Preset)) { RegisterPendingSpearRain(projectile, owner); } } } internal static bool HasPendingSpearRain(Character owner) { if ((Object)(object)owner == (Object)null || !PendingSpearRainByOwner.TryGetValue(owner, out SpearRainPendingState value) || value.Count <= 0) { return false; } return true; } internal static void AddPendingSpearRain(Character owner) { if (!((Object)(object)owner == (Object)null)) { PendingSpearRainByOwner.GetValue(owner, (Character _) => new SpearRainPendingState()).Count++; } } internal static void RemovePendingSpearRain(Character owner) { if (!((Object)(object)owner == (Object)null) && PendingSpearRainByOwner.TryGetValue(owner, out SpearRainPendingState value) && value.Count > 0) { value.Count--; } } internal static void TryTrigger(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_0089: 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_008e: 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_00b8: 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_00c0: 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_00ef: 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_00fd: Unknown result type (might be due to invalid IL or missing references) if (water || (Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || !OnProjectileHitSources.TryGetValue(projectile, out OnProjectileHitSourceState value) || value.Triggered) { return; } Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(collider); if ((!value.Config.TriggerOnCharactersOnly || !((Object)(object)hitCharacter == (Object)null)) && (!((Object)(object)hitCharacter != (Object)null) || IsValidTarget(value.Owner, hitCharacter))) { value.Triggered = true; OnProjectileHitSources.Remove(projectile); ReleasePendingSpearRain(projectile); Vector3 val = (((Object)(object)hitCharacter != (Object)null) ? hitCharacter.GetCenterPoint() : hitPoint); GameObject directHitObject = Projectile.FindHitObject(collider); if (value.Config.Preset.Equals("impactBurst", StringComparison.OrdinalIgnoreCase)) { TryGrantOnProjectileHitAdrenaline(value, hitCharacter); TriggerImpactBurst(value, val, ResolveImpactBurstVfxPoint(hitPoint, val), hitCharacter, directHitObject, normal); } else if (MeleePresetCooldownSystem.TryConsume(value.Owner, value.Weapon, "spearRain", value.Config.PresetCooldown)) { val = ResolveSpearRainTargetPoint(value, hitCharacter, val); TryGrantOnProjectileHitAdrenaline(value, hitCharacter); SpawnSpearRain(value, val, hitCharacter); } } } private static void TryGrantOnProjectileHitAdrenaline(OnProjectileHitSourceState state, Character? target) { if (!((Object)(object)target == (Object)null) && !(state.BaseAdrenaline <= 0f) && state.SourceAttack != null && !((Object)(object)state.Owner == (Object)null) && BaseAI.IsEnemy(state.Owner, target)) { SecondaryAttackAdrenalineSystem.TryGrantOnceRaw(state.SourceAttack, target, state.BaseAdrenaline, 1f, "meleeProjectileSource"); } } internal static bool ShouldIgnoreOnProjectileHitSourceHit(Projectile projectile, Collider collider) { if ((Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || !OnProjectileHitSources.TryGetValue(projectile, out OnProjectileHitSourceState value) || value.Triggered || (Object)(object)value.Owner == (Object)null) { return false; } if ((Object)(object)ProjectileRuntimeSystem.GetHitCharacter(collider) != (Object)(object)value.Owner) { return false; } return true; } private static bool IsValidTarget(Character owner, Character target) { if ((Object)(object)owner == (Object)null || (Object)(object)target == (Object)null || (Object)(object)target == (Object)(object)owner || target.IsDead()) { return false; } if (BaseAI.IsEnemy(owner, target)) { return true; } if (owner.IsPlayer() && (Object)(object)target.GetBaseAI() != (Object)null) { return target.GetBaseAI().IsAggravatable(); } return false; } private static void TriggerImpactBurst(OnProjectileHitSourceState state, Vector3 impactPoint, Vector3 vfxPoint, Character? directTarget, GameObject? directHitObject, Vector3 normal) { //IL_0030: 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_005c: 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_0196: 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_0118: 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_011f: 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_0139: Unknown result type (might be due to invalid IL or missing references) MeleeOnProjectileHitDefinition config = state.Config; if (config.Radius <= 0f || (config.DamageFactor <= 0f && config.PushFactor <= 0f)) { return; } PlayImpactBurstVfx(config, vfxPoint, normal); ImpactBurstTargets.Clear(); ImpactBurstTargetIds.Clear(); int num = 0; try { float radiusSqr = config.Radius * config.Radius; num = Physics.OverlapSphereNonAlloc(impactPoint, config.Radius, ImpactBurstColliders, config.IncludeDestructibles ? GetImpactBurstMask() : GetCharacterMask(), (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val = ImpactBurstColliders[i]; if ((Object)(object)val == (Object)null) { continue; } GameObject val2 = Projectile.FindHitObject(val); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)((Component)state.Owner).gameObject)) { Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(val); IDestructible obj; if (!((Object)(object)hitCharacter != (Object)null)) { obj = val2.GetComponent(); } else { IDestructible val3 = (IDestructible)(object)hitCharacter; obj = val3; } IDestructible val4 = obj; if (val4 != null && !ShouldSkipImpactBurstTarget(state, config, directTarget, directHitObject, val2, hitCharacter, val4) && TryAddImpactBurstTarget(val4, hitCharacter, val, val2)) { Vector3 val5 = ResolveImpactPoint(val, impactPoint, val4); float distanceSqr = ResolveImpactBurstDistanceSqr(impactPoint, val5, hitCharacter, val4, radiusSqr); ImpactBurstTargets.Add(new ImpactBurstTarget(val4, hitCharacter, val, val5, distanceSqr)); } } } ImpactBurstTargets.Sort((ImpactBurstTarget left, ImpactBurstTarget right) => left.DistanceSqr.CompareTo(right.DistanceSqr)); foreach (ImpactBurstTarget impactBurstTarget in ImpactBurstTargets) { ApplyImpactBurstHit(state, impactBurstTarget, impactPoint, normal); } } finally { ImpactBurstTargets.Clear(); ImpactBurstTargetIds.Clear(); Array.Clear(ImpactBurstColliders, 0, ImpactBurstColliders.Length); } } private static void PlayImpactBurstVfx(MeleeOnProjectileHitDefinition config, Vector3 vfxPoint, Vector3 normal) { //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_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) string text = config.Vfx?.Trim() ?? ""; if (text.Length != 0) { Quaternion rotation = SecondaryAttackNamedEffectSystem.RotationFromNormal(normal); SecondaryAttackNamedEffectSystem.Create(text, vfxPoint, rotation, "impactBurst.vfx"); } } private static Vector3 ResolveImpactBurstVfxPoint(Vector3 hitPoint, Vector3 fallbackPoint) { //IL_0010: 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 (!(((Vector3)(ref hitPoint)).sqrMagnitude > 0.001f)) { return fallbackPoint; } return hitPoint; } private static bool ShouldSkipImpactBurstTarget(OnProjectileHitSourceState state, MeleeOnProjectileHitDefinition config, Character? directTarget, GameObject? directHitObject, GameObject hitObject, Character? character, IDestructible destructible) { //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_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_00bb: Invalid comparison between Unknown and I4 if (!config.IncludeDirectTarget && (((Object)(object)directTarget != (Object)null && ((Object)(object)character == (Object)(object)directTarget || ((Object)(object)character != (Object)null && (Object)(object)((Component)character).gameObject == (Object)(object)((Component)directTarget).gameObject))) || ((Object)(object)directHitObject != (Object)null && (Object)(object)hitObject == (Object)(object)directHitObject))) { return true; } if ((Object)(object)character != (Object)null) { if (IsValidTarget(state.Owner, character)) { return false; } return true; } if (!config.IncludeDestructibles) { return true; } ItemData weapon = state.Weapon; if (weapon != null && weapon.m_shared?.m_tamedOnly == true) { return true; } DestructibleType destructibleType = destructible.GetDestructibleType(); if ((int)destructibleType == 0) { return true; } if ((int)destructibleType == 4) { return true; } return false; } private static void ApplyImpactBurstHit(OnProjectileHitSourceState state, ImpactBurstTarget target, Vector3 impactPoint, Vector3 normal) { //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_0101: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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) HitData val = state.BaseHitData.Clone(); SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, null, state.Weapon); float damageFactor = state.Config.DamageFactor; if (!Mathf.Approximately(damageFactor, 1f)) { ((DamageTypes)(ref val.m_damage)).Modify(damageFactor); } float totalDamage = ((DamageTypes)(ref val.m_damage)).GetTotalDamage(); if (totalDamage > 0f && totalDamage < 0.1f) { ((DamageTypes)(ref val.m_damage)).Modify(0.1f / totalDamage); } if (((DamageTypes)(ref val.m_damage)).GetTotalDamage() <= 0f && state.Config.PushFactor <= 0f) { return; } if (!((Object)(object)target.Character != (Object)null) || !val.m_dodgeable || !target.Character.IsDodgeInvincible()) { val.m_pushForce *= state.Config.PushFactor; val.m_skillRaiseAmount = 0f; val.m_point = target.Point; val.m_dir = ResolveImpactDirection(impactPoint, ((Object)(object)target.Character != (Object)null) ? target.Character.GetCenterPoint() : target.Point, normal, state.Owner); val.m_hitCollider = target.Collider; val.SetAttacker(state.Owner); bool isApplyingImpactBurstDamage = IsApplyingImpactBurstDamage; IsApplyingImpactBurstDamage = true; try { target.Destructible.Damage(val); if ((Object)(object)target.Character != (Object)null && BaseAI.IsEnemy(state.Owner, target.Character)) { TryGrantOnProjectileHitAdrenaline(state, target.Character); } return; } finally { IsApplyingImpactBurstDamage = isApplyingImpactBurstDamage; } } Character? character = target.Character; Player val2 = (Player)(object)((character is Player) ? character : null); if (val2 != null) { val2.HitWhileDodging(); } } private static bool TryAddImpactBurstTarget(IDestructible destructible, Character? character, Collider collider, GameObject hitObject) { int item; if ((Object)(object)character != (Object)null) { item = ((Object)((Component)character).gameObject).GetInstanceID(); } else { Component val = (Component)(object)((destructible is Component) ? destructible : null); item = ((val != null) ? ((Object)val.gameObject).GetInstanceID() : ((!((Object)(object)hitObject != (Object)null)) ? ((Object)collider).GetInstanceID() : ((Object)hitObject).GetInstanceID())); } return ImpactBurstTargetIds.Add(item); } private static Vector3 ResolveImpactPoint(Collider collider, Vector3 impactPoint, IDestructible destructible) { //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_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) //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) //IL_003f: 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_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_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) Vector3 val = SecondaryAttackManager.ResolveSafeClosestPoint(collider, impactPoint); Vector3 val2 = val - impactPoint; if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { Character val3 = (Character)(object)((destructible is Character) ? destructible : null); Vector3 val4; if (val3 == null) { Bounds bounds = collider.bounds; val4 = ((Bounds)(ref bounds)).center; } else { val4 = val3.GetCenterPoint(); } val = val4; } return val; } private static float ResolveImpactBurstDistanceSqr(Vector3 impactPoint, Vector3 targetPoint, Character? character, IDestructible destructible, float radiusSqr) { //IL_0020: 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_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_0031: 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_000d: 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_005f: 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) //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) if ((Object)(object)character != (Object)null) { Vector3 val = targetPoint - impactPoint; return Mathf.Min(radiusSqr, ((Vector3)(ref val)).sqrMagnitude); } Vector3 val2 = Vector3.ProjectOnPlane(targetPoint - impactPoint, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { return Mathf.Min(radiusSqr, ((Vector3)(ref val2)).sqrMagnitude); } Component val3 = (Component)(object)((destructible is Component) ? destructible : null); if (val3 != null) { Vector3 val4 = Vector3.ProjectOnPlane(val3.transform.position - impactPoint, Vector3.up); return Mathf.Min(radiusSqr, ((Vector3)(ref val4)).sqrMagnitude); } return 0f; } private static Vector3 ResolveImpactDirection(Vector3 impactPoint, Vector3 targetPoint, Vector3 normal, Character owner) { //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_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_0020: 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_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_0030: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_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) Vector3 val = Vector3.ProjectOnPlane(targetPoint - impactPoint, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(-normal, Vector3.up); } if (((Vector3)(ref val)).sqrMagnitude < 0.001f && (Object)(object)owner != (Object)null) { val = Vector3.ProjectOnPlane(((Component)owner).transform.forward, Vector3.up); } if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref val)).normalized; } private static int GetCharacterMask() { if (_characterMask == 0) { _characterMask = LayerMask.GetMask(new string[5] { "character", "character_net", "character_ghost", "hitbox", "character_noenv" }); } return _characterMask; } private static int GetImpactBurstMask() { if (_impactBurstMask == 0) { _impactBurstMask = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _impactBurstMask; } private static Vector3 ResolveSpearRainTargetPoint(OnProjectileHitSourceState state, Character? target, Vector3 fallbackPoint) { //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_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_0009: 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_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_0027: 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_0082: 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_0075: 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) if ((Object)(object)target == (Object)null) { return fallbackPoint; } Vector3 centerPoint = target.GetCenterPoint(); Vector3 val = ResolveCharacterHorizontalVelocity(target); if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { return centerPoint; } Vector3 val2 = val * Mathf.Max(0.1f, state.Config.FlightTime) * 0.75f; float num = Mathf.Max(0f, 3f); if (num > 0f && ((Vector3)(ref val2)).sqrMagnitude > num * num) { val2 = ((Vector3)(ref val2)).normalized * num; } return centerPoint + val2; } private static Vector3 ResolveCharacterHorizontalVelocity(Character target) { //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_0026: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = (((Object)(object)target.m_body != (Object)null) ? target.m_body : ((Component)target).GetComponent()); if ((Object)(object)val == (Object)null) { return Vector3.zero; } return Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up); } private static void SpawnSpearRain(OnProjectileHitSourceState state, Vector3 targetPoint, Character? markedTarget) { //IL_0067: 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_007e: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00b8: 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_00d5: 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_00da: 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) Projectile component = state.ProjectilePrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return; } float gravity = component.m_gravity; SpearRainTargetMarker targetMarker = CreateSpearRainTargetMarker(markedTarget, state.Config.FlightTime); CopiedThrowProjectileVisualSystem.SpawnedProjectileVisualContext visualContext = ((state.Definition.Behavior is CopiedSecondaryBehavior) ? CopiedThrowProjectileVisualSystem.CreateSpawnedProjectileVisualContext(state.Weapon, state.ProjectilePrefab) : default(CopiedThrowProjectileVisualSystem.SpawnedProjectileVisualContext)); for (int i = 0; i < state.Config.Count; i++) { Vector2 val = Random.insideUnitCircle * state.Config.SpawnRadius; Vector3 spawnPoint = targetPoint + new Vector3(val.x, state.Config.SpawnHeight, val.y); Vector3 launchVelocity = CalculateBallisticVelocity(spawnPoint, targetPoint, gravity, state.Config.FlightTime); if (((Vector3)(ref launchVelocity)).sqrMagnitude < 0.001f) { launchVelocity = Vector3.down; } SpawnFollowupProjectile(state, spawnPoint, launchVelocity, targetMarker, targetPoint, gravity, visualContext); } } private static void SpawnFollowupProjectile(OnProjectileHitSourceState state, Vector3 spawnPoint, Vector3 launchVelocity, SpearRainTargetMarker? targetMarker, Vector3 fallbackTargetPoint, float gravity, CopiedThrowProjectileVisualSystem.SpawnedProjectileVisualContext visualContext) { //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_0013: 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_0057: 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_00b1: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.LookRotation(((Vector3)(ref launchVelocity)).normalized); GameObject val2 = Object.Instantiate(state.ProjectilePrefab, spawnPoint, val); Projectile component = val2.GetComponent(); IProjectile component2 = val2.GetComponent(); if ((Object)(object)component == (Object)null || component2 == null) { ProjectileRuntimeSystem.DestroyProjectileObject(val2); return; } RegisterSpearRainFollowupProjectile(component); SuppressProjectileItemDrops(component); HitData val3 = BuildFollowupHitData(state); component2.Setup(state.Owner, launchVelocity, state.HitNoise, val3, state.Weapon, state.Ammo); component.m_adrenaline = 0f; SuppressProjectileItemDrops(component); if ((Object)(object)targetMarker != (Object)null) { (((Component)component).GetComponent() ?? ((Component)component).gameObject.AddComponent()).Configure(component, targetMarker, fallbackTargetPoint, state.Config.FlightTime, gravity, launchVelocity); } if (visualContext.Active) { CopiedThrowProjectileVisualSystem.ApplyCurrentWeaponVisualForSpawnedProjectile(component, visualContext); } SecondaryAttackRuntimeFacade.SetProjectileAttackAttribution(component, state.WeaponPrefabName, secondaryAttack: true, state.Definition, disableCurrentAttackFallback: false); } internal static void DestroySpearRainFollowupAfterHit(Projectile? projectile) { if (!((Object)(object)projectile == (Object)null) && SpearRainFollowupProjectiles.TryGetValue(projectile, out SpearRainFollowupProjectileState _)) { SpearRainFollowupProjectiles.Remove(projectile); ProjectileRuntimeSystem.DestroyProjectileObject(((Component)projectile).gameObject); } } private static void RegisterSpearRainFollowupProjectile(Projectile projectile) { SpearRainFollowupProjectiles.Remove(projectile); SpearRainFollowupProjectiles.Add(projectile, new SpearRainFollowupProjectileState()); } private static HitData BuildFollowupHitData(OnProjectileHitSourceState state) { HitData val = state.BaseHitData.Clone(); SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, null, state.Weapon); if (!Mathf.Approximately(state.Config.DamageFactor, 1f)) { ((DamageTypes)(ref val.m_damage)).Modify(state.Config.DamageFactor); } val.m_skillRaiseAmount = 0f; val.SetAttacker(state.Owner); return val; } private static void SuppressProjectileItemDrops(Projectile projectile) { projectile.m_respawnItemOnHit = false; projectile.m_spawnItem = null; projectile.m_spawnOnTtl = false; } private static bool IsSpearRainPreset(string preset) { return preset.Equals("spearRain", StringComparison.OrdinalIgnoreCase); } private static void RegisterPendingSpearRain(Projectile projectile, Character owner) { if (!((Object)(object)projectile == (Object)null) && !((Object)(object)owner == (Object)null)) { (((Component)projectile).GetComponent() ?? ((Component)projectile).gameObject.AddComponent()).Initialize(owner); } } private static void ReleasePendingSpearRain(Projectile projectile) { if (!((Object)(object)projectile == (Object)null)) { ((Component)projectile).GetComponent()?.Release(); } } private static SpearRainTargetMarker? CreateSpearRainTargetMarker(Character? target, float flightTime) { if ((Object)(object)target == (Object)null) { return null; } SpearRainTargetMarker obj = ((Component)target).GetComponent() ?? ((Component)target).gameObject.AddComponent(); obj.Configure(target, Mathf.Max(0.1f, flightTime) + 0.5f); return obj; } internal static Vector3 CalculateBallisticVelocity(Vector3 spawnPoint, Vector3 targetPoint, float gravity, float flightTime, float minimumFlightTime = 0.1f) { //IL_000a: 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) //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_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_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_0032: Unknown result type (might be due to invalid IL or missing references) flightTime = Mathf.Max(minimumFlightTime, flightTime); Vector3 val = Vector3.down * gravity; return (targetPoint - spawnPoint - val * (0.5f * flightTime * flightTime)) / flightTime; } } internal sealed class SpearRainPendingProjectileMarker : MonoBehaviour { private Character? _owner; private bool _active; internal void Initialize(Character owner) { Release(); _owner = owner; _active = true; MeleeProjectileHitCascadeSystem.AddPendingSpearRain(owner); } internal void Release() { if (_active) { _active = false; Character owner = _owner; _owner = null; if ((Object)(object)owner != (Object)null) { MeleeProjectileHitCascadeSystem.RemovePendingSpearRain(owner); } } } private void OnDestroy() { Release(); } } internal sealed class SpearRainTargetMarker : MonoBehaviour { private Character? _target; private Vector3 _lastKnownPoint; private float _expiresAt; internal bool IsValid => Time.time <= _expiresAt; internal Vector3 CurrentPoint => _lastKnownPoint; internal void Configure(Character target, float lifetime) { //IL_0009: 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) _target = target; _lastKnownPoint = target.GetCenterPoint(); _expiresAt = Time.time + Mathf.Max(0.1f, lifetime); } private void Update() { //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) if ((Object)(object)_target != (Object)null && !_target.IsDead()) { _lastKnownPoint = _target.GetCenterPoint(); } if (!IsValid) { Object.Destroy((Object)(object)this); } } } internal sealed class SpearRainGuidedProjectileController : MonoBehaviour { private const float MinRemainingFlightTime = 0.05f; private const float MaxSpeedFactor = 1.75f; private const float MaxSpeedBonus = 8f; private Projectile? _projectile; private ZNetView? _nview; private SpearRainTargetMarker? _targetMarker; private Vector3 _fallbackTargetPoint; private float _flightTime; private float _gravity; private float _elapsed; private float _maxSpeed; private bool _active; internal void Configure(Projectile projectile, SpearRainTargetMarker targetMarker, Vector3 fallbackTargetPoint, float flightTime, float gravity, Vector3 initialVelocity) { //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) _projectile = projectile; _nview = ((Component)projectile).GetComponent(); _targetMarker = targetMarker; _fallbackTargetPoint = fallbackTargetPoint; _flightTime = Mathf.Max(0.1f, flightTime); _gravity = gravity; _elapsed = 0f; _maxSpeed = Mathf.Max(((Vector3)(ref initialVelocity)).magnitude * 1.75f, ((Vector3)(ref initialVelocity)).magnitude + 8f); _active = true; projectile.m_ttl = Mathf.Max(projectile.m_ttl, _flightTime + 0.5f); } private void FixedUpdate() { //IL_0084: 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_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_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_0091: 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_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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) if (!_active || (Object)(object)_projectile == (Object)null || ((Object)(object)_nview != (Object)null && _nview.IsValid() && !_nview.IsOwner())) { return; } _elapsed += Time.fixedDeltaTime; if (_elapsed >= _flightTime) { _active = false; return; } Vector3 targetPoint = (((Object)(object)_targetMarker != (Object)null && _targetMarker.IsValid) ? _targetMarker.CurrentPoint : _fallbackTargetPoint); float flightTime = Mathf.Max(0.05f, _flightTime - _elapsed); Vector3 velocity = MeleeProjectileHitCascadeSystem.CalculateBallisticVelocity(((Component)this).transform.position, targetPoint, _gravity, flightTime, 0.05f); if (!(((Vector3)(ref velocity)).sqrMagnitude < 0.001f)) { float magnitude = ((Vector3)(ref velocity)).magnitude; if (_maxSpeed > 0f && magnitude > _maxSpeed) { velocity = ((Vector3)(ref velocity)).normalized * _maxSpeed; } ProjectileAccess.SetVelocity(_projectile, velocity); } } } internal static class MeleeBoomerangProjectileSystem { private readonly struct PendingReturnAutoEquip { public Humanoid Owner { get; } public ItemData Item { get; } public int DueFrame { get; } public PendingReturnAutoEquip(Humanoid owner, ItemData item, int dueFrame) { Owner = owner; Item = item; DueFrame = dueFrame; } } internal const int ReturnAutoEquipDelayFrames = 1; private static readonly List PendingReturnAutoEquips = new List(); internal static void TryApplyToProjectileSetup(Projectile projectile, Attack attack, ItemData weapon) { if (!((Object)(object)projectile == (Object)null) && attack != null && !((Object)(object)weapon?.m_dropPrefab == (Object)null) && SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) && definition.Behavior is CopiedSecondaryBehavior && definition.Boomerang != null) { (((Component)projectile).GetComponent() ?? ((Component)projectile).gameObject.AddComponent()).Configure(projectile, attack, definition.Boomerang); } } internal static void UpdateDeferredReturnAutoEquips(Player player) { if ((Object)(object)player == (Object)null || PendingReturnAutoEquips.Count == 0) { return; } for (int num = PendingReturnAutoEquips.Count - 1; num >= 0; num--) { PendingReturnAutoEquip pendingReturnAutoEquip = PendingReturnAutoEquips[num]; if ((Object)(object)pendingReturnAutoEquip.Owner == (Object)null || pendingReturnAutoEquip.Item == null) { PendingReturnAutoEquips.RemoveAt(num); } else if (!((Object)(object)pendingReturnAutoEquip.Owner != (Object)(object)player) && Time.frameCount >= pendingReturnAutoEquip.DueFrame) { PendingReturnAutoEquips.RemoveAt(num); if (!pendingReturnAutoEquip.Item.m_equipped) { pendingReturnAutoEquip.Owner.EquipItem(pendingReturnAutoEquip.Item, true); } } } } internal static void QueueReturnAutoEquip(Humanoid owner, ItemData item) { int dueFrame = Time.frameCount + 1; for (int num = PendingReturnAutoEquips.Count - 1; num >= 0; num--) { PendingReturnAutoEquip pendingReturnAutoEquip = PendingReturnAutoEquips[num]; if ((Object)(object)pendingReturnAutoEquip.Owner == (Object)(object)owner && pendingReturnAutoEquip.Item == item) { PendingReturnAutoEquips.RemoveAt(num); } } PendingReturnAutoEquips.Add(new PendingReturnAutoEquip(owner, item, dueFrame)); } internal static bool TryHandleProjectileHit(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_001e: 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) BoomerangProjectileController boomerangProjectileController = (((Object)(object)projectile != (Object)null) ? ((Component)projectile).GetComponent() : null); if ((Object)(object)boomerangProjectileController != (Object)null) { return boomerangProjectileController.TryHandleHit(collider, hitPoint, water, normal); } return false; } } internal sealed class BoomerangProjectileController : MonoBehaviour { private const float TwoPi = (float)Math.PI * 2f; private const float ProjectileSpeed = 20f; private const float HitCooldown = 0.25f; private const int MaxReachRaycastHits = 128; private static readonly RaycastHit[] s_reachRaycastHits = (RaycastHit[])(object)new RaycastHit[128]; private static int s_rayMaskSolids; private readonly Dictionary _characterHitTimes = new Dictionary(); private readonly Dictionary _destructibleHitTimes = new Dictionary(); private Projectile? _projectile; private Attack? _sourceAttack; private BoomerangDefinition? _definition; private ZNetView? _nview; private Character? _owner; private Vector3 _origin; private Vector3 _axis; private Vector3 _side; private Vector3 _center; private float _longRadius; private float _shortRadius; private float _flightTime; private float _despawnDistance; private float _despawnDistanceSqr; private float _catchRadius; private float _catchRadiusSqr; private float _catchDelay; private float _baseAdrenaline; private float _elapsed; private float _originalGravity; private int _hitCount; private bool _autoEquipOnCatch; private bool _complete; internal void Configure(Projectile projectile, Attack sourceAttack, BoomerangDefinition definition) { //IL_003e: 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_010d: 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_0130: 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_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0163: 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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01df: 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_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: 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_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0225: 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_022f: Unknown result type (might be due to invalid IL or missing references) _projectile = projectile; _sourceAttack = sourceAttack; _definition = definition; _nview = ((Component)projectile).GetComponent(); _owner = (Character?)(((object)ProjectileAccess.GetOwner(projectile)) ?? ((object)sourceAttack.m_character)); _origin = ((Component)projectile).transform.position; _despawnDistance = Mathf.Max(0.1f, definition.DespawnDistance); _despawnDistanceSqr = _despawnDistance * _despawnDistance; _catchRadius = Mathf.Max(0f, definition.CatchRadius); _catchRadiusSqr = _catchRadius * _catchRadius; _catchDelay = Mathf.Max(0f, definition.CatchDelay); _autoEquipOnCatch = definition.AutoEquipOnCatch; _baseAdrenaline = Mathf.Max(0f, projectile.m_adrenaline); projectile.m_adrenaline = 0f; _elapsed = 0f; _hitCount = 0; _complete = false; _characterHitTimes.Clear(); _destructibleHitTimes.Clear(); Vector3 velocity = projectile.GetVelocity(); Vector3 val = ((((Vector3)(ref velocity)).sqrMagnitude > 0.001f) ? ((Vector3)(ref velocity)).normalized : ((Component)projectile).transform.forward); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); float maxDistance = Mathf.Max(0.5f, definition.MaxDistance); float num = ResolveMaxReach(projectile, val, maxDistance); _longRadius = Mathf.Max(0.1f, num * 0.5f); _shortRadius = Mathf.Max(0f, _longRadius * Mathf.Max(0f, definition.CurveFactor)); _flightTime = Mathf.Max(0.1f, EstimateEllipseCircumference(_longRadius, _shortRadius) / 20f); _axis = val; _center = _origin + _axis * _longRadius; _side = ResolveSideAxis(((Component)projectile).transform, _axis); if (definition.Side.Equals("left", StringComparison.OrdinalIgnoreCase)) { _side = -_side; } _originalGravity = projectile.m_gravity; projectile.m_gravity = 0f; projectile.m_ttl = Mathf.Max(projectile.m_ttl, _flightTime + 1f); } internal bool TryHandleHit(Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_00fa: 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) if (_complete || (Object)(object)_projectile == (Object)null || _definition == null || water || (Object)(object)collider == (Object)null) { return false; } Character val = ResolveOwner(); GameObject val2 = Projectile.FindHitObject(collider); if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)((Component)_projectile).gameObject || ((Component)collider).transform.IsChildOf(((Component)_projectile).transform)) { return true; } if ((Object)(object)val != (Object)null && (Object)(object)val2 == (Object)(object)((Component)val).gameObject) { if (_elapsed >= _catchDelay) { Humanoid val3 = (Humanoid)(object)((val is Humanoid) ? val : null); if (val3 != null) { TryReturnItemToOwner(val3); } } return true; } Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(collider); IDestructible obj; if (!((Object)(object)hitCharacter != (Object)null)) { obj = val2.GetComponent(); } else { IDestructible val4 = (IDestructible)(object)hitCharacter; obj = val4; } IDestructible val5 = obj; if (val5 == null || !IsValidTarget(val, hitCharacter, val5, _definition)) { return false; } if (IsOnHitCooldown(hitCharacter, collider, 0.25f)) { return true; } if (!ApplyHit(val, hitCharacter, val5, collider, hitPoint, normal, _definition)) { return false; } RegisterHitCooldown(hitCharacter, collider); _hitCount++; return true; } private void FixedUpdate() { //IL_0079: 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_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_00d1: 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_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) if (_complete || (Object)(object)_projectile == (Object)null || ((Object)(object)_nview != (Object)null && _nview.IsValid() && !_nview.IsOwner())) { return; } _elapsed += Time.fixedDeltaTime; Character owner = ResolveOwner(); if (TryCatchByOwner(owner)) { return; } float num = Mathf.Clamp01(_elapsed / _flightTime); Vector3 velocity = (EvaluateGuidedEllipsePoint(num, owner) - ((Component)this).transform.position) / Mathf.Max(0.001f, Time.fixedDeltaTime); ProjectileAccess.SetVelocity(_projectile, velocity); if (!(num >= 1f)) { if (!(_elapsed > _flightTime * 0.5f)) { return; } Vector3 val = ((Component)this).transform.position - ResolveReturnEndpoint(owner); if (!(((Vector3)(ref val)).sqrMagnitude <= _despawnDistanceSqr)) { return; } } CompleteReturnAtCurrentPosition(); } private bool TryCatchByOwner(Character? owner) { //IL_0045: 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_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) if ((Object)(object)_projectile == (Object)null || _catchRadius <= 0f || _elapsed < _catchDelay) { return false; } Humanoid val = (Humanoid)(object)((owner is Humanoid) ? owner : null); if (val == null || owner.IsDead()) { return false; } Vector3 val2 = ((Component)this).transform.position - owner.GetCenterPoint(); if (((Vector3)(ref val2)).sqrMagnitude > _catchRadiusSqr) { return false; } return TryReturnItemToOwner(val); } private bool ApplyHit(Character? owner, Character? character, IDestructible destructible, Collider collider, Vector3 hitPoint, Vector3 normal, BoomerangDefinition definition) { //IL_00e0: 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_00ea: 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_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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) HitData? originalHitData = ProjectileAccess.GetOriginalHitData(_projectile); HitData val = ((originalHitData != null) ? originalHitData.Clone() : null); if (val == null) { return false; } SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, _projectile, ProjectileAccess.GetWeapon(_projectile)); float num = Mathf.Pow(1f - definition.HitDamageDecay, (float)_hitCount); float num2 = definition.DamageFactor * num; if (!Mathf.Approximately(num2, 1f)) { ((DamageTypes)(ref val.m_damage)).Modify(num2); } if (((DamageTypes)(ref val.m_damage)).GetTotalDamage() <= 0f && definition.PushFactor <= 0f) { return false; } if ((Object)(object)character != (Object)null && val.m_dodgeable && character.IsDodgeInvincible()) { Player val2 = (Player)(object)((character is Player) ? character : null); if (val2 != null) { val2.HitWhileDodging(); } return true; } val.m_pushForce *= definition.PushFactor * num; val.m_skillRaiseAmount = 0f; val.m_point = hitPoint; val.m_dir = ResolveHitDirection(owner, character, hitPoint, normal); val.m_hitCollider = collider; if ((Object)(object)owner != (Object)null) { val.SetAttacker(owner); } destructible.Damage(val); if ((Object)(object)owner != (Object)null && (Object)(object)character != (Object)null && BaseAI.IsEnemy(owner, character) && _sourceAttack != null && _baseAdrenaline > 0f && character.m_enemyAdrenalineMultiplier > 0f) { SecondaryAttackAdrenalineSystem.TryGrantOnceRaw(_sourceAttack, character, _baseAdrenaline, 1f, "boomerang"); } PlayHitEffects(owner, hitPoint, normal); return true; } private void PlayHitEffects(Character? owner, Vector3 hitPoint, Vector3 normal) { //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) //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) if (!((Object)(object)_projectile == (Object)null)) { Quaternion val = SecondaryAttackNamedEffectSystem.RotationFromNormal(normal); _projectile.m_hitEffects.Create(hitPoint, val, (Transform)null, 1f, -1); if ((Object)(object)owner != (Object)null && _projectile.m_hitNoise > 0f) { owner.AddNoise(_projectile.m_hitNoise); } } } private bool IsOnHitCooldown(Character? character, Collider collider, float cooldown) { if (cooldown <= 0f) { return false; } float time = Time.time; if ((Object)(object)character != (Object)null) { if (_characterHitTimes.TryGetValue(character, out var value)) { return time - value < cooldown; } return false; } if (_destructibleHitTimes.TryGetValue(collider, out var value2)) { return time - value2 < cooldown; } return false; } private void RegisterHitCooldown(Character? character, Collider collider) { float time = Time.time; if ((Object)(object)character != (Object)null) { _characterHitTimes[character] = time; } else { _destructibleHitTimes[collider] = time; } } private static bool IsValidTarget(Character? owner, Character? character, IDestructible destructible, BoomerangDefinition definition) { //IL_005f: 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) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 if ((Object)(object)character != (Object)null) { if ((Object)(object)owner == (Object)null || (Object)(object)character == (Object)(object)owner || character.IsDead()) { return false; } if (BaseAI.IsEnemy(owner, character)) { return true; } if (owner.IsPlayer() && (Object)(object)character.GetBaseAI() != (Object)null) { return character.GetBaseAI().IsAggravatable(); } return false; } if (!definition.IncludeDestructibles) { return false; } DestructibleType destructibleType = destructible.GetDestructibleType(); if ((int)destructibleType != 0) { return (int)destructibleType != 4; } return false; } private static Vector3 ResolveHitDirection(Character? owner, Character? character, Vector3 hitPoint, Vector3 normal) { //IL_0058: 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_0016: 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_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_0026: 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 ((Object)(object)owner != (Object)null) { Vector3 val = (((Object)(object)character != (Object)null) ? character.GetCenterPoint() : hitPoint) - owner.GetCenterPoint(); if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { return ((Vector3)(ref val)).normalized; } } if (((Vector3)(ref normal)).sqrMagnitude > 0.001f) { return -((Vector3)(ref normal)).normalized; } return Vector3.forward; } private Vector3 EvaluateEllipsePoint(float t) { //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) //IL_0021: 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) //IL_003e: 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) float num = t * ((float)Math.PI * 2f); return _center - _axis * (_longRadius * Mathf.Cos(num)) + _side * (_shortRadius * Mathf.Sin(num)); } private Vector3 EvaluateGuidedEllipsePoint(float t, Character? owner) { //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_001e: 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_0055: 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_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_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) Vector3 val = EvaluateEllipsePoint(t); if (t <= 0.5f || (Object)(object)_projectile == (Object)null) { return val; } if ((Object)(object)owner == (Object)null || owner.IsDead()) { return val; } float num = Mathf.InverseLerp(0.5f, 1f, t); float num2 = Mathf.SmoothStep(0f, 1f, num); return val + (owner.GetCenterPoint() - _origin) * num2; } private Vector3 ResolveReturnEndpoint(Character? owner) { //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) if (!((Object)(object)owner != (Object)null) || owner.IsDead()) { return _origin; } return owner.GetCenterPoint(); } private static float EstimateEllipseCircumference(float longRadius, float shortRadius) { if (shortRadius <= 0f) { return longRadius * 4f; } float num = Mathf.Max(longRadius, shortRadius); float num2 = Mathf.Min(longRadius, shortRadius); return (float)Math.PI * (3f * (num + num2) - Mathf.Sqrt((3f * num + num2) * (num + 3f * num2))); } private static float ResolveMaxReach(Projectile projectile, Vector3 aimDirection, float maxDistance) { //IL_000d: 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_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) EnsureRayMask(); int num = 0; try { num = Physics.RaycastNonAlloc(((Component)projectile).transform.position, aimDirection, s_reachRaycastHits, maxDistance, s_rayMaskSolids, (QueryTriggerInteraction)1); if (num == 0) { return maxDistance; } Character owner = ProjectileAccess.GetOwner(projectile); float num2 = maxDistance; bool flag = false; for (int i = 0; i < num; i++) { RaycastHit val = s_reachRaycastHits[i]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).gameObject == (Object)(object)((Component)projectile).gameObject) && !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(((Component)projectile).transform)) { GameObject val2 = Projectile.FindHitObject(((RaycastHit)(ref val)).collider); if (!((Object)(object)val2 == (Object)(object)((Component)projectile).gameObject) && (!((Object)(object)owner != (Object)null) || !((Object)(object)val2 == (Object)(object)((Component)owner).gameObject)) && ((RaycastHit)(ref val)).distance < num2) { num2 = ((RaycastHit)(ref val)).distance; flag = true; } } } return flag ? Mathf.Clamp(num2, 0.5f, maxDistance) : maxDistance; } finally { Array.Clear(s_reachRaycastHits, 0, s_reachRaycastHits.Length); } } private Character? ResolveOwner() { if ((Object)(object)_owner != (Object)null) { return _owner; } _owner = (((Object)(object)_projectile != (Object)null) ? ProjectileAccess.GetOwner(_projectile) : null); if ((Object)(object)_owner == (Object)null) { _owner = (Character?)(object)_sourceAttack?.m_character; } return _owner; } private static Vector3 ResolveSideAxis(Transform source, Vector3 axis) { //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_000c: 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_0021: 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_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_0040: 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_004f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(source.right, axis); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.Cross(Vector3.up, axis); } if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.Cross(Vector3.right, axis); } if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.right; } return ((Vector3)(ref val)).normalized; } private static void EnsureRayMask() { if (s_rayMaskSolids == 0) { s_rayMaskSolids = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } } private bool TryReturnItemToOwner(Humanoid owner) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_projectile == (Object)null) { return false; } ItemData spawnItem = _projectile.m_spawnItem; Inventory inventory = owner.GetInventory(); bool flag = spawnItem != null && inventory != null && inventory.CanAddItem(spawnItem, -1); if (spawnItem == null || inventory == null || !flag) { CompleteReturnAtCurrentPosition(); return true; } spawnItem.m_equipped = false; if (!inventory.AddItem(spawnItem)) { CompleteReturnAtCurrentPosition(); return true; } _complete = true; _projectile.m_respawnItemOnHit = false; _projectile.m_spawnItem = null; _projectile.m_spawnOnTtl = false; _projectile.m_gravity = _originalGravity; ProjectileAccess.SetVelocity(_projectile, Vector3.zero); if (_autoEquipOnCatch) { MeleeBoomerangProjectileSystem.QueueReturnAutoEquip(owner, spawnItem); } ProjectileRuntimeSystem.DestroyProjectileObject(((Component)_projectile).gameObject); ((Behaviour)this).enabled = false; return true; } private void CompleteReturnAtCurrentPosition() { //IL_0029: 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) _complete = true; if (!((Object)(object)_projectile == (Object)null)) { Projectile? projectile = _projectile; projectile.m_gravity = _originalGravity; ProjectileAccess.SetVelocity(projectile, Vector3.zero); projectile.OnHit((Collider)null, ((Component)this).transform.position, false, Vector3.up); ((Behaviour)this).enabled = false; } } } internal static class RangedSecondaryCooldownSystem { private const string FallbackIconPrefabName = "Bow"; internal static bool CanStart(Humanoid humanoid, ItemData? weapon) { if ((Object)(object)humanoid == (Object)null || weapon == null || !SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) || !(definition.Behavior is ProjectileSecondaryBehavior behavior)) { return true; } return CanUse((Character)(object)humanoid, behavior); } internal static bool CanUse(Attack attack, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attack?.m_character == (Object)null) { return true; } return CanUse((Character)(object)attack.m_character, behavior); } internal static bool StartCooldown(Attack attack, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attack?.m_character == (Object)null) { return true; } return StartCooldown((Character)(object)attack.m_character, attack.m_weapon, behavior); } private static bool CanUse(Character attacker, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attacker == (Object)null) { return true; } if (behavior == null) { return true; } string cooldownGroup = SecondaryAttackCooldownGroupResolver.ResolveRangedGroup(behavior); return SecondaryCooldownGroupSystem.IsReady(attacker, cooldownGroup); } private static bool StartCooldown(Character attacker, ItemData? weapon, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attacker == (Object)null) { return true; } if (behavior == null) { return true; } string cooldownGroup = SecondaryAttackCooldownGroupResolver.ResolveRangedGroup(behavior); float num = Mathf.Max(0f, behavior.Cooldown); float num2 = Mathf.Clamp01(ResolveCooldownSkillLevel(attacker, weapon) / 100f) * Mathf.Clamp01(behavior.CooldownReductionFactor); float duration = Mathf.Max(0f, num * (1f - num2)); Sprite icon = ResolveWeaponIcon(weapon) ?? ResolveRegisteredIcon(); return SecondaryCooldownGroupSystem.TryConsume(attacker, cooldownGroup, duration, icon); } private static float ResolveCooldownSkillLevel(Character attacker, ItemData? weapon) { //IL_0014: 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) if (weapon?.m_shared == null || (int)weapon.m_shared.m_skillType == 0) { return 0f; } return Mathf.Clamp(attacker.GetSkillLevel(weapon.m_shared.m_skillType), 0f, 100f); } private static Sprite? ResolveWeaponIcon(ItemData? weapon) { Sprite[] array = weapon?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveIcon(ObjectDB objectDb, string itemPrefabName) { GameObject itemPrefab = objectDb.GetItemPrefab(itemPrefabName); Sprite[] array = ((itemPrefab != null) ? itemPrefab.GetComponent() : null)?.m_itemData?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveRegisteredIcon() { if (!((Object)(object)ObjectDB.instance != (Object)null)) { return null; } return ResolveIcon(ObjectDB.instance, "Bow"); } } internal static class SecondaryCooldownHudSystem { internal enum FillMode { Cooldown, Charge } internal readonly struct Entry { internal readonly Sprite? Icon; internal readonly float Remaining; internal readonly float Duration; internal readonly string Badge; internal readonly FillMode Mode; internal readonly float Fraction; internal readonly int SortOrder; public Entry(Sprite? icon, float remaining, float duration, string badge = "", FillMode fillMode = FillMode.Cooldown) { Icon = icon; Remaining = Mathf.Max(0f, remaining); Duration = Mathf.Max(remaining, duration); Badge = badge ?? ""; Mode = fillMode; Fraction = ((duration > 0.001f) ? Mathf.Clamp01(remaining / duration) : 0f); SortOrder = ((fillMode != FillMode.Charge) ? 1 : 0); } } private sealed class CooldownSlot { private FillMode _textMode; private Sprite? _lastIcon; private string _lastBadge; private float _lastFillAmount; private int _lastTextValue; private bool _hasEntry; private bool _emptyApplied; private bool _lastIconEnabled; private bool _lastOverlayEnabled; private bool _lastTextUsesMinutes; private bool _lastBadgeActive; internal GameObject Root { get; } internal bool IsValid { get { if ((Object)(object)Root != (Object)null && (Object)(object)P != (Object)null && (Object)(object)P != (Object)null && (Object)(object)P != (Object)null && (Object)(object)P != (Object)null) { return (Object)(object)P != (Object)null; } return false; } } public CooldownSlot(GameObject root, Image background, Image icon, Image overlay, TextMeshProUGUI text, TextMeshProUGUI badge) { P = background; P = icon; P = overlay; P = text; P = badge; _lastBadge = ""; _lastFillAmount = -1f; _lastTextValue = int.MinValue; _lastIconEnabled = true; _lastOverlayEnabled = true; _lastBadgeActive = true; Root = root; base..ctor(); } internal void SetRootActive(bool active) { if (Root.activeSelf != active) { Root.SetActive(active); } } internal void Set(Entry entry) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!_hasEntry) { ((Graphic)P).color = FilledBackgroundColor; } _hasEntry = true; _emptyApplied = false; SetIcon(entry.Icon); SetOverlay((entry.Mode == FillMode.Charge) ? (1f - entry.Fraction) : entry.Fraction); SetMainText(entry); SetBadge(entry.Badge); } internal void SetEmpty() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!_emptyApplied || _hasEntry) { _hasEntry = false; _emptyApplied = true; ((Graphic)P).color = EmptyBackgroundColor; SetIcon(null); SetOverlayEnabled(enabled: false); SetText(""); SetBadge(""); _lastTextValue = int.MinValue; _lastFillAmount = -1f; } } private void SetIcon(Sprite? sprite) { //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_0065: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_lastIcon != (Object)(object)sprite) { P.sprite = sprite; _lastIcon = sprite; } bool flag = (Object)(object)sprite != (Object)null; if (_lastIconEnabled != flag) { ((Behaviour)P).enabled = flag; _lastIconEnabled = flag; } if (flag && ((Graphic)P).color != Color.white) { ((Graphic)P).color = Color.white; } } private void SetOverlay(float fillAmount) { SetOverlayEnabled(enabled: true); if (!Mathf.Approximately(_lastFillAmount, fillAmount)) { P.fillAmount = fillAmount; _lastFillAmount = fillAmount; } } private void SetOverlayEnabled(bool enabled) { if (_lastOverlayEnabled != enabled) { ((Behaviour)P).enabled = enabled; _lastOverlayEnabled = enabled; } } private void SetMainText(Entry entry) { ResolveDisplayValue(entry, out var value, out var usesMinutes); if (_textMode != entry.Mode || _lastTextValue != value || _lastTextUsesMinutes != usesMinutes) { _textMode = entry.Mode; _lastTextValue = value; _lastTextUsesMinutes = usesMinutes; SetText(FormatDisplayText(entry.Mode, value, usesMinutes)); } } private void SetText(string value) { if (!string.Equals(((TMP_Text)P).text, value, StringComparison.Ordinal)) { ((TMP_Text)P).text = value; } } private void SetBadge(string value) { string text = value ?? ""; if (!string.Equals(_lastBadge, text, StringComparison.Ordinal)) { ((TMP_Text)P).text = text; _lastBadge = text; } bool flag = !string.IsNullOrWhiteSpace(text); if (_lastBadgeActive != flag) { ((Component)P).gameObject.SetActive(flag); _lastBadgeActive = flag; } } private static void ResolveDisplayValue(Entry entry, out int value, out bool usesMinutes) { if (entry.Mode == FillMode.Charge) { value = Mathf.RoundToInt(Mathf.Clamp01(entry.Fraction) * 100f); usesMinutes = false; } else { int num = Mathf.CeilToInt(Mathf.Max(0f, entry.Remaining)); usesMinutes = num >= 60; value = (usesMinutes ? Mathf.CeilToInt((float)num / 60f) : num); } } private static string FormatDisplayText(FillMode mode, int value, bool usesMinutes) { if (mode == FillMode.Charge) { return $"{value}%"; } if (!usesMinutes) { return value.ToString(); } return $"{value}m"; } } private const int PreviewSlotCount = 10; private const int Columns = 5; private const float SlotSize = 34f; private const float SlotSpacing = 4f; private const float HudScale = 2f; private static readonly Color FilledBackgroundColor = new Color(0f, 0f, 0f, 0.48f); private static readonly Color EmptyBackgroundColor = new Color(0f, 0f, 0f, 0.2f); private static readonly List Entries = new List(); private static readonly List Slots = new List(); private static GameObject? RootObject; private static RectTransform? RootRect; private static TMP_Text? FontSource; internal static bool IsEnabled => SecondaryAttacksPlugin.SecondaryCooldownHudEnabled.Value == SecondaryAttacksPlugin.Toggle.On; internal static void Update(Player? player) { if (!IsEnabled || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)Hud.instance == (Object)null || (Object)(object)Hud.instance.m_rootObject == (Object)null) { Hide(); return; } EnsureHud(); if ((Object)(object)RootObject == (Object)null || (Object)(object)RootRect == (Object)null) { return; } bool flag = InventoryGui.IsVisible(); Entries.Clear(); SneakAmbushChargeSystem.CollectHudEntries(player, Entries); SecondaryCooldownGroupSystem.CollectHudEntries(player, Entries); Entries.Sort(delegate(Entry left, Entry right) { int num2 = left.SortOrder.CompareTo(right.SortOrder); return (num2 == 0) ? left.Remaining.CompareTo(right.Remaining) : num2; }); bool flag2 = flag || Entries.Count > 0; RootObject.SetActive(flag2); if (flag2) { int num = Mathf.Max(Entries.Count, flag ? 10 : 0); EnsureSlotCapacity(num); UpdateRootSize(num); ApplyConfiguredTransform(); if (flag) { RootObject.transform.SetAsLastSibling(); } UpdateSlots(flag, num); } } internal static void Hide() { if ((Object)(object)RootObject != (Object)null) { RootObject.SetActive(false); } } private static void EnsureHud() { //IL_0044: 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_0050: 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: Expected O, but got Unknown //IL_0071: 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_0090: 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_00b3: 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_00e7: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)RootObject != (Object)null) || !((Object)(object)RootRect != (Object)null) || !AreSlotsValid()) { ResetHudReferences(); FontSource = ResolveFontSource(); if (!((Object)(object)FontSource == (Object)null)) { GameObject val = new GameObject("SecondaryAttacks_CooldownHud"); val.SetActive(false); val.transform.SetParent(Hud.instance.m_rootObject.transform, false); RootObject = val; RootRect = val.AddComponent(); RootRect.anchorMin = Vector2.zero; RootRect.anchorMax = Vector2.zero; RootRect.pivot = new Vector2(0.5f, 1f); GridLayoutGroup obj = val.AddComponent(); obj.constraint = (Constraint)1; obj.constraintCount = 5; obj.cellSize = new Vector2(34f, 34f); obj.spacing = new Vector2(4f, 4f); ((LayoutGroup)obj).childAlignment = (TextAnchor)0; EnsureSlotCapacity(10); UpdateRootSize(10); val.SetActive(false); } } } private static void ResetHudReferences() { if ((Object)(object)RootObject != (Object)null) { Object.Destroy((Object)(object)RootObject); } RootObject = null; RootRect = null; FontSource = null; Slots.Clear(); } private static TMP_Text? ResolveFontSource() { if (HasFont((TMP_Text?)(object)Hud.instance.m_hoverName)) { return (TMP_Text?)(object)Hud.instance.m_hoverName; } if (HasFont(Hud.instance.m_pieceDescription)) { return Hud.instance.m_pieceDescription; } TMP_Text[] componentsInChildren = Hud.instance.m_rootObject.GetComponentsInChildren(true); foreach (TMP_Text val in componentsInChildren) { if (HasFont(val)) { return val; } } return null; } private static bool HasFont(TMP_Text? source) { if ((Object)(object)source != (Object)null) { return (Object)(object)source.font != (Object)null; } return false; } private static CooldownSlot CreateSlot(Transform parent, int index) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0033: 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_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_0086: 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_00ac: 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_00d5: 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_00fd: 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_0116: 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_012c: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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_01a4: 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_01b6: 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_01cb: 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_021a: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"CooldownSlot_{index}"); val.transform.SetParent(parent, false); val.AddComponent().sizeDelta = new Vector2(34f, 34f); Image val2 = val.AddComponent(); ((Graphic)val2).color = new Color(0f, 0f, 0f, 0.42f); ((Graphic)val2).raycastTarget = false; GameObject val3 = new GameObject("Icon"); val3.transform.SetParent(val.transform, false); RectTransform obj = val3.AddComponent(); obj.anchorMin = new Vector2(0.5f, 0.5f); obj.anchorMax = new Vector2(0.5f, 0.5f); obj.pivot = new Vector2(0.5f, 0.5f); obj.sizeDelta = new Vector2(29f, 29f); Image val4 = val3.AddComponent(); val4.preserveAspect = true; ((Graphic)val4).raycastTarget = false; GameObject val5 = new GameObject("Overlay"); val5.transform.SetParent(val.transform, false); RectTransform obj2 = val5.AddComponent(); obj2.anchorMin = Vector2.zero; obj2.anchorMax = Vector2.one; obj2.offsetMin = Vector2.zero; obj2.offsetMax = Vector2.zero; Image val6 = val5.AddComponent(); ((Graphic)val6).color = new Color(0f, 0f, 0f, 0.58f); val6.type = (Type)3; val6.fillMethod = (FillMethod)4; val6.fillOrigin = 2; val6.fillClockwise = false; ((Graphic)val6).raycastTarget = false; GameObject val7 = new GameObject("Text"); val7.transform.SetParent(val.transform, false); RectTransform obj3 = val7.AddComponent(); obj3.anchorMin = Vector2.zero; obj3.anchorMax = Vector2.one; obj3.offsetMin = Vector2.zero; obj3.offsetMax = Vector2.zero; TextMeshProUGUI val8 = val7.AddComponent(); if ((Object)(object)FontSource != (Object)null) { ((TMP_Text)val8).font = FontSource.font; ((TMP_Text)val8).fontSharedMaterial = FontSource.fontSharedMaterial; } ((TMP_Text)val8).alignment = (TextAlignmentOptions)514; ((Graphic)val8).color = Color.white; ((TMP_Text)val8).fontSize = 14f; ((TMP_Text)val8).fontStyle = (FontStyles)1; ((TMP_Text)val8).richText = false; ((TMP_Text)val8).textWrappingMode = (TextWrappingModes)0; ((Graphic)val8).raycastTarget = false; Shadow obj4 = val7.AddComponent(); obj4.effectColor = new Color(0f, 0f, 0f, 0.9f); obj4.effectDistance = new Vector2(1f, -1f); GameObject val9 = new GameObject("Badge"); val9.transform.SetParent(val.transform, false); RectTransform obj5 = val9.AddComponent(); obj5.anchorMin = new Vector2(1f, 1f); obj5.anchorMax = new Vector2(1f, 1f); obj5.pivot = new Vector2(1f, 1f); obj5.anchoredPosition = new Vector2(-2f, -1f); obj5.sizeDelta = new Vector2(14f, 14f); TextMeshProUGUI val10 = val9.AddComponent(); if ((Object)(object)FontSource != (Object)null) { ((TMP_Text)val10).font = FontSource.font; ((TMP_Text)val10).fontSharedMaterial = FontSource.fontSharedMaterial; } ((TMP_Text)val10).alignment = (TextAlignmentOptions)260; ((Graphic)val10).color = new Color(1f, 0.92f, 0.58f, 1f); ((TMP_Text)val10).fontSize = 11f; ((TMP_Text)val10).fontStyle = (FontStyles)1; ((TMP_Text)val10).richText = false; ((TMP_Text)val10).textWrappingMode = (TextWrappingModes)0; ((Graphic)val10).raycastTarget = false; Shadow obj6 = val9.AddComponent(); obj6.effectColor = new Color(0f, 0f, 0f, 0.95f); obj6.effectDistance = new Vector2(1f, -1f); return new CooldownSlot(val, val2, val4, val6, val8, val10); } private static void ApplyConfiguredTransform() { //IL_003b: 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_0044: 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_0049: 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_0077: 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_008e: 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) if (!((Object)(object)RootRect == (Object)null)) { Transform parent = ((Transform)RootRect).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); ? val2; if (!((Object)(object)val != (Object)null)) { val2 = new Vector2((float)Screen.width, (float)Screen.height); } else { Rect rect = val.rect; val2 = ((Rect)(ref rect)).size; } Vector2 val3 = (Vector2)val2; float num = Mathf.Clamp01(SecondaryAttacksPlugin.SecondaryCooldownHudPositionX.Value); float num2 = Mathf.Clamp01(SecondaryAttacksPlugin.SecondaryCooldownHudPositionY.Value); RootRect.anchoredPosition = new Vector2(val3.x * num, val3.y * num2); ((Transform)RootRect).localScale = Vector3.one * 2f; } } private static void EnsureSlotCapacity(int requiredCount) { if (!((Object)(object)RootObject == (Object)null)) { while (Slots.Count < requiredCount) { Slots.Add(CreateSlot(RootObject.transform, Slots.Count)); } } } private static void UpdateRootSize(int visibleSlotCount) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)RootRect == (Object)null)) { int num = Mathf.Max(1, Mathf.CeilToInt((float)visibleSlotCount / 5f)); float num2 = 186f; float num3 = (float)num * 34f + (float)(num - 1) * 4f; RootRect.sizeDelta = new Vector2(num2, num3); } } private static void UpdateSlots(bool previewMode, int visibleSlotCount) { if (!AreSlotsValid()) { ResetHudReferences(); EnsureHud(); EnsureSlotCapacity(visibleSlotCount); UpdateRootSize(visibleSlotCount); if ((Object)(object)RootObject == (Object)null || (Object)(object)RootRect == (Object)null || Slots.Count < visibleSlotCount || !AreSlotsValid()) { return; } } for (int i = 0; i < Slots.Count; i++) { CooldownSlot cooldownSlot = Slots[i]; if (!cooldownSlot.IsValid) { continue; } if (i >= visibleSlotCount) { cooldownSlot.SetRootActive(active: false); continue; } bool flag = i < Entries.Count; if (!flag && !previewMode) { cooldownSlot.SetRootActive(active: false); continue; } cooldownSlot.SetRootActive(active: true); if (flag) { cooldownSlot.Set(Entries[i]); } else { cooldownSlot.SetEmpty(); } } } private static bool AreSlotsValid() { foreach (CooldownSlot slot in Slots) { if (!slot.IsValid) { return false; } } return true; } } internal static class SneakVisibilitySystem { internal readonly struct UpdateStealthState { internal readonly float PreviousStealthFactor; public UpdateStealthState(float previousStealthFactor) { PreviousStealthFactor = previousStealthFactor; } } internal readonly struct CrouchSpeedState { internal readonly Player? Player; internal readonly float OriginalCrouchSpeed; public CrouchSpeedState(Player? player, float originalCrouchSpeed) { Player = player; OriginalCrouchSpeed = originalCrouchSpeed; } } private const float MinimumVisibility = 0.1f; internal static UpdateStealthState Capture(Player player) { return new UpdateStealthState(((Object)(object)player != (Object)null) ? player.m_stealthFactor : 1f); } internal static void Apply(Player player, float dt, UpdateStealthState state) { if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner() || QuickstepSystem.IsActive(player) || !((Character)player).IsCrouching()) { return; } float num = Mathf.Clamp(SecondaryAttacksPlugin.SneakVisibilitySkillEffectFactor?.Value ?? 1f, 1f, 2f); if (!Mathf.Approximately(num, 1f)) { float num2 = CalculateTargetVisibility(player, num); float num3 = Mathf.MoveTowards(state.PreviousStealthFactor, num2, dt / 4f); if (!Mathf.Approximately(num3, player.m_stealthFactor)) { player.m_stealthFactorTarget = num2; player.m_stealthFactor = num3; ((Character)player).m_nview.GetZDO().Set(ZDOVars.s_stealth, num3); } } } internal static void ApplyMovementSpeed(Player player, ref float speedFactor) { if (!((Object)(object)player == (Object)null) && !QuickstepSystem.IsActive(player) && ((Character)player).IsCrouching()) { float num = Mathf.Clamp(SecondaryAttacksPlugin.SneakMovementSpeedSkillFactor?.Value ?? 1f, 1f, 2f); if (!Mathf.Approximately(num, 1f)) { float num2 = Mathf.Clamp01(player.m_skills.GetSkillFactor((SkillType)101)); speedFactor *= Mathf.Lerp(1f, num, num2); } } } internal static CrouchSpeedState ApplyCrouchSpeed(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() || QuickstepSystem.IsActive(player) || !((Character)player).IsCrouching()) { return default(CrouchSpeedState); } float num = Mathf.Clamp(SecondaryAttacksPlugin.SneakMovementSpeedSkillFactor?.Value ?? 1f, 1f, 2f); if (Mathf.Approximately(num, 1f)) { return default(CrouchSpeedState); } float num2 = Mathf.Clamp01(player.m_skills.GetSkillFactor((SkillType)101)); float num3 = Mathf.Lerp(1f, num, num2); if (Mathf.Approximately(num3, 1f)) { return default(CrouchSpeedState); } float crouchSpeed = ((Character)player).m_crouchSpeed; ((Character)player).m_crouchSpeed = crouchSpeed * num3; return new CrouchSpeedState(player, crouchSpeed); } internal static void RestoreCrouchSpeed(CrouchSpeedState state) { if ((Object)(object)state.Player != (Object)null) { ((Character)state.Player).m_crouchSpeed = state.OriginalCrouchSpeed; } } internal static void RestoreCrouchSpeed(ref CrouchSpeedState state) { RestoreCrouchSpeed(state); state = default(CrouchSpeedState); } private static float CalculateTargetVisibility(Player player, float factor) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(player.m_skills.GetSkillFactor((SkillType)101)); float num2 = (((Object)(object)StealthSystem.instance != (Object)null) ? Mathf.Clamp01(StealthSystem.instance.GetLightFactor(((Character)player).GetCenterPoint())) : 1f); float num3 = 0.5f + num2 * 0.5f; float num4 = 0.3f + num2 * 0.1f; float num5 = num3 - num4 * num * factor; num5 = Mathf.Clamp(num5, 0.1f, 1f); ((Character)player).m_seman.ModifyStealth(num5, ref num5); return Mathf.Clamp(num5, 0.1f, 1f); } } [HarmonyPatch(typeof(Player), "UpdateStealth")] internal static class PlayerUpdateStealthSneakVisibilityPatch { private static void Prefix(Player __instance, out SneakVisibilitySystem.UpdateStealthState __state) { __state = SneakVisibilitySystem.Capture(__instance); } private static void Postfix(Player __instance, float dt, SneakVisibilitySystem.UpdateStealthState __state) { SneakVisibilitySystem.Apply(__instance, dt, __state); } } [HarmonyPatch(typeof(Player), "GetJogSpeedFactor")] internal static class PlayerGetJogSpeedFactorSneakMovementPatch { private static void Postfix(Player __instance, ref float __result) { SneakVisibilitySystem.ApplyMovementSpeed(__instance, ref __result); } } [HarmonyPatch(typeof(Character), "UpdateWalking")] internal static class CharacterUpdateWalkingSneakMovementPatch { private static void Prefix(Character __instance, out SneakVisibilitySystem.CrouchSpeedState __state) { Player val = (Player)(object)((__instance is Player) ? __instance : null); __state = ((val != null) ? SneakVisibilitySystem.ApplyCrouchSpeed(val) : default(SneakVisibilitySystem.CrouchSpeedState)); } private static void Postfix(ref SneakVisibilitySystem.CrouchSpeedState __state) { SneakVisibilitySystem.RestoreCrouchSpeed(ref __state); } private static void Finalizer(ref SneakVisibilitySystem.CrouchSpeedState __state) { SneakVisibilitySystem.RestoreCrouchSpeed(ref __state); } } internal static class SneakAmbushChargeSystem { private sealed class SneakAmbushChargeState { public float ChargeSeconds { get; set; } public float PendingAttackChargeSeconds { get; set; } public bool HasPendingAttackCharge { get; set; } public float MaxSeconds { get; set; } public bool Display { get; set; } public void ClearDisplay() { MaxSeconds = 0f; Display = false; } public void ClearPendingAttack() { PendingAttackChargeSeconds = 0f; HasPendingAttackCharge = false; } public void Clear() { ChargeSeconds = 0f; ClearPendingAttack(); ClearDisplay(); } } private const string FallbackIconPrefabName = "KnifeWood"; private const float ChargeDecayPerSecond = 1f; private static readonly ConditionalWeakTable States = new ConditionalWeakTable(); internal static void Update(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } SneakAmbushChargeState value = States.GetValue(player, (Player _) => new SneakAmbushChargeState()); bool flag = IsSecondaryAttackActive(player); if (!flag) { value.ClearPendingAttack(); } if (!TryResolveCurrentSneakAmbush(player, out ItemData _, out SneakAmbushDefinition sneakAmbush)) { value.Clear(); return; } if (flag || MeleePresetCooldownSystem.IsCooldownActive((Character)(object)player, "sneakAmbush", sneakAmbush.PresetCooldown, out var _)) { value.ChargeSeconds = 0f; value.ClearDisplay(); return; } float num = Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds); if (num <= 0f) { value.Clear(); return; } float num2 = Mathf.Max(0f, Time.deltaTime); bool flag2 = ((Character)player).IsCrouching() && !QuickstepSystem.IsActive(player); if (num2 > 0f) { if (flag2) { float num3 = Mathf.Clamp(((Character)player).GetSkillLevel((SkillType)101), 0f, 100f); float num4 = Mathf.Lerp(1f, Mathf.Max(0f, sneakAmbush.ChargeSkillFactor), num3 / 100f); value.ChargeSeconds += num2 * num4; } else { value.ChargeSeconds -= num2 * 1f; } } value.ChargeSeconds = Mathf.Clamp(value.ChargeSeconds, 0f, num); value.MaxSeconds = num; value.Display = flag2 || value.ChargeSeconds > 0f; } internal static void BeginSecondaryAttack(Player player, ItemData? weapon) { if ((Object)(object)player == (Object)null || weapon == null) { return; } SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(weapon); if (SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) && definition.SneakAmbush != null) { SneakAmbushDefinition sneakAmbush = definition.SneakAmbush; SneakAmbushChargeState value = States.GetValue(player, (Player _) => new SneakAmbushChargeState()); float num = Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds); value.PendingAttackChargeSeconds = Mathf.Clamp(value.ChargeSeconds, 0f, num); value.HasPendingAttackCharge = true; value.ChargeSeconds = 0f; value.ClearDisplay(); } } internal static float GetPreparedSeconds(Player player, SneakAmbushDefinition sneakAmbush) { if ((Object)(object)player == (Object)null || sneakAmbush == null || !States.TryGetValue(player, out SneakAmbushChargeState value)) { return 0f; } if (value.HasPendingAttackCharge) { return Mathf.Clamp(value.PendingAttackChargeSeconds, 0f, Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds)); } return Mathf.Clamp(value.ChargeSeconds, 0f, Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds)); } internal static void Consume(Player player) { if (!((Object)(object)player == (Object)null) && States.TryGetValue(player, out SneakAmbushChargeState value)) { value.ChargeSeconds = 0f; value.ClearPendingAttack(); value.ClearDisplay(); } } internal static void CollectHudEntries(Player player, List entries) { if (!((Object)(object)player == (Object)null) && entries != null && States.TryGetValue(player, out SneakAmbushChargeState value) && value.Display && !(value.MaxSeconds <= 0f)) { TryResolveCurrentSneakAmbush(player, out ItemData weapon, out SneakAmbushDefinition _); float remaining = Mathf.Clamp(value.ChargeSeconds, 0f, value.MaxSeconds); entries.Add(new SecondaryCooldownHudSystem.Entry(ResolveWeaponIcon(weapon) ?? ResolveFallbackIcon(), remaining, value.MaxSeconds, "", SecondaryCooldownHudSystem.FillMode.Charge)); } } private static bool TryResolveCurrentSneakAmbush(Player player, out ItemData? weapon, out SneakAmbushDefinition? sneakAmbush) { weapon = null; sneakAmbush = null; weapon = ((Humanoid)player).GetCurrentWeapon(); if (weapon == null) { return false; } SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(weapon); if (!SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) || definition.SneakAmbush == null) { return false; } sneakAmbush = definition.SneakAmbush; return true; } private static bool IsSecondaryAttackActive(Player player) { if (((Character)player).InAttack() && ((Humanoid)player).m_currentAttack != null) { return ((Humanoid)player).m_currentAttackIsSecondary; } return false; } private static Sprite? ResolveIcon(ObjectDB objectDb, string itemPrefabName) { GameObject itemPrefab = objectDb.GetItemPrefab(itemPrefabName); Sprite[] array = ((itemPrefab != null) ? itemPrefab.GetComponent() : null)?.m_itemData?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveWeaponIcon(ItemData? weapon) { Sprite[] array = weapon?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveFallbackIcon() { if (!((Object)(object)ObjectDB.instance != (Object)null)) { return null; } return ResolveIcon(ObjectDB.instance, "KnifeWood"); } } internal static class SneakAmbushSystem { private sealed class SneakAmbushUpdater : MonoBehaviour { private void Update() { FlushPendingSmokeEffects(); if (SenseBlocks.Count == 0) { return; } float time = Time.time; SenseBlockRemoveBuffer.Clear(); foreach (KeyValuePair senseBlock in SenseBlocks) { Character key = senseBlock.Key; if ((Object)(object)key == (Object)null) { SenseBlockRemoveBuffer.Add(key); continue; } if (key.IsDead() || time >= senseBlock.Value.Until || (Object)(object)senseBlock.Value.HiddenPlayer == (Object)null || ((Character)senseBlock.Value.HiddenPlayer).IsDead()) { SenseBlockRemoveBuffer.Add(key); continue; } BaseAI baseAI = key.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val != null) { ClearMonsterAwareness(key, val, 0f); } } foreach (Character item in SenseBlockRemoveBuffer) { SenseBlocks.Remove(item); } SenseBlockRemoveBuffer.Clear(); } private static void FlushPendingSmokeEffects() { if (PendingSmokeEffects.Count == 0) { return; } int frameCount = Time.frameCount; for (int num = PendingSmokeEffects.Count - 1; num >= 0; num--) { PendingSmokeEffect pendingSmokeEffect = PendingSmokeEffects[num]; if (frameCount > pendingSmokeEffect.CreatedFrame) { PendingSmokeEffects.RemoveAt(num); if (!((Object)(object)pendingSmokeEffect.Player == (Object)null) && !((Character)pendingSmokeEffect.Player).IsDead()) { ApplySmokeEffect(pendingSmokeEffect.Player, pendingSmokeEffect.Range, pendingSmokeEffect.SenseBlockDuration, pendingSmokeEffect.BackstabResetSeconds); } } } } } private sealed class PendingSmokeEffect { public Player Player { get; } public int CreatedFrame { get; } public float Range { get; } public float SenseBlockDuration { get; } public float BackstabResetSeconds { get; } public PendingSmokeEffect(Player player, int createdFrame, float range, float senseBlockDuration, float backstabResetSeconds) { Player = player; CreatedFrame = createdFrame; Range = range; SenseBlockDuration = senseBlockDuration; BackstabResetSeconds = backstabResetSeconds; } } private sealed class SmokeSenseBlockState { public Player HiddenPlayer { get; set; } public float Until { get; set; } public SmokeSenseBlockState(Player hiddenPlayer, float until) { HiddenPlayer = hiddenPlayer; Until = until; } } internal const string RpcName = "SecondaryAttacks_SpawnSneakAmbushVfx"; internal const string OwnerSmokeRpcName = "SecondaryAttacks_ApplySneakAmbushSmoke"; private const string SmokeBombExplosionPrefabName = "smokebomb_explosion"; private const string SmokeBombVfxChildName = "smoke particles"; private const float DestroyDelay = 10f; private const float MinPreparedSecondsToTrigger = 0.25f; private static readonly Dictionary SenseBlocks = new Dictionary(); private static readonly List PendingSmokeEffects = new List(); private static readonly List SenseBlockRemoveBuffer = new List(); private static readonly FieldRef? CharacterBackstabTimeField = TryCreateFloatFieldRef("m_backstabTime"); private static GameObject? _updaterObject; internal static void TryTriggerForSecondaryHit(Player player, Character target, bool secondaryAttack, SecondaryAttackDefinition? definition) { //IL_0085: 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_0092: 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_00c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)target == (Object)null || !secondaryAttack || definition?.SneakAmbush == null || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)player) || !IsValidSneakAmbushTarget(player, target)) { return; } SneakAmbushDefinition sneakAmbush = definition.SneakAmbush; float preparedSeconds = SneakAmbushChargeSystem.GetPreparedSeconds(player, sneakAmbush); ResolveSmokeEffect(sneakAmbush, preparedSeconds, out var range, out var senseBlockDuration, out var backstabResetSeconds); if (HasEffectiveSmokeEffect(range) && MeleePresetCooldownSystem.TryConsume((Character)(object)player, null, "sneakAmbush", sneakAmbush.PresetCooldown)) { SneakAmbushChargeSystem.Consume(player); QueueSmokeEffect(player, range, senseBlockDuration, backstabResetSeconds); Vector3 position = ((Component)player).transform.position; float y = ((Component)player).transform.eulerAngles.y; if (SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)player, out ZNetView nview, out ZDO _) && ZRoutedRpc.instance != null) { nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_SpawnSneakAmbushVfx", new object[2] { position, y }); } else { SpawnVisual(position, Quaternion.Euler(0f, y, 0f)); } } } internal static void SpawnFromRpc(Character owner, Vector3 position, float yaw) { //IL_000a: 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 (!((Object)(object)owner == (Object)null)) { SpawnVisual(position, Quaternion.Euler(0f, yaw, 0f)); } } internal static void ApplyOwnerSmokeFromRpc(Character monster, ZNetView? monsterView, ZDOID hiddenPlayerId, float range, float senseBlockDuration, float backstabResetSeconds) { //IL_004b: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)monster == (Object)null || (Object)(object)monsterView == (Object)null || !monsterView.IsValid() || !monsterView.IsOwner() || ((ZDOID)(ref hiddenPlayerId)).IsNone() || monster.IsDead() || monster.IsTamed()) { return; } BaseAI baseAI = monster.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val == null) { return; } Player val2 = ResolvePlayer(hiddenPlayerId); if ((Object)(object)val2 == (Object)null || ((Character)val2).IsDead() || !IsValidSneakAmbushTarget(val2, monster)) { return; } float num = Mathf.Max(0f, range) + 1f; if (num > 0f) { Vector3 val3 = ((Component)monster).transform.position - ((Component)val2).transform.position; if (((Vector3)(ref val3)).sqrMagnitude > num * num) { return; } } ApplyOwnedSmokeEffect(val2, monster, val, senseBlockDuration, backstabResetSeconds); } private static bool IsValidSneakAmbushTarget(Player player, Character target) { if ((Object)(object)target == (Object)(object)player || target.IsDead() || target.IsTamed()) { return false; } if (BaseAI.IsEnemy((Character)(object)player, target)) { return true; } BaseAI baseAI = target.GetBaseAI(); if ((Object)(object)baseAI != (Object)null) { return baseAI.IsAggravatable(); } return false; } private static void SpawnVisual(Vector3 position, Quaternion rotation) { //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) //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_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_0075: 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_0088: 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) GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("smokebomb_explosion") : null); Transform val2 = (((Object)(object)val != (Object)null) ? val.transform.Find("smoke particles") : null); if ((Object)(object)val2 == (Object)null) { if (SecondaryAttackWarningLog.TryMarkWarning("smoke_strike_vfx_missing")) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)"Sneak Ambush VFX requires child 'smoke particles' on prefab 'smokebomb_explosion', but it was not found."); } } else { Vector3 val3 = position + rotation * val2.localPosition; Quaternion val4 = rotation * val2.localRotation; Object.Destroy((Object)(object)Object.Instantiate(((Component)val2).gameObject, val3, val4), 10f); } } private static void ResolveSmokeEffect(SneakAmbushDefinition sneakAmbush, float preparedSeconds, out float range, out float senseBlockDuration, out float backstabResetSeconds) { float num = Mathf.Max(0f, preparedSeconds); if (num < 0.25f) { range = 0f; senseBlockDuration = 0f; backstabResetSeconds = 0f; } else { range = Mathf.Max(0f, num * sneakAmbush.AggroResetRangePerChargeSecond); senseBlockDuration = Mathf.Max(0f, num * sneakAmbush.SenseBlockDurationPerChargeSecond); backstabResetSeconds = Mathf.Max(0f, num * sneakAmbush.BackstabResetSecondsPerChargeSecond); } } private static bool HasEffectiveSmokeEffect(float range) { return range > 0f; } private static void QueueSmokeEffect(Player player, float range, float senseBlockDuration, float backstabResetSeconds) { PendingSmokeEffects.Add(new PendingSmokeEffect(player, Time.frameCount, range, senseBlockDuration, backstabResetSeconds)); EnsureUpdater(); } private static void ApplySmokeEffect(Player player, float range, float senseBlockDuration, float backstabResetSeconds) { //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_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_0046: 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) ZDOID zDOID = ((Character)player).GetZDOID(); if (((ZDOID)(ref zDOID)).IsNone()) { return; } Vector3 position = ((Component)player).transform.position; float num = Mathf.Max(0f, range); num *= num; foreach (Character allCharacter in Character.GetAllCharacters()) { if (TryGetAffectedMonster(player, allCharacter, position, num, out MonsterAI monsterAI) && SecondaryAttackManager.TryGetCharacterZdo(allCharacter, out ZNetView nview, out ZDO _)) { if (nview.IsOwner()) { ApplyOwnedSmokeEffect(player, allCharacter, monsterAI, senseBlockDuration, backstabResetSeconds); continue; } nview.InvokeRPC("SecondaryAttacks_ApplySneakAmbushSmoke", new object[4] { zDOID, range, senseBlockDuration, backstabResetSeconds }); } } } private static void ApplyOwnedSmokeEffect(Player player, Character monster, MonsterAI monsterAI, float senseBlockDuration, float backstabResetSeconds) { ClearMonsterAwareness(monster, monsterAI, backstabResetSeconds); if (!(senseBlockDuration <= 0f)) { EnsureUpdater(); float num = Time.time + senseBlockDuration; if (SenseBlocks.TryGetValue(monster, out SmokeSenseBlockState value)) { value.HiddenPlayer = player; value.Until = Mathf.Max(value.Until, num); } else { SenseBlocks[monster] = new SmokeSenseBlockState(player, num); } } } internal static bool ShouldBlockSense(BaseAI baseAI, Character target) { if ((Object)(object)baseAI == (Object)null || (Object)(object)target == (Object)null) { return false; } Character val = (((Object)(object)baseAI.m_character != (Object)null) ? baseAI.m_character : ((Component)baseAI).GetComponent()); if ((Object)(object)val == (Object)null || !SenseBlocks.TryGetValue(val, out SmokeSenseBlockState value)) { return false; } if (val.IsDead() || Time.time >= value.Until || (Object)(object)value.HiddenPlayer == (Object)null || ((Character)value.HiddenPlayer).IsDead()) { SenseBlocks.Remove(val); return false; } return (Object)(object)target == (Object)(object)value.HiddenPlayer; } private static bool TryGetAffectedMonster(Player player, Character? character, Vector3 center, float rangeSqr, out MonsterAI? monsterAI) { //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_0031: Unknown result type (might be due to invalid IL or missing references) monsterAI = null; if ((Object)(object)character == (Object)null || (Object)(object)character == (Object)(object)player || character.IsDead()) { return false; } Vector3 val = ((Component)character).transform.position - center; if (((Vector3)(ref val)).sqrMagnitude > rangeSqr || !BaseAI.IsEnemy((Character)(object)player, character)) { return false; } BaseAI baseAI = character.GetBaseAI(); monsterAI = (MonsterAI?)(object)((baseAI is MonsterAI) ? baseAI : null); return (Object)(object)monsterAI != (Object)null; } private static Player? ResolvePlayer(ZDOID playerId) { //IL_0020: 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_005f: 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) if (((ZDOID)(ref playerId)).IsNone()) { return null; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(playerId) : null); Player result = default(Player); if ((Object)(object)val != (Object)null && val.TryGetComponent(ref result)) { return result; } foreach (Character allCharacter in Character.GetAllCharacters()) { Player val2 = (Player)(object)((allCharacter is Player) ? allCharacter : null); if (val2 != null) { ZDOID zDOID = ((Character)val2).GetZDOID(); if (((ZDOID)(ref zDOID)).Equals(playerId)) { return val2; } } } return null; } private static void ClearMonsterAwareness(Character character, MonsterAI monsterAI, float backstabResetSeconds) { //IL_0008: 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_008e: 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) monsterAI.SetTarget((Character)null); ((BaseAI)monsterAI).SetTargetInfo(ZDOID.None); ((BaseAI)monsterAI).SetAlerted(false); monsterAI.m_targetCreature = null; monsterAI.m_targetStatic = null; monsterAI.m_lastKnownTargetPos = ((Component)character).transform.position; monsterAI.m_beenAtLastPos = true; monsterAI.m_timeSinceSensedTargetCreature = 999f; ((BaseAI)monsterAI).m_timeSinceHurt = 999f; monsterAI.m_updateTargetTimer = 0f; monsterAI.m_unableToAttackTargetTimer = 0f; ZNetView nview = character.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { nview.GetZDO().Set("target", ZDOID.None); if (nview.IsOwner()) { ((BaseAI)monsterAI).SetTargetInfo(ZDOID.None); ((BaseAI)monsterAI).SetAlerted(false); } } if (backstabResetSeconds > 0f && TryGetFloatField(CharacterBackstabTimeField, character, out var value)) { SetFloatField(CharacterBackstabTimeField, character, value - backstabResetSeconds); } } private static FieldRef? TryCreateFloatFieldRef(string fieldName) { if (!HasInstanceField(typeof(T), fieldName)) { return null; } try { return AccessTools.FieldRefAccess(fieldName); } catch (Exception) { return null; } } private static bool HasInstanceField(Type type, string fieldName) { Type type2 = type; while (type2 != null) { if (type2.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null) { return true; } type2 = type2.BaseType; } return false; } private static void SetFloatField(FieldRef? field, T target, float value) { if (field != null) { field.Invoke(target) = value; } } private static bool TryGetFloatField(FieldRef? field, T target, out float value) { if (field == null) { value = 0f; return false; } value = field.Invoke(target); return true; } private static void EnsureUpdater() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_updaterObject != (Object)null)) { _updaterObject = new GameObject("SecondaryAttacks_SneakAmbushUpdater"); ((Object)_updaterObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)_updaterObject); _updaterObject.AddComponent(); } } } internal static class SpinningSweepSystem { private const string PresetName = "spinningSweep"; private const float RepeatDelay = 0f; private const float RotationSpeedFactor = 1f; internal static bool TryStart(Attack attack, SecondaryAttackDefinition definition) { SpinningSweepDefinition spinningSweep = definition.SpinningSweep; Humanoid val = attack?.m_character; if (val == null || attack.m_weapon == null || spinningSweep == null || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)val)) { return false; } SpinningSweepController spinningSweepController = ((Component)val).GetComponent(); if ((Object)(object)spinningSweepController != (Object)null && spinningSweepController.IsActive) { if (!spinningSweepController.MatchesWeapon(attack.m_weapon)) { spinningSweepController.StopAfterCurrentAttack(); return false; } spinningSweepController.AttachAttack(attack, spinningSweep); SweepObserverVisualSystem.SendRefresh(val, harvestSweep: false, attack.m_attackAnimation, spinningSweep.LoopStart, spinningSweep.LoopEnd, spinningSweep.AnimationSpeed); return true; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)val, attack.m_weapon, "spinningSweep", spinningSweep.PresetCooldown)) { return false; } if ((Object)(object)spinningSweepController == (Object)null) { spinningSweepController = ((Component)val).gameObject.AddComponent(); } spinningSweepController.Begin(attack, definition, spinningSweep); SweepObserverVisualSystem.SendStart(val, harvestSweep: false, attack.m_attackAnimation, spinningSweep.LoopStart, spinningSweep.LoopEnd, spinningSweep.AnimationSpeed); return true; } internal static float GetRepeatDelay() { return 0f; } internal static float GetRotationSpeedFactor() { return 1f; } internal static void UpdateInput(Player player, bool secondaryAttackHold, bool primaryAttackHold) { if (!((Object)(object)player == (Object)null)) { ((Component)player).GetComponent()?.UpdateInput(secondaryAttackHold, primaryAttackHold); } } internal static bool StartRepeatAttack(Humanoid humanoid) { return ((Character)humanoid).StartAttack((Character)null, true); } } internal sealed class SpinningSweepController : MonoBehaviour { private static readonly int AttackTagHash = ZSyncAnimation.GetHash("attack"); private Humanoid? _humanoid; private ItemData? _weapon; private SecondaryAttackDefinition? _definition; private SpinningSweepDefinition? _spinningSweep; private Attack? _currentAttack; private Animator? _animator; private ZSyncAnimation? _zanim; private float _nextRepeatTime; private float _originalAnimatorSpeed = 1f; private float _originalRaiseSkillAmount; private float _originalDamageMultiplier = 1f; private float _originalForceMultiplier = 1f; private int _loopStateHash; private int _lastLoopFrame = -1; private int _startedFrame; private bool _stopRequested; private bool _cancelArmed; private bool _primaryCancelArmed; private bool _lastSecondaryHold = true; private bool _lastPrimaryHold; private bool _hasOriginalAnimatorSpeed; private bool _hasOriginalRaiseSkillAmount; private bool _hasOriginalAttackMultipliers; private bool _speedApplied; private bool _initialLoopStartApplied; private bool _loopRearmed = true; private Attack? _skillRaiseAttack; private Attack? _multiplierAttack; internal bool IsActive { get { if (_spinningSweep != null) { return !_stopRequested; } return false; } } internal bool SuppressesHitStop => _spinningSweep != null; internal bool TryGetAnimationSpeed(out float speed) { speed = _spinningSweep?.AnimationSpeed ?? 1f; if (_spinningSweep != null) { return !Mathf.Approximately(speed, 1f); } return false; } internal void Begin(Attack attack, SecondaryAttackDefinition definition, SpinningSweepDefinition spinningSweep) { _humanoid = attack.m_character; _weapon = attack.m_weapon; _definition = definition; _spinningSweep = spinningSweep; _animator = (((Object)(object)_humanoid != (Object)null) ? ((Component)_humanoid).GetComponentInChildren() : null); Humanoid? humanoid = _humanoid; _zanim = ((humanoid != null) ? ((Character)humanoid).GetZAnim() : null); _nextRepeatTime = Time.time + SpinningSweepSystem.GetRepeatDelay(); _loopStateHash = 0; _lastLoopFrame = -1; _startedFrame = Time.frameCount; _stopRequested = false; _cancelArmed = false; _primaryCancelArmed = false; _lastSecondaryHold = true; _lastPrimaryHold = false; _initialLoopStartApplied = false; _loopRearmed = true; AttachAttack(attack, spinningSweep); ((Behaviour)this).enabled = true; } internal bool MatchesWeapon(ItemData weapon) { if (_weapon != weapon) { if ((Object)(object)_weapon?.m_dropPrefab != (Object)null && (Object)(object)weapon?.m_dropPrefab != (Object)null) { return ((Object)_weapon.m_dropPrefab).name == ((Object)weapon.m_dropPrefab).name; } return false; } return true; } internal void AttachAttack(Attack attack, SpinningSweepDefinition spinningSweep) { bool num = _currentAttack != attack; _currentAttack = attack; if (num) { _loopStateHash = 0; _lastLoopFrame = -1; _initialLoopStartApplied = false; _loopRearmed = true; } ApplyMovementFactors(attack, spinningSweep); ApplyAttackMultipliers(attack, spinningSweep); ApplySkillRaiseFactor(attack, spinningSweep); ApplyAnimationSpeed(spinningSweep); _nextRepeatTime = Time.time + SpinningSweepSystem.GetRepeatDelay(); } internal void UpdateInput(bool secondaryAttackHold, bool primaryAttackHold) { if (!IsActive) { return; } UpdatePrimaryCancelInput(primaryAttackHold); if (!IsActive) { return; } if (!secondaryAttackHold) { _cancelArmed = Time.frameCount > _startedFrame + 1; _lastSecondaryHold = false; return; } bool flag = !_lastSecondaryHold; _lastSecondaryHold = true; if (_cancelArmed && flag) { StopAfterCurrentAttack(); } } private void UpdatePrimaryCancelInput(bool primaryAttackHold) { if (!primaryAttackHold) { _primaryCancelArmed = Time.frameCount > _startedFrame + 1; _lastPrimaryHold = false; return; } bool flag = !_lastPrimaryHold; _lastPrimaryHold = true; if (_primaryCancelArmed && flag) { StopAfterCurrentAttack(); } } internal void StopAfterCurrentAttack() { _stopRequested = true; } private void Update() { if ((Object)(object)_humanoid == (Object)null || _weapon == null || _definition == null || _spinningSweep == null || ((Character)_humanoid).IsDead() || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { Object.Destroy((Object)(object)this); return; } Attack currentAttack = _humanoid.m_currentAttack; if (currentAttack != null && ((Character)_humanoid).InAttack()) { if (currentAttack == _currentAttack) { ApplyMovementFactors(currentAttack, _spinningSweep); if (ShouldKeepLooping()) { TryUpdateSeamlessLoop(currentAttack); } } } else if (_stopRequested || !MatchesWeapon(_humanoid.GetCurrentWeapon())) { Object.Destroy((Object)(object)this); } else if (!(Time.time < _nextRepeatTime) && !((Character)_humanoid).IsStaggering() && !((Character)_humanoid).InAttack()) { if (!CanPayNextAttackCost(_weapon)) { Object.Destroy((Object)(object)this); } else if (!SpinningSweepSystem.StartRepeatAttack(_humanoid)) { _nextRepeatTime = Time.time + 0.05f; } } } private bool ShouldKeepLooping() { if (!_stopRequested && (Object)(object)_humanoid != (Object)null) { return MatchesWeapon(_humanoid.GetCurrentWeapon()); } return false; } private bool TryUpdateSeamlessLoop(Attack activeAttack) { //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_006e: 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_00ba: 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) if (_spinningSweep == null || (Object)(object)_humanoid == (Object)null || _weapon == null) { return false; } if (_animator == null) { _animator = ((Component)_humanoid).GetComponentInChildren(); } if (_zanim == null) { _zanim = ((Character)_humanoid).GetZAnim(); } if ((Object)(object)_animator == (Object)null) { return false; } AnimatorStateInfo attackAnimatorState = GetAttackAnimatorState(_animator); if (!IsAttackState(attackAnimatorState)) { return false; } if (_loopStateHash == 0 && ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash != 0) { _loopStateHash = ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash; } if (!_initialLoopStartApplied) { _initialLoopStartApplied = true; if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime < _spinningSweep.LoopStart) { SeekToLoopStart(attackAnimatorState); _loopRearmed = false; return true; } } if (!TryRearmLoop(attackAnimatorState, _spinningSweep.LoopStart, _spinningSweep.LoopEnd)) { return true; } if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime < _spinningSweep.LoopEnd || _lastLoopFrame == Time.frameCount) { return false; } if (!CanPayNextAttackCost(_weapon)) { _stopRequested = true; return false; } PayNextAttackCost(activeAttack); _lastLoopFrame = Time.frameCount; SeekToLoopStart(attackAnimatorState); _loopRearmed = false; return true; } private bool TryRearmLoop(AnimatorStateInfo state, float loopStart, float loopEnd) { if (_loopRearmed) { return true; } if (((AnimatorStateInfo)(ref state)).normalizedTime < loopEnd) { _loopRearmed = true; return true; } return false; } private void SeekToLoopStart(AnimatorStateInfo state) { if ((Object)(object)_animator == (Object)null || _spinningSweep == null) { return; } int num = ((_loopStateHash != 0) ? _loopStateHash : ((AnimatorStateInfo)(ref state)).fullPathHash); if (num != 0) { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); _animator.Play(num, 0, _spinningSweep.LoopStart); if ((Object)(object)_humanoid != (Object)null && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { SweepObserverVisualSystem.SendRefresh(_humanoid, harvestSweep: false, _currentAttack?.m_attackAnimation ?? string.Empty, _spinningSweep.LoopStart, _spinningSweep.LoopEnd, _spinningSweep.AnimationSpeed); } } } private static AnimatorStateInfo GetAttackAnimatorState(Animator animator) { //IL_001d: 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_0019: Unknown result type (might be due to invalid IL or missing references) if (animator.IsInTransition(0)) { AnimatorStateInfo nextAnimatorStateInfo = animator.GetNextAnimatorStateInfo(0); if (IsAttackState(nextAnimatorStateInfo)) { return nextAnimatorStateInfo; } } return animator.GetCurrentAnimatorStateInfo(0); } private static bool IsAttackState(AnimatorStateInfo state) { if (((AnimatorStateInfo)(ref state)).fullPathHash != 0) { return ((AnimatorStateInfo)(ref state)).tagHash == AttackTagHash; } return false; } private bool CanPayNextAttackCost(ItemData weapon) { SharedData shared = weapon.m_shared; Attack val = shared?.m_secondaryAttack; if (val == null) { return false; } float num = Mathf.Max(0f, shared.m_useDurabilityDrain * (_definition?.DurabilityFactor ?? 1f)); if (num > 0f && weapon.m_durability + 0.001f < num) { return false; } float num2 = Mathf.Max(0f, val.m_attackStamina); if (num2 > 0f && !((Character)_humanoid).HaveStamina(num2)) { return false; } float num3 = Mathf.Max(0f, val.m_attackEitr); if (num3 > 0f && !((Character)_humanoid).HaveEitr(num3)) { return false; } float num4 = Mathf.Max(0f, val.m_attackHealth); if (num4 > 0f && !((Character)_humanoid).HaveHealth(num4) && val.m_attackHealthLowBlockUse) { return false; } return true; } private void PayNextAttackCost(Attack activeAttack) { //IL_00e1: 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_010c: 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) if ((Object)(object)_humanoid == (Object)null || _weapon?.m_shared == null) { return; } Attack secondaryAttack = _weapon.m_shared.m_secondaryAttack; if (secondaryAttack != null) { float num = Mathf.Max(0f, secondaryAttack.m_attackStamina); if (num > 0f) { ((Character)_humanoid).UseStamina(num); } float num2 = Mathf.Max(0f, secondaryAttack.m_attackEitr); if (num2 > 0f) { ((Character)_humanoid).UseEitr(num2); } float num3 = Mathf.Max(0f, secondaryAttack.m_attackHealth); if (num3 > 0f) { ((Character)_humanoid).UseHealth(Mathf.Max(0f, Mathf.Min(((Character)_humanoid).GetHealth() - 1f, num3))); } Transform attackOrigin = activeAttack.GetAttackOrigin(); _weapon.m_shared.m_startEffect.Create(attackOrigin.position, ((Component)_humanoid).transform.rotation, attackOrigin, 1f, -1); activeAttack.m_startEffect.Create(attackOrigin.position, ((Component)_humanoid).transform.rotation, attackOrigin, 1f, -1); ((Character)_humanoid).AddNoise(activeAttack.m_attackStartNoise); } } private void ApplyAnimationSpeed(SpinningSweepDefinition spinningSweep) { if (Mathf.Approximately(spinningSweep.AnimationSpeed, 1f)) { return; } if (_animator == null) { _animator = (((Object)(object)_humanoid != (Object)null) ? ((Component)_humanoid).GetComponentInChildren() : null); } if (_zanim == null) { Humanoid? humanoid = _humanoid; _zanim = ((humanoid != null) ? ((Character)humanoid).GetZAnim() : null); } if (!((Object)(object)_animator == (Object)null)) { if (!_hasOriginalAnimatorSpeed) { _originalAnimatorSpeed = _animator.speed; _hasOriginalAnimatorSpeed = true; } ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(spinningSweep.AnimationSpeed); } _animator.speed = spinningSweep.AnimationSpeed; _speedApplied = true; } } private void RestoreAnimationSpeed() { if (_speedApplied && !((Object)(object)_animator == (Object)null)) { ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(_originalAnimatorSpeed); } _animator.speed = _originalAnimatorSpeed; _speedApplied = false; } } private static void ApplyMovementFactors(Attack attack, SpinningSweepDefinition spinningSweep) { attack.m_speedFactor = spinningSweep.MoveSpeedFactor; attack.m_speedFactorRotation = SpinningSweepSystem.GetRotationSpeedFactor(); } private void ApplyAttackMultipliers(Attack attack, SpinningSweepDefinition spinningSweep) { if (_multiplierAttack != attack) { RestoreAttackMultipliers(); _multiplierAttack = attack; _originalDamageMultiplier = attack.m_damageMultiplier; _originalForceMultiplier = attack.m_forceMultiplier; _hasOriginalAttackMultipliers = true; } attack.m_damageMultiplier = _originalDamageMultiplier * Mathf.Max(0f, spinningSweep.DamageFactor); attack.m_forceMultiplier = _originalForceMultiplier * Mathf.Max(0f, spinningSweep.PushFactor); } private void RestoreAttackMultipliers() { if (_hasOriginalAttackMultipliers && _multiplierAttack != null) { _multiplierAttack.m_damageMultiplier = _originalDamageMultiplier; _multiplierAttack.m_forceMultiplier = _originalForceMultiplier; _multiplierAttack = null; _hasOriginalAttackMultipliers = false; } } private void ApplySkillRaiseFactor(Attack attack, SpinningSweepDefinition spinningSweep) { if (_skillRaiseAttack != attack) { RestoreSkillRaiseFactor(); _skillRaiseAttack = attack; _originalRaiseSkillAmount = attack.m_raiseSkillAmount; _hasOriginalRaiseSkillAmount = true; } attack.m_raiseSkillAmount = _originalRaiseSkillAmount * Mathf.Max(0f, spinningSweep.SkillRaiseFactor); } private void RestoreSkillRaiseFactor() { if (_hasOriginalRaiseSkillAmount && _skillRaiseAttack != null) { _skillRaiseAttack.m_raiseSkillAmount = _originalRaiseSkillAmount; _skillRaiseAttack = null; _hasOriginalRaiseSkillAmount = false; } } private void OnDestroy() { if ((Object)(object)_humanoid != (Object)null && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)_humanoid)) { SweepObserverVisualSystem.SendStop(_humanoid); } SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); RestoreAttackMultipliers(); RestoreSkillRaiseFactor(); RestoreAnimationSpeed(); } } internal static class SweepObserverVisualSystem { private readonly struct SweepObserverVisualPayload { public string Animation { get; } public float LoopStart { get; } public float LoopEnd { get; } public float AnimationSpeed { get; } public SweepObserverVisualPayload(string animation, float loopStart, float loopEnd, float animationSpeed) { Animation = animation; LoopStart = loopStart; LoopEnd = loopEnd; AnimationSpeed = animationSpeed; } } private sealed class SweepObserverVisualController : MonoBehaviour { private static readonly int AttackTagHash = ZSyncAnimation.GetHash("attack"); private Character? _character; private Animator? _animator; private ZSyncAnimation? _zanim; private SweepObserverVisualPayload _payload; private float _expiresAt; private float _originalAnimatorSpeed = 1f; private int _loopStateHash; private bool _hasOriginalAnimatorSpeed; private bool _loopRearmed = true; internal void BeginOrRefresh(Character character, SweepObserverVisualPayload payload, bool triggerAnimation, bool forceLoopSeek, float expiresAt) { _character = character; _payload = payload; _expiresAt = expiresAt; if (_animator == null) { _animator = ((Component)character).GetComponentInChildren(); } if (_zanim == null) { _zanim = ((Component)character).GetComponent(); } if ((Object)(object)_animator == (Object)null) { Stop(); return; } if (!_hasOriginalAnimatorSpeed) { _originalAnimatorSpeed = _animator.speed; _hasOriginalAnimatorSpeed = true; } ApplyAnimationSpeed(); if (triggerAnimation && !string.IsNullOrWhiteSpace(payload.Animation)) { _animator.SetTrigger(payload.Animation); } if (forceLoopSeek) { SeekToLoopStart(); } ((Behaviour)this).enabled = true; } internal void Stop() { Object.Destroy((Object)(object)this); } private void Update() { //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_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_character == (Object)null || _character.IsDead() || SecondaryAttackManager.HasCharacterAuthority(_character) || Time.time > _expiresAt) { Stop(); return; } if (_animator == null) { _animator = ((Component)_character).GetComponentInChildren(); } if ((Object)(object)_animator == (Object)null) { Stop(); return; } ApplyAnimationSpeed(); AnimatorStateInfo attackAnimatorState = GetAttackAnimatorState(_animator); if (!IsAttackState(attackAnimatorState)) { return; } if (_loopStateHash == 0 && ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash != 0) { _loopStateHash = ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash; } if (!_loopRearmed) { if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime < _payload.LoopEnd) { _loopRearmed = true; } } else if (((AnimatorStateInfo)(ref attackAnimatorState)).normalizedTime >= _payload.LoopEnd) { SeekToLoopStart(); _loopRearmed = false; } } private void ApplyAnimationSpeed() { if (!((Object)(object)_animator == (Object)null)) { ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(_payload.AnimationSpeed); } _animator.speed = _payload.AnimationSpeed; } } private void SeekToLoopStart() { //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) if (!((Object)(object)_animator == (Object)null)) { AnimatorStateInfo attackAnimatorState = GetAttackAnimatorState(_animator); int num = ((_loopStateHash != 0) ? _loopStateHash : ((AnimatorStateInfo)(ref attackAnimatorState)).fullPathHash); if (num != 0) { SweepTrailResetSystem.ClearWeaponTrails(_character); _animator.Play(num, 0, _payload.LoopStart); } } } private static AnimatorStateInfo GetAttackAnimatorState(Animator animator) { //IL_001d: 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_0019: Unknown result type (might be due to invalid IL or missing references) if (animator.IsInTransition(0)) { AnimatorStateInfo nextAnimatorStateInfo = animator.GetNextAnimatorStateInfo(0); if (IsAttackState(nextAnimatorStateInfo)) { return nextAnimatorStateInfo; } } return animator.GetCurrentAnimatorStateInfo(0); } private static bool IsAttackState(AnimatorStateInfo state) { if (((AnimatorStateInfo)(ref state)).fullPathHash != 0) { return ((AnimatorStateInfo)(ref state)).tagHash == AttackTagHash; } return false; } private void OnDestroy() { if (_hasOriginalAnimatorSpeed && (Object)(object)_animator != (Object)null) { ZSyncAnimation? zanim = _zanim; if (zanim != null) { zanim.SetSpeed(_originalAnimatorSpeed); } _animator.speed = _originalAnimatorSpeed; } } } internal const string RpcName = "SecondaryAttacks_SweepVisual"; private const int StopAction = 0; private const int StartSpinningSweepAction = 1; private const int StartHarvestSweepAction = 2; private const int RefreshSpinningSweepAction = 3; private const int RefreshHarvestSweepAction = 4; private const float ObserverTimeoutSeconds = 2f; internal static void SendStart(Humanoid humanoid, bool harvestSweep, string animation, float loopStart, float loopEnd, float animationSpeed) { Send(humanoid, (!harvestSweep) ? 1 : 2, animation, loopStart, loopEnd, animationSpeed); } internal static void SendRefresh(Humanoid humanoid, bool harvestSweep, string animation, float loopStart, float loopEnd, float animationSpeed) { Send(humanoid, harvestSweep ? 4 : 3, animation, loopStart, loopEnd, animationSpeed); } internal static void SendStop(Humanoid humanoid) { if (SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)humanoid, out ZNetView nview, out ZDO _) && ZRoutedRpc.instance != null) { nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_SweepVisual", new object[2] { 0, string.Empty }); } } internal static void HandleRpc(Character character, ZNetView? nview, int action, string payload) { if ((Object)(object)character == (Object)null || (Object)(object)nview == (Object)null || !nview.IsValid() || nview.IsOwner()) { return; } SweepObserverVisualController sweepObserverVisualController = ((Component)character).GetComponent(); SweepObserverVisualPayload parsed; if (action == 0) { sweepObserverVisualController?.Stop(); } else if (TryParsePayload(payload, out parsed)) { if (sweepObserverVisualController == null) { sweepObserverVisualController = ((Component)character).gameObject.AddComponent(); } bool flag = (uint)(action - 1) <= 1u; bool triggerAnimation = flag; flag = (uint)(action - 3) <= 1u; bool forceLoopSeek = flag; sweepObserverVisualController.BeginOrRefresh(character, parsed, triggerAnimation, forceLoopSeek, Time.time + 2f); } } private static void Send(Humanoid humanoid, int action, string animation, float loopStart, float loopEnd, float animationSpeed) { if (!((Object)(object)humanoid == (Object)null) && SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)humanoid, out ZNetView nview, out ZDO _) && ZRoutedRpc.instance != null) { nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_SweepVisual", new object[2] { action, CreatePayload(animation, loopStart, loopEnd, animationSpeed) }); } } private static string CreatePayload(string animation, float loopStart, float loopEnd, float animationSpeed) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return string.Join("|", animation ?? string.Empty, Mathf.Clamp01(loopStart).ToString("R", invariantCulture), Mathf.Clamp01(loopEnd).ToString("R", invariantCulture), Mathf.Max(0.01f, animationSpeed).ToString("R", invariantCulture)); } private static bool TryParsePayload(string payload, out SweepObserverVisualPayload parsed) { parsed = default(SweepObserverVisualPayload); string[] array = payload.Split('|'); if (array.Length < 4) { return false; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (!float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result) || !float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[3], NumberStyles.Float, invariantCulture, out var result3)) { return false; } parsed = new SweepObserverVisualPayload(array[0], Mathf.Clamp01(result), Mathf.Clamp(Mathf.Max(result2, result + 0.01f), 0.01f, 1.5f), Mathf.Max(0.01f, result3)); return true; } } internal static class SweepTrailResetSystem { private static readonly FieldInfo? TrailMeshField = AccessTools.Field(typeof(MeleeWeaponTrail), "m_trailMesh"); private static readonly FieldInfo? TrailLastPositionField = AccessTools.Field(typeof(MeleeWeaponTrail), "m_lastPosition"); private static readonly FieldInfo? TrailEmitTimeField = AccessTools.Field(typeof(MeleeWeaponTrail), "_emitTime"); private static readonly FieldInfo?[] TrailListFields = new FieldInfo[8] { AccessTools.Field(typeof(MeleeWeaponTrail), "m_points"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_smoothedPoints"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_smoothBaseList"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_smoothTipList"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_newVertices"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_newUV"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_newColors"), AccessTools.Field(typeof(MeleeWeaponTrail), "m_newTriangles") }; internal static void ClearWeaponTrails(Attack? attack) { MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(attack); for (int i = 0; i < trails.Length; i++) { ClearTrail(trails[i]); } } internal static void ClearWeaponTrails(Character? character) { MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(character); for (int i = 0; i < trails.Length; i++) { ClearTrail(trails[i]); } } private static void ClearTrail(MeleeWeaponTrail trail) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)trail == (Object)null) { return; } FieldInfo[] trailListFields = TrailListFields; for (int i = 0; i < trailListFields.Length; i++) { if (WeaponTrailAccess.GetValue(trailListFields[i], trail) is IList list) { TryClearList(list); } } object? value = WeaponTrailAccess.GetValue(TrailMeshField, trail); Mesh val = (Mesh)((value is Mesh) ? value : null); if (val != null) { TryClearMesh(val); } WeaponTrailAccess.SetValue(TrailEmitTimeField, trail, 0f); WeaponTrailAccess.SetValue(TrailLastPositionField, trail, ResolveCurrentTrailPosition(trail)); } private static Vector3 ResolveCurrentTrailPosition(MeleeWeaponTrail trail) { //IL_0011: 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_002d: Unknown result type (might be due to invalid IL or missing references) Transform tipTransform = WeaponTrailAccess.GetTipTransform(trail); if ((Object)(object)tipTransform != (Object)null) { return tipTransform.position; } Transform baseTransform = WeaponTrailAccess.GetBaseTransform(trail); if (!((Object)(object)baseTransform != (Object)null)) { return ((Component)trail).transform.position; } return baseTransform.position; } private static void TryClearList(IList list) { try { list.Clear(); } catch (Exception) { } } private static void TryClearMesh(Mesh mesh) { try { mesh.Clear(); } catch (Exception) { } } } internal static class WeaponTrailAccess { private static readonly FieldInfo? AttackVisEquipmentField = AccessTools.Field(typeof(Attack), "m_visEquipment"); private static readonly FieldInfo? VisRightItemInstanceField = AccessTools.Field(typeof(VisEquipment), "m_rightItemInstance"); private static readonly FieldInfo? TrailBaseField = AccessTools.Field(typeof(MeleeWeaponTrail), "_base"); private static readonly FieldInfo? TrailTipField = AccessTools.Field(typeof(MeleeWeaponTrail), "_tip"); private static readonly FieldInfo? TrailMaterialField = AccessTools.Field(typeof(MeleeWeaponTrail), "_material"); private static readonly FieldInfo? TrailColorsField = AccessTools.Field(typeof(MeleeWeaponTrail), "_colors"); private static readonly FieldInfo? TrailSizesField = AccessTools.Field(typeof(MeleeWeaponTrail), "_sizes"); private static readonly FieldInfo? TrailLifeTimeField = AccessTools.Field(typeof(MeleeWeaponTrail), "_lifeTime"); private static readonly FieldInfo? TrailSubdivisionsField = AccessTools.Field(typeof(MeleeWeaponTrail), "subdivisions"); private static readonly MeleeWeaponTrail[] NoTrails = Array.Empty(); internal static GameObject? GetRightItemInstance(Attack? attack) { if (attack == null) { return null; } object? value = GetValue(AttackVisEquipmentField, attack); GameObject rightItemInstance = GetRightItemInstance((VisEquipment?)((value is VisEquipment) ? value : null)); if (!((Object)(object)rightItemInstance != (Object)null)) { return GetRightItemInstance((Character?)(object)attack.m_character); } return rightItemInstance; } internal static GameObject? GetRightItemInstance(Character? character) { return GetRightItemInstance(((Object)(object)character != (Object)null) ? ((Component)character).GetComponent() : null); } internal static MeleeWeaponTrail[] GetTrails(Attack? attack) { return GetTrails(GetRightItemInstance(attack)); } internal static MeleeWeaponTrail[] GetTrails(Character? character) { return GetTrails(GetRightItemInstance(character)); } internal static MeleeWeaponTrail[] GetTrails(GameObject? rightItemInstance) { if ((Object)(object)rightItemInstance == (Object)null) { return NoTrails; } try { return rightItemInstance.GetComponentsInChildren(true) ?? NoTrails; } catch (Exception) { return NoTrails; } } internal static bool TryGetEndpoints(MeleeWeaponTrail? trail, out Transform baseTransform, out Transform tipTransform) { baseTransform = GetBaseTransform(trail); tipTransform = GetTipTransform(trail); if ((Object)(object)baseTransform != (Object)null) { return (Object)(object)tipTransform != (Object)null; } return false; } internal static Transform? GetBaseTransform(MeleeWeaponTrail? trail) { object? value = GetValue(TrailBaseField, trail); return (Transform?)((value is Transform) ? value : null); } internal static Transform? GetTipTransform(MeleeWeaponTrail? trail) { object? value = GetValue(TrailTipField, trail); return (Transform?)((value is Transform) ? value : null); } internal static Material? GetMaterial(MeleeWeaponTrail? trail) { object? value = GetValue(TrailMaterialField, trail); return (Material?)((value is Material) ? value : null); } internal static Color[]? GetColors(MeleeWeaponTrail? trail) { return GetValue(TrailColorsField, trail) as Color[]; } internal static float[]? GetSizes(MeleeWeaponTrail? trail) { return GetValue(TrailSizesField, trail) as float[]; } internal static float GetLifeTime(MeleeWeaponTrail? trail, float fallback) { object value = GetValue(TrailLifeTimeField, trail); if (value is float) { return (float)value; } return fallback; } internal static int GetSubdivisions(MeleeWeaponTrail? trail, int fallback) { object value = GetValue(TrailSubdivisionsField, trail); if (value is int) { return (int)value; } return fallback; } internal static object? GetValue(FieldInfo? field, object? instance) { if (field == null || instance == null) { return null; } try { return field.GetValue(instance); } catch (Exception) { return null; } } internal static bool SetValue(FieldInfo? field, object? instance, object? value) { if (field == null || instance == null) { return false; } try { field.SetValue(instance, value); return true; } catch (Exception) { return false; } } private static GameObject? GetRightItemInstance(VisEquipment? visEquipment) { object? value = GetValue(VisRightItemInstanceField, visEquipment); return (GameObject?)((value is GameObject) ? value : null); } } internal static class SummonQualityHudCompatibility { private const string CreatureManagerLevelContentName = "CreatureManager_LevelContent"; private const string CreatureManagerBossContentName = "CreatureManager_BossLevelContent"; private const string CreatureManagerStarGroupName = "CreatureManager_StarGroup"; private const string CreatureLevelControlLevelPrefix = "level_"; private const string StarLevelSystemHighLevelName = "SLS_level_n"; private static readonly string[] StarLevelSystemLevelNames = new string[5] { "SLS_level_2", "SLS_level_3", "SLS_level_4", "SLS_level_5", "SLS_level_6" }; private static bool _initialized; private static bool _creatureManagerLoaded; private static bool _creatureLevelControlLoaded; private static bool _starLevelSystemLoaded; internal static void Initialize() { if (!_initialized) { _initialized = true; _creatureManagerLoaded = Chainloader.PluginInfos.ContainsKey("sighsorry.CreatureManager"); _creatureLevelControlLoaded = Chainloader.PluginInfos.ContainsKey("org.bepinex.plugins.creaturelevelcontrol"); _starLevelSystemLoaded = Chainloader.PluginInfos.ContainsKey("MidnightsFX.StarLevelSystem"); List list = new List(); if (_creatureManagerLoaded) { list.Add("CreatureManager"); } if (_creatureLevelControlLoaded) { list.Add("Creature Level & Loot Control"); } if (_starLevelSystemLoaded) { list.Add("StarLevelSystem"); } if (list.Count > 0) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)("Automatic summon star HUD compatibility enabled for: " + string.Join(", ", list) + ".")); } } } internal static bool HasActiveExternalStarHud(HudData hudData) { Initialize(); if ((Object)(object)hudData?.m_gui == (Object)null) { return false; } Transform transform = hudData.m_gui.transform; if ((!_creatureManagerLoaded || !HasActiveCreatureManagerStarHud(hudData)) && (!_creatureLevelControlLoaded || !HasActiveCreatureLevelControlStarHud(transform))) { if (_starLevelSystemLoaded) { return HasActiveStarLevelSystemStarHud(transform); } return false; } return true; } private static bool HasActiveCreatureManagerStarHud(HudData hudData) { Transform creatureManagerHudContentParent = GetCreatureManagerHudContentParent(hudData); if (!HasActiveCreatureManagerStarGroup(creatureManagerHudContentParent, "CreatureManager_LevelContent")) { return HasActiveCreatureManagerStarGroup(creatureManagerHudContentParent, "CreatureManager_BossLevelContent"); } return true; } private static bool HasActiveCreatureManagerStarGroup(Transform parent, string contentName) { Transform val = parent.Find(contentName); if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { return false; } Transform val2 = val.Find("CreatureManager_StarGroup"); if ((Object)(object)val2 != (Object)null) { return ((Component)val2).gameObject.activeSelf; } return false; } private static Transform GetCreatureManagerHudContentParent(HudData hudData) { object obj = (object)(((Object)(object)hudData.m_healthFast != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); Transform obj2 = ((obj != null) ? ((Transform)obj).parent : null); RectTransform val = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if (val != null) { Transform parent = ((Transform)val).parent; RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val2 != null) { return (Transform)(object)val2; } } RectTransform level = hudData.m_level2; Transform obj3 = ((level != null) ? ((Transform)level).parent : null); RectTransform val3 = (RectTransform)(object)((obj3 is RectTransform) ? obj3 : null); if (val3 != null) { return (Transform)(object)val3; } RectTransform level2 = hudData.m_level3; Transform obj4 = ((level2 != null) ? ((Transform)level2).parent : null); RectTransform val4 = (RectTransform)(object)((obj4 is RectTransform) ? obj4 : null); if (val4 != null) { return (Transform)(object)val4; } TextMeshProUGUI name = hudData.m_name; Transform obj5 = ((name != null) ? ((Transform)((TMP_Text)name).rectTransform).parent : null); RectTransform val5 = (RectTransform)(object)((obj5 is RectTransform) ? obj5 : null); if (val5 != null) { return (Transform)(object)val5; } return hudData.m_gui.transform; } private static bool HasActiveCreatureLevelControlStarHud(Transform hudRoot) { for (int i = 0; i < hudRoot.childCount; i++) { Transform child = hudRoot.GetChild(i); if (((Component)child).gameObject.activeSelf && IsCreatureLevelControlLevelGroup(((Object)child).name) && (Object)(object)((Component)child).GetComponent() == (Object)null) { return true; } } return false; } private static bool IsCreatureLevelControlLevelGroup(string name) { if (!name.StartsWith("level_", StringComparison.Ordinal) || name.Length == "level_".Length) { return false; } for (int i = "level_".Length; i < name.Length; i++) { if (!char.IsDigit(name[i])) { return false; } } return true; } private static bool HasActiveStarLevelSystemStarHud(Transform hudRoot) { if (IsSelfActive(hudRoot.Find("SLS_level_n"))) { return true; } string[] starLevelSystemLevelNames = StarLevelSystemLevelNames; foreach (string text in starLevelSystemLevelNames) { if (IsSelfActive(hudRoot.Find(text))) { return true; } } return false; } private static bool IsSelfActive(Transform? transform) { if ((Object)(object)transform != (Object)null) { return ((Component)transform).gameObject.activeSelf; } return false; } } internal sealed class SummonQualityHudMarker : MonoBehaviour { } internal static class SummonQualityHudSystem { private sealed class HudLevelGroup { internal int Level { get; } internal GameObject Group { get; } internal Transform GuiTransform { get; } internal HudLevelGroup(int level, GameObject group, Transform guiTransform) { Level = level; Group = group; GuiTransform = guiTransform; } } private sealed class CachedTag { internal Character Character { get; } internal SummonQualityPresetTag? Tag { get; } internal int LastCheckedFrame { get; } internal CachedTag(Character character, SummonQualityPresetTag? tag, int lastCheckedFrame) { Character = character; Tag = tag; LastCheckedFrame = lastCheckedFrame; } } private const int MaxExtendedStars = 9; private const float StarSpacing = 16f; private const int MissingTagRefreshFrames = 60; private static readonly Dictionary ActiveGroups = new Dictionary(); private static readonly Dictionary TagCache = new Dictionary(); private static readonly HashSet RemoveBuffer = new HashSet(); private static readonly HashSet VisibleCharacters = new HashSet(); private static readonly List TagRemoveBuffer = new List(); internal static void Update(EnemyHud enemyHud) { if (enemyHud?.m_huds == null) { HideAllOwnedGroups(); return; } RemoveBuffer.Clear(); VisibleCharacters.Clear(); foreach (int key in ActiveGroups.Keys) { RemoveBuffer.Add(key); } foreach (var (val3, val4) in enemyHud.m_huds) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val4?.m_gui == (Object)null)) { int instanceID = ((Object)val3).GetInstanceID(); VisibleCharacters.Add(instanceID); RemoveBuffer.Remove(instanceID); UpdateHud(val3, val4, instanceID); } } foreach (int item in RemoveBuffer) { DestroyOwnedGroup(item); } PruneTagCache(); } private static void UpdateHud(Character character, HudData hudData, int instanceId) { SummonQualityPresetTag summonQualityPresetTag = ResolveCachedTag(character, instanceId); int num = (((Object)(object)summonQualityPresetTag != (Object)null && summonQualityPresetTag.UsesLevelByQuality) ? Mathf.Max(character.GetLevel(), summonQualityPresetTag.SummonLevel) : character.GetLevel()); int num2 = Mathf.Clamp(num - 1, 0, 9); RectTransform level = hudData.m_level3; if ((Object)(object)summonQualityPresetTag == (Object)null || !summonQualityPresetTag.UsesLevelByQuality || num2 <= 2 || (Object)(object)level == (Object)null) { DestroyOwnedGroup(instanceId); return; } if (SummonQualityHudCompatibility.HasActiveExternalStarHud(hudData)) { DestroyOwnedGroup(instanceId); return; } GameObject orCreateLevelGroup = GetOrCreateLevelGroup(instanceId, num, num2, hudData, level); if ((Object)(object)orCreateLevelGroup == (Object)null) { DestroyOwnedGroup(instanceId); return; } if ((Object)(object)hudData.m_level2 != (Object)null) { ((Component)hudData.m_level2).gameObject.SetActive(false); } ((Component)level).gameObject.SetActive(false); orCreateLevelGroup.SetActive(true); } private static SummonQualityPresetTag? ResolveCachedTag(Character character, int instanceId) { if (TagCache.TryGetValue(instanceId, out CachedTag value) && (Object)(object)value.Character == (Object)(object)character && ((Object)(object)value.Tag != (Object)null || Time.frameCount - value.LastCheckedFrame < 60)) { return value.Tag; } SummonQualityPresetTag component = ((Component)character).GetComponent(); TagCache[instanceId] = new CachedTag(character, component, Time.frameCount); return component; } private static void PruneTagCache() { TagRemoveBuffer.Clear(); foreach (int key in TagCache.Keys) { if (!VisibleCharacters.Contains(key)) { TagRemoveBuffer.Add(key); } } foreach (int item in TagRemoveBuffer) { TagCache.Remove(item); } TagRemoveBuffer.Clear(); VisibleCharacters.Clear(); } private static GameObject? GetOrCreateLevelGroup(int instanceId, int level, int starCount, HudData hudData, RectTransform level3) { Transform transform = hudData.m_gui.transform; if (ActiveGroups.TryGetValue(instanceId, out HudLevelGroup value) && value.Level == level && (Object)(object)value.GuiTransform == (Object)(object)transform && (Object)(object)value.Group != (Object)null) { return value.Group; } DestroyOwnedGroup(instanceId); string text = $"SecondaryAttacks_level_{level}"; Transform val = transform.Find(text); if ((Object)(object)val != (Object)null) { if ((Object)(object)((Component)val).GetComponent() == (Object)null) { return null; } ActiveGroups[instanceId] = new HudLevelGroup(level, ((Component)val).gameObject, transform); return ((Component)val).gameObject; } GameObject val2 = Object.Instantiate(((Component)level3).gameObject, transform); ((Object)val2).name = text; val2.AddComponent(); val2.SetActive(false); if (!TryAddExtraStars(val2.transform, starCount)) { Object.Destroy((Object)(object)val2); return null; } ActiveGroups[instanceId] = new HudLevelGroup(level, val2, transform); return val2; } private static bool TryAddExtraStars(Transform levelGroup, int starCount) { //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_0051: 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) Transform val = FindStarTemplate(levelGroup); if ((Object)(object)val == (Object)null) { return false; } for (int i = 3; i <= starCount; i++) { Transform transform = Object.Instantiate(((Component)val).gameObject, levelGroup).transform; ((Object)transform).name = $"star_{i}"; transform.localPosition = GetExtraStarPosition(val.localPosition, i); transform.localRotation = val.localRotation; transform.localScale = val.localScale; } return true; } private static Transform? FindStarTemplate(Transform levelGroup) { Transform val = levelGroup.Find("star"); if ((Object)(object)val != (Object)null) { return val; } for (int i = 0; i < levelGroup.childCount; i++) { Transform child = levelGroup.GetChild(i); if (((Object)child).name.StartsWith("star")) { return child; } } return null; } private static Vector3 GetExtraStarPosition(Vector3 templatePosition, int starNumber) { //IL_001e: 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) int num = starNumber - 1; return new Vector3(16f * (float)(num % 5) - 8f, (float)(num / 5) * -16f, templatePosition.z); } private static void DestroyOwnedGroup(int instanceId) { if (ActiveGroups.TryGetValue(instanceId, out HudLevelGroup value)) { if ((Object)(object)value.Group != (Object)null) { value.Group.SetActive(false); Object.Destroy((Object)(object)value.Group); } ActiveGroups.Remove(instanceId); } } private static void HideAllOwnedGroups() { foreach (HudLevelGroup value in ActiveGroups.Values) { if ((Object)(object)value.Group != (Object)null) { value.Group.SetActive(false); } } } } internal static class SweepHitStopSystem { private static bool HasSuppression(Character? character) { if ((Object)(object)character == (Object)null) { return false; } SpinningSweepController component = ((Component)character).GetComponent(); if (component != null && component.SuppressesHitStop) { return true; } HarvestSweepController component2 = ((Component)character).GetComponent(); if (component2 != null && component2.SuppressesHitStop) { return true; } return false; } internal static bool ShouldSuppress(Character? character, float duration) { if (duration > 0f) { return HasSuppression(character); } return false; } internal static bool TryGetAnimationSpeed(Character? character, out float speed) { speed = 1f; if ((Object)(object)character == (Object)null) { return false; } SpinningSweepController component = ((Component)character).GetComponent(); if (component != null && component.TryGetAnimationSpeed(out speed)) { return true; } return ((Component)character).GetComponent()?.TryGetAnimationSpeed(out speed) ?? false; } internal static void ApplyAnimationSpeed(CharacterAnimEvent? animEvent) { if (!((Object)(object)animEvent?.m_animator == (Object)null) && TryGetAnimationSpeed(animEvent.m_character, out var speed)) { animEvent.m_animator.speed = speed; } } } [HarmonyPatch(typeof(Character), "FreezeFrame")] internal static class CharacterFreezeFrameSweepHitStopPatch { private static bool Prefix(Character __instance, float duration) { return !SweepHitStopSystem.ShouldSuppress(__instance, duration); } } [HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")] internal static class CharacterAnimEventCustomFixedUpdateSweepAnimationSpeedPatch { [HarmonyPriority(0)] private static void Prefix(CharacterAnimEvent __instance) { SweepHitStopSystem.ApplyAnimationSpeed(__instance); } [HarmonyPriority(0)] private static void Postfix(CharacterAnimEvent __instance) { SweepHitStopSystem.ApplyAnimationSpeed(__instance); } } internal sealed class ThrowProjectileVisualSpin : MonoBehaviour { internal enum AxisMode { None, HorizontalSide, WorldUp } private const float DegreesPerSecond = 720f; private const float ForwardEpsilonSqr = 0.0001f; private AxisMode _axisMode = AxisMode.HorizontalSide; private Vector3 _horizontalForward; private bool _hasHorizontalForward; private void LateUpdate() { //IL_001a: 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_001f: 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 (_axisMode != AxisMode.None) { Vector3 val = ((_axisMode == AxisMode.WorldUp) ? Vector3.up : ResolveHorizontalSideAxis()); ((Component)this).transform.Rotate(val, 720f * Time.deltaTime, (Space)0); } } private Vector3 ResolveHorizontalSideAxis() { //IL_0009: 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_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_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_005b: 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_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_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_0079: Unknown result type (might be due to invalid IL or missing references) if (_hasHorizontalForward) { return ResolveSideAxis(_horizontalForward); } Transform val = (((Object)(object)((Component)this).transform.parent != (Object)null) ? ((Component)this).transform.parent : ((Component)this).transform); Vector3 forward = Vector3.ProjectOnPlane(val.forward, Vector3.up); if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { forward = Vector3.ProjectOnPlane(val.right, Vector3.up); } if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { return Vector3.right; } return ResolveSideAxis(forward); } private static Vector3 ResolveSideAxis(Vector3 forward) { //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_000b: 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_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_0032: 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_0049: 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) forward = Vector3.ProjectOnPlane(forward, Vector3.up); if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { return Vector3.right; } Vector3 val = Vector3.Cross(Vector3.up, ((Vector3)(ref forward)).normalized); if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.right; } return ((Vector3)(ref val)).normalized; } internal static void Ensure(GameObject? visual, AxisMode axisMode = AxisMode.HorizontalSide, Vector3 horizontalForward = default(Vector3)) { //IL_000c: 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_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) if ((Object)(object)visual == (Object)null || IsConfigured(visual, axisMode, horizontalForward)) { return; } if (axisMode == AxisMode.None) { ThrowProjectileVisualSpin component = visual.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } else { ThrowProjectileVisualSpin throwProjectileVisualSpin = visual.GetComponent() ?? visual.AddComponent(); throwProjectileVisualSpin._axisMode = axisMode; throwProjectileVisualSpin._horizontalForward = Vector3.ProjectOnPlane(horizontalForward, Vector3.up); throwProjectileVisualSpin._hasHorizontalForward = axisMode == AxisMode.HorizontalSide && ((Vector3)(ref throwProjectileVisualSpin._horizontalForward)).sqrMagnitude > 0.001f; } } internal static bool IsConfigured(GameObject? visual, AxisMode axisMode = AxisMode.HorizontalSide, Vector3 horizontalForward = default(Vector3)) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)visual == (Object)null) { return false; } ThrowProjectileVisualSpin component = visual.GetComponent(); if (axisMode == AxisMode.None) { return (Object)(object)component == (Object)null; } if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled) { return component.Matches(axisMode, horizontalForward); } return false; } private bool Matches(AxisMode axisMode, Vector3 horizontalForward) { //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_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_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_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) if (_axisMode != axisMode) { return false; } Vector3 val = Vector3.ProjectOnPlane(horizontalForward, Vector3.up); bool flag = axisMode == AxisMode.HorizontalSide && ((Vector3)(ref val)).sqrMagnitude > 0.001f; if (_hasHorizontalForward != flag) { return false; } if (flag) { Vector3 val2 = _horizontalForward - val; return ((Vector3)(ref val2)).sqrMagnitude <= 0.0001f; } return true; } } internal static class ProjectileSpinAxis { internal const string None = "none"; internal const string Horizontal = "horizontal"; internal const string Vertical = "vertical"; internal static string Normalize(string? raw) { string text = raw?.Trim() ?? ""; if (text.Length == 0) { return ""; } if (text.Equals("none", StringComparison.OrdinalIgnoreCase)) { return "none"; } if (text.Equals("horizontal", StringComparison.OrdinalIgnoreCase)) { return "horizontal"; } if (!text.Equals("vertical", StringComparison.OrdinalIgnoreCase)) { return ""; } return "vertical"; } internal static bool TryResolveAxisMode(string? raw, out ThrowProjectileVisualSpin.AxisMode axisMode) { string text = Normalize(raw); axisMode = ThrowProjectileVisualSpin.AxisMode.None; return text switch { "none" => Set(ThrowProjectileVisualSpin.AxisMode.None, out axisMode), "horizontal" => Set(ThrowProjectileVisualSpin.AxisMode.HorizontalSide, out axisMode), "vertical" => Set(ThrowProjectileVisualSpin.AxisMode.WorldUp, out axisMode), _ => false, }; } private static bool Set(ThrowProjectileVisualSpin.AxisMode value, out ThrowProjectileVisualSpin.AxisMode axisMode) { axisMode = value; return true; } } internal sealed class ThrowProjectileVisualRotationOffset : MonoBehaviour { private const float OffsetEpsilonSqr = 0.0001f; private bool _hasBaseRotation; private Quaternion _baseLocalRotation; private Vector3 _offset; internal static void Ensure(GameObject? visual, Vector3 offset) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)visual == (Object)null)) { (visual.GetComponent() ?? visual.AddComponent()).Apply(offset); } } private void Apply(Vector3 offset) { //IL_0008: 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_000f: 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_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_0052: 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_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) if (_hasBaseRotation) { Vector3 val = offset - _offset; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { return; } } if (!_hasBaseRotation) { _baseLocalRotation = ((Component)this).transform.localRotation; _hasBaseRotation = true; } _offset = offset; ((Component)this).transform.localRotation = _baseLocalRotation * Quaternion.Euler(offset); } } internal static class OverheadStatusUiManager { private readonly struct StatusTextSnapshot { internal bool HasEmpower { get; } internal int EmpowerSeconds { get; } internal bool HasShield { get; } internal int ShieldSeconds { get; } internal bool HasText { get { if (!HasEmpower) { return HasShield; } return true; } } internal int LineCount => (HasEmpower ? 1 : 0) + (HasShield ? 1 : 0); internal StatusTextSnapshot(bool hasEmpower, int empowerSeconds, bool hasShield, int shieldSeconds) { HasEmpower = hasEmpower; EmpowerSeconds = empowerSeconds; HasShield = hasShield; ShieldSeconds = shieldSeconds; } internal bool Equals(StatusTextSnapshot other) { if (HasEmpower == other.HasEmpower && EmpowerSeconds == other.EmpowerSeconds && HasShield == other.HasShield) { return ShieldSeconds == other.ShieldSeconds; } return false; } } private sealed class StatusTextState { private StatusTextSnapshot _snapshot; private bool _hasSnapshot; private string _text = string.Empty; internal string ResolveText(StatusTextSnapshot snapshot) { if (!_hasSnapshot || !_snapshot.Equals(snapshot)) { _snapshot = snapshot; _text = BuildStatusText(snapshot); _hasSnapshot = true; } return _text; } } private const string CharacterHudTextName = "SecondaryAttacks_StatusText"; private const float NameGap = 2f; private static readonly Dictionary ActiveTexts = new Dictionary(); private static readonly Dictionary TrackedCharacters = new Dictionary(); private static readonly Dictionary StatusStates = new Dictionary(); private static readonly List RemoveBuffer = new List(); internal static void RefreshTrackedCharacter(Character? character) { if (!((Object)(object)character == (Object)null)) { int instanceID = ((Object)character).GetInstanceID(); if (HasDisplayStatus(character)) { TrackedCharacters[instanceID] = character; } else { RemoveTrackedCharacter(instanceID); } } } internal static void Update(EnemyHud enemyHud) { if ((Object)(object)enemyHud?.m_hudRoot == (Object)null) { return; } RemoveBuffer.Clear(); foreach (var (num2, val2) in TrackedCharacters) { if ((Object)(object)val2 == (Object)null || val2.IsDead() || !TryBuildStatusSnapshot(val2, out var snapshot)) { RemoveBuffer.Add(num2); continue; } TextMeshProUGUI orCreateStatusText = GetOrCreateStatusText(enemyHud, num2); string text = GetOrCreateStatusState(num2).ResolveText(snapshot); UpdateStatusText(enemyHud, val2, orCreateStatusText, text, snapshot.LineCount); } foreach (int item in RemoveBuffer) { RemoveTrackedCharacter(item); } } private static bool TryBuildStatusSnapshot(Character character, out StatusTextSnapshot snapshot) { float moveSpeedFactor; float attackSpeedFactor; float remainingTime; bool flag = StaffRuntimeSystem.TryGetSummonEmpower(character, out moveSpeedFactor, out attackSpeedFactor, out remainingTime); float remaining; bool flag2 = StaffRuntimeSystem.TryGetDisplayedShieldRemaining(character, out remaining); snapshot = new StatusTextSnapshot(flag, flag ? Mathf.CeilToInt(remainingTime) : 0, flag2, flag2 ? Mathf.CeilToInt(remaining) : 0); return snapshot.HasText; } private static TextMeshProUGUI GetOrCreateStatusText(EnemyHud enemyHud, int characterInstanceId) { //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_003b: 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_00e1: 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_010b: 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_012a: 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) if (ActiveTexts.TryGetValue(characterInstanceId, out TextMeshProUGUI value) && (Object)(object)value != (Object)null) { return value; } GameObject val = new GameObject(string.Format("{0}_{1}", "SecondaryAttacks_StatusText", characterInstanceId)); val.SetActive(false); val.transform.SetParent(enemyHud.m_hudRoot.transform, false); TextMeshProUGUI val2 = val.AddComponent(); TextMeshProUGUI component = ((Component)enemyHud.m_baseHudPlayer.transform.Find("Name")).GetComponent(); ((TMP_Text)val2).font = ((TMP_Text)component).font; ((TMP_Text)val2).fontSharedMaterial = ((TMP_Text)component).fontSharedMaterial; ((TMP_Text)val2).fontSize = ((TMP_Text)component).fontSize * 0.72f; ((Graphic)val2).color = ((Graphic)component).color; ((TMP_Text)val2).alignment = (TextAlignmentOptions)1026; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; ((TMP_Text)val2).richText = false; ((Graphic)val2).raycastTarget = false; RectTransform rectTransform = ((TMP_Text)val2).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 0f); rectTransform.pivot = new Vector2(0.5f, 0f); rectTransform.sizeDelta = ((TMP_Text)component).rectTransform.sizeDelta + new Vector2(0f, 18f); ActiveTexts[characterInstanceId] = val2; return val2; } private static bool HasDisplayStatus(Character character) { float attackSpeedFactor; float remainingTime; if (!StaffRuntimeSystem.TryGetDisplayedShieldRemaining(character, out var remaining)) { return StaffRuntimeSystem.TryGetSummonEmpower(character, out remaining, out attackSpeedFactor, out remainingTime); } return true; } private static void UpdateStatusText(EnemyHud enemyHud, Character character, TextMeshProUGUI statusText, string text, int lineCount) { //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_00b5: 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_00e3: 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_010b: 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) if (!enemyHud.m_huds.TryGetValue(character, out var value) || (Object)(object)value?.m_gui == (Object)null || (Object)(object)value.m_name == (Object)null) { UpdateFallbackStatusText(enemyHud, character, statusText, text, lineCount); return; } if (!value.m_gui.activeInHierarchy) { ((Component)statusText).gameObject.SetActive(false); return; } RectTransform rectTransform = ((TMP_Text)value.m_name).rectTransform; RectTransform rectTransform2 = ((TMP_Text)statusText).rectTransform; float num = Mathf.Max(14f, ((TMP_Text)statusText).fontSize + 2f); EnsureStatusParent(statusText, value.m_gui.transform); rectTransform2.anchorMin = rectTransform.anchorMin; rectTransform2.anchorMax = rectTransform.anchorMax; rectTransform2.pivot = new Vector2(0.5f, 0f); Rect rect = rectTransform.rect; rectTransform2.sizeDelta = new Vector2(Mathf.Max(((Rect)(ref rect)).width, 120f), num * (float)Mathf.Max(1, lineCount)); SetStatusText(statusText, text); rectTransform2.anchoredPosition = rectTransform.anchoredPosition + new Vector2(0f, GetNameTopOffset(rectTransform) + 2f); ((Component)statusText).gameObject.SetActive(true); } private static void UpdateFallbackStatusText(EnemyHud enemyHud, Character character, TextMeshProUGUI statusText, string text, int lineCount) { //IL_006e: 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_0098: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d8: 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_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_012f: 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_0106: 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) if ((Object)(object)character != (Object)(object)Player.m_localPlayer) { ((Component)statusText).gameObject.SetActive(false); return; } Camera mainCamera = Utils.GetMainCamera(); if (!Object.op_Implicit((Object)(object)mainCamera)) { ((Component)statusText).gameObject.SetActive(false); return; } EnsureStatusParent(statusText, enemyHud.m_hudRoot.transform); RectTransform rectTransform = ((TMP_Text)statusText).rectTransform; float num = Mathf.Max(14f, ((TMP_Text)statusText).fontSize + 2f); rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 0f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.sizeDelta = new Vector2(120f, num * (float)Mathf.Max(1, lineCount)); Vector3 val = Utils.WorldToScreenPointScaled(mainCamera, character.GetHeadPoint() + Vector3.up * 0.35f); bool active = val.z > 0f && val.x >= 0f && val.x <= (float)Screen.width && val.y >= 0f && val.y <= (float)Screen.height; SetStatusText(statusText, text); ((Transform)rectTransform).position = val; ((Component)statusText).gameObject.SetActive(active); } private static void SetStatusText(TextMeshProUGUI statusText, string text) { if (!string.Equals(((TMP_Text)statusText).text, text, StringComparison.Ordinal)) { ((TMP_Text)statusText).text = text; } } private static void EnsureStatusParent(TextMeshProUGUI statusText, Transform parent) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)((TMP_Text)statusText).transform.parent == (Object)(object)parent)) { ((Transform)((TMP_Text)statusText).rectTransform).SetParent(parent, false); ((Transform)((TMP_Text)statusText).rectTransform).localScale = Vector3.one; } } private static float GetNameTopOffset(RectTransform nameRect) { //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) //IL_001b: 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) Rect rect = nameRect.rect; float num; if (!(((Rect)(ref rect)).height > 0f)) { num = Mathf.Max(14f, nameRect.sizeDelta.y); } else { rect = nameRect.rect; num = ((Rect)(ref rect)).height; } return num * (1f - nameRect.pivot.y); } private static void RemoveTrackedCharacter(int instanceId) { TrackedCharacters.Remove(instanceId); StatusStates.Remove(instanceId); if (ActiveTexts.TryGetValue(instanceId, out TextMeshProUGUI value)) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } ActiveTexts.Remove(instanceId); } } private static StatusTextState GetOrCreateStatusState(int instanceId) { if (!StatusStates.TryGetValue(instanceId, out StatusTextState value)) { value = new StatusTextState(); StatusStates[instanceId] = value; } return value; } private static string BuildStatusText(StatusTextSnapshot snapshot) { string arg = SecondaryAttackLocalization.Localize("$sa_hud_empower", "Empower"); if (snapshot.HasEmpower && snapshot.HasShield) { return $"{arg} {snapshot.EmpowerSeconds}s\n{snapshot.ShieldSeconds}"; } if (snapshot.HasEmpower) { return $"{arg} {snapshot.EmpowerSeconds}s"; } return snapshot.ShieldSeconds.ToString(); } } internal static class ProjectileRuntimeSystem { internal readonly struct ProjectileLaunchData { public static readonly ProjectileLaunchData Invalid = new ProjectileLaunchData(null, null, 0f, 0f, 0f, 0f, 0f, 0f, 1f, 1f, 1f, useRandomVelocity: false); public GameObject? ProjectilePrefab { get; } public ItemData? AmmoItem { get; } public float ProjectileVelocity { get; } public float ProjectileVelocityMin { get; } public float ProjectileAccuracy { get; } public float ProjectileAccuracyMin { get; } public float AttackHitNoise { get; } public float DamageFactor { get; } public float ConfiguredDamageFactor { get; } public float ConfiguredSkillRaiseFactor { get; } public float ConfiguredAdrenalineFactor { get; } public bool UseRandomVelocity { get; } public bool IsValid => (Object)(object)ProjectilePrefab != (Object)null; public ProjectileLaunchData(GameObject? projectilePrefab, ItemData? ammoItem, float projectileVelocity, float projectileVelocityMin, float projectileAccuracy, float projectileAccuracyMin, float attackHitNoise, float damageFactor, float configuredDamageFactor, float configuredSkillRaiseFactor, float configuredAdrenalineFactor, bool useRandomVelocity) { ProjectilePrefab = projectilePrefab; AmmoItem = ammoItem; ProjectileVelocity = projectileVelocity; ProjectileVelocityMin = projectileVelocityMin; ProjectileAccuracy = projectileAccuracy; ProjectileAccuracyMin = projectileAccuracyMin; AttackHitNoise = attackHitNoise; DamageFactor = damageFactor; ConfiguredDamageFactor = configuredDamageFactor; ConfiguredSkillRaiseFactor = configuredSkillRaiseFactor; ConfiguredAdrenalineFactor = configuredAdrenalineFactor; UseRandomVelocity = useRandomVelocity; } } private sealed class ScatterRicochetState { public Attack Attack { get; } public ProjectileLaunchData LaunchData { get; } public ProjectileSecondaryBehavior Behavior { get; } public float Speed { get; } public ScatterRicochetState(Attack attack, ProjectileLaunchData launchData, ProjectileSecondaryBehavior behavior, float speed) { Attack = attack; LaunchData = launchData; Behavior = behavior; Speed = speed; } } internal readonly struct ScatterRicochetDamageScope { public bool Active { get; } public Projectile? Projectile { get; } public DamageTypes OriginalDamage { get; } public ScatterRicochetDamageScope(Projectile projectile, DamageTypes originalDamage) { //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) Active = true; Projectile = projectile; OriginalDamage = originalDamage; } } private sealed class ScatterRicochetSplitState { public ProjectileSecondaryBehavior Behavior { get; } public ScatterRicochetSplitState(ProjectileSecondaryBehavior behavior) { Behavior = behavior; } } private sealed class PiercingShotState { private readonly Dictionary _lastHitTimes = new Dictionary(); public ProjectileSecondaryBehavior Behavior { get; } public int HitCount { get; private set; } public PiercingShotState(ProjectileSecondaryBehavior behavior) { Behavior = behavior; } public bool IsOnHitCooldown(Character character) { if (_lastHitTimes.TryGetValue(character, out var value)) { return Time.time - value < 0.25f; } return false; } public void RegisterHit(Character character) { _lastHitTimes[character] = Time.time; HitCount++; } } private enum ScheduledBurstMode { Barrage, Volley, Meteor, Spiral } private sealed class BurstFireController : MonoBehaviour { private Attack _attack; private SecondaryAttackDefinition _definition; private Character? _owner; private float _interval; private float _nextShotAt; private int _remainingShots; private bool _reloadConsumed; public void Initialize(Attack attack, SecondaryAttackDefinition definition, int remainingShots) { _attack = attack; _definition = definition; _owner = (Character?)(object)attack.m_character; _remainingShots = Mathf.Max(0, remainingShots); _interval = ((definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) ? Mathf.Max(0.01f, projectileSecondaryBehavior.Interval) : 0.2f); _nextShotAt = Time.time + _interval; ActiveBurstFireControllers.Add(_attack); } private void OnDestroy() { ConsumeReloadIfNeeded(); if (_attack != null) { ActiveBurstFireControllers.Remove(_attack); } } private void Update() { if (_remainingShots <= 0) { Object.Destroy((Object)(object)((Component)this).gameObject); } else if (!CanContinue()) { Object.Destroy((Object)(object)((Component)this).gameObject); } else { if (Time.time < _nextShotAt) { return; } _nextShotAt = Time.time + _interval; _remainingShots--; ReplayFireAnimation(); if (!SecondaryAttackRuntimeFacade.TryBeginBurstFireRepeat(_attack)) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } bool num = FireSingleBurstFireShot(_attack, _definition); SecondaryAttackRuntimeFacade.CompleteBurstFireRepeat(_attack); if (!num || _remainingShots <= 0) { Object.Destroy((Object)(object)((Component)this).gameObject); } } } private bool CanContinue() { if (_attack == null || (Object)(object)_owner == (Object)null || _owner.IsDead() || _owner.IsStaggering() || _attack.m_weapon == null) { return false; } Character? owner = _owner; Humanoid val = (Humanoid)(object)((owner is Humanoid) ? owner : null); if (val == null) { return true; } if (val.GetCurrentWeapon() != _attack.m_weapon) { return false; } return val.m_currentAttack == _attack; } private void ReplayFireAnimation() { if (!((Object)(object)_owner == (Object)null) && !string.IsNullOrWhiteSpace(_attack.m_attackAnimation)) { ZSyncAnimation zAnim = _owner.GetZAnim(); if (zAnim != null) { zAnim.SetTrigger(_attack.m_attackAnimation); } } } private void ConsumeReloadIfNeeded() { if (_reloadConsumed || _attack == null) { return; } _reloadConsumed = true; ClearDeferredBurstFireReloadReset(_attack); if ((Object)(object)_attack.m_character == (Object)null || !_attack.m_requiresReload) { return; } Humanoid character = _attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val != null && val.m_weaponLoaded != _attack.m_weapon) { SecondaryAttackManager.ConsumePersistedReloadedWeaponState(val, _attack.m_weapon); return; } SecondaryAttackManager.ReloadStateConsumptionScope scope = SecondaryAttackManager.BeginReloadStateConsumption(_attack); try { _attack.m_character.ResetLoadedWeapon(); } finally { SecondaryAttackManager.EndReloadStateConsumption(ref scope); } } } private sealed class ScheduledProjectileBurstController : MonoBehaviour { private Attack _attack; private Character? _owner; private ProjectileLaunchData _launchData; private SecondaryAttackDefinition _definition; private ProjectileSecondaryBehavior _behavior; private ScheduledBurstMode _mode; private Vector3 _originPoint; private Vector3 _aimDirection; private Vector3 _targetPoint; private Vector3 _horizontalForward; private Vector3 _horizontalRight; private float _volleyAngleOffset; private float _nextFireAt; private int _emittedCount; public void Initialize(Attack attack, ProjectileLaunchData launchData, SecondaryAttackDefinition definition, ScheduledBurstMode mode, Vector3 originPoint, Vector3 aimDirection, Vector3 targetPoint, Vector3 horizontalForward, Vector3 horizontalRight) { //IL_003b: 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_0043: 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_004b: 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: 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_005b: 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) _attack = attack; _owner = (Character?)(object)attack.m_character; _launchData = launchData; _definition = definition; _behavior = (ProjectileSecondaryBehavior)definition.Behavior; _mode = mode; _originPoint = originPoint; _aimDirection = aimDirection; _targetPoint = targetPoint; _horizontalForward = horizontalForward; _horizontalRight = horizontalRight; _volleyAngleOffset = Random.value * 360f; _nextFireAt = Time.time; } private void Update() { if (_attack == null || (Object)(object)_attack.m_character == (Object)null || ((Character)_attack.m_character).IsDead()) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } while (_emittedCount < _behavior.ProjectileCount && Time.time >= _nextFireAt) { EmitShot(_emittedCount); _emittedCount++; _nextFireAt += _behavior.Interval; } if (_emittedCount >= _behavior.ProjectileCount) { Object.Destroy((Object)(object)((Component)this).gameObject); } } private void EmitShot(int shotIndex) { switch (_mode) { case ScheduledBurstMode.Barrage: EmitBarrageShot(shotIndex); break; case ScheduledBurstMode.Volley: EmitVolleyShot(shotIndex); break; case ScheduledBurstMode.Meteor: EmitMeteorShot(); break; case ScheduledBurstMode.Spiral: EmitSpiralBurstShot(shotIndex); break; } } private void EmitVolleyShot(int shotIndex) { //IL_001e: 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_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_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) float projectileGravity = GetProjectileGravity(_launchData.ProjectilePrefab); CreateVolleyShot(_behavior, _launchData, _originPoint, _targetPoint, _horizontalForward, _horizontalRight, projectileGravity, shotIndex, _behavior.ProjectileCount, _volleyAngleOffset, out var spawnPoint, out var _, out var launchVelocity, out var projectileGravity2, out var flightTime); SpawnVolleyProjectile(_attack, _launchData, spawnPoint, launchVelocity, projectileGravity2, flightTime); } private void EmitMeteorShot() { //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_001f: Unknown result type (might be due to invalid IL or missing references) SpawnMeteor(_attack, _launchData, _behavior, _targetPoint, _horizontalForward, _horizontalRight); } private void EmitSpiralBurstShot(int shotIndex) { //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_001f: 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) SpawnSpiralBurstProjectile(_attack, _launchData, _behavior, _originPoint, _aimDirection, _horizontalRight, _horizontalForward, shotIndex, _behavior.ProjectileCount); } private void EmitBarrageShot(int shotIndex) { //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_001f: Unknown result type (might be due to invalid IL or missing references) SpawnBarrageShot(_attack, _launchData, _behavior, _originPoint, _aimDirection, _horizontalRight, shotIndex); } } private sealed class SentinelController : MonoBehaviour { private Character _owner; private Attack _attack; private ItemData _weapon; private ItemData? _ammo; private Projectile _projectile; private ZNetView? _nview; private SecondaryAttackDefinition _definition; private ProjectileSecondaryBehavior _behavior; private Collider[] _colliders = Array.Empty(); private Rigidbody[] _rigidbodies = Array.Empty(); private bool[] _originalKinematicStates = Array.Empty(); private HitData _hitData = new HitData(); private float _hitNoise; private float _launchSpeed; private float _expireAt; private float _attackReadyAt; private float _nextTargetScanAt; private Character? _cachedTarget; private int _index; private int _count; private bool _released; public void Initialize(Attack attack, Character owner, ItemData weapon, ItemData? ammo, Projectile projectile, float hitNoise, HitData hitData, float launchSpeed, SecondaryAttackDefinition definition, int index, int count) { //IL_0139: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) _attack = attack; _owner = owner; _weapon = weapon; _ammo = ammo; _projectile = projectile; _nview = ((Component)projectile).GetComponent(); _definition = definition; _behavior = (ProjectileSecondaryBehavior)definition.Behavior; _hitNoise = hitNoise; _hitData = hitData; _launchSpeed = launchSpeed; _index = index; _count = Mathf.Max(1, count); _expireAt = Time.time + _behavior.SentinelLifetime; _attackReadyAt = Time.time + _behavior.SentinelAttackDelay + (float)_index * Mathf.Max(0f, _behavior.Interval); _nextTargetScanAt = _attackReadyAt + Random.Range(0f, 0.03f); _cachedTarget = null; _colliders = ((Component)this).GetComponentsInChildren(true); _rigidbodies = ((Component)this).GetComponentsInChildren(true); _originalKinematicStates = new bool[_rigidbodies.Length]; for (int i = 0; i < _rigidbodies.Length; i++) { Rigidbody val = _rigidbodies[i]; _originalKinematicStates[i] = val.isKinematic; val.isKinematic = true; val.linearVelocity = Vector3.zero; val.angularVelocity = Vector3.zero; } SetCollidersEnabled(_colliders, enabled: false); ((Behaviour)_projectile).enabled = false; ((Component)this).transform.position = GetSentinelHoverPosition(_owner, _definition, _index, _count, Time.time); ((Component)this).transform.rotation = Quaternion.LookRotation(GetSentinelForward(_owner), Vector3.up); } private void FixedUpdate() { //IL_0059: 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_0079: Unknown result type (might be due to invalid IL or missing references) if (_released) { UpdateHoming(); return; } if ((Object)(object)_owner == (Object)null || _owner.IsDead()) { DestroyProjectileObject(((Component)this).gameObject); return; } ((Component)this).transform.position = GetSentinelHoverPosition(_owner, _definition, _index, _count, Time.time); ((Component)this).transform.rotation = Quaternion.LookRotation(GetSentinelForward(_owner), Vector3.up); Character val = TryAcquireTarget(); if ((Object)(object)val != (Object)null) { Release(val); } else if (Time.time >= _expireAt) { DestroyProjectileObject(((Component)this).gameObject); } } private Character? TryAcquireTarget() { //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_0033: 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) if (Time.time < _attackReadyAt) { return null; } Vector3 position = ((Component)this).transform.position; float sentinelDetectionRange = _behavior.SentinelDetectionRange; if (IsValidSentinelTarget(_owner, _cachedTarget, position, sentinelDetectionRange)) { return _cachedTarget; } _cachedTarget = null; if (Time.time < _nextTargetScanAt) { return null; } _nextTargetScanAt = Time.time + 0.15f + Random.Range(0f, 0.03f); _cachedTarget = FindSentinelTarget(_owner, position, sentinelDetectionRange); return _cachedTarget; } private void Release(Character target) { //IL_0001: 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_0016: 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_005f: 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_0088: 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_008e: 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) Vector3 val = target.GetCenterPoint() - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = GetSentinelForward(_owner); } ((Vector3)(ref val)).Normalize(); SetCollidersEnabled(_colliders, enabled: true); for (int i = 0; i < _rigidbodies.Length; i++) { Rigidbody obj = _rigidbodies[i]; obj.isKinematic = _originalKinematicStates[i]; obj.linearVelocity = Vector3.zero; obj.angularVelocity = Vector3.zero; } ((Component)this).transform.rotation = Quaternion.LookRotation(val, Vector3.up); ((Behaviour)_projectile).enabled = true; _projectile.Setup(_owner, val * _launchSpeed, _hitNoise, _hitData, _weapon, _ammo); SecondaryAttackAdrenalineSystem.ApplyProjectileFactor(_projectile, _attack, _behavior.AdrenalineFactor); SecondaryAttackRuntimeFacade.SetProjectileAttackAttribution(_projectile, _definition.PrefabName, secondaryAttack: true, _definition, disableCurrentAttackFallback: false); _cachedTarget = target; _released = true; } private void UpdateHoming() { //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) //IL_009e: 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_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_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_00df: 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_00f9: 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_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_projectile == (Object)null || !((Behaviour)_projectile).enabled) { Object.Destroy((Object)(object)this); } else { if ((Object)(object)_nview != (Object)null && (!_nview.IsValid() || !_nview.IsOwner())) { return; } Character cachedTarget = _cachedTarget; if ((Object)(object)_owner == (Object)null || (Object)(object)cachedTarget == (Object)null || cachedTarget.IsDead() || !BaseAI.IsEnemy(_owner, cachedTarget)) { _cachedTarget = null; Object.Destroy((Object)(object)this); return; } Vector3 val = cachedTarget.GetCenterPoint() - ((Component)this).transform.position; Vector3 velocity = _projectile.GetVelocity(); float magnitude = ((Vector3)(ref velocity)).magnitude; if (magnitude < 0.01f) { Object.Destroy((Object)(object)this); } else if (!(((Vector3)(ref val)).sqrMagnitude < 0.001f)) { Vector3 val2 = Vector3.RotateTowards(velocity / magnitude, ((Vector3)(ref val)).normalized, (float)Math.PI * Time.fixedDeltaTime, 0f); _projectile.m_vel = val2 * magnitude; } } } } private static int AimRayMask; private const float MeteorFallbackRange = 32f; private const float PiercingShotHitCooldown = 0.25f; private const float SentinelTargetScanInterval = 0.15f; private const float SentinelTargetScanJitter = 0.03f; private const float SentinelHomingTurnRateDegreesPerSecond = 180f; private static readonly HashSet DeferredBurstFireReloadResets = new HashSet(); private static readonly HashSet ActiveBurstFireControllers = new HashSet(); private static readonly ConditionalWeakTable PiercingShotProjectiles = new ConditionalWeakTable(); private static readonly ConditionalWeakTable ScatterRicochetProjectiles = new ConditionalWeakTable(); private static readonly ConditionalWeakTable ScatterRicochetSplitProjectiles = new ConditionalWeakTable(); internal static bool FireOverchargedBomb(Attack attack, SecondaryAttackDefinition definition) { //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_0052: 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_0067: 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_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_00dd: 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_00e4: 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_00ea: 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_00ef: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { attack.m_consumeItem = false; return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; if (!OverchargedBombSystem.TryConsumeStackCost(attack, projectileSecondaryBehavior)) { attack.m_consumeItem = false; return false; } PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); float num2 = ((num > 1) ? ((0f - projectileSecondaryBehavior.SpreadAngle) * 0.5f) : 0f); float num3 = ((num > 1) ? (projectileSecondaryBehavior.SpreadAngle / (float)(num - 1)) : 0f); Vector3 up = ((Component)attack.m_character).transform.up; for (int i = 0; i < num; i++) { Vector3 direction = Quaternion.AngleAxis(num2 + num3 * (float)i, up) * aimDirection; if (!((Object)(object)SpawnProjectileObject(attack, launchData, val, direction, ResolveProjectileSpeed(launchData), setLastProjectile: true, out Projectile projectileComponent) == (Object)null) && !((Object)(object)projectileComponent == (Object)null)) { OverchargedBombSystem.RegisterProjectile(projectileComponent, projectileSecondaryBehavior.AoeRadiusFactor); } } return true; } internal static string GetPresetName(SecondaryAttackPreset preset) { return SecondaryAttackPresetCatalog.GetKey(preset); } internal static bool TryValidateConfiguredPayload(string weaponPrefabName, Attack primaryAttack, SecondaryAttackPreset preset, bool usesAmmo, out string reason) { reason = ""; GameObject attackProjectile = primaryAttack.m_attackProjectile; if ((Object)(object)attackProjectile == (Object)null) { if (usesAmmo) { return true; } reason = "primary attack is marked projectile-based but has no projectile prefab for preset '" + GetPresetName(preset) + "'."; return false; } if (usesAmmo) { return true; } return TryValidatePayloadPrefab(weaponPrefabName, attackProjectile, preset, out reason); } internal static bool TryValidateBurstPresetPayload(Attack attack, SecondaryAttackDefinition definition, SecondaryAttackPreset preset, ItemData? ammoItem) { if (!UsesProjectilePayloadPreset(preset)) { ReportCompatibilityIssue(attack, definition, "unsupported projectile preset '" + GetPresetName(preset) + "'."); return false; } GameObject val = ResolveEffectiveProjectilePayload(attack, ammoItem); if ((Object)(object)val == (Object)null) { ReportCompatibilityIssue(attack, definition, "no effective projectile payload was found for preset '" + GetPresetName(preset) + "'."); return false; } if (TryValidatePayloadPrefab(((Object)(object)attack.m_weapon?.m_dropPrefab != (Object)null) ? ((Object)attack.m_weapon.m_dropPrefab).name : definition.PrefabName, val, preset, out string reason)) { return true; } ReportCompatibilityIssue(attack, definition, reason); return false; } private static GameObject? ResolveEffectiveProjectilePayload(Attack attack, ItemData? ammoItem) { GameObject val = ammoItem?.m_shared?.m_attack?.m_attackProjectile; if (!((Object)(object)val != (Object)null)) { return attack.m_attackProjectile; } return val; } internal static bool TryHandleBurstPreset(Attack attack, SecondaryAttackDefinition definition, SecondaryAttackPreset preset) { return preset switch { SecondaryAttackPreset.Barrage => FireBarrage(attack, definition), SecondaryAttackPreset.Volley => FireVolley(attack, definition), SecondaryAttackPreset.Piercing => FirePiercingShot(attack, definition), SecondaryAttackPreset.Scatter => FireScatterRicochet(attack, definition), SecondaryAttackPreset.Spiral => FireSpiralBurst(attack, definition), SecondaryAttackPreset.Sentinel => FireSentinel(attack, definition), SecondaryAttackPreset.Meteor => FireMeteor(attack, definition), SecondaryAttackPreset.Burst => FireBurstFire(attack, definition), SecondaryAttackPreset.StickyDetonator => FireStickyDetonator(attack, definition), SecondaryAttackPreset.OverchargedBomb => FireOverchargedBomb(attack, definition), _ => false, }; } internal static bool TryHandleProjectilePresetHit(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //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_000e: 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) if (!TryHandleScatterRicochetProjectileHit(projectile, collider, hitPoint, normal)) { return TryHandlePiercingShotProjectileHit(projectile, collider, hitPoint, water, normal); } return true; } private static bool UsesProjectilePayloadPreset(SecondaryAttackPreset preset) { if ((uint)preset <= 9u) { return true; } return false; } private static bool TryValidatePayloadPrefab(string weaponPrefabName, GameObject payloadPrefab, SecondaryAttackPreset preset, out string reason) { reason = ""; string name = ((Object)payloadPrefab).name; Projectile component = payloadPrefab.GetComponent(); Aoe component2 = payloadPrefab.GetComponent(); IProjectile component3 = payloadPrefab.GetComponent(); if (UsesProjectilePayloadPreset(preset)) { if ((Object)(object)component2 != (Object)null) { reason = "preset '" + GetPresetName(preset) + "' requires a Projectile payload, but '" + name + "' is an Aoe prefab."; return false; } if ((Object)(object)component == (Object)null) { reason = ((component3 != null) ? ("preset '" + GetPresetName(preset) + "' requires a Projectile payload, but '" + name + "' implements IProjectile without a Projectile component.") : ("preset '" + GetPresetName(preset) + "' requires a Projectile payload, but '" + name + "' does not implement Projectile/IProjectile.")); return false; } } if (HasUnregisteredZNetPrefab(payloadPrefab, out string reason2)) { if (UsesProjectilePayloadPreset(preset) && (Object)(object)component != (Object)null) { return true; } reason = "payload '" + name + "' is unsafe for preset '" + GetPresetName(preset) + "': " + reason2; return false; } return true; } private static bool HasUnregisteredZNetPrefab(GameObject payloadPrefab, out string reason) { reason = ""; if ((Object)(object)payloadPrefab.GetComponent() == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } if ((Object)(object)ZNetScene.instance.GetPrefab(((Object)payloadPrefab).name) != (Object)null) { return false; } reason = "prefab has a ZNetView but is not registered in ZNetScene"; return true; } internal static bool TryGetProjectilePayload(Attack attack, SecondaryAttackDefinition definition, ProjectileLaunchData launchData, out Projectile projectilePrefab) { projectilePrefab = null; if (!(definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return false; } if (!launchData.IsValid) { return false; } if (!TryValidatePayloadPrefab(definition.PrefabName, launchData.ProjectilePrefab, projectileSecondaryBehavior.Preset, out string reason)) { ReportCompatibilityIssue(attack, definition, reason); return false; } projectilePrefab = launchData.ProjectilePrefab.GetComponent(); return true; } private static void ReportCompatibilityIssue(Attack attack, SecondaryAttackDefinition definition, string reason) { string text = (((Object)(object)attack.m_weapon?.m_dropPrefab != (Object)null) ? ((Object)attack.m_weapon.m_dropPrefab).name : definition.PrefabName); string text2 = ((definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) ? GetPresetName(projectileSecondaryBehavior.Preset) : "unknown"); if (SecondaryAttackWarningLog.TryMarkIssue(text + "|" + text2 + "|" + reason)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping custom secondary for " + text + ": " + reason)); } } internal static bool FireBarrage(Attack attack, SecondaryAttackDefinition definition) { //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_0062: 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_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_007d: 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_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_0082: 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_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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } ResolveHorizontalAxes(attack, aimDirection, out var horizontalForward, out var horizontalRight); if (projectileSecondaryBehavior.Interval > 0f) { CreateScheduledBurstController(attack, launchData, definition, ScheduledBurstMode.Barrage, val, aimDirection, val, horizontalForward, horizontalRight); return true; } for (int i = 0; i < projectileSecondaryBehavior.ProjectileCount; i++) { SpawnBarrageShot(attack, launchData, projectileSecondaryBehavior, val, aimDirection, horizontalRight, i); } return true; } private static void SpawnBarrageShot(Attack attack, ProjectileLaunchData launchData, ProjectileSecondaryBehavior projectileBehavior, Vector3 originPoint, Vector3 aimDirection, Vector3 horizontalRight, int projectileIndex) { //IL_0014: 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_0018: 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_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_0092: 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_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_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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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) float num = (float)projectileIndex - (float)(projectileBehavior.ProjectileCount - 1) * 0.5f; Vector3 spawnPoint = originPoint + horizontalRight * num * projectileBehavior.BarrageSpacing; float num2 = ((projectileBehavior.ProjectileCount > 1) ? ((0f - projectileBehavior.SpreadAngle) * 0.5f) : 0f); float num3 = ((projectileBehavior.ProjectileCount > 1) ? (projectileBehavior.SpreadAngle / (float)(projectileBehavior.ProjectileCount - 1)) : 0f); float num4 = num2 + num3 * (float)projectileIndex; Vector3 val = (((Object)(object)attack.m_character != (Object)null) ? ((Component)attack.m_character).transform.up : Vector3.up); Vector3 direction = Quaternion.AngleAxis(num4, val) * aimDirection; SpawnProjectile(attack, launchData, spawnPoint, direction, ResolveProjectileSpeed(launchData)); } internal static bool FireVolley(Attack attack, SecondaryAttackDefinition definition) { //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_0036: 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_0071: 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_009a: 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_009c: 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_00a0: 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_00be: 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (!TryResolveVolleyTargetPoint(attack, definition, val, aimDirection, out var targetPoint)) { return false; } PrepareCustomProjectileBurst(attack); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } ResolveHorizontalAxes(attack, aimDirection, out var horizontalForward, out var horizontalRight); float projectileGravity = GetProjectileGravity(launchData.ProjectilePrefab); if (projectileSecondaryBehavior.Interval > 0f) { CreateScheduledBurstController(attack, launchData, definition, ScheduledBurstMode.Volley, val, aimDirection, targetPoint, horizontalForward, horizontalRight); return true; } float volleyAngleOffset = Random.value * 360f; for (int i = 0; i < projectileSecondaryBehavior.ProjectileCount; i++) { CreateVolleyShot(projectileSecondaryBehavior, launchData, val, targetPoint, horizontalForward, horizontalRight, projectileGravity, i, projectileSecondaryBehavior.ProjectileCount, volleyAngleOffset, out var spawnPoint, out var _, out var launchVelocity, out var projectileGravity2, out var flightTime); SpawnVolleyProjectile(attack, launchData, spawnPoint, launchVelocity, projectileGravity2, flightTime); } return true; } internal static bool FirePiercingShot(Attack attack, SecondaryAttackDefinition definition) { //IL_0044: 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_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_0063: 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_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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; float num = Mathf.Max(0.01f, projectileSecondaryBehavior.ProjectileSpeedFactor); PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } int num2 = Mathf.Max(1, attack.m_projectiles); for (int i = 0; i < num2; i++) { if (attack.m_destroyPreviousProjectile && (Object)(object)attack.m_weapon.m_lastProjectile != (Object)null) { DestroyProjectileObject(attack.m_weapon.m_lastProjectile); attack.m_weapon.m_lastProjectile = null; } Vector3 direction = ResolvePrimaryProjectileDirection(attack, aimDirection, launchData.ProjectileAccuracy, i, num2); float speed = ResolveProjectileSpeed(launchData) * num; if (!((Object)(object)SpawnProjectileObject(attack, launchData, val, direction, speed, setLastProjectile: true, out Projectile projectileComponent) == (Object)null) && !((Object)(object)projectileComponent == (Object)null)) { ApplyRangePreservingProjectileSpeedModifiers(projectileComponent, num); OverchargedBombSystem.RegisterProjectile(projectileComponent, projectileSecondaryBehavior.ProjectileScaleFactor, 1f); RegisterPiercingShotProjectile(projectileComponent, projectileSecondaryBehavior); } } return true; } private static void RegisterPiercingShotProjectile(Projectile projectile, ProjectileSecondaryBehavior projectileBehavior) { if (!((Object)(object)projectile == (Object)null)) { PiercingShotProjectiles.Remove(projectile); PiercingShotProjectiles.Add(projectile, new PiercingShotState(projectileBehavior)); } } internal static bool TryHandlePiercingShotProjectileHit(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_0074: 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) if ((Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || !PiercingShotProjectiles.TryGetValue(projectile, out PiercingShotState value)) { return false; } if (water) { PiercingShotProjectiles.Remove(projectile); return false; } Character hitCharacter = GetHitCharacter(collider); if ((Object)(object)hitCharacter == (Object)null) { PiercingShotProjectiles.Remove(projectile); return false; } Character owner = ProjectileAccess.GetOwner(projectile); if (!IsValidPiercingShotTarget(owner, hitCharacter)) { return true; } if (value.IsOnHitCooldown(hitCharacter)) { return true; } if (ApplyPiercingShotHit(projectile, value, owner, hitCharacter, collider, hitPoint, normal)) { value.RegisterHit(hitCharacter); } return true; } private static bool ApplyPiercingShotHit(Projectile projectile, PiercingShotState state, Character? owner, Character character, Collider collider, Vector3 hitPoint, Vector3 normal) { //IL_0097: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: 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_0144: 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) HitData? originalHitData = ProjectileAccess.GetOriginalHitData(projectile); HitData val = ((originalHitData != null) ? originalHitData.Clone() : null); if (val == null) { return false; } if (val.m_dodgeable && character.IsDodgeInvincible()) { Player val2 = (Player)(object)((character is Player) ? character : null); if (val2 != null) { val2.HitWhileDodging(); } return false; } float num = Mathf.Pow(1f - state.Behavior.PierceDamageDecay, (float)state.HitCount); if (!Mathf.Approximately(num, 1f)) { ((DamageTypes)(ref val.m_damage)).Modify(num); val.m_pushForce *= num; } if (state.HitCount > 0) { val.m_skillRaiseAmount = 0f; } val.m_point = hitPoint; val.m_dir = ResolvePiercingShotHitDirection(projectile, owner, character); val.m_hitCollider = collider; if ((Object)(object)owner != (Object)null) { val.SetAttacker(owner); } bool active = SecondaryAttackRuntimeFacade.BeginProjectileHitContext(projectile, collider, hitPoint, water: false, normal); try { SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, projectile, ProjectileAccess.GetWeapon(projectile)); character.Damage(val); if (state.HitCount == 0 && (Object)(object)owner != (Object)null && projectile.m_adrenaline > 0f && character.m_enemyAdrenalineMultiplier > 0f && BaseAI.IsEnemy(owner, character)) { owner.AddAdrenaline(projectile.m_adrenaline * character.m_enemyAdrenalineMultiplier); } } finally { SecondaryAttackRuntimeFacade.EndProjectileHitContext(active); } Quaternion val3 = SecondaryAttackNamedEffectSystem.RotationFromNormal(normal); projectile.m_hitEffects.Create(hitPoint, val3, (Transform)null, 1f, -1); if ((Object)(object)owner != (Object)null && projectile.m_hitNoise > 0f) { owner.AddNoise(projectile.m_hitNoise); } return true; } private static bool IsValidPiercingShotTarget(Character? owner, Character target) { if ((Object)(object)target == (Object)null || target.IsDead()) { return false; } if ((Object)(object)owner == (Object)null) { return true; } if ((Object)(object)target == (Object)(object)owner) { return false; } bool flag = BaseAI.IsEnemy(owner, target) || ((Object)(object)target.GetBaseAI() != (Object)null && target.GetBaseAI().IsAggravatable() && owner.IsPlayer()); if (owner.IsPlayer() && !owner.IsPVPEnabled() && !flag) { return false; } return true; } private static Vector3 ResolvePiercingShotHitDirection(Projectile projectile, Character? owner, Character target) { //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_0017: 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) //IL_0027: 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_0070: 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_0048: Unknown result type (might be due to invalid IL or missing references) Vector3 velocity = projectile.GetVelocity(); if (((Vector3)(ref velocity)).sqrMagnitude > 0.001f) { return ((Vector3)(ref velocity)).normalized; } if ((Object)(object)owner != (Object)null) { Vector3 val = target.GetCenterPoint() - owner.GetCenterPoint(); if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { return ((Vector3)(ref val)).normalized; } } Vector3 forward = ((Component)projectile).transform.forward; if (!(((Vector3)(ref forward)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref forward)).normalized; } internal static bool FireScatterRicochet(Attack attack, SecondaryAttackDefinition definition) { //IL_0027: 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) //IL_005e: 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_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_0043: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } float speed = ResolveProjectileSpeed(launchData); if ((Object)(object)SpawnProjectileObject(attack, launchData, val, aimDirection, speed, setLastProjectile: true, out Projectile projectileComponent) == (Object)null || (Object)(object)projectileComponent == (Object)null) { return false; } RegisterScatterRicochetProjectile(projectileComponent, attack, launchData, (ProjectileSecondaryBehavior)definition.Behavior, speed); return true; } private static void RegisterScatterRicochetProjectile(Projectile projectile, Attack attack, ProjectileLaunchData launchData, ProjectileSecondaryBehavior projectileBehavior, float speed) { if (!((Object)(object)projectile == (Object)null)) { ScatterRicochetProjectiles.Remove(projectile); ScatterRicochetProjectiles.Add(projectile, new ScatterRicochetState(attack, launchData, projectileBehavior, Mathf.Max(0.01f, speed))); } } internal static bool TryHandleScatterRicochetProjectileHit(Projectile projectile, Collider collider, Vector3 hitPoint, Vector3 normal) { //IL_0082: 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) if ((Object)(object)projectile == (Object)null || !ScatterRicochetProjectiles.TryGetValue(projectile, out ScatterRicochetState value)) { return false; } if ((Object)(object)collider == (Object)null) { return false; } Character hitCharacter = GetHitCharacter(collider); Character val = (Character)(((object)ProjectileAccess.GetOwner(projectile)) ?? ((object)value.Attack?.m_character)); if ((Object)(object)hitCharacter != (Object)null && (Object)(object)hitCharacter == (Object)(object)val) { return true; } if ((Object)(object)hitCharacter != (Object)null) { ScatterRicochetProjectiles.Remove(projectile); return false; } ScatterRicochetProjectiles.Remove(projectile); SpawnScatterRicochetProjectiles(projectile, value, hitPoint, normal); if (value.Attack?.m_weapon != null && (Object)(object)value.Attack.m_weapon.m_lastProjectile == (Object)(object)((Component)projectile).gameObject) { value.Attack.m_weapon.m_lastProjectile = null; } DestroyProjectileObject(((Component)projectile).gameObject); return true; } private static void SpawnScatterRicochetProjectiles(Projectile sourceProjectile, ScatterRicochetState state, Vector3 hitPoint, Vector3 normal) { //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_0052: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00bf: 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_011d: 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_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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_0137: 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_00b6: 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_013f: 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_0163: 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) if (state.Attack == null || (Object)(object)state.Attack.m_character == (Object)null || ((Character)state.Attack.m_character).IsDead()) { return; } Vector3 velocity = sourceProjectile.GetVelocity(); Vector3 val = ((((Vector3)(ref velocity)).sqrMagnitude > 0.001f) ? ((Vector3)(ref velocity)).normalized : ((Component)sourceProjectile).transform.forward); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)state.Attack.m_character).transform.forward; } Vector3 val2 = ((((Vector3)(ref normal)).sqrMagnitude > 0.001f) ? ((Vector3)(ref normal)).normalized : (-val)); Vector3 val3 = Vector3.Reflect(val, val2); if (((Vector3)(ref val3)).sqrMagnitude < 0.001f) { val3 = -val; } ((Vector3)(ref val3)).Normalize(); ResolveScatterRicochetAxes(val3, val2, state.Attack, out var right, out var _); int num = Mathf.Max(1, state.Behavior.ProjectileCount); float num2 = ResolveRicochetRetainFactor(state.Behavior); float speed = state.Speed * Mathf.Max(0.01f, num2); float num3 = Mathf.Max(0.1f, sourceProjectile.m_rayRadius + 0.1f); Vector3 spawnPoint = hitPoint + val2 * num3 + val3 * num3; for (int i = 0; i < num; i++) { Vector3 direction = ResolveScatterRicochetDirection(val3, right, state.Behavior.SplitAngle, i, num); if (!((Object)(object)SpawnProjectileObject(state.Attack, state.LaunchData, spawnPoint, direction, speed, setLastProjectile: true, out Projectile projectileComponent) == (Object)null) && !((Object)(object)projectileComponent == (Object)null)) { ApplyRicochetBounce(projectileComponent, state.Behavior); RegisterScatterRicochetSplitProjectile(projectileComponent, state.Behavior); } } } private static void ResolveScatterRicochetAxes(Vector3 reflectedDirection, Vector3 hitNormal, Attack attack, out Vector3 right, out Vector3 up) { //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_000c: 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_0020: 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_0026: 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_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_0059: 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_00ac: 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_005e: 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) right = Vector3.Cross(reflectedDirection, Vector3.up); if (((Vector3)(ref right)).sqrMagnitude < 0.001f) { right = Vector3.Cross(reflectedDirection, hitNormal); } if (((Vector3)(ref right)).sqrMagnitude < 0.001f) { right = (((Object)(object)attack.m_character != (Object)null) ? ((Component)attack.m_character).transform.right : Vector3.right); } ((Vector3)(ref right)).Normalize(); up = Vector3.Cross(right, reflectedDirection); if (((Vector3)(ref up)).sqrMagnitude < 0.001f) { up = (((Object)(object)attack.m_character != (Object)null) ? ((Component)attack.m_character).transform.up : Vector3.up); } ((Vector3)(ref up)).Normalize(); } private static Vector3 ResolveScatterRicochetDirection(Vector3 reflectedDirection, Vector3 scatterRight, float splitAngle, int projectileIndex, int projectileCount) { //IL_000d: 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_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_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_003d: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0058: Unknown result type (might be due to invalid IL or missing references) if (projectileCount <= 1 || splitAngle <= 0f) { return reflectedDirection; } float num = (float)projectileIndex * 137.50777f; Vector3 val = Quaternion.AngleAxis(Mathf.Sqrt(((float)projectileIndex + 0.5f) / (float)projectileCount) * splitAngle * 0.5f, scatterRight) * reflectedDirection; val = Quaternion.AngleAxis(num, reflectedDirection) * val; if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return reflectedDirection; } return ((Vector3)(ref val)).normalized; } private static void ApplyRicochetBounce(Projectile projectile, ProjectileSecondaryBehavior projectileBehavior) { if (!((Object)(object)projectile == (Object)null) && projectileBehavior.RicochetBounces > 0) { projectile.m_bounce = true; projectile.m_maxBounces = Mathf.Max(1, projectileBehavior.RicochetBounces); projectile.m_bouncePower = ResolveRicochetRetainFactor(projectileBehavior); projectile.m_bounceRoughness = projectileBehavior.RicochetRoughness; } } private static float ResolveRicochetRetainFactor(ProjectileSecondaryBehavior projectileBehavior) { return Mathf.Clamp01(1f - projectileBehavior.RicochetDecay); } private static void RegisterScatterRicochetSplitProjectile(Projectile projectile, ProjectileSecondaryBehavior projectileBehavior) { ScatterRicochetSplitProjectiles.Remove(projectile); ScatterRicochetSplitProjectiles.Add(projectile, new ScatterRicochetSplitState(projectileBehavior)); } internal static ScatterRicochetDamageScope BeginScatterRicochetDamageScale(Projectile projectile, Collider collider, bool water, Vector3 normal) { //IL_001e: 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)projectile == (Object)null || !ScatterRicochetSplitProjectiles.TryGetValue(projectile, out ScatterRicochetSplitState value) || value == null || ShouldScatterRicochetProjectileBounce(projectile, collider, water, normal)) { return default(ScatterRicochetDamageScope); } float num = ResolveRicochetRetainFactor(value.Behavior); int num2 = Mathf.Max(1, projectile.m_bounceCount + 1); float num3 = Mathf.Pow(num, (float)num2); ScatterRicochetSplitProjectiles.Remove(projectile); if (Mathf.Approximately(num3, 1f)) { return default(ScatterRicochetDamageScope); } ScatterRicochetDamageScope result = new ScatterRicochetDamageScope(projectile, projectile.m_damage); ((DamageTypes)(ref projectile.m_damage)).Modify(num3); return result; } internal static void EndScatterRicochetDamageScale(ScatterRicochetDamageScope scope) { //IL_0021: 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 (scope.Active && (Object)(object)scope.Projectile != (Object)null) { scope.Projectile.m_damage = scope.OriginalDamage; } } private static bool ShouldScatterRicochetProjectileBounce(Projectile projectile, Collider collider, bool water, Vector3 normal) { //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) if (!projectile.m_bounce || normal == Vector3.zero) { return false; } if (water && !projectile.m_bounceOnWater) { return false; } if ((Object)(object)collider != (Object)null) { GameObject val = Projectile.FindHitObject(collider); if ((((Object)(object)val != (Object)null) ? val.GetComponent() : null) is Character) { return false; } } if (projectile.m_bounceCount < projectile.m_maxBounces) { return ((Vector3)(ref projectile.m_vel)).magnitude > projectile.m_minBounceVel; } return false; } internal static bool FireSpiralBurst(Attack attack, SecondaryAttackDefinition definition) { //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_0062: 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_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_0090: 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_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_0095: 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_00a7: 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_00aa: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } ResolveBurstAxes(attack, aimDirection, out var right, out var up); int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); if (projectileSecondaryBehavior.Interval > 0f && num > 1) { CreateScheduledBurstController(attack, launchData, definition, ScheduledBurstMode.Spiral, val, aimDirection, val, up, right); return true; } for (int i = 0; i < num; i++) { SpawnSpiralBurstProjectile(attack, launchData, projectileSecondaryBehavior, val, aimDirection, right, up, i, num); } return true; } internal static bool FireSentinel(Attack attack, SecondaryAttackDefinition definition) { //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_004d: 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_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_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_009e: 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_00ac: 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_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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); for (int i = 0; i < num; i++) { Vector3 sentinelHoverPosition = GetSentinelHoverPosition((Character)(object)attack.m_character, definition, i, num, Time.time); Quaternion val2 = Quaternion.LookRotation(GetSentinelForward((Character)(object)attack.m_character), Vector3.up); GameObject val3 = Object.Instantiate(launchData.ProjectilePrefab, sentinelHoverPosition, val2); Projectile component = val3.GetComponent(); if ((Object)(object)component == (Object)null) { DestroyProjectileObject(val3); SpawnProjectile(attack, launchData, val, aimDirection); continue; } float launchSpeed = ResolveProjectileSpeed(launchData); HitData hitData = CreateProjectileHitData(attack, launchData.AmmoItem, launchData.DamageFactor, launchData.ConfiguredDamageFactor, launchData.ConfiguredSkillRaiseFactor); val3.AddComponent().Initialize(attack, (Character)(object)attack.m_character, attack.m_weapon, attack.m_lastUsedAmmo, component, launchData.AttackHitNoise, hitData, launchSpeed, definition, i, num); attack.m_weapon.m_lastProjectile = val3; if (attack.m_spawnOnHitChance > 0f && (Object)(object)attack.m_spawnOnHit != (Object)null) { component.m_spawnOnHit = attack.m_spawnOnHit; component.m_spawnOnHitChance = attack.m_spawnOnHitChance; } } return true; } internal static bool FireMeteor(Attack attack, SecondaryAttackDefinition definition) { //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_003e: 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_0046: 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_0065: 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_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_0095: 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_00a8: 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_00ac: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); Vector3 targetPoint = ResolveMeteorTargetPoint(attack, definition, val, aimDirection); ResolveHorizontalAxes(attack, aimDirection, out var horizontalForward, out var horizontalRight); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } if (projectileSecondaryBehavior.Interval > 0f && projectileSecondaryBehavior.ProjectileCount > 1) { CreateScheduledBurstController(attack, launchData, definition, ScheduledBurstMode.Meteor, val, aimDirection, targetPoint, horizontalForward, horizontalRight); return true; } for (int i = 0; i < projectileSecondaryBehavior.ProjectileCount; i++) { SpawnMeteor(attack, launchData, projectileSecondaryBehavior, targetPoint, horizontalForward, horizontalRight); } return true; } private static void SpawnMeteor(Attack attack, ProjectileLaunchData launchData, ProjectileSecondaryBehavior projectileBehavior, Vector3 targetPoint, Vector3 horizontalForward, Vector3 horizontalRight) { //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_000d: 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_0026: 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_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_0038: 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_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_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_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_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_0096: 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_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) Vector2 val = ((projectileBehavior.MeteorRadius > 0f) ? (Random.insideUnitCircle * projectileBehavior.MeteorRadius) : Vector2.zero); Vector3 val2 = targetPoint + horizontalRight * val.x + horizontalForward * val.y; Vector3 val3 = val2 + Vector3.up * projectileBehavior.MeteorSpawnHeight; Vector3 val4 = val2 - val3; if (((Vector3)(ref val4)).sqrMagnitude < 0.001f) { val4 = Vector3.down; } float speed = ResolveProjectileSpeed(launchData) * Mathf.Max(0.01f, projectileBehavior.ProjectileSpeedFactor); if (!((Object)(object)SpawnProjectileObject(attack, launchData, val3, ((Vector3)(ref val4)).normalized, speed, setLastProjectile: true, out Projectile projectileComponent) == (Object)null) && !((Object)(object)projectileComponent == (Object)null)) { OverchargedBombSystem.RegisterProjectile(projectileComponent, projectileBehavior.ProjectileScaleFactor, projectileBehavior.AoeRadiusFactor); } } private static void SpawnSpiralBurstProjectile(Attack attack, ProjectileLaunchData launchData, ProjectileSecondaryBehavior projectileBehavior, Vector3 spawnPoint, Vector3 aimDirection, Vector3 burstRight, Vector3 burstUp, int projectileIndex, int projectileCount) { //IL_0029: 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_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_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_0059: 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_005f: 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_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_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_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) float num = ((projectileCount <= 1) ? 0f : ((float)projectileIndex / (float)(projectileCount - 1))) * projectileBehavior.SpiralTurns * (float)Math.PI * 2f; Vector3 val = burstRight * Mathf.Cos(num) * projectileBehavior.SpiralRadius + burstUp * Mathf.Sin(num) * projectileBehavior.SpiralRadius; Vector3 val2 = aimDirection + val; Vector3 normalized = ((Vector3)(ref val2)).normalized; SpawnProjectile(attack, launchData, spawnPoint, normalized); } internal static bool FireBurstFire(Attack attack, SecondaryAttackDefinition definition) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!(definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return false; } int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); if (!FireSingleBurstFireShot(attack, definition)) { return false; } if (num <= 1) { return true; } DeferredBurstFireReloadResets.Add(attack); new GameObject("SecondaryAttacks_" + GetPresetName(projectileSecondaryBehavior.Preset)).AddComponent().Initialize(attack, definition, num - 1); return true; } internal static bool ShouldDeferBurstFireReloadReset(Attack attack) { if (attack != null) { return DeferredBurstFireReloadResets.Contains(attack); } return false; } internal static bool IsBurstFireControllerActive(Attack attack) { if (attack != null) { return ActiveBurstFireControllers.Contains(attack); } return false; } private static void ClearDeferredBurstFireReloadReset(Attack attack) { if (attack != null) { DeferredBurstFireReloadResets.Remove(attack); } } private static bool FireSingleBurstFireShot(Attack attack, SecondaryAttackDefinition definition) { //IL_0027: 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) //IL_002f: 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_005f: 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_004a: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } PrepareCustomProjectileBurst(attack); Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); OrientCharacterBodyToProjectileAim(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } SpawnPrimaryProjectileCluster(attack, launchData, val, aimDirection); return true; } private static void OrientCharacterBodyToProjectileAim(Attack attack, Vector3 aimDirection) { //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_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_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_0046: Unknown result type (might be due to invalid IL or missing references) Character character = (Character)(object)attack.m_character; if (!((Object)(object)character == (Object)null) && SecondaryAttackManager.HasCharacterAuthority(character)) { Vector3 val = Vector3.ProjectOnPlane(aimDirection, Vector3.up); if (!(((Vector3)(ref val)).sqrMagnitude < 0.001f)) { ((Component)character).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } } } private static void SpawnPrimaryProjectileCluster(Attack attack, ProjectileLaunchData launchData, Vector3 spawnPoint, Vector3 aimDirection) { //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_005b: 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) int num = Mathf.Max(1, attack.m_projectiles); for (int i = 0; i < num; i++) { if (attack.m_destroyPreviousProjectile && (Object)(object)attack.m_weapon.m_lastProjectile != (Object)null) { DestroyProjectileObject(attack.m_weapon.m_lastProjectile); attack.m_weapon.m_lastProjectile = null; } Vector3 direction = ResolvePrimaryProjectileDirection(attack, aimDirection, launchData.ProjectileAccuracy, i, num); SpawnProjectile(attack, launchData, spawnPoint, direction); } } private static void CreateScheduledBurstController(Attack attack, ProjectileLaunchData launchData, SecondaryAttackDefinition definition, ScheduledBurstMode mode, Vector3 originPoint, Vector3 aimDirection, Vector3 targetPoint, Vector3 horizontalForward, Vector3 horizontalRight) { //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_002f: 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_0033: 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) new GameObject("SecondaryAttacks_" + GetPresetName(((ProjectileSecondaryBehavior)definition.Behavior).Preset)).AddComponent().Initialize(attack, launchData, definition, mode, originPoint, aimDirection, targetPoint, horizontalForward, horizontalRight); } private static ProjectileLaunchData CreateLaunchData(Attack attack, SecondaryAttackDefinition definition) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) ItemData ammoItem = attack.m_ammoItem; GameObject attackProjectile = attack.m_attackProjectile; float num = attack.m_projectileVel; float num2 = attack.m_projectileVelMin; float num3 = attack.m_projectileAccuracy; float num4 = attack.m_projectileAccuracyMin; float num5 = attack.m_attackHitNoise; AnimationCurve drawVelocityCurve = attack.m_drawVelocityCurve; if (ammoItem != null && (Object)(object)ammoItem.m_shared.m_attack.m_attackProjectile != (Object)null) { attackProjectile = ammoItem.m_shared.m_attack.m_attackProjectile; num += ammoItem.m_shared.m_attack.m_projectileVel; num2 += ammoItem.m_shared.m_attack.m_projectileVelMin; num3 += ammoItem.m_shared.m_attack.m_projectileAccuracy; num4 += ammoItem.m_shared.m_attack.m_projectileAccuracyMin; num5 += ammoItem.m_shared.m_attack.m_attackHitNoise; drawVelocityCurve = ammoItem.m_shared.m_attack.m_drawVelocityCurve; } if ((Object)(object)attackProjectile == (Object)null) { return ProjectileLaunchData.Invalid; } ProjectileSecondaryBehavior projectileSecondaryBehavior = definition.Behavior as ProjectileSecondaryBehavior; float num6 = ((Character)attack.m_character).GetRandomSkillFactor(attack.m_weapon.m_shared.m_skillType); float configuredDamageFactor = Mathf.Max(0f, projectileSecondaryBehavior?.DamageFactor ?? 1f); float configuredSkillRaiseFactor = Mathf.Max(0f, projectileSecondaryBehavior?.SkillRaiseFactor ?? 1f); float configuredAdrenalineFactor = Mathf.Max(0f, projectileSecondaryBehavior?.AdrenalineFactor ?? 1f); if (attack.m_bowDraw) { num3 = Mathf.Lerp(num4, num3, Mathf.Pow(attack.m_attackDrawPercentage, 0.5f)); num6 *= attack.m_attackDrawPercentage; num = Mathf.Lerp(num2, num, drawVelocityCurve.Evaluate(attack.m_attackDrawPercentage)); if (attack.m_character is Player) { Game.instance.IncrementPlayerStat((PlayerStatType)51, 1f); } } else if (attack.m_skillAccuracy) { float skillFactor = ((Character)attack.m_character).GetSkillFactor(attack.m_weapon.m_shared.m_skillType); num3 = Mathf.Lerp(num4, num3, skillFactor); } return new ProjectileLaunchData(attackProjectile, ammoItem, num, num2, num3, num4, num5, num6, configuredDamageFactor, configuredSkillRaiseFactor, configuredAdrenalineFactor, attack.m_randomVelocity && !attack.m_bowDraw); } private static Vector3 ApplyLaunchAngle(Attack attack, Vector3 aimDirection) { //IL_000f: 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_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_001c: 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_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_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_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) if (attack.m_launchAngle == 0f) { return aimDirection; } Vector3 val = Vector3.Cross(Vector3.up, aimDirection); if (val == Vector3.zero) { val = ((Component)attack.m_character).transform.right; } return Quaternion.AngleAxis(attack.m_launchAngle, val) * aimDirection; } private static void ResolveHorizontalAxes(Attack attack, Vector3 aimDirection, out Vector3 horizontalForward, out Vector3 horizontalRight) { //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_000c: 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_0039: 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_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_0078: 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_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) horizontalForward = Vector3.ProjectOnPlane(aimDirection, Vector3.up); if (((Vector3)(ref horizontalForward)).sqrMagnitude < 0.001f) { horizontalForward = Vector3.ProjectOnPlane(((Component)attack.m_character).transform.forward, Vector3.up); } if (((Vector3)(ref horizontalForward)).sqrMagnitude < 0.001f) { horizontalForward = ((Component)attack.m_character).transform.forward; } ((Vector3)(ref horizontalForward)).Normalize(); horizontalRight = Vector3.Cross(Vector3.up, horizontalForward); if (((Vector3)(ref horizontalRight)).sqrMagnitude < 0.001f) { horizontalRight = ((Component)attack.m_character).transform.right; } ((Vector3)(ref horizontalRight)).Normalize(); } private static void ResolveBurstAxes(Attack attack, Vector3 aimDirection, out Vector3 right, out Vector3 up) { //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_000c: 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_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_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_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) right = Vector3.Cross(aimDirection, Vector3.up); if (((Vector3)(ref right)).sqrMagnitude < 0.001f) { right = ((Component)attack.m_character).transform.right; } ((Vector3)(ref right)).Normalize(); up = Vector3.Cross(right, aimDirection); if (((Vector3)(ref up)).sqrMagnitude < 0.001f) { up = ((Component)attack.m_character).transform.up; } ((Vector3)(ref up)).Normalize(); } private static Vector3 ResolvePrimaryProjectileDirection(Attack attack, Vector3 aimDirection, float projectileAccuracy, int projectileIndex, int projectileCount) { //IL_0022: 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_0027: 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_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_0033: 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_006c: 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_00db: 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_00e1: 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_00e7: 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_00e9: 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_00ef: 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_008d: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_00d2: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((((Vector3)(ref aimDirection)).sqrMagnitude > 0.001f) ? ((Vector3)(ref aimDirection)).normalized : ((Component)attack.m_character).transform.forward); Vector3 val2 = Vector3.Cross(val, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = ((Component)attack.m_character).transform.right; } ((Vector3)(ref val2)).Normalize(); Quaternion val3 = Quaternion.AngleAxis(Random.Range(0f - projectileAccuracy, projectileAccuracy), Vector3.up); if (attack.m_circularProjectileLaunch && !attack.m_distributeProjectilesAroundCircle) { val3 = Quaternion.AngleAxis(Random.value * 360f, Vector3.up); } else if (attack.m_circularProjectileLaunch && attack.m_distributeProjectilesAroundCircle) { float num = ((projectileCount > 0) ? (360f / (float)projectileCount) : 360f); val3 = Quaternion.AngleAxis(Random.Range(0f - projectileAccuracy, projectileAccuracy) + (float)projectileIndex * num, Vector3.up); } val = Quaternion.AngleAxis(Random.Range(0f - projectileAccuracy, projectileAccuracy), val2) * val; val = val3 * val; if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return ((Component)attack.m_character).transform.forward; } return ((Vector3)(ref val)).normalized; } private static void ApplyRangePreservingProjectileSpeedModifiers(Projectile projectile, float speedFactor) { speedFactor = Mathf.Max(0.01f, speedFactor); float num = 1f / speedFactor; projectile.m_ttl *= num; projectile.m_gravity *= speedFactor * speedFactor; projectile.m_drag *= speedFactor; } private static void PrepareCustomProjectileBurst(Attack attack) { if (attack.m_destroyPreviousProjectile && (Object)(object)attack.m_weapon.m_lastProjectile != (Object)null) { DestroyProjectileObject(attack.m_weapon.m_lastProjectile); attack.m_weapon.m_lastProjectile = null; } } private static void SpawnProjectile(Attack attack, ProjectileLaunchData launchData, Vector3 spawnPoint, Vector3 direction) { //IL_0009: 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) float speed = ResolveProjectileSpeed(launchData); SpawnProjectile(attack, launchData, spawnPoint, direction, speed); } private static void SpawnProjectile(Attack attack, ProjectileLaunchData launchData, Vector3 spawnPoint, Vector3 direction, float speed) { //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) SpawnProjectileObject(attack, launchData, spawnPoint, direction, speed, setLastProjectile: true, out Projectile _); } private static GameObject SpawnProjectileObject(Attack attack, ProjectileLaunchData launchData, Vector3 spawnPoint, Vector3 direction, float speed, bool setLastProjectile, out Projectile? projectileComponent) { //IL_0004: 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_0031: 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_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_007b: 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) projectileComponent = null; if (direction == Vector3.zero) { direction = ((Component)attack.m_character).transform.forward; } ((Vector3)(ref direction)).Normalize(); GameObject val = Object.Instantiate(launchData.ProjectilePrefab, spawnPoint, Quaternion.LookRotation(direction)); HitData val2 = CreateProjectileHitData(attack, launchData.AmmoItem, launchData.DamageFactor, launchData.ConfiguredDamageFactor, launchData.ConfiguredSkillRaiseFactor); IProjectile component = val.GetComponent(); projectileComponent = val.GetComponent(); if (component != null) { component.Setup((Character)(object)attack.m_character, direction * speed, launchData.AttackHitNoise, val2, attack.m_weapon, attack.m_lastUsedAmmo); } if ((Object)(object)projectileComponent != (Object)null) { SecondaryAttackAdrenalineSystem.ApplyProjectileFactor(projectileComponent, attack, launchData.ConfiguredAdrenalineFactor); RegisterProjectileAttackAttribution(projectileComponent, attack); } if (setLastProjectile) { attack.m_weapon.m_lastProjectile = val; } if (attack.m_spawnOnHitChance > 0f && (Object)(object)attack.m_spawnOnHit != (Object)null) { Projectile val3 = (Projectile)(object)((component is Projectile) ? component : null); if (val3 != null) { val3.m_spawnOnHit = attack.m_spawnOnHit; val3.m_spawnOnHitChance = attack.m_spawnOnHitChance; } } return val; } internal static void RegisterProjectileAttackAttribution(Projectile projectile, Attack attack) { if (!((Object)(object)projectile == (Object)null) && !((Object)(object)attack?.m_weapon?.m_dropPrefab == (Object)null) && SecondaryAttackRuntimeFacade.TryResolveProjectileAttackAttributionData(attack, out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition definition)) { SecondaryAttackRuntimeFacade.SetProjectileAttackAttribution(projectile, weaponPrefabName, secondaryAttack, definition, disableCurrentAttackFallback: false); } } internal static void RegisterProjectileAttackAttribution(Projectile projectile, bool disableCurrentAttackFallback) { if (!((Object)(object)projectile == (Object)null)) { SecondaryAttackRuntimeFacade.SetProjectileAttackAttribution(projectile, string.Empty, secondaryAttack: false, null, disableCurrentAttackFallback); } } private static HitData CreateProjectileHitData(Attack attack, ItemData? ammoItem, float damageFactor, float configuredDamageFactor, float configuredSkillRaiseFactor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0068: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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_02ae: Unknown result type (might be due to invalid IL or missing references) HitData val = new HitData(); val.m_toolTier = (short)attack.m_weapon.m_shared.m_toolTier; val.m_pushForce = attack.m_weapon.m_shared.m_attackForce * attack.m_forceMultiplier; val.m_backstabBonus = attack.m_weapon.m_shared.m_backstabBonus; val.m_staggerMultiplier = attack.m_staggerMultiplier; ((DamageTypes)(ref val.m_damage)).Add(attack.m_weapon.GetDamage(), 1); val.m_statusEffectHash = (((Object)(object)attack.m_weapon.m_shared.m_attackStatusEffect != (Object)null && (attack.m_weapon.m_shared.m_attackStatusEffectChance == 1f || Random.Range(0f, 1f) < attack.m_weapon.m_shared.m_attackStatusEffectChance)) ? attack.m_weapon.m_shared.m_attackStatusEffect.NameHash() : 0); val.m_skillLevel = ((Character)attack.m_character).GetSkillLevel(attack.m_weapon.m_shared.m_skillType); val.m_itemLevel = (short)attack.m_weapon.m_quality; val.m_itemWorldLevel = (byte)attack.m_weapon.m_worldLevel; val.m_blockable = attack.m_weapon.m_shared.m_blockable; val.m_dodgeable = attack.m_weapon.m_shared.m_dodgeable; val.m_skill = attack.m_weapon.m_shared.m_skillType; val.m_skillRaiseAmount = attack.m_raiseSkillAmount * Mathf.Max(0f, configuredSkillRaiseFactor); val.SetAttacker((Character)(object)attack.m_character); val.m_hitType = (HitType)((!(val.GetAttacker() is Player)) ? 1 : 2); val.m_healthReturn = attack.m_attackHealthReturnHit; if (ammoItem != null) { ((DamageTypes)(ref val.m_damage)).Add(ammoItem.GetDamage(), 1); HitData obj = val; obj.m_pushForce += ammoItem.m_shared.m_attackForce; if ((Object)(object)ammoItem.m_shared.m_attackStatusEffect != (Object)null && (ammoItem.m_shared.m_attackStatusEffectChance == 1f || Random.Range(0f, 1f) < ammoItem.m_shared.m_attackStatusEffectChance)) { val.m_statusEffectHash = ammoItem.m_shared.m_attackStatusEffect.NameHash(); } if (!ammoItem.m_shared.m_blockable) { val.m_blockable = false; } if (!ammoItem.m_shared.m_dodgeable) { val.m_dodgeable = false; } } HitData obj2 = val; obj2.m_pushForce *= damageFactor; attack.ModifyDamage(val, damageFactor); if (!Mathf.Approximately(configuredDamageFactor, 1f)) { ((DamageTypes)(ref val.m_damage)).Modify(Mathf.Max(0f, configuredDamageFactor)); } ((Character)attack.m_character).GetSEMan().ModifyAttack(attack.m_weapon.m_shared.m_skillType, ref val); return val; } private static float GetProjectileGravity(GameObject projectilePrefab) { Projectile component = projectilePrefab.GetComponent(); if (!((Object)(object)component != (Object)null)) { return 0f; } return component.m_gravity; } internal static bool CanStartBurstPreset(Attack attack, SecondaryAttackDefinition definition, SecondaryAttackPreset preset, ItemData? ammoItem) { if (!(definition.Behavior is ProjectileSecondaryBehavior behavior) || !TryValidateBurstPresetPayload(attack, definition, preset, ammoItem)) { return false; } if (preset == SecondaryAttackPreset.OverchargedBomb) { Humanoid character = attack.m_character; if (character != null && !OverchargedBombSystem.CanStart(character, attack.m_weapon, behavior)) { return false; } } return true; } private static bool TryResolveVolleyTargetPoint(Attack attack, SecondaryAttackDefinition definition, Vector3 originPoint, Vector3 aimDirection, out Vector3 targetPoint) { //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_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_019b: 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_007b: 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_0091: 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_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: 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_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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: 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_0161: 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_0103: 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) targetPoint = Vector3.zero; if (!(definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return false; } if ((Object)(object)attack.m_baseAI != (Object)null) { Character targetCreature = attack.m_baseAI.GetTargetCreature(); if ((Object)(object)targetCreature != (Object)null) { targetPoint = targetCreature.GetCenterPoint(); return true; } } Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val != null && (Object)(object)GameCamera.instance != (Object)null) { Vector3 position = ((Component)GameCamera.instance).transform.position; Vector3 forward = ((Component)GameCamera.instance).transform.forward; RaycastHit[] array = Physics.RaycastAll(position, forward, float.PositiveInfinity, GetAimRayMask()); 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 val2 = array2[num]; Character hitCharacter = GetHitCharacter(((RaycastHit)(ref val2)).collider); if ((Object)(object)hitCharacter != (Object)null && (Object)(object)hitCharacter != (Object)(object)val) { targetPoint = ClampVolleyTargetPoint(originPoint, hitCharacter.GetCenterPoint(), projectileSecondaryBehavior.VolleyMaxRange); return true; } if (!((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody != (Object)null) || !((Object)(object)((Component)((RaycastHit)(ref val2)).collider.attachedRigidbody).gameObject == (Object)(object)((Component)val).gameObject)) { targetPoint = ClampVolleyTargetPoint(originPoint, ((RaycastHit)(ref val2)).point, projectileSecondaryBehavior.VolleyMaxRange); return true; } } } Vector3 val3 = ((((Vector3)(ref aimDirection)).sqrMagnitude > 0.001f) ? ((Vector3)(ref aimDirection)).normalized : ((Component)attack.m_character).transform.forward); targetPoint = originPoint + ((Vector3)(ref val3)).normalized * projectileSecondaryBehavior.VolleyMaxRange; return true; } private static Vector3 ClampVolleyTargetPoint(Vector3 originPoint, Vector3 targetPoint, float maxRange) { //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_000f: 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_0029: 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_002e: 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) maxRange = Mathf.Max(1f, maxRange); Vector3 val = targetPoint - originPoint; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= maxRange || magnitude <= 0.001f) { return targetPoint; } return originPoint + val / magnitude * maxRange; } private static Vector3 ResolveMeteorTargetPoint(Attack attack, SecondaryAttackDefinition definition, Vector3 originPoint, Vector3 aimDirection) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_0056: 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_00a9: 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_00e4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)attack.m_baseAI != (Object)null) { Character targetCreature = attack.m_baseAI.GetTargetCreature(); if ((Object)(object)targetCreature != (Object)null) { return targetCreature.GetCenterPoint(); } } Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val != null && (Object)(object)GameCamera.instance != (Object)null) { Vector3 position = ((Component)GameCamera.instance).transform.position; Vector3 forward = ((Component)GameCamera.instance).transform.forward; RaycastHit[] array = Physics.RaycastAll(position, forward, float.PositiveInfinity, GetAimRayMask()); 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 val2 = array2[num]; if (!((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody != (Object)null) || !((Object)(object)((Component)((RaycastHit)(ref val2)).collider.attachedRigidbody).gameObject == (Object)(object)((Component)val).gameObject)) { return ((RaycastHit)(ref val2)).point; } } } return originPoint + aimDirection * 32f; } internal static Character? GetHitCharacter(Collider collider) { if ((Object)(object)collider.attachedRigidbody != (Object)null) { Character component = ((Component)collider.attachedRigidbody).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } } return ((Component)collider).GetComponentInParent(); } internal static int GetAimRayMask() { if (AimRayMask == 0) { AimRayMask = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return AimRayMask; } private static Vector3 CalculateGravityAdjustedBallisticVelocity(Vector3 spawnPoint, Vector3 targetPoint, float originalGravity, float angleDegrees, float speed, out float projectileGravity, out float flightTime) { //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_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_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_003a: 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_007e: 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_0106: 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_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: 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_0195: 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) speed = Mathf.Max(0.01f, speed); projectileGravity = Mathf.Max(0f, originalGravity); Vector3 val = targetPoint - spawnPoint; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { flightTime = 0.1f; return Vector3.forward * speed; } Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(val.x, 0f, val.z); float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.001f) { flightTime = ((Vector3)(ref val)).magnitude / speed; return ((Vector3)(ref val)).normalized * speed; } angleDegrees = Mathf.Clamp(angleDegrees, 1f, 89f); float y = val.y; float num = Mathf.Atan2(y, magnitude) * 57.29578f + 0.1f; if (num > angleDegrees) { angleDegrees = Mathf.Clamp(num, 1f, 89f); } float num2 = angleDegrees * ((float)Math.PI / 180f); float num3 = Mathf.Cos(num2); float num4 = Mathf.Sin(num2); float num5 = speed * num3; if (num5 <= 0.001f) { flightTime = ((Vector3)(ref val)).magnitude / speed; return ((Vector3)(ref val)).normalized * speed; } flightTime = magnitude / num5; float num6 = 2f * (speed * num4 * flightTime - y) / Mathf.Max(0.001f, flightTime * flightTime); if (num6 <= 0.001f || float.IsNaN(num6) || float.IsInfinity(num6)) { flightTime = ((Vector3)(ref val)).magnitude / speed; return ((Vector3)(ref val)).normalized * speed; } projectileGravity = num6; return (val2 / magnitude * num3 + Vector3.up * num4) * speed; } private static void CreateVolleyShot(ProjectileSecondaryBehavior behavior, ProjectileLaunchData launchData, Vector3 originPoint, Vector3 targetPoint, Vector3 horizontalForward, Vector3 horizontalRight, float gravity, int projectileIndex, int projectileCount, float volleyAngleOffset, out Vector3 spawnPoint, out Vector3 impactPoint, out Vector3 launchVelocity, out float projectileGravity, out float flightTime) { //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_0014: 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_0017: 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) //IL_0027: 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_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_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_0049: 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_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_008b: 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) Vector2 val = ResolveVolleyScatterOffset(behavior.VolleyRadius, projectileIndex, projectileCount, volleyAngleOffset); impactPoint = targetPoint + horizontalRight * val.x + horizontalForward * val.y; spawnPoint = originPoint; float angleDegrees = ResolveVolleyArcAngle(behavior, spawnPoint, impactPoint); float speed = ResolveProjectileSpeed(launchData) * Mathf.Max(0.01f, behavior.ProjectileSpeedFactor); launchVelocity = CalculateGravityAdjustedBallisticVelocity(spawnPoint, impactPoint, gravity, angleDegrees, speed, out projectileGravity, out flightTime); } private static float ResolveVolleyArcAngle(ProjectileSecondaryBehavior behavior, Vector3 spawnPoint, Vector3 impactPoint) { //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) Vector3 val = impactPoint - spawnPoint; val.y = 0f; float num = Mathf.Clamp01(((Vector3)(ref val)).magnitude / Mathf.Max(1f, behavior.VolleyMaxRange)); return Mathf.Lerp(behavior.VolleyArcAngleMax, behavior.VolleyArcAngleMin, num); } private static void SpawnVolleyProjectile(Attack attack, ProjectileLaunchData launchData, Vector3 spawnPoint, Vector3 launchVelocity, float projectileGravity, float flightTime) { //IL_0022: 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_001b: 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_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) float magnitude = ((Vector3)(ref launchVelocity)).magnitude; Vector3 direction = ((magnitude > 0.001f) ? (launchVelocity / magnitude) : ((Component)attack.m_character).transform.forward); SpawnProjectileObject(attack, launchData, spawnPoint, direction, magnitude, setLastProjectile: true, out Projectile projectileComponent); if ((Object)(object)projectileComponent != (Object)null) { projectileComponent.m_gravity = Mathf.Max(0f, projectileGravity); projectileComponent.m_ttl = Mathf.Max(projectileComponent.m_ttl, flightTime + 0.5f); } } private static Vector2 ResolveVolleyScatterOffset(float radius, int projectileIndex, int projectileCount, float volleyAngleOffset) { //IL_0008: 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_0061: 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_0018: Unknown result type (might be due to invalid IL or missing references) if (radius <= 0.001f) { return Vector2.zero; } if (projectileCount <= 1) { return Random.insideUnitCircle * radius; } float num = Mathf.Max(1, projectileCount); float num2 = (volleyAngleOffset + (float)projectileIndex * 137.50777f) * ((float)Math.PI / 180f); float num3 = Mathf.Sqrt(Mathf.Clamp01(((float)projectileIndex + 0.5f) / num)) * radius; return new Vector2(Mathf.Cos(num2), Mathf.Sin(num2)) * num3; } internal static float ResolveProjectileSpeed(ProjectileLaunchData launchData) { float num = (launchData.UseRandomVelocity ? Random.Range(launchData.ProjectileVelocityMin, launchData.ProjectileVelocity) : launchData.ProjectileVelocity); return Mathf.Max(0.01f, num); } private static Character? FindSentinelTarget(Character owner, Vector3 origin, float detectionRange) { //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_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) Character result = null; float num = Mathf.Max(0f, detectionRange); float num2 = num * num; foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)owner) && !allCharacter.IsDead() && allCharacter.GetBaseAI() is MonsterAI && BaseAI.IsEnemy(owner, allCharacter)) { Vector3 val = allCharacter.GetCenterPoint() - origin; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude > num2)) { num2 = sqrMagnitude; result = allCharacter; } } } return result; } private static bool IsValidSentinelTarget(Character owner, Character? candidate, Vector3 origin, float detectionRange) { //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) //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) if ((Object)(object)candidate == (Object)null || (Object)(object)candidate == (Object)(object)owner || candidate.IsDead()) { return false; } if (!(candidate.GetBaseAI() is MonsterAI)) { return false; } if (!BaseAI.IsEnemy(owner, candidate)) { return false; } float num = Mathf.Max(0f, detectionRange); num *= num; Vector3 val = candidate.GetCenterPoint() - origin; return ((Vector3)(ref val)).sqrMagnitude <= num; } internal static Vector3 GetSentinelForward(Character owner) { //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_0015: 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_0046: 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_0043: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.ProjectOnPlane(((Component)owner).transform.forward, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)owner).transform.forward; } if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } return ((Vector3)(ref val)).normalized; } private static Vector3 GetSentinelHoverPosition(Character owner, SecondaryAttackDefinition definition, int index, int count, float time) { //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_0027: 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) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_0091: 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_0047: 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_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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_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_0103: 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_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_011b: 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_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014e: 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_015c: 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_016c: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0175: 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) if (!(definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return ((Component)owner).transform.position; } Vector3 sentinelForward = GetSentinelForward(owner); Vector3 val = Vector3.Cross(Vector3.up, sentinelForward); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)owner).transform.right; } ((Vector3)(ref val)).Normalize(); float num = Mathf.Clamp(projectileSecondaryBehavior.SentinelHoverElevationAngle, 0f, 90f) * ((float)Math.PI / 180f); Vector3 val2 = -sentinelForward * Mathf.Cos(num) + Vector3.up * Mathf.Sin(num); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = -sentinelForward; } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.Cross(val, val2); if (((Vector3)(ref val3)).sqrMagnitude < 0.001f) { val3 = Vector3.up; } ((Vector3)(ref val3)).Normalize(); Vector3 val4 = ((Component)owner).transform.position + val2 * projectileSecondaryBehavior.SentinelHoverDistance + Vector3.up * projectileSecondaryBehavior.SentinelHoverHeight; if (projectileSecondaryBehavior.SentinelOrbitRadius <= 0f || count <= 1) { return val4; } float num2 = (time * projectileSecondaryBehavior.SentinelOrbitSpeed + (float)index * (360f / (float)count)) * ((float)Math.PI / 180f); Vector3 val5 = val * Mathf.Cos(num2) * projectileSecondaryBehavior.SentinelOrbitRadius + val3 * Mathf.Sin(num2) * projectileSecondaryBehavior.SentinelOrbitRadius; return val4 + val5; } private static void SetCollidersEnabled(Collider[] colliders, bool enabled) { foreach (Collider val in colliders) { if ((Object)(object)val != (Object)null) { val.enabled = enabled; } } } internal static void DestroyProjectileObject(GameObject? projectileObject) { if (!((Object)(object)projectileObject == (Object)null)) { if ((Object)(object)projectileObject.GetComponent() != (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(projectileObject); } else { Object.Destroy((Object)(object)projectileObject); } } } internal static bool FireStickyDetonator(Attack attack, SecondaryAttackDefinition definition) { //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_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_0049: 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_00ae: 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_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_00c6: 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_00cf: 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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (attack.m_burstEffect.HasEffects()) { attack.m_burstEffect.Create(val, Quaternion.LookRotation(aimDirection), (Transform)null, 1f, -1); } int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); float num2 = ((num > 1) ? ((0f - projectileSecondaryBehavior.SpreadAngle) * 0.5f) : 0f); float num3 = ((num > 1) ? (projectileSecondaryBehavior.SpreadAngle / (float)(num - 1)) : 0f); Vector3 up = ((Component)attack.m_character).transform.up; for (int i = 0; i < num; i++) { Vector3 direction = Quaternion.AngleAxis(num2 + num3 * (float)i, up) * aimDirection; if (!((Object)(object)SpawnProjectileObject(attack, launchData, val, direction, ResolveProjectileSpeed(launchData), setLastProjectile: true, out Projectile projectileComponent) == (Object)null) && !((Object)(object)projectileComponent == (Object)null)) { StickyDetonatorSystem.RegisterLaunchedProjectile(projectileComponent, launchData.ProjectilePrefab, (Character)(object)attack.m_character, attack.m_weapon, attack.m_lastUsedAmmo, launchData.AttackHitNoise, definition, projectileSecondaryBehavior); } } return true; } } internal static class OverchargedBombSystem { private sealed class ProjectileState { public float ProjectileScaleFactor { get; } public float AoeRadiusFactor { get; } public ProjectileState(float projectileScaleFactor, float aoeRadiusFactor) { ProjectileScaleFactor = projectileScaleFactor; AoeRadiusFactor = aoeRadiusFactor; } } private sealed class RuntimeVisualScaleMarker : MonoBehaviour { public float Scale { get; set; } } private sealed class ProjectileInstanceScaleMarker : MonoBehaviour { public float Scale { get; set; } } internal readonly struct AoeRadiusState { public Aoe Aoe { get; } public float OriginalRadius { get; } public AoeRadiusState(Aoe aoe, float originalRadius) { Aoe = aoe; OriginalRadius = originalRadius; } } internal readonly struct TransformScaleState { public Transform Transform { get; } public Vector3 OriginalScale { get; } public TransformScaleState(Transform transform, Vector3 originalScale) { //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) Transform = transform; OriginalScale = originalScale; } } internal readonly struct ProjectileHitScaleState { internal static readonly ProjectileHitScaleState Empty = new ProjectileHitScaleState(null, 0f, Array.Empty(), Array.Empty(), visualScalePushed: false); public Projectile? Projectile { get; } public float OriginalProjectileAoe { get; } public AoeRadiusState[] ScaledAoes { get; } public TransformScaleState[] ScaledTransforms { get; } public bool VisualScalePushed { get; } public bool Applies { get { if (!((Object)(object)Projectile != (Object)null)) { AoeRadiusState[] scaledAoes = ScaledAoes; if (scaledAoes == null || scaledAoes.Length <= 0) { TransformScaleState[] scaledTransforms = ScaledTransforms; if (scaledTransforms == null || scaledTransforms.Length <= 0) { return VisualScalePushed; } } } return true; } } public ProjectileHitScaleState(Projectile? projectile, float originalProjectileAoe, AoeRadiusState[] scaledAoes, TransformScaleState[] scaledTransforms, bool visualScalePushed) { Projectile = projectile; OriginalProjectileAoe = originalProjectileAoe; ScaledAoes = scaledAoes; ScaledTransforms = scaledTransforms; VisualScalePushed = visualScalePushed; } } private static readonly ConditionalWeakTable Projectiles = new ConditionalWeakTable(); [ThreadStatic] private static List? ActiveVisualScales; internal static bool CanStart(Humanoid humanoid, ItemData weapon, ProjectileSecondaryBehavior behavior) { if (!RequiresStackCost(weapon, behavior)) { return true; } Inventory inventory = humanoid.GetInventory(); int requiredStackConsumption = GetRequiredStackConsumption(behavior); if (inventory != null && CountMatchingItems(inventory, weapon) >= requiredStackConsumption) { return true; } ((Character)humanoid).Message((MessageType)2, "$msg_outof " + weapon.m_shared.m_name, 0, (Sprite)null); return false; } internal static bool TryConsumeStackCost(Attack attack, ProjectileSecondaryBehavior behavior) { ItemData weapon = attack.m_weapon; if (!RequiresStackCost(weapon, behavior)) { return true; } Inventory inventory = attack.m_character.GetInventory(); int requiredStackConsumption = GetRequiredStackConsumption(behavior); if (inventory == null || CountMatchingItems(inventory, weapon) < requiredStackConsumption) { ((Character)attack.m_character).Message((MessageType)2, "$msg_outof " + weapon.m_shared.m_name, 0, (Sprite)null); return false; } if (!attack.m_consumeItem) { attack.m_consumeItem = true; } int num = 1; int amount = Mathf.Max(0, requiredStackConsumption - num); RemoveMatchingItems(inventory, weapon, amount, num); return true; } internal static void RegisterProjectile(Projectile projectile, float aoeRadiusFactor) { RegisterProjectile(projectile, 1f, aoeRadiusFactor); } internal static void RegisterProjectile(Projectile projectile, float projectileScaleFactor, float aoeRadiusFactor) { if (!((Object)(object)projectile == (Object)null)) { Projectiles.Remove(projectile); Projectiles.Add(projectile, new ProjectileState(Mathf.Max(0.01f, projectileScaleFactor), Mathf.Max(0.01f, aoeRadiusFactor))); ScaleProjectileInstance(projectile, projectileScaleFactor); } } internal static ProjectileHitScaleState BeginProjectileHit(Projectile projectile) { if ((Object)(object)projectile == (Object)null || !Projectiles.TryGetValue(projectile, out ProjectileState value) || Mathf.Approximately(value.AoeRadiusFactor, 1f)) { return ProjectileHitScaleState.Empty; } List list = new List(); List list2 = new List(); HashSet seenAoes = new HashSet(); HashSet seenTransforms = new HashSet(); float aoe = projectile.m_aoe; projectile.m_aoe *= value.AoeRadiusFactor; ScaleEffectList(projectile.m_hitEffects, value.AoeRadiusFactor, list2, seenTransforms); ScaleEffectList(projectile.m_spawnOnHitEffects, value.AoeRadiusFactor, list2, seenTransforms); ScaleAoePrefab(projectile.m_spawnOnHit, value.AoeRadiusFactor, list, list2, seenAoes, seenTransforms); foreach (GameObject item in projectile.m_randomSpawnOnHit ?? new List()) { ScaleAoePrefab(item, value.AoeRadiusFactor, list, list2, seenAoes, seenTransforms); } PushActiveVisualScale(value.AoeRadiusFactor); return new ProjectileHitScaleState(projectile, aoe, list.ToArray(), list2.ToArray(), visualScalePushed: true); } internal static void EndProjectileHit(ProjectileHitScaleState state) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!state.Applies) { return; } if ((Object)(object)state.Projectile != (Object)null) { state.Projectile.m_aoe = state.OriginalProjectileAoe; } AoeRadiusState[] scaledAoes = state.ScaledAoes; for (int i = 0; i < scaledAoes.Length; i++) { AoeRadiusState aoeRadiusState = scaledAoes[i]; if ((Object)(object)aoeRadiusState.Aoe != (Object)null) { aoeRadiusState.Aoe.m_radius = aoeRadiusState.OriginalRadius; } } TransformScaleState[] scaledTransforms = state.ScaledTransforms; for (int i = 0; i < scaledTransforms.Length; i++) { TransformScaleState transformScaleState = scaledTransforms[i]; if ((Object)(object)transformScaleState.Transform != (Object)null) { transformScaleState.Transform.localScale = transformScaleState.OriginalScale; } } if (state.VisualScalePushed) { PopActiveVisualScale(); } } internal static void EndProjectileHit(ref ProjectileHitScaleState state) { EndProjectileHit(state); state = ProjectileHitScaleState.Empty; } internal static void ScaleCreatedVisuals(GameObject? instance) { if (!((Object)(object)instance == (Object)null) && TryGetActiveVisualScale(out var scale)) { ScaleRuntimeVisuals(instance, scale); } } internal static void ScaleCreatedVisuals(GameObject[]? instances) { if (instances != null && TryGetActiveVisualScale(out var scale)) { for (int i = 0; i < instances.Length; i++) { ScaleRuntimeVisuals(instances[i], scale); } } } private static bool RequiresStackCost(ItemData? weapon, ProjectileSecondaryBehavior behavior) { if (weapon?.m_shared != null && behavior.Preset == SecondaryAttackPreset.OverchargedBomb && GetRequiredStackConsumption(behavior) > 1 && string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType)) { return weapon.m_shared.m_maxStackSize > 1; } return false; } private static int GetRequiredStackConsumption(ProjectileSecondaryBehavior behavior) { return Mathf.Max(1, behavior.AmmoConsumption); } private static int CountMatchingItems(Inventory inventory, ItemData weapon) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (IsMatchingItem(allItem, weapon)) { num += Mathf.Max(0, allItem.m_stack); } } return num; } private static void RemoveMatchingItems(Inventory inventory, ItemData weapon, int amount, int keepWeaponStack) { if (amount <= 0) { return; } List list = inventory.GetAllItems().FindAll((ItemData item) => IsMatchingItem(item, weapon)); list.Sort((ItemData left, ItemData right) => (left == weapon).CompareTo(right == weapon)); foreach (ItemData item in list) { if (amount <= 0) { break; } int num = Mathf.Min(Mathf.Max(0, item.m_stack - ((item == weapon) ? keepWeaponStack : 0)), amount); if (num > 0) { inventory.RemoveItem(item, num); amount -= num; } } } private static bool IsMatchingItem(ItemData? item, ItemData weapon) { if (item?.m_shared == null || weapon?.m_shared == null) { return false; } string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); string value = (((Object)(object)weapon.m_dropPrefab != (Object)null) ? ((Object)weapon.m_dropPrefab).name : ""); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(value)) { return text.Equals(value, StringComparison.OrdinalIgnoreCase); } return item.m_shared.m_name.Equals(weapon.m_shared.m_name, StringComparison.OrdinalIgnoreCase); } private static void ScaleAoePrefab(GameObject? prefab, float radiusFactor, List scaledAoes, List scaledTransforms, HashSet seenAoes, HashSet seenTransforms) { if ((Object)(object)prefab == (Object)null) { return; } ScalePrefabTransform(prefab, radiusFactor, scaledTransforms, seenTransforms); Aoe[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Aoe val in componentsInChildren) { if (seenAoes.Add(val)) { scaledAoes.Add(new AoeRadiusState(val, val.m_radius)); val.m_radius *= radiusFactor; } } } private static void ScaleEffectList(EffectList? effectList, float scale, List scaledTransforms, HashSet seenTransforms) { if (effectList?.m_effectPrefabs == null) { return; } EffectData[] effectPrefabs = effectList.m_effectPrefabs; foreach (EffectData val in effectPrefabs) { if (!((Object)(object)val?.m_prefab == (Object)null)) { ScalePrefabTransform(val.m_prefab, scale, scaledTransforms, seenTransforms); } } } private static void ScalePrefabTransform(GameObject prefab, float scale, List scaledTransforms, HashSet seenTransforms) { //IL_0014: 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_002b: Unknown result type (might be due to invalid IL or missing references) Transform transform = prefab.transform; if (seenTransforms.Add(transform)) { scaledTransforms.Add(new TransformScaleState(transform, transform.localScale)); transform.localScale *= scale; } } private static void PushActiveVisualScale(float scale) { if (ActiveVisualScales == null) { ActiveVisualScales = new List(); } ActiveVisualScales.Add(Mathf.Max(0.01f, scale)); } private static void PopActiveVisualScale() { if (ActiveVisualScales != null && ActiveVisualScales.Count != 0) { ActiveVisualScales.RemoveAt(ActiveVisualScales.Count - 1); } } private static bool TryGetActiveVisualScale(out float scale) { if (ActiveVisualScales == null || ActiveVisualScales.Count == 0) { scale = 1f; return false; } scale = ActiveVisualScales[ActiveVisualScales.Count - 1]; return !Mathf.Approximately(scale, 1f); } private static void ScaleProjectileInstance(Projectile projectile, float scale) { //IL_0040: 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) if (!((Object)(object)projectile == (Object)null) && !Mathf.Approximately(scale, 1f)) { GameObject gameObject = ((Component)projectile).gameObject; if (!((Object)(object)gameObject.GetComponent() != (Object)null)) { gameObject.AddComponent().Scale = scale; Transform transform = gameObject.transform; transform.localScale *= scale; projectile.m_rayRadius = ((projectile.m_rayRadius > 0f) ? (projectile.m_rayRadius * scale) : (Mathf.Max(0f, scale - 1f) * 0.1f)); } } } private static void ScaleRuntimeVisuals(GameObject instance, float scale) { if (!((Object)(object)instance == (Object)null) && !Mathf.Approximately(scale, 1f) && !((Object)(object)instance.GetComponent() != (Object)null)) { instance.AddComponent().Scale = scale; ScaleParticleSystems(instance, scale); ScaleTrailRenderers(instance, scale); ScaleLineRenderers(instance, scale); ScaleLights(instance, scale); } } private static void ScaleParticleSystems(GameObject instance, float scale) { //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_0064: 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_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) //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_004e: 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: 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_008e: 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_00aa: 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_00bd: 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_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) ParticleSystem[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (ParticleSystem obj in componentsInChildren) { MainModule main = obj.main; if (((MainModule)(ref main)).startSize3D) { ((MainModule)(ref main)).startSizeX = ScaleCurve(((MainModule)(ref main)).startSizeX, scale); ((MainModule)(ref main)).startSizeY = ScaleCurve(((MainModule)(ref main)).startSizeY, scale); ((MainModule)(ref main)).startSizeZ = ScaleCurve(((MainModule)(ref main)).startSizeZ, scale); } else { ((MainModule)(ref main)).startSize = ScaleCurve(((MainModule)(ref main)).startSize, scale); } ((MainModule)(ref main)).startSpeed = ScaleCurve(((MainModule)(ref main)).startSpeed, scale); ShapeModule shape = obj.shape; if (((ShapeModule)(ref shape)).enabled) { ((ShapeModule)(ref shape)).radius = ((ShapeModule)(ref shape)).radius * scale; ((ShapeModule)(ref shape)).scale = ((ShapeModule)(ref shape)).scale * scale; ((ShapeModule)(ref shape)).position = ((ShapeModule)(ref shape)).position * scale; } TrailModule trails = obj.trails; if (((TrailModule)(ref trails)).enabled) { ((TrailModule)(ref trails)).widthOverTrail = ScaleCurve(((TrailModule)(ref trails)).widthOverTrail, scale); } ParticleSystemRenderer component = ((Component)obj).GetComponent(); if ((Object)(object)component != (Object)null) { component.lengthScale *= scale; component.velocityScale *= scale; } } } private static void ScaleTrailRenderers(GameObject instance, float scale) { TrailRenderer[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (TrailRenderer obj in componentsInChildren) { obj.widthMultiplier *= scale; } } private static void ScaleLineRenderers(GameObject instance, float scale) { LineRenderer[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (LineRenderer obj in componentsInChildren) { obj.widthMultiplier *= scale; } } private static void ScaleLights(GameObject instance, float scale) { Light[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (Light obj in componentsInChildren) { obj.range *= scale; } } private static MinMaxCurve ScaleCurve(MinMaxCurve curve, float scale) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) ParticleSystemCurveMode mode = ((MinMaxCurve)(ref curve)).mode; switch ((int)mode) { case 0: ((MinMaxCurve)(ref curve)).constant = ((MinMaxCurve)(ref curve)).constant * scale; break; case 3: ((MinMaxCurve)(ref curve)).constantMin = ((MinMaxCurve)(ref curve)).constantMin * scale; ((MinMaxCurve)(ref curve)).constantMax = ((MinMaxCurve)(ref curve)).constantMax * scale; break; case 1: case 2: ((MinMaxCurve)(ref curve)).curveMultiplier = ((MinMaxCurve)(ref curve)).curveMultiplier * scale; break; } return curve; } } [BepInPlugin("sighsorry.SecondaryAttacks", "SecondaryAttacks", "1.1.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class SecondaryAttacksPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } public enum RangedPresetSelection { Off = -1, Barrage, Volley, Piercing, Scatter, Spiral, Sentinel, Meteor, Burst } public enum BombPresetSelection { Off = -1, Auto, StickyDetonator, OverchargedBomb } public enum MagicSummonQualityPresetSelection { Off, CountByQuality, LevelByQuality } internal sealed class PluginSettings { internal GeneralSettings General { get; } = new GeneralSettings(); internal RangedSettings Ranged { get; } = new RangedSettings(); internal UiSettings Ui { get; } = new UiSettings(); internal AdminSettings Admin { get; } = new AdminSettings(); internal void Bind(SecondaryAttacksPlugin plugin) { General.Bind(plugin); Ranged.Bind(plugin); Ui.Bind(plugin); Admin.Bind(plugin); } } internal sealed class GeneralSettings { internal ConfigEntry LockConfiguration; internal ConfigEntry BloodMagicHealthCostSkillRaiseFactor; internal ConfigEntry BloodMagicHealthCostUsesMaxHealth; internal ConfigEntry MagicSummonQualityPreset; internal ConfigEntry BackstabSneakSkillRaiseAmount; internal ConfigEntry SneakVisibilitySkillEffectFactor; internal ConfigEntry SneakMovementSpeedSkillFactor; internal ConfigEntry QuickstepEnabled; internal void Bind(SecondaryAttacksPlugin plugin) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Expected O, but got Unknown LockConfiguration = plugin.config("1 - General", "Lock Configuration", Toggle.On, new ConfigDescription("If on, the configuration is locked and can be changed by server admins only.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 700 } })); SneakMovementSpeedSkillFactor = plugin.config("1 - General", "Sneak Movement Speed Skill Factor", 1f, new ConfigDescription("Sneak movement speed multiplier at Sneak skill 100 while crouching. 1.0 keeps vanilla; 2.0 doubles crouched movement speed at Sneak 100, with lower Sneak levels linearly interpolated.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 2f), new object[1] { new ConfigurationManagerAttributes { Order = 690 } })); SneakVisibilitySkillEffectFactor = plugin.config("1 - General", "Sneak Visibility Skill Effect Factor", 1f, new ConfigDescription("Multiplier for the visibility reduction gained from Sneak skill while crouching. 1.0 keeps vanilla; 2.0 doubles only the skill-based reduction. Visibility is clamped to a fixed minimum of 0.1. At factor 1.0, Sneak 0 is 0.5 in darkness and 1.0 in bright light; Sneak 100 is 0.2 in darkness and 0.6 in bright light.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 2f), new object[1] { new ConfigurationManagerAttributes { Order = 680 } })); BackstabSneakSkillRaiseAmount = plugin.config("1 - General", "Backstab Sneak Skill Raise Amount", 1f, new ConfigDescription("Sneak skill raise amount awarded whenever any attack successfully triggers backstab damage. 0 disables this reward.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), new object[1] { new ConfigurationManagerAttributes { Order = 670 } })); MagicSummonQualityPreset = plugin.config("1 - General", "Magic Summon Quality Preset", MagicSummonQualityPresetSelection.LevelByQuality, new ConfigDescription("Global quality preset for BloodMagic summon items whose primary or secondary projectile resolves to a SpawnAbility. Explicit summon blocks in SecondaryAttacks.BloodMagic.yml override this. Off disables automatic quality scaling; CountByQuality makes item quality raise active summon count; LevelByQuality makes item quality raise summoned creature level.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 660 } })); BloodMagicHealthCostUsesMaxHealth = plugin.config("1 - General", "Blood Magic Health Cost Uses Max Health", Toggle.On, new ConfigDescription("If on, Blood Magic attack health percentage costs are calculated from max health at cast time instead of current health. Flat health cost and Blood Magic skill cost reduction are unchanged.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 650 } })); BloodMagicHealthCostSkillRaiseFactor = plugin.config("1 - General", "Blood Magic Health Cost Skill Raise Factor", 0.01f, new ConfigDescription("Additional Blood Magic skill raise amount per actual consumed health. Vanilla Blood Magic skill gain always remains active. 0 disables only this custom health-cost skill gain. Example: consuming 160 health and 0.01 factor awards 1.6 extra raise amount.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.1f), new object[1] { new ConfigurationManagerAttributes { Order = 640 } })); QuickstepEnabled = plugin.config("1 - General", "Quickstep Enabled", Toggle.Off, new ConfigDescription("Enables the fixed quickstep for equipped Knives and Unarmed weapons. Bare fists are excluded. Quickstep uses 200 horizontal acceleration for 0.25 seconds, full-duration invincibility without a shield, 0.15 seconds of invincibility with a shield, and a 0.5-second cooldown. Quickstep and its double-press dodge handoff each consume 60% of the current dodge stamina cost.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 630 } })); } } internal sealed class RangedSettings { internal ConfigEntry FireballStaffPreset; internal ConfigEntry RapidStaffPreset; internal ConfigEntry LightningStaffPreset; internal ConfigEntry BowPreset; internal ConfigEntry CrossbowPreset; internal ConfigEntry BombPreset; internal void Bind(SecondaryAttacksPlugin plugin) { FireballStaffPreset = plugin.config("2 - Ranged", "Fireball Staff Preset", RangedPresetSelection.Sentinel, "Default ranged preset for ElementalMagic items whose primary attack animation is staff_fireball. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic assignment for this group."); RapidStaffPreset = plugin.config("2 - Ranged", "Rapidfire Staff Preset", RangedPresetSelection.Spiral, "Default ranged preset for ElementalMagic items whose primary attack animation is staff_rapidfire. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic assignment for this group."); LightningStaffPreset = plugin.config("2 - Ranged", "Reload Staff Preset", RangedPresetSelection.Burst, "Default ranged preset for ElementalMagic items whose primary attack animation is staff_lightningshot. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic assignment for this group."); BowPreset = plugin.config("2 - Ranged", "Bow Preset", RangedPresetSelection.Barrage, "Default ranged preset for bow items. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic assignment for this group."); CrossbowPreset = plugin.config("2 - Ranged", "Crossbow Preset", RangedPresetSelection.Burst, "Default ranged preset for reload-based crossbow-style projectile items. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic assignment for this group."); BombPreset = plugin.config("2 - Ranged", "Bomb Preset", BombPresetSelection.Auto, "Default ranged preset for throw_bomb projectile items. Auto uses overchargedBomb when the primary projectile itself has AOE or spawns an Aoe prefab on hit, and stickyDetonator otherwise. Explicit prefab entries in SecondaryAttacks.Ranged.yml override this automatic group preset. Select Off to disable automatic bomb assignment."); } } internal sealed class UiSettings { internal ConfigEntry SecondaryCooldownHudEnabled; internal ConfigEntry SecondaryAttackTooltipsEnabled; internal ConfigEntry SecondaryCooldownHudPositionX; internal ConfigEntry SecondaryCooldownHudPositionY; internal void Bind(SecondaryAttacksPlugin plugin) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown SecondaryCooldownHudEnabled = plugin.config("3 - UI", "Secondary Cooldown HUD Enabled", Toggle.On, "If on, secondary attack cooldowns and charge progress are shown in a dedicated HUD block. Off hides this display without changing cooldown behavior.", synchronizedSetting: false); SecondaryAttackTooltipsEnabled = plugin.config("3 - UI", "Secondary Attack Tooltips Enabled", Toggle.On, "If on, weapons with an applied SecondaryAttacks preset show its localized name and description in item tooltips. Off hides only this client-side tooltip section.", synchronizedSetting: false); SecondaryCooldownHudPositionX = plugin.config("3 - UI", "Secondary Cooldown HUD Position X", 0.615f, new ConfigDescription("Client-side normalized horizontal position for the secondary cooldown HUD. 0 is left, 1 is right. Open inventory to preview the configured position.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty()), synchronizedSetting: false); SecondaryCooldownHudPositionY = plugin.config("3 - UI", "Secondary Cooldown HUD Position Y", 0.22f, new ConfigDescription("Client-side normalized vertical position for the secondary cooldown HUD. 0 is bottom, 1 is top. Open inventory to preview the configured position.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty()), synchronizedSetting: false); } } internal sealed class AdminSettings { internal ConfigEntry AdminNoPresetCooldowns; internal void Bind(SecondaryAttacksPlugin plugin) { AdminNoPresetCooldowns = plugin.config("4 - Admin", "Admin No Preset Cooldowns", Toggle.Off, "Client-side admin convenience. If on, host or server-admin players use SecondaryAttacks presets without preset cooldowns. This does not change server-synced YAML values and does not remove internal hit throttles.", synchronizedSetting: false); } } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action? CustomDrawer; } internal const string MagicPluginGuid = "blacks7ar.MagicPlugin"; internal const string QuickstepPluginGuid = "shudnal.Quickstep"; internal const string CreatureManagerGuid = "sighsorry.CreatureManager"; internal const string CreatureLevelControlGuid = "org.bepinex.plugins.creaturelevelcontrol"; internal const string StarLevelSystemGuid = "MidnightsFX.StarLevelSystem"; internal const string ModName = "SecondaryAttacks"; internal const string ModVersion = "1.1.9"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.SecondaryAttacks"; private static string ConfigFileName = "sighsorry.SecondaryAttacks.cfg"; private static string ConfigFileFullPath = Paths.ConfigPath + Path.DirectorySeparatorChar + ConfigFileName; private readonly Harmony _harmony = new Harmony("sighsorry.SecondaryAttacks"); public static readonly ManualLogSource ModLogger = Logger.CreateLogSource("SecondaryAttacks"); internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.SecondaryAttacks") { DisplayName = "SecondaryAttacks", CurrentVersion = "1.1.9", MinimumRequiredVersion = "1.1.9" }; private FileSystemWatcher? _watcher; private SecondaryAttackReloadDebouncer? _configReloadDebouncer; private readonly object _reloadLock = new object(); private string? _lastConfigFileText; private bool _suppressWorldApplySettingChange; private static ConfigEntry _serverConfigLocked = null; internal static PluginSettings Settings { get; } = new PluginSettings(); internal static ConfigEntry BloodMagicHealthCostSkillRaiseFactor => Settings.General.BloodMagicHealthCostSkillRaiseFactor; internal static ConfigEntry BloodMagicHealthCostUsesMaxHealth => Settings.General.BloodMagicHealthCostUsesMaxHealth; internal static ConfigEntry MagicSummonQualityPreset => Settings.General.MagicSummonQualityPreset; internal static ConfigEntry BackstabSneakSkillRaiseAmount => Settings.General.BackstabSneakSkillRaiseAmount; internal static ConfigEntry SneakVisibilitySkillEffectFactor => Settings.General.SneakVisibilitySkillEffectFactor; internal static ConfigEntry SneakMovementSpeedSkillFactor => Settings.General.SneakMovementSpeedSkillFactor; internal static ConfigEntry FireballStaffPreset => Settings.Ranged.FireballStaffPreset; internal static ConfigEntry RapidStaffPreset => Settings.Ranged.RapidStaffPreset; internal static ConfigEntry LightningStaffPreset => Settings.Ranged.LightningStaffPreset; internal static ConfigEntry BowPreset => Settings.Ranged.BowPreset; internal static ConfigEntry CrossbowPreset => Settings.Ranged.CrossbowPreset; internal static ConfigEntry BombPreset => Settings.Ranged.BombPreset; internal static ConfigEntry SecondaryCooldownHudEnabled => Settings.Ui.SecondaryCooldownHudEnabled; internal static ConfigEntry SecondaryAttackTooltipsEnabled => Settings.Ui.SecondaryAttackTooltipsEnabled; internal static ConfigEntry SecondaryCooldownHudPositionX => Settings.Ui.SecondaryCooldownHudPositionX; internal static ConfigEntry SecondaryCooldownHudPositionY => Settings.Ui.SecondaryCooldownHudPositionY; internal static ConfigEntry AdminNoPresetCooldowns => Settings.Admin.AdminNoPresetCooldowns; internal static ConfigEntry QuickstepEnabled => Settings.General.QuickstepEnabled; public void Awake() { SecondaryAttackLocalization.Load(); bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { Settings.Bind(this); QuickstepSystem.Initialize(); SummonQualityHudCompatibility.Initialize(); RemoveLegacyConfigEntries(); RegisterWorldApplySettingHandlers(); _serverConfigLocked = Settings.General.LockConfiguration; ConfigSync.AddLockingConfigEntry(_serverConfigLocked); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SecondaryAttackFacade.Initialize(); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { _watcher?.Dispose(); _watcher = null; _configReloadDebouncer?.Dispose(); _configReloadDebouncer = null; QuickstepSystem.Dispose(); UnregisterWorldApplySettingHandlers(); SecondaryAttackFacade.Dispose(); SaveWithRespectToConfigSet(); } private void SetupWatcher() { _configReloadDebouncer = new SecondaryAttackReloadDebouncer(ReloadConfigValues, "SecondaryAttacks config reload"); _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += QueueConfigReload; _watcher.Created += QueueConfigReload; _watcher.Renamed += QueueConfigReload; _watcher.Deleted += QueueConfigReload; _watcher.Error += QueueConfigWatcherRecovery; _watcher.IncludeSubdirectories = false; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void QueueConfigReload(object sender, FileSystemEventArgs e) { _configReloadDebouncer?.Schedule(); } private void QueueConfigWatcherRecovery(object sender, ErrorEventArgs e) { ModLogger.LogWarning((object)("Config file watcher reported an error; scheduling a full config rescan. " + e.GetException().Message)); _configReloadDebouncer?.Schedule(); } private bool ReloadConfigValues() { lock (_reloadLock) { if (!TryReadStableFileText(ConfigFileFullPath, out string fileText)) { return false; } try { if (string.Equals(_lastConfigFileText, fileText, StringComparison.Ordinal)) { return true; } RangedPresetSelection value = FireballStaffPreset.Value; RangedPresetSelection value2 = RapidStaffPreset.Value; RangedPresetSelection value3 = LightningStaffPreset.Value; RangedPresetSelection value4 = BowPreset.Value; RangedPresetSelection value5 = CrossbowPreset.Value; BombPresetSelection value6 = BombPreset.Value; MagicSummonQualityPresetSelection value7 = MagicSummonQualityPreset.Value; _suppressWorldApplySettingChange = true; try { SaveWithRespectToConfigSet(reload: true); } finally { _suppressWorldApplySettingChange = false; } if (value != FireballStaffPreset.Value || value2 != RapidStaffPreset.Value || value3 != LightningStaffPreset.Value || value4 != BowPreset.Value || value5 != CrossbowPreset.Value || value6 != BombPreset.Value || value7 != MagicSummonQualityPreset.Value) { SecondaryAttackFacade.RequestCurrentWorldReapply(); } _lastConfigFileText = fileText; ModLogger.LogInfo((object)"Configuration reload complete."); return true; } catch (Exception ex) { ModLogger.LogWarning((object)("Configuration reload attempt failed and will be retried: " + ex.Message)); return false; } } } private static string? ReadFileTextIfExists(string path) { if (!File.Exists(path)) { return null; } return File.ReadAllText(path); } private static bool TryReadStableFileText(string path, out string? fileText) { fileText = null; if (!File.Exists(path)) { return false; } FileInfo fileInfo = new FileInfo(path); fileInfo.Refresh(); if (!fileInfo.Exists) { return false; } long length = fileInfo.Length; DateTime lastWriteTimeUtc = fileInfo.LastWriteTimeUtc; string text = File.ReadAllText(path); FileInfo fileInfo2 = new FileInfo(path); fileInfo2.Refresh(); if (!fileInfo2.Exists || fileInfo2.Length != length || fileInfo2.LastWriteTimeUtc != lastWriteTimeUtc) { return false; } fileText = text; return true; } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; try { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } else { ((BaseUnityPlugin)this).Config.Save(); } } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void RemoveLegacyConfigEntries() { RemoveLegacyConfigEntry("3 - UI", "Secondary Cooldown HUD Scale", 2f); RemoveLegacyConfigEntry("5 - Quickstep", "Quickstep Enabled", Toggle.Off); RemoveLegacyConfigEntry("5 - Quickstep", "Dash Force", 50f); RemoveLegacyConfigEntry("5 - Quickstep", "Dash Time", 0.25f); RemoveLegacyConfigEntry("5 - Quickstep", "Invincibility Time With Shield", 0.15f); RemoveLegacyConfigEntry("5 - Quickstep", "Quickstep Cooldown", 0.5f); RemoveLegacyConfigEntry("5 - Quickstep", "Stamina Usage Multiplier", 0.6f); RemoveLegacyConfigEntry("5 - Quickstep", "Dodge On Double Click", Toggle.On); } private void RemoveLegacyConfigEntry(string group, string name, T defaultValue) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(group, name); ((BaseUnityPlugin)this).Config.Bind(val, defaultValue, new ConfigDescription("Legacy configuration entry.", (AcceptableValueBase)null, Array.Empty())); ((BaseUnityPlugin)this).Config.Remove(val); } private void RegisterWorldApplySettingHandlers() { FireballStaffPreset.SettingChanged += OnWorldApplySettingChanged; RapidStaffPreset.SettingChanged += OnWorldApplySettingChanged; LightningStaffPreset.SettingChanged += OnWorldApplySettingChanged; BowPreset.SettingChanged += OnWorldApplySettingChanged; CrossbowPreset.SettingChanged += OnWorldApplySettingChanged; BombPreset.SettingChanged += OnWorldApplySettingChanged; MagicSummonQualityPreset.SettingChanged += OnWorldApplySettingChanged; } private void UnregisterWorldApplySettingHandlers() { FireballStaffPreset.SettingChanged -= OnWorldApplySettingChanged; RapidStaffPreset.SettingChanged -= OnWorldApplySettingChanged; LightningStaffPreset.SettingChanged -= OnWorldApplySettingChanged; BowPreset.SettingChanged -= OnWorldApplySettingChanged; CrossbowPreset.SettingChanged -= OnWorldApplySettingChanged; BombPreset.SettingChanged -= OnWorldApplySettingChanged; MagicSummonQualityPreset.SettingChanged -= OnWorldApplySettingChanged; } private void OnWorldApplySettingChanged(object? sender, EventArgs e) { if (!_suppressWorldApplySettingChange) { SecondaryAttackFacade.RequestCurrentWorldReapply(); } } 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; } private ConfigEntry config(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty()), synchronizedSetting); } } public static class KeyboardExtensions { [SpecialName] public sealed class $8D1D3E80A18AA9715780B6CB7003B2F1 { [SpecialName] public static class $895AB635D4D087636CF1C26BA650BA11 { } [ExtensionMarker("$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyDown() { throw new NotSupportedException(); } [ExtensionMarker("$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyHeld() { throw new NotSupportedException(); } } public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func)Input.GetKey); } return false; } } public static class ToggleExtentions { [SpecialName] public sealed class $0D0DD11C97D8A550F170FE2FF9C7A655 { [SpecialName] public static class $AA5EC2123F2FF2796E81CE6602146E8A { } [ExtensionMarker("$AA5EC2123F2FF2796E81CE6602146E8A")] public bool IsOn() { throw new NotSupportedException(); } [ExtensionMarker("$AA5EC2123F2FF2796E81CE6602146E8A")] public bool IsOff() { throw new NotSupportedException(); } } public static bool IsOn(this SecondaryAttacksPlugin.Toggle value) { return value == SecondaryAttacksPlugin.Toggle.On; } public static bool IsOff(this SecondaryAttacksPlugin.Toggle value) { return value == SecondaryAttacksPlugin.Toggle.Off; } } internal static class ProjectileAccess { private static readonly FieldInfo? WeaponField = AccessTools.Field(typeof(Projectile), "m_weapon"); private static readonly FieldInfo? OwnerField = AccessTools.Field(typeof(Projectile), "m_owner"); private static readonly FieldInfo? OriginalHitDataField = AccessTools.Field(typeof(Projectile), "m_originalHitData"); private static readonly FieldInfo? VelocityField = AccessTools.Field(typeof(Projectile), "m_vel"); private static readonly FieldInfo? DidHitField = AccessTools.Field(typeof(Projectile), "m_didHit"); internal static ItemData? GetWeapon(Projectile projectile) { object? obj = WeaponField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? obj : null); } internal static Character? GetOwner(Projectile projectile) { object? obj = OwnerField?.GetValue(projectile); return (Character?)((obj is Character) ? obj : null); } internal static HitData? GetOriginalHitData(Projectile projectile) { object? obj = OriginalHitDataField?.GetValue(projectile); return (HitData?)((obj is HitData) ? obj : null); } internal static Vector3 GetVelocity(Projectile projectile) { //IL_0024: 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_002a: Unknown result type (might be due to invalid IL or missing references) object obj = VelocityField?.GetValue(projectile); if (obj is Vector3) { return (Vector3)obj; } return Vector3.zero; } internal static void SetVelocity(Projectile projectile, Vector3 velocity) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) VelocityField?.SetValue(projectile, velocity); } internal static void SetDidHit(Projectile projectile, bool didHit) { DidHitField?.SetValue(projectile, didHit); } } internal static class QuickstepSystem { internal const float DashAcceleration = 200f; internal const float DashDuration = 0.25f; internal const float ShieldInvincibilityDuration = 0.15f; internal const float CooldownDuration = 0.5f; internal const float StaminaUsageFactor = 0.6f; private static bool _initialized; private static bool _externalQuickstepLoaded; private static bool _conflictWarningLogged; private static bool _disabledAfterRuntimeError; private static QuickstepController? _controller; internal static void Initialize() { if (!_initialized) { _initialized = true; _externalQuickstepLoaded = IsExternalQuickstepLoaded(); _conflictWarningLogged = false; _disabledAfterRuntimeError = false; SecondaryAttacksPlugin.QuickstepEnabled.SettingChanged += OnEnabledSettingChanged; WarnAboutConflictIfNeeded(); } } internal static void Dispose() { if (_initialized) { SecondaryAttacksPlugin.QuickstepEnabled.SettingChanged -= OnEnabledSettingChanged; } _initialized = false; _controller?.ResetState(applyExitDamping: false, clearDodgeHandoff: true); _controller = null; } internal static bool IsActive(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)_controller != (Object)null && _controller.IsFor(player)) { return _controller.Active; } return false; } internal static bool HandleUpdateDodge(Player player, float dt) { try { return HandleUpdateDodgeCore(player, dt); } catch (Exception exception) { DisableAfterRuntimeError(player, exception); return true; } } internal static void AdjustDodgeStaminaCost(Player player, ref float staminaUse) { if ((Object)(object)player != (Object)null && (Object)(object)_controller != (Object)null && _controller.IsFor(player) && (_controller.Active || _controller.DodgeHandoffActive)) { staminaUse = _controller.DodgeStaminaUse; } } internal static void ObserveDodgeUpdate(Player player) { if ((Object)(object)_controller != (Object)null && _controller.IsFor(player)) { _controller.UpdateDodgeHandoff(); } } internal static void Stop(Player player, bool applyExitDamping = false) { if ((Object)(object)_controller != (Object)null && _controller.IsFor(player)) { _controller.Stop(applyExitDamping, startCooldown: false); } } internal static void Reset(Player player) { if (!((Object)(object)_controller == (Object)null) && _controller.IsFor(player)) { _controller.ResetState(applyExitDamping: false, clearDodgeHandoff: true); } } internal static void NotifyControllerDestroyed(QuickstepController controller) { if ((Object)(object)_controller == (Object)(object)controller) { _controller = null; } } private static bool HandleUpdateDodgeCore(Player player, float dt) { //IL_01d1: Unknown result type (might be due to invalid IL or missing references) QuickstepController quickstepController = (((Object)(object)_controller != (Object)null && _controller.IsFor(player)) ? _controller : null); quickstepController?.UpdateDodgeHandoff(); quickstepController?.RetryPendingCleanup(); if (!IsFeatureAvailable(player)) { Stop(player, applyExitDamping: true); return true; } if (!HasCharacterAuthority(player)) { Stop(player); return true; } if (!HasEligibleEquippedWeapon(player)) { if ((Object)(object)_controller != (Object)null && _controller.IsFor(player) && _controller.Active) { _controller.Stop(applyExitDamping: true, startCooldown: true); } return true; } quickstepController = GetExistingController(player); if (quickstepController != null && quickstepController.Active) { if (((Character)player).IsDead() || ((Character)player).IsTeleporting()) { quickstepController.Stop(applyExitDamping: false, startCooldown: false); return true; } if (player.m_queuedDodgeTimer > 0f) { if (!CanHandoffToDodge(player)) { player.m_queuedDodgeTimer = 0f; } else { float dodgeStaminaUse = quickstepController.DodgeStaminaUse; if (((Character)player).HaveStamina(dodgeStaminaUse)) { quickstepController.Stop(applyExitDamping: true, startCooldown: true); quickstepController.BeginDodgeHandoff(dodgeStaminaUse); return true; } player.m_queuedDodgeTimer = 0f; if ((Object)(object)Hud.instance != (Object)null) { Hud.instance.StaminaBarEmptyFlash(); } } } quickstepController.Tick(dt); return false; } if (((Character)player).InDodge() || (quickstepController != null && quickstepController.DodgeHandoffActive) || (quickstepController != null && quickstepController.CoolingDown) || player.m_queuedDodgeTimer <= 0f || !CanStart(player)) { return true; } float num = Mathf.Max(0f, player.GetDodgeStaminaUse() * 0.6f); if (!((Character)player).HaveStamina(num)) { player.m_queuedDodgeTimer = Mathf.Max(0f, player.m_queuedDodgeTimer - Mathf.Max(0f, dt)); if ((Object)(object)Hud.instance != (Object)null) { Hud.instance.StaminaBarEmptyFlash(); } return false; } if (quickstepController == null) { quickstepController = GetOrAddController(player); } quickstepController.Begin(player.m_queuedDodgeDir, HasShieldEquipped(player), num); return false; } private static bool IsFeatureAvailable(Player player) { if (!_initialized || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || _disabledAfterRuntimeError || SecondaryAttacksPlugin.QuickstepEnabled.Value != SecondaryAttacksPlugin.Toggle.On) { return false; } if (!_externalQuickstepLoaded) { _externalQuickstepLoaded = IsExternalQuickstepLoaded(); } if (!_externalQuickstepLoaded) { return true; } WarnAboutConflictIfNeeded(); return false; } private static bool HasCharacterAuthority(Player player) { if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid()) { return ((Character)player).m_nview.IsOwner(); } return false; } private static bool HasEligibleEquippedWeapon(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //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_003d: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 ItemData val = ((Humanoid)player).GetRightItem() ?? ((Humanoid)player).GetLeftItem(); if (val?.m_shared == null || (int)val.m_shared.m_itemType == 5) { return false; } SkillType skillType = val.m_shared.m_skillType; if ((int)skillType == 2 || (int)skillType == 11) { return true; } return false; } private static bool HasShieldEquipped(Player player) { //IL_0020: 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: Invalid comparison between Unknown and I4 ItemData leftItem = ((Humanoid)player).GetLeftItem(); if (leftItem == null) { return false; } return (int)(leftItem.m_shared?.m_itemType).GetValueOrDefault() == 5; } private static bool CanStart(Player player) { if (((Character)player).IsOnGround() && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InAttack() && !((Character)player).IsEncumbered() && !((Character)player).InDodge()) { return !((Character)player).IsStaggering(); } return false; } private static bool CanHandoffToDodge(Player player) { if (((Character)player).IsOnGround() && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InAttack() && !((Character)player).IsEncumbered()) { return !((Character)player).IsStaggering(); } return false; } private static QuickstepController? GetExistingController(Player player) { if ((Object)(object)_controller != (Object)null && _controller.IsFor(player)) { return _controller; } QuickstepController component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null) { component.Initialize(player); _controller = component; } return component; } private static QuickstepController GetOrAddController(Player player) { QuickstepController existingController = GetExistingController(player); if ((Object)(object)existingController != (Object)null) { return existingController; } existingController = ((Component)player).gameObject.AddComponent(); existingController.Initialize(player); _controller = existingController; return existingController; } private static bool IsExternalQuickstepLoaded() { return Chainloader.PluginInfos.ContainsKey("shudnal.Quickstep"); } private static void WarnAboutConflictIfNeeded() { if (_externalQuickstepLoaded && !_conflictWarningLogged && SecondaryAttacksPlugin.QuickstepEnabled.Value == SecondaryAttacksPlugin.Toggle.On) { _conflictWarningLogged = true; SecondaryAttacksPlugin.ModLogger.LogWarning((object)"Integrated Quickstep is disabled because shudnal.Quickstep is loaded. Disable or remove one Quickstep implementation to avoid competing Player.UpdateDodge patches."); } } private static void OnEnabledSettingChanged(object? sender, EventArgs eventArgs) { if (SecondaryAttacksPlugin.QuickstepEnabled.Value == SecondaryAttacksPlugin.Toggle.On) { if (!_externalQuickstepLoaded) { _externalQuickstepLoaded = IsExternalQuickstepLoaded(); } WarnAboutConflictIfNeeded(); } else { _controller?.ResetState(applyExitDamping: true, clearDodgeHandoff: false); } } private static void DisableAfterRuntimeError(Player player, Exception exception) { _disabledAfterRuntimeError = true; Stop(player, applyExitDamping: true); SecondaryAttacksPlugin.ModLogger.LogError((object)$"Integrated Quickstep was disabled for this session after a runtime error. {exception}"); } } internal sealed class QuickstepController : MonoBehaviour { private enum QuickstepPhase { None, AfterBlocking, AfterCrouch, AfterEquipping, Dashing } private static readonly int EquippingAnimatorHash = ZSyncAnimation.GetHash("equipping"); private Player? _player; private QuickstepPhase _phase; private Vector3 _direction; private Vector3 _startingVelocity; private float _dashElapsed; private float _activeElapsed; private float _readyAt; private float _dodgeStaminaUse; private float _originalAnimatorSpeed = 1f; private float _appliedAnimatorSpeed = 1f; private bool _hasShield; private bool _originalCrouchToggled; private bool _originalCrouchAnimator; private bool _appliedCrouchToggled; private bool _crouchTouched; private bool _originalEquipping; private bool _equippingTouched; private bool _animatorSpeedTouched; private bool _dodgeStateTouched; private bool _invincibleApplied; private bool _dodgeHandoff; private bool _dodgeHandoffObserved; private float _dodgeHandoffObserveDeadline; private float _dodgeHandoffHardDeadline; private bool _cleanupErrorLogged; internal bool Active { get; private set; } internal bool CoolingDown => Time.time < _readyAt; internal bool DodgeHandoffActive => _dodgeHandoff; internal float DodgeStaminaUse => _dodgeStaminaUse; private bool CleanupPending { get { if (!_dodgeStateTouched && !_crouchTouched && !_equippingTouched) { return _animatorSpeedTouched; } return true; } } private void Awake() { _player = ((Component)this).GetComponent(); } private void OnDisable() { ResetState(applyExitDamping: false, clearDodgeHandoff: true); } private void OnDestroy() { ResetState(applyExitDamping: false, clearDodgeHandoff: true); QuickstepSystem.NotifyControllerDestroyed(this); } internal void Initialize(Player player) { _player = player; } internal bool IsFor(Player player) { return (Object)(object)_player == (Object)(object)player; } internal void Begin(Vector3 direction, bool hasShield, float staminaUse) { //IL_002f: 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_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_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) //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_01d7: 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) Player val = RequirePlayer(); if (!Active) { RetryPendingCleanup(); if (CleanupPending) { throw new InvalidOperationException("Quickstep could not restore the previous player state."); } ClearDodgeHandoff(); Vector3 val2 = direction; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude <= 0.0001f) { val2 = ((Component)val).transform.forward; val2.y = 0f; } _direction = ((Vector3)(ref val2)).normalized; _hasShield = hasShield; _dodgeStaminaUse = staminaUse; _dashElapsed = 0f; _activeElapsed = 0f; _startingVelocity = ((Character)val).m_body.linearVelocity; _originalCrouchToggled = val.m_crouchToggled; _originalCrouchAnimator = ((Character)val).m_animator.GetBool(Player.s_crouching); _originalEquipping = ((Character)val).m_zanim.m_animator.GetBool(EquippingAnimatorHash); _originalAnimatorSpeed = ((Character)val).m_zanim.m_animator.speed; _appliedAnimatorSpeed = _originalAnimatorSpeed; _crouchTouched = false; _equippingTouched = false; _animatorSpeedTouched = false; _dodgeStateTouched = false; _invincibleApplied = false; _cleanupErrorLogged = false; bool flag = ((Character)val).IsCrouching(); bool num = ((Character)val).IsBlocking(); _appliedCrouchToggled = !flag; Active = true; ((Humanoid)val).ClearActionQueue(); val.m_queuedDodgeTimer = 0f; val.m_inDodge = true; val.m_beenHitWhileDodging = false; _dodgeStateTouched = true; SetInvincible(value: true); if (!flag) { SetAnimatorSpeed(_originalAnimatorSpeed * 3f); SetEquipping(value: true); } if (num) { ClearBlockingState(); _phase = QuickstepPhase.AfterBlocking; } else { ApplyOppositeCrouch(); _phase = QuickstepPhase.AfterCrouch; } ((Character)val).AddNoise(3f); ((Character)val).UseStamina(staminaUse); ((Character)val).UpdateBodyFriction(); val.m_dodgeEffects.Create(((Component)val).transform.position, Quaternion.identity, ((Component)val).transform, 1f, -1); } } internal void Tick(float dt) { if (Active) { float dt2 = Mathf.Max(0f, dt); UpdateShieldInvincibility(dt2); switch (_phase) { default: return; case QuickstepPhase.AfterBlocking: ApplyOppositeCrouch(); _phase = QuickstepPhase.AfterCrouch; return; case QuickstepPhase.AfterCrouch: SetEquipping(value: false); _phase = QuickstepPhase.AfterEquipping; return; case QuickstepPhase.AfterEquipping: SetAnimatorSpeed(_originalAnimatorSpeed * 1.5f); _phase = QuickstepPhase.Dashing; break; case QuickstepPhase.Dashing: break; } TickDash(dt2); } } internal void Stop(bool applyExitDamping, bool startCooldown) { if (!Active && !CleanupPending) { return; } Active = false; _phase = QuickstepPhase.None; if (startCooldown) { _readyAt = Time.time + 0.5f; } Exception ex = null; try { ClearOwnedDodgeState(); } catch (Exception ex2) { ex = ex2; } try { RestoreVisualState(); } catch (Exception ex3) { if (ex == null) { ex = ex3; } } if (applyExitDamping) { try { ApplyExitDamping(); } catch (Exception ex4) { if (ex == null) { ex = ex4; } } } if (ex != null && !_cleanupErrorLogged) { _cleanupErrorLogged = true; SecondaryAttacksPlugin.ModLogger.LogError((object)$"Quickstep cleanup completed with an error: {ex}"); } else if (!CleanupPending) { _cleanupErrorLogged = false; } } private void ClearCooldown() { _readyAt = 0f; } internal void ResetState(bool applyExitDamping, bool clearDodgeHandoff) { Stop(applyExitDamping, startCooldown: false); ClearCooldown(); if (clearDodgeHandoff) { ClearDodgeHandoff(); } } internal void RetryPendingCleanup() { if (!Active && CleanupPending) { Stop(applyExitDamping: false, startCooldown: false); } } internal void BeginDodgeHandoff(float staminaUse) { _dodgeStaminaUse = Mathf.Max(0f, staminaUse); _dodgeHandoff = true; _dodgeHandoffObserved = false; _dodgeHandoffObserveDeadline = Time.time + 0.6f; _dodgeHandoffHardDeadline = Time.time + 3f; } internal void UpdateDodgeHandoff() { if (!_dodgeHandoff || (Object)(object)_player == (Object)null) { return; } if (((Character)_player).IsDead() || ((Character)_player).IsTeleporting() || Time.time >= _dodgeHandoffHardDeadline) { ClearDodgeHandoff(); return; } bool flag = ((Character)_player).InDodge(); if (_dodgeHandoffObserved) { if (!flag && !_player.m_dodgeInvincible) { ClearDodgeHandoff(); } } else if (flag || _player.m_dodgeInvincible) { _dodgeHandoffObserved = true; } else if (_player.m_queuedDodgeTimer <= 0f || Time.time >= _dodgeHandoffObserveDeadline) { ClearDodgeHandoff(); } } private void ClearDodgeHandoff() { _dodgeHandoff = false; _dodgeHandoffObserved = false; _dodgeHandoffObserveDeadline = 0f; _dodgeHandoffHardDeadline = 0f; _dodgeStaminaUse = 0f; } private void TickDash(float dt) { //IL_003f: 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) Player val = RequirePlayer(); if (!(dt <= 0f)) { float num = Mathf.Max(0f, 0.25f - _dashElapsed); float num2 = Mathf.Clamp01(num / dt); if (num2 > 0f) { ((Character)val).m_body.AddForce(_direction * (200f * num2), (ForceMode)5); } _dashElapsed += Mathf.Min(dt, num); if (_dashElapsed >= 0.25f) { Stop(applyExitDamping: true, startCooldown: true); } } } private void ApplyOppositeCrouch() { Player obj = RequirePlayer(); ((Character)obj).SetCrouch(_appliedCrouchToggled); ((Character)obj).m_zanim.SetBool(Player.s_crouching, _appliedCrouchToggled); _crouchTouched = true; } private void SetEquipping(bool value) { ((Character)RequirePlayer()).m_zanim.SetBool(EquippingAnimatorHash, value); _equippingTouched = true; } private void SetAnimatorSpeed(float speed) { Player obj = RequirePlayer(); _appliedAnimatorSpeed = speed; ((Character)obj).m_zanim.SetSpeed(speed); _animatorSpeedTouched = true; } private void SetInvincible(bool value) { Player val = RequirePlayer(); if (_invincibleApplied != value || val.m_dodgeInvincible != value || val.m_dodgeInvincibleCached != value) { val.m_dodgeInvincible = value; val.m_dodgeInvincibleCached = value; if ((Object)(object)((Character)val).m_nview != (Object)null && ((Character)val).m_nview.IsValid() && ((Character)val).m_nview.IsOwner() && ((Character)val).m_nview.GetZDO() != null) { ((Character)val).m_nview.GetZDO().Set(ZDOVars.s_dodgeinv, value); } _invincibleApplied = value; } } private void ClearBlockingState() { Player val = RequirePlayer(); ((Humanoid)val).m_internalBlockingState = false; if ((Object)(object)((Character)val).m_nview != (Object)null && ((Character)val).m_nview.IsValid() && ((Character)val).m_nview.IsOwner() && ((Character)val).m_nview.GetZDO() != null) { ((Character)val).m_nview.GetZDO().Set(ZDOVars.s_isBlockingHash, false); } ((Character)val).m_zanim.SetBool(Humanoid.s_blocking, false); } private void ClearOwnedDodgeState() { if (!((Object)(object)_player == (Object)null) && _dodgeStateTouched) { bool flag = _invincibleApplied || _player.m_dodgeInvincible || _player.m_dodgeInvincibleCached; _player.m_dodgeInvincible = false; _player.m_dodgeInvincibleCached = false; _player.m_inDodge = false; _player.m_beenHitWhileDodging = false; if ((Object)(object)((Character)_player).m_nview != (Object)null && ((Character)_player).m_nview.IsValid() && ((Character)_player).m_nview.IsOwner() && ((Character)_player).m_nview.GetZDO() != null && flag) { ((Character)_player).m_nview.GetZDO().Set(ZDOVars.s_dodgeinv, false); } _invincibleApplied = false; _dodgeStateTouched = false; } } private void RestoreVisualState() { if (!((Object)(object)_player == (Object)null)) { if (_equippingTouched) { ((Character)_player).m_zanim.SetBool(EquippingAnimatorHash, _originalEquipping); _equippingTouched = false; } if (_crouchTouched && _player.m_crouchToggled == _appliedCrouchToggled) { ((Character)_player).m_zanim.SetBool(Player.s_crouching, _originalCrouchAnimator); ((Character)_player).SetCrouch(_originalCrouchToggled); } _crouchTouched = false; if (_animatorSpeedTouched && Mathf.Approximately(((Character)_player).m_zanim.m_animator.speed, _appliedAnimatorSpeed)) { ((Character)_player).m_zanim.SetSpeed(_originalAnimatorSpeed); } _animatorSpeedTouched = false; } } private void ApplyExitDamping() { //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_0033: 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_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_0051: 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)_player == (Object)null) && !((Object)(object)((Character)_player).m_body == (Object)null)) { Vector3 linearVelocity = ((Character)_player).m_body.linearVelocity; Vector3 linearVelocity2 = Vector3.Lerp(linearVelocity, _startingVelocity, 0.5f) * 0.3f; linearVelocity2.y = linearVelocity.y; ((Character)_player).m_body.linearVelocity = linearVelocity2; } } private void UpdateShieldInvincibility(float dt) { if (_hasShield && _invincibleApplied) { _activeElapsed += dt; if (_activeElapsed >= 0.15f) { SetInvincible(value: false); } } } private Player RequirePlayer() { return _player ?? throw new InvalidOperationException("Quickstep controller is not attached to a Player."); } } [HarmonyPatch(typeof(Player), "UpdateDodge")] internal static class PlayerUpdateDodgeQuickstepPatch { private static bool Prefix(Player __instance, float dt) { return QuickstepSystem.HandleUpdateDodge(__instance, dt); } private static void Postfix(Player __instance) { QuickstepSystem.ObserveDodgeUpdate(__instance); } } [HarmonyPatch(typeof(Player), "GetDodgeStaminaUse")] internal static class PlayerGetDodgeStaminaUseQuickstepPatch { private static void Postfix(Player __instance, ref float __result) { QuickstepSystem.AdjustDodgeStaminaCost(__instance, ref __result); } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class PlayerOnDeathQuickstepPatch { private static void Prefix(Player __instance) { QuickstepSystem.Reset(__instance); } } [HarmonyPatch(typeof(Player), "OnSneaking")] internal static class PlayerOnSneakingQuickstepPatch { private static void Prefix(Player __instance, ref float dt) { if (QuickstepSystem.IsActive(__instance)) { dt = 0f; } } } internal static class RiftTrailSystem { private sealed class RaycastHitDistanceComparer : IComparer { internal static readonly RaycastHitDistanceComparer Instance = new RaycastHitDistanceComparer(); public int Compare(RaycastHit left, RaycastHit right) { return ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance); } } private readonly struct RiftTrailObserverVisualData { public float Range { get; } public float Angle { get; } public float Width { get; } public float Duration { get; } public float VisualScaleFactor { get; } public float VisualForwardOffset { get; } public float VisualAlphaFactor { get; } public string VisualTint { get; } public RiftTrailObserverVisualData(float range, float angle, float width, float duration, float visualScaleFactor, float visualForwardOffset, float visualAlphaFactor, string visualTint) { Range = range; Angle = angle; Width = width; Duration = duration; VisualScaleFactor = visualScaleFactor; VisualForwardOffset = visualForwardOffset; VisualAlphaFactor = visualAlphaFactor; VisualTint = visualTint; } } private readonly struct RiftTrailObserverSampleVisualData { public float Duration { get; } public float VisualScaleFactor { get; } public float VisualForwardOffset { get; } public float VisualAlphaFactor { get; } public string VisualTint { get; } public IReadOnlyList Ribbons { get; } public RiftTrailObserverSampleVisualData(float duration, float visualScaleFactor, float visualForwardOffset, float visualAlphaFactor, string visualTint, IReadOnlyList ribbons) { Duration = duration; VisualScaleFactor = visualScaleFactor; VisualForwardOffset = visualForwardOffset; VisualAlphaFactor = visualAlphaFactor; VisualTint = visualTint; Ribbons = ribbons; } } private readonly struct RiftTrailObserverSampleRibbonData { public IReadOnlyList Samples { get; } public RiftTrailObserverSampleRibbonData(IReadOnlyList samples) { Samples = samples; } } private readonly struct RiftTrailObserverSample { public Vector3 Base { get; } public Vector3 Tip { get; } public float NormalizedAge { get; } public RiftTrailObserverSample(Vector3 basePosition, Vector3 tipPosition, float normalizedAge) { //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) Base = basePosition; Tip = tipPosition; NormalizedAge = normalizedAge; } } private readonly struct ObserverTrailMetadata { public Material? Material { get; } public Color[]? Colors { get; } public float[]? Sizes { get; } public ObserverTrailMetadata(Material? material, Color[]? colors, float[]? sizes) { Material = material; Colors = colors; Sizes = sizes; } } private sealed class RiftTrailObserverFallbackController : MonoBehaviour { private sealed class ObserverRibbonVisual { public Mesh Mesh { get; } public Material Material { get; } public Color[] BaseColors { get; } public Color[] WorkingColors { get; } public Color BaseMaterialColor { get; } public ObserverRibbonVisual(Mesh mesh, Material material, Color[] baseColors, Color baseMaterialColor) { //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) Mesh = mesh; Material = material; BaseColors = baseColors; WorkingColors = (Color[])(object)new Color[baseColors.Length]; BaseMaterialColor = baseMaterialColor; } } private readonly List _visualRibbons = new List(); private GameObject? _visualObject; private float _endTime; private float _visualEndTime; internal void InitializeSampled(Character character, RiftTrailObserverSampleVisualData data) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown DestroyVisuals(); if (data.VisualScaleFactor <= 0f || data.Ribbons.Count == 0) { Object.Destroy((Object)(object)this); return; } _visualObject = new GameObject("SecondaryAttacks_RiftTrailObserverSampled"); List list = ResolveObserverTrailMetadata(character); for (int i = 0; i < data.Ribbons.Count; i++) { ObserverTrailMetadata trailMetadata = ((i < list.Count) ? list[i] : default(ObserverTrailMetadata)); CreateSampledRibbonVisual(data, data.Ribbons[i], trailMetadata); } if (_visualRibbons.Count == 0) { DestroyVisuals(); Object.Destroy((Object)(object)this); return; } _endTime = Time.time + data.Duration; _visualEndTime = _endTime + 1f; UpdateVisualFade(); ((Behaviour)this).enabled = true; } internal void Initialize(Vector3 origin, Vector3 forward, RiftTrailObserverVisualData data) { //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_0045: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //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_0060: 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) DestroyVisuals(); if (((Vector3)(ref forward)).sqrMagnitude <= 0.001f || data.VisualScaleFactor <= 0f) { Object.Destroy((Object)(object)this); return; } Material riftMaterial = GetRiftMaterial(); if ((Object)(object)riftMaterial == (Object)null) { Object.Destroy((Object)(object)this); return; } forward = Vector3.ProjectOnPlane(forward, Vector3.up); if (((Vector3)(ref forward)).sqrMagnitude <= 0.001f) { forward = ((Component)this).transform.forward; } ((Vector3)(ref forward)).Normalize(); _visualObject = new GameObject("SecondaryAttacks_RiftTrailObserverFallback"); CreateFallbackRibbonVisual(riftMaterial, origin, forward, data); if (_visualRibbons.Count == 0) { DestroyVisuals(); Object.Destroy((Object)(object)this); return; } _endTime = Time.time + data.Duration; _visualEndTime = _endTime + 1f; UpdateVisualFade(); ((Behaviour)this).enabled = true; } private void CreateSampledRibbonVisual(RiftTrailObserverSampleVisualData data, RiftTrailObserverSampleRibbonData ribbon, ObserverTrailMetadata trailMetadata) { //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) //IL_0067: 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_007a: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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_0138: 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_0165: 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_016f: 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_017e: 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_018d: 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_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: 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_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0295: 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_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Expected O, but got Unknown //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_030f: 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) if ((Object)(object)_visualObject == (Object)null || ribbon.Samples.Count < 2) { return; } Material val = (((Object)(object)trailMetadata.Material != (Object)null) ? trailMetadata.Material : GetRiftMaterial()); if (!((Object)(object)val == (Object)null)) { GameObject val2 = new GameObject("RiftTrailObserverSampledRibbon"); val2.transform.SetParent(_visualObject.transform, false); MeshFilter val3 = val2.AddComponent(); MeshRenderer obj = val2.AddComponent(); Material val4 = new Material(val); if (val4.HasProperty("_Cull")) { val4.SetInt("_Cull", 0); } ((Renderer)obj).material = val4; int count = ribbon.Samples.Count; Vector3[] array = (Vector3[])(object)new Vector3[count * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[count * 2]; Color[] array3 = (Color[])(object)new Color[count * 2]; int[] array4 = new int[(count - 1) * 6]; float num = Mathf.Max(0.01f, data.VisualScaleFactor); Color tint = ResolveVisualTint(data.VisualTint); float alphaFactor = Mathf.Max(0f, data.VisualAlphaFactor); for (int i = 0; i < count; i++) { RiftTrailObserverSample riftTrailObserverSample = ribbon.Samples[i]; Color val5 = ApplyVisualTint(EvaluateTrailColor(trailMetadata.Colors, riftTrailObserverSample.NormalizedAge), tint, alphaFactor); float num2 = Mathf.Max(0.01f, EvaluateTrailSize(trailMetadata.Sizes, riftTrailObserverSample.NormalizedAge)) * num; Vector3 val6 = riftTrailObserverSample.Tip - riftTrailObserverSample.Base; array[i * 2] = riftTrailObserverSample.Base - val6 * (num2 * 0.5f); array[i * 2 + 1] = riftTrailObserverSample.Tip + val6 * (num2 * 0.5f); array3[i * 2] = val5; array3[i * 2 + 1] = val5; float num3 = (float)i / (float)count; array2[i * 2] = new Vector2(num3, 0f); array2[i * 2 + 1] = new Vector2(num3, 1f); } int num4 = 0; for (int j = 1; j < count; j++) { array4[num4++] = j * 2 - 2; array4[num4++] = j * 2 - 1; array4[num4++] = j * 2; array4[num4++] = j * 2 + 1; array4[num4++] = j * 2; array4[num4++] = j * 2 - 1; } Mesh val7 = new Mesh { vertices = array, uv = array2, colors = array3, triangles = array4 }; val7.RecalculateBounds(); val3.sharedMesh = val7; Color color = (val4.HasProperty("_Color") ? val4.color : Color.white); color = ApplyVisualTint(color, tint, alphaFactor); if (val4.HasProperty("_Color")) { val4.color = color; } _visualRibbons.Add(new ObserverRibbonVisual(val7, val4, array3, color)); } } private void CreateFallbackRibbonVisual(Material baseMaterial, Vector3 origin, Vector3 forward, RiftTrailObserverVisualData data) { //IL_003a: 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_0047: 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_00a8: 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_00c3: 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_00d1: 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_00dd: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_0134: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: 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_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: 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_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: 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_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: 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_01e5: 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_01ef: 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_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0243: 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_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Expected O, but got Unknown //IL_0326: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_visualObject == (Object)null)) { int num = Mathf.Clamp(Mathf.CeilToInt(data.Angle / 8f) + 2, 4, 24); float num2 = data.Angle * 0.5f; Vector3 val = forward * data.VisualForwardOffset; float num3 = Mathf.Max(0.4f, data.Range); float num4 = Mathf.Clamp(Mathf.Max(0.25f, data.Width * 1.25f) * data.VisualScaleFactor, 0.2f, num3 * 0.75f); float num5 = Mathf.Max(0.1f, num3 - num4); Color tint = ResolveVisualTint(data.VisualTint); Color val2 = ApplyVisualTint(new Color(0.9f, 0.95f, 1f, 0.65f), tint, data.VisualAlphaFactor); GameObject val3 = new GameObject("RiftTrailObserverFallbackRibbon"); val3.transform.SetParent(_visualObject.transform, false); MeshFilter val4 = val3.AddComponent(); MeshRenderer obj = val3.AddComponent(); Material val5 = new Material(baseMaterial); if (val5.HasProperty("_Cull")) { val5.SetInt("_Cull", 0); } if (val5.HasProperty("_Color")) { val5.color = val2; } ((Renderer)obj).material = val5; Vector3[] array = (Vector3[])(object)new Vector3[num * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[num * 2]; Color[] array3 = (Color[])(object)new Color[num * 2]; int[] array4 = new int[(num - 1) * 6]; for (int i = 0; i < num; i++) { float num6 = ((num <= 1) ? 0f : ((float)i / (float)(num - 1))); Vector3 val6 = Quaternion.AngleAxis(Mathf.Lerp(0f - num2, num2, num6), Vector3.up) * forward; Vector3 normalized = ((Vector3)(ref val6)).normalized; array[i * 2] = origin + val + normalized * num5; array[i * 2 + 1] = origin + val + normalized * num3; Color val7 = val2; val7.a *= Mathf.Lerp(0.35f, 1f, Mathf.Sin(num6 * (float)Math.PI)); array3[i * 2] = val7; array3[i * 2 + 1] = val7; array2[i * 2] = new Vector2(num6, 0f); array2[i * 2 + 1] = new Vector2(num6, 1f); } int num7 = 0; for (int j = 1; j < num; j++) { array4[num7++] = j * 2 - 2; array4[num7++] = j * 2 - 1; array4[num7++] = j * 2; array4[num7++] = j * 2 + 1; array4[num7++] = j * 2; array4[num7++] = j * 2 - 1; } Mesh val8 = new Mesh { vertices = array, uv = array2, colors = array3, triangles = array4 }; val8.RecalculateBounds(); val4.sharedMesh = val8; _visualRibbons.Add(new ObserverRibbonVisual(val8, val5, array3, val2)); } } private void Update() { UpdateVisualFade(); if (Time.time >= _visualEndTime) { Object.Destroy((Object)(object)this); } } private void UpdateVisualFade() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_0099: 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_00ea: Unknown result type (might be due to invalid IL or missing references) if (_visualRibbons.Count == 0) { return; } float num = ((Time.time <= _endTime) ? 1f : Mathf.Clamp01((_visualEndTime - Time.time) / 1f)); foreach (ObserverRibbonVisual visualRibbon in _visualRibbons) { if (!((Object)(object)visualRibbon.Mesh == (Object)null) && !((Object)(object)visualRibbon.Material == (Object)null)) { for (int i = 0; i < visualRibbon.WorkingColors.Length; i++) { Color val = visualRibbon.BaseColors[i]; val.a *= num; visualRibbon.WorkingColors[i] = val; } visualRibbon.Mesh.colors = visualRibbon.WorkingColors; if (visualRibbon.Material.HasProperty("_Color")) { Color baseMaterialColor = visualRibbon.BaseMaterialColor; baseMaterialColor.a *= num; visualRibbon.Material.color = baseMaterialColor; } } } } private void OnDestroy() { DestroyVisuals(); } private void DestroyVisuals() { if ((Object)(object)_visualObject != (Object)null) { Object.Destroy((Object)(object)_visualObject); _visualObject = null; } foreach (ObserverRibbonVisual visualRibbon in _visualRibbons) { if ((Object)(object)visualRibbon.Material != (Object)null) { Object.Destroy((Object)(object)visualRibbon.Material); } if ((Object)(object)visualRibbon.Mesh != (Object)null) { Object.Destroy((Object)(object)visualRibbon.Mesh); } } _visualRibbons.Clear(); } } private readonly struct RiftTrailHitTarget { public IDestructible Destructible { get; } public Character? Character { get; } public Collider Collider { get; } public Vector3 Point { get; } public Vector3 HitDirection { get; } public bool IsEnemy { get; } public RiftTrailHitTarget(IDestructible destructible, Character? character, Collider collider, Vector3 point, Vector3 hitDirection, bool isEnemy) { //IL_0016: 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_001e: 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) Destructible = destructible; Character = character; Collider = collider; Point = point; HitDirection = hitDirection; IsEnemy = isEnemy; } } internal sealed class RiftTrailController : MonoBehaviour { private sealed class TrailSampler { private readonly Transform _baseTransform; private readonly Transform _tipTransform; private Vector3 _lastBase; private Vector3 _lastTip; private bool _hasLastSample; public List Samples { get; } = new List(); public Material? Material { get; } public Color[]? Colors { get; } public float[]? Sizes { get; } public float LifeTime { get; } public int Subdivisions { get; } public TrailSampler(Transform baseTransform, Transform tipTransform, Material? material, Color[]? colors, float[]? sizes, float lifeTime, int subdivisions) { _baseTransform = baseTransform; _tipTransform = tipTransform; Material = material; Colors = colors; Sizes = sizes; LifeTime = Mathf.Max(0.01f, lifeTime); Subdivisions = Mathf.Max(0, subdivisions); } public void Sample() { //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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a0: 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_00b2: 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: 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_0063: 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_006f: 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_0085: 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) if ((Object)(object)_baseTransform == (Object)null || (Object)(object)_tipTransform == (Object)null || Samples.Count >= 64) { return; } Vector3 position = _baseTransform.position; Vector3 position2 = _tipTransform.position; Vector3 val = position2 - position; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { return; } if (_hasLastSample) { val = position - _lastBase; if (((Vector3)(ref val)).sqrMagnitude < 0.00062500004f) { val = position2 - _lastTip; if (((Vector3)(ref val)).sqrMagnitude < 0.00062500004f) { return; } } } Samples.Add(new TrailSample(position, position2, Time.time)); _lastBase = position; _lastTip = position2; _hasLastSample = true; } } private readonly struct TrailSample { public Vector3 Base { get; } public Vector3 Tip { get; } public float Time { get; } public TrailSample(Vector3 basePosition, Vector3 tipPosition, float time) { //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) Base = basePosition; Tip = tipPosition; Time = time; } } private sealed class RibbonVisual { public Mesh Mesh { get; } public Material Material { get; } public Color[] BaseColors { get; } public Color[] WorkingColors { get; } public Color BaseMaterialColor { get; } public RibbonVisual(Mesh mesh, Material material, Color[] baseColors, Color baseMaterialColor) { //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) Mesh = mesh; Material = material; BaseColors = baseColors; WorkingColors = (Color[])(object)new Color[baseColors.Length]; BaseMaterialColor = baseMaterialColor; } } private float _endTime; private float _visualEndTime; private float _nextTickTime; private bool _activated; private bool _durabilityDrained; private bool _finished; private bool _visualCreated; private bool _visualCreationFinalized; private bool _observerVisualSent; private float _visualCaptureUntil; private GameObject? _visualObject; private readonly List _trailSamplers = new List(); private readonly List _visualRibbons = new List(); internal Attack Attack { get; private set; } internal RiftTrailDefinition RiftTrail { get; private set; } internal Vector3 Origin { get; private set; } internal Vector3 Forward { get; private set; } internal float Range { get; private set; } internal float Angle { get; private set; } internal float Width { get; private set; } private Vector3 VisualOffset => Forward * RiftTrail.VisualForwardOffset; internal void Initialize(Attack attack, RiftTrailDefinition riftTrail) { //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_002a: 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_003b: 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_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_0075: 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_0085: Unknown result type (might be due to invalid IL or missing references) Attack = attack; RiftTrail = riftTrail; Transform transform = ((Component)attack.m_character).transform; Vector3 val = Vector3.ProjectOnPlane(transform.forward, Vector3.up); Forward = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : transform.forward); Origin = transform.position + Vector3.up * Mathf.Max(0f, attack.m_attackHeight) + transform.right * attack.m_attackOffset; Range = Mathf.Max(0.1f, attack.m_attackRange); Angle = Mathf.Clamp(attack.m_attackAngle, 1f, 360f); Width = Mathf.Max(0.01f, attack.m_attackRayWidth + Mathf.Max(0f, attack.m_attackRayWidthCharExtra)); TryResolveTrailSamplers(); SampleTrails(); ((Behaviour)this).enabled = true; } internal void Activate() { if (!_activated && !_finished) { RefreshAttackShape(); SampleTrails(); _activated = true; _endTime = Time.time + RiftTrail.Duration; _visualEndTime = _endTime + 1f; _nextTickTime = Time.time + RiftTrail.TickInterval; _visualCaptureUntil = Time.time + 0.18f; TryCreateVisual(); } } private void Update() { if ((Object)(object)Attack?.m_character == (Object)null || Attack.m_weapon == null || ((Character)Attack.m_character).IsDead() || !SecondaryAttackManager.HasCharacterAuthority((Character?)(object)Attack.m_character)) { Finish(); return; } if (!_activated) { if (!IsCurrentAttack()) { Finish(); } else { SampleTrails(); } return; } TryCreateDelayedVisual(); UpdateVisualFade(); if (_nextTickTime <= _endTime && Time.time >= _nextTickTime) { ApplyTick(this); _nextTickTime += RiftTrail.TickInterval; } float num = ((_visualRibbons.Count > 0) ? _visualEndTime : _endTime); if (Time.time >= num && _nextTickTime > _endTime) { Finish(); } } private bool IsCurrentAttack() { Humanoid character = Attack.m_character; if (character != null) { return character.m_currentAttack == Attack; } return true; } private void RefreshAttackShape() { //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_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_003b: 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_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) //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_0071: 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) Transform transform = ((Component)Attack.m_character).transform; Vector3 val = Vector3.ProjectOnPlane(transform.forward, Vector3.up); Forward = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : transform.forward); Origin = transform.position + Vector3.up * Mathf.Max(0f, Attack.m_attackHeight) + transform.right * Attack.m_attackOffset; Range = Mathf.Max(0.1f, Attack.m_attackRange); Angle = Mathf.Clamp(Attack.m_attackAngle, 1f, 360f); Width = Mathf.Max(0.01f, Attack.m_attackRayWidth + Mathf.Max(0f, Attack.m_attackRayWidthCharExtra)); } internal void DrainDurabilityOnce() { if (!_durabilityDrained) { SecondaryAttackManager.DrainAttackDurability(Attack, RiftTrail.DurabilityFactor); _durabilityDrained = true; } } private void TryCreateDelayedVisual() { if (!_visualCreated && !_visualCreationFinalized) { bool flag = Time.time <= _visualCaptureUntil && IsCurrentAttack(); if (flag) { SampleTrails(); } if ((HasEnoughTrailSamples() || !flag) && !TryCreateVisual() && !flag) { TryCreateFallbackVisual(); _visualCreationFinalized = true; } } } private bool TryCreateVisual() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (RiftTrail.VisualScaleFactor <= 0f) { _visualCreationFinalized = true; return false; } if (_visualCreated) { return true; } if (!HasEnoughTrailSamples()) { return false; } _visualObject = new GameObject("SecondaryAttacks_RiftTrail"); for (int i = 0; i < _trailSamplers.Count; i++) { CreateRibbonVisual(_trailSamplers[i]); } if (_visualRibbons.Count == 0) { if ((Object)(object)_visualObject != (Object)null) { Object.Destroy((Object)(object)_visualObject); _visualObject = null; } return false; } _visualCreated = true; UpdateVisualFade(); SendObserverSampleVisual(); return true; } private bool TryCreateFallbackVisual() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if (RiftTrail.VisualScaleFactor <= 0f || _visualCreated) { return false; } Material riftMaterial = GetRiftMaterial(); if ((Object)(object)riftMaterial == (Object)null) { return false; } _visualObject = new GameObject("SecondaryAttacks_RiftTrailFallback"); CreateFallbackRibbonVisual(riftMaterial); if (_visualRibbons.Count == 0) { if ((Object)(object)_visualObject != (Object)null) { Object.Destroy((Object)(object)_visualObject); _visualObject = null; } return false; } _visualCreated = true; UpdateVisualFade(); SendObserverFallbackVisualOnce(); return true; } private void SendObserverSampleVisual() { //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) if (_observerVisualSent) { return; } List list = BuildObserverSampleRibbons(); if (list.Count != 0) { string payload = CreateObserverSampleVisualPayload(RiftTrail, list); if (SendObserverVisualPayload(Attack, Origin, Forward, payload)) { _observerVisualSent = true; } } } private void SendObserverFallbackVisualOnce() { if (!_observerVisualSent) { SendObserverFallbackVisual(Attack, RiftTrail); _observerVisualSent = true; } } private List BuildObserverSampleRibbons() { //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_011e: 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_0124: 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_0131: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Vector3 visualOffset = VisualOffset; foreach (TrailSampler trailSampler in _trailSamplers) { if (trailSampler.Samples.Count < 2) { continue; } List list2 = BuildSmoothSamples(trailSampler.Samples, trailSampler.Subdivisions); if (list2.Count >= 2) { List list3 = new List(Mathf.Min(list2.Count, 32)); int num = Mathf.Min(list2.Count, 32); for (int i = 0; i < num; i++) { int index = ((num == list2.Count) ? i : Mathf.Clamp(Mathf.RoundToInt((float)(i * (list2.Count - 1)) / (float)(num - 1)), 0, list2.Count - 1)); TrailSample trailSample = list2[index]; float num2 = Mathf.Max(0f, Time.time - trailSample.Time); float normalizedAge = ((trailSampler.LifeTime > 0.001f) ? Mathf.Clamp01(num2 / trailSampler.LifeTime) : ((num <= 1) ? 1f : (1f - (float)i / (float)(num - 1)))); list3.Add(new RiftTrailObserverSample(trailSample.Base + visualOffset, trailSample.Tip + visualOffset, normalizedAge)); } if (list3.Count >= 2) { list.Add(new RiftTrailObserverSampleRibbonData(list3)); } } } return list; } private void CreateFallbackRibbonVisual(Material baseMaterial) { //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_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_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_00e1: 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_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) //IL_0109: 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_011e: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: 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_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_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: 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_01da: 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_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: 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_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: 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_0205: 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_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0214: 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) //IL_0245: 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_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: 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_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Expected O, but got Unknown //IL_034b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_visualObject == (Object)null)) { int num = Mathf.Clamp(Mathf.CeilToInt(Angle / 8f) + 2, 4, 24); float num2 = Angle * 0.5f; float num3 = Mathf.Max(0.01f, RiftTrail.VisualScaleFactor); Vector3 visualOffset = VisualOffset; float num4 = Mathf.Max(0.4f, Range); float num5 = Mathf.Clamp(Mathf.Max(0.25f, Width * 1.25f) * num3, 0.2f, num4 * 0.75f); float num6 = Mathf.Max(0.1f, num4 - num5); Color tint = ResolveVisualTint(RiftTrail); Color val = ApplyVisualTint(new Color(0.9f, 0.95f, 1f, 0.65f), tint, Mathf.Max(0f, RiftTrail.VisualAlphaFactor)); GameObject val2 = new GameObject("RiftTrailFallbackRibbon"); val2.transform.SetParent(_visualObject.transform, false); MeshFilter val3 = val2.AddComponent(); MeshRenderer obj = val2.AddComponent(); Material val4 = new Material(baseMaterial); if (val4.HasProperty("_Cull")) { val4.SetInt("_Cull", 0); } if (val4.HasProperty("_Color")) { val4.color = val; } ((Renderer)obj).material = val4; Vector3[] array = (Vector3[])(object)new Vector3[num * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[num * 2]; Color[] array3 = (Color[])(object)new Color[num * 2]; int[] array4 = new int[(num - 1) * 6]; for (int i = 0; i < num; i++) { float num7 = ((num <= 1) ? 0f : ((float)i / (float)(num - 1))); Vector3 val5 = Quaternion.AngleAxis(Mathf.Lerp(0f - num2, num2, num7), Vector3.up) * Forward; Vector3 normalized = ((Vector3)(ref val5)).normalized; array[i * 2] = Origin + visualOffset + normalized * num6; array[i * 2 + 1] = Origin + visualOffset + normalized * num4; Color val6 = val; val6.a *= Mathf.Lerp(0.35f, 1f, Mathf.Sin(num7 * (float)Math.PI)); array3[i * 2] = val6; array3[i * 2 + 1] = val6; array2[i * 2] = new Vector2(num7, 0f); array2[i * 2 + 1] = new Vector2(num7, 1f); } int num8 = 0; for (int j = 1; j < num; j++) { array4[num8++] = j * 2 - 2; array4[num8++] = j * 2 - 1; array4[num8++] = j * 2; array4[num8++] = j * 2 + 1; array4[num8++] = j * 2; array4[num8++] = j * 2 - 1; } Mesh val7 = new Mesh { vertices = array, uv = array2, colors = array3, triangles = array4 }; val7.RecalculateBounds(); val3.sharedMesh = val7; _visualRibbons.Add(new RibbonVisual(val7, val4, array3, val)); } } private bool HasEnoughTrailSamples() { TryResolveTrailSamplers(); foreach (TrailSampler trailSampler in _trailSamplers) { if (trailSampler.Samples.Count >= 2) { return true; } } return false; } private void TryResolveTrailSamplers() { if (_trailSamplers.Count > 0) { return; } MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(Attack); foreach (MeleeWeaponTrail trail in trails) { if (WeaponTrailAccess.TryGetEndpoints(trail, out Transform baseTransform, out Transform tipTransform)) { Material material = WeaponTrailAccess.GetMaterial(trail); Color[] colors = WeaponTrailAccess.GetColors(trail); float[] sizes = WeaponTrailAccess.GetSizes(trail); float lifeTime = WeaponTrailAccess.GetLifeTime(trail, 0.2f); int subdivisions = WeaponTrailAccess.GetSubdivisions(trail, 0); _trailSamplers.Add(new TrailSampler(baseTransform, tipTransform, material, colors, sizes, lifeTime, subdivisions)); } } } private void SampleTrails() { TryResolveTrailSamplers(); foreach (TrailSampler trailSampler in _trailSamplers) { trailSampler.Sample(); } } private void CreateRibbonVisual(TrailSampler trailSampler) { //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_0082: 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_0096: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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_0189: 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_0192: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: 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_01e8: 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_01f2: 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_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: 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) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: 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_022e: 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_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0257: 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_0270: 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_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Expected O, but got Unknown //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) if (trailSampler.Samples.Count < 2 || (Object)(object)_visualObject == (Object)null) { return; } Material val = (((Object)(object)trailSampler.Material != (Object)null) ? trailSampler.Material : GetRiftMaterial()); if ((Object)(object)val == (Object)null) { return; } List list = BuildSmoothSamples(trailSampler.Samples, trailSampler.Subdivisions); int count = list.Count; if (count >= 2) { GameObject val2 = new GameObject("RiftTrailRibbon"); val2.transform.SetParent(_visualObject.transform, false); MeshFilter val3 = val2.AddComponent(); MeshRenderer obj = val2.AddComponent(); Material val4 = new Material(val); if (val4.HasProperty("_Cull")) { val4.SetInt("_Cull", 0); } ((Renderer)obj).material = val4; Vector3[] array = (Vector3[])(object)new Vector3[count * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[count * 2]; Color[] array3 = (Color[])(object)new Color[count * 2]; int[] array4 = new int[(count - 1) * 6]; float num = Mathf.Max(0.01f, RiftTrail.VisualScaleFactor); Vector3 visualOffset = VisualOffset; Color tint = ResolveVisualTint(RiftTrail); float alphaFactor = Mathf.Max(0f, RiftTrail.VisualAlphaFactor); float time = Time.time; for (int i = 0; i < count; i++) { TrailSample trailSample = list[i]; float num2 = Mathf.Max(0f, time - trailSample.Time); float normalizedAge = ((trailSampler.LifeTime > 0.001f) ? Mathf.Clamp01(num2 / trailSampler.LifeTime) : (1f - (float)i / (float)(count - 1))); Color val5 = ApplyVisualTint(EvaluateTrailColor(trailSampler.Colors, normalizedAge), tint, alphaFactor); float num3 = Mathf.Max(0.01f, EvaluateTrailSize(trailSampler.Sizes, normalizedAge)) * num; Vector3 val6 = trailSample.Tip - trailSample.Base; array[i * 2] = trailSample.Base + visualOffset - val6 * (num3 * 0.5f); array[i * 2 + 1] = trailSample.Tip + visualOffset + val6 * (num3 * 0.5f); array3[i * 2] = val5; array3[i * 2 + 1] = val5; float num4 = (float)i / (float)count; array2[i * 2] = new Vector2(num4, 0f); array2[i * 2 + 1] = new Vector2(num4, 1f); } int num5 = 0; for (int j = 1; j < count; j++) { array4[num5++] = j * 2 - 2; array4[num5++] = j * 2 - 1; array4[num5++] = j * 2; array4[num5++] = j * 2 + 1; array4[num5++] = j * 2; array4[num5++] = j * 2 - 1; } Mesh val7 = new Mesh { vertices = array, uv = array2, colors = array3, triangles = array4 }; val7.RecalculateBounds(); val3.sharedMesh = val7; Color color = (val4.HasProperty("_Color") ? val4.color : Color.white); color = ApplyVisualTint(color, tint, alphaFactor); if (val4.HasProperty("_Color")) { val4.color = color; } _visualRibbons.Add(new RibbonVisual(val7, val4, array3, color)); } } private static List BuildSmoothSamples(IReadOnlyList samples, int subdivisions) { //IL_0079: 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_0087: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_00b1: 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) int count = samples.Count; int num = Mathf.Clamp(subdivisions, 0, 8) + 1; if (count < 4 || num <= 1) { return new List(samples); } List list = new List(count * num); for (int i = 0; i < count - 1; i++) { TrailSample trailSample = samples[Mathf.Max(0, i - 1)]; TrailSample trailSample2 = samples[i]; TrailSample trailSample3 = samples[i + 1]; TrailSample trailSample4 = samples[Mathf.Min(count - 1, i + 2)]; for (int j = 0; j < num; j++) { float num2 = (float)j / (float)num; list.Add(new TrailSample(CatmullRom(trailSample.Base, trailSample2.Base, trailSample3.Base, trailSample4.Base, num2), CatmullRom(trailSample.Tip, trailSample2.Tip, trailSample3.Tip, trailSample4.Tip, num2), Mathf.Lerp(trailSample2.Time, trailSample3.Time, num2))); } } list.Add(samples[count - 1]); return list; } private static Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { //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_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_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_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_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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_005e: 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: 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_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_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_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_0090: 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_009c: 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) float num = t * t; float num2 = num * t; return 0.5f * (2f * p1 + (-p0 + p2) * t + (2f * p0 - 5f * p1 + 4f * p2 - p3) * num + (-p0 + 3f * p1 - 3f * p2 + p3) * num2); } private void UpdateVisualFade() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_0099: 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_00ea: Unknown result type (might be due to invalid IL or missing references) if (_visualRibbons.Count == 0) { return; } float num = ((Time.time <= _endTime) ? 1f : Mathf.Clamp01((_visualEndTime - Time.time) / 1f)); foreach (RibbonVisual visualRibbon in _visualRibbons) { if (!((Object)(object)visualRibbon.Mesh == (Object)null) && !((Object)(object)visualRibbon.Material == (Object)null)) { for (int i = 0; i < visualRibbon.WorkingColors.Length; i++) { Color val = visualRibbon.BaseColors[i]; val.a *= num; visualRibbon.WorkingColors[i] = val; } visualRibbon.Mesh.colors = visualRibbon.WorkingColors; if (visualRibbon.Material.HasProperty("_Color")) { Color baseMaterialColor = visualRibbon.BaseMaterialColor; baseMaterialColor.a *= num; visualRibbon.Material.color = baseMaterialColor; } } } } private void Finish() { if (!_finished) { _finished = true; if ((Object)(object)_visualObject != (Object)null) { Object.Destroy((Object)(object)_visualObject); _visualObject = null; } DestroyVisualRibbons(); Object.Destroy((Object)(object)this); } } private void OnDestroy() { if ((Object)(object)_visualObject != (Object)null) { Object.Destroy((Object)(object)_visualObject); _visualObject = null; } DestroyVisualRibbons(); UnregisterController(Attack, this); } private void DestroyVisualRibbons() { foreach (RibbonVisual visualRibbon in _visualRibbons) { if ((Object)(object)visualRibbon.Material != (Object)null) { Object.Destroy((Object)(object)visualRibbon.Material); } if ((Object)(object)visualRibbon.Mesh != (Object)null) { Object.Destroy((Object)(object)visualRibbon.Mesh); } } _visualRibbons.Clear(); } } internal const string ObserverFallbackVisualRpcName = "SecondaryAttacks_RiftTrailVisual"; private const bool HitThroughWalls = false; private const int MaxTrailSamples = 64; private const float MinTrailSampleDistance = 0.025f; private const int MaxTrailSubdivisions = 8; private const int MaxObserverTrailSamples = 32; private const float PostTriggerVisualCaptureSeconds = 0.18f; private const float VisualFadeSeconds = 1f; private const float FanStepDegrees = 4f; private const string ObserverSamplePayloadType = "S"; private const string ObserverFallbackPayloadType = "F"; private static readonly RaycastHit[] SweepHits = (RaycastHit[])(object)new RaycastHit[128]; private static readonly HashSet HitObjects = new HashSet(); private static readonly List HitTargets = new List(); private static readonly Dictionary ControllersByAttack = new Dictionary(); private static Material? _riftMaterial; private static int _attackMask; private static int _environmentMask; internal static bool CanHandle(Attack attack, SecondaryAttackDefinition definition) { if (attack != null && definition?.RiftTrail != null && (Object)(object)attack.m_character != (Object)null && attack.m_weapon?.m_shared != null) { return IsOneHandedSwordSecondaryAttack(attack); } return false; } private static bool IsOneHandedSwordSecondaryAttack(Attack attack) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)attack.m_attackType == 0 && (Object)(object)attack.m_attackProjectile == (Object)null && attack.m_attackRange > 0f && attack.m_attackRayWidth > 0f) { return string.Equals(attack.m_attackAnimation, "sword_secondary", StringComparison.OrdinalIgnoreCase); } return false; } internal static void BeginSampling(Attack attack, SecondaryAttackDefinition definition) { if (CanHandle(attack, definition) && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)attack.m_character) && !ControllersByAttack.ContainsKey(attack)) { RiftTrailController riftTrailController = ((Component)attack.m_character).gameObject.AddComponent(); riftTrailController.Initialize(attack, definition.RiftTrail); ControllersByAttack[attack] = riftTrailController; } } internal static void Trigger(Attack attack, SecondaryAttackDefinition definition) { if (CanHandle(attack, definition) && SecondaryAttackManager.HasCharacterAuthority((Character?)(object)attack.m_character)) { if (!ControllersByAttack.TryGetValue(attack, out RiftTrailController value) || (Object)(object)value == (Object)null) { value = ((Component)attack.m_character).gameObject.AddComponent(); value.Initialize(attack, definition.RiftTrail); ControllersByAttack[attack] = value; } value.Activate(); } } internal static void HandleObserverFallbackVisualRpc(Character character, ZNetView? nview, Vector3 origin, Vector3 forward, string payload) { //IL_006c: 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 (!((Object)(object)character == (Object)null) && !((Object)(object)nview == (Object)null) && nview.IsValid() && !nview.IsOwner()) { RiftTrailObserverVisualData data2; if (TryParseObserverSampleVisualPayload(payload, out var data)) { (((Component)character).GetComponent() ?? ((Component)character).gameObject.AddComponent()).InitializeSampled(character, data); } else if (TryParseObserverFallbackVisualPayload(payload, out data2)) { (((Component)character).GetComponent() ?? ((Component)character).gameObject.AddComponent()).Initialize(origin, forward, data2); } } } private static void SendObserverFallbackVisual(Attack attack, RiftTrailDefinition riftTrail) { //IL_0075: 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) if (!((Object)(object)attack?.m_character == (Object)null) && !(riftTrail.VisualScaleFactor <= 0f) && SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)attack.m_character, out ZNetView _, out ZDO _) && ZRoutedRpc.instance != null) { ResolveAttackShape(attack, out var origin, out var forward, out var range, out var angle, out var width); string payload = CreateObserverFallbackVisualPayload(range, angle, width, riftTrail.Duration, riftTrail.VisualScaleFactor, riftTrail.VisualForwardOffset, riftTrail.VisualAlphaFactor, riftTrail.VisualTint); SendObserverVisualPayload(attack, origin, forward, payload); } } private static bool SendObserverVisualPayload(Attack attack, Vector3 origin, Vector3 forward, string payload) { //IL_0049: 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) if (string.IsNullOrWhiteSpace(payload) || (Object)(object)attack?.m_character == (Object)null || !SecondaryAttackManager.TryGetCharacterZdo((Character?)(object)attack.m_character, out ZNetView nview, out ZDO _) || ZRoutedRpc.instance == null) { return false; } nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_RiftTrailVisual", new object[3] { origin, forward, payload }); return true; } private static void UnregisterController(Attack? attack, RiftTrailController controller) { if (attack != null && ControllersByAttack.TryGetValue(attack, out RiftTrailController value) && (Object)(object)value == (Object)(object)controller) { ControllersByAttack.Remove(attack); } } private static void ApplyTick(RiftTrailController controller) { //IL_004f: 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_009d: 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_00d6: 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) Attack attack = controller.Attack; RiftTrailDefinition riftTrail = controller.RiftTrail; Character character = (Character)(object)attack.m_character; ItemData weapon = attack.m_weapon; if ((Object)(object)character == (Object)null || weapon?.m_shared == null) { return; } HitObjects.Clear(); HitTargets.Clear(); try { float randomSkillFactor = character.GetRandomSkillFactor(weapon.m_shared.m_skillType); GatherSweepTargets(controller, attack); float skillFactor = randomSkillFactor * ResolveMultiTargetPenalty(HitTargets.Count); foreach (RiftTrailHitTarget hitTarget in HitTargets) { HitData val = CreateHitData(attack, hitTarget.Collider, hitTarget.Point, hitTarget.HitDirection, skillFactor, riftTrail); character.GetSEMan().ModifyAttack(weapon.m_shared.m_skillType, ref val); hitTarget.Destructible.Damage(val); CreateHitEffects(attack, weapon, hitTarget.Point, hitTarget.HitDirection); if ((Object)(object)hitTarget.Character != (Object)null && attack.m_attackHealthReturnHit > 0f && hitTarget.IsEnemy) { character.Heal(attack.m_attackHealthReturnHit, true); } controller.DrainDurabilityOnce(); } } finally { HitObjects.Clear(); HitTargets.Clear(); Array.Clear(SweepHits, 0, SweepHits.Length); } } private static void GatherSweepTargets(RiftTrailController controller, Attack attack) { //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_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_007b: 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_0099: 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) float num = controller.Angle * 0.5f; int num2 = Mathf.Max(1, Mathf.CeilToInt(controller.Angle / 4f)); float num3 = Mathf.Max(0.01f, controller.Width); float num4 = Mathf.Max(0.01f, controller.Range - num3); int attackMask = GetAttackMask(); for (int i = 0; i <= num2; i++) { Vector3 val = Quaternion.AngleAxis(Mathf.Lerp(0f - num, num, (float)i / (float)num2), Vector3.up) * controller.Forward; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { continue; } ((Vector3)(ref val)).Normalize(); int num5 = Physics.SphereCastNonAlloc(controller.Origin, num3, val, SweepHits, num4, attackMask, (QueryTriggerInteraction)0); Array.Sort(SweepHits, 0, num5, RaycastHitDistanceComparer.Instance); for (int j = 0; j < num5; j++) { TryAddSweepHit(controller, attack, SweepHits[j], val, out var blocked); if (blocked) { break; } } } } private static bool TryAddSweepHit(RiftTrailController controller, Attack attack, RaycastHit hit, Vector3 direction, out bool blocked) { //IL_00a6: 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_00a8: 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_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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) blocked = false; Collider collider = ((RaycastHit)(ref hit)).collider; Character character = (Character)(object)attack.m_character; if ((Object)(object)collider == (Object)null || (Object)(object)((Component)collider).gameObject == (Object)(object)((Component)character).gameObject) { return false; } GameObject val = Projectile.FindHitObject(collider); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)((Component)character).gameObject) { blocked = IsEnvironmentBlocker(collider); return false; } IDestructible component = val.GetComponent(); Character val2 = (Character)(object)((component is Character) ? component : null); if (component == null || (Object)(object)val2 == (Object)null) { blocked = IsEnvironmentBlocker(collider) || component != null; return false; } if (!IsValidCharacterTarget(attack, val2, out var isEnemy) || !HitObjects.Add(val)) { return false; } Vector3 val3 = ResolveSweepHitPoint(controller, hit, direction); Vector3 hitDirection = ResolveHitDirection(controller, val3); HitTargets.Add(new RiftTrailHitTarget(component, val2, collider, val3, hitDirection, isEnemy)); return true; } private static Vector3 ResolveSweepHitPoint(RiftTrailController controller, RaycastHit hit, Vector3 direction) { //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_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_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_0018: 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_0023: 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_0039: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((RaycastHit)(ref hit)).point; if (((Vector3)(ref val)).sqrMagnitude > 0f) { val = ((RaycastHit)(ref hit)).point - controller.Origin; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { return ((RaycastHit)(ref hit)).point; } } float num = Mathf.Clamp(((RaycastHit)(ref hit)).distance, 0f, Mathf.Max(0f, controller.Range)); return controller.Origin + direction * num; } private static bool IsEnvironmentBlocker(Collider collider) { int num = 1 << ((Component)collider).gameObject.layer; return (GetEnvironmentMask() & num) != 0; } private static float ResolveMultiTargetPenalty(int hitCount) { if (hitCount <= 1) { return 1f; } return 1f / ((float)hitCount * 0.75f); } private static void CreateHitEffects(Attack attack, ItemData weapon, Vector3 point, Vector3 hitDirection) { //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_000e: 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_002e: 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) Quaternion val = ((((Vector3)(ref hitDirection)).sqrMagnitude > 0.001f) ? Quaternion.LookRotation(((Vector3)(ref hitDirection)).normalized) : Quaternion.identity); weapon.m_shared.m_hitEffect.Create(point, val, (Transform)null, 1f, -1); attack.m_hitEffect.Create(point, val, (Transform)null, 1f, -1); } private static HitData CreateHitData(Attack attack, Collider collider, Vector3 hitPoint, Vector3 hitDirection, float skillFactor, RiftTrailDefinition riftTrail) { //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) return SecondaryAttackHitDataFactory.CreateMeleeHit(attack, collider, hitPoint, hitDirection, skillFactor, riftTrail.DamageFactor, riftTrail.PushFactor); } private static bool IsValidCharacterTarget(Attack attack, Character target, out bool isEnemy) { Character character = (Character)(object)attack.m_character; isEnemy = IsEnemy(character, target); if (!isEnemy) { return false; } if (((!attack.m_hitFriendly || character.IsTamed()) && !character.IsPlayer() && !isEnemy) || (!attack.m_weapon.m_shared.m_tamedOnly && character.IsPlayer() && !character.IsPVPEnabled() && !isEnemy) || (attack.m_weapon.m_shared.m_tamedOnly && !target.IsTamed())) { return false; } if (attack.m_weapon.m_shared.m_dodgeable && target.IsDodgeInvincible()) { if (target.IsPlayer()) { Character obj = ((target is Player) ? target : null); if (obj != null) { ((Player)obj).HitWhileDodging(); } } return false; } return true; } private static bool IsEnemy(Character attacker, Character target) { if (!BaseAI.IsEnemy(attacker, target)) { if ((Object)(object)target.GetBaseAI() != (Object)null && target.GetBaseAI().IsAggravatable()) { return attacker.IsPlayer(); } return false; } return true; } private static Vector3 ResolveHitDirection(RiftTrailController controller, Vector3 hitPoint) { //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_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_0016: 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_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) Vector3 val = Vector3.ProjectOnPlane(hitPoint - controller.Origin, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = controller.Forward; } return ((Vector3)(ref val)).normalized; } private static int GetEnvironmentMask() { if (_environmentMask == 0) { _environmentMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain" }); } return _environmentMask; } private static int GetAttackMask() { if (Attack.m_attackMask != 0) { return Attack.m_attackMask; } if (_attackMask == 0) { _attackMask = LayerMask.GetMask(new string[11] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); } return _attackMask; } private static Material? GetRiftMaterial() { //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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown if ((Object)(object)_riftMaterial != (Object)null) { return _riftMaterial; } Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Legacy Shaders/Particles/Alpha Blended") ?? Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { return null; } _riftMaterial = new Material(val) { color = new Color(0.55f, 0.9f, 1f, 0.65f) }; return _riftMaterial; } private static List ResolveObserverTrailMetadata(Character character) { List list = new List(); MeleeWeaponTrail[] trails = WeaponTrailAccess.GetTrails(character); foreach (MeleeWeaponTrail trail in trails) { list.Add(new ObserverTrailMetadata(WeaponTrailAccess.GetMaterial(trail), WeaponTrailAccess.GetColors(trail), WeaponTrailAccess.GetSizes(trail))); } return list; } private static void ResolveAttackShape(Attack attack, out Vector3 origin, out Vector3 forward, out float range, out float angle, out float width) { //IL_000d: 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_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_0036: 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_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_0047: 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_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_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) Transform transform = ((Component)attack.m_character).transform; Vector3 val = Vector3.ProjectOnPlane(transform.forward, Vector3.up); forward = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : transform.forward); origin = transform.position + Vector3.up * Mathf.Max(0f, attack.m_attackHeight) + transform.right * attack.m_attackOffset; range = Mathf.Max(0.1f, attack.m_attackRange); angle = Mathf.Clamp(attack.m_attackAngle, 1f, 360f); width = Mathf.Max(0.01f, attack.m_attackRayWidth + Mathf.Max(0f, attack.m_attackRayWidthCharExtra)); } private static string CreateObserverFallbackVisualPayload(float range, float angle, float width, float duration, float visualScaleFactor, float visualForwardOffset, float visualAlphaFactor, string visualTint) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return string.Join("|", "F", Mathf.Max(0.1f, range).ToString("R", invariantCulture), Mathf.Clamp(angle, 1f, 360f).ToString("R", invariantCulture), Mathf.Max(0.01f, width).ToString("R", invariantCulture), Mathf.Max(0.05f, duration).ToString("R", invariantCulture), Mathf.Max(0.01f, visualScaleFactor).ToString("R", invariantCulture), visualForwardOffset.ToString("R", invariantCulture), Mathf.Max(0f, visualAlphaFactor).ToString("R", invariantCulture), (visualTint ?? "#ffffff").Replace("|", string.Empty)); } private static bool TryParseObserverFallbackVisualPayload(string payload, out RiftTrailObserverVisualData data) { data = default(RiftTrailObserverVisualData); string[] array = payload.Split('|'); int num = ((array.Length != 0 && string.Equals(array[0], "F", StringComparison.OrdinalIgnoreCase)) ? 1 : 0); if (array.Length < num + 7) { return false; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (!float.TryParse(array[num], NumberStyles.Float, invariantCulture, out var result) || !float.TryParse(array[num + 1], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[num + 2], NumberStyles.Float, invariantCulture, out var result3) || !float.TryParse(array[num + 3], NumberStyles.Float, invariantCulture, out var result4) || !float.TryParse(array[num + 4], NumberStyles.Float, invariantCulture, out var result5) || !float.TryParse(array[num + 5], NumberStyles.Float, invariantCulture, out var result6) || !float.TryParse(array[num + 6], NumberStyles.Float, invariantCulture, out var result7)) { return false; } data = new RiftTrailObserverVisualData(Mathf.Max(0.1f, result), Mathf.Clamp(result2, 1f, 360f), Mathf.Max(0.01f, result3), Mathf.Max(0.05f, result4), Mathf.Max(0.01f, result5), result6, Mathf.Max(0f, result7), (array.Length >= num + 8) ? array[num + 7] : "#ffffff"); return true; } private static string CreateObserverSampleVisualPayload(RiftTrailDefinition riftTrail, IReadOnlyList ribbons) { //IL_0141: 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_0162: 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_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_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_01c5: 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_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) if (ribbons.Count == 0) { return string.Empty; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; List list = new List(8 + ribbons.Count * 32 * 7) { "S", Mathf.Max(0.05f, riftTrail.Duration).ToString("R", invariantCulture), Mathf.Max(0.01f, riftTrail.VisualScaleFactor).ToString("R", invariantCulture), riftTrail.VisualForwardOffset.ToString("R", invariantCulture), Mathf.Max(0f, riftTrail.VisualAlphaFactor).ToString("R", invariantCulture), (riftTrail.VisualTint ?? "#ffffff").Replace("|", string.Empty), ribbons.Count.ToString(invariantCulture) }; foreach (RiftTrailObserverSampleRibbonData ribbon in ribbons) { list.Add(ribbon.Samples.Count.ToString(invariantCulture)); foreach (RiftTrailObserverSample sample in ribbon.Samples) { list.Add(sample.Base.x.ToString("R", invariantCulture)); list.Add(sample.Base.y.ToString("R", invariantCulture)); list.Add(sample.Base.z.ToString("R", invariantCulture)); list.Add(sample.Tip.x.ToString("R", invariantCulture)); list.Add(sample.Tip.y.ToString("R", invariantCulture)); list.Add(sample.Tip.z.ToString("R", invariantCulture)); list.Add(Mathf.Clamp01(sample.NormalizedAge).ToString("R", invariantCulture)); } } return string.Join("|", list); } private static bool TryParseObserverSampleVisualPayload(string payload, out RiftTrailObserverSampleVisualData data) { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) data = default(RiftTrailObserverSampleVisualData); string[] array = payload.Split('|'); if (array.Length < 7 || !string.Equals(array[0], "S", StringComparison.OrdinalIgnoreCase)) { return false; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (!float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result) || !float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[3], NumberStyles.Float, invariantCulture, out var result3) || !float.TryParse(array[4], NumberStyles.Float, invariantCulture, out var result4) || !int.TryParse(array[6], NumberStyles.Integer, invariantCulture, out var result5) || result5 <= 0 || result5 > 8) { return false; } List list = new List(result5); int num = 7; for (int i = 0; i < result5; i++) { if (num >= array.Length || !int.TryParse(array[num++], NumberStyles.Integer, invariantCulture, out var result6) || result6 < 2 || result6 > 32) { return false; } List list2 = new List(result6); for (int j = 0; j < result6; j++) { if (num + 6 >= array.Length || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result7) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result8) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result9) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result10) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result11) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result12) || !float.TryParse(array[num++], NumberStyles.Float, invariantCulture, out var result13)) { return false; } list2.Add(new RiftTrailObserverSample(new Vector3(result7, result8, result9), new Vector3(result10, result11, result12), Mathf.Clamp01(result13))); } list.Add(new RiftTrailObserverSampleRibbonData(list2)); } data = new RiftTrailObserverSampleVisualData(Mathf.Max(0.05f, result), Mathf.Max(0.01f, result2), result3, Mathf.Max(0f, result4), array[5], list); return true; } private static Color ResolveVisualTint(RiftTrailDefinition riftTrail) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ResolveVisualTint(riftTrail.VisualTint); } private static Color ResolveVisualTint(string visualTint) { //IL_0019: 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) Color result = default(Color); if (!string.IsNullOrWhiteSpace(visualTint) && ColorUtility.TryParseHtmlString(visualTint.Trim(), ref result)) { return result; } return Color.white; } private static Color ApplyVisualTint(Color color, Color tint, float alphaFactor) { //IL_0009: 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_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) //IL_0046: Unknown result type (might be due to invalid IL or missing references) color.r *= tint.r; color.g *= tint.g; color.b *= tint.b; color.a *= tint.a * alphaFactor; return color; } private static Color EvaluateTrailColor(Color[]? colors, float normalizedAge) { //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_0012: 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_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_0020: Unknown result type (might be due to invalid IL or missing references) if (colors == null || colors.Length == 0) { return Color.Lerp(Color.white, Color.clear, normalizedAge); } if (colors.Length == 1) { return colors[0]; } float num = Mathf.Clamp01(normalizedAge) * (float)(colors.Length - 1); int num2 = Mathf.Clamp(Mathf.FloorToInt(num), 0, colors.Length - 1); int num3 = Mathf.Clamp(Mathf.CeilToInt(num), 0, colors.Length - 1); float num4 = Mathf.InverseLerp((float)num2, (float)num3, num); return Color.Lerp(colors[num2], colors[num3], num4); } private static float EvaluateTrailSize(float[]? sizes, float normalizedAge) { if (sizes == null || sizes.Length == 0) { return 1f; } if (sizes.Length == 1) { return sizes[0]; } float num = Mathf.Clamp01(normalizedAge) * (float)(sizes.Length - 1); int num2 = Mathf.Clamp(Mathf.FloorToInt(num), 0, sizes.Length - 1); int num3 = Mathf.Clamp(Mathf.CeilToInt(num), 0, sizes.Length - 1); float num4 = Mathf.InverseLerp((float)num2, (float)num3, num); return Mathf.Lerp(sizes[num2], sizes[num3], num4); } } internal sealed class SecondaryAttackCompiledSnapshot { public static readonly SecondaryAttackCompiledSnapshot Empty = new SecondaryAttackCompiledSnapshot(0, new Dictionary(StringComparer.OrdinalIgnoreCase), new Dictionary(StringComparer.OrdinalIgnoreCase), new Dictionary(StringComparer.OrdinalIgnoreCase), null, new Dictionary(StringComparer.OrdinalIgnoreCase)); public int SnapshotId { get; } public IReadOnlyDictionary Weapons { get; } public IReadOnlyDictionary GlobalRangedPresets { get; } public IReadOnlyDictionary GlobalBloodMagicPresets { get; } public NormalizedWeaponConfig? GlobalMeleeFallback { get; } public IReadOnlyDictionary MagicSummons { get; } public SecondaryAttackCompiledSnapshot(int snapshotId, IReadOnlyDictionary weapons, IReadOnlyDictionary globalRangedPresets, IReadOnlyDictionary globalBloodMagicPresets, NormalizedWeaponConfig? globalMeleeFallback, IReadOnlyDictionary magicSummons) { SnapshotId = snapshotId; Weapons = CopyAsReadOnly(weapons); GlobalRangedPresets = CopyAsReadOnly(globalRangedPresets); GlobalBloodMagicPresets = CopyAsReadOnly(globalBloodMagicPresets); GlobalMeleeFallback = globalMeleeFallback?.Clone(); MagicSummons = CopyAsReadOnly(magicSummons); } private static IReadOnlyDictionary CopyAsReadOnly(IReadOnlyDictionary source) { if (source == null) { throw new ArgumentNullException("source"); } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in source) { item.Deconstruct(out var key, out var value); string key2 = key; TValue value2 = value; dictionary[key2] = value2; } return new ReadOnlyDictionary(dictionary); } } internal sealed class SecondaryAttackAppliedWorldSnapshot { public static readonly SecondaryAttackAppliedWorldSnapshot Empty = new SecondaryAttackAppliedWorldSnapshot(SecondaryAttackCompiledSnapshot.Empty, new Dictionary(StringComparer.OrdinalIgnoreCase), 0); public SecondaryAttackCompiledSnapshot CompiledSnapshot { get; } public int SnapshotId => CompiledSnapshot.SnapshotId; public int ApplyRevision { get; } public IReadOnlyDictionary DefinitionsByPrefabName { get; } public SecondaryAttackAppliedWorldSnapshot(SecondaryAttackCompiledSnapshot compiledSnapshot, IReadOnlyDictionary definitionsByPrefabName, int applyRevision) { CompiledSnapshot = compiledSnapshot ?? throw new ArgumentNullException("compiledSnapshot"); DefinitionsByPrefabName = definitionsByPrefabName ?? throw new ArgumentNullException("definitionsByPrefabName"); ApplyRevision = applyRevision; } } internal static class SecondaryAttackCompendiumManager { private const string PageTopic = "$sa_compendium_title"; private const string BodyIconPrefix = "SecondaryAttacks_CompendiumPresetIcon_"; private const char IconMarker = '*'; private const float IconSize = 18f; private const float IconTextGap = 5f; internal static void AddPresetPage(TextsDialog dialog) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (dialog?.m_texts != null) { dialog.m_texts.RemoveAll((TextInfo text) => IsPresetPage(text?.m_topic)); if (SecondaryAttackPresetCatalog.Entries.Count != 0) { dialog.m_texts.Add(new TextInfo("$sa_compendium_title", BuildPageText())); } } } internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info) { //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_0081: 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) if ((Object)(object)dialog?.m_textArea == (Object)null || info == null) { return; } ClearPageContentIcons(dialog); if (!IsPresetPage(info.m_topic)) { return; } 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; int num = 0; for (int i = 0; i < textInfo.characterCount; i++) { if (num >= SecondaryAttackPresetCatalog.Entries.Count) { break; } TMP_CharacterInfo val2 = textInfo.characterInfo[i]; if (val2.character == '*') { SecondaryAttackPresetInfo secondaryAttackPresetInfo = SecondaryAttackPresetCatalog.Entries[num]; AttachBodyIcon(val, textArea.rectTransform, val2, SecondaryAttackPresetCatalog.ResolveIcon(secondaryAttackPresetInfo.Key), num); num++; } } } private static string BuildPageText() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("").Append(SecondaryAttackLocalization.Localize("$sa_compendium_intro", "Attack preset cooldowns are reduced according to the equipped weapon's skill level.")).Append("\n\n"); stringBuilder.Append("").Append(SecondaryAttackLocalization.Localize("$sa_compendium_quickstep_name", "Quickstep")).Append("\n\n") .Append(SecondaryAttackLocalization.Localize("$sa_compendium_quickstep_description", "Quickstep is available while using fist or knife weapons. Press Dodge twice to transition from Quickstep into a regular dodge.")) .Append("\n\n\n"); SecondaryAttackPresetGroup? secondaryAttackPresetGroup = null; foreach (SecondaryAttackPresetInfo entry in SecondaryAttackPresetCatalog.Entries) { if (secondaryAttackPresetGroup != entry.Group) { if (secondaryAttackPresetGroup.HasValue) { stringBuilder.Append('\n'); } stringBuilder.Append("").Append(LocalizeGroupHeading(entry.Group)).Append("\n\n"); secondaryAttackPresetGroup = entry.Group; } stringBuilder.Append("").Append('*').Append(" ") .Append("") .Append(SecondaryAttackLocalization.Localize(entry.NameToken, entry.FallbackName)) .Append("\n ") .Append(SecondaryAttackLocalization.Localize(entry.DescriptionToken, entry.FallbackDescription)) .Append("\n\n"); } return stringBuilder.ToString().TrimEnd(); } private static string LocalizeGroupHeading(SecondaryAttackPresetGroup group) { return group switch { SecondaryAttackPresetGroup.Ranged => SecondaryAttackLocalization.Localize("$sa_compendium_group_ranged", "Ranged Presets"), SecondaryAttackPresetGroup.Melee => SecondaryAttackLocalization.Localize("$sa_compendium_group_melee", "Melee Presets"), SecondaryAttackPresetGroup.BloodMagic => SecondaryAttackLocalization.Localize("$sa_compendium_group_blood_magic", "Blood Magic Presets"), SecondaryAttackPresetGroup.SummonQuality => SecondaryAttackLocalization.Localize("$sa_compendium_group_summon_quality", "Magic Summon Quality"), _ => "", }; } private static void AttachBodyIcon(RectTransform content, RectTransform textArea, TMP_CharacterInfo marker, Sprite? sprite, int index) { //IL_005f: 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_0075: 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_0088: 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_0094: 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_00a0: 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_00b5: 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_00ca: 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_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_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_00f7: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_010c: 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_011c: 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(string.Format("{0}{1}", "SecondaryAttacks_CompendiumPresetIcon_", index), 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("SecondaryAttacks_CompendiumPresetIcon_", StringComparison.Ordinal)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } } private static bool IsPresetPage(string? topic) { return string.Equals(topic, "$sa_compendium_title", StringComparison.Ordinal); } } [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] internal static class SecondaryAttackTextsDialogUpdateTextsListPatch { private static void Postfix(TextsDialog __instance) { SecondaryAttackCompendiumManager.AddPresetPage(__instance); } } [HarmonyPatch(typeof(TextsDialog), "ShowText", new Type[] { typeof(TextInfo) })] internal static class SecondaryAttackTextsDialogShowTextPatch { private static void Postfix(TextsDialog __instance, TextInfo text) { SecondaryAttackCompendiumManager.RefreshPageContentIcons(__instance, text); } } internal enum SecondaryAttackYamlDomainId { Ranged, Melee, BloodMagic } internal sealed class SecondaryAttackYamlDomain { public SecondaryAttackYamlDomainId Id { get; } public string FileName { get; } public string FilePath { get; } public Func GetDefaultContents { get; } internal SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId id, string fileName, string filePath, Func getDefaultContents) { Id = id; FileName = fileName; FilePath = filePath; GetDefaultContents = getDefaultContents; } } internal static class SecondaryAttackYamlDomainRegistry { internal const string ConfigDirectoryName = "SecondaryAttacks"; internal const string RangedYamlFileName = "SecondaryAttacks.Ranged.yml"; internal const string MeleeYamlFileName = "SecondaryAttacks.Melee.yml"; internal const string BloodMagicYamlFileName = "SecondaryAttacks.BloodMagic.yml"; internal const string AnimationReferenceFileName = "SecondaryAttacks_AnimationReferences.txt"; internal const string SyncedYamlEnvelopeIdentifier = "secondary_attack_yaml_envelope"; internal static readonly string ConfigDirectoryPath = Path.Combine(Paths.ConfigPath, "SecondaryAttacks"); internal static readonly string RangedYamlFilePath = Path.Combine(ConfigDirectoryPath, "SecondaryAttacks.Ranged.yml"); internal static readonly string MeleeYamlFilePath = Path.Combine(ConfigDirectoryPath, "SecondaryAttacks.Melee.yml"); internal static readonly string BloodMagicYamlFilePath = Path.Combine(ConfigDirectoryPath, "SecondaryAttacks.BloodMagic.yml"); internal static readonly string AnimationReferenceFilePath = Path.Combine(ConfigDirectoryPath, "SecondaryAttacks_AnimationReferences.txt"); private static readonly SecondaryAttackYamlDomain[] OrderedDomains = new SecondaryAttackYamlDomain[3] { new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.Ranged, "SecondaryAttacks.Ranged.yml", RangedYamlFilePath, () => SecondaryAttackDefaultYamlResources.Load("SecondaryAttacks.Ranged.yml")), new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.Melee, "SecondaryAttacks.Melee.yml", MeleeYamlFilePath, () => SecondaryAttackDefaultYamlResources.Load("SecondaryAttacks.Melee.yml")), new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.BloodMagic, "SecondaryAttacks.BloodMagic.yml", BloodMagicYamlFilePath, () => SecondaryAttackDefaultYamlResources.Load("SecondaryAttacks.BloodMagic.yml")) }; private static readonly Dictionary DomainsById = OrderedDomains.ToDictionary((SecondaryAttackYamlDomain domain) => domain.Id); public static IReadOnlyList Domains => OrderedDomains; public static SecondaryAttackYamlDomain Get(SecondaryAttackYamlDomainId id) { return DomainsById[id]; } } internal sealed class SecondaryAttackYamlTexts { private readonly Dictionary _texts; public SecondaryAttackYamlTexts(IReadOnlyDictionary texts) { _texts = new Dictionary(); foreach (KeyValuePair text in texts) { _texts[text.Key] = text.Value; } foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { if (!_texts.ContainsKey(domain.Id)) { _texts[domain.Id] = string.Empty; } } } public string Get(SecondaryAttackYamlDomainId id) { if (!_texts.TryGetValue(id, out string value)) { return string.Empty; } return value; } public string GetContentFingerprint() { return ToEnvelope(); } internal string ToEnvelope() { StringBuilder stringBuilder = new StringBuilder(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { string text = Get(domain.Id); stringBuilder.Append((int)domain.Id).Append(':').Append(text.Length) .Append(':') .Append(text) .Append('\n'); } return stringBuilder.ToString(); } internal static bool TryFromEnvelope(string envelope, out SecondaryAttackYamlTexts? yamlTexts) { yamlTexts = null; if (string.IsNullOrEmpty(envelope)) { return false; } Dictionary dictionary = new Dictionary(); int num = 0; foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { string text = $"{(int)domain.Id}:"; if (num + text.Length > envelope.Length || !string.Equals(envelope.Substring(num, text.Length), text, StringComparison.Ordinal)) { return false; } num += text.Length; int num2 = envelope.IndexOf(':', num); if (num2 < 0 || !int.TryParse(envelope.Substring(num, num2 - num), NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 0) { return false; } num = num2 + 1; if (result > envelope.Length - num) { return false; } dictionary[domain.Id] = envelope.Substring(num, result); num += result; if (num >= envelope.Length || envelope[num] != '\n') { return false; } num++; } if (num != envelope.Length) { return false; } yamlTexts = new SecondaryAttackYamlTexts(dictionary); return true; } } internal sealed class SecondaryAttackParsedYaml { public IReadOnlyDictionary Ranged { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public IReadOnlyDictionary Melee { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public IReadOnlyDictionary BloodMagic { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } internal static class SecondaryAttackConfigLoader { private sealed class BufferedYamlParser : IParser { private readonly List _events; private int _index = -1; public ParsingEvent Current { get { if (_index < 0 || _index >= _events.Count) { return null; } return _events[_index]; } } public BufferedYamlParser(IReadOnlyList nodeEvents) { _events = new List(nodeEvents.Count + 4) { new YamlDotNet.Core.Events.StreamStart(), new YamlDotNet.Core.Events.DocumentStart() }; _events.AddRange(nodeEvents); _events.Add(new YamlDotNet.Core.Events.DocumentEnd(isImplicit: true)); _events.Add(new YamlDotNet.Core.Events.StreamEnd()); } public bool MoveNext() { if (_index + 1 >= _events.Count) { return false; } _index++; return true; } } private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); public static void EnsureLocalFilesExist() { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { if (!File.Exists(domain.FilePath)) { File.WriteAllText(domain.FilePath, domain.GetDefaultContents()); } } } public static SecondaryAttackYamlTexts ReadLocalYamlTexts() { Dictionary dictionary = new Dictionary(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { dictionary[domain.Id] = File.ReadAllText(domain.FilePath); } return new SecondaryAttackYamlTexts(dictionary); } public static bool TryReadStableLocalYamlTexts(out SecondaryAttackYamlTexts? yamlTexts) { yamlTexts = null; Dictionary dictionary = new Dictionary(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { FileInfo fileInfo = new FileInfo(domain.FilePath); fileInfo.Refresh(); if (!fileInfo.Exists) { return false; } dictionary[domain.Id] = (fileInfo.Length, fileInfo.LastWriteTimeUtc); } Dictionary dictionary2 = new Dictionary(); foreach (SecondaryAttackYamlDomain domain2 in SecondaryAttackYamlDomainRegistry.Domains) { dictionary2[domain2.Id] = File.ReadAllText(domain2.FilePath); } foreach (SecondaryAttackYamlDomain domain3 in SecondaryAttackYamlDomainRegistry.Domains) { FileInfo fileInfo2 = new FileInfo(domain3.FilePath); fileInfo2.Refresh(); (long, DateTime) tuple = dictionary[domain3.Id]; if (!fileInfo2.Exists || fileInfo2.Length != tuple.Item1 || fileInfo2.LastWriteTimeUtc != tuple.Item2) { return false; } } yamlTexts = new SecondaryAttackYamlTexts(dictionary2); return true; } public static bool TryCompileSnapshot(int snapshotId, SecondaryAttackYamlTexts yamlTexts, out SecondaryAttackCompiledSnapshot? snapshot) { snapshot = null; if (!TryParseYamlTexts(yamlTexts, out SecondaryAttackParsedYaml parsedYaml)) { return false; } snapshot = SecondaryAttackConfigCompiler.Compile(snapshotId, parsedYaml); return true; } private static bool TryParseYamlTexts(SecondaryAttackYamlTexts yamlTexts, out SecondaryAttackParsedYaml? parsedYaml) { parsedYaml = null; if (!TryParseDictionary(SecondaryAttackYamlDomainId.Ranged, yamlTexts.Get(SecondaryAttackYamlDomainId.Ranged), out Dictionary parsed)) { return false; } if (!TryParseDictionary(SecondaryAttackYamlDomainId.Melee, yamlTexts.Get(SecondaryAttackYamlDomainId.Melee), out Dictionary parsed2)) { return false; } if (!TryParseDictionary(SecondaryAttackYamlDomainId.BloodMagic, yamlTexts.Get(SecondaryAttackYamlDomainId.BloodMagic), out Dictionary parsed3)) { return false; } parsedYaml = new SecondaryAttackParsedYaml { Ranged = parsed, Melee = parsed2, BloodMagic = parsed3 }; return true; } private static bool TryParseDictionary(SecondaryAttackYamlDomainId domainId, string yamlText, out Dictionary? parsed) { parsed = null; if (string.IsNullOrWhiteSpace(yamlText)) { parsed = new Dictionary(StringComparer.OrdinalIgnoreCase); return true; } try { parsed = new Dictionary(StringComparer.OrdinalIgnoreCase); Parser parser = new Parser(new StringReader(yamlText)); if (!TryMoveToRootMapping(parser, out var hasRootMapping)) { return true; } if (!hasRootMapping) { return true; } SecondaryAttackYamlDomain secondaryAttackYamlDomain = SecondaryAttackYamlDomainRegistry.Get(domainId); while (parser.MoveNext()) { if (parser.Current is YamlDotNet.Core.Events.Comment) { continue; } if (parser.Current is MappingEnd) { break; } string text = ((parser.Current as YamlDotNet.Core.Events.Scalar) ?? throw new InvalidDataException("Root keys must be scalar prefab names.")).Value?.Trim() ?? ""; if (!TryMoveToNextNode(parser)) { throw new InvalidDataException("Root block '" + text + "' is missing a value."); } List nodeEvents = CollectCurrentNode(parser); if (string.IsNullOrWhiteSpace(text) || (!text.Equals("Global", StringComparison.OrdinalIgnoreCase) && IsRootEntryDisabled(nodeEvents))) { continue; } try { if (parsed.ContainsKey(text)) { SecondaryAttacksPlugin.ModLogger.LogError((object)("Failed to parse " + secondaryAttackYamlDomain.FileName + ": Duplicate enabled key " + text + ". Keep only one enabled entry for a prefab; entries with enabled: false are ignored before duplicate checks.")); return false; } Dictionary? obj = parsed; T val = DeserializeRootEntry(secondaryAttackYamlDomain, text, nodeEvents); obj[text] = ((val != null) ? val : Activator.CreateInstance()); } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + secondaryAttackYamlDomain.FileName + " block '" + text + "': " + ex.Message)); } } return true; } catch (Exception ex2) { SecondaryAttackYamlDomain secondaryAttackYamlDomain2 = SecondaryAttackYamlDomainRegistry.Get(domainId); SecondaryAttacksPlugin.ModLogger.LogError((object)("Failed to parse " + secondaryAttackYamlDomain2.FileName + ": " + ex2.Message)); return false; } } private static bool TryMoveToRootMapping(Parser parser, out bool hasRootMapping) { hasRootMapping = false; while (parser.MoveNext()) { ParsingEvent current = parser.Current; if ((!(current is YamlDotNet.Core.Events.StreamStart) && !(current is YamlDotNet.Core.Events.DocumentStart) && !(current is YamlDotNet.Core.Events.Comment)) || 1 == 0) { current = parser.Current; if ((current is YamlDotNet.Core.Events.StreamEnd || current is YamlDotNet.Core.Events.DocumentEnd) ? true : false) { return true; } if (parser.Current is MappingStart) { hasRootMapping = true; return true; } throw new InvalidDataException("The YAML document root must be a mapping of prefab names."); } } return true; } private static bool TryMoveToNextNode(Parser parser) { while (parser.MoveNext()) { if (!(parser.Current is YamlDotNet.Core.Events.Comment)) { return true; } } return false; } private static List CollectCurrentNode(Parser parser) { ParsingEvent parsingEvent = parser.Current ?? throw new InvalidDataException("YAML parser is not positioned on a node."); List list = new List { parsingEvent }; if (!(parsingEvent is MappingStart) && !(parsingEvent is SequenceStart)) { return list; } int num = 1; while (num > 0 && parser.MoveNext()) { parsingEvent = parser.Current ?? throw new InvalidDataException("YAML parser produced a null event."); list.Add(parsingEvent); if ((parsingEvent is MappingStart || parsingEvent is SequenceStart) ? true : false) { num++; } else if ((parsingEvent is MappingEnd || parsingEvent is SequenceEnd) ? true : false) { num--; } } if (num != 0) { throw new InvalidDataException("YAML node ended before its mapping or sequence was closed."); } return list; } private static bool IsRootEntryDisabled(IReadOnlyList nodeEvents) { if (nodeEvents.Count == 0 || !(nodeEvents[0] is MappingStart)) { return false; } int num = 1; while (num < nodeEvents.Count) { if (nodeEvents[num] is YamlDotNet.Core.Events.Comment) { num++; continue; } if (nodeEvents[num] is MappingEnd) { return false; } if (!(nodeEvents[num] is YamlDotNet.Core.Events.Scalar scalar)) { return false; } string text = scalar.Value?.Trim() ?? ""; for (num++; num < nodeEvents.Count && nodeEvents[num] is YamlDotNet.Core.Events.Comment; num++) { } if (num >= nodeEvents.Count) { return false; } int num2 = num; int nodeEndIndex = GetNodeEndIndex(nodeEvents, num2); if (text.Equals("enabled", StringComparison.OrdinalIgnoreCase) && nodeEvents[num2] is YamlDotNet.Core.Events.Scalar scalar2 && IsFalseScalar(scalar2.Value)) { return true; } num = nodeEndIndex; } return false; } private static int GetNodeEndIndex(IReadOnlyList nodeEvents, int startIndex) { ParsingEvent parsingEvent = nodeEvents[startIndex]; if (!(parsingEvent is MappingStart) && !(parsingEvent is SequenceStart)) { return startIndex + 1; } int num = 1; int i; for (i = startIndex + 1; i < nodeEvents.Count; i++) { if (num <= 0) { break; } parsingEvent = nodeEvents[i]; if ((parsingEvent is MappingStart || parsingEvent is SequenceStart) ? true : false) { num++; continue; } parsingEvent = nodeEvents[i]; if ((parsingEvent is MappingEnd || parsingEvent is SequenceEnd) ? true : false) { num--; } } return i; } private static bool IsFalseScalar(string? value) { string text = value?.Trim() ?? ""; if (!text.Equals("false", StringComparison.OrdinalIgnoreCase) && !text.Equals("off", StringComparison.OrdinalIgnoreCase) && !text.Equals("no", StringComparison.OrdinalIgnoreCase)) { return text.Equals("0", StringComparison.OrdinalIgnoreCase); } return true; } private static T? DeserializeRootEntry(SecondaryAttackYamlDomain domain, string rootKey, IReadOnlyList nodeEvents) { YamlNode node = DeserializeYamlNode(nodeEvents); return DeserializeRootEntry(domain, rootKey, node); } private static T? DeserializeRootEntry(SecondaryAttackYamlDomain domain, string rootKey, YamlNode node) { if (typeof(T) == typeof(MeleeWeaponConfig)) { return (T)(object)DeserializeMeleeWeaponConfig(domain, rootKey, node); } return DeserializeYamlNode(node); } private static MeleeWeaponConfig DeserializeMeleeWeaponConfig(SecondaryAttackYamlDomain domain, string rootKey, YamlNode node) { if (!(node is YamlMappingNode yamlMappingNode)) { return DeserializeYamlNode(node) ?? new MeleeWeaponConfig(); } MeleeWeaponConfig meleeWeaponConfig = new MeleeWeaponConfig(); foreach (KeyValuePair child in yamlMappingNode.Children) { string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text)) { continue; } string text2 = rootKey + "." + text; switch (text) { case "enabled": { if (TryDeserializeBlock(domain, text2, child.Value, out var value18)) { meleeWeaponConfig.Enabled = value18; } break; } case "preset": { if (TryDeserializeBlock(domain, text2, child.Value, out string value6)) { meleeWeaponConfig.Preset = value6 ?? ""; } break; } case "copyFrom": { if (TryDeserializeBlock(domain, text2, child.Value, out string value14)) { meleeWeaponConfig.CopyFrom = value14 ?? ""; } break; } case "animation": { if (TryDeserializeBlock(domain, text2, child.Value, out string value10)) { meleeWeaponConfig.Animation = value10 ?? ""; } break; } case "resourceMultiplier": { if (TryDeserializeBlock(domain, text2, child.Value, out var value2)) { meleeWeaponConfig.ResourceMultiplier = value2; } break; } case "outputMultiplier": { if (TryDeserializeBlock(domain, text2, child.Value, out var value16)) { meleeWeaponConfig.OutputMultiplier = value16; } break; } case "durabilityFactor": { if (TryDeserializeBlock(domain, text2, child.Value, out var value12)) { meleeWeaponConfig.DurabilityFactor = value12; } break; } case "sneakAmbush": { if (TryDeserializeBlock(domain, text2, child.Value, out SneakAmbushConfig value8)) { meleeWeaponConfig.SneakAmbush = value8; } break; } case "cleavingThrust": { if (TryDeserializeBlock(domain, text2, child.Value, out CleavingThrustConfig value4)) { meleeWeaponConfig.CleavingThrust = value4; } break; } case "spearRain": { if (TryDeserializeBlock(domain, text2, child.Value, out MeleeOnProjectileHitConfig value19)) { meleeWeaponConfig.SpearRain = value19; } break; } case "impactBurst": { if (TryDeserializeBlock(domain, text2, child.Value, out ImpactBurstConfig value17)) { meleeWeaponConfig.ImpactBurst = value17; } break; } case "boomerang": { if (TryDeserializeBlock(domain, text2, child.Value, out BoomerangConfig value15)) { meleeWeaponConfig.Boomerang = value15; } break; } case "spinningSweep": { if (TryDeserializeBlock(domain, text2, child.Value, out SpinningSweepConfig value13)) { meleeWeaponConfig.SpinningSweep = value13; } break; } case "launchSlam": { if (TryDeserializeBlock(domain, text2, child.Value, out LaunchSlamConfig value11)) { meleeWeaponConfig.LaunchSlam = value11; } break; } case "knockbackChain": { if (TryDeserializeBlock(domain, text2, child.Value, out KnockbackChainConfig value9)) { meleeWeaponConfig.KnockbackChain = value9; } break; } case "aftershock": { if (TryDeserializeBlock(domain, text2, child.Value, out AftershockConfig value7)) { meleeWeaponConfig.Aftershock = value7; } break; } case "riftTrail": { if (TryDeserializeBlock(domain, text2, child.Value, out RiftTrailConfig value5)) { meleeWeaponConfig.RiftTrail = value5; } break; } case "fractureLine": { if (TryDeserializeBlock(domain, text2, child.Value, out FractureLineConfig value3)) { meleeWeaponConfig.FractureLine = value3; } break; } case "harvestSweep": { if (TryDeserializeBlock(domain, text2, child.Value, out HarvestSweepConfig value)) { meleeWeaponConfig.HarvestSweep = value; } break; } default: SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + domain.FileName + " block '" + text2 + "': unknown field.")); break; } } return meleeWeaponConfig; } private static bool TryDeserializeBlock(SecondaryAttackYamlDomain domain, string blockPath, YamlNode node, out T? value) { try { value = DeserializeYamlNode(node); return true; } catch (Exception ex) { value = default(T); SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + domain.FileName + " block '" + blockPath + "': " + ex.Message)); return false; } } private static T? DeserializeYamlNode(YamlNode node) { using StringWriter stringWriter = new StringWriter(); new YamlStream(new YamlDocument(node)).Save(stringWriter, assignAnchors: false); return Deserializer.Deserialize(stringWriter.ToString()); } private static YamlNode DeserializeYamlNode(IReadOnlyList nodeEvents) { YamlStream yamlStream = new YamlStream(); yamlStream.Load(new BufferedYamlParser(nodeEvents)); if (yamlStream.Documents.Count <= 0) { return new YamlMappingNode(); } return yamlStream.Documents[0].RootNode; } } internal sealed class SecondaryAttackReloadDebouncer : IDisposable { internal const long DefaultDelayTicks = 2500000L; private static readonly double[] RetryDelayMilliseconds; private readonly object _lock = new object(); private readonly Func _action; private readonly string _operationName; private readonly System.Timers.Timer _timer; private bool _disposed; private int _attempt; private int _generation; internal SecondaryAttackReloadDebouncer(Func action, string operationName) { _action = action; _operationName = (string.IsNullOrWhiteSpace(operationName) ? "configuration reload" : operationName); _timer = new System.Timers.Timer(RetryDelayMilliseconds[0]) { AutoReset = false, SynchronizingObject = ThreadingHelper.SynchronizingObject }; _timer.Elapsed += OnElapsed; } internal void Schedule() { lock (_lock) { if (!_disposed) { _generation++; _attempt = 0; _timer.Stop(); _timer.Interval = RetryDelayMilliseconds[0]; _timer.Start(); } } } public void Dispose() { lock (_lock) { if (!_disposed) { _disposed = true; _timer.Stop(); _timer.Elapsed -= OnElapsed; _timer.Dispose(); } } } private void OnElapsed(object sender, ElapsedEventArgs e) { int generation; lock (_lock) { if (_disposed) { return; } generation = _generation; } bool flag; try { flag = _action(); } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogError((object)("Error during " + _operationName + ": " + ex.Message)); flag = false; } lock (_lock) { if (!(_disposed || generation != _generation || flag)) { _attempt++; if (_attempt >= RetryDelayMilliseconds.Length) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)$"Stopped automatic {_operationName} after {RetryDelayMilliseconds.Length} attempts. A later file or configuration event will start a new retry cycle."); return; } _timer.Stop(); _timer.Interval = RetryDelayMilliseconds[_attempt]; _timer.Start(); } } } static SecondaryAttackReloadDebouncer() { double[] obj = new double[4] { 0.0, 500.0, 1000.0, 2000.0 }; obj[0] = TimeSpan.FromTicks(2500000L).TotalMilliseconds; RetryDelayMilliseconds = obj; } } internal static class SecondaryAttackLocalization { internal const string HintDetonate = "$sa_hint_detonate"; internal const string HudEmpower = "$sa_hud_empower"; internal const string SummonNameFormat = "$sa_summon_name_format"; internal const string ItemTooltipSecondaryAttack = "$sa_item_tooltip_secondary_attack"; internal const string ItemTooltipSummonQuality = "$sa_item_tooltip_summon_quality"; internal static void Load() { Localizer.Load(); } internal static string Localize(string token, string fallback) { if (Localization.instance == null) { return fallback; } string text = Localization.instance.Localize(token); if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, token, StringComparison.Ordinal)) { return text; } return fallback; } internal static string Format(string token, string fallback, params object[] args) { string format = Localize(token, fallback); try { return string.Format(CultureInfo.InvariantCulture, format, args); } catch (FormatException) { return string.Format(CultureInfo.InvariantCulture, fallback, args); } } } internal static class SecondaryAttackItemTooltipSystem { private const string HeadingColor = "#FFD27A"; private const string DescriptionColor = "#D6D6D6"; internal static void AppendPresetDescriptions(ItemData item, ref string tooltip) { SecondaryAttackDefinition definition; bool flag = SecondaryAttackRuntimeFacade.TryGetDefinition(item, out definition); MagicSummonQualityPreset preset; bool flag2 = MagicSummonQualityPresetSystem.TryGetItemQualityPreset(item, out preset); if (!flag && !flag2) { return; } List presetKeys = new List((flag ? 3 : 0) + (flag2 ? 1 : 0)); HashSet seenPresetKeys = new HashSet(StringComparer.OrdinalIgnoreCase); if (flag) { AddPresetKey(presetKeys, seenPresetKeys, SecondaryAttackPresetCatalog.GetKey(definition.Behavior)); AddCompiledMeleePresetKeys(definition, presetKeys, seenPresetKeys); if (definition.HarvestSweep != null && definition.SpinningSweep == null) { AddPresetKey(presetKeys, seenPresetKeys, "harvestSweep"); } } if (flag2) { AddPresetKey(presetKeys, seenPresetKeys, (preset == MagicSummonQualityPreset.CountByQuality) ? "countByQuality" : "levelByQuality"); } string text = BuildPresetTooltip(presetKeys); if (!string.IsNullOrEmpty(text) && (string.IsNullOrEmpty(tooltip) || tooltip.IndexOf(text, StringComparison.Ordinal) < 0)) { string text2 = (string.IsNullOrEmpty(tooltip) ? "" : (tooltip.EndsWith("\n\n", StringComparison.Ordinal) ? "" : (tooltip.EndsWith("\n", StringComparison.Ordinal) ? "\n" : "\n\n"))); tooltip = (string.IsNullOrEmpty(tooltip) ? text : (tooltip + text2 + text)); } } private static void AddCompiledMeleePresetKeys(SecondaryAttackDefinition definition, ICollection presetKeys, ISet seenPresetKeys) { if (definition.SneakAmbush != null) { AddPresetKey(presetKeys, seenPresetKeys, "sneakAmbush"); } if (definition.CleavingThrust != null) { AddPresetKey(presetKeys, seenPresetKeys, "cleavingThrust"); } if (definition.LaunchSlam != null) { AddPresetKey(presetKeys, seenPresetKeys, "launchSlam"); } if (definition.KnockbackChain != null) { AddPresetKey(presetKeys, seenPresetKeys, "knockbackChain"); } if (definition.Aftershock != null) { AddPresetKey(presetKeys, seenPresetKeys, "aftershock"); } if (definition.RiftTrail != null) { AddPresetKey(presetKeys, seenPresetKeys, "riftTrail"); } if (definition.FractureLine != null) { AddPresetKey(presetKeys, seenPresetKeys, "fractureLine"); } if (definition.BehaviorType == SecondaryAttackBehaviorType.CopiedSecondary) { AddPresetKey(presetKeys, seenPresetKeys, definition.OnProjectileHit?.Preset); if (definition.Boomerang != null) { AddPresetKey(presetKeys, seenPresetKeys, "boomerang"); } } if (definition.SpinningSweep != null) { AddPresetKey(presetKeys, seenPresetKeys, "spinningSweep"); } } private static void AddPresetKey(ICollection presetKeys, ISet seenPresetKeys, string? presetKey) { if (!string.IsNullOrWhiteSpace(presetKey)) { string item = presetKey.Trim(); if (seenPresetKeys.Add(item)) { presetKeys.Add(item); } } } private static string BuildPresetTooltip(IReadOnlyList presetKeys) { StringBuilder stringBuilder = new StringBuilder(); foreach (string presetKey in presetKeys) { if (SecondaryAttackPresetCatalog.TryGet(presetKey, out SecondaryAttackPresetInfo info)) { if (stringBuilder.Length > 0) { stringBuilder.Append("\n\n"); } string value = ((info.Group == SecondaryAttackPresetGroup.SummonQuality) ? SecondaryAttackLocalization.Localize("$sa_item_tooltip_summon_quality", "Summon Quality") : SecondaryAttackLocalization.Localize("$sa_item_tooltip_secondary_attack", "Secondary Attack")); string value2 = SecondaryAttackLocalization.Localize(info.NameToken, info.FallbackName); string value3 = SecondaryAttackLocalization.Localize(info.DescriptionToken, info.FallbackDescription); stringBuilder.Append("") .Append(value) .Append(": ") .Append(value2) .Append("\n') .Append(value3) .Append(""); } } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] [HarmonyAfter(new string[] { "randyknapp.mods.epicloot", "org.bepinex.plugins.jewelcrafting", "MidnightsFX.EpicJewels" })] internal static class ItemDataGetTooltipSecondaryAttackPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(ItemData item, bool crafting, ref string __result) { if (!crafting && item != null && SecondaryAttacksPlugin.SecondaryAttackTooltipsEnabled.Value == SecondaryAttacksPlugin.Toggle.On) { SecondaryAttackItemTooltipSystem.AppendPresetDescriptions(item, ref __result); } } } internal enum SecondaryAttackPresetGroup { Ranged, Melee, BloodMagic, SummonQuality } internal static class SecondaryAttackPresetCatalog { private static readonly Dictionary EntriesByKey = BuildEntriesByKey(); internal static IReadOnlyList Entries { get; } = new <>z__ReadOnlyArray(new SecondaryAttackPresetInfo[26] { new SecondaryAttackPresetInfo("barrage", "barrage", SecondaryAttackPresetGroup.Ranged, "BowHuntsman", "Barrage", "Fires multiple projectiles across a spread or a laterally spaced formation."), new SecondaryAttackPresetInfo("volley", "volley", SecondaryAttackPresetGroup.Ranged, "CrossbowArbalest", "Volley", "Launches multiple projectiles in arcs so they land around the aimed point."), new SecondaryAttackPresetInfo("piercing", "piercing", SecondaryAttackPresetGroup.Ranged, "StaffLightning", "Piercing", "Fires the weapon's full projectile pattern through multiple characters, with damage falling after each hit."), new SecondaryAttackPresetInfo("scatter", "scatter", SecondaryAttackPresetGroup.Ranged, "StaffClusterbomb", "Scatter", "Fires a projectile that splits into a cone of ricocheting projectiles when it first hits a non-character surface."), new SecondaryAttackPresetInfo("spiral", "spiral", SecondaryAttackPresetGroup.Ranged, "StaffIceShards", "Spiral", "Fires projectiles in a rotating spiral around the aim direction."), new SecondaryAttackPresetInfo("sentinel", "sentinel", SecondaryAttackPresetGroup.Ranged, "StaffFireball", "Sentinel", "Creates orbiting projectiles that seek nearby enemies and launch themselves at a target."), new SecondaryAttackPresetInfo("meteor", "meteor", SecondaryAttackPresetGroup.Ranged, "StaffFireball", "Meteor", "Calls down projectiles from above across an area around the aimed point."), new SecondaryAttackPresetInfo("burst", "burst", SecondaryAttackPresetGroup.Ranged, "CrossbowArbalest", "Burst", "Repeats the weapon's full primary projectile pattern through successive firing animations."), new SecondaryAttackPresetInfo("stickyDetonator", "sticky_detonator", SecondaryAttackPresetGroup.Ranged, "BombOoze", "Sticky Detonator", "Throws charges that stick where they hit. Use Block to detonate all active charges."), new SecondaryAttackPresetInfo("overchargedBomb", "overcharged_bomb", SecondaryAttackPresetGroup.Ranged, "BombBile", "Overcharged Bomb", "Can consume additional bombs to launch a stronger bomb with increased damage and blast radius."), new SecondaryAttackPresetInfo("sneakAmbush", "sneak_ambush", SecondaryAttackPresetGroup.Melee, "KnifeWood", "Sneak Ambush", "Builds preparation while sneaking, charging faster with higher Sneak skill. The next valid secondary hit releases smoke that clears nearby enemies' awareness; more preparation increases its radius, how long affected enemies cannot sense you, and the reduction to their backstab cooldown."), new SecondaryAttackPresetInfo("riftTrail", "rift_trail", SecondaryAttackPresetGroup.Melee, "SwordWood", "Rift Trail", "Leaves the sword's swing trail active, repeatedly damaging enemies within the swept path."), new SecondaryAttackPresetInfo("cleavingThrust", "cleaving_thrust", SecondaryAttackPresetGroup.Melee, "THSwordWood", "Cleaving Thrust", "Extends a greatsword thrust into a long cleave that strikes every valid target along its path."), new SecondaryAttackPresetInfo("launchSlam", "launch_slam", SecondaryAttackPresetGroup.Melee, "MaceWood", "Launch Slam", "Launches the secondary-hit target upward, then deals area damage when it lands."), new SecondaryAttackPresetInfo("aftershock", "aftershock", SecondaryAttackPresetGroup.Melee, "SledgeWood", "Aftershock", "Follows the initial area slam with weakening shockwaves that advance forward."), new SecondaryAttackPresetInfo("knockbackChain", "knockback_chain", SecondaryAttackPresetGroup.Melee, "FistBjornClaw", "Knockback Chain", "Greatly increases kick knockback. Collisions pass diminishing damage and force through nearby enemies."), new SecondaryAttackPresetInfo("boomerang", "boomerang", SecondaryAttackPresetGroup.Melee, "AxeBronze", "Boomerang", "Throws the weapon along a curved return path, hitting multiple targets before returning to the wielder."), new SecondaryAttackPresetInfo("impactBurst", "impact_burst", SecondaryAttackPresetGroup.Melee, "Battleaxe", "Impact Burst", "Throws the weapon and releases a damaging, forceful burst around its impact point."), new SecondaryAttackPresetInfo("spearRain", "spear_rain", SecondaryAttackPresetGroup.Melee, "SpearWood", "Spear Rain", "Calls down additional spears after the thrown spear hits a character, guiding them toward the struck target."), new SecondaryAttackPresetInfo("spinningSweep", "spinning_sweep", SecondaryAttackPresetGroup.Melee, "AtgeirWood", "Spinning Sweep", "Sustains a repeating polearm spin until cancelled, the weapon changes, or the next attack cost cannot be paid."), new SecondaryAttackPresetInfo("fractureLine", "fracture_line", SecondaryAttackPresetGroup.Melee, "PickaxeAntler", "Fracture Line", "Opens a crack along the ground that repeatedly damages enemies and destructibles standing over it."), new SecondaryAttackPresetInfo("harvestSweep", "harvest_sweep", SecondaryAttackPresetGroup.Melee, "Scythe", "Harvest Sweep", "Sustains a repeating scythe sweep that harvests nearby crops and pickables."), new SecondaryAttackPresetInfo("summonEmpower", "summon_empower", SecondaryAttackPresetGroup.BloodMagic, "StaffSkeleton", "Summon Empower", "Temporarily increases matching nearby summons' non-crouching movement, acceleration, and turning speeds and accelerates their attack animations and AI attack cadence."), new SecondaryAttackPresetInfo("shieldConvert", "shield_convert", SecondaryAttackPresetGroup.BloodMagic, "StaffShield", "Shield Convert", "Removes active shields from the wielder and nearby non-hostile targets, restoring health from their remaining shield strength."), new SecondaryAttackPresetInfo("countByQuality", "count_by_quality", SecondaryAttackPresetGroup.SummonQuality, "StaffRedTroll", "Count by Quality", "Weapon quality increases the maximum number of simultaneous summons while summon level stays fixed."), new SecondaryAttackPresetInfo("levelByQuality", "level_by_quality", SecondaryAttackPresetGroup.SummonQuality, "StaffSkeleton", "Level by Quality", "Weapon quality increases summon level while the maximum number of simultaneous summons stays fixed.") }); internal static bool TryGet(string presetKey, out SecondaryAttackPresetInfo info) { return EntriesByKey.TryGetValue(presetKey ?? "", out info); } internal static string CanonicalizeKey(string? presetKey) { if (string.IsNullOrWhiteSpace(presetKey)) { return ""; } string text = presetKey.Trim(); if (!TryGet(text, out SecondaryAttackPresetInfo info)) { return text; } return info.Key; } internal static string GetKey(SecondaryAttackPreset preset) { return preset switch { SecondaryAttackPreset.Barrage => "barrage", SecondaryAttackPreset.Volley => "volley", SecondaryAttackPreset.Piercing => "piercing", SecondaryAttackPreset.Scatter => "scatter", SecondaryAttackPreset.Spiral => "spiral", SecondaryAttackPreset.Sentinel => "sentinel", SecondaryAttackPreset.Meteor => "meteor", SecondaryAttackPreset.Burst => "burst", SecondaryAttackPreset.StickyDetonator => "stickyDetonator", SecondaryAttackPreset.OverchargedBomb => "overchargedBomb", _ => preset.ToString(), }; } internal static string? GetKey(MeleeSpecialPreset preset) { return preset switch { MeleeSpecialPreset.SneakAmbush => "sneakAmbush", MeleeSpecialPreset.CleavingThrust => "cleavingThrust", MeleeSpecialPreset.SpearRain => "spearRain", MeleeSpecialPreset.ImpactBurst => "impactBurst", MeleeSpecialPreset.Boomerang => "boomerang", MeleeSpecialPreset.SpinningSweep => "spinningSweep", MeleeSpecialPreset.LaunchSlam => "launchSlam", MeleeSpecialPreset.KnockbackChain => "knockbackChain", MeleeSpecialPreset.Aftershock => "aftershock", MeleeSpecialPreset.RiftTrail => "riftTrail", MeleeSpecialPreset.FractureLine => "fractureLine", _ => null, }; } internal static string? GetKey(SecondaryAttackBehavior behavior) { if (!(behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { if (!(behavior is SummonEmpowerSecondaryBehavior)) { if (behavior is ShieldConvertSecondaryBehavior) { return "shieldConvert"; } return null; } return "summonEmpower"; } return GetKey(projectileSecondaryBehavior.Preset); } internal static Sprite? ResolveIcon(string presetKey) { if ((Object)(object)ObjectDB.instance == (Object)null || !TryGet(presetKey, out SecondaryAttackPresetInfo info)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(info.IconPrefabName); Sprite[] array = ((itemPrefab != null) ? itemPrefab.GetComponent() : null)?.m_itemData?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Dictionary BuildEntriesByKey() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (SecondaryAttackPresetInfo entry in Entries) { dictionary[entry.Key] = entry; } return dictionary; } } internal sealed class SecondaryAttackPresetInfo { internal string Key { get; } internal SecondaryAttackPresetGroup Group { get; } internal string IconPrefabName { get; } internal string NameToken { get; } internal string DescriptionToken { get; } internal string FallbackName { get; } internal string FallbackDescription { get; } internal SecondaryAttackPresetInfo(string key, string tokenKey, SecondaryAttackPresetGroup group, string iconPrefabName, string fallbackName, string fallbackDescription) { Key = key; Group = group; IconPrefabName = iconPrefabName; NameToken = "$sa_compendium_preset_" + tokenKey + "_name"; DescriptionToken = "$sa_compendium_preset_" + tokenKey + "_description"; FallbackName = fallbackName; FallbackDescription = fallbackDescription; } } internal static class SecondaryAttackAdrenalineSystem { private sealed class AttackAdrenalineState { internal readonly HashSet GrantedKeys = new HashSet(); } private sealed class AttackUseAdrenalineProjectileHitState { internal float BaseAdrenaline { get; } internal bool Granted { get; set; } internal AttackUseAdrenalineProjectileHitState(float baseAdrenaline) { BaseAdrenaline = Mathf.Max(0f, baseAdrenaline); } } private sealed class ProjectileUseAdrenalineProjectileHitState { internal Attack Attack { get; } internal AttackUseAdrenalineProjectileHitState AttackState { get; } internal ProjectileUseAdrenalineProjectileHitState(Attack attack, AttackUseAdrenalineProjectileHitState attackState) { Attack = attack; AttackState = attackState; } } private sealed class PendingSecondaryStartState { internal ItemData? Weapon { get; set; } } internal readonly struct AdrenalineScope : IDisposable { private readonly Attack? _previousAttack; private readonly float _previousFactor; private readonly string? _previousKey; private readonly bool _previousOnce; internal AdrenalineScope(Attack? previousAttack, float previousFactor, string? previousKey, bool previousOnce) { _previousAttack = previousAttack; _previousFactor = previousFactor; _previousKey = previousKey; _previousOnce = previousOnce; } public void Dispose() { ScopedAttack = _previousAttack; ScopedFactor = _previousFactor; ScopedKey = _previousKey; ScopedOnce = _previousOnce; } } private const string StaffLightningShotAnimation = "staff_lightningshot"; private static readonly ConditionalWeakTable AttackStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable AttackUseAdrenalineProjectileHitStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable ProjectileUseAdrenalineProjectileHitStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable PendingSecondaryStarts = new ConditionalWeakTable(); [ThreadStatic] private static Attack? ScopedAttack; [ThreadStatic] private static float ScopedFactor; [ThreadStatic] private static string? ScopedKey; [ThreadStatic] private static bool ScopedOnce; internal static void BeginConfiguredSecondaryStart(Humanoid humanoid, ItemData weapon) { if (!((Object)(object)humanoid == (Object)null) && weapon != null) { PendingSecondaryStarts.GetValue(humanoid, (Humanoid _) => new PendingSecondaryStartState()).Weapon = weapon; } } internal static void EndConfiguredSecondaryStart(Humanoid humanoid) { if ((Object)(object)humanoid != (Object)null) { PendingSecondaryStarts.Remove(humanoid); } } internal static bool ShouldSuppressAttackUseAdrenaline(Attack attack) { Humanoid val = attack?.m_character; if (val == null || attack.m_weapon == null || !PendingSecondaryStarts.TryGetValue(val, out PendingSecondaryStartState value)) { return false; } if (MatchesWeapon(value.Weapon, attack.m_weapon) && SecondaryAttackRuntimeFacade.TryGetDefinition(attack.m_weapon, out SecondaryAttackDefinition definition)) { return HasAdrenalineManagedSecondary(definition); } return false; } internal static bool TryBeginAttackUseAdrenalineProjectileHitConversion(Attack attack) { if (!ShouldConvertAttackUseAdrenalineToProjectileHit(attack)) { return false; } AttackUseAdrenalineProjectileHitStates.Remove(attack); AttackUseAdrenalineProjectileHitStates.Add(attack, new AttackUseAdrenalineProjectileHitState(attack.m_attackUseAdrenaline)); return true; } internal static void TryApplyAttackUseAdrenalineProjectileHitConversion(Projectile projectile, ItemData item) { if (!((Object)(object)projectile == (Object)null) && item != null) { Character? owner = ProjectileAccess.GetOwner(projectile); Humanoid val = (Humanoid)(object)((owner is Humanoid) ? owner : null); if (val != null && val.m_currentAttack != null && !val.m_currentAttackIsSecondary && MatchesWeapon(val.m_currentAttack.m_weapon, item) && IsStaffLightningShotAttack(val.m_currentAttack) && AttackUseAdrenalineProjectileHitStates.TryGetValue(val.m_currentAttack, out AttackUseAdrenalineProjectileHitState value)) { ProjectileUseAdrenalineProjectileHitStates.Remove(projectile); ProjectileUseAdrenalineProjectileHitStates.Add(projectile, new ProjectileUseAdrenalineProjectileHitState(val.m_currentAttack, value)); projectile.m_adrenaline = 0f; } } } internal static void TryGrantAttackUseAdrenalineOnProjectileHit(Projectile projectile, Collider collider) { if ((Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || !ProjectileUseAdrenalineProjectileHitStates.TryGetValue(projectile, out ProjectileUseAdrenalineProjectileHitState value) || value.AttackState.Granted) { return; } Character owner = ProjectileAccess.GetOwner(projectile); Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(collider); if ((Object)(object)owner == (Object)null || (Object)(object)hitCharacter == (Object)null || hitCharacter.m_enemyAdrenalineMultiplier <= 0f || !BaseAI.IsEnemy(owner, hitCharacter)) { return; } value.AttackState.Granted = true; using (BeginScope(value.Attack, 1f, "attackUseProjectileHit:staff_lightningshot", once: false)) { owner.AddAdrenaline(value.AttackState.BaseAdrenaline * hitCharacter.m_enemyAdrenalineMultiplier); } } internal static void Reset(Attack attack) { if (attack != null) { AttackStates.Remove(attack); AttackUseAdrenalineProjectileHitStates.Remove(attack); } } internal static bool TryModify(Character character, ref float amount) { if ((Object)(object)character == (Object)null || amount <= 0f) { return true; } Attack val = ScopedAttack; float num = ScopedFactor; string key = ScopedKey ?? ""; bool flag = ScopedOnce; if (val == null) { val = ((Humanoid)(((character is Humanoid) ? character : null)?)).m_currentAttack; if (val == null || !SecondaryAttackRuntimeContext.TryGetActiveAttack(val, out ActiveSecondaryAttack activeAttack) || activeAttack == null) { return true; } bool flag2 = IsSecondaryProjectileHitContext(); if (flag2) { return true; } if (activeAttack.Definition.Behavior is ProjectileSecondaryBehavior && !flag2) { amount = 0f; return false; } num = ResolveFactor(activeAttack); key = ResolveKey(activeAttack); flag = true; } num = Mathf.Max(0f, num); if (num <= 0f) { amount = 0f; return false; } if (flag && !TryMarkGranted(val, key)) { amount = 0f; return false; } amount *= num; return true; } internal static AdrenalineScope BeginScope(Attack attack, float factor, string key, bool once = true) { Attack? scopedAttack = ScopedAttack; float scopedFactor = ScopedFactor; string scopedKey = ScopedKey; bool scopedOnce = ScopedOnce; ScopedAttack = attack; ScopedFactor = Mathf.Max(0f, factor); ScopedKey = key; ScopedOnce = once; return new AdrenalineScope(scopedAttack, scopedFactor, scopedKey, scopedOnce); } internal static bool TryGrantOnce(Attack attack, Character target, float factor, string key) { if ((Object)(object)attack?.m_character == (Object)null || (Object)(object)target == (Object)null || target.m_enemyAdrenalineMultiplier <= 0f || attack.m_attackAdrenaline <= 0f) { return false; } using (BeginScope(attack, factor, key)) { ((Character)attack.m_character).AddAdrenaline(attack.m_attackAdrenaline * target.m_enemyAdrenalineMultiplier); } return true; } internal static bool TryGrantOnce(Attack attack, float enemyAdrenalineMultiplier, float factor, string key) { if ((Object)(object)attack?.m_character == (Object)null || enemyAdrenalineMultiplier <= 0f || attack.m_attackAdrenaline <= 0f) { return false; } using (BeginScope(attack, factor, key)) { ((Character)attack.m_character).AddAdrenaline(attack.m_attackAdrenaline * enemyAdrenalineMultiplier); } return true; } internal static bool TryGrantOnceRaw(Attack attack, Character target, float baseAdrenaline, float factor, string key) { if ((Object)(object)attack?.m_character == (Object)null || (Object)(object)target == (Object)null || target.m_enemyAdrenalineMultiplier <= 0f || baseAdrenaline <= 0f) { return false; } using (BeginScope(attack, factor, key)) { ((Character)attack.m_character).AddAdrenaline(baseAdrenaline * target.m_enemyAdrenalineMultiplier); } return true; } internal static void ApplyProjectileFactor(Projectile projectile, Attack? attack, float factor) { if (!((Object)(object)projectile == (Object)null)) { float num = projectile.m_adrenaline; if (num <= 0f && attack != null) { num = ((attack.m_attackAdrenaline > 0f) ? attack.m_attackAdrenaline : attack.m_attackUseAdrenaline); } projectile.m_adrenaline = Mathf.Max(0f, num * Mathf.Max(0f, factor)); } } internal static float ResolveFactor(ActiveSecondaryAttack activeAttack) { if (activeAttack.Definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) { return projectileSecondaryBehavior.AdrenalineFactor; } return 1f; } private static bool HasAdrenalineManagedSecondary(SecondaryAttackDefinition definition) { if (definition.BehaviorType == SecondaryAttackBehaviorType.EffectOnly && definition.CleavingThrust == null && definition.LaunchSlam == null && definition.KnockbackChain == null && definition.Aftershock == null && definition.RiftTrail == null && definition.FractureLine == null && definition.OnProjectileHit == null && definition.Boomerang == null) { return definition.SpinningSweep != null; } return true; } private static string ResolveKey(ActiveSecondaryAttack activeAttack) { return activeAttack.Definition.PrefabName + "|" + activeAttack.Definition.BehaviorType; } private static bool IsSecondaryProjectileHitContext() { if (SecondaryAttackRuntimeContext.TryPeekProjectileHitContext(out ProjectileHitContext context)) { if (context == null) { return false; } return context.Attribution?.SecondaryAttack == true; } return false; } private static bool TryMarkGranted(Attack attack, string key) { if (attack == null) { return true; } return AttackStates.GetValue(attack, (Attack _) => new AttackAdrenalineState()).GrantedKeys.Add(key); } private static bool MatchesWeapon(ItemData? left, ItemData? right) { if (left == null || right == null) { return false; } if (left == right) { return true; } if ((Object)(object)left.m_dropPrefab != (Object)null && (Object)(object)right.m_dropPrefab != (Object)null) { return ((Object)left.m_dropPrefab).name == ((Object)right.m_dropPrefab).name; } return false; } private static bool ShouldConvertAttackUseAdrenalineToProjectileHit(Attack attack) { if (attack != null && attack.m_attackUseAdrenaline > 0f) { return IsStaffLightningShotAttack(attack); } return false; } private static bool IsStaffLightningShotAttack(Attack attack) { return string.Equals(attack?.m_attackAnimation, "staff_lightningshot", StringComparison.OrdinalIgnoreCase); } } internal static class SecondaryAttackAdminAccessSystem { internal static bool ShouldBypassPresetCooldowns(Character? character) { if (SecondaryAttacksPlugin.AdminNoPresetCooldowns.Value == SecondaryAttacksPlugin.Toggle.On) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { return SecondaryAttacksPlugin.ConfigSync.IsAdmin; } } return false; } } internal static class SecondaryAttackNamedEffectSystem { private const float EffectLifetime = 10f; private static readonly Dictionary PrefabCache = new Dictionary(); internal static bool Create(string? prefabName, Vector3 position, Quaternion rotation, string context) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(prefabName)) { return false; } string text = prefabName.Trim(); if (!TryResolvePrefab(text, out GameObject prefab)) { if (SecondaryAttackWarningLog.TryMarkWarning("named_effect_missing_" + context + "_" + text)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Configured effect prefab '" + text + "' was not found for " + context + ".")); } return false; } Object.Destroy((Object)(object)Object.Instantiate(prefab, position, rotation), 10f); return true; } private static bool TryResolvePrefab(string prefabName, out GameObject? prefab) { if (PrefabCache.TryGetValue(prefabName, out prefab)) { if ((Object)(object)prefab != (Object)null) { return true; } PrefabCache.Remove(prefabName); } ZNetScene instance = ZNetScene.instance; prefab = ((instance != null) ? instance.GetPrefab(prefabName) : null); if ((Object)(object)prefab == (Object)null) { return false; } PrefabCache[prefabName] = prefab; return true; } internal static Quaternion RotationFromNormal(Vector3 normal) { //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_000e: Unknown result type (might be due to invalid IL or missing references) if (!(((Vector3)(ref normal)).sqrMagnitude > 0.001f)) { return Quaternion.identity; } return Quaternion.LookRotation(((Vector3)(ref normal)).normalized); } } internal static class SecondaryAttackMagicSummonNormalizer { private const int MaxSpawnChoiceWeight = 100; internal static Dictionary Normalize(IReadOnlyDictionary bloodMagic) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var (text2, bloodMagicWeaponConfig2) in bloodMagic) { if (string.IsNullOrWhiteSpace(text2) || text2.Equals("Global", StringComparison.OrdinalIgnoreCase) || (bloodMagicWeaponConfig2 != null && bloodMagicWeaponConfig2.Enabled == false) || bloodMagicWeaponConfig2?.Summon == null) { continue; } MagicSummonOverrideConfig summon = bloodMagicWeaponConfig2.Summon; string text3 = text2.Trim(); MagicSummonQualityPreset magicSummonQualityPreset = NormalizeMagicSummonQualityPreset(summon.QualityPreset, text3); List obj = summon.SpawnChoices?.Where((MagicSummonCloneConfig magicSummonCloneConfig) => magicSummonCloneConfig != null).ToList() ?? new List(); List list = new List(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (MagicSummonCloneConfig item in obj) { int num = ClampInt(item.Weight ?? 1, 0, 100); if (num <= 0) { continue; } string text4 = TrimOrEmpty(item.SourcePrefab); if (string.IsNullOrWhiteSpace(text4)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping summon override '" + text3 + "' from SecondaryAttacks.BloodMagic.yml: sourcePrefab is required.")); continue; } string text5 = TrimOrEmpty(item.ClonePrefab); if (string.IsNullOrWhiteSpace(text5)) { text5 = "SA_" + SanitizeMagicSummonId(text3) + "_" + SanitizeMagicSummonId(text4); } if (!hashSet.Add(text5)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping duplicate clonePrefab '" + text5 + "' in summon override '" + text3 + "'.")); } else { list.Add(new NormalizedMagicSummonCloneConfig { SourcePrefab = text4, ClonePrefab = text5, DisplayName = TrimOrEmpty(item.DisplayName), Health = item.Health, Weight = num }); } } if (list.Count != 0 || magicSummonQualityPreset != MagicSummonQualityPreset.None) { dictionary[text3] = new NormalizedMagicSummonOverrideConfig { EntryId = text3, QualityPreset = magicSummonQualityPreset, MaxQuality = ClampInt(summon.MaxQuality ?? 4, 1, 10), Summons = list }; } } return dictionary; } private static string TrimOrEmpty(string? value) { if (!string.IsNullOrWhiteSpace(value)) { return value.Trim(); } return string.Empty; } private static MagicSummonQualityPreset NormalizeMagicSummonQualityPreset(string? rawPreset, string entryId) { string text = TrimOrEmpty(rawPreset); if (string.IsNullOrWhiteSpace(text) || text.Equals("none", StringComparison.OrdinalIgnoreCase) || text.Equals("off", StringComparison.OrdinalIgnoreCase) || text.Equals("disabled", StringComparison.OrdinalIgnoreCase)) { return MagicSummonQualityPreset.None; } if (text.Equals("countByQuality", StringComparison.OrdinalIgnoreCase) || text.Equals("qualityCount", StringComparison.OrdinalIgnoreCase) || text.Equals("count", StringComparison.OrdinalIgnoreCase)) { return MagicSummonQualityPreset.CountByQuality; } if (text.Equals("levelByQuality", StringComparison.OrdinalIgnoreCase) || text.Equals("qualityLevel", StringComparison.OrdinalIgnoreCase) || text.Equals("level", StringComparison.OrdinalIgnoreCase)) { return MagicSummonQualityPreset.LevelByQuality; } SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Ignoring invalid qualityPreset '" + text + "' in summon override '" + entryId + "' from SecondaryAttacks.BloodMagic.yml. Valid values are countByQuality or levelByQuality.")); return MagicSummonQualityPreset.None; } private static int ClampInt(int value, int min, int max) { return Math.Min(max, Math.Max(min, value)); } private static string SanitizeMagicSummonId(string value) { string text = new string(value.Select((char character) => (!char.IsLetterOrDigit(character)) ? '_' : character).ToArray()); text = text.Trim('_'); if (!string.IsNullOrWhiteSpace(text)) { return text; } return "Summon"; } } internal static class SecondaryAttackCooldownGroupResolver { internal static void Apply(string prefabName, ItemDrop itemDrop, SecondaryAttackDefinition definition) { string text = SecondaryAttackWeaponFamilyResolver.ResolveAutomaticCooldownGroup(prefabName, itemDrop); Assign(definition.SneakAmbush?.PresetCooldown, text, "sneakAmbush"); Assign(definition.CleavingThrust?.PresetCooldown, text, "cleavingThrust"); Assign(definition.LaunchSlam?.PresetCooldown, text, "launchSlam"); Assign(definition.KnockbackChain?.PresetCooldown, text, "knockbackChain"); Assign(definition.Aftershock?.PresetCooldown, text, "aftershock"); Assign(definition.RiftTrail?.PresetCooldown, text, "riftTrail"); Assign(definition.FractureLine?.PresetCooldown, text, "fractureLine"); Assign(definition.HarvestSweep?.PresetCooldown, text, "harvestSweep"); Assign(definition.OnProjectileHit?.PresetCooldown, text, definition.OnProjectileHit?.Preset); Assign(definition.Boomerang?.PresetCooldown, text, "boomerang"); Assign(definition.SpinningSweep?.PresetCooldown, text, "spinningSweep"); SecondaryAttackBehavior behavior = definition.Behavior; if (!(behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { if (!(behavior is SummonEmpowerSecondaryBehavior summonEmpowerSecondaryBehavior)) { if (behavior is ShieldConvertSecondaryBehavior shieldConvertSecondaryBehavior) { shieldConvertSecondaryBehavior.PresetCooldown.CooldownGroup = "family:blood:shield"; } } else { summonEmpowerSecondaryBehavior.PresetCooldown.CooldownGroup = "family:blood:summon"; } } else { projectileSecondaryBehavior.CooldownGroup = ((!string.IsNullOrWhiteSpace(text)) ? text : CreatePresetFallback("ranged", ProjectileRuntimeSystem.GetPresetName(projectileSecondaryBehavior.Preset))); } } internal static string ResolveMeleeGroup(string presetName, MeleePresetCooldownDefinition? cooldown) { if (string.IsNullOrWhiteSpace(cooldown?.CooldownGroup)) { return CreatePresetFallback("melee", presetName); } return cooldown.CooldownGroup.Trim(); } internal static string ResolveRangedGroup(ProjectileSecondaryBehavior? behavior) { if (behavior == null) { return ""; } if (string.IsNullOrWhiteSpace(behavior.CooldownGroup)) { return CreatePresetFallback("ranged", ProjectileRuntimeSystem.GetPresetName(behavior.Preset)); } return behavior.CooldownGroup.Trim(); } private static void Assign(MeleePresetCooldownDefinition? cooldown, string automaticFamilyGroup, string? presetName) { if (cooldown != null) { cooldown.CooldownGroup = ((!string.IsNullOrWhiteSpace(automaticFamilyGroup)) ? automaticFamilyGroup : CreatePresetFallback("melee", presetName)); } } private static string CreatePresetFallback(string domain, string? presetName) { string text = NormalizeToken(domain); string text2 = NormalizeToken(presetName); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { return ""; } return "preset:" + text + ":" + text2; } private static string NormalizeToken(string? value) { if (!string.IsNullOrWhiteSpace(value)) { return value.Trim().ToLowerInvariant(); } return ""; } } internal enum BloodMagicAutomaticWeaponFamily { None, Summon, Shield } internal enum RangedAutomaticWeaponFamily { None, Bomb, FireballStaff, RapidStaff, ReloadStaff, Crossbow, Bow } internal enum MeleeAutomaticWeaponFamily { None, Knives, TwoHandedSword, OneHandedSword, OneHandedClub, Unarmed, Sledge, Polearm, Farming, Pickaxe, Spear, Battleaxe, OneHandedAxe } internal static class SecondaryAttackWeaponFamilyResolver { internal const string BloodSummonCooldownGroup = "family:blood:summon"; internal const string BloodShieldCooldownGroup = "family:blood:shield"; internal static string ResolveAutomaticCooldownGroup(string prefabName, ItemDrop itemDrop) { return ResolveBloodMagicFamily(prefabName, itemDrop) switch { BloodMagicAutomaticWeaponFamily.Summon => "family:blood:summon", BloodMagicAutomaticWeaponFamily.Shield => "family:blood:shield", BloodMagicAutomaticWeaponFamily.None => ResolveRangedFamily(itemDrop) switch { RangedAutomaticWeaponFamily.Bomb => "family:ranged:bomb", RangedAutomaticWeaponFamily.FireballStaff => "family:ranged:staff-fireball", RangedAutomaticWeaponFamily.RapidStaff => "family:ranged:staff-rapid", RangedAutomaticWeaponFamily.ReloadStaff => "family:ranged:staff-reload", RangedAutomaticWeaponFamily.Crossbow => "family:ranged:crossbow", RangedAutomaticWeaponFamily.Bow => "family:ranged:bow", RangedAutomaticWeaponFamily.None => ResolveMeleeFamily(itemDrop) switch { MeleeAutomaticWeaponFamily.Knives => "family:melee:knives", MeleeAutomaticWeaponFamily.TwoHandedSword => "family:melee:sword-2h", MeleeAutomaticWeaponFamily.OneHandedSword => "family:melee:sword-1h", MeleeAutomaticWeaponFamily.OneHandedClub => "family:melee:club-1h", MeleeAutomaticWeaponFamily.Unarmed => "family:melee:unarmed", MeleeAutomaticWeaponFamily.Sledge => "family:melee:sledge", MeleeAutomaticWeaponFamily.Polearm => "family:melee:polearm", MeleeAutomaticWeaponFamily.Farming => "family:melee:farming", MeleeAutomaticWeaponFamily.Pickaxe => "family:melee:pickaxe", MeleeAutomaticWeaponFamily.Spear => "family:melee:spear", MeleeAutomaticWeaponFamily.Battleaxe => "family:melee:battleaxe", MeleeAutomaticWeaponFamily.OneHandedAxe => "family:melee:axe-1h", _ => "", }, _ => "", }, _ => "", }; } internal static BloodMagicAutomaticWeaponFamily ResolveBloodMagicFamily(string prefabName, ItemDrop itemDrop) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 SharedData val = itemDrop.m_itemData?.m_shared; Attack val2 = val?.m_attack; if (val == null || val2 == null || (int)val.m_skillType != 10) { return BloodMagicAutomaticWeaponFamily.None; } if (IsDefaultShieldConvertWeapon(prefabName)) { return BloodMagicAutomaticWeaponFamily.Shield; } if (!IsDefaultSummonEmpowerWeapon(val2)) { return BloodMagicAutomaticWeaponFamily.None; } return BloodMagicAutomaticWeaponFamily.Summon; } internal static RangedAutomaticWeaponFamily ResolveRangedFamily(ItemDrop itemDrop) { //IL_0027: 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_0054: Invalid comparison between Unknown and I4 //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Invalid comparison between Unknown and I4 //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Invalid comparison between Unknown and I4 //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Invalid comparison between Unknown and I4 SharedData val = itemDrop.m_itemData?.m_shared; Attack val2 = val?.m_attack; if (val == null || val2 == null || IsAmmoItemType(val.m_itemType) || string.IsNullOrWhiteSpace(val2.m_attackAnimation)) { return RangedAutomaticWeaponFamily.None; } if (IsBombProjectileAttack(val2)) { return RangedAutomaticWeaponFamily.Bomb; } if ((int)val.m_skillType == 9) { string a = val2.m_attackAnimation ?? ""; if (string.Equals(a, "staff_fireball", StringComparison.OrdinalIgnoreCase)) { return RangedAutomaticWeaponFamily.FireballStaff; } if (string.Equals(a, "staff_rapidfire", StringComparison.OrdinalIgnoreCase)) { return RangedAutomaticWeaponFamily.RapidStaff; } if (string.Equals(a, "staff_lightningshot", StringComparison.OrdinalIgnoreCase)) { return RangedAutomaticWeaponFamily.ReloadStaff; } } if ((int)val.m_skillType == 14 || (val2.m_requiresReload && (int)val2.m_attackType == 2 && !string.IsNullOrWhiteSpace(val.m_ammoType))) { return RangedAutomaticWeaponFamily.Crossbow; } if ((int)val.m_itemType != 4 && (int)val.m_skillType != 8) { return RangedAutomaticWeaponFamily.None; } return RangedAutomaticWeaponFamily.Bow; } internal static MeleeAutomaticWeaponFamily ResolveMeleeFamily(ItemDrop itemDrop) { SharedData sharedData = itemDrop.m_itemData?.m_shared; if (IsDefaultSpearRainWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.Spear; } if (IsDefaultImpactBurstWeapon(itemDrop)) { return MeleeAutomaticWeaponFamily.Battleaxe; } if (IsDefaultBoomerangWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.OneHandedAxe; } if (IsDefaultSpinningSweepWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.Polearm; } if (IsDefaultCleavingThrustWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.TwoHandedSword; } if (IsDefaultRiftTrailWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.OneHandedSword; } if (IsDefaultLaunchSlamWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.OneHandedClub; } if (IsDefaultKnockbackChainWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.Unarmed; } if (IsDefaultAftershockWeapon(itemDrop)) { return MeleeAutomaticWeaponFamily.Sledge; } if (IsDefaultFractureLineWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.Pickaxe; } if (IsDefaultSneakAmbushWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.Knives; } if (!IsDefaultHarvestSweepWeapon(sharedData)) { return MeleeAutomaticWeaponFamily.None; } return MeleeAutomaticWeaponFamily.Farming; } internal static bool IsDefaultShieldConvertWeapon(string prefabName) { return string.Equals(prefabName, "StaffShield", StringComparison.OrdinalIgnoreCase); } internal static bool IsDefaultSummonEmpowerWeapon(Attack primaryAttack) { if ((Object)(object)primaryAttack.m_attackProjectile == (Object)null) { return false; } SpawnAbility component = primaryAttack.m_attackProjectile.GetComponent(); if (component?.m_spawnPrefab != null) { return component.m_spawnPrefab.Any((GameObject spawnPrefab) => (Object)(object)spawnPrefab != (Object)null); } return false; } internal static bool IsBombProjectileAttack(Attack primaryAttack) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if ((Object)(object)primaryAttack.m_attackProjectile != (Object)null && (Object)(object)primaryAttack.m_attackProjectile.GetComponent() != (Object)null && ((int)primaryAttack.m_attackType == 2 || primaryAttack.m_attackProjectile.GetComponent() != null)) { return string.Equals(primaryAttack.m_attackAnimation, "throw_bomb", StringComparison.OrdinalIgnoreCase); } return false; } internal static bool IsDefaultSneakAmbushWeapon(SharedData? sharedData) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if (sharedData == null) { return false; } return (int)sharedData.m_skillType == 2; } internal static bool IsDefaultCleavingThrustWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 1) { return (int)sharedData.m_itemType == 14; } return false; } internal static bool IsDefaultRiftTrailWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 1 && (int)sharedData.m_itemType == 3 && sharedData.m_secondaryAttack != null) { return string.Equals(sharedData.m_secondaryAttack.m_attackAnimation, "sword_secondary", StringComparison.OrdinalIgnoreCase); } return false; } internal static bool IsDefaultLaunchSlamWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 3) { return (int)sharedData.m_itemType == 3; } return false; } internal static bool IsDefaultKnockbackChainWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 11 && sharedData.m_secondaryAttack != null) { return string.Equals(sharedData.m_secondaryAttack.m_attackAnimation, "unarmed_kick", StringComparison.OrdinalIgnoreCase); } return false; } internal static bool IsDefaultAftershockWeapon(ItemDrop itemDrop) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Invalid comparison between Unknown and I4 SharedData val = itemDrop.m_itemData?.m_shared; string text = ((Object)itemDrop).name ?? ""; if (val != null && (int)val.m_skillType == 3 && (int)val.m_itemType == 14 && text.Contains("Sledge", StringComparison.OrdinalIgnoreCase)) { Attack attack = val.m_attack; if (attack == null || (int)attack.m_attackType != 4) { Attack secondaryAttack = val.m_secondaryAttack; if (secondaryAttack == null) { return false; } return (int)secondaryAttack.m_attackType == 4; } return true; } return false; } internal static bool IsDefaultSpinningSweepWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 4 && sharedData.m_secondaryAttack != null) { return string.Equals(sharedData.m_secondaryAttack.m_attackAnimation, "atgeir_secondary", StringComparison.OrdinalIgnoreCase); } return false; } internal static bool IsDefaultHarvestSweepWeapon(SharedData? sharedData) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if (sharedData == null) { return false; } return (int)sharedData.m_skillType == 106; } internal static bool IsDefaultFractureLineWeapon(SharedData? sharedData) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if (sharedData == null) { return false; } return (int)sharedData.m_skillType == 12; } internal static bool IsDefaultSpearRainWeapon(SharedData? sharedData) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 Attack val = sharedData?.m_secondaryAttack; if (sharedData != null && (int)sharedData.m_skillType == 5 && val != null) { return !string.IsNullOrWhiteSpace(val.m_attackAnimation); } return false; } internal static bool IsDefaultImpactBurstWeapon(ItemDrop itemDrop) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 SharedData val = itemDrop.m_itemData?.m_shared; if (val == null || (int)val.m_skillType != 7 || (int)val.m_itemType != 14) { return false; } string text = ((Object)itemDrop).name ?? ""; if (text.Contains("DualAxe", StringComparison.OrdinalIgnoreCase)) { return false; } string text2 = val.m_secondaryAttack?.m_attackAnimation ?? ""; if (!string.Equals(text2, "battleaxe_secondary", StringComparison.OrdinalIgnoreCase)) { return false; } if (text.Contains("Battleaxe", StringComparison.OrdinalIgnoreCase)) { return true; } string text3 = val.m_attack?.m_attackAnimation ?? ""; if ((text3.Contains("battleaxe", StringComparison.OrdinalIgnoreCase) || text2.Contains("battleaxe", StringComparison.OrdinalIgnoreCase)) && !text3.Contains("dualaxe", StringComparison.OrdinalIgnoreCase)) { return !text2.Contains("dualaxe", StringComparison.OrdinalIgnoreCase); } return false; } internal static bool IsDefaultBoomerangWeapon(SharedData? sharedData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (sharedData != null && (int)sharedData.m_skillType == 7) { return (int)sharedData.m_itemType == 3; } return false; } private static bool IsAmmoItemType(ItemType itemType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)itemType == 9 || (int)itemType == 23) { return true; } return false; } } internal sealed class SecondaryAttackWeaponNormalizationResult { public Dictionary Weapons { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary GlobalRangedPresets { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary GlobalBloodMagicPresets { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public NormalizedWeaponConfig? GlobalMeleeFallback { get; set; } } internal static class SecondaryAttackWeaponConfigNormalizer { private const string FixedThrowCarrierPrefab = "SpearFlint"; private const string FixedImpactBurstVfx = "vfx_archerytarget_bullseye_double"; private const string GlobalFallbackKey = "Global"; internal static SecondaryAttackWeaponNormalizationResult Normalize(IReadOnlyDictionary ranged, IReadOnlyDictionary melee, IReadOnlyDictionary bloodMagic) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary globalRangedPresets = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary globalBloodMagicPresets = new Dictionary(StringComparer.OrdinalIgnoreCase); RangedWeaponConfig rangedWeaponConfig = null; string key; RangedWeaponConfig value; foreach (KeyValuePair item in ranged) { item.Deconstruct(out key, out value); string text = key; RangedWeaponConfig rangedWeaponConfig2 = value; if (!string.IsNullOrWhiteSpace(text) && rangedWeaponConfig2 != null && text.Trim().Equals("Global", StringComparison.OrdinalIgnoreCase)) { rangedWeaponConfig = rangedWeaponConfig2; } } AddGlobalRangedPreset(globalRangedPresets, "barrage", rangedWeaponConfig?.Barrage); AddGlobalRangedPreset(globalRangedPresets, "volley", rangedWeaponConfig?.Volley); AddGlobalRangedPreset(globalRangedPresets, "piercing", rangedWeaponConfig?.Piercing); AddGlobalRangedPreset(globalRangedPresets, "scatter", rangedWeaponConfig?.Scatter); AddGlobalRangedPreset(globalRangedPresets, "spiral", rangedWeaponConfig?.Spiral); AddGlobalRangedPreset(globalRangedPresets, "sentinel", rangedWeaponConfig?.Sentinel); AddGlobalRangedPreset(globalRangedPresets, "meteor", rangedWeaponConfig?.Meteor); AddGlobalRangedPreset(globalRangedPresets, "burst", rangedWeaponConfig?.Burst); AddGlobalRangedPreset(globalRangedPresets, "stickyDetonator", rangedWeaponConfig?.StickyDetonator); AddGlobalRangedPreset(globalRangedPresets, "overchargedBomb", rangedWeaponConfig?.OverchargedBomb); foreach (KeyValuePair item2 in ranged) { item2.Deconstruct(out key, out value); string text2 = key; RangedWeaponConfig rangedWeaponConfig3 = value; if (!string.IsNullOrWhiteSpace(text2) && rangedWeaponConfig3 != null) { string text3 = text2.Trim(); if (!text3.Equals("Global", StringComparison.OrdinalIgnoreCase)) { NormalizedWeaponConfig fallback = ResolveRangedPresetFallback(rangedWeaponConfig3, globalRangedPresets); AddNormalizedWeapon(dictionary, text3, FromRangedRaw(rangedWeaponConfig3, fallback), "SecondaryAttacks.Ranged.yml"); } } } MeleeWeaponConfig meleeWeaponConfig = null; MeleeWeaponConfig value2; foreach (KeyValuePair item3 in melee) { item3.Deconstruct(out key, out value2); string text4 = key; MeleeWeaponConfig meleeWeaponConfig2 = value2; if (!string.IsNullOrWhiteSpace(text4) && meleeWeaponConfig2 != null && text4.Trim().Equals("Global", StringComparison.OrdinalIgnoreCase)) { meleeWeaponConfig = meleeWeaponConfig2; break; } } NormalizedWeaponConfig normalizedWeaponConfig = ((meleeWeaponConfig != null) ? CreateGlobalMeleeFallback(meleeWeaponConfig) : null); foreach (KeyValuePair item4 in melee) { item4.Deconstruct(out key, out value2); string text5 = key; MeleeWeaponConfig meleeWeaponConfig3 = value2; if (!string.IsNullOrWhiteSpace(text5) && meleeWeaponConfig3 != null) { string text6 = text5.Trim(); if (!text6.Equals("Global", StringComparison.OrdinalIgnoreCase)) { AddNormalizedWeapon(dictionary, text6, FromMeleeRaw(text6, meleeWeaponConfig3, normalizedWeaponConfig), "SecondaryAttacks.Melee.yml"); } } } BloodMagicWeaponConfig bloodMagicWeaponConfig = null; BloodMagicWeaponConfig value3; foreach (KeyValuePair item5 in bloodMagic) { item5.Deconstruct(out key, out value3); string text7 = key; BloodMagicWeaponConfig bloodMagicWeaponConfig2 = value3; if (!string.IsNullOrWhiteSpace(text7) && bloodMagicWeaponConfig2 != null && text7.Trim().Equals("Global", StringComparison.OrdinalIgnoreCase)) { bloodMagicWeaponConfig = bloodMagicWeaponConfig2; } } AddGlobalBloodMagicPreset(globalBloodMagicPresets, "summonEmpower", bloodMagicWeaponConfig?.SummonEmpower); AddGlobalBloodMagicPreset(globalBloodMagicPresets, "shieldConvert", bloodMagicWeaponConfig?.ShieldConvert); foreach (KeyValuePair item6 in bloodMagic) { item6.Deconstruct(out key, out value3); string text8 = key; BloodMagicWeaponConfig bloodMagicWeaponConfig3 = value3; if (!string.IsNullOrWhiteSpace(text8) && bloodMagicWeaponConfig3 != null) { string text9 = text8.Trim(); if (!text9.Equals("Global", StringComparison.OrdinalIgnoreCase)) { NormalizedWeaponConfig fallback2 = ResolveBloodMagicPresetFallback(bloodMagicWeaponConfig3, globalBloodMagicPresets); AddNormalizedWeapon(dictionary, text9, FromBloodMagicRaw(bloodMagicWeaponConfig3, fallback2), "SecondaryAttacks.BloodMagic.yml"); } } } return new SecondaryAttackWeaponNormalizationResult { Weapons = dictionary, GlobalRangedPresets = globalRangedPresets, GlobalBloodMagicPresets = globalBloodMagicPresets, GlobalMeleeFallback = normalizedWeaponConfig }; } private static void AddGlobalRangedPreset(Dictionary globalRangedPresets, string presetName, RangedWeaponConfig? rawConfig) { if (rawConfig != null) { globalRangedPresets[presetName] = new NormalizedWeaponConfig { Enabled = true, Secondary = NormalizeRanged(rawConfig, null, presetName) }; } } private static void AddGlobalBloodMagicPreset(Dictionary globalBloodMagicPresets, string presetName, BloodMagicWeaponConfig? rawConfig) { if (rawConfig != null) { globalBloodMagicPresets[presetName] = new NormalizedWeaponConfig { Enabled = (rawConfig.Enabled ?? true), Secondary = NormalizeBloodMagic(rawConfig, null, presetName) }; } } private static NormalizedWeaponConfig? ResolveRangedPresetFallback(RangedWeaponConfig rawConfig, IReadOnlyDictionary globalRangedPresets) { string text = rawConfig.Preset?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text) || text.Equals("none", StringComparison.OrdinalIgnoreCase)) { return null; } if (!globalRangedPresets.TryGetValue(text, out NormalizedWeaponConfig value)) { return null; } return value; } private static NormalizedWeaponConfig? ResolveBloodMagicPresetFallback(BloodMagicWeaponConfig rawConfig, IReadOnlyDictionary globalBloodMagicPresets) { string text = rawConfig.Preset?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text)) { return null; } if (!globalBloodMagicPresets.TryGetValue(text, out NormalizedWeaponConfig value)) { return null; } return value; } private static void AddNormalizedWeapon(Dictionary normalizedWeapons, string prefabName, NormalizedWeaponConfig weaponConfig, string sourceFileName) { string text = prefabName.Trim(); if (normalizedWeapons.ContainsKey(text)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping duplicate '" + text + "' entry from " + sourceFileName + ": the prefab is already configured by another SecondaryAttacks YAML file.")); } else { normalizedWeapons[text] = weaponConfig; } } public static NormalizedWeaponConfig FromRangedRaw(RangedWeaponConfig raw, NormalizedWeaponConfig? fallback = null) { return new NormalizedWeaponConfig { Enabled = (raw.Enabled ?? true), UseAutomaticFallback = (fallback == null && string.IsNullOrWhiteSpace(raw.Preset)), Secondary = NormalizeRanged(raw, fallback?.Secondary) }; } public static NormalizedWeaponConfig CreateGlobalMeleeFallback(MeleeWeaponConfig raw) { return new NormalizedWeaponConfig { Enabled = (raw.Enabled ?? true), SneakAmbush = NormalizeSneakAmbush(raw.SneakAmbush, null), CleavingThrust = NormalizeCleavingThrust(raw.CleavingThrust, null), LaunchSlam = NormalizeLaunchSlam(raw.LaunchSlam, null), KnockbackChain = NormalizeKnockbackChain(raw.KnockbackChain, null), Aftershock = NormalizeAftershock(raw.Aftershock, null), RiftTrail = NormalizeRiftTrail(raw.RiftTrail, null), FractureLine = NormalizeFractureLine(raw.FractureLine, null), ImpactBurst = NormalizeImpactBurst(raw.ImpactBurst, null), Boomerang = NormalizeBoomerang(raw.Boomerang, null), SpinningSweep = NormalizeSpinningSweep(raw.SpinningSweep, null), HarvestSweep = NormalizeHarvestSweep(raw.HarvestSweep, null), SpearRain = NormalizeMeleeOnProjectileHit(raw.SpearRain, forceSpearRainPreset: true, null), Secondary = ((raw.Aftershock != null && raw.Aftershock.Enabled != false) ? NormalizeMelee(raw, null, forceSpearRainPreset: false, null, "aftershock") : null), MeleePreset = MeleeSpecialPreset.None, HasExplicitMeleePreset = false }; } public static NormalizedWeaponConfig FromMeleeRaw(string prefabName, MeleeWeaponConfig raw, NormalizedWeaponConfig? fallback = null) { MeleeSpecialPreset preset; bool flag = TryParseExplicitMeleePreset(prefabName, raw.Preset, out preset); if (!flag) { preset = ResolveImplicitMeleePreset(prefabName, raw); } SneakAmbushConfig sneakAmbush = raw.SneakAmbush; MeleeOnProjectileHitConfig spearRain = raw.SpearRain; object obj; if (preset != MeleeSpecialPreset.ImpactBurst) { ImpactBurstConfig? impactBurst = raw.ImpactBurst; obj = ((impactBurst != null && impactBurst.Enabled == false) ? NormalizeImpactBurst(raw.ImpactBurst, fallback?.ImpactBurst) : null); } else { obj = NormalizeImpactBurst(raw.ImpactBurst ?? new ImpactBurstConfig(), fallback?.ImpactBurst); } NormalizedImpactBurstConfig normalizedImpactBurstConfig = (NormalizedImpactBurstConfig)obj; object obj2; if (preset != MeleeSpecialPreset.Boomerang) { BoomerangConfig? boomerang = raw.Boomerang; obj2 = ((boomerang != null && boomerang.Enabled == false) ? NormalizeBoomerang(raw.Boomerang, fallback?.Boomerang) : null); } else { obj2 = NormalizeBoomerang(raw.Boomerang ?? new BoomerangConfig(), fallback?.Boomerang); } NormalizedBoomerangConfig normalizedBoomerangConfig = (NormalizedBoomerangConfig)obj2; object obj3; if (preset != MeleeSpecialPreset.SpinningSweep) { SpinningSweepConfig? spinningSweep = raw.SpinningSweep; obj3 = ((spinningSweep != null && spinningSweep.Enabled == false) ? NormalizeSpinningSweep(raw.SpinningSweep, fallback?.SpinningSweep) : null); } else { obj3 = NormalizeSpinningSweep(raw.SpinningSweep ?? new SpinningSweepConfig(), fallback?.SpinningSweep); } NormalizedSpinningSweepConfig normalizedSpinningSweepConfig = (NormalizedSpinningSweepConfig)obj3; if (flag) { LogIgnoredExplicitMeleePresetBlocks(prefabName, raw, preset); } MeleeOnProjectileHitConfig meleeOnProjectileHitConfig = ((preset == MeleeSpecialPreset.SpearRain) ? (spearRain ?? new MeleeOnProjectileHitConfig()) : null); NormalizedMeleeOnProjectileHitConfig fallbackOnProjectileHit = ((preset != MeleeSpecialPreset.SpearRain) ? null : fallback?.SpearRain); NormalizedSecondaryModeConfig secondary = ((preset == MeleeSpecialPreset.ImpactBurst && normalizedImpactBurstConfig != null) ? CreateImpactBurstSecondary(normalizedImpactBurstConfig) : ((preset == MeleeSpecialPreset.Boomerang && normalizedBoomerangConfig != null) ? CreateBoomerangSecondary(normalizedBoomerangConfig) : ((preset == MeleeSpecialPreset.SpinningSweep && normalizedSpinningSweepConfig != null) ? CreateSpinningSweepSecondary(normalizedSpinningSweepConfig) : (HasMeleeSecondaryConfig(raw, preset, meleeOnProjectileHitConfig) ? NormalizeMelee(raw, meleeOnProjectileHitConfig, preset == MeleeSpecialPreset.SpearRain, fallbackOnProjectileHit, ResolveMeleeSecondaryType(raw, preset)) : null)))); NormalizedWeaponConfig obj4 = new NormalizedWeaponConfig { Enabled = (raw.Enabled ?? true), Secondary = secondary, SneakAmbush = ((preset == MeleeSpecialPreset.SneakAmbush) ? NormalizeSneakAmbush(sneakAmbush ?? new SneakAmbushConfig(), fallback?.SneakAmbush) : ((sneakAmbush != null && sneakAmbush.Enabled == false) ? NormalizeSneakAmbush(sneakAmbush, fallback?.SneakAmbush) : null)) }; object cleavingThrust2; if (preset != MeleeSpecialPreset.CleavingThrust) { CleavingThrustConfig? cleavingThrust = raw.CleavingThrust; cleavingThrust2 = ((cleavingThrust != null && cleavingThrust.Enabled == false) ? NormalizeCleavingThrust(raw.CleavingThrust, fallback?.CleavingThrust) : null); } else { cleavingThrust2 = NormalizeCleavingThrust(raw.CleavingThrust ?? new CleavingThrustConfig(), fallback?.CleavingThrust); } obj4.CleavingThrust = (NormalizedCleavingThrustConfig?)cleavingThrust2; object launchSlam2; if (preset != MeleeSpecialPreset.LaunchSlam) { LaunchSlamConfig? launchSlam = raw.LaunchSlam; launchSlam2 = ((launchSlam != null && launchSlam.Enabled == false) ? NormalizeLaunchSlam(raw.LaunchSlam, fallback?.LaunchSlam) : null); } else { launchSlam2 = NormalizeLaunchSlam(raw.LaunchSlam ?? new LaunchSlamConfig(), fallback?.LaunchSlam); } obj4.LaunchSlam = (NormalizedLaunchSlamConfig?)launchSlam2; object knockbackChain2; if (preset != MeleeSpecialPreset.KnockbackChain) { KnockbackChainConfig? knockbackChain = raw.KnockbackChain; knockbackChain2 = ((knockbackChain != null && knockbackChain.Enabled == false) ? NormalizeKnockbackChain(raw.KnockbackChain, fallback?.KnockbackChain) : null); } else { knockbackChain2 = NormalizeKnockbackChain(raw.KnockbackChain ?? new KnockbackChainConfig(), fallback?.KnockbackChain); } obj4.KnockbackChain = (NormalizedKnockbackChainConfig?)knockbackChain2; object aftershock2; if (preset != MeleeSpecialPreset.Aftershock) { AftershockConfig? aftershock = raw.Aftershock; aftershock2 = ((aftershock != null && aftershock.Enabled == false) ? NormalizeAftershock(raw.Aftershock, fallback?.Aftershock) : null); } else { aftershock2 = NormalizeAftershock(raw.Aftershock ?? new AftershockConfig(), fallback?.Aftershock); } obj4.Aftershock = (NormalizedAftershockConfig?)aftershock2; object riftTrail2; if (preset != MeleeSpecialPreset.RiftTrail) { RiftTrailConfig? riftTrail = raw.RiftTrail; riftTrail2 = ((riftTrail != null && riftTrail.Enabled == false) ? NormalizeRiftTrail(raw.RiftTrail, fallback?.RiftTrail) : null); } else { riftTrail2 = NormalizeRiftTrail(raw.RiftTrail ?? new RiftTrailConfig(), fallback?.RiftTrail); } obj4.RiftTrail = (NormalizedRiftTrailConfig?)riftTrail2; object fractureLine2; if (preset != MeleeSpecialPreset.FractureLine) { FractureLineConfig? fractureLine = raw.FractureLine; fractureLine2 = ((fractureLine != null && fractureLine.Enabled == false) ? NormalizeFractureLine(raw.FractureLine, fallback?.FractureLine) : null); } else { fractureLine2 = NormalizeFractureLine(raw.FractureLine ?? new FractureLineConfig(), fallback?.FractureLine); } obj4.FractureLine = (NormalizedFractureLineConfig?)fractureLine2; obj4.ImpactBurst = normalizedImpactBurstConfig; obj4.Boomerang = normalizedBoomerangConfig; obj4.SpinningSweep = normalizedSpinningSweepConfig; obj4.HarvestSweep = ((raw.HarvestSweep != null) ? NormalizeHarvestSweep(raw.HarvestSweep, fallback?.HarvestSweep) : null); obj4.MeleePreset = preset; obj4.HasExplicitMeleePreset = flag; return obj4; } public static NormalizedWeaponConfig FromBloodMagicRaw(BloodMagicWeaponConfig raw, NormalizedWeaponConfig? fallback = null) { return new NormalizedWeaponConfig { Enabled = (raw.Enabled ?? true), UseAutomaticFallback = (fallback == null && string.IsNullOrWhiteSpace(raw.Preset)), Secondary = NormalizeBloodMagic(raw, fallback?.Secondary) }; } private static bool TryParseExplicitMeleePreset(string prefabName, string? rawPreset, out MeleeSpecialPreset preset) { preset = MeleeSpecialPreset.None; if (string.IsNullOrWhiteSpace(rawPreset)) { return false; } string text = rawPreset.Trim(); if (TryParseMeleePreset(text, out preset)) { return true; } SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Unknown melee preset '" + text + "' on " + prefabName + "; no melee special preset will be applied. Valid values: none, sneakAmbush, cleavingThrust, spearRain, impactBurst, boomerang, spinningSweep, launchSlam, knockbackChain, aftershock, riftTrail, fractureLine.")); preset = MeleeSpecialPreset.None; return true; } private static bool TryParseMeleePreset(string rawPreset, out MeleeSpecialPreset preset) { string text = rawPreset.Trim(); if (text.Equals("none", StringComparison.OrdinalIgnoreCase) || text.Equals("off", StringComparison.OrdinalIgnoreCase) || text.Equals("disabled", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.None; return true; } preset = MeleeSpecialPreset.None; if (SecondaryAttackPresetCatalog.TryGet(text, out SecondaryAttackPresetInfo info) && info.Group == SecondaryAttackPresetGroup.Melee && Enum.TryParse(info.Key, ignoreCase: true, out preset)) { return preset != MeleeSpecialPreset.None; } return false; } private static MeleeSpecialPreset ResolveImplicitMeleePreset(string prefabName, MeleeWeaponConfig raw) { List list = new List(); SneakAmbushConfig sneakAmbush = raw.SneakAmbush; if (sneakAmbush != null && sneakAmbush.Enabled != false) { list.Add(MeleeSpecialPreset.SneakAmbush); } if (raw.CleavingThrust != null && raw.CleavingThrust.Enabled != false) { list.Add(MeleeSpecialPreset.CleavingThrust); } if (raw.SpearRain != null && raw.SpearRain.Enabled != false) { list.Add(MeleeSpecialPreset.SpearRain); } if (raw.ImpactBurst != null && raw.ImpactBurst.Enabled != false) { list.Add(MeleeSpecialPreset.ImpactBurst); } if (raw.Boomerang != null && raw.Boomerang.Enabled != false) { list.Add(MeleeSpecialPreset.Boomerang); } if (raw.SpinningSweep != null && raw.SpinningSweep.Enabled != false) { list.Add(MeleeSpecialPreset.SpinningSweep); } if (raw.LaunchSlam != null && raw.LaunchSlam.Enabled != false) { list.Add(MeleeSpecialPreset.LaunchSlam); } if (raw.KnockbackChain != null && raw.KnockbackChain.Enabled != false) { list.Add(MeleeSpecialPreset.KnockbackChain); } if (raw.Aftershock != null && raw.Aftershock.Enabled != false) { list.Add(MeleeSpecialPreset.Aftershock); } if (raw.RiftTrail != null && raw.RiftTrail.Enabled != false) { list.Add(MeleeSpecialPreset.RiftTrail); } if (raw.FractureLine != null && raw.FractureLine.Enabled != false) { list.Add(MeleeSpecialPreset.FractureLine); } if (list.Count == 0) { return MeleeSpecialPreset.None; } MeleeSpecialPreset meleeSpecialPreset = SelectImplicitMeleePreset(list); if (list.Count > 1) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Multiple melee preset blocks configured for " + prefabName + "; using " + FormatMeleePreset(meleeSpecialPreset) + " and ignoring " + FormatIgnoredMeleePresets(list, meleeSpecialPreset) + ". Add 'preset: " + FormatMeleePreset(meleeSpecialPreset) + "' to make this explicit.")); } return meleeSpecialPreset; } private static MeleeSpecialPreset SelectImplicitMeleePreset(List candidates) { if (candidates.Contains(MeleeSpecialPreset.SpearRain)) { return MeleeSpecialPreset.SpearRain; } if (candidates.Contains(MeleeSpecialPreset.ImpactBurst)) { return MeleeSpecialPreset.ImpactBurst; } if (candidates.Contains(MeleeSpecialPreset.Boomerang)) { return MeleeSpecialPreset.Boomerang; } if (candidates.Contains(MeleeSpecialPreset.SpinningSweep)) { return MeleeSpecialPreset.SpinningSweep; } if (candidates.Contains(MeleeSpecialPreset.CleavingThrust)) { return MeleeSpecialPreset.CleavingThrust; } if (candidates.Contains(MeleeSpecialPreset.RiftTrail)) { return MeleeSpecialPreset.RiftTrail; } if (candidates.Contains(MeleeSpecialPreset.FractureLine)) { return MeleeSpecialPreset.FractureLine; } if (candidates.Contains(MeleeSpecialPreset.LaunchSlam)) { return MeleeSpecialPreset.LaunchSlam; } if (candidates.Contains(MeleeSpecialPreset.KnockbackChain)) { return MeleeSpecialPreset.KnockbackChain; } if (candidates.Contains(MeleeSpecialPreset.Aftershock)) { return MeleeSpecialPreset.Aftershock; } if (!candidates.Contains(MeleeSpecialPreset.SneakAmbush)) { return MeleeSpecialPreset.None; } return MeleeSpecialPreset.SneakAmbush; } private static string FormatIgnoredMeleePresets(List candidates, MeleeSpecialPreset selectedPreset) { List list = new List(); foreach (MeleeSpecialPreset candidate in candidates) { if (candidate != selectedPreset) { list.Add(FormatMeleePreset(candidate)); } } return string.Join(", ", list); } private static string FormatMeleePreset(MeleeSpecialPreset preset) { return SecondaryAttackPresetCatalog.GetKey(preset) ?? "none"; } private static void LogIgnoredExplicitMeleePresetBlocks(string prefabName, MeleeWeaponConfig raw, MeleeSpecialPreset selectedPreset) { List list = new List(); if (selectedPreset != MeleeSpecialPreset.SneakAmbush && raw.SneakAmbush != null) { list.Add("sneakAmbush"); } if (selectedPreset != MeleeSpecialPreset.CleavingThrust && raw.CleavingThrust != null) { list.Add("cleavingThrust"); } if (selectedPreset != MeleeSpecialPreset.SpearRain && raw.SpearRain != null) { list.Add("spearRain"); } if (selectedPreset != MeleeSpecialPreset.ImpactBurst && raw.ImpactBurst != null) { list.Add("impactBurst"); } if (selectedPreset != MeleeSpecialPreset.Boomerang && raw.Boomerang != null) { list.Add("boomerang"); } if (selectedPreset != MeleeSpecialPreset.SpinningSweep && raw.SpinningSweep != null) { list.Add("spinningSweep"); } if (selectedPreset != MeleeSpecialPreset.LaunchSlam && raw.LaunchSlam != null) { list.Add("launchSlam"); } if (selectedPreset != MeleeSpecialPreset.KnockbackChain && raw.KnockbackChain != null) { list.Add("knockbackChain"); } if (selectedPreset != MeleeSpecialPreset.Aftershock && raw.Aftershock != null) { list.Add("aftershock"); } if (selectedPreset != MeleeSpecialPreset.RiftTrail && raw.RiftTrail != null) { list.Add("riftTrail"); } if (selectedPreset != MeleeSpecialPreset.FractureLine && raw.FractureLine != null) { list.Add("fractureLine"); } if (list.Count != 0) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Ignoring melee preset block(s) " + string.Join(", ", list) + " on " + prefabName + " because preset is " + FormatMeleePreset(selectedPreset) + ".")); } } internal static NormalizedSecondaryModeConfig CreateImpactBurstSecondary(NormalizedImpactBurstConfig impactBurst) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) return new NormalizedSecondaryModeConfig { Type = "copy", Animation = impactBurst.Animation, ResourceMultiplier = impactBurst.ResourceMultiplier, OutputMultiplier = 1f, DurabilityFactor = impactBurst.DurabilityFactor, Projectile = new NormalizedProjectileSecondaryConfig(), CopyFrom = "SpearFlint", OnProjectileHit = new NormalizedMeleeOnProjectileHitConfig { Preset = "impactBurst", Cooldown = impactBurst.Cooldown, CooldownReductionFactor = impactBurst.CooldownReductionFactor, ResourceMultiplier = impactBurst.ResourceMultiplier, DurabilityFactor = impactBurst.DurabilityFactor, ProjectileSpinAxis = impactBurst.ProjectileSpinAxis, ProjectileVisualRotationOffset = impactBurst.ProjectileVisualRotationOffset, Vfx = impactBurst.Vfx, DamageFactor = impactBurst.DamageFactor, PushFactor = impactBurst.PushFactor, Radius = impactBurst.Radius, IncludeDirectTarget = false, IncludeDestructibles = true, TriggerOnCharactersOnly = false }, SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } internal static NormalizedSecondaryModeConfig CreateBoomerangSecondary(NormalizedBoomerangConfig boomerang) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = boomerang.Animation, ResourceMultiplier = boomerang.ResourceMultiplier, OutputMultiplier = 1f, DurabilityFactor = boomerang.DurabilityFactor, Projectile = new NormalizedProjectileSecondaryConfig(), CopyFrom = "SpearFlint", SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } internal static NormalizedSecondaryModeConfig CreateSpinningSweepSecondary(NormalizedSpinningSweepConfig spinningSweep) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = (string.IsNullOrWhiteSpace(spinningSweep.Animation) ? "atgeir_secondary" : spinningSweep.Animation), ResourceMultiplier = spinningSweep.ResourceMultiplier, OutputMultiplier = 1f, DurabilityFactor = spinningSweep.DurabilityFactor, Projectile = new NormalizedProjectileSecondaryConfig(), CopyFrom = "", SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig NormalizeRanged(RangedWeaponConfig rawRanged, NormalizedSecondaryModeConfig? fallback, string? presetOverride = null) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = fallback ?? new NormalizedSecondaryModeConfig(); string preset = SecondaryAttackPresetCatalog.CanonicalizeKey(presetOverride ?? rawRanged.Preset ?? normalizedSecondaryModeConfig.Projectile.Preset); bool flag = IsBombRangedPreset(preset); return new NormalizedSecondaryModeConfig { Type = "projectile", Animation = (flag ? "" : ((rawRanged.Animation != null) ? rawRanged.Animation.Trim() : normalizedSecondaryModeConfig.Animation)), ResourceMultiplier = (flag ? 1f : (rawRanged.ResourceMultiplier ?? normalizedSecondaryModeConfig.ResourceMultiplier)), DurabilityFactor = (flag ? 1f : (rawRanged.DurabilityFactor ?? normalizedSecondaryModeConfig.DurabilityFactor)), OutputMultiplier = 1f, Projectile = NormalizeRangedProjectile(rawRanged, normalizedSecondaryModeConfig.Projectile, preset), CopyFrom = "", SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig NormalizeMelee(MeleeWeaponConfig rawMelee, MeleeOnProjectileHitConfig? rawOnProjectileHit, bool forceSpearRainPreset, NormalizedMeleeOnProjectileHitConfig? fallbackOnProjectileHit, string secondaryType = "copy") { return new NormalizedSecondaryModeConfig { Type = secondaryType, Animation = (rawMelee.Animation?.Trim() ?? ""), ResourceMultiplier = rawMelee.ResourceMultiplier, OutputMultiplier = rawMelee.OutputMultiplier, DurabilityFactor = (rawMelee.DurabilityFactor ?? 1f), Projectile = new NormalizedProjectileSecondaryConfig(), CopyFrom = (rawMelee.CopyFrom?.Trim() ?? ""), OnProjectileHit = NormalizeMeleeOnProjectileHit(rawOnProjectileHit, forceSpearRainPreset, fallbackOnProjectileHit), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static bool HasMeleeSecondaryConfig(MeleeWeaponConfig rawMelee, MeleeSpecialPreset selectedPreset, MeleeOnProjectileHitConfig? selectedProjectileHit) { if (string.IsNullOrWhiteSpace(rawMelee.CopyFrom) && string.IsNullOrWhiteSpace(rawMelee.Animation) && selectedProjectileHit == null && selectedPreset != MeleeSpecialPreset.SpearRain && selectedPreset != MeleeSpecialPreset.ImpactBurst && selectedPreset != MeleeSpecialPreset.Boomerang && selectedPreset != MeleeSpecialPreset.SpinningSweep && selectedPreset != MeleeSpecialPreset.Aftershock && selectedPreset != MeleeSpecialPreset.RiftTrail && selectedPreset != MeleeSpecialPreset.FractureLine && !rawMelee.DurabilityFactor.HasValue && !(Math.Abs(rawMelee.ResourceMultiplier - 1f) > 0.0001f)) { return Math.Abs(rawMelee.OutputMultiplier - 1f) > 0.0001f; } return true; } private static string ResolveMeleeSecondaryType(MeleeWeaponConfig rawMelee, MeleeSpecialPreset selectedPreset) { return selectedPreset switch { MeleeSpecialPreset.Aftershock => "aftershock", MeleeSpecialPreset.FractureLine => "fractureLine", _ => "copy", }; } private static TNormalized? NormalizeOptional(TSource? raw, TNormalized? fallback, Func createDefault, Func clone, Func merge, bool inheritFallbackWhenRawMissing = true) where TSource : class where TNormalized : class { if (raw == null) { if (!inheritFallbackWhenRawMissing || fallback == null) { return null; } return clone(fallback); } return merge(raw, fallback ?? createDefault()); } private static NormalizedMeleeOnProjectileHitConfig? NormalizeMeleeOnProjectileHit(MeleeOnProjectileHitConfig? rawOnHit, bool forceSpearRainPreset, NormalizedMeleeOnProjectileHitConfig? fallback) { return NormalizeOptional(rawOnHit, fallback, NormalizedMeleeOnProjectileHitConfig.CreateDefault, (NormalizedMeleeOnProjectileHitConfig config) => config.Clone(), delegate(MeleeOnProjectileHitConfig raw, NormalizedMeleeOnProjectileHitConfig baseConfig) { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_014e: 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) string text = (forceSpearRainPreset ? "spearRain" : (raw.Preset?.Trim() ?? "")); if (string.IsNullOrWhiteSpace(text)) { text = ((fallback != null) ? baseConfig.Preset : ""); } text = SecondaryAttackPresetCatalog.CanonicalizeKey(text); bool flag = text.Equals("spearRain", StringComparison.OrdinalIgnoreCase); return new NormalizedMeleeOnProjectileHitConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Preset = text, Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), ProjectileSpinAxis = (flag ? "none" : NormalizeProjectileSpinAxis(raw.ProjectileSpinAxis, baseConfig.ProjectileSpinAxis)), ProjectileVisualRotationOffset = (flag ? Vector3.zero : NormalizeProjectileVisualRotationOffset(raw.ProjectileVisualRotationOffset, baseConfig.ProjectileVisualRotationOffset)), Vfx = ((raw.Vfx != null) ? raw.Vfx.Trim() : baseConfig.Vfx), Count = (raw.Count ?? baseConfig.Count), SpawnHeight = (raw.SpawnHeight ?? baseConfig.SpawnHeight), SpawnRadius = (raw.SpawnRadius ?? baseConfig.SpawnRadius), FlightTime = (raw.FlightTime ?? baseConfig.FlightTime), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor), Radius = (raw.Radius ?? baseConfig.Radius), IncludeDirectTarget = (raw.IncludeDirectTarget ?? baseConfig.IncludeDirectTarget), IncludeDestructibles = (raw.IncludeDestructibles ?? baseConfig.IncludeDestructibles), TriggerOnCharactersOnly = true }; }); } private static NormalizedImpactBurstConfig? NormalizeImpactBurst(ImpactBurstConfig? rawImpactBurst, NormalizedImpactBurstConfig? fallback) { return NormalizeOptional(rawImpactBurst, fallback, NormalizedImpactBurstConfig.CreateDefault, (NormalizedImpactBurstConfig config) => config.Clone(), (ImpactBurstConfig raw, NormalizedImpactBurstConfig baseConfig) => new NormalizedImpactBurstConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Animation = ((!string.IsNullOrWhiteSpace(raw.Animation)) ? raw.Animation.Trim() : baseConfig.Animation), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), ProjectileSpinAxis = NormalizeProjectileSpinAxis(raw.ProjectileSpinAxis, baseConfig.ProjectileSpinAxis), ProjectileVisualRotationOffset = NormalizeProjectileVisualRotationOffset(raw.ProjectileVisualRotationOffset, baseConfig.ProjectileVisualRotationOffset), Vfx = "vfx_archerytarget_bullseye_double", Radius = (raw.Radius ?? baseConfig.Radius), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor) }); } private static NormalizedBoomerangConfig? NormalizeBoomerang(BoomerangConfig? rawBoomerang, NormalizedBoomerangConfig? fallback) { return NormalizeOptional(rawBoomerang, fallback, NormalizedBoomerangConfig.CreateDefault, (NormalizedBoomerangConfig config) => config.Clone(), (BoomerangConfig raw, NormalizedBoomerangConfig baseConfig) => new NormalizedBoomerangConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Animation = ((!string.IsNullOrWhiteSpace(raw.Animation)) ? raw.Animation.Trim() : baseConfig.Animation), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), ProjectileSpinAxis = NormalizeProjectileSpinAxis(raw.ProjectileSpinAxis, baseConfig.ProjectileSpinAxis), ProjectileVisualRotationOffset = NormalizeProjectileVisualRotationOffset(raw.ProjectileVisualRotationOffset, baseConfig.ProjectileVisualRotationOffset), MaxDistance = (raw.MaxDistance ?? baseConfig.MaxDistance), CurveFactor = (raw.CurveFactor ?? baseConfig.CurveFactor), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor), HitDamageDecay = (raw.HitDamageDecay ?? baseConfig.HitDamageDecay), IncludeDestructibles = true }); } private static NormalizedSpinningSweepConfig? NormalizeSpinningSweep(SpinningSweepConfig? rawSpinningSweep, NormalizedSpinningSweepConfig? fallback) { return NormalizeOptional(rawSpinningSweep, fallback, NormalizedSpinningSweepConfig.CreateDefault, (NormalizedSpinningSweepConfig config) => config.Clone(), (SpinningSweepConfig raw, NormalizedSpinningSweepConfig baseConfig) => new NormalizedSpinningSweepConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Animation = ((!string.IsNullOrWhiteSpace(raw.Animation)) ? raw.Animation.Trim() : baseConfig.Animation), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor), LoopStart = (raw.LoopStart ?? baseConfig.LoopStart), LoopEnd = (raw.LoopEnd ?? baseConfig.LoopEnd), AnimationSpeed = (raw.AnimationSpeed ?? baseConfig.AnimationSpeed), MoveSpeedFactor = (raw.MoveSpeedFactor ?? baseConfig.MoveSpeedFactor), SkillRaiseFactor = (raw.SkillRaiseFactor ?? baseConfig.SkillRaiseFactor) }); } private static string NormalizeProjectileSpinAxis(string? rawSpinAxis, string fallback) { if (rawSpinAxis == null) { return fallback; } string text = ProjectileSpinAxis.Normalize(rawSpinAxis); if (!string.IsNullOrWhiteSpace(text)) { return text; } string text2 = rawSpinAxis.Trim(); if (text2.Length > 0) { SecondaryAttackWarningLog.WarnOnce("projectile_spin_axis:" + text2, "Invalid projectileSpinAxis '" + text2 + "'. Expected 'none', 'horizontal', or 'vertical'. Falling back to '" + fallback + "'."); } return fallback; } private static Vector3 NormalizeProjectileVisualRotationOffset(string? rawOffset, Vector3 fallback) { //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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(rawOffset)) { return fallback; } string[] array = rawOffset.Split(','); if (array.Length != 3) { return fallback; } if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return fallback; } return new Vector3(result, result2, result3); } private static NormalizedSneakAmbushConfig? NormalizeSneakAmbush(SneakAmbushConfig? rawSneakAmbush, NormalizedSneakAmbushConfig? fallback) { return NormalizeOptional(rawSneakAmbush, fallback, NormalizedSneakAmbushConfig.CreateDefault, (NormalizedSneakAmbushConfig config) => config.Clone(), (SneakAmbushConfig raw, NormalizedSneakAmbushConfig baseConfig) => new NormalizedSneakAmbushConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), ChargeMaxSeconds = (raw.ChargeMaxSeconds ?? baseConfig.ChargeMaxSeconds), ChargeSkillFactor = (raw.ChargeSkillFactor ?? baseConfig.ChargeSkillFactor), AggroResetRangePerChargeSecond = (raw.AggroResetRangePerChargeSecond ?? baseConfig.AggroResetRangePerChargeSecond), SenseBlockDurationPerChargeSecond = (raw.SenseBlockDurationPerChargeSecond ?? baseConfig.SenseBlockDurationPerChargeSecond), BackstabResetSecondsPerChargeSecond = (raw.BackstabResetSecondsPerChargeSecond ?? baseConfig.BackstabResetSecondsPerChargeSecond) }); } private static NormalizedCleavingThrustConfig? NormalizeCleavingThrust(CleavingThrustConfig? rawCleavingThrust, NormalizedCleavingThrustConfig? fallback) { return NormalizeOptional(rawCleavingThrust, fallback, NormalizedCleavingThrustConfig.CreateDefault, (NormalizedCleavingThrustConfig config) => config.Clone(), (CleavingThrustConfig raw, NormalizedCleavingThrustConfig baseConfig) => new NormalizedCleavingThrustConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), RangeFactor = (raw.RangeFactor ?? baseConfig.RangeFactor), Angle = (raw.Angle ?? baseConfig.Angle), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor) }); } private static NormalizedLaunchSlamConfig? NormalizeLaunchSlam(LaunchSlamConfig? rawLaunchSlam, NormalizedLaunchSlamConfig? fallback) { return NormalizeOptional(rawLaunchSlam, fallback, NormalizedLaunchSlamConfig.CreateDefault, (NormalizedLaunchSlamConfig config) => config.Clone(), (LaunchSlamConfig raw, NormalizedLaunchSlamConfig baseConfig) => new NormalizedLaunchSlamConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), LaunchHeight = (raw.LaunchHeight ?? baseConfig.LaunchHeight), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor) }); } private static NormalizedKnockbackChainConfig? NormalizeKnockbackChain(KnockbackChainConfig? rawKnockbackChain, NormalizedKnockbackChainConfig? fallback) { return NormalizeOptional(rawKnockbackChain, fallback, NormalizedKnockbackChainConfig.CreateDefault, (NormalizedKnockbackChainConfig config) => config.Clone(), (KnockbackChainConfig raw, NormalizedKnockbackChainConfig baseConfig) => new NormalizedKnockbackChainConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor), ChainDecay = (raw.ChainDecay ?? baseConfig.ChainDecay) }); } private static NormalizedAftershockConfig? NormalizeAftershock(AftershockConfig? rawAftershock, NormalizedAftershockConfig? fallback) { return NormalizeOptional(rawAftershock, fallback, NormalizedAftershockConfig.CreateDefault, (NormalizedAftershockConfig config) => config.Clone(), (AftershockConfig raw, NormalizedAftershockConfig baseConfig) => new NormalizedAftershockConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), Waves = (raw.Waves ?? baseConfig.Waves), Interval = (raw.Interval ?? baseConfig.Interval), WaveDecay = (raw.WaveDecay ?? baseConfig.WaveDecay), ForwardStep = (raw.ForwardStep ?? baseConfig.ForwardStep), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor) }); } private static NormalizedRiftTrailConfig? NormalizeRiftTrail(RiftTrailConfig? rawRiftTrail, NormalizedRiftTrailConfig? fallback) { return NormalizeOptional(rawRiftTrail, fallback, NormalizedRiftTrailConfig.CreateDefault, (NormalizedRiftTrailConfig config) => config.Clone(), (RiftTrailConfig raw, NormalizedRiftTrailConfig baseConfig) => new NormalizedRiftTrailConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), Duration = (raw.Duration ?? baseConfig.Duration), TickInterval = (raw.TickInterval ?? baseConfig.TickInterval), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), PushFactor = (raw.PushFactor ?? baseConfig.PushFactor), Range = (raw.Range ?? baseConfig.Range), Angle = (raw.Angle ?? baseConfig.Angle), Width = (raw.Width ?? baseConfig.Width), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), VisualScaleFactor = baseConfig.VisualScaleFactor, VisualForwardOffset = baseConfig.VisualForwardOffset, VisualTint = baseConfig.VisualTint, VisualAlphaFactor = baseConfig.VisualAlphaFactor }); } private static NormalizedFractureLineConfig? NormalizeFractureLine(FractureLineConfig? rawFractureLine, NormalizedFractureLineConfig? fallback) { return NormalizeOptional(rawFractureLine, fallback, NormalizedFractureLineConfig.CreateDefault, (NormalizedFractureLineConfig config) => config.Clone(), (FractureLineConfig raw, NormalizedFractureLineConfig baseConfig) => new NormalizedFractureLineConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), Range = (raw.Range ?? baseConfig.Range), HitSpacing = (raw.HitSpacing ?? baseConfig.HitSpacing), Duration = (raw.Duration ?? baseConfig.Duration), TickInterval = (raw.TickInterval ?? baseConfig.TickInterval), DamageFactor = (raw.DamageFactor ?? baseConfig.DamageFactor), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor) }); } private static NormalizedHarvestSweepConfig? NormalizeHarvestSweep(HarvestSweepConfig? rawHarvestSweep, NormalizedHarvestSweepConfig? fallback) { return NormalizeOptional(rawHarvestSweep, fallback, NormalizedHarvestSweepConfig.CreateDefault, (NormalizedHarvestSweepConfig config) => config.Clone(), (HarvestSweepConfig raw, NormalizedHarvestSweepConfig baseConfig) => new NormalizedHarvestSweepConfig { Enabled = (raw.Enabled ?? baseConfig.Enabled), Cooldown = (raw.Cooldown ?? baseConfig.Cooldown), CooldownReductionFactor = (raw.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor), ResourceMultiplier = (raw.ResourceMultiplier ?? baseConfig.ResourceMultiplier), DurabilityFactor = (raw.DurabilityFactor ?? baseConfig.DurabilityFactor), Animation = ((!string.IsNullOrWhiteSpace(raw.Animation)) ? raw.Animation.Trim() : baseConfig.Animation), LoopStart = (raw.LoopStart ?? baseConfig.LoopStart), LoopEnd = (raw.LoopEnd ?? baseConfig.LoopEnd), AnimationSpeed = (raw.AnimationSpeed ?? baseConfig.AnimationSpeed), MoveSpeedFactor = (raw.MoveSpeedFactor ?? baseConfig.MoveSpeedFactor), SkillRaiseFactor = (raw.SkillRaiseFactor ?? baseConfig.SkillRaiseFactor) }, inheritFallbackWhenRawMissing: false); } private static NormalizedSecondaryModeConfig NormalizeBloodMagic(BloodMagicWeaponConfig rawBloodMagic, NormalizedSecondaryModeConfig? fallback, string? presetOverride = null) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = fallback ?? new NormalizedSecondaryModeConfig(); NormalizedSummonEmpowerSecondaryConfig normalizedSummonEmpowerSecondaryConfig = fallback?.SummonEmpower ?? new NormalizedSummonEmpowerSecondaryConfig(); NormalizedShieldConvertSecondaryConfig normalizedShieldConvertSecondaryConfig = fallback?.ShieldConvert ?? new NormalizedShieldConvertSecondaryConfig(); string type = SecondaryAttackPresetCatalog.CanonicalizeKey(presetOverride ?? rawBloodMagic.Preset ?? normalizedSecondaryModeConfig.Type); float radius = rawBloodMagic.Radius ?? normalizedSummonEmpowerSecondaryConfig.Radius; float radius2 = rawBloodMagic.Radius ?? normalizedShieldConvertSecondaryConfig.Radius; return new NormalizedSecondaryModeConfig { Type = type, Animation = ((rawBloodMagic.Animation != null) ? rawBloodMagic.Animation.Trim() : normalizedSecondaryModeConfig.Animation), ResourceMultiplier = (rawBloodMagic.ResourceMultiplier ?? normalizedSecondaryModeConfig.ResourceMultiplier), DurabilityFactor = (rawBloodMagic.DurabilityFactor ?? normalizedSecondaryModeConfig.DurabilityFactor), Projectile = new NormalizedProjectileSecondaryConfig(), CopyFrom = "", SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig { PresetCooldown = new MeleePresetCooldownDefinition { Cooldown = (rawBloodMagic.Cooldown ?? normalizedSummonEmpowerSecondaryConfig.PresetCooldown.Cooldown), CooldownSkill = (string.IsNullOrWhiteSpace(normalizedSummonEmpowerSecondaryConfig.PresetCooldown.CooldownSkill) ? "bloodMagic" : normalizedSummonEmpowerSecondaryConfig.PresetCooldown.CooldownSkill.Trim()), CooldownReductionFactor = (rawBloodMagic.CooldownReductionFactor ?? normalizedSummonEmpowerSecondaryConfig.PresetCooldown.CooldownReductionFactor) }, Radius = radius, Duration = (rawBloodMagic.Duration ?? normalizedSummonEmpowerSecondaryConfig.Duration), MoveSpeedFactor = (rawBloodMagic.MoveSpeedFactor ?? normalizedSummonEmpowerSecondaryConfig.MoveSpeedFactor), AttackSpeedFactor = (rawBloodMagic.AttackSpeedFactor ?? normalizedSummonEmpowerSecondaryConfig.AttackSpeedFactor) }, ShieldConvert = new NormalizedShieldConvertSecondaryConfig { PresetCooldown = new MeleePresetCooldownDefinition { Cooldown = (rawBloodMagic.Cooldown ?? normalizedShieldConvertSecondaryConfig.PresetCooldown.Cooldown), CooldownSkill = (string.IsNullOrWhiteSpace(normalizedShieldConvertSecondaryConfig.PresetCooldown.CooldownSkill) ? "bloodMagic" : normalizedShieldConvertSecondaryConfig.PresetCooldown.CooldownSkill.Trim()), CooldownReductionFactor = (rawBloodMagic.CooldownReductionFactor ?? normalizedShieldConvertSecondaryConfig.PresetCooldown.CooldownReductionFactor) }, Radius = radius2, HealFactor = (rawBloodMagic.HealFactor ?? normalizedShieldConvertSecondaryConfig.HealFactor) } }; } private static NormalizedProjectileSecondaryConfig NormalizeRangedProjectile(RangedWeaponConfig rawRanged, NormalizedProjectileSecondaryConfig baseConfig, string preset) { bool flag = IsStickyDetonatorRangedPreset(preset); bool flag2 = IsOverchargedBombRangedPreset(preset); bool flag3 = flag || flag2; NormalizedProjectileSecondaryConfig normalizedProjectileSecondaryConfig = new NormalizedProjectileSecondaryConfig(); return new NormalizedProjectileSecondaryConfig { Preset = preset, Cooldown = (flag3 ? 0f : (rawRanged.Cooldown ?? baseConfig.Cooldown)), CooldownReductionFactor = (flag3 ? 0f : (rawRanged.CooldownReductionFactor ?? baseConfig.CooldownReductionFactor)), DamageFactor = (flag ? 1f : (rawRanged.DamageFactor ?? baseConfig.DamageFactor)), ProjectileSpeedFactor = (rawRanged.ProjectileSpeedFactor ?? baseConfig.ProjectileSpeedFactor), ProjectileScaleFactor = (rawRanged.ProjectileScaleFactor ?? baseConfig.ProjectileScaleFactor), DurabilityFactor = (flag3 ? 1f : (rawRanged.DurabilityFactor ?? baseConfig.DurabilityFactor)), Count = (flag3 ? 1 : (rawRanged.Count ?? baseConfig.Count)), SpreadAngle = (flag3 ? 0f : (rawRanged.SpreadAngle ?? baseConfig.SpreadAngle)), AmmoConsumption = (rawRanged.AmmoConsumption ?? baseConfig.AmmoConsumption), VolleyRadius = (rawRanged.VolleyRadius ?? baseConfig.VolleyRadius), VolleyArcAngleMin = (rawRanged.VolleyArcAngleMin ?? baseConfig.VolleyArcAngleMin), VolleyArcAngleMax = (rawRanged.VolleyArcAngleMax ?? baseConfig.VolleyArcAngleMax), VolleyMaxRange = (rawRanged.VolleyMaxRange ?? baseConfig.VolleyMaxRange), Interval = (rawRanged.Interval ?? baseConfig.Interval), HoldRepeatInterval = (rawRanged.HoldRepeatInterval ?? baseConfig.HoldRepeatInterval), BarrageSpacing = (rawRanged.BarrageSpacing ?? baseConfig.BarrageSpacing), MeteorRadius = (rawRanged.MeteorRadius ?? baseConfig.MeteorRadius), PierceDamageDecay = (rawRanged.PierceDamageDecay ?? baseConfig.PierceDamageDecay), SplitAngle = (rawRanged.SplitAngle ?? baseConfig.SplitAngle), RicochetBounces = (rawRanged.RicochetBounces ?? baseConfig.RicochetBounces), RicochetDecay = (rawRanged.RicochetDecay ?? baseConfig.RicochetDecay), RicochetRoughness = (rawRanged.RicochetRoughness ?? baseConfig.RicochetRoughness), SpiralRadius = (rawRanged.SpiralRadius ?? baseConfig.SpiralRadius), SpiralTurns = (rawRanged.SpiralTurns ?? baseConfig.SpiralTurns), SentinelDetectionRange = (rawRanged.DetectionRange ?? baseConfig.SentinelDetectionRange), SentinelHoverDistance = (rawRanged.HoverDistance ?? baseConfig.SentinelHoverDistance), SentinelHoverHeight = (rawRanged.HoverHeight ?? baseConfig.SentinelHoverHeight), SentinelHoverElevationAngle = (rawRanged.HoverElevationAngle ?? baseConfig.SentinelHoverElevationAngle), SentinelOrbitRadius = (rawRanged.OrbitRadius ?? baseConfig.SentinelOrbitRadius), SentinelOrbitSpeed = (rawRanged.OrbitSpeed ?? baseConfig.SentinelOrbitSpeed), SentinelLifetime = (rawRanged.Lifetime ?? baseConfig.SentinelLifetime), SentinelAttackDelay = (rawRanged.AttackDelay ?? baseConfig.SentinelAttackDelay), MeteorSpawnHeight = (rawRanged.MeteorSpawnHeight ?? baseConfig.MeteorSpawnHeight), MaxCharges = ((!flag) ? normalizedProjectileSecondaryConfig.MaxCharges : (rawRanged.MaxCharges ?? baseConfig.MaxCharges)), DetonateAnimation = ((!flag) ? "" : ((rawRanged.DetonateAnimation != null) ? rawRanged.DetonateAnimation.Trim() : baseConfig.DetonateAnimation)), AoeRadiusFactor = (flag ? 1f : (rawRanged.AoeRadiusFactor ?? baseConfig.AoeRadiusFactor)) }; } private static bool IsBombRangedPreset(string? preset) { if (!IsStickyDetonatorRangedPreset(preset)) { return IsOverchargedBombRangedPreset(preset); } return true; } private static bool IsStickyDetonatorRangedPreset(string? preset) { return preset?.Trim().Equals("stickyDetonator", StringComparison.OrdinalIgnoreCase) ?? false; } private static bool IsOverchargedBombRangedPreset(string? preset) { return preset?.Trim().Equals("overchargedBomb", StringComparison.OrdinalIgnoreCase) ?? false; } } internal static class SecondaryAttackFacade { private sealed class ApplyFailureState { internal int ConsecutiveFailures; internal DateTime NextAttemptUtc = DateTime.MinValue; internal DateTime NextLogUtc = DateTime.MinValue; internal string Signature = string.Empty; internal int SuppressedLogCount; } private enum YamlAuthorityMode { LocalFiles, SyncedOnly } private static readonly TimeSpan ApplyFailureLogInterval = TimeSpan.FromSeconds(10.0); private static readonly object ReloadLock = new object(); private static readonly ApplyFailureState PendingConfigApplyFailure = new ApplyFailureState(); private static readonly ApplyFailureState PendingWorldApplyFailure = new ApplyFailureState(); private static FileSystemWatcher? _watcher; private static SecondaryAttackReloadDebouncer? _yamlReloadDebouncer; private static SecondaryAttackReloadDebouncer? _applyRetryDebouncer; private static CustomSyncedValue? _syncedYamlEnvelope; private static SecondaryAttackCompiledSnapshot _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; private static SecondaryAttackCompiledSnapshot? _pendingCompiledSnapshot; private static SecondaryAttackAppliedWorldSnapshot _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; private static bool _hasPendingConfig; private static bool _hasPendingWorldReapply; private static int _nextSnapshotId = 1; private static bool _suppressSyncedYamlChanged; private static YamlAuthorityMode _yamlAuthorityMode; private static string _currentYamlFingerprint = string.Empty; private static string? _pendingYamlFingerprint; internal static SecondaryAttackAppliedWorldSnapshot CurrentAppliedWorldSnapshot => _currentAppliedWorldSnapshot; public static void Initialize() { _applyRetryDebouncer = new SecondaryAttackReloadDebouncer(RetryFailedPendingApply, "SecondaryAttacks pending world apply"); SecondaryAttackConfigLoader.EnsureLocalFilesExist(); InitializeSyncedYamlEnvelope(); RefreshYamlAuthorityMode(force: true); } public static void Dispose() { DisposeSyncedYamlEnvelope(); _watcher?.Dispose(); _watcher = null; _yamlReloadDebouncer?.Dispose(); _yamlReloadDebouncer = null; _applyRetryDebouncer?.Dispose(); _applyRetryDebouncer = null; } internal static void TryApplyPendingConfig() { RefreshYamlAuthorityMode(); if (_hasPendingConfig) { CommitPendingConfig(force: false); } else { CommitPendingWorldReapply(force: false); } } internal static void RequestCurrentWorldReapply() { lock (ReloadLock) { StageWorldReapply(); } } internal static void ApplyPendingConfigToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { ApplyPendingConfigToWorld(ZNetScene.instance, objectDb, emitMissingWarnings); } internal static void ApplyPendingConfigToZNetScene(ZNetScene scene, bool emitMissingWarnings) { ApplyPendingConfigToWorld(scene, ObjectDB.instance, emitMissingWarnings); } private static void ApplyPendingConfigToWorld(ZNetScene? scene, ObjectDB? objectDb, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); bool flag = _hasPendingConfig && _pendingCompiledSnapshot != null; SecondaryAttackCompiledSnapshot secondaryAttackCompiledSnapshot = (flag ? _pendingCompiledSnapshot : _currentCompiledSnapshot); string fingerprint = (flag ? (_pendingYamlFingerprint ?? _currentYamlFingerprint) : _currentYamlFingerprint); ApplyFailureState state = (flag ? PendingConfigApplyFailure : PendingWorldApplyFailure); try { ApplyCompiledSnapshotToWorld(scene, objectDb, secondaryAttackCompiledSnapshot, emitMissingWarnings); } catch (Exception exception) { if (!flag) { _hasPendingWorldReapply = true; } RecordApplyFailure(flag ? "staged YAML configuration" : "world configuration", exception, state); return; } if (flag) { CompletePendingConfigCommit(secondaryAttackCompiledSnapshot, fingerprint); } if ((Object)(object)objectDb != (Object)null) { _hasPendingWorldReapply = false; } ResetApplyFailure(PendingConfigApplyFailure); ResetApplyFailure(PendingWorldApplyFailure); } private static void SetupWatcher() { if (_watcher == null) { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); _yamlReloadDebouncer = new SecondaryAttackReloadDebouncer(ReloadLocalYamlFromWatcher, "SecondaryAttacks YAML reload"); _watcher = new FileSystemWatcher(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath, "SecondaryAttacks.*.yml"); _watcher.Changed += OnYamlFileChanged; _watcher.Created += OnYamlFileChanged; _watcher.Deleted += OnYamlFileChanged; _watcher.Renamed += OnYamlFileChanged; _watcher.Error += OnYamlWatcherError; _watcher.IncludeSubdirectories = false; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } } private static void OnYamlFileChanged(object sender, FileSystemEventArgs e) { if (_yamlAuthorityMode == YamlAuthorityMode.LocalFiles) { _yamlReloadDebouncer?.Schedule(); } } private static void OnYamlWatcherError(object sender, ErrorEventArgs e) { if (_yamlAuthorityMode == YamlAuthorityMode.LocalFiles) { SecondaryAttacksPlugin.ModLogger.LogError((object)$"SecondaryAttacks YAML file watcher error; scheduling a full reload. {e.GetException()}"); _yamlReloadDebouncer?.Schedule(); } } private static bool ReloadLocalYamlFromWatcher() { lock (ReloadLock) { try { if (!SecondaryAttackConfigLoader.TryReadStableLocalYamlTexts(out SecondaryAttackYamlTexts yamlTexts)) { return false; } PublishAndApplyLocalYaml(yamlTexts); return true; } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogError((object)("Error reloading SecondaryAttacks YAML configuration: " + ex.Message)); return false; } } } private static void ReloadLocalYaml() { if (_yamlAuthorityMode == YamlAuthorityMode.LocalFiles) { SecondaryAttackConfigLoader.EnsureLocalFilesExist(); PublishAndApplyLocalYaml(SecondaryAttackConfigLoader.ReadLocalYamlTexts()); } } private static void PublishAndApplyLocalYaml(SecondaryAttackYamlTexts yamlTexts) { if (_syncedYamlEnvelope != null) { _suppressSyncedYamlChanged = true; try { _syncedYamlEnvelope.AssignLocalValue(yamlTexts.ToEnvelope()); } finally { _suppressSyncedYamlChanged = false; } } ApplyYamlTexts(yamlTexts); } private static void OnSyncedYamlChanged() { if (!_suppressSyncedYamlChanged && TryReadSyncedYamlTexts(out SecondaryAttackYamlTexts yamlTexts, out bool _)) { ApplyYamlTexts(yamlTexts); } } private static void RefreshYamlAuthorityMode(bool force = false) { YamlAuthorityMode yamlAuthorityMode = DetermineYamlAuthorityMode(); if (!force && yamlAuthorityMode == _yamlAuthorityMode) { return; } _yamlAuthorityMode = yamlAuthorityMode; DiscardPendingConfig(); switch (yamlAuthorityMode) { case YamlAuthorityMode.LocalFiles: SetupWatcher(); ReloadLocalYaml(); SecondaryAttacksPlugin.ModLogger.LogInfo((object)"SecondaryAttacks YAML authority mode: LocalFiles."); break; case YamlAuthorityMode.SyncedOnly: { DisposeWatcher(); if (TryReadSyncedYamlTexts(out SecondaryAttackYamlTexts yamlTexts, out bool hasValue)) { ApplyYamlTexts(yamlTexts); } else if (!hasValue) { _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; _currentYamlFingerprint = string.Empty; _hasPendingWorldReapply = true; ResetApplyFailure(PendingWorldApplyFailure); } SecondaryAttacksPlugin.ModLogger.LogInfo((object)"SecondaryAttacks YAML authority mode: SyncedOnly."); break; } } } private static YamlAuthorityMode DetermineYamlAuthorityMode() { if (!((Object)(object)ZNet.instance != (Object)null) || ZNet.instance.IsServer()) { return YamlAuthorityMode.LocalFiles; } return YamlAuthorityMode.SyncedOnly; } private static void InitializeSyncedYamlEnvelope() { DisposeSyncedYamlEnvelope(); _syncedYamlEnvelope = new CustomSyncedValue(SecondaryAttacksPlugin.ConfigSync, "secondary_attack_yaml_envelope", ""); _syncedYamlEnvelope.ValueChanged += OnSyncedYamlChanged; } private static void DisposeSyncedYamlEnvelope() { if (_syncedYamlEnvelope != null) { _syncedYamlEnvelope.ValueChanged -= OnSyncedYamlChanged; _syncedYamlEnvelope = null; } } private static bool TryReadSyncedYamlTexts(out SecondaryAttackYamlTexts? yamlTexts, out bool hasValue) { yamlTexts = null; string text = _syncedYamlEnvelope?.Value ?? string.Empty; hasValue = !string.IsNullOrEmpty(text); if (!hasValue) { return false; } if (SecondaryAttackYamlTexts.TryFromEnvelope(text, out yamlTexts)) { return true; } SecondaryAttacksPlugin.ModLogger.LogError((object)"Ignoring malformed synchronized SecondaryAttacks YAML envelope."); return false; } private static void DisposeWatcher() { if (_watcher != null) { _watcher.Dispose(); _watcher = null; _yamlReloadDebouncer?.Dispose(); _yamlReloadDebouncer = null; } } private static void ApplyYamlTexts(SecondaryAttackYamlTexts yamlTexts) { string contentFingerprint = yamlTexts.GetContentFingerprint(); if (string.Equals(_currentYamlFingerprint, contentFingerprint, StringComparison.Ordinal)) { DiscardPendingConfig(); return; } if (_hasPendingConfig && string.Equals(_pendingYamlFingerprint, contentFingerprint, StringComparison.Ordinal)) { CommitPendingConfig(force: false); return; } DiscardPendingConfig(); if (SecondaryAttackConfigLoader.TryCompileSnapshot(_nextSnapshotId++, yamlTexts, out SecondaryAttackCompiledSnapshot snapshot)) { StageConfig(snapshot, contentFingerprint); } } private static void DiscardPendingConfig() { _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; ResetApplyFailure(PendingConfigApplyFailure); } private static void StageConfig(SecondaryAttackCompiledSnapshot snapshot, string fingerprint) { _pendingCompiledSnapshot = snapshot; _pendingYamlFingerprint = fingerprint; _hasPendingConfig = true; ResetApplyFailure(PendingConfigApplyFailure); CommitPendingConfig(force: false); } private static void StageWorldReapply() { _hasPendingWorldReapply = true; ResetApplyFailure(PendingWorldApplyFailure); if (_hasPendingConfig) { CommitPendingConfig(force: false); } else { CommitPendingWorldReapply(force: false); } } private static bool CommitPendingConfig(bool force) { if (!_hasPendingConfig || _pendingCompiledSnapshot == null) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } if (!force && !CanAttemptApply(PendingConfigApplyFailure)) { return false; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return false; } SecondaryAttackCompiledSnapshot pendingCompiledSnapshot = _pendingCompiledSnapshot; string fingerprint = _pendingYamlFingerprint ?? _currentYamlFingerprint; try { ApplyCompiledSnapshotToWorld(ZNetScene.instance, instance, pendingCompiledSnapshot, emitMissingWarnings: true); } catch (Exception exception) { RecordApplyFailure("staged YAML configuration", exception, PendingConfigApplyFailure); return false; } CompletePendingConfigCommit(pendingCompiledSnapshot, fingerprint); _hasPendingWorldReapply = false; ResetApplyFailure(PendingConfigApplyFailure); ResetApplyFailure(PendingWorldApplyFailure); return true; } private static bool CommitPendingWorldReapply(bool force) { if (!_hasPendingWorldReapply) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } if (!force && !CanAttemptApply(PendingWorldApplyFailure)) { return false; } if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } try { ApplyCompiledSnapshotToWorld(ZNetScene.instance, ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings: true); } catch (Exception exception) { RecordApplyFailure("world configuration", exception, PendingWorldApplyFailure); return false; } _hasPendingWorldReapply = false; ResetApplyFailure(PendingWorldApplyFailure); SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged world-apply config changes."); return true; } private static void ApplyCompiledSnapshotToWorld(ZNetScene? scene, ObjectDB? objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { SecondaryAttackCompiledSnapshot compiledSnapshot2 = _currentAppliedWorldSnapshot.CompiledSnapshot; try { if ((Object)(object)scene != (Object)null) { SecondaryAttackWorldApplySystem.ApplyToZNetScene(scene, compiledSnapshot, emitMissingWarnings); } if ((Object)(object)objectDb != (Object)null) { _currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, compiledSnapshot, emitMissingWarnings); SecondaryAttackManager.RefreshLocalPlayerRuntimeWeaponDefinitions(); } } catch (Exception applyException) { TryCompensateFailedWorldApply(scene, objectDb, compiledSnapshot2, applyException); throw; } } private static void TryCompensateFailedWorldApply(ZNetScene? scene, ObjectDB? objectDb, SecondaryAttackCompiledSnapshot rollbackSnapshot, Exception applyException) { try { if ((Object)(object)scene != (Object)null) { SecondaryAttackWorldApplySystem.ApplyToZNetScene(scene, rollbackSnapshot, emitMissingWarnings: false); } if ((Object)(object)objectDb != (Object)null) { _currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, rollbackSnapshot, emitMissingWarnings: false); SecondaryAttackManager.RefreshLocalPlayerRuntimeWeaponDefinitions(); } } catch (Exception arg) { SecondaryAttacksPlugin.ModLogger.LogError((object)("Failed to restore the previously applied SecondaryAttacks world after an apply error. " + $"Apply error: {applyException}\nCompensation error: {arg}")); } } private static bool CanApplyPendingConfigNow() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } Attack currentAttack = ((Humanoid)localPlayer).m_currentAttack; if (currentAttack != null) { return currentAttack.IsDone(); } return true; } private static void CompletePendingConfigCommit(SecondaryAttackCompiledSnapshot snapshot, string fingerprint) { if (_hasPendingConfig && _pendingCompiledSnapshot == snapshot) { _currentCompiledSnapshot = snapshot; _currentYamlFingerprint = fingerprint; _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); } } private static bool CanAttemptApply(ApplyFailureState state) { return DateTime.UtcNow >= state.NextAttemptUtc; } private static void RecordApplyFailure(string operation, Exception exception, ApplyFailureState state) { DateTime utcNow = DateTime.UtcNow; state.ConsecutiveFailures++; int num = Math.Min(state.ConsecutiveFailures - 1, 4); double value = Math.Min(0.25 * (double)(1 << num), 4.0); state.NextAttemptUtc = utcNow.AddSeconds(value); if (state.ConsecutiveFailures == 1) { _applyRetryDebouncer?.Schedule(); } string text = exception.GetType().FullName + ": " + exception.Message; if (!string.Equals(text, state.Signature, StringComparison.Ordinal) || utcNow >= state.NextLogUtc) { string arg = ((state.SuppressedLogCount > 0) ? $" ({state.SuppressedLogCount} repeated failures suppressed.)" : string.Empty); SecondaryAttacksPlugin.ModLogger.LogError((object)$"Failed to apply {operation}; the pending change will be retried.{arg}\n{exception}"); state.Signature = text; state.NextLogUtc = utcNow + ApplyFailureLogInterval; state.SuppressedLogCount = 0; } else { state.SuppressedLogCount++; } } private static bool RetryFailedPendingApply() { lock (ReloadLock) { if (!CanApplyPendingConfigNow() || (Object)(object)ObjectDB.instance == (Object)null) { return true; } if (_hasPendingConfig) { return CommitPendingConfig(force: false) || PendingConfigApplyFailure.ConsecutiveFailures == 0; } if (_hasPendingWorldReapply) { return CommitPendingWorldReapply(force: false) || PendingWorldApplyFailure.ConsecutiveFailures == 0; } return true; } } private static void ResetApplyFailure(ApplyFailureState state) { state.ConsecutiveFailures = 0; state.NextAttemptUtc = DateTime.MinValue; state.NextLogUtc = DateTime.MinValue; state.Signature = string.Empty; state.SuppressedLogCount = 0; } } internal static class SecondaryAttackConfigCompiler { public static SecondaryAttackCompiledSnapshot Compile(int snapshotId, SecondaryAttackParsedYaml parsedYaml) { SecondaryAttackWeaponNormalizationResult secondaryAttackWeaponNormalizationResult = SecondaryAttackWeaponConfigNormalizer.Normalize(parsedYaml.Ranged, parsedYaml.Melee, parsedYaml.BloodMagic); return new SecondaryAttackCompiledSnapshot(snapshotId, secondaryAttackWeaponNormalizationResult.Weapons, secondaryAttackWeaponNormalizationResult.GlobalRangedPresets, secondaryAttackWeaponNormalizationResult.GlobalBloodMagicPresets, secondaryAttackWeaponNormalizationResult.GlobalMeleeFallback, SecondaryAttackMagicSummonNormalizer.Normalize(parsedYaml.BloodMagic)); } } internal static class SecondaryAttackDefinitionCompiler { private readonly struct DefinitionFeatures { public bool HasMeleeFeatureConfig { get; } public bool HasSecondaryConfig { get; } public string SecondaryType { get; } public bool UsesSummonEmpower { get; } public bool UsesShieldConvert { get; } public bool UsesAftershock { get; } public bool UsesFractureLine { get; } public bool HasCustomPayload { get; } public bool HasCopiedSecondary { get; } public DefinitionFeatures(bool hasMeleeFeatureConfig, bool hasSecondaryConfig, string secondaryType, bool usesSummonEmpower, bool usesShieldConvert, bool usesAftershock, bool usesFractureLine, bool hasCustomPayload, bool hasCopiedSecondary) { HasMeleeFeatureConfig = hasMeleeFeatureConfig; HasSecondaryConfig = hasSecondaryConfig; SecondaryType = secondaryType; UsesSummonEmpower = usesSummonEmpower; UsesShieldConvert = usesShieldConvert; UsesAftershock = usesAftershock; UsesFractureLine = usesFractureLine; HasCustomPayload = hasCustomPayload; HasCopiedSecondary = hasCopiedSecondary; } } private enum DefinitionValidationDisposition { Continue, Skip, EffectOnly } private readonly struct DefinitionValidationResult { public DefinitionValidationDisposition Disposition { get; } public Attack? PrimaryAttack { get; } public DefinitionValidationResult(DefinitionValidationDisposition disposition, Attack? primaryAttack = null) { Disposition = disposition; PrimaryAttack = primaryAttack; } } internal static bool TryCreateDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, ItemDrop itemDrop, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; SharedData val = itemDrop.m_itemData?.m_shared; if (val == null) { return false; } if (!weaponConfig.Enabled) { return false; } if (IsPresetOptOut(weaponConfig)) { return false; } DefinitionFeatures features = AnalyzeDefinitionFeatures(weaponConfig); DefinitionValidationResult definitionValidationResult = ValidateDefinitionRequest(prefabName, val, weaponConfig, features); switch (definitionValidationResult.Disposition) { case DefinitionValidationDisposition.EffectOnly: definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; case DefinitionValidationDisposition.Skip: return false; default: return TryCreateValidatedDefinition(buildContext, prefabName, val, definitionValidationResult.PrimaryAttack, weaponConfig, features, out definition); } } private static bool IsPresetOptOut(NormalizedWeaponConfig weaponConfig) { NormalizedSecondaryModeConfig secondary = weaponConfig.Secondary; if (secondary == null) { return false; } if (string.Equals(secondary.Type, "none", StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(secondary.Type, "projectile", StringComparison.OrdinalIgnoreCase)) { return string.Equals(secondary.Projectile.Preset, "none", StringComparison.OrdinalIgnoreCase); } return false; } private static DefinitionFeatures AnalyzeDefinitionFeatures(NormalizedWeaponConfig weaponConfig) { bool hasEnabledMeleeFeatureConfig = weaponConfig.HasEnabledMeleeFeatureConfig; bool hasSecondaryConfig = weaponConfig.Secondary != null; string text = weaponConfig.Secondary?.Type?.Trim() ?? ""; bool usesSummonEmpower = text == "summonEmpower"; bool usesShieldConvert = text == "shieldConvert"; bool usesAftershock = text == "aftershock"; bool usesFractureLine = text == "fractureLine"; bool hasCustomPayload = text == "projectile"; bool hasCopiedSecondary = text == "copy"; return new DefinitionFeatures(hasEnabledMeleeFeatureConfig, hasSecondaryConfig, text, usesSummonEmpower, usesShieldConvert, usesAftershock, usesFractureLine, hasCustomPayload, hasCopiedSecondary); } private static bool TryCreateValidatedDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, DefinitionFeatures features, out SecondaryAttackDefinition? definition) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 definition = null; if (features.UsesSummonEmpower) { return SecondaryAttackManager.TryCreateSummonEmpowerDefinition(prefabName, sharedData, primaryAttack, weaponConfig, out definition); } if (features.UsesShieldConvert) { return SecondaryAttackManager.TryCreateShieldConvertDefinition(prefabName, sharedData, primaryAttack, weaponConfig, out definition); } if (features.HasCustomPayload) { if ((int)primaryAttack.m_attackType != 2) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": primary attack is not projectile-based.")); if (features.HasMeleeFeatureConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } return false; } return SecondaryAttackManager.TryCreateCustomPayloadDefinition(prefabName, sharedData, primaryAttack, weaponConfig, out definition); } if (features.UsesAftershock) { return SecondaryAttackManager.TryCreateAftershockDefinition(buildContext, prefabName, sharedData, primaryAttack, weaponConfig, out definition); } if (features.UsesFractureLine) { return SecondaryAttackManager.TryCreateFractureLineDefinition(buildContext, prefabName, primaryAttack, weaponConfig, out definition); } string sourcePrefabName = (string.IsNullOrWhiteSpace(weaponConfig.Secondary?.CopyFrom) ? prefabName : weaponConfig.Secondary.CopyFrom.Trim()); if (features.HasCopiedSecondary) { if (!SecondaryAttackManager.TryResolveSecondarySourceAttack(buildContext.ObjectDb, sourcePrefabName, out Attack sourceAttack, out string reason)) { if (buildContext.EmitMissingWarnings) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": " + reason)); } if (features.HasMeleeFeatureConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } return false; } return SecondaryAttackManager.TryCreateSecondaryOverrideDefinition(prefabName, sourcePrefabName, primaryAttack, sourceAttack, weaponConfig, out definition); } if (features.HasMeleeFeatureConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": unsupported secondary.type '" + features.SecondaryType + "'.")); return false; } private static DefinitionValidationResult ValidateDefinitionRequest(string prefabName, SharedData sharedData, NormalizedWeaponConfig weaponConfig, DefinitionFeatures features) { if (!features.HasSecondaryConfig) { if (features.HasMeleeFeatureConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } if (string.IsNullOrWhiteSpace(features.SecondaryType)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": a secondary behavior preset is required.")); if (!features.HasMeleeFeatureConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } if (features.HasCustomPayload && (weaponConfig.Secondary?.Projectile == null || string.IsNullOrWhiteSpace(weaponConfig.Secondary.Projectile.Preset))) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": ranged secondary requires preset.")); if (!features.HasMeleeFeatureConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } Attack attack = sharedData.m_attack; if (attack == null || string.IsNullOrWhiteSpace(attack.m_attackAnimation)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": primary attack is missing.")); if (!features.HasMeleeFeatureConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } return new DefinitionValidationResult(DefinitionValidationDisposition.Continue, attack); } } internal readonly struct SecondaryAttackDefinitionBuildContext { public ObjectDB ObjectDb { get; } public bool EmitMissingWarnings { get; } public SecondaryAttackDefinitionBuildContext(ObjectDB objectDb, bool emitMissingWarnings) { ObjectDb = objectDb; EmitMissingWarnings = emitMissingWarnings; } } internal enum MagicSummonQualityPreset { None, CountByQuality, LevelByQuality } internal sealed class NormalizedMagicSummonOverrideConfig { public string EntryId { get; set; } = ""; public MagicSummonQualityPreset QualityPreset { get; set; } public int MaxQuality { get; set; } = 4; public bool HasQualityPreset => QualityPreset != MagicSummonQualityPreset.None; public List Summons { get; set; } = new List(); } internal sealed class NormalizedMagicSummonCloneConfig { public string SourcePrefab { get; set; } = ""; public string ClonePrefab { get; set; } = ""; public string DisplayName { get; set; } = ""; public float? Health { get; set; } public int Weight { get; set; } = 1; } internal sealed class NormalizedSummonEmpowerSecondaryConfig { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { Cooldown = 30f, CooldownSkill = "bloodMagic" }; public float Radius { get; set; } = 10f; public float Duration { get; set; } = 15f; public float MoveSpeedFactor { get; set; } = 1.3f; public float AttackSpeedFactor { get; set; } = 1.5f; } internal sealed class NormalizedShieldConvertSecondaryConfig { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { Cooldown = 30f, CooldownSkill = "bloodMagic" }; public float Radius { get; set; } = 7f; public float HealFactor { get; set; } } internal sealed class NormalizedSneakAmbushConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } = 30f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float ChargeMaxSeconds { get; set; } = 8f; public float ChargeSkillFactor { get; set; } = 2f; public float AggroResetRangePerChargeSecond { get; set; } = 1f; public float SenseBlockDurationPerChargeSecond { get; set; } = 0.25f; public float BackstabResetSecondsPerChargeSecond { get; set; } = 35f; public static NormalizedSneakAmbushConfig CreateDefault() { return new NormalizedSneakAmbushConfig(); } public NormalizedSneakAmbushConfig Clone() { return (NormalizedSneakAmbushConfig)MemberwiseClone(); } } internal sealed class NormalizedCleavingThrustConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float RangeFactor { get; set; } = 3f; public float Angle { get; set; } = 90f; public float DamageFactor { get; set; } = 1f; public float PushFactor { get; set; } = 6f; public static NormalizedCleavingThrustConfig CreateDefault() { return new NormalizedCleavingThrustConfig(); } public NormalizedCleavingThrustConfig Clone() { return (NormalizedCleavingThrustConfig)MemberwiseClone(); } } internal sealed class NormalizedLaunchSlamConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float LaunchHeight { get; set; } = 4f; public float DamageFactor { get; set; } = 1f; public static NormalizedLaunchSlamConfig CreateDefault() { return new NormalizedLaunchSlamConfig(); } public NormalizedLaunchSlamConfig Clone() { return (NormalizedLaunchSlamConfig)MemberwiseClone(); } } internal sealed class NormalizedKnockbackChainConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float PushFactor { get; set; } = 8f; public float ChainDecay { get; set; } = 0.75f; public static NormalizedKnockbackChainConfig CreateDefault() { return new NormalizedKnockbackChainConfig(); } public NormalizedKnockbackChainConfig Clone() { return (NormalizedKnockbackChainConfig)MemberwiseClone(); } } internal sealed class NormalizedAftershockConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public int Waves { get; set; } = 3; public float Interval { get; set; } = 0.5f; public float WaveDecay { get; set; } = 0.2f; public float ForwardStep { get; set; } = 3f; public float DurabilityFactor { get; set; } = 1f; public static NormalizedAftershockConfig CreateDefault() { return new NormalizedAftershockConfig(); } public NormalizedAftershockConfig Clone() { return (NormalizedAftershockConfig)MemberwiseClone(); } } internal sealed class NormalizedRiftTrailConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float Duration { get; set; } = 2f; public float TickInterval { get; set; } = 0.5f; public float DamageFactor { get; set; } = 0.25f; public float PushFactor { get; set; } public float Range { get; set; } public float Angle { get; set; } public float Width { get; set; } public float DurabilityFactor { get; set; } = 1f; public float VisualScaleFactor { get; set; } = 1.5f; public float VisualForwardOffset { get; set; } = 1.5f; public string VisualTint { get; set; } = "#ffffff"; public float VisualAlphaFactor { get; set; } = 1f; public static NormalizedRiftTrailConfig CreateDefault() { return new NormalizedRiftTrailConfig(); } public NormalizedRiftTrailConfig Clone() { return (NormalizedRiftTrailConfig)MemberwiseClone(); } } internal sealed class NormalizedFractureLineConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } = 6f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float Range { get; set; } = 5f; public float HitSpacing { get; set; } = 0.75f; public float Duration { get; set; } = 1.5f; public float TickInterval { get; set; } = 0.3f; public float DamageFactor { get; set; } = 0.35f; public float DurabilityFactor { get; set; } = 1f; public static NormalizedFractureLineConfig CreateDefault() { return new NormalizedFractureLineConfig(); } public NormalizedFractureLineConfig Clone() { return (NormalizedFractureLineConfig)MemberwiseClone(); } } internal sealed class NormalizedSpinningSweepConfig { public bool Enabled { get; set; } = true; public string Animation { get; set; } = "atgeir_secondary"; public float Cooldown { get; set; } = 8f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float DamageFactor { get; set; } = 0.75f; public float PushFactor { get; set; } = 0.5f; public float LoopStart { get; set; } = 0.4f; public float LoopEnd { get; set; } = 0.6f; public float AnimationSpeed { get; set; } = 1f; public float MoveSpeedFactor { get; set; } = 0.75f; public float SkillRaiseFactor { get; set; } = 0.25f; public static NormalizedSpinningSweepConfig CreateDefault() { return new NormalizedSpinningSweepConfig(); } public NormalizedSpinningSweepConfig Clone() { return (NormalizedSpinningSweepConfig)MemberwiseClone(); } } internal sealed class NormalizedHarvestSweepConfig { public bool Enabled { get; set; } = true; public float Cooldown { get; set; } = 16f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1.5f; public float DurabilityFactor { get; set; } = 1.5f; public string Animation { get; set; } = "atgeir_secondary"; public float LoopStart { get; set; } = 0.4f; public float LoopEnd { get; set; } = 0.6f; public float AnimationSpeed { get; set; } = 1f; public float MoveSpeedFactor { get; set; } = 0.75f; public float SkillRaiseFactor { get; set; } = 0.25f; public static NormalizedHarvestSweepConfig CreateDefault() { return new NormalizedHarvestSweepConfig(); } public NormalizedHarvestSweepConfig Clone() { return (NormalizedHarvestSweepConfig)MemberwiseClone(); } } internal sealed class NormalizedMeleeOnProjectileHitConfig { public bool Enabled { get; set; } = true; public string Preset { get; set; } = ""; public float Cooldown { get; set; } = 20f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public string ProjectileSpinAxis { get; set; } = "none"; public Vector3 ProjectileVisualRotationOffset { get; set; } = Vector3.zero; public string Vfx { get; set; } = ""; public int Count { get; set; } = 6; public float SpawnHeight { get; set; } = 10f; public float SpawnRadius { get; set; } = 10f; public float FlightTime { get; set; } = 1f; public float DamageFactor { get; set; } = 0.25f; public float PushFactor { get; set; } = 2f; public float Radius { get; set; } = 4f; public bool IncludeDirectTarget { get; set; } public bool IncludeDestructibles { get; set; } public bool TriggerOnCharactersOnly { get; set; } = true; public static NormalizedMeleeOnProjectileHitConfig CreateDefault() { return new NormalizedMeleeOnProjectileHitConfig { Preset = "spearRain" }; } public NormalizedMeleeOnProjectileHitConfig Clone() { return (NormalizedMeleeOnProjectileHitConfig)MemberwiseClone(); } } internal sealed class NormalizedBoomerangConfig { public bool Enabled { get; set; } = true; public string Animation { get; set; } = "swing_axe2"; public float Cooldown { get; set; } = 16f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1.5f; public float DurabilityFactor { get; set; } = 1f; public string ProjectileSpinAxis { get; set; } = "vertical"; public Vector3 ProjectileVisualRotationOffset { get; set; } = Vector3.zero; public float MaxDistance { get; set; } = 12f; public float CurveFactor { get; set; } = 0.5f; public float DamageFactor { get; set; } = 1f; public float PushFactor { get; set; } = 1f; public float HitDamageDecay { get; set; } = 0.2f; public bool IncludeDestructibles { get; set; } = true; public static NormalizedBoomerangConfig CreateDefault() { return new NormalizedBoomerangConfig(); } public NormalizedBoomerangConfig Clone() { return (NormalizedBoomerangConfig)MemberwiseClone(); } } internal sealed class NormalizedImpactBurstConfig { public bool Enabled { get; set; } = true; public string Animation { get; set; } = "battleaxe_attack1"; public float Cooldown { get; set; } = 16f; public float CooldownReductionFactor { get; set; } = 0.5f; public float ResourceMultiplier { get; set; } = 1.5f; public float DurabilityFactor { get; set; } = 1f; public string ProjectileSpinAxis { get; set; } = "vertical"; public Vector3 ProjectileVisualRotationOffset { get; set; } = Vector3.zero; public string Vfx { get; set; } = "vfx_archerytarget_bullseye_double"; public float Radius { get; set; } = 4f; public float DamageFactor { get; set; } = 0.75f; public float PushFactor { get; set; } = 4f; public static NormalizedImpactBurstConfig CreateDefault() { return new NormalizedImpactBurstConfig(); } public NormalizedImpactBurstConfig Clone() { return (NormalizedImpactBurstConfig)MemberwiseClone(); } } internal sealed class NormalizedProjectileSecondaryConfig { public string Preset { get; set; } = ""; public float Cooldown { get; set; } = 8f; public float CooldownReductionFactor { get; set; } = 0.5f; public float DamageFactor { get; set; } = 1f; public float ProjectileSpeedFactor { get; set; } = 1f; public float ProjectileScaleFactor { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public int Count { get; set; } = 1; public float SpreadAngle { get; set; } = 24f; public int AmmoConsumption { get; set; } = -1; public float VolleyRadius { get; set; } = 4f; public float VolleyArcAngleMin { get; set; } = 35f; public float VolleyArcAngleMax { get; set; } = 65f; public float VolleyMaxRange { get; set; } = 64f; public float Interval { get; set; } public float HoldRepeatInterval { get; set; } = 0.2f; public float BarrageSpacing { get; set; } = 0.8f; public float MeteorRadius { get; set; } public float PierceDamageDecay { get; set; } = 0.25f; public float SplitAngle { get; set; } = 30f; public int RicochetBounces { get; set; } = 3; public float RicochetDecay { get; set; } = 0.2f; public float RicochetRoughness { get; set; } = 0.2f; public float SpiralRadius { get; set; } = 0.35f; public float SpiralTurns { get; set; } = 1.5f; public float SentinelDetectionRange { get; set; } = 25f; public float SentinelHoverDistance { get; set; } = 1.75f; public float SentinelHoverHeight { get; set; } = 1.6f; public float SentinelHoverElevationAngle { get; set; } public float SentinelOrbitRadius { get; set; } = 0.35f; public float SentinelOrbitSpeed { get; set; } = 120f; public float SentinelLifetime { get; set; } = 12f; public float SentinelAttackDelay { get; set; } public float MeteorSpawnHeight { get; set; } = 12f; public int MaxCharges { get; set; } = 6; public string DetonateAnimation { get; set; } = "emote_blowkiss"; public float AoeRadiusFactor { get; set; } = 1f; public static NormalizedProjectileSecondaryConfig CreateDefault() { return new NormalizedProjectileSecondaryConfig(); } public NormalizedProjectileSecondaryConfig Clone() { return (NormalizedProjectileSecondaryConfig)MemberwiseClone(); } } internal sealed class NormalizedWeaponConfig { public bool Enabled { get; set; } = true; public bool UseAutomaticFallback { get; set; } public NormalizedSecondaryModeConfig? Secondary { get; set; } public NormalizedSneakAmbushConfig? SneakAmbush { get; set; } public NormalizedCleavingThrustConfig? CleavingThrust { get; set; } public NormalizedLaunchSlamConfig? LaunchSlam { get; set; } public NormalizedKnockbackChainConfig? KnockbackChain { get; set; } public NormalizedAftershockConfig? Aftershock { get; set; } public NormalizedRiftTrailConfig? RiftTrail { get; set; } public NormalizedFractureLineConfig? FractureLine { get; set; } public NormalizedHarvestSweepConfig? HarvestSweep { get; set; } public NormalizedMeleeOnProjectileHitConfig? SpearRain { get; set; } public NormalizedImpactBurstConfig? ImpactBurst { get; set; } public NormalizedBoomerangConfig? Boomerang { get; set; } public NormalizedSpinningSweepConfig? SpinningSweep { get; set; } public MeleeSpecialPreset MeleePreset { get; set; } public bool HasExplicitMeleePreset { get; set; } public bool HasEnabledMeleeFeatureConfig { get { NormalizedSneakAmbushConfig? sneakAmbush = SneakAmbush; if (sneakAmbush == null || !sneakAmbush.Enabled) { NormalizedCleavingThrustConfig? cleavingThrust = CleavingThrust; if (cleavingThrust == null || !cleavingThrust.Enabled) { NormalizedLaunchSlamConfig? launchSlam = LaunchSlam; if (launchSlam == null || !launchSlam.Enabled) { NormalizedKnockbackChainConfig? knockbackChain = KnockbackChain; if (knockbackChain == null || !knockbackChain.Enabled) { NormalizedAftershockConfig? aftershock = Aftershock; if (aftershock == null || !aftershock.Enabled) { NormalizedRiftTrailConfig? riftTrail = RiftTrail; if (riftTrail == null || !riftTrail.Enabled) { NormalizedFractureLineConfig? fractureLine = FractureLine; if (fractureLine == null || !fractureLine.Enabled) { NormalizedImpactBurstConfig? impactBurst = ImpactBurst; if (impactBurst == null || !impactBurst.Enabled) { NormalizedBoomerangConfig? boomerang = Boomerang; if (boomerang == null || !boomerang.Enabled) { NormalizedSpinningSweepConfig? spinningSweep = SpinningSweep; if (spinningSweep == null || !spinningSweep.Enabled) { return HarvestSweep?.Enabled ?? false; } } } } } } } } } } return true; } } public NormalizedWeaponConfig Clone() { return (NormalizedWeaponConfig)MemberwiseClone(); } } internal sealed class NormalizedSecondaryModeConfig { public string Type { get; set; } = ""; public string Animation { get; set; } = ""; public float ResourceMultiplier { get; set; } = 1f; public float OutputMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public NormalizedProjectileSecondaryConfig Projectile { get; set; } = new NormalizedProjectileSecondaryConfig(); public string CopyFrom { get; set; } = ""; public NormalizedMeleeOnProjectileHitConfig? OnProjectileHit { get; set; } public NormalizedSummonEmpowerSecondaryConfig SummonEmpower { get; set; } = new NormalizedSummonEmpowerSecondaryConfig(); public NormalizedShieldConvertSecondaryConfig ShieldConvert { get; set; } = new NormalizedShieldConvertSecondaryConfig(); } internal sealed class RangedWeaponConfig { public bool? Enabled { get; set; } public RangedWeaponConfig? Barrage { get; set; } public RangedWeaponConfig? Volley { get; set; } public RangedWeaponConfig? Piercing { get; set; } public RangedWeaponConfig? Scatter { get; set; } public RangedWeaponConfig? Spiral { get; set; } public RangedWeaponConfig? Sentinel { get; set; } public RangedWeaponConfig? Meteor { get; set; } public RangedWeaponConfig? Burst { get; set; } public RangedWeaponConfig? StickyDetonator { get; set; } public RangedWeaponConfig? OverchargedBomb { get; set; } public string? Animation { get; set; } public string? DetonateAnimation { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? DamageFactor { get; set; } public float? ProjectileSpeedFactor { get; set; } public float? ProjectileScaleFactor { get; set; } public string? Preset { get; set; } public int? Count { get; set; } public float? SpreadAngle { get; set; } public int? AmmoConsumption { get; set; } public float? VolleyRadius { get; set; } public float? VolleyArcAngleMin { get; set; } public float? VolleyArcAngleMax { get; set; } public float? VolleyMaxRange { get; set; } public float? Interval { get; set; } public float? HoldRepeatInterval { get; set; } public float? BarrageSpacing { get; set; } public float? MeteorRadius { get; set; } public float? PierceDamageDecay { get; set; } public float? SplitAngle { get; set; } public int? RicochetBounces { get; set; } public float? RicochetDecay { get; set; } public float? RicochetRoughness { get; set; } public float? SpiralRadius { get; set; } public float? SpiralTurns { get; set; } public float? DetectionRange { get; set; } public float? HoverDistance { get; set; } public float? HoverHeight { get; set; } public float? HoverElevationAngle { get; set; } public float? OrbitRadius { get; set; } public float? OrbitSpeed { get; set; } public float? Lifetime { get; set; } public float? AttackDelay { get; set; } public float? MeteorSpawnHeight { get; set; } public int? MaxCharges { get; set; } public float? AoeRadiusFactor { get; set; } } internal sealed class MeleeWeaponConfig { public bool? Enabled { get; set; } public string Preset { get; set; } = ""; public string CopyFrom { get; set; } = ""; public string Animation { get; set; } = ""; public float ResourceMultiplier { get; set; } = 1f; public float OutputMultiplier { get; set; } = 1f; public float? DurabilityFactor { get; set; } public SneakAmbushConfig? SneakAmbush { get; set; } public CleavingThrustConfig? CleavingThrust { get; set; } public MeleeOnProjectileHitConfig? SpearRain { get; set; } public ImpactBurstConfig? ImpactBurst { get; set; } public BoomerangConfig? Boomerang { get; set; } public SpinningSweepConfig? SpinningSweep { get; set; } public LaunchSlamConfig? LaunchSlam { get; set; } public KnockbackChainConfig? KnockbackChain { get; set; } public AftershockConfig? Aftershock { get; set; } public RiftTrailConfig? RiftTrail { get; set; } public FractureLineConfig? FractureLine { get; set; } public HarvestSweepConfig? HarvestSweep { get; set; } } internal sealed class MeleeOnProjectileHitConfig { public bool? Enabled { get; set; } public string Preset { get; set; } = ""; public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public string? ProjectileSpinAxis { get; set; } public string? ProjectileVisualRotationOffset { get; set; } public string? Vfx { get; set; } public int? Count { get; set; } public float? SpawnHeight { get; set; } public float? SpawnRadius { get; set; } public float? FlightTime { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? Radius { get; set; } public bool? IncludeDirectTarget { get; set; } public bool? IncludeDestructibles { get; set; } public bool? TriggerOnCharactersOnly { get; set; } } internal sealed class ImpactBurstConfig { public bool? Enabled { get; set; } public string Animation { get; set; } = ""; public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public string? ProjectileSpinAxis { get; set; } public string? ProjectileVisualRotationOffset { get; set; } public float? Radius { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } } internal sealed class BoomerangConfig { public bool? Enabled { get; set; } public string Animation { get; set; } = ""; public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public string? ProjectileSpinAxis { get; set; } public string? ProjectileVisualRotationOffset { get; set; } public float? MaxDistance { get; set; } public float? CurveFactor { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? HitDamageDecay { get; set; } } internal sealed class SpinningSweepConfig { public bool? Enabled { get; set; } public string Animation { get; set; } = ""; public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? LoopStart { get; set; } public float? LoopEnd { get; set; } public float? AnimationSpeed { get; set; } public float? MoveSpeedFactor { get; set; } public float? SkillRaiseFactor { get; set; } } internal sealed class SneakAmbushConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? ChargeMaxSeconds { get; set; } public float? ChargeSkillFactor { get; set; } public float? AggroResetRangePerChargeSecond { get; set; } public float? SenseBlockDurationPerChargeSecond { get; set; } public float? BackstabResetSecondsPerChargeSecond { get; set; } } internal sealed class CleavingThrustConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? RangeFactor { get; set; } public float? Angle { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public bool? HitThroughWalls { get; set; } } internal sealed class LaunchSlamConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? LaunchHeight { get; set; } public float? DamageFactor { get; set; } } internal sealed class KnockbackChainConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? PushFactor { get; set; } public float? ChainDecay { get; set; } } internal sealed class AftershockConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public int? Waves { get; set; } public float? Interval { get; set; } public float? WaveDecay { get; set; } public float? ForwardStep { get; set; } public float? DurabilityFactor { get; set; } } internal sealed class RiftTrailConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? Range { get; set; } public float? Angle { get; set; } public float? Width { get; set; } public bool? HitThroughWalls { get; set; } public bool? IncludeDestructibles { get; set; } public float? DurabilityFactor { get; set; } } internal sealed class FractureLineConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? Range { get; set; } public float? HitSpacing { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? DurabilityFactor { get; set; } } internal sealed class HarvestSweepConfig { public bool? Enabled { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public string? Animation { get; set; } public float? LoopStart { get; set; } public float? LoopEnd { get; set; } public float? AnimationSpeed { get; set; } public float? MoveSpeedFactor { get; set; } public float? SkillRaiseFactor { get; set; } } internal sealed class BloodMagicWeaponConfig { public bool? Enabled { get; set; } public string? Preset { get; set; } public BloodMagicWeaponConfig? SummonEmpower { get; set; } public BloodMagicWeaponConfig? ShieldConvert { get; set; } public string? Animation { get; set; } public float? ResourceMultiplier { get; set; } public float? DurabilityFactor { get; set; } public float? Cooldown { get; set; } public float? CooldownReductionFactor { get; set; } public float? Radius { get; set; } public float? Duration { get; set; } public float? MoveSpeedFactor { get; set; } public float? AttackSpeedFactor { get; set; } public float? HealFactor { get; set; } public MagicSummonOverrideConfig? Summon { get; set; } } internal sealed class MagicSummonOverrideConfig { public string QualityPreset { get; set; } = ""; public int? MaxQuality { get; set; } public List? SpawnChoices { get; set; } } internal sealed class MagicSummonCloneConfig { public string SourcePrefab { get; set; } = ""; public string ClonePrefab { get; set; } = ""; public string DisplayName { get; set; } = ""; public float? Health { get; set; } public int? Weight { get; set; } } internal enum SecondaryAttackPreset { Barrage, Volley, Piercing, Scatter, Spiral, Sentinel, Meteor, Burst, StickyDetonator, OverchargedBomb } internal enum MeleeSpecialPreset { None, SneakAmbush, CleavingThrust, SpearRain, ImpactBurst, Boomerang, SpinningSweep, LaunchSlam, KnockbackChain, Aftershock, RiftTrail, FractureLine } internal enum SecondaryAttackBehaviorType { EffectOnly, Projectile, SummonEmpower, ShieldConvert, CopiedSecondary, Aftershock, FractureLine } internal abstract class SecondaryAttackBehavior { public abstract SecondaryAttackBehaviorType BehaviorType { get; } } internal sealed class AftershockSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.Aftershock; public string SourcePrefabName { get; set; } = ""; } internal sealed class FractureLineSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.FractureLine; public string SourcePrefabName { get; set; } = ""; } internal sealed class EffectOnlySecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.EffectOnly; } internal sealed class ProjectileSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.Projectile; public SecondaryAttackPreset Preset { get; set; } public string CooldownGroup { get; set; } = ""; public float Cooldown { get; set; } = 8f; public float CooldownReductionFactor { get; set; } = 0.5f; public float DamageFactor { get; set; } = 1f; public float SkillRaiseFactor { get; set; } = 1f; public float AdrenalineFactor { get; set; } = 1f; public float ProjectileSpeedFactor { get; set; } = 1f; public float ProjectileScaleFactor { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public int ProjectileCount { get; set; } public float SpreadAngle { get; set; } public int AmmoConsumption { get; set; } public float VolleyRadius { get; set; } public float VolleyArcAngleMin { get; set; } public float VolleyArcAngleMax { get; set; } public float VolleyMaxRange { get; set; } public float Interval { get; set; } public float HoldRepeatInterval { get; set; } public float BarrageSpacing { get; set; } public float MeteorRadius { get; set; } public float PierceDamageDecay { get; set; } = 0.25f; public float SplitAngle { get; set; } public int RicochetBounces { get; set; } public float RicochetDecay { get; set; } public float RicochetRoughness { get; set; } public float SpiralRadius { get; set; } public float SpiralTurns { get; set; } public float SentinelDetectionRange { get; set; } public float SentinelHoverDistance { get; set; } public float SentinelHoverHeight { get; set; } public float SentinelHoverElevationAngle { get; set; } public float SentinelOrbitRadius { get; set; } public float SentinelOrbitSpeed { get; set; } public float SentinelLifetime { get; set; } public float SentinelAttackDelay { get; set; } public float MeteorSpawnHeight { get; set; } public int MaxCharges { get; set; } = 6; public string DetonateAnimation { get; set; } = ""; public float AoeRadiusFactor { get; set; } = 1f; } internal sealed class SummonEmpowerSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.SummonEmpower; public List SummonSourcePrefabs { get; set; } = new List(); public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { CooldownSkill = "bloodMagic" }; public float Radius { get; set; } public float Duration { get; set; } public float MoveSpeedFactor { get; set; } = 1f; public float AttackSpeedFactor { get; set; } = 1f; } internal sealed class ShieldConvertSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.ShieldConvert; public int ShieldStatusEffectHash { get; set; } public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { CooldownSkill = "bloodMagic" }; public float Radius { get; set; } public float HealFactor { get; set; } } internal sealed class CopiedSecondaryBehavior : SecondaryAttackBehavior { public override SecondaryAttackBehaviorType BehaviorType => SecondaryAttackBehaviorType.CopiedSecondary; public string SourcePrefabName { get; set; } = ""; } internal sealed class SecondaryAttackDefinition { public string PrefabName { get; set; } = ""; public bool AppliesSecondaryOverride { get; set; } public SecondaryAttackBehavior Behavior { get; set; } = new EffectOnlySecondaryBehavior(); public SecondaryAttackBehaviorType BehaviorType => Behavior.BehaviorType; public string AttackAnimation { get; set; } = ""; public bool HasCustomAttackAnimation { get; set; } public Attack? CooldownFallbackSecondaryAttack { get; set; } public Attack? ConfiguredSecondaryAttack { get; set; } public float ResourceMultiplier { get; set; } = 1f; public float OutputMultiplier { get; set; } = 1f; public float DurabilityFactor { get; set; } = 1f; public float RawAttackHealth { get; set; } public float RawAttackHealthPercentage { get; set; } public float RawAttackStamina { get; set; } public float RawAttackEitr { get; set; } public float RawDrawStamina { get; set; } public float RawDrawEitr { get; set; } public float RawReloadStamina { get; set; } public float RawReloadEitr { get; set; } public SneakAmbushDefinition? SneakAmbush { get; set; } public CleavingThrustDefinition? CleavingThrust { get; set; } public LaunchSlamDefinition? LaunchSlam { get; set; } public KnockbackChainDefinition? KnockbackChain { get; set; } public AftershockDefinition? Aftershock { get; set; } public RiftTrailDefinition? RiftTrail { get; set; } public FractureLineDefinition? FractureLine { get; set; } public HarvestSweepDefinition? HarvestSweep { get; set; } public MeleeOnProjectileHitDefinition? OnProjectileHit { get; set; } public BoomerangDefinition? Boomerang { get; set; } public SpinningSweepDefinition? SpinningSweep { get; set; } } internal sealed class MeleeOnProjectileHitDefinition { public string Preset { get; set; } = ""; public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float DurabilityFactor { get; set; } = 1f; public string ProjectileSpinAxis { get; set; } = "none"; public Vector3 ProjectileVisualRotationOffset { get; set; } = Vector3.zero; public string Vfx { get; set; } = ""; public int Count { get; set; } public float SpawnHeight { get; set; } public float SpawnRadius { get; set; } public float FlightTime { get; set; } public float DamageFactor { get; set; } public float PushFactor { get; set; } public float Radius { get; set; } public bool IncludeDirectTarget { get; set; } public bool IncludeDestructibles { get; set; } public bool TriggerOnCharactersOnly { get; set; } = true; } internal sealed class BoomerangDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { Cooldown = 6f, CooldownSkill = "weapon", CooldownReductionFactor = 0.5f }; public string Side { get; set; } = "right"; public string ProjectileSpinAxis { get; set; } = "horizontal"; public Vector3 ProjectileVisualRotationOffset { get; set; } = Vector3.zero; public float MaxDistance { get; set; } = 20f; public float CurveFactor { get; set; } = 0.5f; public float DespawnDistance { get; set; } = 0.8f; public float CatchRadius { get; set; } = 1.2f; public float CatchDelay { get; set; } = 0.25f; public bool AutoEquipOnCatch { get; set; } = true; public float DamageFactor { get; set; } = 1f; public float PushFactor { get; set; } = 1f; public float HitDamageDecay { get; set; } = 0.2f; public bool IncludeDestructibles { get; set; } } internal sealed class SneakAmbushDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { Cooldown = 30f, CooldownSkill = "weapon", CooldownReductionFactor = 0.5f }; public float ChargeMaxSeconds { get; set; } = 8f; public float ChargeSkillFactor { get; set; } = 2f; public float AggroResetRangePerChargeSecond { get; set; } = 1f; public float SenseBlockDurationPerChargeSecond { get; set; } = 0.25f; public float BackstabResetSecondsPerChargeSecond { get; set; } = 35f; public float DurabilityFactor { get; set; } = 1f; } internal sealed class SpinningSweepDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition { Cooldown = 8f, CooldownSkill = "weapon", CooldownReductionFactor = 0.5f }; public float DurabilityFactor { get; set; } = 1f; public float DamageFactor { get; set; } = 0.75f; public float PushFactor { get; set; } = 0.5f; public float LoopStart { get; set; } = 0.4f; public float LoopEnd { get; set; } = 0.6f; public float AnimationSpeed { get; set; } = 1f; public float MoveSpeedFactor { get; set; } = 0.75f; public float SkillRaiseFactor { get; set; } = 0.25f; } internal sealed class CleavingThrustDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float DurabilityFactor { get; set; } = 1f; public float RangeFactor { get; set; } = 3f; public float Angle { get; set; } = 90f; public float DamageFactor { get; set; } = 1f; public float PushFactor { get; set; } = 6f; } internal sealed class LaunchSlamDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float DurabilityFactor { get; set; } = 1f; public float LaunchHeight { get; set; } = 4f; public float DamageFactor { get; set; } = 1f; } internal sealed class KnockbackChainDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float DurabilityFactor { get; set; } = 1f; public float PushFactor { get; set; } = 8f; public float ChainDecay { get; set; } = 0.75f; } internal sealed class AftershockDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public int Waves { get; set; } = 3; public float Interval { get; set; } = 0.5f; public float WaveDecay { get; set; } = 0.2f; public float ForwardStep { get; set; } = 3f; public float DurabilityFactor { get; set; } = 1f; } internal sealed class RiftTrailDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float Duration { get; set; } = 2f; public float TickInterval { get; set; } = 0.5f; public float DamageFactor { get; set; } = 0.25f; public float PushFactor { get; set; } public float Range { get; set; } public float Angle { get; set; } public float Width { get; set; } public float DurabilityFactor { get; set; } = 1f; public float VisualScaleFactor { get; set; } = 1.5f; public float VisualForwardOffset { get; set; } = 1.5f; public string VisualTint { get; set; } = "#ffffff"; public float VisualAlphaFactor { get; set; } = 1f; } internal sealed class FractureLineDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float Range { get; set; } = 5f; public float HitSpacing { get; set; } = 0.75f; public float Duration { get; set; } = 1.2f; public float TickInterval { get; set; } = 0.3f; public float DamageFactor { get; set; } = 0.35f; public float DurabilityFactor { get; set; } = 1f; } internal sealed class MeleePresetCooldownDefinition { public float Cooldown { get; set; } public string CooldownGroup { get; set; } = ""; public string CooldownSkill { get; set; } = "weapon"; public float CooldownReductionFactor { get; set; } = 0.5f; } internal static class ProjectilePresetCooldownPolicy { internal static bool UsesDynamicOriginalSecondary(SecondaryAttackDefinition? definition) { if (definition == null || !definition.AppliesSecondaryOverride || !(definition.Behavior is CopiedSecondaryBehavior)) { return false; } if (definition.Boomerang == null) { return definition.OnProjectileHit != null; } return true; } } internal sealed class HarvestSweepDefinition { public MeleePresetCooldownDefinition PresetCooldown { get; set; } = new MeleePresetCooldownDefinition(); public float DurabilityFactor { get; set; } = 1f; public float LoopStart { get; set; } = 0.4f; public float LoopEnd { get; set; } = 0.6f; public float AnimationSpeed { get; set; } = 1f; public float MoveSpeedFactor { get; set; } = 0.75f; public float SkillRaiseFactor { get; set; } = 0.25f; } internal static class SecondaryAttackDefaultYamlResources { private const string ResourcePrefix = "SecondaryAttacks.Resources.Defaults."; internal static string Load(string fileName) { string text = "SecondaryAttacks.Resources.Defaults." + fileName; using Stream stream = typeof(SecondaryAttackDefaultYamlResources).Assembly.GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Embedded default YAML resource '" + text + "' was not found."); } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); return streamReader.ReadToEnd(); } } internal static class SecondaryAttackManager { internal readonly struct ReloadStateConsumptionScope { internal Humanoid? Character { get; } internal ItemData? PreviousWeapon { get; } internal bool HadPreviousWeapon { get; } internal bool Active { get; } internal ReloadStateConsumptionScope(Humanoid character, ItemData? previousWeapon, bool hadPreviousWeapon) { Character = character; PreviousWeapon = previousWeapon; HadPreviousWeapon = hadPreviousWeapon; Active = true; } } internal readonly struct SecondaryAttackDurabilityAdjustmentState { internal static readonly SecondaryAttackDurabilityAdjustmentState Empty = new SecondaryAttackDurabilityAdjustmentState(null, 0f, 1f); internal ItemData? Weapon { get; } internal float BeforeDurability { get; } internal float Factor { get; } internal bool Applies => Weapon != null; internal SecondaryAttackDurabilityAdjustmentState(ItemData? weapon, float beforeDurability, float factor) { Weapon = weapon; BeforeDurability = beforeDurability; Factor = factor; } } private sealed class BowSecondaryState { public string PrefabName { get; set; } = ""; public bool PendingSecondary { get; set; } } private sealed class RuntimeWeaponDefinitionState { public int AppliedWorldRevision { get; set; } = -1; } internal sealed class ReloadSecondaryResourceCostContext { public static readonly ReloadSecondaryResourceCostContext Empty = new ReloadSecondaryResourceCostContext(0f, 0f); public float StaminaDelta { get; } public float EitrDelta { get; } public bool HasDelta { get { if (!(Math.Abs(StaminaDelta) > 0.001f)) { return Math.Abs(EitrDelta) > 0.001f; } return true; } } public ReloadSecondaryResourceCostContext(float staminaDelta, float eitrDelta) { StaminaDelta = staminaDelta; EitrDelta = eitrDelta; } } private static readonly Dictionary ReloadConsumptionWeapons = new Dictionary(); private static readonly MethodInfo MemberwiseCloneMethod = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null); private const string StaffRapidFireAnimation = "staff_rapidfire"; private static readonly ConditionalWeakTable BowSecondaryStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable RuntimeWeaponDefinitionStates = new ConditionalWeakTable(); private static readonly SortedSet PlayerAnimatorTriggers = new SortedSet(StringComparer.Ordinal); private static bool _animatorDumpWritten; private static bool _customAnimationDumpWritten; private static float GetNormalizedResourceMultiplier(NormalizedWeaponConfig weaponConfig) { NormalizedHarvestSweepConfig? harvestSweep = weaponConfig.HarvestSweep; if (harvestSweep != null && harvestSweep.Enabled) { return weaponConfig.HarvestSweep.ResourceMultiplier; } return weaponConfig.Secondary?.ResourceMultiplier ?? 1f; } private static float GetSelectedMeleeResourceMultiplier(NormalizedWeaponConfig weaponConfig) { float normalizedResourceMultiplier = GetNormalizedResourceMultiplier(weaponConfig); return weaponConfig.MeleePreset switch { MeleeSpecialPreset.SneakAmbush => weaponConfig.SneakAmbush?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.CleavingThrust => weaponConfig.CleavingThrust?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.SpearRain => weaponConfig.SpearRain?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.ImpactBurst => weaponConfig.ImpactBurst?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.Boomerang => weaponConfig.Boomerang?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.SpinningSweep => weaponConfig.SpinningSweep?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.LaunchSlam => weaponConfig.LaunchSlam?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.KnockbackChain => weaponConfig.KnockbackChain?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.Aftershock => weaponConfig.Aftershock?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.RiftTrail => weaponConfig.RiftTrail?.ResourceMultiplier ?? 1f, MeleeSpecialPreset.FractureLine => weaponConfig.FractureLine?.ResourceMultiplier ?? 1f, _ => normalizedResourceMultiplier, }; } private static float GetNormalizedOutputMultiplier(NormalizedWeaponConfig weaponConfig) { return weaponConfig.Secondary?.OutputMultiplier ?? 1f; } private static float GetNormalizedDurabilityFactor(NormalizedWeaponConfig weaponConfig) { return weaponConfig.Secondary?.DurabilityFactor ?? 1f; } private static float GetSelectedMeleeDurabilityFactor(NormalizedWeaponConfig weaponConfig) { float normalizedDurabilityFactor = GetNormalizedDurabilityFactor(weaponConfig); return weaponConfig.MeleePreset switch { MeleeSpecialPreset.SneakAmbush => weaponConfig.SneakAmbush?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.CleavingThrust => weaponConfig.CleavingThrust?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.SpearRain => weaponConfig.SpearRain?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.ImpactBurst => weaponConfig.ImpactBurst?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.Boomerang => weaponConfig.Boomerang?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.SpinningSweep => weaponConfig.SpinningSweep?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.LaunchSlam => weaponConfig.LaunchSlam?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.KnockbackChain => weaponConfig.KnockbackChain?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.Aftershock => weaponConfig.Aftershock?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.RiftTrail => weaponConfig.RiftTrail?.DurabilityFactor ?? normalizedDurabilityFactor, MeleeSpecialPreset.FractureLine => weaponConfig.FractureLine?.DurabilityFactor ?? normalizedDurabilityFactor, _ => normalizedDurabilityFactor, }; } private static string GetNormalizedAttackAnimation(NormalizedWeaponConfig weaponConfig) { string text = weaponConfig.Secondary?.Animation; if (string.IsNullOrWhiteSpace(text)) { return ""; } return text.Trim(); } private static MeleePresetCooldownDefinition CreatePresetCooldown(float cooldown, float cooldownReductionFactor, string cooldownSkill = "weapon") { return new MeleePresetCooldownDefinition { Cooldown = Mathf.Max(0f, cooldown), CooldownSkill = (string.IsNullOrWhiteSpace(cooldownSkill) ? "weapon" : cooldownSkill.Trim()), CooldownReductionFactor = Mathf.Clamp01(cooldownReductionFactor) }; } private static SneakAmbushDefinition? CreateSneakAmbushDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedSneakAmbushConfig sneakAmbush = weaponConfig.SneakAmbush; if (sneakAmbush == null || !sneakAmbush.Enabled) { return null; } return new SneakAmbushDefinition { PresetCooldown = CreatePresetCooldown(sneakAmbush.Cooldown, sneakAmbush.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, sneakAmbush.DurabilityFactor), ChargeMaxSeconds = Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds), ChargeSkillFactor = Mathf.Max(0f, sneakAmbush.ChargeSkillFactor), AggroResetRangePerChargeSecond = sneakAmbush.AggroResetRangePerChargeSecond, SenseBlockDurationPerChargeSecond = sneakAmbush.SenseBlockDurationPerChargeSecond, BackstabResetSecondsPerChargeSecond = sneakAmbush.BackstabResetSecondsPerChargeSecond }; } private static CleavingThrustDefinition? CreateCleavingThrustDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedCleavingThrustConfig cleavingThrust = weaponConfig.CleavingThrust; if (cleavingThrust == null || !cleavingThrust.Enabled) { return null; } return new CleavingThrustDefinition { PresetCooldown = CreatePresetCooldown(cleavingThrust.Cooldown, cleavingThrust.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, cleavingThrust.DurabilityFactor), RangeFactor = Mathf.Max(0.1f, cleavingThrust.RangeFactor), Angle = Mathf.Clamp(cleavingThrust.Angle, 1f, 360f), DamageFactor = Mathf.Max(0f, cleavingThrust.DamageFactor), PushFactor = Mathf.Max(0f, cleavingThrust.PushFactor) }; } private static LaunchSlamDefinition? CreateLaunchSlamDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedLaunchSlamConfig launchSlam = weaponConfig.LaunchSlam; if (launchSlam == null || !launchSlam.Enabled) { return null; } return new LaunchSlamDefinition { PresetCooldown = CreatePresetCooldown(launchSlam.Cooldown, launchSlam.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, launchSlam.DurabilityFactor), LaunchHeight = Mathf.Max(0f, launchSlam.LaunchHeight), DamageFactor = Mathf.Max(0f, launchSlam.DamageFactor) }; } private static KnockbackChainDefinition? CreateKnockbackChainDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedKnockbackChainConfig knockbackChain = weaponConfig.KnockbackChain; if (knockbackChain == null || !knockbackChain.Enabled) { return null; } return new KnockbackChainDefinition { PresetCooldown = CreatePresetCooldown(knockbackChain.Cooldown, knockbackChain.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, knockbackChain.DurabilityFactor), PushFactor = Mathf.Max(0f, knockbackChain.PushFactor), ChainDecay = Mathf.Clamp01(knockbackChain.ChainDecay) }; } private static AftershockDefinition? CreateAftershockDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedAftershockConfig aftershock = weaponConfig.Aftershock; if (aftershock == null || !aftershock.Enabled) { return null; } return new AftershockDefinition { PresetCooldown = CreatePresetCooldown(aftershock.Cooldown, aftershock.CooldownReductionFactor), Waves = Mathf.Max(0, aftershock.Waves), Interval = Mathf.Max(0f, aftershock.Interval), WaveDecay = Mathf.Clamp01(aftershock.WaveDecay), ForwardStep = aftershock.ForwardStep, DurabilityFactor = Mathf.Max(0f, aftershock.DurabilityFactor) }; } private static RiftTrailDefinition? CreateRiftTrailDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedRiftTrailConfig riftTrail = weaponConfig.RiftTrail; if (riftTrail == null || !riftTrail.Enabled) { return null; } return new RiftTrailDefinition { PresetCooldown = CreatePresetCooldown(riftTrail.Cooldown, riftTrail.CooldownReductionFactor), Duration = Mathf.Max(0.01f, riftTrail.Duration), TickInterval = Mathf.Max(0.01f, riftTrail.TickInterval), DamageFactor = Mathf.Max(0f, riftTrail.DamageFactor), PushFactor = Mathf.Max(0f, riftTrail.PushFactor), Range = Mathf.Max(0f, riftTrail.Range), Angle = ((riftTrail.Angle <= 0f) ? 0f : Mathf.Clamp(riftTrail.Angle, 1f, 360f)), Width = Mathf.Max(0f, riftTrail.Width), DurabilityFactor = Mathf.Max(0f, riftTrail.DurabilityFactor), VisualScaleFactor = Mathf.Max(0f, riftTrail.VisualScaleFactor), VisualForwardOffset = riftTrail.VisualForwardOffset, VisualTint = riftTrail.VisualTint, VisualAlphaFactor = Mathf.Max(0f, riftTrail.VisualAlphaFactor) }; } private static FractureLineDefinition? CreateFractureLineDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedFractureLineConfig fractureLine = weaponConfig.FractureLine; if (fractureLine == null || !fractureLine.Enabled) { return null; } return new FractureLineDefinition { PresetCooldown = CreatePresetCooldown(fractureLine.Cooldown, fractureLine.CooldownReductionFactor, "Pickaxes"), Range = Mathf.Max(0.1f, fractureLine.Range), HitSpacing = Mathf.Max(0.1f, fractureLine.HitSpacing), Duration = Mathf.Max(0.01f, fractureLine.Duration), TickInterval = Mathf.Max(0.01f, fractureLine.TickInterval), DamageFactor = Mathf.Max(0f, fractureLine.DamageFactor), DurabilityFactor = Mathf.Max(0f, fractureLine.DurabilityFactor) }; } private static HarvestSweepDefinition? CreateHarvestSweepDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedHarvestSweepConfig harvestSweep = weaponConfig.HarvestSweep; if (harvestSweep == null || !harvestSweep.Enabled) { return null; } float num = Mathf.Clamp(harvestSweep.LoopStart, 0f, 0.93f); float loopEnd = Mathf.Clamp(harvestSweep.LoopEnd, num + 0.05f, 0.98f); return new HarvestSweepDefinition { PresetCooldown = CreatePresetCooldown(harvestSweep.Cooldown, harvestSweep.CooldownReductionFactor, "Farming"), DurabilityFactor = Mathf.Max(0f, harvestSweep.DurabilityFactor), LoopStart = num, LoopEnd = loopEnd, AnimationSpeed = Mathf.Clamp(harvestSweep.AnimationSpeed, 0.1f, 5f), MoveSpeedFactor = Mathf.Max(0f, harvestSweep.MoveSpeedFactor), SkillRaiseFactor = Mathf.Max(0f, harvestSweep.SkillRaiseFactor) }; } private static BoomerangDefinition? CreateBoomerangDefinition(NormalizedWeaponConfig weaponConfig) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) NormalizedBoomerangConfig boomerang = weaponConfig.Boomerang; if (boomerang == null || !boomerang.Enabled) { return null; } return new BoomerangDefinition { PresetCooldown = CreatePresetCooldown(boomerang.Cooldown, boomerang.CooldownReductionFactor), Side = "right", ProjectileSpinAxis = boomerang.ProjectileSpinAxis, ProjectileVisualRotationOffset = boomerang.ProjectileVisualRotationOffset, MaxDistance = Mathf.Max(0.5f, boomerang.MaxDistance), CurveFactor = Mathf.Max(0f, boomerang.CurveFactor), DespawnDistance = 0.8f, CatchRadius = 1.2f, CatchDelay = 0.25f, AutoEquipOnCatch = true, DamageFactor = Mathf.Max(0f, boomerang.DamageFactor), PushFactor = Mathf.Max(0f, boomerang.PushFactor), HitDamageDecay = Mathf.Clamp01(boomerang.HitDamageDecay), IncludeDestructibles = boomerang.IncludeDestructibles }; } private static SpinningSweepDefinition? CreateSpinningSweepDefinition(NormalizedWeaponConfig weaponConfig) { NormalizedSpinningSweepConfig spinningSweep = weaponConfig.SpinningSweep; if (spinningSweep == null || !spinningSweep.Enabled) { return null; } float num = Mathf.Clamp(spinningSweep.LoopStart, 0f, 0.93f); float loopEnd = Mathf.Clamp(spinningSweep.LoopEnd, num + 0.05f, 0.98f); return new SpinningSweepDefinition { PresetCooldown = CreatePresetCooldown(spinningSweep.Cooldown, spinningSweep.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, spinningSweep.DurabilityFactor), DamageFactor = Mathf.Max(0f, spinningSweep.DamageFactor), PushFactor = Mathf.Max(0f, spinningSweep.PushFactor), LoopStart = num, LoopEnd = loopEnd, AnimationSpeed = Mathf.Clamp(spinningSweep.AnimationSpeed, 0.1f, 5f), MoveSpeedFactor = Mathf.Max(0f, spinningSweep.MoveSpeedFactor), SkillRaiseFactor = Mathf.Max(0f, spinningSweep.SkillRaiseFactor) }; } private static void ApplyCoreDefinitionFeatures(SecondaryAttackDefinition definition, NormalizedWeaponConfig weaponConfig) { definition.SneakAmbush = CreateSneakAmbushDefinition(weaponConfig); definition.CleavingThrust = CreateCleavingThrustDefinition(weaponConfig); definition.LaunchSlam = CreateLaunchSlamDefinition(weaponConfig); definition.KnockbackChain = CreateKnockbackChainDefinition(weaponConfig); definition.Aftershock = CreateAftershockDefinition(weaponConfig); } private static void ApplyExtendedDefinitionFeatures(SecondaryAttackDefinition definition, NormalizedWeaponConfig weaponConfig) { ApplyCoreDefinitionFeatures(definition, weaponConfig); definition.RiftTrail = CreateRiftTrailDefinition(weaponConfig); definition.FractureLine = CreateFractureLineDefinition(weaponConfig); definition.HarvestSweep = CreateHarvestSweepDefinition(weaponConfig); definition.Boomerang = CreateBoomerangDefinition(weaponConfig); definition.SpinningSweep = CreateSpinningSweepDefinition(weaponConfig); } private static void ApplyProjectileDefinitionFeatures(SecondaryAttackDefinition definition, string prefabName, NormalizedWeaponConfig weaponConfig) { ApplyExtendedDefinitionFeatures(definition, weaponConfig); definition.OnProjectileHit = CreateMeleeOnProjectileHitDefinition(prefabName, weaponConfig.Secondary?.OnProjectileHit); } private static int ResolveAmmoConsumption(int configuredValue, bool usesAmmo, SecondaryAttackPreset preset) { switch (preset) { case SecondaryAttackPreset.OverchargedBomb: if (configuredValue >= 0) { return Mathf.Max(1, configuredValue); } return 1; case SecondaryAttackPreset.StickyDetonator: return 0; default: if (!usesAmmo) { return 0; } if (configuredValue >= 0) { return Mathf.Max(0, configuredValue); } return 1; } } internal static SecondaryAttackDefinition CreateEffectOnlyDefinition(string prefabName, NormalizedWeaponConfig weaponConfig) { SecondaryAttackDefinition obj = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = false, Behavior = new EffectOnlySecondaryBehavior(), AttackAnimation = "", HasCustomAttackAnimation = false, ResourceMultiplier = Mathf.Max(0f, GetNormalizedResourceMultiplier(weaponConfig)), OutputMultiplier = Mathf.Max(0f, GetNormalizedOutputMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetSelectedMeleeDurabilityFactor(weaponConfig)) }; ApplyExtendedDefinitionFeatures(obj, weaponConfig); return obj; } internal static int GetRuntimeWeaponAppliedWorldRevision(ItemData weapon) { return RuntimeWeaponDefinitionStates.GetValue(weapon, (ItemData _) => new RuntimeWeaponDefinitionState()).AppliedWorldRevision; } internal static void SetRuntimeWeaponAppliedWorldRevision(ItemData weapon, int applyRevision) { RuntimeWeaponDefinitionStates.GetValue(weapon, (ItemData _) => new RuntimeWeaponDefinitionState()).AppliedWorldRevision = applyRevision; } internal static void TryRestorePersistedReloadedWeaponState(Player player, ItemData weapon) { if (!((Object)(object)player == (Object)null) && weapon != null && IsReloadableWeapon(weapon) && player.m_weaponLoaded != weapon && weapon.m_customData.TryGetValue("SecondaryAttacks.ReloadLoaded", out var value) && !(value != "1")) { player.SetWeaponLoaded(weapon); } } internal static void OnWeaponLoadedStateChanged(Player player, ItemData? previousWeapon, ItemData? newWeapon) { if ((Object)(object)player == (Object)null) { return; } if (newWeapon != null) { SetPersistedReloadState(player, newWeapon, loaded: true); } else if (previousWeapon != null) { if (IsReloadStateConsumptionActive((Humanoid)(object)player, previousWeapon)) { SetPersistedReloadState(player, previousWeapon, loaded: false); } else { SetPersistedReloadState(player, previousWeapon, loaded: true); } } } internal static void ConsumePersistedReloadedWeaponState(Player player, ItemData weapon) { SetPersistedReloadState(player, weapon, loaded: false); } internal static ReloadStateConsumptionScope BeginReloadStateConsumption(Attack attack) { if ((Object)(object)attack?.m_character == (Object)null || attack.m_weapon == null || !attack.m_requiresReload) { return default(ReloadStateConsumptionScope); } Humanoid character = attack.m_character; ItemData value; bool hadPreviousWeapon = ReloadConsumptionWeapons.TryGetValue(character, out value); ReloadConsumptionWeapons[character] = attack.m_weapon; return new ReloadStateConsumptionScope(character, value, hadPreviousWeapon); } internal static void EndReloadStateConsumption(ref ReloadStateConsumptionScope scope) { if (!scope.Active) { scope = default(ReloadStateConsumptionScope); return; } Humanoid character = scope.Character; if (character != null) { if (scope.HadPreviousWeapon && scope.PreviousWeapon != null) { ReloadConsumptionWeapons[character] = scope.PreviousWeapon; } else { ReloadConsumptionWeapons.Remove(character); } } scope = default(ReloadStateConsumptionScope); } private static bool IsReloadableWeapon(ItemData weapon) { if (weapon == null) { return false; } return weapon.m_shared?.m_attack?.m_requiresReload == true; } private static bool IsReloadStateConsumptionActive(Humanoid humanoid, ItemData weapon) { if (ReloadConsumptionWeapons.TryGetValue(humanoid, out ItemData value)) { return value == weapon; } return false; } private static void SetPersistedReloadState(Player player, ItemData weapon, bool loaded) { if (!IsReloadableWeapon(weapon)) { return; } bool flag; if (loaded) { flag = !weapon.m_customData.TryGetValue("SecondaryAttacks.ReloadLoaded", out var value) || value != "1"; weapon.m_customData["SecondaryAttacks.ReloadLoaded"] = "1"; } else { flag = weapon.m_customData.Remove("SecondaryAttacks.ReloadLoaded"); } if (flag) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { inventory.Changed(); } } } internal static Attack CloneAttack(Attack? sourceAttack) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown if (sourceAttack == null) { return new Attack(); } return (Attack)MemberwiseCloneMethod.Invoke(sourceAttack, Array.Empty()); } internal static void NormalizeCopiedProjectileAim(Attack? attack, SecondaryAttackDefinition? definition) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 if (attack != null && definition != null && definition.Behavior is CopiedSecondaryBehavior && (definition.OnProjectileHit != null || definition.Boomerang != null) && ((int)attack.m_attackType == 2 || !((Object)(object)attack.m_attackProjectile == (Object)null))) { attack.m_useCharacterFacing = false; attack.m_useCharacterFacingYAim = false; } } internal static bool HasCharacterAuthority(Character? character) { if (TryGetCharacterZdo(character, out ZNetView nview, out ZDO _)) { return nview.IsOwner(); } return false; } internal static bool TryGetCharacterZdo(Character? character, out ZNetView? nview, out ZDO? zdo) { nview = (((Object)(object)character != (Object)null) ? ((Component)character).GetComponent() : null); zdo = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); if ((Object)(object)nview != (Object)null && nview.IsValid()) { return zdo != null; } return false; } internal static double GetNetworkTimeSeconds() { if (!((Object)(object)ZNet.instance != (Object)null)) { return Time.timeAsDouble; } return ZNet.instance.GetTimeSeconds(); } internal static bool TryGetShieldRemaining(Character character, int preferredStatusEffectHash, out SE_Shield? shield, out float remaining, out float remainingTime) { shield = null; remaining = 0f; remainingTime = 0f; SEMan val = ((character != null) ? character.GetSEMan() : null); if (val == null) { return false; } if (preferredStatusEffectHash != 0) { StatusEffect statusEffect = val.GetStatusEffect(preferredStatusEffectHash); SE_Shield val2 = (SE_Shield)(object)((statusEffect is SE_Shield) ? statusEffect : null); if (val2 != null && ShieldAccess.TryReadRemaining(val2, out remaining, out remainingTime)) { shield = val2; return true; } } foreach (StatusEffect statusEffect2 in val.GetStatusEffects()) { SE_Shield val3 = (SE_Shield)(object)((statusEffect2 is SE_Shield) ? statusEffect2 : null); if (val3 != null && ShieldAccess.TryReadRemaining(val3, out remaining, out remainingTime)) { shield = val3; return true; } } return false; } internal static void PlayTriggeredAttackEffects(Attack attack, float durabilityFactor) { //IL_0024: 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_004d: 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) DrainAttackDurability(attack, durabilityFactor); Transform transform = ((Component)attack.m_character).transform; attack.m_weapon.m_shared.m_triggerEffect.Create(transform.position, ((Component)attack.m_character).transform.rotation, transform, 1f, -1); attack.m_triggerEffect.Create(transform.position, ((Component)attack.m_character).transform.rotation, transform, 1f, -1); ((Character)attack.m_character).AddNoise(attack.m_attackHitNoise); } internal static void DrainAttackDurability(Attack attack, float durabilityFactor) { if (attack?.m_weapon != null && !((Object)(object)attack.m_character == (Object)null)) { DrainItemDurability((Character)(object)attack.m_character, attack.m_weapon, durabilityFactor); } } internal static void DrainItemDurability(Character character, ItemData weapon, float durabilityFactor) { if (!((Object)(object)character == (Object)null) && weapon?.m_shared != null && weapon.m_shared.m_useDurability && character.IsPlayer()) { float num = GetItemDurabilityDrain(weapon) * Mathf.Max(0f, durabilityFactor); if (!(num <= 0f)) { weapon.m_durability = Mathf.Max(0f, weapon.m_durability - num); } } } internal static float GetItemDurabilityDrain(ItemData weapon) { float valueOrDefault = (weapon?.m_shared?.m_useDurabilityDrain).GetValueOrDefault(); if (!(valueOrDefault > 0f)) { return 1f; } return valueOrDefault; } internal static SecondaryAttackDurabilityAdjustmentState BeginSecondaryAttackDurabilityAdjustment(Attack attack) { if (attack?.m_weapon?.m_shared == null || (Object)(object)attack.m_character == (Object)null || !attack.m_weapon.m_shared.m_useDurability || !((Character)attack.m_character).IsPlayer() || !SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) || activeAttack == null) { return SecondaryAttackDurabilityAdjustmentState.Empty; } float num = Mathf.Max(0f, activeAttack.Definition.DurabilityFactor); if (Mathf.Approximately(num, 1f)) { return SecondaryAttackDurabilityAdjustmentState.Empty; } return new SecondaryAttackDurabilityAdjustmentState(attack.m_weapon, attack.m_weapon.m_durability, num); } internal static void EndSecondaryAttackDurabilityAdjustment(SecondaryAttackDurabilityAdjustmentState state) { if (state.Applies && state.Weapon?.m_shared != null) { float num = state.BeforeDurability - state.Weapon.m_durability; if (!(num <= 0.001f)) { float num2 = num * state.Factor; state.Weapon.m_durability = Mathf.Clamp(state.BeforeDurability - num2, 0f, Mathf.Max(state.Weapon.m_shared.m_maxDurability, state.BeforeDurability)); } } } internal static void EndSecondaryAttackDurabilityAdjustment(ref SecondaryAttackDurabilityAdjustmentState state) { EndSecondaryAttackDurabilityAdjustment(state); state = SecondaryAttackDurabilityAdjustmentState.Empty; } internal static void EnsureRuntimeWeaponDefinitionApplied(ItemData? weapon) { SecondaryAttackRuntimeWeaponRebind.Apply(weapon, SecondaryAttackFacade.CurrentAppliedWorldSnapshot); } internal static void RefreshLocalPlayerRuntimeWeaponDefinitions() { SecondaryAttackRuntimeWeaponRebind.RefreshLocalPlayerInventory(SecondaryAttackFacade.CurrentAppliedWorldSnapshot); } internal static Vector3 ResolveSafeClosestPoint(Collider collider, Vector3 origin) { //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_0013: 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) MeshCollider val = (MeshCollider)(object)((collider is MeshCollider) ? collider : null); if (val != null && !val.convex) { return ((Collider)val).ClosestPointOnBounds(origin); } return collider.ClosestPoint(origin); } internal static bool TryCreateCustomPayloadDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; NormalizedProjectileSecondaryConfig normalizedProjectileSecondaryConfig = weaponConfig.Secondary?.Projectile ?? new NormalizedProjectileSecondaryConfig(); if (!TryParsePreset(normalizedProjectileSecondaryConfig.Preset, out var preset)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": unknown projectile preset '" + normalizedProjectileSecondaryConfig.Preset + "'.")); return false; } bool usesAmmo = !string.IsNullOrWhiteSpace(sharedData.m_ammoType); if (!ProjectileRuntimeSystem.TryValidateConfiguredPayload(prefabName, primaryAttack, preset, usesAmmo, out string reason)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": " + reason)); return false; } int projectileCount = Mathf.Max(1, normalizedProjectileSecondaryConfig.Count); int num = ResolveAmmoConsumption(normalizedProjectileSecondaryConfig.AmmoConsumption, usesAmmo, preset); string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : primaryAttack.m_attackAnimation.Trim()); bool flag2 = IsBombProjectilePreset(preset); float resourceMultiplier = (flag2 ? 1f : Mathf.Max(0f, GetNormalizedResourceMultiplier(weaponConfig))); float durabilityFactor = (flag2 ? 1f : Mathf.Max(0f, normalizedProjectileSecondaryConfig.DurabilityFactor)); float spreadAngle = (flag2 ? 0f : Mathf.Max(0f, normalizedProjectileSecondaryConfig.SpreadAngle)); float num2 = ResolveProjectileRewardFactor(preset, projectileCount); float num3 = Mathf.Clamp(normalizedProjectileSecondaryConfig.VolleyArcAngleMin, 1f, 89f); float num4 = Mathf.Clamp(normalizedProjectileSecondaryConfig.VolleyArcAngleMax, 1f, 89f); if (num4 < num3) { float num5 = num4; num4 = num3; num3 = num5; } float num6 = normalizedProjectileSecondaryConfig.Interval; if (preset != SecondaryAttackPreset.Barrage && preset != SecondaryAttackPreset.Volley && preset != SecondaryAttackPreset.Meteor && preset != SecondaryAttackPreset.Spiral && num6 <= 0f) { num6 = 0.12f; } SecondaryAttackDefinition secondaryAttackDefinition = new SecondaryAttackDefinition(); secondaryAttackDefinition.PrefabName = prefabName; secondaryAttackDefinition.AppliesSecondaryOverride = true; SecondaryAttackDefinition secondaryAttackDefinition2 = secondaryAttackDefinition; ProjectileSecondaryBehavior projectileSecondaryBehavior = new ProjectileSecondaryBehavior(); projectileSecondaryBehavior.Preset = preset; projectileSecondaryBehavior.Cooldown = (flag2 ? 0f : Mathf.Max(0f, normalizedProjectileSecondaryConfig.Cooldown)); projectileSecondaryBehavior.CooldownReductionFactor = (flag2 ? 0f : Mathf.Clamp01(normalizedProjectileSecondaryConfig.CooldownReductionFactor)); projectileSecondaryBehavior.DamageFactor = Mathf.Max(0f, normalizedProjectileSecondaryConfig.DamageFactor); projectileSecondaryBehavior.SkillRaiseFactor = num2; projectileSecondaryBehavior.AdrenalineFactor = num2; projectileSecondaryBehavior.ProjectileSpeedFactor = Mathf.Max(0.01f, normalizedProjectileSecondaryConfig.ProjectileSpeedFactor); projectileSecondaryBehavior.ProjectileScaleFactor = Mathf.Max(0.01f, normalizedProjectileSecondaryConfig.ProjectileScaleFactor); projectileSecondaryBehavior.DurabilityFactor = durabilityFactor; projectileSecondaryBehavior.ProjectileCount = projectileCount; projectileSecondaryBehavior.SpreadAngle = spreadAngle; projectileSecondaryBehavior.AmmoConsumption = Mathf.Max(0, num); projectileSecondaryBehavior.VolleyRadius = Mathf.Max(0f, normalizedProjectileSecondaryConfig.VolleyRadius); projectileSecondaryBehavior.VolleyArcAngleMin = num3; projectileSecondaryBehavior.VolleyArcAngleMax = num4; projectileSecondaryBehavior.VolleyMaxRange = Mathf.Max(1f, normalizedProjectileSecondaryConfig.VolleyMaxRange); ProjectileSecondaryBehavior projectileSecondaryBehavior2 = projectileSecondaryBehavior; bool flag3; switch (preset) { case SecondaryAttackPreset.Barrage: case SecondaryAttackPreset.Volley: case SecondaryAttackPreset.Spiral: case SecondaryAttackPreset.Meteor: flag3 = true; break; default: flag3 = false; break; } projectileSecondaryBehavior2.Interval = (flag3 ? Mathf.Max(0f, num6) : Mathf.Max(0.01f, num6)); projectileSecondaryBehavior.HoldRepeatInterval = Mathf.Max(0.01f, normalizedProjectileSecondaryConfig.HoldRepeatInterval); projectileSecondaryBehavior.BarrageSpacing = Mathf.Max(0f, normalizedProjectileSecondaryConfig.BarrageSpacing); projectileSecondaryBehavior.MeteorRadius = Mathf.Max(0f, normalizedProjectileSecondaryConfig.MeteorRadius); projectileSecondaryBehavior.PierceDamageDecay = Mathf.Clamp01(normalizedProjectileSecondaryConfig.PierceDamageDecay); projectileSecondaryBehavior.SplitAngle = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SplitAngle); projectileSecondaryBehavior.RicochetBounces = Mathf.Max(0, normalizedProjectileSecondaryConfig.RicochetBounces); projectileSecondaryBehavior.RicochetDecay = Mathf.Clamp01(normalizedProjectileSecondaryConfig.RicochetDecay); projectileSecondaryBehavior.RicochetRoughness = Mathf.Clamp01(normalizedProjectileSecondaryConfig.RicochetRoughness); projectileSecondaryBehavior.SpiralRadius = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SpiralRadius); projectileSecondaryBehavior.SpiralTurns = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SpiralTurns); projectileSecondaryBehavior.SentinelDetectionRange = Mathf.Max(1f, normalizedProjectileSecondaryConfig.SentinelDetectionRange); projectileSecondaryBehavior.SentinelHoverDistance = Mathf.Max(0.5f, normalizedProjectileSecondaryConfig.SentinelHoverDistance); projectileSecondaryBehavior.SentinelHoverHeight = Mathf.Max(0.5f, normalizedProjectileSecondaryConfig.SentinelHoverHeight); projectileSecondaryBehavior.SentinelHoverElevationAngle = Mathf.Clamp(normalizedProjectileSecondaryConfig.SentinelHoverElevationAngle, 0f, 90f); projectileSecondaryBehavior.SentinelOrbitRadius = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SentinelOrbitRadius); projectileSecondaryBehavior.SentinelOrbitSpeed = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SentinelOrbitSpeed); projectileSecondaryBehavior.SentinelLifetime = Mathf.Max(0.5f, normalizedProjectileSecondaryConfig.SentinelLifetime); projectileSecondaryBehavior.SentinelAttackDelay = Mathf.Max(0f, normalizedProjectileSecondaryConfig.SentinelAttackDelay); projectileSecondaryBehavior.MeteorSpawnHeight = Mathf.Max(1f, normalizedProjectileSecondaryConfig.MeteorSpawnHeight); projectileSecondaryBehavior.MaxCharges = Mathf.Max(1, normalizedProjectileSecondaryConfig.MaxCharges); projectileSecondaryBehavior.DetonateAnimation = normalizedProjectileSecondaryConfig.DetonateAnimation.Trim(); projectileSecondaryBehavior.AoeRadiusFactor = Mathf.Max(0.01f, normalizedProjectileSecondaryConfig.AoeRadiusFactor); secondaryAttackDefinition2.Behavior = projectileSecondaryBehavior; secondaryAttackDefinition.AttackAnimation = attackAnimation; secondaryAttackDefinition.HasCustomAttackAnimation = flag; secondaryAttackDefinition.ResourceMultiplier = resourceMultiplier; secondaryAttackDefinition.OutputMultiplier = Mathf.Max(0f, GetNormalizedOutputMultiplier(weaponConfig)); secondaryAttackDefinition.DurabilityFactor = durabilityFactor; definition = secondaryAttackDefinition; ApplyProjectileDefinitionFeatures(definition, prefabName, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, resourceMultiplier); return true; } private static bool IsBombProjectilePreset(SecondaryAttackPreset preset) { if ((uint)(preset - 8) <= 1u) { return true; } return false; } private static float ResolveProjectileRewardFactor(SecondaryAttackPreset preset, int projectileCount) { switch (preset) { case SecondaryAttackPreset.StickyDetonator: case SecondaryAttackPreset.OverchargedBomb: return 0f; case SecondaryAttackPreset.Piercing: return 1f; default: return 1f / (float)Mathf.Max(1, projectileCount); } } internal static bool TryCreateSecondaryOverrideDefinition(string prefabName, string sourcePrefabName, Attack primaryAttack, Attack sourceSecondaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : sourceSecondaryAttack.m_attackAnimation.Trim()); definition = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = true, Behavior = new CopiedSecondaryBehavior { SourcePrefabName = sourcePrefabName }, AttackAnimation = attackAnimation, HasCustomAttackAnimation = flag, ResourceMultiplier = Mathf.Max(0f, GetSelectedMeleeResourceMultiplier(weaponConfig)), OutputMultiplier = Mathf.Max(0f, GetNormalizedOutputMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetSelectedMeleeDurabilityFactor(weaponConfig)) }; ApplyProjectileDefinitionFeatures(definition, prefabName, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateAftershockDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; string sourcePrefabName = (string.IsNullOrWhiteSpace(weaponConfig.Secondary?.CopyFrom) ? prefabName : weaponConfig.Secondary.CopyFrom.Trim()); if (!TryResolveAftershockSourceAttack(buildContext.ObjectDb, sourcePrefabName, out Attack sourceAttack, out string reason)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": " + reason)); NormalizedSneakAmbushConfig? sneakAmbush = weaponConfig.SneakAmbush; if (sneakAmbush == null || !sneakAmbush.Enabled) { NormalizedCleavingThrustConfig? cleavingThrust = weaponConfig.CleavingThrust; if (cleavingThrust == null || !cleavingThrust.Enabled) { NormalizedLaunchSlamConfig? launchSlam = weaponConfig.LaunchSlam; if (launchSlam == null || !launchSlam.Enabled) { NormalizedKnockbackChainConfig? knockbackChain = weaponConfig.KnockbackChain; if (knockbackChain == null || !knockbackChain.Enabled) { NormalizedRiftTrailConfig? riftTrail = weaponConfig.RiftTrail; if (riftTrail == null || !riftTrail.Enabled) { NormalizedFractureLineConfig? fractureLine = weaponConfig.FractureLine; if (fractureLine == null || !fractureLine.Enabled) { NormalizedHarvestSweepConfig? harvestSweep = weaponConfig.HarvestSweep; if (harvestSweep == null || !harvestSweep.Enabled) { NormalizedSpinningSweepConfig? spinningSweep = weaponConfig.SpinningSweep; if (spinningSweep == null || !spinningSweep.Enabled) { return false; } } } } } } } } definition = CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : sourceAttack.m_attackAnimation.Trim()); definition = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = true, Behavior = new AftershockSecondaryBehavior { SourcePrefabName = sourcePrefabName }, AttackAnimation = attackAnimation, HasCustomAttackAnimation = flag, ResourceMultiplier = Mathf.Max(0f, GetSelectedMeleeResourceMultiplier(weaponConfig)), OutputMultiplier = Mathf.Max(0f, GetNormalizedOutputMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetSelectedMeleeDurabilityFactor(weaponConfig)) }; ApplyProjectileDefinitionFeatures(definition, prefabName, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateFractureLineDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; string sourcePrefabName = (string.IsNullOrWhiteSpace(weaponConfig.Secondary?.CopyFrom) ? prefabName : weaponConfig.Secondary.CopyFrom.Trim()); if (!TryResolveFractureLineSourceAttack(buildContext.ObjectDb, sourcePrefabName, out Attack sourceAttack, out string reason)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": " + reason)); NormalizedSneakAmbushConfig? sneakAmbush = weaponConfig.SneakAmbush; if (sneakAmbush == null || !sneakAmbush.Enabled) { NormalizedCleavingThrustConfig? cleavingThrust = weaponConfig.CleavingThrust; if (cleavingThrust == null || !cleavingThrust.Enabled) { NormalizedLaunchSlamConfig? launchSlam = weaponConfig.LaunchSlam; if (launchSlam == null || !launchSlam.Enabled) { NormalizedKnockbackChainConfig? knockbackChain = weaponConfig.KnockbackChain; if (knockbackChain == null || !knockbackChain.Enabled) { NormalizedRiftTrailConfig? riftTrail = weaponConfig.RiftTrail; if (riftTrail == null || !riftTrail.Enabled) { NormalizedHarvestSweepConfig? harvestSweep = weaponConfig.HarvestSweep; if (harvestSweep == null || !harvestSweep.Enabled) { NormalizedSpinningSweepConfig? spinningSweep = weaponConfig.SpinningSweep; if (spinningSweep == null || !spinningSweep.Enabled) { return false; } } } } } } } definition = CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : sourceAttack.m_attackAnimation.Trim()); definition = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = true, Behavior = new FractureLineSecondaryBehavior { SourcePrefabName = sourcePrefabName }, AttackAnimation = attackAnimation, HasCustomAttackAnimation = flag, ResourceMultiplier = Mathf.Max(0f, GetSelectedMeleeResourceMultiplier(weaponConfig)), OutputMultiplier = Mathf.Max(0f, GetNormalizedOutputMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetSelectedMeleeDurabilityFactor(weaponConfig)) }; ApplyProjectileDefinitionFeatures(definition, prefabName, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } private static MeleeOnProjectileHitDefinition? CreateMeleeOnProjectileHitDefinition(string prefabName, NormalizedMeleeOnProjectileHitConfig? config) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if (config == null || !config.Enabled || string.IsNullOrWhiteSpace(config.Preset)) { return null; } string text = config.Preset.Trim(); if (!text.Equals("spearRain", StringComparison.OrdinalIgnoreCase) && !text.Equals("impactBurst", StringComparison.OrdinalIgnoreCase)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + " onProjectileHit: unknown preset '" + config.Preset + "'.")); return null; } return new MeleeOnProjectileHitDefinition { Preset = (text.Equals("impactBurst", StringComparison.OrdinalIgnoreCase) ? "impactBurst" : "spearRain"), PresetCooldown = CreatePresetCooldown(config.Cooldown, config.CooldownReductionFactor), DurabilityFactor = Mathf.Max(0f, config.DurabilityFactor), ProjectileSpinAxis = config.ProjectileSpinAxis, ProjectileVisualRotationOffset = config.ProjectileVisualRotationOffset, Vfx = config.Vfx, Count = Mathf.Max(1, config.Count), SpawnHeight = Mathf.Max(0.1f, config.SpawnHeight), SpawnRadius = Mathf.Max(0f, config.SpawnRadius), FlightTime = Mathf.Max(0.1f, config.FlightTime), DamageFactor = Mathf.Max(0f, config.DamageFactor), PushFactor = Mathf.Max(0f, config.PushFactor), Radius = Mathf.Max(0.1f, config.Radius), IncludeDirectTarget = config.IncludeDirectTarget, IncludeDestructibles = config.IncludeDestructibles, TriggerOnCharactersOnly = config.TriggerOnCharactersOnly }; } public static void DumpPlayerAnimatorReferences(Player player) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 if (_animatorDumpWritten) { return; } try { Animator val = ((Component)player).GetComponentsInChildren(true).FirstOrDefault(); if ((Object)(object)val == (Object)null) { return; } SortedSet sortedSet = new SortedSet(StringComparer.Ordinal); AnimatorControllerParameter[] parameters = val.parameters; foreach (AnimatorControllerParameter val2 in parameters) { if ((int)val2.type == 9 && !string.IsNullOrWhiteSpace(val2.name)) { sortedSet.Add(val2.name); } } PlayerAnimatorTriggers.Clear(); foreach (string item in sortedSet) { PlayerAnimatorTriggers.Add(item); } _animatorDumpWritten = true; WriteAnimationReferenceFile(); } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Failed to write SecondaryAttacks_AnimationReferences.txt: " + ex.Message)); } } public static void DumpCustomAnimationReferences(Player player) { if (_customAnimationDumpWritten) { return; } try { _customAnimationDumpWritten = true; WriteAnimationReferenceFile(); } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Failed to write SecondaryAttacks_AnimationReferences.txt: " + ex.Message)); } } private static void WriteAnimationReferenceFile() { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("SecondaryAttacks animation reference dump"); stringBuilder.AppendLine("This file is informational only; SecondaryAttacks does not read it as config."); stringBuilder.AppendLine(); stringBuilder.AppendLine("[Vanilla Animations]"); stringBuilder.AppendLine(); stringBuilder.AppendLine("[Player Animator Triggers]"); if (PlayerAnimatorTriggers.Count == 0) { stringBuilder.AppendLine(""); } else { foreach (string playerAnimatorTrigger in PlayerAnimatorTriggers) { stringBuilder.AppendLine("- " + playerAnimatorTrigger); } } stringBuilder.AppendLine(); stringBuilder.AppendLine("[Mod Animations]"); stringBuilder.AppendLine(); AppendAnimationReplaceManagerDump(stringBuilder); AppendPixMovementSlideDump(stringBuilder); File.WriteAllText(SecondaryAttackYamlDomainRegistry.AnimationReferenceFilePath, stringBuilder.ToString()); } private static void AppendAnimationReplaceManagerDump(StringBuilder builder) { builder.AppendLine("[KG_Managers.AnimationReplaceManager]"); Type type = FindLoadedType("KG_Managers.AnimationReplaceManager"); if (type == null) { builder.AppendLine("Loaded: false"); builder.AppendLine(); return; } AssemblyName name = type.Assembly.GetName(); builder.AppendLine("Loaded: true"); builder.AppendLine($"Assembly: {name.Name} {name.Version}"); builder.AppendLine(); AppendAnimationSetsDump(builder, type); builder.AppendLine(); } private static void AppendPixMovementSlideDump(StringBuilder builder) { builder.AppendLine("[Pix.Movement.Slide]"); Type type = FindLoadedType("Pix.Movement.Slide"); if (type == null) { builder.AppendLine("Loaded: false"); builder.AppendLine(); return; } AssemblyName name = type.Assembly.GetName(); builder.AppendLine("Loaded: true"); builder.AppendLine($"Assembly: {name.Name} {name.Version}"); if (GetInstancePropertyValue(GetStaticFieldValue(type, "CfgAnimTriggerName"), "Value") is string text && !string.IsNullOrWhiteSpace(text)) { builder.AppendLine("Trigger: " + text); } object? staticFieldValue = GetStaticFieldValue(type, "_slideController"); RuntimeAnimatorController val = (RuntimeAnimatorController)((staticFieldValue is RuntimeAnimatorController) ? staticFieldValue : null); builder.AppendLine(); builder.AppendLine("[Animation Sets]"); if ((Object)(object)val == (Object)null) { builder.AppendLine(""); builder.AppendLine(); } else { AppendStringList(builder, GetRuntimeAnimatorControllerClipNames(val), ""); builder.AppendLine(); } } private static void AppendAnimationSetsDump(StringBuilder builder, Type managerType) { builder.AppendLine("[Animation Sets]"); if (!(GetStaticFieldValue(managerType, "AllAnimationSets") is IEnumerable enumerable)) { builder.AppendLine(""); return; } int num = 0; foreach (object item in enumerable) { if (!(item is string)) { IEnumerable source = ((item is IEnumerable source2) ? source2.Cast() : Enumerable.Empty()); builder.AppendLine($"Set {num}:"); AppendStringList(builder, source.Select((object name) => name?.ToString() ?? ""), " "); num++; } } if (num == 0) { builder.AppendLine(""); } } private static void AppendStringList(StringBuilder builder, IEnumerable values, string indent) { List list = values.Where((string value) => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).OrderBy((string value) => value, StringComparer.Ordinal) .ToList(); if (list.Count == 0) { builder.AppendLine(indent + ""); return; } foreach (string item in list) { builder.AppendLine(indent + "- " + item); } } private static object? GetStaticFieldValue(Type type, string fieldName) { return type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); } private static object? GetInstancePropertyValue(object? instance, string propertyName) { return instance?.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); } private static IEnumerable GetRuntimeAnimatorControllerClipNames(RuntimeAnimatorController controller) { return (from clip in controller.animationClips where (Object)(object)clip != (Object)null select ((Object)clip).name into name where !string.IsNullOrWhiteSpace(name) select name).Distinct().OrderBy((string name) => name, StringComparer.Ordinal); } private static Type? FindLoadedType(string fullTypeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(fullTypeName, throwOnError: false); if (type != null) { return type; } } return null; } internal static bool TryCreateSummonEmpowerDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; if (!TryResolveSummonSourcePrefabs(primaryAttack, out List summonSourcePrefabs)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": summon empower requires a summon projectile with a SpawnAbility payload.")); return false; } string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : primaryAttack.m_attackAnimation.Trim()); NormalizedSummonEmpowerSecondaryConfig normalizedSummonEmpowerSecondaryConfig = weaponConfig.Secondary?.SummonEmpower ?? new NormalizedSummonEmpowerSecondaryConfig(); float moveSpeedFactor = normalizedSummonEmpowerSecondaryConfig.MoveSpeedFactor; float attackSpeedFactor = normalizedSummonEmpowerSecondaryConfig.AttackSpeedFactor; definition = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = true, Behavior = new SummonEmpowerSecondaryBehavior { SummonSourcePrefabs = summonSourcePrefabs, PresetCooldown = CreatePresetCooldown(normalizedSummonEmpowerSecondaryConfig.PresetCooldown.Cooldown, normalizedSummonEmpowerSecondaryConfig.PresetCooldown.CooldownReductionFactor, ResolveBloodMagicCooldownSkill(normalizedSummonEmpowerSecondaryConfig.PresetCooldown)), Radius = Mathf.Max(0f, normalizedSummonEmpowerSecondaryConfig.Radius), Duration = Mathf.Max(0.1f, normalizedSummonEmpowerSecondaryConfig.Duration), MoveSpeedFactor = Mathf.Max(0.05f, moveSpeedFactor), AttackSpeedFactor = Mathf.Max(0.05f, attackSpeedFactor) }, AttackAnimation = attackAnimation, HasCustomAttackAnimation = flag, ResourceMultiplier = Mathf.Max(0f, GetNormalizedResourceMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetNormalizedDurabilityFactor(weaponConfig)) }; ApplyCoreDefinitionFeatures(definition, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, GetNormalizedResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateShieldConvertDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { definition = null; NormalizedShieldConvertSecondaryConfig normalizedShieldConvertSecondaryConfig = weaponConfig.Secondary?.ShieldConvert ?? new NormalizedShieldConvertSecondaryConfig(); int num = (Object.op_Implicit((Object)(object)sharedData.m_attackStatusEffect) ? sharedData.m_attackStatusEffect.NameHash() : 0); if (num == 0) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": shield convert requires a primary attack shield status effect.")); return false; } string normalizedAttackAnimation = GetNormalizedAttackAnimation(weaponConfig); bool flag = !string.IsNullOrWhiteSpace(normalizedAttackAnimation); string attackAnimation = (flag ? normalizedAttackAnimation.Trim() : primaryAttack.m_attackAnimation.Trim()); definition = new SecondaryAttackDefinition { PrefabName = prefabName, AppliesSecondaryOverride = true, Behavior = new ShieldConvertSecondaryBehavior { ShieldStatusEffectHash = num, PresetCooldown = CreatePresetCooldown(normalizedShieldConvertSecondaryConfig.PresetCooldown.Cooldown, normalizedShieldConvertSecondaryConfig.PresetCooldown.CooldownReductionFactor, ResolveBloodMagicCooldownSkill(normalizedShieldConvertSecondaryConfig.PresetCooldown)), Radius = Mathf.Max(0f, normalizedShieldConvertSecondaryConfig.Radius), HealFactor = Mathf.Max(0f, normalizedShieldConvertSecondaryConfig.HealFactor) }, AttackAnimation = attackAnimation, HasCustomAttackAnimation = flag, ResourceMultiplier = Mathf.Max(0f, GetNormalizedResourceMultiplier(weaponConfig)), DurabilityFactor = Mathf.Max(0f, GetNormalizedDurabilityFactor(weaponConfig)) }; ApplyCoreDefinitionFeatures(definition, weaponConfig); ApplyAttackResourceScaling(definition, primaryAttack, GetNormalizedResourceMultiplier(weaponConfig)); return true; } private static string ResolveBloodMagicCooldownSkill(MeleePresetCooldownDefinition presetCooldown) { if (!string.IsNullOrWhiteSpace(presetCooldown.CooldownSkill)) { return presetCooldown.CooldownSkill.Trim(); } return "bloodMagic"; } private static bool TryResolveSummonSourcePrefabs(Attack primaryAttack, out List summonSourcePrefabs) { summonSourcePrefabs = new List(); if ((Object)(object)primaryAttack?.m_attackProjectile == (Object)null) { return false; } SpawnAbility component = primaryAttack.m_attackProjectile.GetComponent(); if ((Object)(object)component == (Object)null || component.m_spawnPrefab == null || component.m_spawnPrefab.Length == 0) { return false; } GameObject[] spawnPrefab = component.m_spawnPrefab; foreach (GameObject val in spawnPrefab) { if (!((Object)(object)val == (Object)null)) { string prefabName = Utils.GetPrefabName(val); if (!string.IsNullOrWhiteSpace(prefabName) && !summonSourcePrefabs.Any((string existing) => string.Equals(existing, prefabName, StringComparison.OrdinalIgnoreCase))) { summonSourcePrefabs.Add(prefabName); } } } return summonSourcePrefabs.Count > 0; } internal static Attack BuildSecondaryAttack(Attack sourceAttack, SecondaryAttackDefinition definition) { //IL_001b: 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_00b9: Unknown result type (might be due to invalid IL or missing references) Attack val = CloneAttack(sourceAttack); if (definition.BehaviorType == SecondaryAttackBehaviorType.SummonEmpower || definition.BehaviorType == SecondaryAttackBehaviorType.ShieldConvert) { val.m_attackType = (AttackType)3; val.m_bowDraw = false; val.m_requiresReload = false; val.m_projectiles = 1; val.m_projectileBursts = 1; val.m_attackChainLevels = 1; val.m_attackRandomAnimations = 0; } else if (definition.BehaviorType == SecondaryAttackBehaviorType.Projectile) { val.m_attackType = (AttackType)2; val.m_bowDraw = sourceAttack.m_bowDraw; val.m_requiresReload = sourceAttack.m_requiresReload; if (!(definition.Behavior is ProjectileSecondaryBehavior { Preset: var preset }) || (preset != SecondaryAttackPreset.Piercing && preset != SecondaryAttackPreset.Burst) || 1 == 0) { val.m_projectiles = 1; val.m_projectileBursts = 1; } } else if (definition.BehaviorType == SecondaryAttackBehaviorType.Aftershock) { val.m_attackType = (AttackType)4; val.m_bowDraw = false; val.m_requiresReload = false; val.m_projectiles = 1; val.m_projectileBursts = 1; } val.m_attackAnimation = definition.AttackAnimation; if (definition.BehaviorType == SecondaryAttackBehaviorType.Projectile && string.Equals(val.m_attackAnimation, "staff_rapidfire", StringComparison.Ordinal)) { val.m_loopingAttack = true; } val.m_attackHealth = definition.RawAttackHealth; val.m_attackHealthPercentage = definition.RawAttackHealthPercentage; val.m_attackStamina = definition.RawAttackStamina; val.m_attackEitr = definition.RawAttackEitr; val.m_drawStaminaDrain = definition.RawDrawStamina; val.m_drawEitrDrain = definition.RawDrawEitr; val.m_reloadStaminaDrain = definition.RawReloadStamina; val.m_reloadEitrDrain = definition.RawReloadEitr; val.m_damageMultiplier *= definition.OutputMultiplier; val.m_forceMultiplier *= definition.OutputMultiplier; val.m_staggerMultiplier *= definition.OutputMultiplier; if (definition.HasCustomAttackAnimation) { val.m_attackChainLevels = 1; val.m_attackRandomAnimations = 0; } return val; } public static void UpdateCustomBowDraw(Player player, ItemData weapon, float dt, ref float attackDrawTime, bool blocking, bool attackHold, bool secondaryAttackHold, bool secondaryAttackPressed, ZSyncAnimation zanim, SEMan seman) { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) BowSecondaryState value = BowSecondaryStates.GetValue(player, (Player _) => new BowSecondaryState()); string text = (((Object)(object)weapon.m_dropPrefab != (Object)null) ? ((Object)weapon.m_dropPrefab).name : ""); if (!string.Equals(value.PrefabName, text, StringComparison.Ordinal)) { value.PrefabName = text; value.PendingSecondary = false; } if (!SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { value.PendingSecondary = false; return; } bool flag = attackHold; float skillAdjustedDrawCost = GetSkillAdjustedDrawCost(player, weapon, weapon.m_shared.m_attack.m_drawStaminaDrain); float drawEitrDrain = weapon.m_shared.m_attack.m_drawEitrDrain; bool flag2 = skillAdjustedDrawCost <= 0f || ((Character)player).HaveStamina(0f); bool flag3 = drawEitrDrain <= 0f || ((Character)player).HaveEitr(0f); if (blocking || ((Character)player).InMinorAction() || ((Character)player).IsAttached()) { attackDrawTime = -1f; value.PendingSecondary = false; if (!string.IsNullOrEmpty(weapon.m_shared.m_attack.m_drawAnimationState)) { zanim.SetBool(weapon.m_shared.m_attack.m_drawAnimationState, false); } return; } if (flag && attackDrawTime == 0f) { value.PendingSecondary = false; } else if (secondaryAttackPressed && flag && attackDrawTime > 0f) { value.PendingSecondary = true; RangedSecondaryCooldownSystem.CanStart((Humanoid)(object)player, weapon); } if (attackDrawTime < 0f) { if (!flag) { attackDrawTime = 0f; } } else if (flag && flag2 && flag3 && attackDrawTime >= 0f) { if (attackDrawTime == 0f) { if (!weapon.m_shared.m_attack.StartDraw((Humanoid)(object)player, weapon)) { attackDrawTime = -1f; value.PendingSecondary = false; return; } weapon.m_shared.m_holdStartEffect.Create(((Component)player).transform.position, Quaternion.identity, ((Component)player).transform, 1f, -1); } attackDrawTime += Time.fixedDeltaTime; if (!string.IsNullOrEmpty(weapon.m_shared.m_attack.m_drawAnimationState)) { zanim.SetBool(weapon.m_shared.m_attack.m_drawAnimationState, true); zanim.SetFloat("drawpercent", ((Humanoid)player).GetAttackDrawPercentage()); } ((Character)player).UseStamina(skillAdjustedDrawCost * dt); ((Character)player).UseEitr(drawEitrDrain * dt); } else { if (!(attackDrawTime > 0f)) { return; } if (flag2 && flag3) { bool pendingSecondary = value.PendingSecondary; float extraStaminaCost = 0f; if (!pendingSecondary || CanPayBowSecondaryReleaseExtraStamina(player, weapon, definition, skillAdjustedDrawCost, attackDrawTime, out extraStaminaCost)) { if (((Character)player).StartAttack((Character)null, pendingSecondary) && pendingSecondary && extraStaminaCost > 0f) { ((Character)player).UseStamina(extraStaminaCost); } } else { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } } if (!string.IsNullOrEmpty(weapon.m_shared.m_attack.m_drawAnimationState)) { zanim.SetBool(weapon.m_shared.m_attack.m_drawAnimationState, false); } attackDrawTime = 0f; value.PendingSecondary = false; } } private static bool CanPayBowSecondaryReleaseExtraStamina(Player player, ItemData weapon, SecondaryAttackDefinition definition, float drawStaminaDrain, float attackDrawTime, out float extraStaminaCost) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 extraStaminaCost = 0f; if ((int)weapon.m_shared.m_itemType != 4 || !weapon.m_shared.m_attack.m_bowDraw) { return true; } float num = Mathf.Max(0f, definition.ResourceMultiplier); if (num <= 1f || drawStaminaDrain <= 0f) { return true; } float skillAdjustedFullDrawTime = GetSkillAdjustedFullDrawTime(player, weapon); float num2 = Mathf.Min(Mathf.Max(0f, attackDrawTime), skillAdjustedFullDrawTime); extraStaminaCost = drawStaminaDrain * num2 * (num - 1f); if (!(extraStaminaCost <= 0f)) { return ((Character)player).HaveStamina(extraStaminaCost); } return true; } private static float GetSkillAdjustedFullDrawTime(Player player, ItemData weapon) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0f, weapon.m_shared.m_attack.m_drawDurationMin); if (num <= 0f) { return 0f; } float skillFactor = ((Character)player).GetSkillFactor(weapon.m_shared.m_skillType); return Mathf.Lerp(num, num * 0.2f, skillFactor); } internal static bool TryPrepareReloadSecondaryResourceCost(Humanoid humanoid, ItemData weapon, out ReloadSecondaryResourceCostContext context) { context = ReloadSecondaryResourceCostContext.Empty; if (weapon == null || !SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return true; } if (definition.BehaviorType != SecondaryAttackBehaviorType.Projectile) { return true; } Attack attack = weapon.m_shared.m_attack; if (!attack.m_requiresReload || !IsCurrentReloadWeaponLoaded(humanoid, weapon)) { return true; } float num = Mathf.Max(0f, definition.ResourceMultiplier) - 1f; if (Mathf.Approximately(num, 0f)) { return true; } float num2 = Mathf.Max(0f, weapon.GetWeaponLoadingTime()); if (num2 <= 0f) { return true; } float num3 = Mathf.Max(0f, attack.m_reloadStaminaDrain) * num2 * num; float num4 = Mathf.Max(0f, attack.m_reloadEitrDrain) * num2 * num; if (num3 > 0f && !((Character)humanoid).HaveStamina(num3)) { if (((Character)humanoid).IsPlayer()) { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } return false; } if (num4 > 0f && !((Character)humanoid).TryUseEitr(num4)) { return false; } context = new ReloadSecondaryResourceCostContext(num3, num4); return true; } private static bool IsCurrentReloadWeaponLoaded(Humanoid humanoid, ItemData weapon) { Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null) { return val.m_weaponLoaded == weapon; } return humanoid.IsWeaponLoaded(); } internal static void ApplyReloadSecondaryResourceCost(Humanoid humanoid, ReloadSecondaryResourceCostContext? context) { if (context != null && context.HasDelta) { if (context.StaminaDelta > 0f) { ((Character)humanoid).UseStamina(context.StaminaDelta); } else if (context.StaminaDelta < 0f) { ((Character)humanoid).AddStamina(0f - context.StaminaDelta); } if (context.EitrDelta > 0f) { ((Character)humanoid).UseEitr(context.EitrDelta); } else if (context.EitrDelta < 0f) { ((Character)humanoid).AddEitr(0f - context.EitrDelta); } } } private static bool TryParsePreset(string presetText, out SecondaryAttackPreset preset) { string presetKey = presetText.Trim(); preset = SecondaryAttackPreset.Barrage; if (SecondaryAttackPresetCatalog.TryGet(presetKey, out SecondaryAttackPresetInfo info) && info.Group == SecondaryAttackPresetGroup.Ranged) { return Enum.TryParse(info.Key, ignoreCase: true, out preset); } return false; } private static void ApplyAttackResourceScaling(SecondaryAttackDefinition definition, Attack sourceAttack, float resourceMultiplier) { float num = (definition.ResourceMultiplier = Mathf.Max(0f, resourceMultiplier)); definition.RawAttackHealth = Mathf.Max(0f, sourceAttack.m_attackHealth * num); definition.RawAttackHealthPercentage = Mathf.Max(0f, sourceAttack.m_attackHealthPercentage * num); definition.RawAttackStamina = Mathf.Max(0f, sourceAttack.m_attackStamina * num); definition.RawAttackEitr = Mathf.Max(0f, sourceAttack.m_attackEitr * num); definition.RawDrawStamina = Mathf.Max(0f, sourceAttack.m_drawStaminaDrain * num); definition.RawDrawEitr = Mathf.Max(0f, sourceAttack.m_drawEitrDrain * num); definition.RawReloadStamina = Mathf.Max(0f, sourceAttack.m_reloadStaminaDrain * num); definition.RawReloadEitr = Mathf.Max(0f, sourceAttack.m_reloadEitrDrain * num); } internal static Attack ResolveSourceAttack(ObjectDB objectDb, ItemDrop itemDrop, SecondaryAttackDefinition definition) { if (definition.BehaviorType == SecondaryAttackBehaviorType.Projectile) { return itemDrop.m_itemData.m_shared.m_attack; } if (definition.BehaviorType == SecondaryAttackBehaviorType.SummonEmpower || definition.BehaviorType == SecondaryAttackBehaviorType.ShieldConvert) { return itemDrop.m_itemData.m_shared.m_attack; } string reason; if (definition.BehaviorType == SecondaryAttackBehaviorType.Aftershock) { AftershockSecondaryBehavior aftershockSecondaryBehavior = definition.Behavior as AftershockSecondaryBehavior; object obj; if (!string.IsNullOrWhiteSpace(aftershockSecondaryBehavior?.SourcePrefabName)) { obj = aftershockSecondaryBehavior.SourcePrefabName; } else { GameObject dropPrefab = itemDrop.m_itemData.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? ((Object)itemDrop).name; } string sourcePrefabName = (string)obj; if (TryResolveAftershockSourceAttack(objectDb, sourcePrefabName, out Attack sourceAttack, out reason)) { return sourceAttack; } GameObject dropPrefab2 = itemDrop.m_itemData.m_dropPrefab; return ResolveOriginalSecondaryAttack(objectDb, ((dropPrefab2 != null) ? ((Object)dropPrefab2).name : null) ?? ((Object)itemDrop).name, itemDrop) ?? itemDrop.m_itemData.m_shared.m_attack; } if (definition.BehaviorType == SecondaryAttackBehaviorType.FractureLine) { FractureLineSecondaryBehavior fractureLineSecondaryBehavior = definition.Behavior as FractureLineSecondaryBehavior; object obj2; if (!string.IsNullOrWhiteSpace(fractureLineSecondaryBehavior?.SourcePrefabName)) { obj2 = fractureLineSecondaryBehavior.SourcePrefabName; } else { GameObject dropPrefab3 = itemDrop.m_itemData.m_dropPrefab; obj2 = ((dropPrefab3 != null) ? ((Object)dropPrefab3).name : null) ?? ((Object)itemDrop).name; } string sourcePrefabName2 = (string)obj2; if (TryResolveFractureLineSourceAttack(objectDb, sourcePrefabName2, out Attack sourceAttack2, out reason)) { return sourceAttack2; } GameObject dropPrefab4 = itemDrop.m_itemData.m_dropPrefab; return ResolveOriginalSecondaryAttack(objectDb, ((dropPrefab4 != null) ? ((Object)dropPrefab4).name : null) ?? ((Object)itemDrop).name, itemDrop) ?? itemDrop.m_itemData.m_shared.m_attack; } CopiedSecondaryBehavior copiedSecondaryBehavior = definition.Behavior as CopiedSecondaryBehavior; object obj3; if (!string.IsNullOrWhiteSpace(copiedSecondaryBehavior?.SourcePrefabName)) { obj3 = copiedSecondaryBehavior.SourcePrefabName; } else { GameObject dropPrefab5 = itemDrop.m_itemData.m_dropPrefab; obj3 = ((dropPrefab5 != null) ? ((Object)dropPrefab5).name : null) ?? ((Object)itemDrop).name; } string sourcePrefabName3 = (string)obj3; if (TryResolveSecondarySourceAttack(objectDb, sourcePrefabName3, out Attack sourceAttack3, out reason)) { return sourceAttack3; } GameObject dropPrefab6 = itemDrop.m_itemData.m_dropPrefab; return ResolveOriginalSecondaryAttack(objectDb, ((dropPrefab6 != null) ? ((Object)dropPrefab6).name : null) ?? ((Object)itemDrop).name, itemDrop) ?? itemDrop.m_itemData.m_shared.m_attack; } internal static bool TryResolveSecondarySourceAttack(ObjectDB objectDb, string sourcePrefabName, out Attack? sourceAttack, out string reason) { sourceAttack = null; reason = ""; ItemDrop val = FindItemDropByPrefabName(objectDb, sourcePrefabName); if ((Object)(object)val == (Object)null) { reason = "source weapon '" + sourcePrefabName + "' was not found in ObjectDB."; return false; } sourceAttack = ResolveOriginalSecondaryAttack(objectDb, sourcePrefabName, val); if (sourceAttack == null || string.IsNullOrWhiteSpace(sourceAttack.m_attackAnimation)) { reason = "source weapon '" + sourcePrefabName + "' does not have a valid secondary attack."; return false; } return true; } private static bool TryResolveAftershockSourceAttack(ObjectDB objectDb, string sourcePrefabName, out Attack? sourceAttack, out string reason) { sourceAttack = null; reason = ""; ItemDrop val = FindItemDropByPrefabName(objectDb, sourcePrefabName); if ((Object)(object)val == (Object)null) { reason = "aftershock source weapon '" + sourcePrefabName + "' was not found in ObjectDB."; return false; } Attack attack = val.m_itemData.m_shared.m_attack; if (IsValidAftershockSourceAttack(attack)) { sourceAttack = attack; return true; } Attack val2 = ResolveOriginalSecondaryAttack(objectDb, sourcePrefabName, val); if (IsValidAftershockSourceAttack(val2)) { sourceAttack = val2; return true; } reason = "aftershock source weapon '" + sourcePrefabName + "' does not have a valid Area primary or secondary attack."; return false; } private static bool IsValidAftershockSourceAttack(Attack? attack) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (attack != null && (int)attack.m_attackType == 4 && !string.IsNullOrWhiteSpace(attack.m_attackAnimation)) { return attack.m_attackRayWidth > 0f; } return false; } private static bool TryResolveFractureLineSourceAttack(ObjectDB objectDb, string sourcePrefabName, out Attack? sourceAttack, out string reason) { sourceAttack = null; reason = ""; ItemDrop val = FindItemDropByPrefabName(objectDb, sourcePrefabName); if ((Object)(object)val == (Object)null) { reason = "fractureLine source weapon '" + sourcePrefabName + "' was not found in ObjectDB."; return false; } Attack val2 = ResolveOriginalSecondaryAttack(objectDb, sourcePrefabName, val); if (IsValidFractureLineSourceAttack(val2)) { sourceAttack = val2; return true; } Attack attack = val.m_itemData.m_shared.m_attack; if (IsValidFractureLineSourceAttack(attack)) { sourceAttack = attack; return true; } reason = "fractureLine source weapon '" + sourcePrefabName + "' does not have a valid melee primary or secondary attack."; return false; } private static bool IsValidFractureLineSourceAttack(Attack? attack) { //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_0012: Invalid comparison between Unknown and I4 if (attack != null && ((int)attack.m_attackType == 0 || (int)attack.m_attackType == 1)) { return !string.IsNullOrWhiteSpace(attack.m_attackAnimation); } return false; } private static Attack? ResolveOriginalSecondaryAttack(ObjectDB objectDb, string sourcePrefabName, ItemDrop sourceItemDrop) { if (!SecondaryAttackObjectDbStateStore.TryGetOriginalSecondaryAttack(objectDb, sourcePrefabName, out Attack attack)) { return sourceItemDrop.m_itemData.m_shared.m_secondaryAttack; } return attack; } private static ItemDrop? FindItemDropByPrefabName(ObjectDB objectDb, string prefabName) { foreach (GameObject item in objectDb.m_items) { if (!((Object)(object)item == (Object)null) && string.Equals(((Object)item).name, prefabName, StringComparison.OrdinalIgnoreCase)) { return item.GetComponent(); } } return null; } private static float GetSkillAdjustedDrawCost(Player player, ItemData weapon, float rawDrawCost) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (rawDrawCost <= 0f) { return 0f; } float skillFactor = ((Character)player).GetSkillFactor(weapon.m_shared.m_skillType); return rawDrawCost - rawDrawCost * 0.33f * skillFactor; } } internal static class SecondaryAttackHarmonyDispatch { internal struct ProjectileOnHitState { internal bool RuntimeContext; internal DirectWeaponHitContextSystem.Scope DirectHitContext; internal ProjectileRuntimeSystem.ScatterRicochetDamageScope ScatterRicochetDamageScope; } internal static bool ProjectileOnHitPrefix(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal, out ProjectileOnHitState state) { //IL_000a: 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_0019: 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_0028: 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_004c: 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_006b: Unknown result type (might be due to invalid IL or missing references) state = default(ProjectileOnHitState); if (StickyDetonatorSystem.TryHandleProjectileHit(projectile, collider, hitPoint, water, normal)) { return false; } if (MeleeBoomerangProjectileSystem.TryHandleProjectileHit(projectile, collider, hitPoint, water, normal)) { return false; } if (ProjectileRuntimeSystem.TryHandleProjectilePresetHit(projectile, collider, hitPoint, water, normal)) { return false; } if (MeleeProjectileHitCascadeSystem.ShouldIgnoreOnProjectileHitSourceHit(projectile, collider)) { return false; } SecondaryAttackAdrenalineSystem.TryGrantAttackUseAdrenalineOnProjectileHit(projectile, collider); state.ScatterRicochetDamageScope = ProjectileRuntimeSystem.BeginScatterRicochetDamageScale(projectile, collider, water, normal); state.DirectHitContext = DirectWeaponHitContextSystem.BeginProjectileHit(projectile); state.RuntimeContext = SecondaryAttackRuntimeFacade.BeginProjectileHitContext(projectile, collider, hitPoint, water, normal); return true; } internal static void ProjectileOnHitPostfix(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal, ref ProjectileOnHitState state) { //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) DirectWeaponHitContextSystem.End(ref state.DirectHitContext); try { if (state.RuntimeContext) { MeleeProjectileHitCascadeSystem.TryTrigger(projectile, collider, hitPoint, water, normal); } } finally { CleanupProjectileOnHitState(ref state); } MeleeProjectileHitCascadeSystem.DestroySpearRainFollowupAfterHit(projectile); } internal static void ProjectileOnHitFinalizer(ref ProjectileOnHitState state) { DirectWeaponHitContextSystem.End(ref state.DirectHitContext); CleanupProjectileOnHitState(ref state); } private static void CleanupProjectileOnHitState(ref ProjectileOnHitState state) { SecondaryAttackRuntimeFacade.EndProjectileHitContext(state.RuntimeContext); state.RuntimeContext = false; ProjectileRuntimeSystem.EndScatterRicochetDamageScale(state.ScatterRicochetDamageScope); state.ScatterRicochetDamageScope = default(ProjectileRuntimeSystem.ScatterRicochetDamageScope); } internal static void PlayerUpdatePostfix(Player player, bool primaryAttackHold, bool secondaryAttackHold, ref bool blocking) { if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { SecondaryAttackFacade.TryApplyPendingConfig(); MeleeBoomerangProjectileSystem.UpdateDeferredReturnAutoEquips(player); SecondaryAttackRuntimeFacade.TryUpdateSecondaryProjectileHoldRepeat(player, secondaryAttackHold); SecondaryCooldownGroupSystem.UpdateActiveCooldowns(player); SneakAmbushChargeSystem.Update(player); SecondaryCooldownHudSystem.Update(player); BowSecondaryKeyHintSystem.RefreshKeyHintUi(); SpinningSweepSystem.UpdateInput(player, secondaryAttackHold, primaryAttackHold); HarvestSweepSystem.UpdateInput(player, secondaryAttackHold, primaryAttackHold); StickyDetonatorSystem.UpdateInput(player, ref blocking); } } internal static bool AttackFireProjectileBurstPrefix(Attack attack, out CopiedThrowProjectileVisualSystem.BurstScope state) { state = CopiedThrowProjectileVisualSystem.BeginBurst(attack); if (SecondaryAttackRuntimeFacade.TryHandleCustomProjectileBurst(attack)) { return false; } if (state.Active && !SecondaryAttackStartAttackDispatch.TryConsumeProjectilePresetCooldownAtBurst(attack)) { CopiedThrowProjectileVisualSystem.EndBurst(ref state); return false; } return true; } internal static void AttackFireProjectileBurstPostfix(ref CopiedThrowProjectileVisualSystem.BurstScope state) { CopiedThrowProjectileVisualSystem.EndBurst(ref state); } } internal static class SecondaryAttackHitDataFactory { internal static HitData CreateMeleeHit(Attack attack, Collider collider, Vector3 hitPoint, Vector3 hitDirection, float skillFactor, float damageFactor, float pushFactor, float skillRaiseAmount = 0f) { //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_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) //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_003f: 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_0057: 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_0082: 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_009f: 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_00c1: 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_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_00d0: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_00de: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) ItemData weapon = attack.m_weapon; Character character = (Character)(object)attack.m_character; SkillType skillType = weapon.m_shared.m_skillType; HitData val = new HitData { m_toolTier = (short)weapon.m_shared.m_toolTier, m_statusEffectHash = ResolveStatusEffectHash(weapon), m_skillLevel = character.GetSkillLevel(skillType), m_itemLevel = (short)weapon.m_quality, m_itemWorldLevel = (byte)weapon.m_worldLevel, m_pushForce = weapon.m_shared.m_attackForce * skillFactor * attack.m_forceMultiplier * pushFactor, m_backstabBonus = weapon.m_shared.m_backstabBonus, m_staggerMultiplier = attack.m_staggerMultiplier, m_dodgeable = weapon.m_shared.m_dodgeable, m_blockable = weapon.m_shared.m_blockable, m_skill = skillType, m_skillRaiseAmount = skillRaiseAmount, m_point = hitPoint, m_dir = hitDirection, m_hitCollider = collider, m_hitType = (HitType)((!(character is Player)) ? 1 : 2), m_healthReturn = attack.m_attackHealthReturnHit }; ((DamageTypes)(ref val.m_damage)).Add(weapon.GetDamage(), 1); val.SetAttacker(character); ApplyDamageModifiers(attack, val, skillFactor, damageFactor); return val; } private static int ResolveStatusEffectHash(ItemData weapon) { StatusEffect attackStatusEffect = weapon.m_shared.m_attackStatusEffect; if ((Object)(object)attackStatusEffect == (Object)null) { return 0; } if (!(weapon.m_shared.m_attackStatusEffectChance >= 1f) && !(Random.Range(0f, 1f) < weapon.m_shared.m_attackStatusEffectChance)) { return 0; } return attackStatusEffect.NameHash(); } private static void ApplyDamageModifiers(Attack attack, HitData hitData, float skillFactor, float damageFactor) { if (!Mathf.Approximately(attack.m_damageMultiplier, 1f)) { ((DamageTypes)(ref hitData.m_damage)).Modify(attack.m_damageMultiplier); } if (!Mathf.Approximately(skillFactor, 1f)) { ((DamageTypes)(ref hitData.m_damage)).Modify(skillFactor); } if (!Mathf.Approximately(damageFactor, 1f)) { ((DamageTypes)(ref hitData.m_damage)).Modify(damageFactor); } ((DamageTypes)(ref hitData.m_damage)).Modify(1f + (float)Mathf.Max(0, ((Character)attack.m_character).GetLevel() - 1) * 0.5f); if (attack.m_damageMultiplierPerMissingHP > 0f) { ((DamageTypes)(ref hitData.m_damage)).Modify(1f + (((Character)attack.m_character).GetMaxHealth() - ((Character)attack.m_character).GetHealth()) * attack.m_damageMultiplierPerMissingHP); } if (attack.m_damageMultiplierByTotalHealthMissing > 0f) { ((DamageTypes)(ref hitData.m_damage)).Modify(1f + (1f - ((Character)attack.m_character).GetHealthPercentage()) * attack.m_damageMultiplierByTotalHealthMissing); } } } internal static class SecondaryAttackProjectileToolTierSystem { internal static void ApplyCurrentProjectileHitToolTierIfNeeded(HitData? hit) { if (hit != null && SecondaryAttackRuntimeContext.TryPeekProjectileHitContext(out ProjectileHitContext context) && !((Object)(object)context?.Projectile == (Object)null) && ShouldApplyToProjectile(context.Attribution)) { ApplyToHitData(hit, context.Projectile, ProjectileAccess.GetWeapon(context.Projectile)); } } internal static void ApplyToHitData(HitData? hit, Projectile? projectile, ItemData? weapon) { if (hit != null) { short num = ResolveToolTier(projectile, weapon); byte b = ResolveItemWorldLevel(projectile, weapon); if (num > hit.m_toolTier) { hit.m_toolTier = num; } if (b > hit.m_itemWorldLevel) { hit.m_itemWorldLevel = b; } } } private static bool ShouldApplyToProjectile(ProjectileAttackAttribution? attribution) { bool flag = attribution?.SecondaryAttack ?? false; if (!flag) { bool flag2; switch (attribution?.Definition?.BehaviorType) { case SecondaryAttackBehaviorType.Projectile: case SecondaryAttackBehaviorType.CopiedSecondary: flag2 = true; break; default: flag2 = false; break; } flag = flag2; } return flag; } private static short ResolveToolTier(Projectile? projectile, ItemData? weapon) { short num = 0; HitData val = (((Object)(object)projectile != (Object)null) ? ProjectileAccess.GetOriginalHitData(projectile) : null); if (val != null) { num = val.m_toolTier; } ItemData val2 = (((Object)(object)projectile != (Object)null) ? ProjectileAccess.GetWeapon(projectile) : null); if (val2?.m_shared != null) { num = (short)Mathf.Max((int)num, val2.m_shared.m_toolTier); } if (weapon?.m_shared != null) { num = (short)Mathf.Max((int)num, weapon.m_shared.m_toolTier); } return num; } private static byte ResolveItemWorldLevel(Projectile? projectile, ItemData? weapon) { byte b = 0; HitData val = (((Object)(object)projectile != (Object)null) ? ProjectileAccess.GetOriginalHitData(projectile) : null); if (val != null) { b = val.m_itemWorldLevel; } ItemData val2 = (((Object)(object)projectile != (Object)null) ? ProjectileAccess.GetWeapon(projectile) : null); if (val2 != null) { b = (byte)Mathf.Max((int)b, val2.m_worldLevel); } if (weapon != null) { b = (byte)Mathf.Max((int)b, weapon.m_worldLevel); } return b; } } internal static class SecondaryAttackStartAttackDispatch { internal readonly struct StartAttackState { internal static readonly StartAttackState Empty = new StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext.Empty); internal SecondaryAttackManager.ReloadSecondaryResourceCostContext ReloadCostContext { get; } internal ItemData? CooldownFallbackWeapon { get; } internal Attack? ConfiguredSecondaryAttack { get; } internal bool SkipActiveRegistration { get; } internal bool IsOriginalCooldownFallback { get; } internal StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext reloadCostContext, ItemData? cooldownFallbackWeapon = null, Attack? configuredSecondaryAttack = null, bool skipActiveRegistration = false, bool originalCooldownFallback = false) { ReloadCostContext = reloadCostContext; CooldownFallbackWeapon = cooldownFallbackWeapon; ConfiguredSecondaryAttack = configuredSecondaryAttack; SkipActiveRegistration = skipActiveRegistration; IsOriginalCooldownFallback = originalCooldownFallback; } internal void RestoreCooldownFallbackSecondary() { if (CooldownFallbackWeapon?.m_shared != null && ConfiguredSecondaryAttack != null) { CooldownFallbackWeapon.m_shared.m_secondaryAttack = ConfiguredSecondaryAttack; } } } private sealed class ProjectilePresetCooldownConsumedState { } private sealed class ProjectilePresetOriginalCooldownFallbackState { } private static readonly ConditionalWeakTable ProjectilePresetCooldownConsumedAttacks = new ConditionalWeakTable(); private static readonly ConditionalWeakTable ProjectilePresetOriginalCooldownFallbackAttacks = new ConditionalWeakTable(); internal static bool Prefix(Humanoid humanoid, bool secondaryAttack, ref bool result, out StartAttackState state) { state = StartAttackState.Empty; if (!secondaryAttack) { return true; } return TryPrepareConfiguredSecondaryStart(humanoid, ref result, out state); } internal static void Postfix(Humanoid humanoid, bool secondaryAttack, bool result, StartAttackState state) { Cleanup(humanoid, secondaryAttack, state); if (secondaryAttack && result && humanoid.m_currentAttack != null) { if (state.IsOriginalCooldownFallback) { MarkProjectilePresetOriginalCooldownFallback(humanoid.m_currentAttack); } Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null) { SneakAmbushChargeSystem.BeginSecondaryAttack(val, humanoid.GetCurrentWeapon()); } if (!state.SkipActiveRegistration) { SecondaryAttackManager.ApplyReloadSecondaryResourceCost(humanoid, state.ReloadCostContext); RegisterActiveAttackIfNeeded(humanoid); } } } internal static void Cleanup(Humanoid humanoid, bool secondaryAttack, StartAttackState state) { try { SecondaryAttackAdrenalineSystem.EndConfiguredSecondaryStart(humanoid); } finally { if (secondaryAttack) { state.RestoreCooldownFallbackSecondary(); } } } private static bool TryPrepareConfiguredSecondaryStart(Humanoid humanoid, ref bool result, out StartAttackState state) { state = StartAttackState.Empty; ItemData currentWeapon = humanoid.GetCurrentWeapon(); Attack secondaryBeforeRuntimeApply = currentWeapon?.m_shared?.m_secondaryAttack; SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(currentWeapon); if (currentWeapon == null) { return true; } if (TryPrepareOriginalSecondaryForProjectilePresetCooldown(humanoid, currentWeapon, secondaryBeforeRuntimeApply, ref result, out state, out var runOriginal)) { return runOriginal; } if (!RangedSecondaryCooldownSystem.CanStart(humanoid, currentWeapon)) { result = false; return false; } if (!StaffRuntimeSystem.CanStartStaffSpecial(humanoid, currentWeapon)) { result = false; return false; } if (!SecondaryAttackRuntimeFacade.CanStartConfiguredSecondary(humanoid, currentWeapon)) { result = false; return false; } if (!SecondaryAttackManager.TryPrepareReloadSecondaryResourceCost(humanoid, currentWeapon, out SecondaryAttackManager.ReloadSecondaryResourceCostContext context)) { result = false; return false; } state = new StartAttackState(context); SecondaryAttackAdrenalineSystem.BeginConfiguredSecondaryStart(humanoid, currentWeapon); return true; } internal static bool TryConsumeProjectilePresetCooldownAtBurst(Attack attack) { if ((Object)(object)attack?.m_character == (Object)null || attack.m_weapon == null) { return true; } if (IsProjectilePresetOriginalCooldownFallback(attack)) { return true; } if (ProjectilePresetCooldownConsumedAttacks.TryGetValue(attack, out ProjectilePresetCooldownConsumedState _)) { return true; } SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(attack.m_weapon); if (!TryResolveProjectilePresetCooldown(attack.m_weapon, out string presetName, out MeleePresetCooldownDefinition cooldown, out SecondaryAttackDefinition _)) { return true; } if (IsSpearRainPreset(presetName)) { bool num = MeleePresetCooldownSystem.IsReady((Character)(object)attack.m_character, presetName, cooldown); bool flag = MeleeProjectileHitCascadeSystem.HasPendingSpearRain((Character)(object)attack.m_character); if (num && !flag) { return true; } attack.Stop(); return false; } if (MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, presetName, cooldown)) { MarkProjectilePresetCooldownConsumed(attack); return true; } attack.Stop(); return false; } internal static bool IsProjectilePresetOriginalCooldownFallback(Attack? attack) { ProjectilePresetOriginalCooldownFallbackState value; if (attack != null) { return ProjectilePresetOriginalCooldownFallbackAttacks.TryGetValue(attack, out value); } return false; } private static bool TryPrepareOriginalSecondaryForProjectilePresetCooldown(Humanoid humanoid, ItemData currentWeapon, Attack? secondaryBeforeRuntimeApply, ref bool result, out StartAttackState state, out bool runOriginal) { state = StartAttackState.Empty; runOriginal = true; if (!TryResolveProjectilePresetCooldown(currentWeapon, out string presetName, out MeleePresetCooldownDefinition cooldown, out SecondaryAttackDefinition definition)) { return false; } bool num = MeleePresetCooldownSystem.IsReady((Character)(object)humanoid, presetName, cooldown); bool flag = IsSpearRainPreset(presetName) && MeleeProjectileHitCascadeSystem.HasPendingSpearRain((Character)(object)humanoid); if (num && !flag) { if (ProjectilePresetCooldownPolicy.UsesDynamicOriginalSecondary(definition)) { return TryPrepareDynamicProjectilePresetSecondary(currentWeapon, definition, ref result, out state, out runOriginal); } return false; } if (ProjectilePresetCooldownPolicy.UsesDynamicOriginalSecondary(definition) && TryPrepareDynamicOriginalSecondaryFallback(currentWeapon, definition, out state)) { return true; } if (!TryResolveOriginalSecondaryAttack(currentWeapon, definition, secondaryBeforeRuntimeApply, definition.ConfiguredSecondaryAttack ?? currentWeapon.m_shared?.m_secondaryAttack, out Attack originalSecondaryAttack)) { result = false; runOriginal = false; return true; } if (currentWeapon.m_shared == null) { result = false; runOriginal = false; return true; } Attack configuredSecondaryAttack = SecondaryAttackManager.CloneAttack(currentWeapon.m_shared.m_secondaryAttack); currentWeapon.m_shared.m_secondaryAttack = SecondaryAttackManager.CloneAttack(originalSecondaryAttack); state = new StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext.Empty, currentWeapon, configuredSecondaryAttack, skipActiveRegistration: true, originalCooldownFallback: true); return true; } private static bool TryPrepareDynamicProjectilePresetSecondary(ItemData currentWeapon, SecondaryAttackDefinition definition, ref bool result, out StartAttackState state, out bool runOriginal) { state = StartAttackState.Empty; runOriginal = true; if (currentWeapon.m_shared == null || !HasUsableSecondaryAttack(definition.ConfiguredSecondaryAttack)) { result = false; runOriginal = false; return true; } Attack configuredSecondaryAttack = SecondaryAttackManager.CloneAttack(currentWeapon.m_shared.m_secondaryAttack); currentWeapon.m_shared.m_secondaryAttack = SecondaryAttackManager.CloneAttack(definition.ConfiguredSecondaryAttack); state = new StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext.Empty, currentWeapon, configuredSecondaryAttack); return true; } private static bool TryPrepareDynamicOriginalSecondaryFallback(ItemData currentWeapon, SecondaryAttackDefinition definition, out StartAttackState state) { state = StartAttackState.Empty; Attack val = currentWeapon.m_shared?.m_secondaryAttack; if (!HasUsableSecondaryAttack(val) || HasSameAttackShape(val, definition.ConfiguredSecondaryAttack)) { return false; } state = new StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext.Empty, null, null, skipActiveRegistration: true, originalCooldownFallback: true); return true; } private static bool TryResolveProjectilePresetCooldown(ItemData currentWeapon, out string presetName, out MeleePresetCooldownDefinition? cooldown, out SecondaryAttackDefinition? definition) { presetName = ""; cooldown = null; definition = null; if (!SecondaryAttackRuntimeFacade.TryGetDefinition(currentWeapon, out SecondaryAttackDefinition definition2) || !(definition2.Behavior is CopiedSecondaryBehavior)) { return false; } definition = definition2; if (definition2.Boomerang != null) { presetName = "boomerang"; cooldown = definition2.Boomerang.PresetCooldown; return true; } if (definition2.OnProjectileHit == null || (!definition2.OnProjectileHit.Preset.Equals("impactBurst", StringComparison.OrdinalIgnoreCase) && !definition2.OnProjectileHit.Preset.Equals("spearRain", StringComparison.OrdinalIgnoreCase))) { return false; } presetName = definition2.OnProjectileHit.Preset; cooldown = definition2.OnProjectileHit.PresetCooldown; return true; } private static bool IsSpearRainPreset(string presetName) { return presetName.Equals("spearRain", StringComparison.OrdinalIgnoreCase); } private static void MarkProjectilePresetCooldownConsumed(Attack attack) { ProjectilePresetCooldownConsumedAttacks.Remove(attack); ProjectilePresetCooldownConsumedAttacks.Add(attack, new ProjectilePresetCooldownConsumedState()); } private static void MarkProjectilePresetOriginalCooldownFallback(Attack attack) { ProjectilePresetOriginalCooldownFallbackAttacks.Remove(attack); ProjectilePresetOriginalCooldownFallbackAttacks.Add(attack, new ProjectilePresetOriginalCooldownFallbackState()); } private static bool TryResolveOriginalSecondaryAttack(ItemData currentWeapon, SecondaryAttackDefinition definition, Attack? secondaryBeforeRuntimeApply, Attack? configuredSecondaryAttack, out Attack? originalSecondaryAttack) { originalSecondaryAttack = null; bool rejectCopiedCarrier = ShouldRejectMatchingCopiedCarrier(currentWeapon, definition); if (HasUsableSecondaryAttack(secondaryBeforeRuntimeApply) && !ShouldSkipOriginalSecondaryCandidate(secondaryBeforeRuntimeApply, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = SecondaryAttackManager.CloneAttack(secondaryBeforeRuntimeApply); return true; } if (HasUsableSecondaryAttack(definition.CooldownFallbackSecondaryAttack) && !ShouldSkipOriginalSecondaryCandidate(definition.CooldownFallbackSecondaryAttack, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = SecondaryAttackManager.CloneAttack(definition.CooldownFallbackSecondaryAttack); return true; } if ((Object)(object)ObjectDB.instance != (Object)null && (Object)(object)currentWeapon?.m_dropPrefab != (Object)null && currentWeapon.m_shared != null && SecondaryAttackObjectDbStateStore.TryGetOriginalSecondaryAttack(ObjectDB.instance, ((Object)currentWeapon.m_dropPrefab).name, out Attack attack) && HasUsableSecondaryAttack(attack) && !ShouldSkipOriginalSecondaryCandidate(attack, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = attack; return true; } return false; } private static bool ShouldRejectMatchingCopiedCarrier(ItemData currentWeapon, SecondaryAttackDefinition definition) { if ((Object)(object)currentWeapon?.m_dropPrefab == (Object)null || !(definition.Behavior is CopiedSecondaryBehavior copiedSecondaryBehavior) || string.IsNullOrWhiteSpace(copiedSecondaryBehavior.SourcePrefabName)) { return false; } return !string.Equals(copiedSecondaryBehavior.SourcePrefabName.Trim(), ((Object)currentWeapon.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase); } private static bool ShouldSkipOriginalSecondaryCandidate(Attack? candidate, Attack? configuredSecondaryAttack, bool rejectCopiedCarrier) { if (rejectCopiedCarrier) { return HasSameAttackShape(candidate, configuredSecondaryAttack); } return false; } private static bool HasSameAttackShape(Attack? left, Attack? right) { //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) if (left == null || right == null) { return false; } if (left.m_attackType == right.m_attackType && string.Equals(left.m_attackAnimation?.Trim() ?? "", right.m_attackAnimation?.Trim() ?? "", StringComparison.OrdinalIgnoreCase)) { GameObject attackProjectile = left.m_attackProjectile; string a = ((attackProjectile != null) ? ((Object)attackProjectile).name : null) ?? ""; GameObject attackProjectile2 = right.m_attackProjectile; return string.Equals(a, ((attackProjectile2 != null) ? ((Object)attackProjectile2).name : null) ?? "", StringComparison.OrdinalIgnoreCase); } return false; } private static bool HasUsableSecondaryAttack(Attack? attack) { if (attack != null) { return !string.IsNullOrWhiteSpace(attack.m_attackAnimation); } return false; } private static void RegisterActiveAttackIfNeeded(Humanoid humanoid) { ItemData currentWeapon = humanoid.GetCurrentWeapon(); if (currentWeapon != null) { SecondaryAttackRuntimeFacade.RegisterActiveAttack(humanoid.m_currentAttack, currentWeapon); } } } internal sealed class SecondaryAttacksCharacterRpc : MonoBehaviour { private Character _character; private ZNetView? _nview; private void Awake() { _character = ((Component)this).GetComponent(); _nview = ((Component)this).GetComponent(); if (!((Object)(object)_nview == (Object)null) && _nview.IsValid()) { _nview.Register("SecondaryAttacks_ApplySummonEmpower", (Action)RPC_ApplySummonEmpower); _nview.Register("SecondaryAttacks_ConvertShieldToHeal", (Action)RPC_ConvertShieldToHeal); _nview.Register("SecondaryAttacks_GrantBackstabSneakSkill", (Action)RPC_GrantBackstabSneakSkill); _nview.Register("SecondaryAttacks_SpawnSneakAmbushVfx", (Action)RPC_SpawnSneakAmbushVfx); _nview.Register("SecondaryAttacks_ApplySneakAmbushSmoke", (Method)RPC_ApplySneakAmbushSmoke); _nview.Register("SecondaryAttacks_SweepVisual", (Action)RPC_SweepVisual); _nview.Register("SecondaryAttacks_RiftTrailVisual", (Action)RPC_RiftTrailVisual); _nview.Register("SecondaryAttacks_CleavingThrustVisualSessionV2", (Method)RPC_CleavingThrustVisualSession); _nview.Register("SecondaryAttacks_SpawnStaffTargetEffect", (Action)RPC_SpawnStaffTargetEffect); } } private void RPC_ApplySummonEmpower(long sender, float expiry, float moveSpeedFactor, float attackSpeedFactor) { if (!((Object)(object)_character == (Object)null) && !((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner()) { StaffRuntimeSystem.ApplySummonEmpowerState(_character, expiry, moveSpeedFactor, attackSpeedFactor); } } private void RPC_ConvertShieldToHeal(long sender, float healFactor, int shieldStatusEffectHash) { if (!((Object)(object)_character == (Object)null) && !((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner()) { StaffRuntimeSystem.ApplyShieldConvertToCharacter(_character, healFactor, shieldStatusEffectHash); } } private void RPC_GrantBackstabSneakSkill(long sender, float amount) { Character character = _character; Player val = (Player)(object)((character is Player) ? character : null); if (val != null && !((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner()) { BackstabSkillGainSystem.GrantLocal(val, amount); } } private void RPC_SpawnSneakAmbushVfx(long sender, Vector3 position, float yaw) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) SneakAmbushSystem.SpawnFromRpc(_character, position, yaw); } private void RPC_ApplySneakAmbushSmoke(long sender, ZDOID hiddenPlayerId, float range, float senseBlockDuration, float backstabResetSeconds) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) SneakAmbushSystem.ApplyOwnerSmokeFromRpc(_character, _nview, hiddenPlayerId, range, senseBlockDuration, backstabResetSeconds); } private void RPC_SweepVisual(long sender, int action, string payload) { SweepObserverVisualSystem.HandleRpc(_character, _nview, action, payload); } private void RPC_RiftTrailVisual(long sender, Vector3 origin, Vector3 forward, string payload) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) RiftTrailSystem.HandleObserverFallbackVisualRpc(_character, _nview, origin, forward, payload); } private void RPC_CleavingThrustVisualSession(long sender, uint sequence, float rangeScale, double startedAt, double expiresAt) { GreatSwordSkillScalingSystem.HandleCleavingThrustVisualSessionRpc(_character, _nview, sender, sequence, rangeScale, startedAt, expiresAt); } private void RPC_SpawnStaffTargetEffect(long sender) { StaffRuntimeSystem.CreateStaffTargetEffect(_character); } } [HarmonyPatch(typeof(BaseAI), "CanSenseTarget", new Type[] { typeof(Character) })] internal static class BaseAICanSenseTargetSneakAmbushPatch { private static bool Prefix(BaseAI __instance, Character target, ref bool __result) { if (!SneakAmbushSystem.ShouldBlockSense(__instance, target)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(BaseAI), "CanSenseTarget", new Type[] { typeof(Character), typeof(bool) })] internal static class BaseAICanSenseTargetPassiveSneakAmbushPatch { private static bool Prefix(BaseAI __instance, Character target, ref bool __result) { if (!SneakAmbushSystem.ShouldBlockSense(__instance, target)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Character) })] internal static class BaseAICanSeeTargetSneakAmbushPatch { private static bool Prefix(BaseAI __instance, Character target, ref bool __result) { if (!SneakAmbushSystem.ShouldBlockSense(__instance, target)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(BaseAI), "CanHearTarget", new Type[] { typeof(Character) })] internal static class BaseAICanHearTargetSneakAmbushPatch { private static bool Prefix(BaseAI __instance, Character target, ref bool __result) { if (!SneakAmbushSystem.ShouldBlockSense(__instance, target)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Projectile), "UpdateVisual")] internal static class ProjectileUpdateVisualPatch { private static void Prefix(Projectile __instance) { CopiedThrowProjectileVisualSystem.PrepareProjectileIfNeeded(__instance); } private static void Postfix(Projectile __instance) { CopiedThrowProjectileVisualSystem.EnsureProjectileVisualSpinIfNeeded(__instance); } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class ProjectileSetupCopiedThrowVisualPatch { [HarmonyPriority(0)] private static void Postfix(Projectile __instance, ItemData item) { SecondaryAttackRuntimeFacade.TryApplyProjectileSetupAttribution(__instance, item); SecondaryAttackAdrenalineSystem.TryApplyAttackUseAdrenalineProjectileHitConversion(__instance, item); CopiedThrowProjectileVisualSystem.TryApplyToProjectileSetup(__instance, item); OverchargedBombSystem.ScaleCreatedVisuals(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(Aoe), "Setup")] internal static class AoeSetupOverchargedBombVisualPatch { [HarmonyPriority(0)] private static void Postfix(Aoe __instance) { OverchargedBombSystem.ScaleCreatedVisuals(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(EffectList), "Create")] internal static class EffectListCreateOverchargedBombVisualPatch { [HarmonyPriority(0)] private static void Postfix(GameObject[] __result) { OverchargedBombSystem.ScaleCreatedVisuals(__result); } } [HarmonyPatch(typeof(Projectile), "OnHit")] internal static class ProjectileOnHitPatch { [HarmonyPriority(0)] private static bool Prefix(Projectile __instance, Collider collider, Vector3 hitPoint, bool water, Vector3 normal, out SecondaryAttackHarmonyDispatch.ProjectileOnHitState __state) { //IL_0002: 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 SecondaryAttackHarmonyDispatch.ProjectileOnHitPrefix(__instance, collider, hitPoint, water, normal, out __state); } [HarmonyPriority(800)] private static void Postfix(Projectile __instance, Collider collider, Vector3 hitPoint, bool water, Vector3 normal, ref SecondaryAttackHarmonyDispatch.ProjectileOnHitState __state) { //IL_0002: 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) SecondaryAttackHarmonyDispatch.ProjectileOnHitPostfix(__instance, collider, hitPoint, water, normal, ref __state); } private static void Finalizer(ref SecondaryAttackHarmonyDispatch.ProjectileOnHitState __state) { SecondaryAttackHarmonyDispatch.ProjectileOnHitFinalizer(ref __state); } } [HarmonyPatch(typeof(Projectile), "OnHit")] internal static class ProjectileOnHitOverchargedBombPatch { [HarmonyPriority(800)] private static void Prefix(Projectile __instance, out OverchargedBombSystem.ProjectileHitScaleState __state) { __state = OverchargedBombSystem.BeginProjectileHit(__instance); } [HarmonyPriority(0)] private static void Postfix(ref OverchargedBombSystem.ProjectileHitScaleState __state) { OverchargedBombSystem.EndProjectileHit(ref __state); } [HarmonyPriority(0)] private static void Finalizer(ref OverchargedBombSystem.ProjectileHitScaleState __state) { OverchargedBombSystem.EndProjectileHit(ref __state); } } [HarmonyPatch(typeof(Destructible), "Damage")] internal static class DestructibleDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5DamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(TreeBase), "Damage")] internal static class TreeBaseDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(TreeLog), "Damage")] internal static class TreeLogDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class WearNTearDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class CharacterAwakeSecondaryAttacksPatch { private static void Postfix(Character __instance) { if (!((Object)(object)((Component)__instance).GetComponent() == (Object)null) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch(typeof(EnemyHud), "LateUpdate")] internal static class EnemyHudLateUpdatePatch { private static void Postfix(EnemyHud __instance) { OverheadStatusUiManager.Update(__instance); SummonQualityHudSystem.Update(__instance); } } [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerUpdatePendingConfigPatch { private static void Postfix(Player __instance, bool ___m_attackHold, bool ___m_secondaryAttackHold, ref bool ___m_blocking) { SecondaryAttackHarmonyDispatch.PlayerUpdatePostfix(__instance, ___m_attackHold, ___m_secondaryAttackHold, ref ___m_blocking); } } [HarmonyPatch(typeof(Humanoid), "IsBlocking")] internal static class HumanoidIsBlockingStickyDetonatorPatch { private static bool Prefix(Humanoid __instance, ref bool __result) { if (!StickyDetonatorSystem.ShouldSuppressBlock(__instance)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(SEMan), "Internal_AddStatusEffect")] internal static class SEManInternalAddStatusEffectShieldDisplayPatch { private static void Postfix(SEMan __instance, int nameHash) { if (__instance.GetStatusEffect(nameHash) is SE_Shield) { Character seManCharacter = ShieldAccess.GetSeManCharacter(__instance); if ((Object)(object)seManCharacter != (Object)null) { StaffRuntimeSystem.SyncShieldDisplayState(seManCharacter); } } } } [HarmonyPatch(typeof(SEMan), "RemoveStatusEffect", new Type[] { typeof(int), typeof(bool) })] internal static class SEManRemoveStatusEffectShieldDisplayPatch { private static void Prefix(SEMan __instance, int nameHash, out bool __state) { __state = __instance.GetStatusEffect(nameHash) is SE_Shield; } private static void Postfix(SEMan __instance, bool __result, bool __state) { if (__result && __state) { Character seManCharacter = ShieldAccess.GetSeManCharacter(__instance); if ((Object)(object)seManCharacter != (Object)null) { StaffRuntimeSystem.SyncShieldDisplayState(seManCharacter); } } } } [HarmonyPatch(typeof(SE_Shield), "OnDamaged")] internal static class SEShieldOnDamagedShieldDisplayPatch { private static void Postfix(SE_Shield __instance) { if (!((Object)(object)((StatusEffect)__instance).m_character == (Object)null)) { StaffRuntimeSystem.SyncShieldDisplayState(((StatusEffect)__instance).m_character); } } } [HarmonyPatch(typeof(SE_Shield), "IsDone")] internal static class SEShieldIsDoneShieldDisplayPatch { private static void Postfix(SE_Shield __instance, bool __result) { if (__result && !((Object)(object)((StatusEffect)__instance).m_character == (Object)null)) { StaffRuntimeSystem.SyncShieldDisplayState(((StatusEffect)__instance).m_character); } } } [HarmonyPatch(typeof(Character), "UseHealth")] internal static class CharacterUseHealthBloodMagicSkillGainPatch { private static void Prefix(Character __instance, out float __state) { __state = (((Object)(object)__instance != (Object)null) ? __instance.GetHealth() : 0f); } private static void Postfix(Character __instance, float __state) { if ((Object)(object)__instance != (Object)null) { BloodMagicSkillGainSystem.TryGrantForHealthUse(__instance, __state); } } } [HarmonyPatch(typeof(Attack), "GetAttackHealth")] internal static class AttackGetAttackHealthBloodMagicCostPatch { private static void Postfix(Attack __instance, ref float __result) { BloodMagicSkillGainSystem.ApplyMaxHealthPercentageCost(__instance, ref __result); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CharacterRpcDamageBackstabSkillGainPatch { private static void Prefix(Character __instance, HitData hit, out BackstabSkillGainSystem.BackstabDamageState __state) { __state = BackstabSkillGainSystem.CaptureBackstabState(__instance, hit); } private static void Postfix(Character __instance, HitData hit, BackstabSkillGainSystem.BackstabDamageState __state) { BackstabSkillGainSystem.TryGrantForBackstab(__instance, hit, __state); } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class SkillsRaiseSkillBloodMagicPatch { private static bool Prefix(SkillType skillType, ref float factor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) HarvestSweepSystem.ApplySkillRaiseFactor(skillType, ref factor); return true; } } [HarmonyPatch(typeof(Humanoid), "GetTimeSinceLastAttack")] internal static class HumanoidGetTimeSinceLastAttackPatch { private static void Postfix(Humanoid __instance, ref float __result) { if (StaffRuntimeSystem.TryGetSummonEmpowerAttackSpeedFactor((Character?)(object)__instance, out var attackSpeedFactor)) { __result *= attackSpeedFactor; } } } [HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")] internal static class CharacterAnimEventCustomFixedUpdateSummonEmpowerAttackSpeedPatch { [HarmonyPriority(0)] private static void Postfix(CharacterAnimEvent __instance) { if (!((Object)(object)__instance?.m_character == (Object)null) && !((Object)(object)__instance.m_animator == (Object)null) && __instance.m_character.InAttack() && !(__instance.m_animator.speed <= 0.001f) && StaffRuntimeSystem.TryGetSummonEmpowerAttackSpeedFactor(__instance.m_character, out var attackSpeedFactor)) { __instance.m_animator.speed = Mathf.Max(__instance.m_animator.speed, attackSpeedFactor); } } } [HarmonyPatch(typeof(Attack), "Start")] internal static class AttackStartCooldownAdjustmentPatch { private readonly struct AttackStartState { internal float OriginalAttackUseAdrenaline { get; } internal bool Pending { get; } internal AttackStartState(float originalAttackUseAdrenaline) { OriginalAttackUseAdrenaline = originalAttackUseAdrenaline; Pending = true; } } private static void Prefix(Attack __instance, out AttackStartState __state) { __state = ((__instance != null) ? new AttackStartState(__instance.m_attackUseAdrenaline) : default(AttackStartState)); if (__instance != null && (SecondaryAttackAdrenalineSystem.ShouldSuppressAttackUseAdrenaline(__instance) || SecondaryAttackAdrenalineSystem.TryBeginAttackUseAdrenalineProjectileHitConversion(__instance))) { __instance.m_attackUseAdrenaline = 0f; } } private static void Postfix(Attack __instance, bool __result, ref AttackStartState __state) { if (__instance != null) { RestoreAttackUseAdrenaline(__instance, ref __state); if (__result && !((Object)(object)__instance.m_character == (Object)null) && __instance.m_weapon != null && StaffRuntimeSystem.TryGetSummonEmpowerAttackSpeedFactor((Character?)(object)__instance.m_character, out var attackSpeedFactor)) { float num = 1f - 1f / attackSpeedFactor; ItemData weapon = __instance.m_weapon; weapon.m_lastAttackTime -= __instance.m_weapon.m_shared.m_aiAttackInterval * num; } } } private static void Finalizer(Attack __instance, ref AttackStartState __state) { if (__instance != null) { RestoreAttackUseAdrenaline(__instance, ref __state); } } private static void RestoreAttackUseAdrenaline(Attack attack, ref AttackStartState state) { if (state.Pending) { attack.m_attackUseAdrenaline = state.OriginalAttackUseAdrenaline; state = default(AttackStartState); } } } [HarmonyPatch(typeof(Player), "AddAdrenaline")] internal static class PlayerAddAdrenalineSecondaryAttackPatch { private static bool Prefix(Player __instance, ref float v) { return SecondaryAttackAdrenalineSystem.TryModify((Character)(object)__instance, ref v); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDbAwakePatch { private static void Postfix(ObjectDB __instance) { SecondaryAttackFacade.ApplyPendingConfigToObjectDb(__instance, emitMissingWarnings: false); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDbCopyOtherDbPatch { private static void Postfix(ObjectDB __instance) { SecondaryAttackFacade.ApplyPendingConfigToObjectDb(__instance, emitMissingWarnings: false); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyAfter(new string[] { "blacks7ar.MagicPlugin" })] internal static class ZNetSceneAwakeSummonPrefabOverridePatch { private static void Postfix(ZNetScene __instance) { SecondaryAttackFacade.ApplyPendingConfigToZNetScene(__instance, emitMissingWarnings: false); } } [HarmonyPatch(typeof(Player), "UpdateAttackBowDraw")] internal static class PlayerUpdateAttackBowDrawPatch { private static bool Prefix(Player __instance, ItemData weapon, float dt, ref float ___m_attackDrawTime, ref bool ___m_blocking, ref bool ___m_attackHold, ref bool ___m_secondaryAttackHold, ref bool ___m_secondaryAttack, ZSyncAnimation ___m_zanim, SEMan ___m_seman) { if (weapon == null || !SecondaryAttackRuntimeFacade.ShouldHandleBowDraw(weapon)) { return true; } SecondaryAttackManager.UpdateCustomBowDraw(__instance, weapon, dt, ref ___m_attackDrawTime, ___m_blocking, ___m_attackHold, ___m_secondaryAttackHold, ___m_secondaryAttack, ___m_zanim, ___m_seman); return false; } } [HarmonyPatch(typeof(Player), "UpdateWeaponLoading")] internal static class PlayerUpdateWeaponLoadingReloadStatePatch { private static void Prefix(Player __instance, ItemData weapon) { SecondaryAttackManager.TryRestorePersistedReloadedWeaponState(__instance, weapon); } } [HarmonyPatch(typeof(Player), "SetWeaponLoaded")] internal static class PlayerSetWeaponLoadedReloadStatePatch { private static void Prefix(Player __instance, out ItemData? __state) { __state = __instance.m_weaponLoaded; } private static void Postfix(Player __instance, ItemData weapon, ItemData? __state) { SecondaryAttackManager.OnWeaponLoadedStateChanged(__instance, __state, weapon); } } [HarmonyPatch(typeof(Player), "Awake")] internal static class PlayerAwakeAnimationDumpPatch { private static void Postfix(Player __instance) { SecondaryAttackManager.DumpPlayerAnimatorReferences(__instance); } } [HarmonyPatch(typeof(Player), "Start")] internal static class PlayerStartCustomAnimationDumpPatch { private static void Postfix(Player __instance) { SecondaryAttackManager.DumpCustomAnimationReferences(__instance); } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class HumanoidStartAttackPatch { private static bool Prefix(Humanoid __instance, bool secondaryAttack, ref bool __result, out SecondaryAttackStartAttackDispatch.StartAttackState __state) { return SecondaryAttackStartAttackDispatch.Prefix(__instance, secondaryAttack, ref __result, out __state); } private static void Postfix(Humanoid __instance, bool secondaryAttack, bool __result, SecondaryAttackStartAttackDispatch.StartAttackState __state) { SecondaryAttackStartAttackDispatch.Postfix(__instance, secondaryAttack, __result, __state); } private static Exception? Finalizer(Exception? __exception, Humanoid __instance, bool secondaryAttack, SecondaryAttackStartAttackDispatch.StartAttackState __state) { if (__exception != null) { try { SecondaryAttackStartAttackDispatch.Cleanup(__instance, secondaryAttack, __state); } catch (Exception ex) { SecondaryAttacksPlugin.ModLogger.LogError((object)("Failed to clean up an interrupted StartAttack: " + ex.Message)); } } return __exception; } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class AttackOnAttackTriggerPatch { [HarmonyPriority(800)] private static bool Prefix(Attack __instance, out SecondaryAttackManager.ReloadStateConsumptionScope __state) { __state = SecondaryAttackManager.BeginReloadStateConsumption(__instance); return !SecondaryAttackRuntimeFacade.TryHandleCustomAttackTrigger(__instance); } private static void Postfix(Attack __instance, ref SecondaryAttackManager.ReloadStateConsumptionScope __state) { SecondaryAttackManager.EndReloadStateConsumption(ref __state); SecondaryAttackRuntimeFacade.TryTriggerRiftTrailAfterAttack(__instance); SecondaryAttackRuntimeFacade.TryTriggerFractureLineAfterAttack(__instance); } private static void Finalizer(ref SecondaryAttackManager.ReloadStateConsumptionScope __state) { SecondaryAttackManager.EndReloadStateConsumption(ref __state); } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] internal static class AttackDoMeleeAttackSecondaryDurabilityFactorPatch { private static void Prefix(Attack __instance, out SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { __state = SecondaryAttackManager.BeginSecondaryAttackDurabilityAdjustment(__instance); } private static void Postfix(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } private static void Finalizer(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } } [HarmonyPatch(typeof(Attack), "DoAreaAttack")] internal static class AttackDoAreaAttackSecondaryDurabilityFactorPatch { private static void Prefix(Attack __instance, out SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { __state = SecondaryAttackManager.BeginSecondaryAttackDurabilityAdjustment(__instance); } private static void Postfix(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } private static void Finalizer(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } } [HarmonyPatch(typeof(Attack), "ProjectileAttackTriggered")] internal static class AttackProjectileAttackTriggeredSecondaryDurabilityFactorPatch { private static void Prefix(Attack __instance, out SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { __state = SecondaryAttackManager.BeginSecondaryAttackDurabilityAdjustment(__instance); } private static void Postfix(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } private static void Finalizer(ref SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(ref __state); } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] internal static class AttackFireProjectileBurstPatch { private static bool Prefix(Attack __instance, out CopiedThrowProjectileVisualSystem.BurstScope __state) { return SecondaryAttackHarmonyDispatch.AttackFireProjectileBurstPrefix(__instance, out __state); } private static void Postfix(ref CopiedThrowProjectileVisualSystem.BurstScope __state) { SecondaryAttackHarmonyDispatch.AttackFireProjectileBurstPostfix(ref __state); } private static void Finalizer(ref CopiedThrowProjectileVisualSystem.BurstScope __state) { SecondaryAttackHarmonyDispatch.AttackFireProjectileBurstPostfix(ref __state); } } internal static class SecondaryAttackObjectDbStateStore { private static readonly ConditionalWeakTable> Snapshots = new ConditionalWeakTable>(); public static void CaptureSecondaryAttack(ObjectDB objectDb, string prefabName, Attack? secondaryAttack) { if (!((Object)(object)objectDb == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { Dictionary value = Snapshots.GetValue(objectDb, (ObjectDB _) => new Dictionary(StringComparer.OrdinalIgnoreCase)); string key = prefabName.Trim(); if (!value.ContainsKey(key)) { value[key] = SecondaryAttackManager.CloneAttack(secondaryAttack); } } } public static void Restore(ObjectDB objectDb) { if (!Snapshots.TryGetValue(objectDb, out Dictionary value)) { return; } foreach (GameObject item in objectDb.m_items) { if (!((Object)(object)item == (Object)null)) { ItemDrop component = item.GetComponent(); if (!((Object)(object)component == (Object)null) && value.TryGetValue(((Object)item).name, out var value2)) { component.m_itemData.m_shared.m_secondaryAttack = SecondaryAttackManager.CloneAttack(value2); } } } value.Clear(); } public static bool TryGetOriginalSecondaryAttack(ObjectDB objectDb, string prefabName, out Attack? attack) { attack = null; if ((Object)(object)objectDb == (Object)null || string.IsNullOrWhiteSpace(prefabName) || !Snapshots.TryGetValue(objectDb, out Dictionary value) || !value.TryGetValue(prefabName.Trim(), out var value2)) { return false; } attack = SecondaryAttackManager.CloneAttack(value2); return true; } } internal static class StaffRuntimeSystem { internal const string StaffTargetEffectRpcName = "SecondaryAttacks_SpawnStaffTargetEffect"; private const string SummonEmpowerExpiryZdoKey = "SecondaryAttacks_SummonEmpowerExpiry"; private const string SummonEmpowerMoveSpeedBonusZdoKey = "SecondaryAttacks_SummonEmpowerMoveSpeedBonus"; private const string SummonEmpowerAttackCooldownReductionZdoKey = "SecondaryAttacks_SummonEmpowerAttackCooldownReduction"; private const string ShieldRemainingDisplayZdoKey = "SecondaryAttacks_ShieldRemainingDisplay"; private const string ShieldDisplayExpiryZdoKey = "SecondaryAttacks_ShieldDisplayExpiry"; private const string ApplySummonEmpowerRpcName = "SecondaryAttacks_ApplySummonEmpower"; private const string ConvertShieldToHealRpcName = "SecondaryAttacks_ConvertShieldToHeal"; private const string SummonEmpowerPresetName = "summonEmpower"; private const string ShieldConvertPresetName = "shieldConvert"; private const string StaffTargetEffectPrefabName = "fx_bloodweapon_hit"; internal static bool TryTriggerStaffSpecialFromRuntimeFacade(Attack attack, ActiveSecondaryAttack activeAttack) { if (activeAttack.Triggered) { return true; } if (!TryConsumeStaffSpecialCooldown(attack, activeAttack.Definition)) { return false; } activeAttack.Triggered = true; SecondaryAttackManager.PlayTriggeredAttackEffects(attack, activeAttack.Definition.DurabilityFactor); if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.SummonEmpower) { StartSummonEmpower(attack, activeAttack.Definition); return true; } if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.ShieldConvert) { StartShieldConvert(attack, activeAttack.Definition); } return true; } private static bool TryConsumeStaffSpecialCooldown(Attack attack, SecondaryAttackDefinition definition) { if ((Object)(object)attack?.m_character == (Object)null) { return true; } if (!TryResolveStaffSpecialCooldown(definition, out string presetName, out MeleePresetCooldownDefinition cooldown)) { return true; } return MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, presetName, cooldown); } internal static bool CanStartStaffSpecial(Humanoid humanoid, ItemData weapon) { if ((Object)(object)humanoid == (Object)null || weapon == null) { return true; } SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(weapon); if (!SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out SecondaryAttackDefinition definition) || !TryResolveStaffSpecialCooldown(definition, out string presetName, out MeleePresetCooldownDefinition cooldown)) { return true; } return MeleePresetCooldownSystem.IsReady((Character)(object)humanoid, presetName, cooldown); } private static bool TryResolveStaffSpecialCooldown(SecondaryAttackDefinition definition, out string presetName, out MeleePresetCooldownDefinition cooldown) { if (definition.Behavior is SummonEmpowerSecondaryBehavior summonEmpowerSecondaryBehavior) { presetName = "summonEmpower"; cooldown = summonEmpowerSecondaryBehavior.PresetCooldown; return true; } if (definition.Behavior is ShieldConvertSecondaryBehavior shieldConvertSecondaryBehavior) { presetName = "shieldConvert"; cooldown = shieldConvertSecondaryBehavior.PresetCooldown; return true; } presetName = ""; cooldown = null; return false; } private static void StartSummonEmpower(Attack attack, SecondaryAttackDefinition definition) { //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_007e: Unknown result type (might be due to invalid IL or missing references) Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val == null || !(definition.Behavior is SummonEmpowerSecondaryBehavior summonEmpowerSecondaryBehavior)) { return; } float moveSpeedFactor = Mathf.Max(0.05f, summonEmpowerSecondaryBehavior.MoveSpeedFactor); float attackSpeedFactor = Mathf.Max(0.05f, summonEmpowerSecondaryBehavior.AttackSpeedFactor); float expiry = (float)SecondaryAttackManager.GetNetworkTimeSeconds() + Mathf.Max(0.1f, summonEmpowerSecondaryBehavior.Duration); Vector3 centerPoint = ((Character)val).GetCenterPoint(); foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsMatchingSummonEmpowerTarget(val, allCharacter, definition, centerPoint)) { ApplySummonEmpower(allCharacter, expiry, moveSpeedFactor, attackSpeedFactor); } } } private static void StartShieldConvert(Attack attack, SecondaryAttackDefinition definition) { //IL_0021: 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_0046: Unknown result type (might be due to invalid IL or missing references) Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val == null || !(definition.Behavior is ShieldConvertSecondaryBehavior shieldConvertSecondaryBehavior)) { return; } Vector3 centerPoint = ((Character)val).GetCenterPoint(); foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsValidShieldConvertTarget(val, allCharacter, shieldConvertSecondaryBehavior.Radius, centerPoint)) { ConvertShieldToHeal(allCharacter, shieldConvertSecondaryBehavior.HealFactor, shieldConvertSecondaryBehavior.ShieldStatusEffectHash); } } } private static bool IsMatchingSummonEmpowerTarget(Player player, Character candidate, SecondaryAttackDefinition definition, Vector3 origin) { //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_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)candidate == (Object)null || candidate.IsDead() || candidate.IsPlayer()) { return false; } if (!(definition.Behavior is SummonEmpowerSecondaryBehavior summonEmpowerSecondaryBehavior)) { return false; } Vector3 val = candidate.GetCenterPoint() - origin; if (((Vector3)(ref val)).sqrMagnitude > summonEmpowerSecondaryBehavior.Radius * summonEmpowerSecondaryBehavior.Radius) { return false; } string prefabName = Utils.GetPrefabName(((Component)candidate).gameObject); return summonEmpowerSecondaryBehavior.SummonSourcePrefabs.Any((string sourcePrefab) => string.Equals(sourcePrefab, prefabName, StringComparison.OrdinalIgnoreCase)); } private static bool IsValidShieldConvertTarget(Player player, Character candidate, float radius, Vector3 origin) { //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_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) if ((Object)(object)candidate == (Object)null || candidate.IsDead()) { return false; } Vector3 val = candidate.GetCenterPoint() - origin; if (((Vector3)(ref val)).sqrMagnitude > radius * radius) { return false; } if (!((Object)(object)candidate == (Object)(object)player)) { return !BaseAI.IsEnemy((Character)(object)player, candidate); } return true; } private static void ApplySummonEmpower(Character target, float expiry, float moveSpeedFactor, float attackSpeedFactor) { if (!((Object)(object)target == (Object)null)) { ZNetView nview; ZDO zdo; if (SecondaryAttackManager.HasCharacterAuthority(target)) { ApplySummonEmpowerState(target, expiry, moveSpeedFactor, attackSpeedFactor); } else if (SecondaryAttackManager.TryGetCharacterZdo(target, out nview, out zdo)) { nview.InvokeRPC("SecondaryAttacks_ApplySummonEmpower", new object[3] { expiry, moveSpeedFactor, attackSpeedFactor }); } } } private static void ConvertShieldToHeal(Character target, float healFactor, int shieldStatusEffectHash) { if (!((Object)(object)target == (Object)null)) { ZNetView nview; ZDO zdo; if (SecondaryAttackManager.HasCharacterAuthority(target)) { ApplyShieldConvertToCharacter(target, healFactor, shieldStatusEffectHash); } else if (SecondaryAttackManager.TryGetCharacterZdo(target, out nview, out zdo)) { nview.InvokeRPC("SecondaryAttacks_ConvertShieldToHeal", new object[2] { healFactor, shieldStatusEffectHash }); } } } internal static void ApplySummonEmpowerState(Character character, float expiry, float moveSpeedFactor, float attackSpeedFactor) { if (SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView _, out ZDO zdo)) { zdo.Set("SecondaryAttacks_SummonEmpowerExpiry", Mathf.Max(0f, expiry)); zdo.Set("SecondaryAttacks_SummonEmpowerMoveSpeedBonus", Mathf.Max(0.05f, moveSpeedFactor)); zdo.Set("SecondaryAttacks_SummonEmpowerAttackCooldownReduction", Mathf.Max(0.05f, attackSpeedFactor)); OverheadStatusUiManager.RefreshTrackedCharacter(character); BroadcastStaffTargetEffect(character); } } internal static bool ApplyShieldConvertToCharacter(Character character, float healFactor, int shieldStatusEffectHash) { if ((Object)(object)character == (Object)null || character.IsDead() || healFactor <= 0f) { return false; } if (!SecondaryAttackManager.TryGetShieldRemaining(character, shieldStatusEffectHash, out SE_Shield shield, out float remaining, out float _)) { SyncShieldDisplayState(character); return false; } float num = Mathf.Max(0f, remaining * healFactor); if (num > 0f) { character.Heal(num, true); } character.GetSEMan().RemoveStatusEffect(((StatusEffect)shield).NameHash(), false); SyncShieldDisplayState(character); BroadcastStaffTargetEffect(character); return true; } internal static void CreateStaffTargetEffect(Character character) { //IL_0010: 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 (!((Object)(object)character == (Object)null)) { SecondaryAttackNamedEffectSystem.Create("fx_bloodweapon_hit", character.GetCenterPoint(), ((Component)character).transform.rotation, "staff_target_effect"); } } private static void BroadcastStaffTargetEffect(Character character) { if (!SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView nview, out ZDO _)) { CreateStaffTargetEffect(character); } else { nview.InvokeRPC(ZNetView.Everybody, "SecondaryAttacks_SpawnStaffTargetEffect", Array.Empty()); } } internal static void SyncShieldDisplayState(Character character) { if (SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView _, out ZDO zdo)) { float num = (float)SecondaryAttackManager.GetNetworkTimeSeconds(); if (SecondaryAttackManager.TryGetShieldRemaining(character, 0, out SE_Shield _, out float remaining, out float remainingTime)) { zdo.Set("SecondaryAttacks_ShieldRemainingDisplay", Mathf.Max(0f, remaining)); zdo.Set("SecondaryAttacks_ShieldDisplayExpiry", num + Mathf.Max(0f, remainingTime)); OverheadStatusUiManager.RefreshTrackedCharacter(character); } else { zdo.Set("SecondaryAttacks_ShieldRemainingDisplay", 0f); zdo.Set("SecondaryAttacks_ShieldDisplayExpiry", 0f); OverheadStatusUiManager.RefreshTrackedCharacter(character); } } } internal static bool TryGetSummonEmpower(Character character, out float moveSpeedFactor, out float attackSpeedFactor, out float remainingTime) { moveSpeedFactor = 1f; attackSpeedFactor = 1f; remainingTime = 0f; if (!SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView _, out ZDO zdo)) { return false; } float num = (float)SecondaryAttackManager.GetNetworkTimeSeconds(); float num2 = zdo.GetFloat("SecondaryAttacks_SummonEmpowerExpiry", 0f); if (num2 <= num) { if (num2 > 0f && SecondaryAttackManager.HasCharacterAuthority(character)) { zdo.Set("SecondaryAttacks_SummonEmpowerExpiry", 0f); zdo.Set("SecondaryAttacks_SummonEmpowerMoveSpeedBonus", 0f); zdo.Set("SecondaryAttacks_SummonEmpowerAttackCooldownReduction", 0f); } return false; } moveSpeedFactor = Mathf.Max(0.05f, zdo.GetFloat("SecondaryAttacks_SummonEmpowerMoveSpeedBonus", 1f)); attackSpeedFactor = Mathf.Max(0.05f, zdo.GetFloat("SecondaryAttacks_SummonEmpowerAttackCooldownReduction", 1f)); remainingTime = num2 - num; return remainingTime > 0f; } internal static bool TryGetSummonEmpowerAttackSpeedFactor(Character? character, out float attackSpeedFactor) { attackSpeedFactor = 1f; if ((Object)(object)character == (Object)null || character.IsPlayer() || !TryGetSummonEmpower(character, out var _, out var attackSpeedFactor2, out var _)) { return false; } attackSpeedFactor = Mathf.Max(1f, attackSpeedFactor2); return !Mathf.Approximately(attackSpeedFactor, 1f); } internal static bool TryGetDisplayedShieldRemaining(Character character, out float remaining) { remaining = 0f; SE_Shield shield; float remainingTime; if (!SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView _, out ZDO zdo)) { return SecondaryAttackManager.TryGetShieldRemaining(character, 0, out shield, out remaining, out remainingTime); } float num = (float)SecondaryAttackManager.GetNetworkTimeSeconds(); if (zdo.GetFloat("SecondaryAttacks_ShieldDisplayExpiry", 0f) <= num) { return false; } remaining = Mathf.Max(0f, zdo.GetFloat("SecondaryAttacks_ShieldRemainingDisplay", 0f)); return remaining > 0f; } } internal static class SecondaryAttackRuntimeWeaponRebind { public static void Apply(ItemData? weapon, SecondaryAttackAppliedWorldSnapshot appliedWorldSnapshot) { if ((Object)(object)weapon?.m_dropPrefab == (Object)null) { return; } int applyRevision = appliedWorldSnapshot.ApplyRevision; if (SecondaryAttackManager.GetRuntimeWeaponAppliedWorldRevision(weapon) == applyRevision) { return; } ItemDrop component = weapon.m_dropPrefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { if (appliedWorldSnapshot.DefinitionsByPrefabName.TryGetValue(((Object)weapon.m_dropPrefab).name, out SecondaryAttackDefinition value) && value.AppliesSecondaryOverride && (Object)(object)ObjectDB.instance != (Object)null) { Attack val = SecondaryAttackManager.BuildSecondaryAttack(SecondaryAttackManager.ResolveSourceAttack(ObjectDB.instance, component, value), value); SecondaryAttackManager.NormalizeCopiedProjectileAim(val, value); weapon.m_shared.m_secondaryAttack = (Attack)(ProjectilePresetCooldownPolicy.UsesDynamicOriginalSecondary(value) ? ((object)SecondaryAttackManager.CloneAttack(value.CooldownFallbackSecondaryAttack ?? component.m_itemData?.m_shared?.m_secondaryAttack)) : ((object)val)); } else { weapon.m_shared.m_secondaryAttack = SecondaryAttackManager.CloneAttack(component.m_itemData?.m_shared?.m_secondaryAttack); } SecondaryAttackManager.SetRuntimeWeaponAppliedWorldRevision(weapon, applyRevision); } } public static void RefreshLocalPlayerInventory(SecondaryAttackAppliedWorldSnapshot appliedWorldSnapshot) { Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return; } foreach (ItemData allItem in val.GetAllItems()) { Apply(allItem, appliedWorldSnapshot); } } } internal static class SecondaryAttackRuntimeContext { private static readonly ConditionalWeakTable ActiveAttacks = new ConditionalWeakTable(); private static readonly ConditionalWeakTable ProjectileAttackAttributions = new ConditionalWeakTable(); private static readonly List ActiveProjectileHitContexts = new List(); internal static void SetActiveAttack(Attack attack, ActiveSecondaryAttack activeAttack) { ActiveAttacks.Remove(attack); ActiveAttacks.Add(attack, activeAttack); } internal static bool TryGetActiveAttack(Attack attack, out ActiveSecondaryAttack? activeAttack) { return ActiveAttacks.TryGetValue(attack, out activeAttack); } internal static void SetProjectileAttackAttribution(Projectile projectile, ProjectileAttackAttribution attribution) { ProjectileAttackAttributions.Remove(projectile); ProjectileAttackAttributions.Add(projectile, attribution); } internal static bool TryGetProjectileAttackAttribution(Projectile projectile, out ProjectileAttackAttribution? attribution) { return ProjectileAttackAttributions.TryGetValue(projectile, out attribution); } internal static void PushProjectileHitContext(ProjectileHitContext context) { ActiveProjectileHitContexts.Add(context); } internal static void PopProjectileHitContext() { if (ActiveProjectileHitContexts.Count != 0) { ActiveProjectileHitContexts.RemoveAt(ActiveProjectileHitContexts.Count - 1); } } internal static bool TryPeekProjectileHitContext(out ProjectileHitContext? context) { if (ActiveProjectileHitContexts.Count == 0) { context = null; return false; } context = ActiveProjectileHitContexts[ActiveProjectileHitContexts.Count - 1]; return true; } } internal sealed class ActiveSecondaryAttack { public SecondaryAttackDefinition Definition { get; } public bool Triggered { get; set; } public bool ProjectileTriggered { get; set; } public bool BurstTriggerHandled { get; set; } public bool BurstRuntimeStarted { get; set; } public float NextHoldRepeatTime { get; set; } public ActiveSecondaryAttack(SecondaryAttackDefinition definition) { Definition = definition; } } internal sealed class ProjectileAttackAttribution { public string WeaponPrefabName { get; } public bool SecondaryAttack { get; } public SecondaryAttackDefinition? Definition { get; } public bool DisableCurrentAttackFallback { get; } public ProjectileAttackAttribution(string weaponPrefabName, bool secondaryAttack, SecondaryAttackDefinition? definition, bool disableCurrentAttackFallback) { WeaponPrefabName = weaponPrefabName; SecondaryAttack = secondaryAttack; Definition = definition; DisableCurrentAttackFallback = disableCurrentAttackFallback; } } internal sealed class ProjectileHitContext { public Projectile Projectile { get; } public Collider Collider { get; } public Vector3 HitPoint { get; } public bool Water { get; } public Vector3 Normal { get; } public ProjectileAttackAttribution? Attribution { get; } public ProjectileHitContext(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal, ProjectileAttackAttribution? attribution) { //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_0024: 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) Projectile = projectile; Collider = collider; HitPoint = hitPoint; Water = water; Normal = normal; Attribution = attribution; } } internal static class SecondaryAttackRuntimeFacade { private readonly struct ConfiguredAmmoContext { internal Inventory? Inventory { get; } internal ItemData? AmmoItem { get; } internal int RemovalCount { get; } internal ConfiguredAmmoContext(Inventory? inventory, ItemData? ammoItem, int removalCount) { Inventory = inventory; AmmoItem = ammoItem; RemovalCount = removalCount; } } internal const string ReloadLoadedCustomDataKey = "SecondaryAttacks.ReloadLoaded"; private const string StaffRapidFireAnimation = "staff_rapidfire"; private const float FallbackHoldRepeatInterval = 0.2f; internal static bool TryGetDefinition(ItemData weapon, out SecondaryAttackDefinition definition) { definition = null; if ((Object)(object)weapon?.m_dropPrefab == (Object)null) { return false; } return SecondaryAttackFacade.CurrentAppliedWorldSnapshot.DefinitionsByPrefabName.TryGetValue(((Object)weapon.m_dropPrefab).name, out definition); } internal static bool TryGetDefinition(string weaponPrefabName, out SecondaryAttackDefinition definition) { return SecondaryAttackFacade.CurrentAppliedWorldSnapshot.DefinitionsByPrefabName.TryGetValue(weaponPrefabName, out definition); } internal static bool ShouldHandleBowDraw(ItemData weapon) { if (weapon != null && weapon.m_shared.m_attack.m_bowDraw && TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return definition.BehaviorType == SecondaryAttackBehaviorType.Projectile; } return false; } internal static bool CanStartConfiguredSecondary(Humanoid humanoid, ItemData weapon) { if (!TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return true; } if (!(definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return true; } if (!OverchargedBombSystem.CanStart(humanoid, weapon, projectileSecondaryBehavior)) { return false; } if (projectileSecondaryBehavior.Preset == SecondaryAttackPreset.Burst && weapon.m_shared.m_attack.m_requiresReload) { Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null && val.m_weaponLoaded != weapon) { return false; } } string ammoType = weapon.m_shared.m_ammoType; Inventory inventory = humanoid.GetInventory(); ItemData val2 = null; if (!string.IsNullOrWhiteSpace(ammoType)) { val2 = FindConfiguredAmmo(humanoid, weapon, inventory, ammoType); if (val2 == null || (projectileSecondaryBehavior.AmmoConsumption > 0 && (inventory == null || CountAmmo(inventory, val2) < projectileSecondaryBehavior.AmmoConsumption))) { ((Character)humanoid).Message((MessageType)2, "$msg_outof " + ammoType, 0, (Sprite)null); return false; } } Attack secondaryAttack = weapon.m_shared.m_secondaryAttack; if (secondaryAttack == null) { return true; } return ProjectileRuntimeSystem.TryValidateBurstPresetPayload(secondaryAttack, definition, projectileSecondaryBehavior.Preset, val2); } internal static bool BeginProjectileHitContext(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_0021: 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) if ((Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null) { return false; } ProjectileAttackAttribution attribution = null; SecondaryAttackRuntimeContext.TryGetProjectileAttackAttribution(projectile, out attribution); SecondaryAttackRuntimeContext.PushProjectileHitContext(new ProjectileHitContext(projectile, collider, hitPoint, water, normal, attribution)); return true; } internal static void EndProjectileHitContext(bool active) { if (active) { SecondaryAttackRuntimeContext.PopProjectileHitContext(); } } internal static bool TryGetProjectileHitAttackContext(out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition? definition, out bool disableCurrentAttackFallback) { weaponPrefabName = string.Empty; secondaryAttack = false; definition = null; disableCurrentAttackFallback = false; if (!SecondaryAttackRuntimeContext.TryPeekProjectileHitContext(out ProjectileHitContext context) || context == null) { return false; } ProjectileAttackAttribution attribution = context.Attribution; if (attribution == null) { return false; } disableCurrentAttackFallback = attribution.DisableCurrentAttackFallback; definition = attribution.Definition; weaponPrefabName = attribution.WeaponPrefabName; secondaryAttack = attribution.SecondaryAttack; if (string.IsNullOrEmpty(weaponPrefabName)) { return definition != null; } return true; } internal static bool TryResolveProjectileAttackAttributionData(Attack attack, out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition? definition) { ItemData weapon = attack.m_weapon; object obj; if (weapon == null) { obj = null; } else { GameObject dropPrefab = weapon.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if (obj == null) { obj = string.Empty; } weaponPrefabName = (string)obj; secondaryAttack = false; definition = null; if ((Object)(object)attack.m_weapon?.m_dropPrefab == (Object)null) { return false; } if (SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) && activeAttack != null) { definition = activeAttack.Definition; weaponPrefabName = activeAttack.Definition.PrefabName; secondaryAttack = true; return true; } if (TryGetDefinition(attack.m_weapon, out SecondaryAttackDefinition definition2)) { definition = definition2; } return true; } internal static void SetProjectileAttackAttribution(Projectile projectile, string weaponPrefabName, bool secondaryAttack, SecondaryAttackDefinition? definition, bool disableCurrentAttackFallback) { SecondaryAttackRuntimeContext.SetProjectileAttackAttribution(projectile, new ProjectileAttackAttribution(weaponPrefabName, secondaryAttack, definition, disableCurrentAttackFallback)); } internal static void TryApplyProjectileSetupAttribution(Projectile projectile, ItemData item) { if (!((Object)(object)projectile == (Object)null) && !((Object)(object)item?.m_dropPrefab == (Object)null) && !((Object)(object)ProjectileAccess.GetOwner(projectile) != (Object)(object)Player.m_localPlayer) && TryGetDefinition(item, out SecondaryAttackDefinition definition)) { bool flag = false; Player localPlayer = Player.m_localPlayer; Attack val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).m_currentAttack : null); if ((Object)(object)val?.m_weapon?.m_dropPrefab != (Object)null && string.Equals(((Object)val.m_weapon.m_dropPrefab).name, ((Object)item.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase)) { flag = ((Humanoid)localPlayer).m_currentAttackIsSecondary; } if (flag) { float factor = ((definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) ? projectileSecondaryBehavior.AdrenalineFactor : 1f); SecondaryAttackAdrenalineSystem.ApplyProjectileFactor(projectile, val, factor); } SetProjectileAttackAttribution(projectile, definition.PrefabName, flag, definition, disableCurrentAttackFallback: false); } } internal static void RegisterActiveAttack(Attack attack, ItemData weapon) { if (!TryGetDefinition(weapon, out SecondaryAttackDefinition definition)) { return; } bool num = definition.BehaviorType == SecondaryAttackBehaviorType.Projectile || definition.BehaviorType == SecondaryAttackBehaviorType.SummonEmpower || definition.BehaviorType == SecondaryAttackBehaviorType.ShieldConvert || definition.SneakAmbush != null || definition.CleavingThrust != null || definition.LaunchSlam != null || definition.KnockbackChain != null || definition.Aftershock != null || definition.RiftTrail != null || definition.FractureLine != null || definition.HarvestSweep != null || definition.SpinningSweep != null || (definition.BehaviorType == SecondaryAttackBehaviorType.CopiedSecondary && definition.Boomerang != null) || (definition.BehaviorType == SecondaryAttackBehaviorType.CopiedSecondary && definition.OnProjectileHit != null); if (definition.SpinningSweep != null) { SpinningSweepSystem.TryStart(attack, definition); } else if (definition.HarvestSweep != null) { HarvestSweepSystem.TryStart(attack, definition); } if (num) { ActiveSecondaryAttack activeAttack = new ActiveSecondaryAttack(definition); SecondaryAttackRuntimeContext.SetActiveAttack(attack, activeAttack); SecondaryAttackAdrenalineSystem.Reset(attack); if (definition.CleavingThrust != null) { GreatSwordSkillScalingSystem.BeginCleavingThrustVisualSession(attack, definition); } if (definition.RiftTrail != null) { RiftTrailSystem.BeginSampling(attack, definition); } } } internal static bool TryHandleCustomAttackTrigger(Attack attack) { if (!SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) || activeAttack == null) { return false; } if (((Character)attack.m_character).IsStaggering()) { return true; } if (activeAttack.Definition.CleavingThrust != null) { if (!CleavingThrustSystem.CanHandle(attack)) { return false; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, "cleavingThrust", activeAttack.Definition.CleavingThrust.PresetCooldown)) { return false; } if (!TriggerCleavingThrust(attack, activeAttack)) { attack.Stop(); } return true; } if (activeAttack.Definition.Aftershock != null) { if (!AftershockSystem.CanHandle(attack, activeAttack.Definition)) { return false; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, "aftershock", activeAttack.Definition.Aftershock.PresetCooldown)) { return false; } activeAttack.Triggered = true; AftershockSystem.Trigger(attack, activeAttack.Definition); return true; } if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.CopiedSecondary) { return false; } if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.SummonEmpower || activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.ShieldConvert) { if (!activeAttack.Triggered && !StaffRuntimeSystem.TryTriggerStaffSpecialFromRuntimeFacade(attack, activeAttack)) { attack.Stop(); } return true; } if (activeAttack.Definition.Behavior is ProjectileSecondaryBehavior { Preset: SecondaryAttackPreset.Burst }) { if (activeAttack.BurstTriggerHandled) { return true; } activeAttack.BurstTriggerHandled = true; } if (!TriggerConfiguredAttack(attack, activeAttack)) { attack.Stop(); } return true; } internal static void TryTriggerRiftTrailAfterAttack(Attack attack) { if (SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) && activeAttack != null && !activeAttack.Triggered && activeAttack.Definition.RiftTrail != null && RiftTrailSystem.CanHandle(attack, activeAttack.Definition) && MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, "riftTrail", activeAttack.Definition.RiftTrail.PresetCooldown)) { activeAttack.Triggered = true; RiftTrailSystem.Trigger(attack, activeAttack.Definition); } } internal static void TryTriggerFractureLineAfterAttack(Attack attack) { if (SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) && activeAttack != null && !activeAttack.Triggered && activeAttack.Definition.FractureLine != null && FractureLineSystem.CanHandle(attack, activeAttack.Definition) && MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, "fractureLine", activeAttack.Definition.FractureLine.PresetCooldown)) { activeAttack.Triggered = true; FractureLineSystem.Trigger(attack, activeAttack.Definition); } } private static bool TriggerCleavingThrust(Attack attack, ActiveSecondaryAttack activeAttack) { if (!TryPrepareConfiguredAmmo(attack, activeAttack.Definition, out var context)) { return false; } CommitConfiguredAmmo(attack, context); activeAttack.Triggered = true; CleavingThrustSystem.Trigger(attack, activeAttack.Definition); ApplyAttackTriggerSideEffects(attack); return true; } internal static void TryUpdateSecondaryProjectileHoldRepeat(Player player, bool secondaryAttackHold) { if (!secondaryAttackHold || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead()) { return; } Attack currentAttack = ((Humanoid)player).m_currentAttack; if (currentAttack == null || !((Humanoid)player).m_currentAttackIsSecondary || !SecondaryAttackRuntimeContext.TryGetActiveAttack(currentAttack, out ActiveSecondaryAttack activeAttack) || activeAttack == null || !IsHoldRepeatProjectileAttack(currentAttack, activeAttack) || !((Character)player).InAttack() || (Object)(object)currentAttack.m_character == (Object)null || ((Character)currentAttack.m_character).IsStaggering()) { return; } float num = ResolveHoldRepeatInterval(currentAttack, activeAttack); if (activeAttack.NextHoldRepeatTime <= 0f) { activeAttack.NextHoldRepeatTime = Time.time + num; } else if (!(Time.time < activeAttack.NextHoldRepeatTime)) { bool projectileTriggered = activeAttack.ProjectileTriggered; activeAttack.NextHoldRepeatTime = Time.time + num; if (!TryPrepareConfiguredAttack(currentAttack, activeAttack, out var ammoContext) || (projectileTriggered && !TryConsumeRepeatedProjectileStartResources(currentAttack)) || !TriggerPreparedConfiguredAttack(currentAttack, activeAttack, ammoContext)) { currentAttack.Stop(); } } } internal static bool TryBeginBurstFireRepeat(Attack attack) { if (!SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) || activeAttack == null || !activeAttack.BurstRuntimeStarted || !(activeAttack.Definition.Behavior is ProjectileSecondaryBehavior { Preset: SecondaryAttackPreset.Burst }) || !ProjectileRuntimeSystem.IsBurstFireControllerActive(attack)) { return false; } if (TryPrepareConfiguredAttack(attack, activeAttack, out var ammoContext)) { CommitConfiguredAmmo(attack, ammoContext); attack.ProjectileAttackTriggered(); activeAttack.ProjectileTriggered = true; return true; } attack.Stop(); return false; } internal static void CompleteBurstFireRepeat(Attack attack) { ApplyAttackTriggerSideEffects(attack); } private static bool TriggerConfiguredAttack(Attack attack, ActiveSecondaryAttack activeAttack) { if (!TryPrepareConfiguredAttack(attack, activeAttack, out var ammoContext)) { return false; } return TriggerPreparedConfiguredAttack(attack, activeAttack, ammoContext); } private static bool TryPrepareConfiguredAttack(Attack attack, ActiveSecondaryAttack activeAttack, out ConfiguredAmmoContext ammoContext) { //IL_0008: 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_002e: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Invalid comparison between Unknown and I4 ammoContext = default(ConfiguredAmmoContext); if (!IsSupportedConfiguredAttackType(attack.m_attackType)) { return false; } ProjectileSecondaryBehavior projectileSecondaryBehavior = activeAttack.Definition.Behavior as ProjectileSecondaryBehavior; if ((int)attack.m_attackType == 2 && projectileSecondaryBehavior != null && !ProjectileRuntimeSystem.IsBurstFireControllerActive(attack) && !RangedSecondaryCooldownSystem.CanUse(attack, projectileSecondaryBehavior)) { return false; } if (!TryPrepareConfiguredAmmo(attack, activeAttack.Definition, out ammoContext)) { return false; } if ((int)attack.m_attackType != 2 || projectileSecondaryBehavior == null) { return true; } if (!ProjectileRuntimeSystem.CanStartBurstPreset(attack, activeAttack.Definition, projectileSecondaryBehavior.Preset, ammoContext.AmmoItem)) { return false; } if (attack.m_perBurstResourceUsage) { return HasAttackResources(attack, stopAttackOnFailure: false, flashHudOnFailure: true); } return true; } private static bool TriggerPreparedConfiguredAttack(Attack attack, ActiveSecondaryAttack activeAttack, ConfiguredAmmoContext ammoContext) { //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_0028: Expected I4, but got Unknown CommitConfiguredAmmo(attack, ammoContext); AttackType attackType = attack.m_attackType; switch ((int)attackType) { case 0: case 1: attack.DoMeleeAttack(); break; case 4: attack.DoAreaAttack(); break; case 2: attack.ProjectileAttackTriggered(); activeAttack.ProjectileTriggered = true; UpdateHoldRepeatAfterProjectileTrigger(attack, activeAttack); break; case 3: attack.DoNonAttack(); break; default: return false; } ApplyAttackTriggerSideEffects(attack); return true; } private static bool IsSupportedConfiguredAttackType(AttackType attackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)attackType <= 4) { return true; } return false; } private static void ApplyAttackTriggerSideEffects(Attack attack) { //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) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown if (attack.m_toggleFlying) { if (((Character)attack.m_character).IsFlying()) { ((Character)attack.m_character).Land(); } else { ((Character)attack.m_character).TakeOff(); } } if (attack.m_recoilPushback != 0f) { ((Character)attack.m_character).ApplyPushback(-((Component)attack.m_character).transform.forward, attack.m_recoilPushback); } if (attack.m_selfDamage > 0) { HitData val = new HitData(); val.m_damage.m_damage = attack.m_selfDamage; ((Character)attack.m_character).Damage(val); } if (attack.m_consumeItem) { attack.ConsumeItem(); } if (attack.m_requiresReload && !ProjectileRuntimeSystem.ShouldDeferBurstFireReloadReset(attack)) { attack.m_character.ResetLoadedWeapon(); } } private static bool IsHoldRepeatProjectileAttack(Attack attack, ActiveSecondaryAttack activeAttack) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.Projectile && !(activeAttack.Definition.Behavior is ProjectileSecondaryBehavior { Preset: SecondaryAttackPreset.Burst }) && (int)attack.m_attackType == 2 && attack.m_loopingAttack) { return string.Equals(attack.m_attackAnimation, "staff_rapidfire", StringComparison.Ordinal); } return false; } private static void UpdateHoldRepeatAfterProjectileTrigger(Attack attack, ActiveSecondaryAttack activeAttack) { if (IsHoldRepeatProjectileAttack(attack, activeAttack)) { activeAttack.NextHoldRepeatTime = Time.time + ResolveHoldRepeatInterval(attack, activeAttack); } } private static float ResolveHoldRepeatInterval(Attack attack, ActiveSecondaryAttack activeAttack) { if (activeAttack.Definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) { return Mathf.Max(0.01f, projectileSecondaryBehavior.HoldRepeatInterval); } return 0.2f; } private static bool TryConsumeRepeatedProjectileStartResources(Attack attack) { if (attack.m_perBurstResourceUsage) { return true; } return TryConsumeAttackResources(attack, stopAttackOnFailure: false, flashHudOnFailure: true); } private static bool TryConsumeAttackResources(Attack attack, bool stopAttackOnFailure, bool flashHudOnFailure) { if (!HasAttackResources(attack, stopAttackOnFailure, flashHudOnFailure)) { return false; } float attackStamina = attack.GetAttackStamina(); float attackEitr = attack.GetAttackEitr(); float attackHealth = attack.GetAttackHealth(); if (attackStamina > 0f) { ((Character)attack.m_character).UseStamina(attackStamina); } if (attackEitr > 0f) { ((Character)attack.m_character).UseEitr(attackEitr); } if (attackHealth > 0f) { ((Character)attack.m_character).UseHealth(Mathf.Min(((Character)attack.m_character).GetHealth() - 1f, attackHealth)); } return true; } private static bool HasAttackResources(Attack attack, bool stopAttackOnFailure, bool flashHudOnFailure) { float attackStamina = attack.GetAttackStamina(); float attackEitr = attack.GetAttackEitr(); float attackHealth = attack.GetAttackHealth(); if (attackStamina > 0f && !((Character)attack.m_character).HaveStamina(attackStamina)) { return HandleResourceFailure(attack, stopAttackOnFailure, flashHudOnFailure); } if (attackEitr > 0f && !((Character)attack.m_character).HaveEitr(attackEitr)) { return HandleResourceFailure(attack, stopAttackOnFailure); } if (attackHealth > 0f && !((Character)attack.m_character).HaveHealth(attackHealth) && attack.m_attackHealthLowBlockUse) { return HandleResourceFailure(attack, stopAttackOnFailure, flashStamina: false, flashHudOnFailure); } return true; } private static bool HandleResourceFailure(Attack attack, bool stopAttack, bool flashStamina = false, bool flashHealth = false) { if (stopAttack) { attack.Stop(); } else if (((Character)attack.m_character).IsPlayer()) { if (flashStamina) { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } else if (flashHealth) { Hud instance2 = Hud.instance; if (instance2 != null) { instance2.FlashHealthBar(); } } } return false; } internal static bool TryHandleCustomProjectileBurst(Attack attack) { if (!SecondaryAttackRuntimeContext.TryGetActiveAttack(attack, out ActiveSecondaryAttack activeAttack) || activeAttack == null) { return false; } if (!(activeAttack.Definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior)) { return false; } bool flag = ProjectileRuntimeSystem.IsBurstFireControllerActive(attack); bool flag2 = projectileSecondaryBehavior.Preset == SecondaryAttackPreset.Burst; if (flag2 && activeAttack.BurstRuntimeStarted) { return true; } if (!RangedSecondaryCooldownSystem.CanUse(attack, projectileSecondaryBehavior)) { if (!flag) { attack.Stop(); } return true; } if (!ProjectileRuntimeSystem.CanStartBurstPreset(attack, activeAttack.Definition, projectileSecondaryBehavior.Preset, attack.m_ammoItem)) { attack.Stop(); return true; } if (!ConsumePerBurstResourcesIfNeeded(attack)) { return true; } if (ProjectileRuntimeSystem.TryHandleBurstPreset(attack, activeAttack.Definition, projectileSecondaryBehavior.Preset)) { if (flag2) { activeAttack.BurstRuntimeStarted = true; } RangedSecondaryCooldownSystem.StartCooldown(attack, projectileSecondaryBehavior); } else if (!flag) { attack.Stop(); } return true; } private static bool TryPrepareConfiguredAmmo(Attack attack, SecondaryAttackDefinition definition, out ConfiguredAmmoContext context) { context = default(ConfiguredAmmoContext); ProjectileSecondaryBehavior projectileSecondaryBehavior = definition.Behavior as ProjectileSecondaryBehavior; string ammoType = attack.m_weapon.m_shared.m_ammoType; if (string.IsNullOrWhiteSpace(ammoType)) { return true; } Inventory inventory = attack.m_character.GetInventory(); ItemData val = FindConfiguredAmmo(attack.m_character, attack.m_weapon, inventory, ammoType); if (val == null) { ((Character)attack.m_character).Message((MessageType)2, "$msg_outof " + ammoType, 0, (Sprite)null); return false; } int num = Mathf.Max(0, projectileSecondaryBehavior?.AmmoConsumption ?? 0); if (num > 0 && (inventory == null || CountAmmo(inventory, val) < num)) { ((Character)attack.m_character).Message((MessageType)2, "$msg_outof " + ammoType, 0, (Sprite)null); return false; } context = new ConfiguredAmmoContext(inventory, val, num); return true; } private static ItemData? FindConfiguredAmmo(Humanoid character, ItemData weapon, Inventory? inventory, string ammoType) { ItemData val = Attack.FindAmmo(character, weapon); if (val != null && IsAmmoItemForType(val, ammoType)) { return val; } if (inventory == null) { return null; } return ((IEnumerable)inventory.GetAllItems()).FirstOrDefault((Func)((ItemData item) => IsAmmoItemForType(item, ammoType))); } private static void CommitConfiguredAmmo(Attack attack, ConfiguredAmmoContext context) { attack.m_ammoItem = context.AmmoItem; attack.m_lastUsedAmmo = context.AmmoItem; if (context.Inventory != null && context.AmmoItem != null && context.RemovalCount > 0) { RemoveAmmo(context.Inventory, context.AmmoItem, context.RemovalCount); } } private static int CountAmmo(Inventory inventory, ItemData selectedAmmo) { return (from item in inventory.GetAllItems() where IsSameAmmoPrefab(item, selectedAmmo) select item).Sum((ItemData item) => item.m_stack); } private static void RemoveAmmo(Inventory inventory, ItemData selectedAmmo, int amount) { int num = Mathf.Min(selectedAmmo.m_stack, amount); if (num > 0) { inventory.RemoveItem(selectedAmmo, num); amount -= num; } foreach (ItemData item in (from item in inventory.GetAllItems() where item != selectedAmmo && IsSameAmmoPrefab(item, selectedAmmo) select item).ToList()) { if (amount <= 0) { break; } int num2 = Mathf.Min(item.m_stack, amount); inventory.RemoveItem(item, num2); amount -= num2; } } private static bool IsSameAmmoPrefab(ItemData? candidate, ItemData? selectedAmmo) { if (candidate == null || selectedAmmo == null) { return false; } if (candidate == selectedAmmo) { return true; } if ((Object)(object)candidate.m_dropPrefab != (Object)null && (Object)(object)selectedAmmo.m_dropPrefab != (Object)null) { return string.Equals(((Object)candidate.m_dropPrefab).name, ((Object)selectedAmmo.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase); } return false; } private static bool IsAmmoItemForType(ItemData? item, string ammoType) { //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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 if (item?.m_shared == null || item.m_shared.m_ammoType != ammoType) { return false; } ItemType itemType = item.m_shared.m_itemType; if ((int)itemType == 9 || (int)itemType == 23) { return true; } return false; } private static bool ConsumePerBurstResourcesIfNeeded(Attack attack) { if (!attack.m_perBurstResourceUsage) { return true; } return TryConsumeAttackResources(attack, stopAttackOnFailure: true, flashHudOnFailure: false); } } internal static class SecondaryAttackWorldApplySystem { private static int _nextApplyRevision = 1; public static SecondaryAttackAppliedWorldSnapshot Apply(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { if ((Object)(object)objectDb == (Object)null) { return SecondaryAttackAppliedWorldSnapshot.Empty; } SecondaryAttackObjectDbStateStore.Restore(objectDb); MagicSummonQualityPresetSystem.RestoreObjectDb(objectDb); SecondaryAttackDefinitionBuildContext buildContext = new SecondaryAttackDefinitionBuildContext(objectDb, emitMissingWarnings); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; HashSet seenConfiguredPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (GameObject item in objectDb.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent(); if ((Object)(object)component == (Object)null) { continue; } bool flag = false; bool flag2 = false; bool flag3 = false; compiledSnapshot.Weapons.TryGetValue(((Object)item).name, out NormalizedWeaponConfig value); if (value != null) { seenConfiguredPrefabs.Add(((Object)item).name); value = ((!value.Enabled || value.UseAutomaticFallback) ? null : ResolveDefaultMeleeFallbacks(component, value, compiledSnapshot.GlobalMeleeFallback)); } if (value == null) { NormalizedWeaponConfig fallback2; if (TryCreateDefaultBloodMagicFallback(((Object)item).name, component, compiledSnapshot.GlobalBloodMagicPresets, out NormalizedWeaponConfig fallback)) { value = fallback; flag3 = true; } else if (TryCreateDefaultRangedFallback(component, compiledSnapshot.GlobalRangedPresets, out fallback2)) { value = fallback2; flag2 = true; } else { if (!TryCreateDefaultMeleeFallback(component, compiledSnapshot.GlobalMeleeFallback, out NormalizedWeaponConfig defaultMeleeFallback)) { continue; } value = defaultMeleeFallback; flag = true; } } if (!SecondaryAttackDefinitionCompiler.TryCreateDefinition(buildContext, ((Object)item).name, component, value, out SecondaryAttackDefinition definition)) { continue; } SecondaryAttackDefinition secondaryAttackDefinition = definition; SecondaryAttackCooldownGroupResolver.Apply(((Object)item).name, component, secondaryAttackDefinition); secondaryAttackDefinition.CooldownFallbackSecondaryAttack = ResolveCooldownFallbackSecondaryAttack(objectDb, ((Object)item).name, component); dictionary[((Object)item).name] = secondaryAttackDefinition; if (secondaryAttackDefinition.AppliesSecondaryOverride) { Attack val = SecondaryAttackManager.BuildSecondaryAttack(SecondaryAttackManager.ResolveSourceAttack(objectDb, component, secondaryAttackDefinition), secondaryAttackDefinition); SecondaryAttackManager.NormalizeCopiedProjectileAim(val, secondaryAttackDefinition); secondaryAttackDefinition.ConfiguredSecondaryAttack = SecondaryAttackManager.CloneAttack(val); if (!ProjectilePresetCooldownPolicy.UsesDynamicOriginalSecondary(secondaryAttackDefinition)) { SecondaryAttackObjectDbStateStore.CaptureSecondaryAttack(objectDb, ((Object)item).name, component.m_itemData.m_shared.m_secondaryAttack); component.m_itemData.m_shared.m_secondaryAttack = val; } } num++; if (flag2) { num2++; } if (flag3) { num3++; } if (flag) { num4++; } } SecondaryAttackAppliedWorldSnapshot secondaryAttackAppliedWorldSnapshot = new SecondaryAttackAppliedWorldSnapshot(compiledSnapshot, dictionary, _nextApplyRevision++); foreach (string item2 in compiledSnapshot.Weapons.Keys.Where((string key) => !seenConfiguredPrefabs.Contains(key))) { if (emitMissingWarnings && SecondaryAttackWarningLog.TryMarkWarning("missing_objectdb_prefab:" + item2)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Configured prefab '" + item2 + "' was not found in ObjectDB.")); } } MagicSummonQualityPresetSystem.ApplyToObjectDb(objectDb, secondaryAttackAppliedWorldSnapshot.CompiledSnapshot.MagicSummons); SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"Applied {num} secondary attack definition(s), including {num2} global ranged fallback definition(s), {num3} global blood magic fallback definition(s), and {num4} global melee fallback definition(s)."); return secondaryAttackAppliedWorldSnapshot; } internal static void ApplyToZNetScene(ZNetScene scene, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { SummonPrefabOverrideSystem.Apply(scene, compiledSnapshot.MagicSummons, emitMissingWarnings); MagicSummonQualityPresetSystem.ApplyToZNetScene(scene, compiledSnapshot.MagicSummons); } private static bool TryCreateDefaultBloodMagicFallback(string prefabName, ItemDrop itemDrop, IReadOnlyDictionary globalBloodMagicPresets, out NormalizedWeaponConfig? fallback) { fallback = null; if (globalBloodMagicPresets.Count == 0) { return false; } BloodMagicAutomaticWeaponFamily bloodMagicAutomaticWeaponFamily = SecondaryAttackWeaponFamilyResolver.ResolveBloodMagicFamily(prefabName, itemDrop); if (bloodMagicAutomaticWeaponFamily == BloodMagicAutomaticWeaponFamily.Shield && globalBloodMagicPresets.TryGetValue("shieldConvert", out NormalizedWeaponConfig value)) { fallback = value; return true; } if (bloodMagicAutomaticWeaponFamily == BloodMagicAutomaticWeaponFamily.Summon && globalBloodMagicPresets.TryGetValue("summonEmpower", out NormalizedWeaponConfig value2)) { fallback = value2; return true; } return false; } private static bool TryCreateDefaultRangedFallback(ItemDrop itemDrop, IReadOnlyDictionary globalRangedPresets, out NormalizedWeaponConfig? fallback) { fallback = null; if (!TryResolveRangedPresetName(itemDrop, out string presetName)) { return false; } if (globalRangedPresets.TryGetValue(presetName, out NormalizedWeaponConfig value)) { fallback = value; return true; } fallback = SecondaryAttackWeaponConfigNormalizer.FromRangedRaw(new RangedWeaponConfig { Preset = presetName }); return true; } private static bool TryResolveRangedPresetName(ItemDrop itemDrop, out string presetName) { presetName = ""; return SecondaryAttackWeaponFamilyResolver.ResolveRangedFamily(itemDrop) switch { RangedAutomaticWeaponFamily.Bomb => TryResolveBombPresetName(itemDrop.m_itemData.m_shared.m_attack, out presetName), RangedAutomaticWeaponFamily.FireballStaff => TryGetRangedPresetName(SecondaryAttacksPlugin.FireballStaffPreset.Value, out presetName), RangedAutomaticWeaponFamily.RapidStaff => TryGetRangedPresetName(SecondaryAttacksPlugin.RapidStaffPreset.Value, out presetName), RangedAutomaticWeaponFamily.ReloadStaff => TryGetRangedPresetName(SecondaryAttacksPlugin.LightningStaffPreset.Value, out presetName), RangedAutomaticWeaponFamily.Crossbow => TryGetRangedPresetName(SecondaryAttacksPlugin.CrossbowPreset.Value, out presetName), RangedAutomaticWeaponFamily.Bow => TryGetRangedPresetName(SecondaryAttacksPlugin.BowPreset.Value, out presetName), _ => false, }; } private static bool TryResolveBombPresetName(Attack primaryAttack, out string presetName) { presetName = ""; if (!SecondaryAttackWeaponFamilyResolver.IsBombProjectileAttack(primaryAttack)) { return false; } string text; switch (SecondaryAttacksPlugin.BombPreset.Value) { case SecondaryAttacksPlugin.BombPresetSelection.Off: return false; case SecondaryAttacksPlugin.BombPresetSelection.StickyDetonator: text = ProjectileRuntimeSystem.GetPresetName(SecondaryAttackPreset.StickyDetonator); break; case SecondaryAttacksPlugin.BombPresetSelection.OverchargedBomb: text = ProjectileRuntimeSystem.GetPresetName(SecondaryAttackPreset.OverchargedBomb); break; default: text = (BombProjectileSpawnsAoe(primaryAttack.m_attackProjectile) ? ProjectileRuntimeSystem.GetPresetName(SecondaryAttackPreset.OverchargedBomb) : ProjectileRuntimeSystem.GetPresetName(SecondaryAttackPreset.StickyDetonator)); break; } presetName = text; return true; } private static bool BombProjectileSpawnsAoe(GameObject? projectilePrefab) { if ((Object)(object)projectilePrefab == (Object)null) { return false; } Projectile component = projectilePrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } if (component.m_aoe > 0f) { return true; } if (PrefabContainsAoe(component.m_spawnOnHit)) { return true; } List randomSpawnOnHit = component.m_randomSpawnOnHit; if (randomSpawnOnHit == null) { return false; } foreach (GameObject item in randomSpawnOnHit) { if (PrefabContainsAoe(item)) { return true; } } return false; } private static bool PrefabContainsAoe(GameObject? prefab) { return PrefabContainsAoe(prefab, new HashSet()); } private static bool PrefabContainsAoe(GameObject? prefab, HashSet visitedPrefabs) { if ((Object)(object)prefab == (Object)null || !visitedPrefabs.Add(prefab)) { return false; } if ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return true; } SpawnAbility[] componentsInChildren = prefab.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { GameObject[] array = componentsInChildren[i].m_spawnPrefab ?? Array.Empty(); for (int j = 0; j < array.Length; j++) { if (PrefabContainsAoe(array[j], visitedPrefabs)) { return true; } } } return false; } private static bool TryGetRangedPresetName(SecondaryAttacksPlugin.RangedPresetSelection selection, out string presetName) { presetName = ""; if (selection == SecondaryAttacksPlugin.RangedPresetSelection.Off) { return false; } if (!Enum.TryParse(selection.ToString(), out var result)) { return false; } presetName = ProjectileRuntimeSystem.GetPresetName(result); return true; } private static bool TryCreateDefaultMeleeFallback(ItemDrop itemDrop, NormalizedWeaponConfig? globalMeleeFallback, out NormalizedWeaponConfig? defaultMeleeFallback) { defaultMeleeFallback = null; if (globalMeleeFallback == null || !globalMeleeFallback.Enabled) { return false; } NormalizedWeaponConfig normalizedWeaponConfig = new NormalizedWeaponConfig(); switch (SecondaryAttackWeaponFamilyResolver.ResolveMeleeFamily(itemDrop)) { case MeleeAutomaticWeaponFamily.Knives: { NormalizedSneakAmbushConfig? sneakAmbush = globalMeleeFallback.SneakAmbush; if (sneakAmbush != null && sneakAmbush.Enabled) { normalizedWeaponConfig.SneakAmbush = globalMeleeFallback.SneakAmbush.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.SneakAmbush; break; } goto default; } case MeleeAutomaticWeaponFamily.TwoHandedSword: { NormalizedCleavingThrustConfig? cleavingThrust = globalMeleeFallback.CleavingThrust; if (cleavingThrust != null && cleavingThrust.Enabled) { normalizedWeaponConfig.CleavingThrust = globalMeleeFallback.CleavingThrust.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.CleavingThrust; break; } goto default; } case MeleeAutomaticWeaponFamily.OneHandedSword: { NormalizedRiftTrailConfig? riftTrail = globalMeleeFallback.RiftTrail; if (riftTrail != null && riftTrail.Enabled) { normalizedWeaponConfig.RiftTrail = globalMeleeFallback.RiftTrail.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.RiftTrail; break; } goto default; } case MeleeAutomaticWeaponFamily.OneHandedClub: { NormalizedLaunchSlamConfig? launchSlam = globalMeleeFallback.LaunchSlam; if (launchSlam != null && launchSlam.Enabled) { normalizedWeaponConfig.LaunchSlam = globalMeleeFallback.LaunchSlam.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.LaunchSlam; break; } goto default; } case MeleeAutomaticWeaponFamily.Unarmed: { NormalizedKnockbackChainConfig? knockbackChain = globalMeleeFallback.KnockbackChain; if (knockbackChain != null && knockbackChain.Enabled) { normalizedWeaponConfig.KnockbackChain = globalMeleeFallback.KnockbackChain.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.KnockbackChain; break; } goto default; } case MeleeAutomaticWeaponFamily.Sledge: { NormalizedAftershockConfig? aftershock = globalMeleeFallback.Aftershock; if (aftershock != null && aftershock.Enabled) { normalizedWeaponConfig.Aftershock = globalMeleeFallback.Aftershock.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.Aftershock; break; } goto default; } case MeleeAutomaticWeaponFamily.Polearm: { NormalizedSpinningSweepConfig? spinningSweep = globalMeleeFallback.SpinningSweep; if (spinningSweep != null && spinningSweep.Enabled) { normalizedWeaponConfig.SpinningSweep = globalMeleeFallback.SpinningSweep.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.SpinningSweep; break; } goto default; } case MeleeAutomaticWeaponFamily.Farming: { NormalizedHarvestSweepConfig? harvestSweep = globalMeleeFallback.HarvestSweep; if (harvestSweep != null && harvestSweep.Enabled) { normalizedWeaponConfig.HarvestSweep = globalMeleeFallback.HarvestSweep.Clone(); break; } goto default; } case MeleeAutomaticWeaponFamily.Pickaxe: { NormalizedFractureLineConfig? fractureLine = globalMeleeFallback.FractureLine; if (fractureLine != null && fractureLine.Enabled) { normalizedWeaponConfig.FractureLine = globalMeleeFallback.FractureLine.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.FractureLine; break; } goto default; } case MeleeAutomaticWeaponFamily.Spear: { NormalizedMeleeOnProjectileHitConfig? spearRain = globalMeleeFallback.SpearRain; if (spearRain != null && spearRain.Enabled) { normalizedWeaponConfig.SpearRain = globalMeleeFallback.SpearRain.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.SpearRain; break; } goto default; } case MeleeAutomaticWeaponFamily.Battleaxe: { NormalizedImpactBurstConfig? impactBurst = globalMeleeFallback.ImpactBurst; if (impactBurst != null && impactBurst.Enabled) { normalizedWeaponConfig.ImpactBurst = globalMeleeFallback.ImpactBurst.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.ImpactBurst; break; } goto default; } case MeleeAutomaticWeaponFamily.OneHandedAxe: { NormalizedBoomerangConfig? boomerang = globalMeleeFallback.Boomerang; if (boomerang != null && boomerang.Enabled) { normalizedWeaponConfig.Boomerang = globalMeleeFallback.Boomerang.Clone(); normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.Boomerang; break; } goto default; } default: return false; } normalizedWeaponConfig.Secondary = CreateDefaultMeleeSecondary(globalMeleeFallback.Secondary, normalizedWeaponConfig); defaultMeleeFallback = normalizedWeaponConfig; return true; } private static NormalizedSecondaryModeConfig? CreateDefaultMeleeSecondary(NormalizedSecondaryModeConfig? source, NormalizedWeaponConfig fallback) { if (fallback.Aftershock != null) { return CloneAftershockSecondary(source); } if (fallback.FractureLine != null) { return CreateFractureLineSecondary(source); } if (fallback.SpearRain != null) { return CreateSpearRainSecondary(source, fallback.SpearRain); } if (fallback.ImpactBurst != null) { return SecondaryAttackWeaponConfigNormalizer.CreateImpactBurstSecondary(fallback.ImpactBurst); } if (fallback.Boomerang != null) { return SecondaryAttackWeaponConfigNormalizer.CreateBoomerangSecondary(fallback.Boomerang); } if (fallback.SpinningSweep != null) { return SecondaryAttackWeaponConfigNormalizer.CreateSpinningSweepSecondary(fallback.SpinningSweep); } if (fallback.HarvestSweep == null) { return null; } return CreateHarvestSweepSecondary(fallback.HarvestSweep); } private static Attack ResolveCooldownFallbackSecondaryAttack(ObjectDB objectDb, string prefabName, ItemDrop itemDrop) { if (SecondaryAttackObjectDbStateStore.TryGetOriginalSecondaryAttack(objectDb, prefabName, out Attack attack) && attack != null && !string.IsNullOrWhiteSpace(attack.m_attackAnimation)) { return SecondaryAttackManager.CloneAttack(attack); } return SecondaryAttackManager.CloneAttack(itemDrop.m_itemData.m_shared.m_secondaryAttack); } private static NormalizedSecondaryModeConfig CloneAftershockSecondary(NormalizedSecondaryModeConfig? source) { return new NormalizedSecondaryModeConfig { Type = "aftershock", Animation = (source?.Animation ?? ""), ResourceMultiplier = 1f, OutputMultiplier = (source?.OutputMultiplier ?? 1f), DurabilityFactor = (source?.DurabilityFactor ?? 1f), CopyFrom = (source?.CopyFrom ?? ""), Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig CreateFractureLineSecondary(NormalizedSecondaryModeConfig? source) { return new NormalizedSecondaryModeConfig { Type = "fractureLine", Animation = (source?.Animation ?? ""), ResourceMultiplier = 1f, OutputMultiplier = (source?.OutputMultiplier ?? 1f), DurabilityFactor = (source?.DurabilityFactor ?? 1f), CopyFrom = (source?.CopyFrom ?? ""), Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig CreateSpearRainSecondary(NormalizedSecondaryModeConfig? source, NormalizedMeleeOnProjectileHitConfig spearRain) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = (source?.Animation ?? ""), ResourceMultiplier = spearRain.ResourceMultiplier, OutputMultiplier = (source?.OutputMultiplier ?? 1f), DurabilityFactor = (source?.DurabilityFactor ?? spearRain.DurabilityFactor), CopyFrom = (source?.CopyFrom ?? ""), OnProjectileHit = spearRain.Clone(), Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig CreateHarvestSweepSecondary(NormalizedHarvestSweepConfig? harvestSweep) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = ResolveHarvestSweepAnimation(harvestSweep), ResourceMultiplier = (harvestSweep?.ResourceMultiplier ?? 1f), OutputMultiplier = 1f, DurabilityFactor = (harvestSweep?.DurabilityFactor ?? 1f), CopyFrom = "AtgeirIron", Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static string ResolveHarvestSweepAnimation(NormalizedHarvestSweepConfig? harvestSweep) { string text = harvestSweep?.Animation; if (string.IsNullOrWhiteSpace(text)) { return "atgeir_secondary"; } return text; } private static NormalizedWeaponConfig ResolveDefaultMeleeFallbacks(ItemDrop itemDrop, NormalizedWeaponConfig weaponConfig, NormalizedWeaponConfig? globalMeleeFallback) { if (!weaponConfig.Enabled) { return weaponConfig; } if (!TryCreateDefaultMeleeFallback(itemDrop, globalMeleeFallback, out NormalizedWeaponConfig defaultMeleeFallback) || defaultMeleeFallback == null) { return weaponConfig; } NormalizedWeaponConfig normalizedWeaponConfig = weaponConfig.Clone(); bool flag = normalizedWeaponConfig.HarvestSweep == null && defaultMeleeFallback.HarvestSweep != null; NormalizedWeaponConfig normalizedWeaponConfig2 = normalizedWeaponConfig; if (normalizedWeaponConfig2.HarvestSweep == null) { NormalizedHarvestSweepConfig normalizedHarvestSweepConfig = (normalizedWeaponConfig2.HarvestSweep = defaultMeleeFallback.HarvestSweep); } if (defaultMeleeFallback.HarvestSweep != null) { NormalizedHarvestSweepConfig? harvestSweep2 = normalizedWeaponConfig.HarvestSweep; if (harvestSweep2 != null && harvestSweep2.Enabled && normalizedWeaponConfig.Secondary == null) { normalizedWeaponConfig.Secondary = CreateHarvestSweepSecondary(normalizedWeaponConfig.HarvestSweep); } } if (normalizedWeaponConfig.HasExplicitMeleePreset || normalizedWeaponConfig.MeleePreset != MeleeSpecialPreset.None) { return normalizedWeaponConfig; } if (TryApplyDefaultMeleePreset(normalizedWeaponConfig, defaultMeleeFallback)) { normalizedWeaponConfig2 = normalizedWeaponConfig; if (normalizedWeaponConfig2.Secondary == null) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = (normalizedWeaponConfig2.Secondary = defaultMeleeFallback.Secondary); } normalizedWeaponConfig.MeleePreset = defaultMeleeFallback.MeleePreset; } else if (flag) { normalizedWeaponConfig.MeleePreset = MeleeSpecialPreset.None; } return normalizedWeaponConfig; } private static bool TryApplyDefaultMeleePreset(NormalizedWeaponConfig target, NormalizedWeaponConfig fallback) { switch (fallback.MeleePreset) { case MeleeSpecialPreset.SneakAmbush: if (target.SneakAmbush == null) { target.SneakAmbush = fallback.SneakAmbush; return target.SneakAmbush != null; } break; case MeleeSpecialPreset.CleavingThrust: if (target.CleavingThrust == null) { target.CleavingThrust = fallback.CleavingThrust; return target.CleavingThrust != null; } break; case MeleeSpecialPreset.SpearRain: if (target.SpearRain == null) { target.SpearRain = fallback.SpearRain; return target.SpearRain != null; } break; case MeleeSpecialPreset.ImpactBurst: if (target.ImpactBurst == null) { target.ImpactBurst = fallback.ImpactBurst; return target.ImpactBurst != null; } break; case MeleeSpecialPreset.Boomerang: if (target.Boomerang == null) { target.Boomerang = fallback.Boomerang; return target.Boomerang != null; } break; case MeleeSpecialPreset.SpinningSweep: if (target.SpinningSweep == null) { target.SpinningSweep = fallback.SpinningSweep; return target.SpinningSweep != null; } break; case MeleeSpecialPreset.LaunchSlam: if (target.LaunchSlam == null) { target.LaunchSlam = fallback.LaunchSlam; return target.LaunchSlam != null; } break; case MeleeSpecialPreset.KnockbackChain: if (target.KnockbackChain == null) { target.KnockbackChain = fallback.KnockbackChain; return target.KnockbackChain != null; } break; case MeleeSpecialPreset.Aftershock: if (target.Aftershock == null) { target.Aftershock = fallback.Aftershock; return target.Aftershock != null; } break; case MeleeSpecialPreset.RiftTrail: if (target.RiftTrail == null) { target.RiftTrail = fallback.RiftTrail; return target.RiftTrail != null; } break; case MeleeSpecialPreset.FractureLine: if (target.FractureLine == null) { target.FractureLine = fallback.FractureLine; return target.FractureLine != null; } break; } return false; } } internal static class SecondaryAttackWarningLog { private static readonly HashSet ReportedWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ReportedIssues = new HashSet(StringComparer.OrdinalIgnoreCase); internal static bool TryMarkWarning(string key) { return ReportedWarnings.Add(key); } internal static bool TryMarkIssue(string key) { return ReportedIssues.Add(key); } internal static void WarnOnce(string key, string message, bool emit = true) { if (emit && TryMarkWarning(key)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)message); } } } internal static class SecondaryCooldownGroupSystem { private readonly struct CooldownEntry { internal double ReadyAt { get; } internal float Duration { get; } internal Sprite? Icon { get; } internal CooldownEntry(double readyAt, float duration, Sprite? icon) { ReadyAt = readyAt; Duration = duration; Icon = icon; } } private sealed class CharacterCooldownState { public int ApplyRevision { get; set; } public Dictionary EntriesByGroup { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public List UpdateKeys { get; } = new List(); } private static readonly ConditionalWeakTable Cooldowns = new ConditionalWeakTable(); internal static bool IsReady(Character attacker, string cooldownGroup) { if ((Object)(object)attacker == (Object)null || string.IsNullOrWhiteSpace(cooldownGroup)) { return true; } if (ShouldBypassCooldowns(attacker)) { return true; } string key = cooldownGroup.Trim(); CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); EnsureCurrentApplyRevision(value); CooldownEntry entry; return !TryGetActive(value, key, SecondaryAttackManager.GetNetworkTimeSeconds(), out entry); } internal static bool TryConsume(Character attacker, string cooldownGroup, float duration, Sprite? icon) { if ((Object)(object)attacker == (Object)null) { return true; } if (ShouldBypassCooldowns(attacker)) { return true; } if (string.IsNullOrWhiteSpace(cooldownGroup)) { return true; } string key = cooldownGroup.Trim(); CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); EnsureCurrentApplyRevision(value); double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); if (TryGetActive(value, key, networkTimeSeconds, out var _)) { return false; } ClearCooldownState(value, key); float num = Mathf.Max(0f, duration); if (num <= 0f) { return true; } value.EntriesByGroup[key] = new CooldownEntry(networkTimeSeconds + (double)num, num, icon); return true; } internal static bool IsCooldownActive(Character attacker, string cooldownGroup, out float remaining) { remaining = 0f; if ((Object)(object)attacker == (Object)null || string.IsNullOrWhiteSpace(cooldownGroup)) { return false; } if (ShouldBypassCooldowns(attacker)) { return false; } string key = cooldownGroup.Trim(); if (!Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { return false; } EnsureCurrentApplyRevision(value); double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); if (!TryGetActive(value, key, networkTimeSeconds, out var entry)) { return false; } remaining = (float)Math.Max(0.0, entry.ReadyAt - networkTimeSeconds); return true; } internal static void UpdateActiveCooldowns(Player player) { if (!((Object)(object)player == (Object)null) && !ShouldBypassCooldowns((Character)(object)player) && Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { EnsureCurrentApplyRevision(value); PruneExpired(value, SecondaryAttackManager.GetNetworkTimeSeconds()); } } internal static void CollectHudEntries(Player player, List entries) { if ((Object)(object)player == (Object)null || entries == null || ShouldBypassCooldowns((Character)(object)player) || !Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { return; } EnsureCurrentApplyRevision(value); double networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); PruneExpired(value, networkTimeSeconds); foreach (CooldownEntry value2 in value.EntriesByGroup.Values) { float remaining = (float)Math.Max(0.0, value2.ReadyAt - networkTimeSeconds); entries.Add(new SecondaryCooldownHudSystem.Entry(value2.Icon, remaining, value2.Duration)); } } internal static void ClearAllCooldowns(Character attacker) { if (Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { value.EntriesByGroup.Clear(); value.ApplyRevision = SecondaryAttackFacade.CurrentAppliedWorldSnapshot.ApplyRevision; } } private static bool ShouldBypassCooldowns(Character attacker) { if (!SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { return false; } ClearAllCooldowns(attacker); return true; } private static bool TryGetActive(CharacterCooldownState state, string key, double now, out CooldownEntry entry) { if (state.EntriesByGroup.TryGetValue(key, out entry) && now < entry.ReadyAt) { return true; } ClearCooldownState(state, key); entry = default(CooldownEntry); return false; } private static void PruneExpired(CharacterCooldownState state, double now) { List updateKeys = state.UpdateKeys; updateKeys.Clear(); foreach (var (item, cooldownEntry2) in state.EntriesByGroup) { if (cooldownEntry2.ReadyAt <= now) { updateKeys.Add(item); } } foreach (string item2 in updateKeys) { ClearCooldownState(state, item2); } updateKeys.Clear(); } private static void ClearCooldownState(CharacterCooldownState state, string key) { state.EntriesByGroup.Remove(key); } private static void EnsureCurrentApplyRevision(CharacterCooldownState state) { int applyRevision = SecondaryAttackFacade.CurrentAppliedWorldSnapshot.ApplyRevision; if (state.ApplyRevision != applyRevision) { state.EntriesByGroup.Clear(); state.ApplyRevision = applyRevision; } } } internal static class SummonPrefabOverrideSystem { private sealed class SceneState { internal Dictionary OriginalSpawnPrefabs { get; } = new Dictionary(); internal Dictionary CreatedClonePrefabs { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); internal Dictionary CloneSourcePrefabs { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } private const string CloneContainerName = "SecondaryAttacks_SummonClonedPrefabs"; private const string FriendlyTameablePrefabName = "Skeleton_Friendly"; private const string DefaultSummonNameFormat = "{0} (Summon)"; private const float DefaultSummonOwnerLogoutSeconds = 120f; private static readonly ConditionalWeakTable SceneStates = new ConditionalWeakTable(); private static GameObject? _cloneContainer; internal static void Apply(ZNetScene scene, IReadOnlyDictionary overrides, bool emitMissingWarnings) { if ((Object)(object)scene == (Object)null) { return; } SceneState value = SceneStates.GetValue(scene, (ZNetScene _) => new SceneState()); RestoreOriginalSpawnPrefabs(value); PruneUnusedClonePrefabs(scene, value, BuildDesiredClonePrefabNames(overrides)); if (overrides.Count == 0) { return; } int num = 0; foreach (NormalizedMagicSummonOverrideConfig value2 in overrides.Values) { if (value2.Summons.Count == 0 || !TryResolveTargetSpawnAbility(scene, value2, emitMissingWarnings, out SpawnAbility spawnAbility, out string _) || (Object)(object)spawnAbility == (Object)null) { continue; } CaptureOriginalSpawnPrefabs(value, spawnAbility); List list = new List(); foreach (NormalizedMagicSummonCloneConfig summon in value2.Summons) { GameObject val = CreateOrUpdateClonePrefab(scene, value, value2.EntryId, summon, emitMissingWarnings); if ((Object)(object)val != (Object)null) { for (int num2 = 0; num2 < summon.Weight; num2++) { list.Add(val); } } } if (list.Count != 0) { spawnAbility.m_spawnPrefab = list.ToArray(); num++; } } if (num > 0) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"Applied {num} magic summon prefab override(s)."); } } private static HashSet BuildDesiredClonePrefabNames(IReadOnlyDictionary overrides) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (NormalizedMagicSummonOverrideConfig value in overrides.Values) { foreach (NormalizedMagicSummonCloneConfig summon in value.Summons) { if (!string.IsNullOrWhiteSpace(summon.ClonePrefab)) { hashSet.Add(summon.ClonePrefab); } } } return hashSet; } private static void PruneUnusedClonePrefabs(ZNetScene scene, SceneState state, HashSet desiredClonePrefabNames) { List list = new List(); foreach (var (item, val2) in state.CreatedClonePrefabs) { if (!desiredClonePrefabNames.Contains(item)) { if ((Object)(object)val2 != (Object)null) { UnregisterClonePrefab(scene, val2); Object.Destroy((Object)(object)val2); } list.Add(item); } } foreach (string item2 in list) { state.CreatedClonePrefabs.Remove(item2); state.CloneSourcePrefabs.Remove(item2); } } private static void RestoreOriginalSpawnPrefabs(SceneState state) { foreach (var (val2, array2) in state.OriginalSpawnPrefabs) { if (!((Object)(object)val2 == (Object)null)) { val2.m_spawnPrefab = (GameObject[])array2.Clone(); } } state.OriginalSpawnPrefabs.Clear(); } private static void CaptureOriginalSpawnPrefabs(SceneState state, SpawnAbility spawnAbility) { if (!state.OriginalSpawnPrefabs.ContainsKey(spawnAbility)) { GameObject[] array = spawnAbility.m_spawnPrefab ?? Array.Empty(); state.OriginalSpawnPrefabs[spawnAbility] = (GameObject[])array.Clone(); } } private static bool TryResolveTargetSpawnAbility(ZNetScene scene, NormalizedMagicSummonOverrideConfig summonOverride, bool emitMissingWarnings, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; string text = summonOverride.EntryId.Trim(); GameObject val = ResolveItemPrefab(scene, text); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (val2?.m_itemData?.m_shared == null) { WarnOnce(emitMissingWarnings, "missing_item:" + summonOverride.EntryId + ":" + text, "Summon override '" + summonOverride.EntryId + "' skipped: item prefab '" + text + "' was not found."); return false; } if (SummonSpawnAbilityResolver.TryResolve(val2, out spawnAbility, out targetName)) { return true; } WarnOnce(emitMissingWarnings, "missing_item_spawnability:" + summonOverride.EntryId + ":" + text, "Summon override '" + summonOverride.EntryId + "' skipped: item prefab '" + text + "' has no attack projectile with a SpawnAbility payload."); return false; } private static GameObject? ResolveItemPrefab(ZNetScene scene, string itemPrefabName) { if (string.IsNullOrWhiteSpace(itemPrefabName)) { return null; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemPrefabName) : null); if (!((Object)(object)val != (Object)null)) { return scene.GetPrefab(itemPrefabName); } return val; } private static GameObject? CreateOrUpdateClonePrefab(ZNetScene scene, SceneState state, string entryId, NormalizedMagicSummonCloneConfig summon, bool emitMissingWarnings) { GameObject prefab = scene.GetPrefab(summon.SourcePrefab); if ((Object)(object)prefab == (Object)null) { WarnOnce(emitMissingWarnings, "missing_source:" + entryId + ":" + summon.SourcePrefab, "Summon override '" + entryId + "' skipped clone '" + summon.ClonePrefab + "': sourcePrefab '" + summon.SourcePrefab + "' was not found in ZNetScene."); return null; } if (state.CreatedClonePrefabs.TryGetValue(summon.ClonePrefab, out GameObject value) && (Object)(object)value != (Object)null) { if (!state.CloneSourcePrefabs.TryGetValue(summon.ClonePrefab, out string value2) || string.Equals(value2, summon.SourcePrefab, StringComparison.OrdinalIgnoreCase)) { ConfigureFriendlySummonClone(scene, value, prefab, summon); return value; } UnregisterClonePrefab(scene, value); Object.Destroy((Object)(object)value); state.CreatedClonePrefabs.Remove(summon.ClonePrefab); state.CloneSourcePrefabs.Remove(summon.ClonePrefab); } if ((Object)(object)scene.GetPrefab(summon.ClonePrefab) != (Object)null) { WarnOnce(emitMissingWarnings, "clone_name_exists:" + entryId + ":" + summon.ClonePrefab, "Summon override '" + entryId + "' skipped clone '" + summon.ClonePrefab + "': that prefab name already exists in ZNetScene."); return null; } GameObject val = Object.Instantiate(prefab, GetCloneContainerTransform()); ((Object)val).name = summon.ClonePrefab; ConfigureFriendlySummonClone(scene, val, prefab, summon); if ((Object)(object)val.GetComponent() == (Object)null) { WarnOnce(emitMissingWarnings, "clone_not_character:" + entryId + ":" + summon.ClonePrefab, "Summon override '" + entryId + "' skipped clone '" + summon.ClonePrefab + "': sourcePrefab '" + summon.SourcePrefab + "' has no Character component."); Object.Destroy((Object)(object)val); return null; } RegisterClonePrefab(scene, val); state.CreatedClonePrefabs[summon.ClonePrefab] = val; state.CloneSourcePrefabs[summon.ClonePrefab] = summon.SourcePrefab; return val; } private static void ConfigureFriendlySummonClone(ZNetScene scene, GameObject clone, GameObject sourcePrefab, NormalizedMagicSummonCloneConfig summon) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) clone.SetActive(sourcePrefab.activeSelf); Character component = clone.GetComponent(); if (!((Object)(object)component == (Object)null)) { string text = ResolveSummonDisplayName(sourcePrefab, summon); if (!string.IsNullOrWhiteSpace(text)) { component.m_name = text; } Character component2 = sourcePrefab.GetComponent(); component.m_health = ((summon.Health.HasValue && summon.Health.Value > 0f) ? summon.Health.Value : (component2?.m_health ?? component.m_health)); component.m_group = ""; component.m_defeatSetGlobalKey = ""; component.m_faction = (Faction)0; TryCopyFriendlyDeathEffects(scene, component); ConfigureFriendlyMonsterAi(clone); RemoveIfPresent(clone); RemoveIfPresent(clone); ConfigureTameable(scene, clone, component); } } private static string ResolveSummonDisplayName(GameObject sourcePrefab, NormalizedMagicSummonCloneConfig summon) { if (!string.IsNullOrWhiteSpace(summon.DisplayName)) { return summon.DisplayName.Trim(); } string text = sourcePrefab.GetComponent()?.m_name ?? ""; if (string.IsNullOrWhiteSpace(text)) { text = Utils.GetPrefabName(sourcePrefab); } if (string.IsNullOrWhiteSpace(text)) { text = ((Object)sourcePrefab).name; } string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text.Trim()).Trim() : text.Trim()); if (string.IsNullOrWhiteSpace(text2)) { text2 = text.Trim(); } if (!HasSummonNameSuffix(text2)) { return SecondaryAttackLocalization.Format("$sa_summon_name_format", "{0} (Summon)", text2); } return text2; } private static bool HasSummonNameSuffix(string localizedName) { string value = SecondaryAttackLocalization.Format("$sa_summon_name_format", "{0} (Summon)", ""); if (!string.IsNullOrEmpty(value) && localizedName.EndsWith(value, StringComparison.Ordinal)) { return true; } string value2 = string.Format("{0} (Summon)", ""); if (!string.IsNullOrEmpty(value2)) { return localizedName.EndsWith(value2, StringComparison.Ordinal); } return false; } private static void TryCopyFriendlyDeathEffects(ZNetScene scene, Character character) { GameObject prefab = scene.GetPrefab("Skeleton_Friendly"); Character val = ((prefab != null) ? prefab.GetComponent() : null); if ((Object)(object)val != (Object)null && val.m_deathEffects != null) { character.m_deathEffects = val.m_deathEffects; } } private static void ConfigureFriendlyMonsterAi(GameObject clone) { MonsterAI component = clone.GetComponent(); if (!((Object)(object)component == (Object)null)) { ((BaseAI)component).m_hearRange = Mathf.Max(((BaseAI)component).m_hearRange, 9999f); component.m_alertRange = Mathf.Max(component.m_alertRange, 20f); } } private static void ConfigureTameable(ZNetScene scene, GameObject clone, Character character) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) Tameable val = clone.GetComponent(); if ((Object)(object)val == (Object)null) { val = clone.AddComponent(); } GameObject prefab = scene.GetPrefab("Skeleton_Friendly"); Tameable val2 = ((prefab != null) ? prefab.GetComponent() : null); if ((Object)(object)val2 != (Object)null) { val.m_tamedEffect = val2.m_tamedEffect; val.m_sootheEffect = val2.m_sootheEffect; val.m_petEffect = val2.m_petEffect; } val.m_startsTamed = true; val.m_commandable = false; val.m_unsummonDistance = 0f; val.m_unsummonOnOwnerLogoutSeconds = 120f; val.m_levelUpOwnerSkill = (SkillType)10; val.m_dropSaddleOnDeath = false; val.m_unSummonEffect = character.m_deathEffects; } private static void RemoveIfPresent(GameObject clone) where T : Component { T component = clone.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } private static Transform GetCloneContainerTransform() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if ((Object)(object)_cloneContainer != (Object)null) { return _cloneContainer.transform; } _cloneContainer = new GameObject("SecondaryAttacks_SummonClonedPrefabs"); _cloneContainer.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_cloneContainer); return _cloneContainer.transform; } private static void RegisterClonePrefab(ZNetScene scene, GameObject clone) { if (!scene.m_prefabs.Contains(clone)) { scene.m_prefabs.Add(clone); } scene.m_namedPrefabs[StringExtensionMethods.GetStableHashCode(((Object)clone).name)] = clone; } private static void UnregisterClonePrefab(ZNetScene scene, GameObject clone) { scene.m_prefabs.Remove(clone); int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)clone).name); if (scene.m_namedPrefabs.TryGetValue(stableHashCode, out var value) && (Object)(object)value == (Object)(object)clone) { scene.m_namedPrefabs.Remove(stableHashCode); } } private static void WarnOnce(bool emitMissingWarnings, string warningKey, string message) { SecondaryAttackWarningLog.WarnOnce("summon_override:" + warningKey, message, emitMissingWarnings); } } internal static class SummonSpawnAbilityResolver { internal static bool TryResolve(ItemDrop? itemDrop, out SpawnAbility? spawnAbility, out string targetName) { return TryResolve(itemDrop?.m_itemData?.m_shared, out spawnAbility, out targetName); } internal static bool TryResolve(SharedData? shared, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; if (shared != null) { if (!TryResolveFromAttack(shared.m_attack, out spawnAbility, out targetName)) { return TryResolveFromAttack(shared.m_secondaryAttack, out spawnAbility, out targetName); } return true; } return false; } private static bool TryResolveFromAttack(Attack? attack, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; if (attack == null) { return false; } GameObject attackProjectile = attack.m_attackProjectile; IProjectile val = (((Object)(object)attackProjectile != (Object)null) ? attackProjectile.GetComponent() : null); SpawnAbility val2 = (SpawnAbility)(object)((val is SpawnAbility) ? val : null); if (val2 != null) { spawnAbility = val2; targetName = ((Object)attackProjectile).name; return true; } Projectile val3 = (Projectile)(object)((val is Projectile) ? val : null); if (val3 != null && TryResolveSpawnOnHit(val3.m_spawnOnHit, out spawnAbility, out targetName)) { return true; } if (attack.m_spawnOnHitChance > 0f) { return TryResolveSpawnOnHit(attack.m_spawnOnHit, out spawnAbility, out targetName); } return false; } private static bool TryResolveSpawnOnHit(GameObject? prefab, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = (SpawnAbility?)(((Object)(object)prefab != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)spawnAbility != (Object)null) { targetName = ((Object)prefab).name; return true; } targetName = (((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : ""); return false; } } [HarmonyPatch] internal static class CharacterSummonEmpowerMovementPatch { 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(CharacterSummonEmpowerMovementPatch), "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 { SecondaryAttackWarningLog.WarnOnce("summon-empower-movement-target-" + key, "Summon Empower could not patch Character." + key + "(float); that movement mode will not receive its speed bonus."); } } if (list.Count == 0) { SecondaryAttackWarningLog.WarnOnce("summon-empower-movement-no-targets", "Summon Empower 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 = 1f; if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && StaffRuntimeSystem.TryGetSummonEmpower(__instance, out var moveSpeedFactor, out var _, out var _) && !Mathf.Approximately(moveSpeedFactor, 1f)) { _activeMovementFactor = moveSpeedFactor; } } [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) { SecondaryAttackWarningLog.WarnOnce("summon-empower-movement-" + __originalMethod.Name + "-" + text, "Summon Empower could not patch Character." + __originalMethod.Name + "'s " + text + " read; that movement mode's Empower 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 ShieldAccess { private static readonly FieldInfo? SeManCharacterField = AccessTools.Field(typeof(SEMan), "m_character"); private static readonly FieldInfo? ShieldTotalAbsorbDamageField = AccessTools.Field(typeof(SE_Shield), "m_totalAbsorbDamage"); private static readonly FieldInfo? ShieldAbsorbedDamageField = AccessTools.Field(typeof(SE_Shield), "m_damage"); internal static Character? GetSeManCharacter(SEMan seMan) { object? obj = SeManCharacterField?.GetValue(seMan); return (Character?)((obj is Character) ? obj : null); } internal static bool TryReadRemaining(SE_Shield shield, out float remaining, out float remainingTime) { remaining = 0f; remainingTime = 0f; if ((Object)(object)shield == (Object)null || ShieldTotalAbsorbDamageField == null || ShieldAbsorbedDamageField == null) { return false; } if (!(ShieldTotalAbsorbDamageField.GetValue(shield) is float num) || !(ShieldAbsorbedDamageField.GetValue(shield) is float num2)) { return false; } remaining = Mathf.Max(0f, num - num2); remainingTime = Mathf.Max(0f, ((StatusEffect)shield).GetRemaningTime()); if (remaining > 0f) { return remainingTime > 0f; } return false; } } internal static class StickyDetonatorSystem { private sealed class StickyChargeState { public GameObject ProjectilePrefab { get; } public Character Owner { get; } public ItemData Weapon { get; } public ItemData? Ammo { get; } public float HitNoise { get; } public HitData HitData { get; } public SecondaryAttackDefinition Definition { get; } public float Lifetime { get; } public float AoeRadiusFactor { get; } public Vector3 HitPoint { get; set; } public Vector3 HitNormal { get; set; } = Vector3.up; public Vector3 ImpactForward { get; set; } = Vector3.forward; public float ExpiresAt { get; set; } private Transform? Attachment { get; set; } private Vector3 LocalHitPoint { get; set; } private Vector3 LocalHitNormal { get; set; } = Vector3.up; private Vector3 LocalImpactForward { get; set; } = Vector3.forward; public StickyChargeState(GameObject projectilePrefab, Character owner, ItemData weapon, ItemData? ammo, float hitNoise, HitData hitData, SecondaryAttackDefinition definition, float lifetime, float aoeRadiusFactor) { //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_0011: 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_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) ProjectilePrefab = projectilePrefab; Owner = owner; Weapon = weapon; Ammo = ammo; HitNoise = hitNoise; HitData = hitData; Definition = definition; Lifetime = lifetime; AoeRadiusFactor = aoeRadiusFactor; } public void AttachTo(Transform attachment) { //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) //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_0026: 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_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_0040: 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) Attachment = attachment; LocalHitPoint = attachment.InverseTransformPoint(HitPoint); Vector3 val = attachment.InverseTransformDirection(HitNormal); LocalHitNormal = ((Vector3)(ref val)).normalized; val = attachment.InverseTransformDirection(ImpactForward); LocalImpactForward = ((Vector3)(ref val)).normalized; } public void RefreshAttachedPose() { //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_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_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_0049: 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_0059: 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_007b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Attachment == (Object)null)) { HitPoint = Attachment.TransformPoint(LocalHitPoint); Vector3 val = Attachment.TransformDirection(LocalHitNormal); Vector3 val2 = Attachment.TransformDirection(LocalImpactForward); HitNormal = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : Vector3.up); ImpactForward = ((((Vector3)(ref val2)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val2)).normalized : Vector3.forward); } } } private sealed class StickyChargeController : MonoBehaviour { private Projectile _projectile; private bool _finished; public StickyChargeState State { get; private set; } public Character Owner => State.Owner; public bool IsValid { get { if (!_finished && (Object)(object)this != (Object)null && State != null) { return (Object)(object)_projectile != (Object)null; } return false; } } public void Initialize(Projectile projectile, StickyChargeState state) { _projectile = projectile; State = state; } public bool Detonate() { if (_finished) { return false; } _finished = true; StickyDetonatorSystem.Detonate(this); return true; } public void Expire() { if (!_finished) { _finished = true; ProjectileRuntimeSystem.DestroyProjectileObject(((Component)this).gameObject); } } private void Update() { if (!_finished && ((Object)(object)State.Owner == (Object)null || State.Owner.IsDead() || Time.time >= State.ExpiresAt)) { Expire(); } } private void OnDestroy() { _finished = true; Unregister(this); } } private sealed class StickyDetonatorInputState { public bool WasBlocking { get; set; } } private const float StickSurfaceOffset = 0.03f; private static readonly ConditionalWeakTable PendingProjectiles = new ConditionalWeakTable(); private static readonly List ActiveCharges = new List(); private static readonly ConditionalWeakTable InputStates = new ConditionalWeakTable(); private static bool _detonating; internal static void RegisterLaunchedProjectile(Projectile projectile, GameObject projectilePrefab, Character owner, ItemData weapon, ItemData? ammo, float hitNoise, SecondaryAttackDefinition definition, ProjectileSecondaryBehavior behavior) { if (!((Object)(object)projectile == (Object)null) && !((Object)(object)projectilePrefab == (Object)null) && !((Object)(object)owner == (Object)null) && !((Object)(object)weapon?.m_dropPrefab == (Object)null) && projectile.m_originalHitData != null) { TrimOwnerCharges(owner, Mathf.Max(1, behavior.MaxCharges - 1)); SuppressProjectileItemDrops(projectile); PendingProjectiles.Remove(projectile); PendingProjectiles.Add(projectile, new StickyChargeState(projectilePrefab, owner, weapon, ammo, hitNoise, projectile.m_originalHitData.Clone(), definition, Mathf.Max(0.5f, behavior.SentinelLifetime), Mathf.Max(0.01f, behavior.AoeRadiusFactor))); } } internal static bool TryHandleProjectileHit(Projectile projectile, Collider collider, Vector3 hitPoint, bool water, Vector3 normal) { //IL_005e: 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) if (_detonating || (Object)(object)projectile == (Object)null || !PendingProjectiles.TryGetValue(projectile, out StickyChargeState value)) { return false; } if (water) { PendingProjectiles.Remove(projectile); return false; } if ((Object)(object)collider != (Object)null && (Object)(object)ProjectileRuntimeSystem.GetHitCharacter(collider) == (Object)(object)value.Owner) { return true; } PendingProjectiles.Remove(projectile); StickProjectile(projectile, collider, hitPoint, normal, value); return true; } internal static void UpdateInput(Player player, ref bool blocking) { StickyDetonatorInputState value = InputStates.GetValue(player, (Player _) => new StickyDetonatorInputState()); if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !IsStickyDetonatorInputContext(player)) { value.WasBlocking = false; return; } bool num = ZInput.GetButtonDown("Block") || (blocking && !value.WasBlocking); value.WasBlocking = blocking; if (blocking) { blocking = false; } if (num) { if (CountActiveCharges((Character)(object)player) > 0) { TryPlayDetonateAnimation(player, ResolveDetonateAnimation(player)); } DetonateAll(player); } } internal static bool ShouldSuppressBlock(Humanoid humanoid) { Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { return IsStickyDetonatorInputContext(val); } return false; } internal static bool ShouldShowDetonateBlockHint(Player? player) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { return IsHoldingStickyDetonator(player); } return false; } private static bool IsStickyDetonatorInputContext(Player player) { if (IsHoldingStickyDetonator(player)) { return true; } if (((Humanoid)player).GetRightItem() == null) { return HasActiveCharges((Character)(object)player); } return false; } private static bool IsHoldingStickyDetonator(Player player) { ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (currentWeapon != null && SecondaryAttackRuntimeFacade.TryGetDefinition(currentWeapon, out SecondaryAttackDefinition definition)) { if (definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) { return projectileSecondaryBehavior.Preset == SecondaryAttackPreset.StickyDetonator; } return false; } return false; } private static bool HasActiveCharges(Character owner) { return CountActiveCharges(owner) > 0; } private static int CountActiveCharges(Character owner) { int num = 0; for (int num2 = ActiveCharges.Count - 1; num2 >= 0; num2--) { StickyChargeController stickyChargeController = ActiveCharges[num2]; if ((Object)(object)stickyChargeController == (Object)null || !stickyChargeController.IsValid) { ActiveCharges.RemoveAt(num2); } else if ((Object)(object)stickyChargeController.Owner == (Object)(object)owner) { num++; } } return num; } private static string ResolveDetonateAnimation(Player player) { ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (currentWeapon != null && SecondaryAttackRuntimeFacade.TryGetDefinition(currentWeapon, out SecondaryAttackDefinition definition) && definition.Behavior is ProjectileSecondaryBehavior { Preset: SecondaryAttackPreset.StickyDetonator } projectileSecondaryBehavior && !string.IsNullOrWhiteSpace(projectileSecondaryBehavior.DetonateAnimation)) { return projectileSecondaryBehavior.DetonateAnimation; } for (int num = ActiveCharges.Count - 1; num >= 0; num--) { StickyChargeController stickyChargeController = ActiveCharges[num]; if (!((Object)(object)stickyChargeController == (Object)null) && stickyChargeController.IsValid && !((Object)(object)stickyChargeController.Owner != (Object)(object)player) && stickyChargeController.State.Definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior2 && !string.IsNullOrWhiteSpace(projectileSecondaryBehavior2.DetonateAnimation)) { return projectileSecondaryBehavior2.DetonateAnimation; } } return ""; } private static bool TryPlayDetonateAnimation(Player player, string animation) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(animation)) { return false; } string text = animation.Trim(); if (((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).InMinorAction() || ((Character)player).IsStaggering() || !((Character)player).CanMove()) { return false; } ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (currentWeapon == null) { ZSyncAnimation zAnim = ((Character)player).GetZAnim(); if (zAnim != null) { zAnim.SetTrigger(text); } return true; } Attack currentAttack = ((Humanoid)player).m_currentAttack; Attack val = CreateDetonateAnimationAttack(text); ZSyncAnimation zAnim2 = ((Character)player).GetZAnim(); CharacterAnimEvent componentInChildren = ((Component)player).GetComponentInChildren(); VisEquipment component = ((Component)player).GetComponent(); if ((Object)(object)zAnim2 == (Object)null || (Object)(object)componentInChildren == (Object)null || (Object)(object)component == (Object)null || !val.Start((Humanoid)(object)player, ((Character)player).m_body, zAnim2, componentInChildren, component, currentWeapon, currentAttack, ((Character)player).GetTimeSinceLastAttack(), ((Humanoid)player).GetAttackDrawPercentage())) { return false; } ((Humanoid)player).m_currentAttack = val; ((Humanoid)player).m_currentAttackIsSecondary = true; return true; } private static Attack CreateDetonateAnimationAttack(string animation) { //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_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_0013: 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_0029: 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_003f: 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_0055: 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_006b: 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_0079: 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_0090: Expected O, but got Unknown return new Attack { m_attackType = (AttackType)5, m_attackAnimation = animation, m_attackStamina = 0f, m_attackAdrenaline = 0f, m_attackUseAdrenaline = 0f, m_attackEitr = 0f, m_attackHealth = 0f, m_attackHealthPercentage = 0f, m_attackStartNoise = 0f, m_attackHitNoise = 0f, m_consumeItem = false, m_requiresReload = false, m_speedFactor = 1f, m_speedFactorRotation = 1f }; } private static void StickProjectile(Projectile projectile, Collider? collider, Vector3 hitPoint, Vector3 normal, StickyChargeState state) { //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) //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_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_0020: 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_0051: 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_007d: 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_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_0046: 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_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_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_00dc: 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_00ff: 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) Vector3 val = ((((Vector3)(ref normal)).sqrMagnitude > 0.001f) ? ((Vector3)(ref normal)).normalized : Vector3.up); Vector3 val2 = hitPoint; if (val2 == Vector3.zero && (Object)(object)collider != (Object)null) { val2 = SecondaryAttackManager.ResolveSafeClosestPoint(collider, ((Component)projectile).transform.position); } state.HitPoint = val2; state.HitNormal = val; Vector3 velocity = projectile.GetVelocity(); state.ImpactForward = ((((Vector3)(ref velocity)).sqrMagnitude > 0.001f) ? ((Vector3)(ref velocity)).normalized : ((Component)projectile).transform.forward); state.ExpiresAt = Time.time + state.Lifetime; SuppressProjectileItemDrops(projectile); ProjectileAccess.SetVelocity(projectile, Vector3.zero); ProjectileAccess.SetDidHit(projectile, didHit: true); ((Behaviour)projectile).enabled = false; ((Component)projectile).transform.position = val2 + val * 0.03f; ((Component)projectile).transform.rotation = ResolveImpactRotation(-val); Rigidbody[] componentsInChildren = ((Component)projectile).GetComponentsInChildren(); foreach (Rigidbody obj in componentsInChildren) { obj.linearVelocity = Vector3.zero; obj.angularVelocity = Vector3.zero; obj.isKinematic = true; obj.detectCollisions = false; } Collider[] componentsInChildren2 = ((Component)projectile).GetComponentsInChildren(); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } Character val3 = (((Object)(object)collider != (Object)null) ? ProjectileRuntimeSystem.GetHitCharacter(collider) : null); GameObject val4 = (((Object)(object)collider != (Object)null) ? Projectile.FindHitObject(collider) : null); Transform val5 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).transform : ((val4 != null) ? val4.transform : null)); if ((Object)(object)val5 != (Object)null && (Object)(object)((Component)val5).gameObject != (Object)(object)((Component)projectile).gameObject && !((Component)val5).gameObject.isStatic) { ((Component)projectile).transform.SetParent(val5, true); state.AttachTo(val5); } StickyChargeController stickyChargeController = ((Component)projectile).GetComponent() ?? ((Component)projectile).gameObject.AddComponent(); stickyChargeController.Initialize(projectile, state); ActiveCharges.Add(stickyChargeController); } private static int DetonateAll(Player player) { int num = 0; for (int num2 = ActiveCharges.Count - 1; num2 >= 0; num2--) { StickyChargeController stickyChargeController = ActiveCharges[num2]; if ((Object)(object)stickyChargeController == (Object)null || !stickyChargeController.IsValid) { ActiveCharges.RemoveAt(num2); } else if (!((Object)(object)stickyChargeController.Owner != (Object)(object)player) && stickyChargeController.Detonate()) { num++; } } return num; } private static void TrimOwnerCharges(Character owner, int keepCount) { if (keepCount < 0) { keepCount = 0; } int num = 0; for (int num2 = ActiveCharges.Count - 1; num2 >= 0; num2--) { StickyChargeController stickyChargeController = ActiveCharges[num2]; if ((Object)(object)stickyChargeController == (Object)null || !stickyChargeController.IsValid) { ActiveCharges.RemoveAt(num2); } else if (!((Object)(object)stickyChargeController.Owner != (Object)(object)owner)) { num++; if (num > keepCount) { stickyChargeController.Expire(); } } } } private static void Detonate(StickyChargeController controller) { StickyChargeState state = controller.State; state.RefreshAttachedPose(); if (!TrySpawnAoeExplosion(state)) { SpawnProjectileExplosion(state); } ProjectileRuntimeSystem.DestroyProjectileObject(((Component)controller).gameObject); } private static bool TrySpawnAoeExplosion(StickyChargeState state) { //IL_0053: 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_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_0070: 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_008a: 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_00f7: Unknown result type (might be due to invalid IL or missing references) Projectile component = state.ProjectilePrefab.GetComponent(); GameObject val = component?.m_spawnOnHit; if ((Object)(object)val == (Object)null) { return false; } Aoe obj = val.GetComponent() ?? val.GetComponentInChildren(); IProjectile val2 = val.GetComponent() ?? val.GetComponentInChildren(); if ((Object)(object)obj == (Object)null || val2 == null) { return false; } Quaternion val3 = ResolveSurfaceAlignedRotation(state.HitNormal, state.ImpactForward); component.m_hitEffects.Create(state.HitPoint, val3, (Transform)null, 1f, -1); component.m_spawnOnHitEffects.Create(state.HitPoint, val3, (Transform)null, 1f, -1); GameObject val4 = Object.Instantiate(val, state.HitPoint, val3); Aoe val5 = val4.GetComponent() ?? val4.GetComponentInChildren(); IProjectile component2 = val4.GetComponent(); if ((Object)(object)val5 == (Object)null || component2 == null) { ProjectileRuntimeSystem.DestroyProjectileObject(val4); return false; } val5.m_radius *= state.AoeRadiusFactor; component2.Setup(state.Owner, Vector3.zero, state.HitNoise, state.HitData.Clone(), state.Weapon, state.Ammo); return true; } private static void SpawnProjectileExplosion(StickyChargeState state) { //IL_0001: 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_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_0066: 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) Quaternion val = ResolveSurfaceAlignedRotation(state.HitNormal, state.ImpactForward); GameObject val2 = Object.Instantiate(state.ProjectilePrefab, state.HitPoint, val); Projectile component = val2.GetComponent(); IProjectile component2 = val2.GetComponent(); if ((Object)(object)component == (Object)null || component2 == null) { ProjectileRuntimeSystem.DestroyProjectileObject(val2); return; } SuppressProjectileItemDrops(component); component.m_aoe *= state.AoeRadiusFactor; component2.Setup(state.Owner, Vector3.zero, state.HitNoise, state.HitData.Clone(), state.Weapon, state.Ammo); component.m_aoe *= state.AoeRadiusFactor; bool detonating = _detonating; _detonating = true; try { component.OnHit((Collider)null, state.HitPoint, false, state.HitNormal); } finally { _detonating = detonating; } } private static Quaternion ResolveImpactRotation(Vector3 normal) { //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) //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_001e: 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_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_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_003a: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((((Vector3)(ref normal)).sqrMagnitude > 0.001f) ? ((Vector3)(ref normal)).normalized : Vector3.up); if (Mathf.Abs(Vector3.Dot(val, Vector3.up)) > 0.98f) { return Quaternion.LookRotation(val, Vector3.forward); } return Quaternion.LookRotation(val, Vector3.up); } private static Quaternion ResolveSurfaceAlignedRotation(Vector3 normal, Vector3 preferredForward) { //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) //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_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_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_003e: 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_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_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_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) Vector3 val = ((((Vector3)(ref normal)).sqrMagnitude > 0.001f) ? ((Vector3)(ref normal)).normalized : Vector3.up); Vector3 val2 = Vector3.ProjectOnPlane(preferredForward, val); if (((Vector3)(ref val2)).sqrMagnitude <= 0.001f) { val2 = Vector3.ProjectOnPlane(Vector3.forward, val); } if (((Vector3)(ref val2)).sqrMagnitude <= 0.001f) { val2 = Vector3.ProjectOnPlane(Vector3.right, val); } return Quaternion.LookRotation(((Vector3)(ref val2)).normalized, val); } private static void SuppressProjectileItemDrops(Projectile projectile) { projectile.m_respawnItemOnHit = false; projectile.m_spawnItem = null; projectile.m_spawnOnTtl = false; } private static void Unregister(StickyChargeController controller) { ActiveCharges.Remove(controller); } } public static class WarfareTweaksBridge { public static bool IsGeneratedDamageActive { get { if (!LaunchSlamSystem.IsApplyingLandingDamage && !KnockbackChainSystem.IsApplyingChainDamage) { return MeleeProjectileHitCascadeSystem.IsApplyingImpactBurstDamage; } return true; } } public static bool ShouldSuppressProjectile(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return false; } if (SecondaryAttackRuntimeContext.TryGetProjectileAttackAttribution(projectile, out ProjectileAttackAttribution attribution) && attribution != null) { if (!attribution.SecondaryAttack) { return attribution.DisableCurrentAttackFallback; } return true; } return false; } } } 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; } } }