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.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using 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.0.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [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 _registered; 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; _registered = true; SecondaryAttackManager.RegisterAsyncSecondaryWork((Character?)(object)attack.m_character); ((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 void OnDestroy() { if (_registered) { SecondaryAttackManager.UnregisterAsyncSecondaryWork((Character?)(object)Attack?.m_character); _registered = false; } } } 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_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: 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_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0226: 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_028e: 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_02ab: 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(); 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); } } 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"; internal const float BackstabCooldownSeconds = 300f; 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 bool _allowConfiguredBloodMagicRaise; 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 bool ShouldBlockBloodMagicRaise(SkillType skillType) { //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 == 10 && IsHealthCostSkillGainEnabled()) { return !_allowConfiguredBloodMagicRaise; } return false; } 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) { return; } float healthCostSkillRaiseFactor = GetHealthCostSkillRaiseFactor(); float num2 = num * healthCostSkillRaiseFactor; if (num2 <= 0f) { return; } _allowConfiguredBloodMagicRaise = true; try { ((Character)val).RaiseSkill((SkillType)10, num2); } finally { _allowConfiguredBloodMagicRaise = false; } } } internal static class BowSecondaryKeyHintSystem { private const int MissingBlockHintRefreshFrames = 30; private static readonly string[] BowSecondaryHintKeys = new string[1]; private static readonly string[] DetonateBlockHintKeys = new string[1]; 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)) { BowSecondaryHintKeys[0] = text; _bowSecondaryHint.SetKeys(BowSecondaryHintKeys, hideExtraTexts: true); _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) { DetonateBlockHintKeys[0] = buttonLabel; hint.Set(text, DetonateBlockHintKeys, 0f, hideExtraTexts: true); } 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 TryApplyToProjectileSetup(Projectile projectile, ItemData item) { MeleeProjectileHitCascadeSystem.TryDescribeSpearRainFollowupProjectile(projectile, out string _); if ((Object)(object)projectile == (Object)null || ActiveCopiedThrowBursts.Count == 0) { return; } Attack val = ActiveCopiedThrowBursts[ActiveCopiedThrowBursts.Count - 1]; if (val == null) { return; } 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); if (!SecondaryAttackStartAttackDispatch.ShouldSkipProjectilePresetEffectsForCooldown(val, out string _)) { 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, out string _)) { 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, "CopiedThrowProjectileVisualSystem.Setup"); } } 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; } SecondaryAttackPerformanceLog.Start(); try { SecondaryAttackPerformanceLog.Start(); PrepareProjectileForVisualSwap(projectile); SecondaryAttackPerformanceLog.Start(); MarkCopiedThrowProjectile(projectile, context); SecondaryAttackPerformanceLog.Start(); 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()) { SecondaryAttackPerformanceLog.Start(); component.GetZDO().Set(ZDOVars.s_visual, context.VisualPrefabName); } SecondaryAttackPerformanceLog.Start(); projectile.UpdateVisual(); SecondaryAttackPerformanceLog.Start(); ApplyCopiedThrowVisualSpin(projectile, context); } else { SecondaryAttackPerformanceLog.Start(); ApplyLocalFallbackVisual(projectile, context); } } finally { } } 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown SecondaryAttackPerformanceLog.Start(); 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; } SecondaryAttackPerformanceLog.Start(); HideSourcePresentation(projectile); SecondaryAttackPerformanceLog.Start(); 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(); ((Component)projectile).GetComponentsInChildren(true, RendererBuffer); foreach (Renderer item in RendererBuffer) { if (!(item is TrailRenderer) && !(item is ParticleSystemRenderer)) { item.enabled = false; } } RendererBuffer.Clear(); } private static void ApplyLocalFallbackVisual(Projectile projectile, ItemData weapon) { ApplyLocalFallbackVisual(projectile, CreateSpawnedProjectileVisualContext(weapon, includeHitEffects: false)); } private static void ApplyLocalFallbackVisual(Projectile projectile, SpawnedProjectileVisualContext context) { ApplyLocalFallbackVisual(projectile, context, "copiedThrow.visual.fallback"); } private static void ApplyLocalFallbackVisual(Projectile projectile, SpawnedProjectileVisualContext context, string perfScopePrefix) { //IL_005c: 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 (context.Active && !((Object)(object)context.AttachPrefab == (Object)null)) { GameObject visual = projectile.m_visual; SecondaryAttackPerformanceLog.Start(); 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); } SecondaryAttackPerformanceLog.Start(); IEquipmentVisual componentInChildren = val.GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.Setup(context.Weapon.m_variant); } projectile.m_visual = val; SecondaryAttackPerformanceLog.Start(); 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(); } private static string FormatVector(Vector3 value) { return ((Vector3)(ref value)).ToString("F2"); } } 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_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_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) 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, origin); SecondaryAttackManager.PlayTriggeredAttackEffects(attack, definition.CleavingThrust?.DurabilityFactor ?? definition.DurabilityFactor); GatherTargets(attack, cleavingThrust, origin, forward); if (HitTargets.Count != 0) { for (int i = 0; i < HitTargets.Count; i++) { ApplyHit(attack, cleavingThrust, HitTargets[i], HitTargets.Count); } } } 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, Vector3 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_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, null)) { 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; internal static bool IsDirectWeaponHitActive => _directHitDepth > 0; internal static bool ShouldCountWeaponEffectHit { get { if (_directHitDepth > 0 && _characterDamageDepth == 1 && !WeaponEffectManager.IsApplyingGeneratedEffectDamage && !LaunchSlamSystem.IsApplyingLandingDamage && !KnockbackChainSystem.IsApplyingChainDamage) { return !MeleeProjectileHitCascadeSystem.IsApplyingImpactBurstDamage; } return false; } } 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; } } 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; } } [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(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__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(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__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(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__state); } } 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 _registered; 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; _registered = true; CreateVisual(); SecondaryAttackManager.RegisterAsyncSecondaryWork((Character?)(object)attack.m_character); ((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(); if (_registered) { SecondaryAttackManager.UnregisterAsyncSecondaryWork((Character?)(object)Attack?.m_character); _registered = false; } } } 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(); 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); } HitTargets.Clear(); } 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; } bool flag = IsDebugLoggingEnabled(); 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); return true; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)val, attack.m_weapon, "harvestSweep", harvestSweep.PresetCooldown, out var _)) { return false; } if ((Object)(object)harvestSweepController == (Object)null) { harvestSweepController = ((Component)val).gameObject.AddComponent(); } harvestSweepController.Begin(attack, definition, harvestSweep); 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) { bool result = ((Character)humanoid).StartAttack((Character)null, true); IsDebugLoggingEnabled(); return result; } internal static float GetRepeatDelay() { return 0f; } internal static float GetRotationSpeedFactor() { return 1f; } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogDebug(string message) { } internal static bool IsDebugLoggingEnabled() { return false; } 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 static readonly int AttackTagHash = ZSyncAnimation.GetHash("attack"); private static readonly Collider[] Hits = (Collider[])(object)new Collider[200]; private static int _harvestMask; 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() { bool flag = HarvestSweepSystem.IsDebugLoggingEnabled(); 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); } } else if (flag) { _ = Time.frameCount % 10; } return; } if (_currentAttack != null && !_harvestedCurrentSweep) { HarvestCurrentSweep(_currentAttack); } string reason; if (_stopRequested || !MatchesWeapon(_humanoid.GetCurrentWeapon())) { Object.Destroy((Object)(object)this); } else if (Time.time < _nextRepeatTime || ((Character)_humanoid).IsStaggering() || ((Character)_humanoid).InAttack()) { if (flag) { _ = Time.frameCount % 20; } } else if (!CanPayNextAttackCost(_weapon, out reason)) { 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_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_005b: 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_00c3: 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) if ((Object)(object)_humanoid == (Object)null || _weapon == null || _harvestSweep == null) { return false; } bool flag = HarvestSweepSystem.IsDebugLoggingEnabled(); if (_animator == null) { _animator = ((Component)_humanoid).GetComponentInChildren(); } if ((Object)(object)_animator == (Object)null) { return false; } AnimatorStateInfo attackAnimatorState = GetAttackAnimatorState(_animator); if (!IsAttackState(attackAnimatorState)) { if (flag) { _ = Time.frameCount % 10; } 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) { if (flag) { _ = Time.frameCount % 10; } return false; } HarvestCurrentSweep(activeAttack); if (!CanPayNextAttackCost(_weapon, out string _)) { _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) { bool flag = HarvestSweepSystem.IsDebugLoggingEnabled(); if (_loopRearmed) { return true; } if (((AnimatorStateInfo)(ref state)).normalizedTime < loopEnd) { _loopRearmed = true; return true; } if (flag) { _ = Time.frameCount % 10; } return false; } private void SeekToLoopStart(AnimatorStateInfo state) { if (!((Object)(object)_animator == (Object)null) && _harvestSweep != null) { int num = ((_loopStateHash != 0) ? _loopStateHash : ((AnimatorStateInfo)(ref state)).fullPathHash); if (num != 0) { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); _animator.Play(num, 0, _harvestSweep.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 static string DescribeState(AnimatorStateInfo state) { return $"stateHash={((AnimatorStateInfo)(ref state)).fullPathHash} tagHash={((AnimatorStateInfo)(ref state)).tagHash} normalized={((AnimatorStateInfo)(ref state)).normalizedTime:0.###} length={((AnimatorStateInfo)(ref state)).length:0.###} speed={((AnimatorStateInfo)(ref state)).speed:0.###}"; } private bool CanPayNextAttackCost(ItemData weapon, out string reason) { reason = ""; SharedData shared = weapon.m_shared; Attack val = shared?.m_secondaryAttack; if (val == null) { reason = "secondary attack is null"; return false; } float num = Mathf.Max(0f, shared.m_useDurabilityDrain * (_definition?.DurabilityFactor ?? 1f)); if (num > 0f && weapon.m_durability + 0.001f < num) { reason = $"durability {weapon.m_durability:0.###} < {num:0.###}"; return false; } float num2 = Mathf.Max(0f, val.m_attackStamina); if (num2 > 0f && !((Character)_humanoid).HaveStamina(num2)) { reason = $"stamina cost={num2:0.###}"; return false; } float num3 = Mathf.Max(0f, val.m_attackEitr); if (num3 > 0f && !((Character)_humanoid).HaveEitr(num3)) { reason = $"eitr cost={num3:0.###}"; return false; } float num4 = Mathf.Max(0f, val.m_attackHealth); if (num4 > 0f && !((Character)_humanoid).HaveHealth(num4) && val.m_attackHealthLowBlockUse) { reason = $"health cost={num4:0.###}"; return false; } reason = "ok"; 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) { HarvestSweepSystem.IsDebugLoggingEnabled(); return; } _harvestedCurrentSweep = true; HarvestSweepSystem.IsDebugLoggingEnabled(); HarvestOnce(activeAttack); } private void HarvestOnce(Attack activeAttack) { //IL_0062: 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 num = HarvestSweepSystem.ResolveHarvestRadius(_weapon, val); int num2 = Physics.OverlapSphereNonAlloc(ResolveHarvestCenter(activeAttack, harvestAttack), num, Hits, GetHarvestMask(), (QueryTriggerInteraction)0); int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; _seenThisTick.Clear(); for (int i = 0; i < num2; i++) { Collider val2 = Hits[i]; if ((Object)(object)val2 == (Object)null) { continue; } Pickable componentInParent = ((Component)val2).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { num3++; if (!_seenThisTick.Add(componentInParent) || !CanHarvest(componentInParent)) { num5++; continue; } using (GroundworkCompat.SuppressForagingRangePickup()) { using (HarvestSweepSystem.BeginSkillRaiseFactor(_harvestSweep.SkillRaiseFactor)) { componentInParent.Interact((Humanoid)(object)val, false, false); } } num4++; } else if (TryDestroyUnhealthyPlant(val2)) { num6++; } else { num7++; } } _seenThisTick.Clear(); HarvestSweepSystem.IsDebugLoggingEnabled(); } private bool TryDestroyUnhealthyPlant(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) { return false; } Destructible componentInParent2 = ((Component)hit).GetComponentInParent(); if ((Object)(object)componentInParent2 == (Object)null || !_destroyedPlants.Add(componentInParent2)) { return false; } componentInParent2.Destroy((HitData)null); return true; } private static bool CanHarvest(Pickable pickable) { if (pickable.m_harvestable || GroundworkCompat.IsForagingTarget(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 string FormatVector(Vector3 value) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return $"{value.x:0.##},{value.y:0.##},{value.z:0.##}"; } 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() { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); RestoreAnimationSpeed(); } private static int GetHarvestMask() { if (_harvestMask == 0) { _harvestMask = LayerMask.GetMask(new string[3] { "piece", "piece_nonsolid", "item" }); } return _harvestMask; } } internal static class GreatSwordSkillScalingSystem { internal readonly struct AttackRangeScope { public static readonly AttackRangeScope Inactive = new AttackRangeScope(active: false, 0f, 0f, 0f); public bool Active { get; } public float OriginalRange { get; } public float OriginalRayWidth { get; } public float OriginalRayWidthCharExtra { get; } public AttackRangeScope(bool active, float originalRange, float originalRayWidth, float originalRayWidthCharExtra) { Active = active; OriginalRange = originalRange; OriginalRayWidth = originalRayWidth; OriginalRayWidthCharExtra = originalRayWidthCharExtra; } } 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 Dictionary OriginalTrailTipLocalPositions = new Dictionary(); private static readonly Dictionary> ScaledTrailTipsByAttack = new Dictionary>(); internal static AttackRangeScope BeginAttackRangeScope(Attack attack) { return AttackRangeScope.Inactive; } internal static void EndAttackRangeScope(Attack attack, AttackRangeScope scope) { if (scope.Active) { attack.m_attackRange = scope.OriginalRange; attack.m_attackRayWidth = scope.OriginalRayWidth; attack.m_attackRayWidthCharExtra = scope.OriginalRayWidthCharExtra; } } internal static void ApplyTrailScaleForAttack(Attack attack) { if (TryGetConfiguredTrailScale(attack, out var rangeScale)) { ApplyTrailScale(attack, rangeScale); } } internal static void ApplyTrailScaleForActiveDefinition(Attack attack, SecondaryAttackDefinition definition) { if (TryGetConfiguredTrailScale(attack, definition, requireSecondaryAttack: false, out var rangeScale)) { ApplyTrailScale(attack, rangeScale); } } internal static void ApplyCleavingThrustTrailScaleForTriggeredAttack(Attack attack, SecondaryAttackDefinition definition) { if (TryGetCleavingThrustTrailScale(attack, definition, out var rangeScale)) { ApplyTrailScale(attack, rangeScale); } } private static void ApplyTrailScale(Attack attack, float rangeScale) { //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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_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_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) GameObject rightItemInstance = GetRightItemInstance(attack); if ((Object)(object)rightItemInstance == (Object)null) { return; } List list = new List(); MeleeWeaponTrail[] componentsInChildren = rightItemInstance.GetComponentsInChildren(true); foreach (MeleeWeaponTrail obj in componentsInChildren) { object? value = TrailBaseField.GetValue(obj); Transform val = (Transform)((value is Transform) ? value : null); object? value2 = TrailTipField.GetValue(obj); Transform val2 = (Transform)((value2 is Transform) ? value2 : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { if (!OriginalTrailTipLocalPositions.TryGetValue(val2, out var value3)) { value3 = val2.localPosition; OriginalTrailTipLocalPositions[val2] = value3; } Vector3 val3 = (((Object)(object)val2.parent != (Object)null) ? val2.parent.InverseTransformPoint(val.position) : val.position); Vector3 val4 = value3 - val3; val2.localPosition = val3 + val4 * rangeScale; list.Add(val2); } } if (list.Count > 0) { ScaledTrailTipsByAttack[attack] = list; } } internal static void RestoreTrailScaleForAttack(Attack attack) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!ScaledTrailTipsByAttack.TryGetValue(attack, out List value)) { return; } ScaledTrailTipsByAttack.Remove(attack); foreach (Transform item in value) { if ((Object)(object)item != (Object)null && OriginalTrailTipLocalPositions.TryGetValue(item, out var value2)) { item.localPosition = value2; } } } private static bool TryGetConfiguredTrailScale(Attack attack, out float rangeScale) { rangeScale = 1f; if ((Object)(object)attack?.m_character == (Object)null || attack.m_weapon?.m_shared == null || !IsSecondaryAttack(attack) || !SecondaryAttackRuntimeFacade.TryGetDefinition(attack.m_weapon, out SecondaryAttackDefinition definition)) { return false; } return TryGetConfiguredTrailScale(attack, definition, requireSecondaryAttack: false, out rangeScale); } private static bool TryGetConfiguredTrailScale(Attack attack, SecondaryAttackDefinition definition, bool requireSecondaryAttack, out float rangeScale) { rangeScale = 1f; if ((Object)(object)attack?.m_character == (Object)null || attack.m_weapon?.m_shared == null || (requireSecondaryAttack && !IsSecondaryAttack(attack))) { return false; } if (TryGetReadyCleavingThrustTrailScale(attack, definition, out var rangeScale2)) { rangeScale = Mathf.Max(rangeScale, rangeScale2); } return rangeScale > 1.0001f; } private static bool TryGetCleavingThrustTrailScale(Attack attack, SecondaryAttackDefinition definition, out float rangeScale) { rangeScale = 1f; if (definition.CleavingThrust == null) { return false; } rangeScale = CleavingThrustSystem.ResolveVisualRangeScale(attack, definition); return rangeScale > 1.0001f; } 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, attack.m_weapon, "cleavingThrust", cleavingThrust.PresetCooldown)) { return false; } rangeScale = CleavingThrustSystem.ResolveVisualRangeScale(attack, definition); return rangeScale > 1.0001f; } private static bool IsSecondaryAttack(Attack attack) { Humanoid character = attack.m_character; if (character != null && character.m_currentAttack == attack) { return character.m_currentAttackIsSecondary; } return attack.m_weapon?.m_shared?.m_secondaryAttack == attack; } private static GameObject? GetRightItemInstance(Attack attack) { object? value = AttackVisEquipmentField.GetValue(attack); VisEquipment val = (VisEquipment)((value is VisEquipment) ? value : null); GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null) { return val2; } val = (((Object)(object)attack.m_character != (Object)null) ? ((Component)attack.m_character).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return null; } object? value2 = VisRightItemInstanceField.GetValue(val); return (GameObject?)((value2 is GameObject) ? value2 : null); } } [HarmonyPatch(typeof(Attack), "Start")] internal static class AttackStartGreatSwordSkillScalingPatch { private static void Postfix(Attack __instance, bool __result) { if (__result) { GreatSwordSkillScalingSystem.ApplyTrailScaleForAttack(__instance); } } } [HarmonyPatch(typeof(Attack), "Stop")] internal static class AttackStopGreatSwordSkillScalingPatch { private static void Postfix(Attack __instance) { GreatSwordSkillScalingSystem.RestoreTrailScaleForAttack(__instance); } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] internal static class AttackDoMeleeAttackGreatSwordSkillScalingPatch { private static void Prefix(Attack __instance, out GreatSwordSkillScalingSystem.AttackRangeScope __state) { __state = GreatSwordSkillScalingSystem.BeginAttackRangeScope(__instance); } private static void Postfix(Attack __instance, GreatSwordSkillScalingSystem.AttackRangeScope __state) { GreatSwordSkillScalingSystem.EndAttackRangeScope(__instance, __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 _originalKeyParentStates = new List(); private readonly List _originalExtraTextStates = new List(); private readonly HashSet _generatedKeyParents = new HashSet(); private readonly List _generatedSeparatorTexts = new List(); private TMP_Text? _label; private bool _capturedOriginals; 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? Resolve(Transform owner, string transformPath) { Transform val = owner.Find(transformPath); if (!((Object)(object)val != (Object)null) || !IsUsableTemplate(((Component)val).gameObject)) { return null; } return new KeyHintCell(((Component)val).gameObject, hideOnRestore: false); } internal static KeyHintCell? CloneFrom(KeyHintCell? template, string name, bool hideOnRestore) { return CloneFrom(template?.Root, name, hideOnRestore); } 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 static Transform? FindParentWithTemplates(GameObject root, string name) { Transform val = root.transform.Find(name); if ((Object)(object)val == (Object)null) { return null; } if (!((IEnumerable)val).Cast().Any((Transform child) => IsUsableTemplate(((Component)child).gameObject))) { return null; } return val; } internal void Set(string label, IReadOnlyList keys, float preferredTextWidth = 0f, bool hideExtraTexts = false) { EnsureKeyCount(keys.Count); CaptureOriginals(); Root.SetActive(true); if ((Object)(object)_label != (Object)null) { SetText(_label, label); LayoutElement val = default(LayoutElement); if (preferredTextWidth > 0f && ((Component)_label).TryGetComponent(ref val)) { val.preferredWidth = preferredTextWidth; } } for (int i = 0; i < _keys.Count; i++) { bool flag = i < keys.Count; if (i < _keyParents.Count && (Object)(object)_keyParents[i] != (Object)null) { _keyParents[i].SetActive(flag); } if (flag) { SetText(_keys[i], keys[i]); } } if (hideExtraTexts) { foreach (TMP_Text extraText in _extraTexts) { if ((Object)(object)extraText != (Object)null) { ((Component)extraText).gameObject.SetActive(false); } } return; } EnsureSeparatorCount(Mathf.Max(0, keys.Count - 1)); for (int j = 0; j < _generatedSeparatorTexts.Count; j++) { TMP_Text val2 = _generatedSeparatorTexts[j]; if (!((Object)(object)val2 == (Object)null)) { bool flag2 = j < keys.Count - 1; ((Component)val2).gameObject.SetActive(flag2); if (flag2) { SetText(val2, "+"); } } } foreach (TMP_Text extraText2 in _extraTexts) { if ((Object)(object)extraText2 != (Object)null) { ((Component)extraText2).gameObject.SetActive(keys.Count > 1); } } } internal void SetKeys(IReadOnlyList keys, bool hideExtraTexts = false) { EnsureKeyCount(keys.Count); CaptureOriginals(); Root.SetActive(true); for (int i = 0; i < _keys.Count; i++) { bool flag = i < keys.Count; if (i < _keyParents.Count && (Object)(object)_keyParents[i] != (Object)null) { _keyParents[i].SetActive(flag); } if (flag) { SetText(_keys[i], keys[i]); } } if (hideExtraTexts) { foreach (TMP_Text extraText in _extraTexts) { if ((Object)(object)extraText != (Object)null) { ((Component)extraText).gameObject.SetActive(false); } } return; } EnsureSeparatorCount(Mathf.Max(0, keys.Count - 1)); for (int j = 0; j < _generatedSeparatorTexts.Count; j++) { TMP_Text val = _generatedSeparatorTexts[j]; if (!((Object)(object)val == (Object)null)) { bool flag2 = j < keys.Count - 1; ((Component)val).gameObject.SetActive(flag2); if (flag2) { SetText(val, "+"); } } } foreach (TMP_Text extraText2 in _extraTexts) { if ((Object)(object)extraText2 != (Object)null) { ((Component)extraText2).gameObject.SetActive(keys.Count > 1); } } } internal void SetText(string value) { CaptureOriginals(); Root.SetActive(true); TMP_Text val = _label ?? _keys.FirstOrDefault() ?? _extraTexts.FirstOrDefault(); SetText(val, value); foreach (GameObject keyParent in _keyParents) { if ((Object)(object)keyParent != (Object)null) { keyParent.SetActive(false); } } 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 (!_capturedOriginals) { if (_hideOnRestore) { Root.SetActive(false); } return; } Root.SetActive(!_hideOnRestore && _originalRootActive); if ((Object)(object)_label != (Object)null) { SetText(_label, _originalLabel); 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 < _keyParents.Count && j < _originalKeyParentStates.Count; j++) { if ((Object)(object)_keyParents[j] != (Object)null) { bool active = !_generatedKeyParents.Contains(_keyParents[j]) && _originalKeyParentStates[j]; _keyParents[j].SetActive(active); } } for (int k = 0; k < _extraTexts.Count && k < _originalExtraTextStates.Count; k++) { if ((Object)(object)_extraTexts[k] != (Object)null) { ((Component)_extraTexts[k]).gameObject.SetActive(_originalExtraTextStates[k]); } } foreach (TMP_Text generatedSeparatorText in _generatedSeparatorTexts) { if ((Object)(object)generatedSeparatorText != (Object)null) { ((Component)generatedSeparatorText).gameObject.SetActive(false); } } } internal void SetActive(bool active) { if ((Object)(object)Root != (Object)null) { Root.SetActive(active); } } internal void MoveBefore(GameObject? template) { if (!((Object)(object)template == (Object)null) && !((Object)(object)Root == (Object)null) && !((Object)(object)Root == (Object)(object)template) && !((Object)(object)Root.transform.parent != (Object)(object)template.transform.parent)) { int siblingIndex = Root.transform.GetSiblingIndex(); int siblingIndex2 = template.transform.GetSiblingIndex(); int num = ((siblingIndex < siblingIndex2) ? (siblingIndex2 - 1) : siblingIndex2); if (siblingIndex != num) { Root.transform.SetSiblingIndex(Mathf.Max(0, num)); } } } internal void MoveToEnd() { if ((Object)(object)Root != (Object)null) { Root.transform.SetAsLastSibling(); } } internal void MoveToStart() { if ((Object)(object)Root != (Object)null) { Root.transform.SetAsFirstSibling(); } } internal bool Contains(TMP_Text? text) { if ((Object)(object)text != (Object)null && (Object)(object)Root != (Object)null) { return text.transform.IsChildOf(Root.transform); } return false; } 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) { return; } _capturedOriginals = true; _originalRootActive = Root.activeSelf; _originalLabel = (((Object)(object)_label != (Object)null) ? _label.text : string.Empty); 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); } _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 EnsureKeyCount(int count) { if (count <= _keys.Count && _keys.Count == _keyParents.Count && _keys.TrueForAll((TMP_Text key) => (Object)(object)key != (Object)null) && _keyParents.TrueForAll((GameObject val3) => (Object)(object)val3 != (Object)null)) { return; } RefreshChildren(); if (count <= _keys.Count || _keyParents.Count == 0) { return; } GameObject val = _keyParents[0]; Transform parent = val.transform.parent; while (_keys.Count < count) { GameObject val2 = Object.Instantiate(val, parent, false); ((Object)val2).name = ((_keys.Count == 1) ? "key_bkg (1)" : $"key_bkg ({_keys.Count})"); _generatedKeyParents.Add(val2); RefreshChildren(); if (_keys.Count == 0) { break; } } } private void EnsureSeparatorCount(int count) { //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_008f: 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_00a9: 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_00e2: Unknown result type (might be due to invalid IL or missing references) if (count <= 0 || _keyParents.Count == 0 || _extraTexts.Count > 0) { return; } Transform parent = _keyParents[0].transform.parent; TMP_Text val = _label ?? _keys.FirstOrDefault(); if ((Object)(object)parent == (Object)null || (Object)(object)val == (Object)null) { return; } while (_generatedSeparatorTexts.Count < count) { GameObject val2 = new GameObject($"SecondaryAttacks_KeyHintSeparator_{_generatedSeparatorTexts.Count}"); val2.transform.SetParent(parent, false); val2.AddComponent().sizeDelta = new Vector2(18f, 24f); TextMeshProUGUI val3 = val2.AddComponent(); ((TMP_Text)val3).font = val.font; ((TMP_Text)val3).fontSharedMaterial = val.fontSharedMaterial; ((TMP_Text)val3).fontSize = val.fontSize; ((TMP_Text)val3).fontStyle = val.fontStyle; ((Graphic)val3).color = ((Graphic)val).color; ((TMP_Text)val3).alignment = (TextAlignmentOptions)514; ((Graphic)val3).raycastTarget = false; ((TMP_Text)val3).text = "+"; LayoutElement obj = val2.AddComponent(); obj.preferredWidth = 18f; obj.minWidth = 12f; _generatedSeparatorTexts.Add((TMP_Text)(object)val3); } for (int i = 0; i < _generatedSeparatorTexts.Count; i++) { TMP_Text val4 = _generatedSeparatorTexts[i]; if (!((Object)(object)val4 == (Object)null)) { int num = Mathf.Min(i + 1, _keyParents.Count - 1); if (num >= 0 && num < _keyParents.Count && (Object)(object)_keyParents[num] != (Object)null) { val4.transform.SetSiblingIndex(_keyParents[num].transform.GetSiblingIndex()); } } } } 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 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 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_00e0: 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_00fd: 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) //IL_011f: 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_00c5: 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, out var _)) { return false; } hit.m_pushForce = num; if (knockbackChainDefinition.MaxChainTargets <= 0) { SpawnVfx(knockbackChainDefinition.InitialHitVfx, ResolveVfxPoint(hit, target), ((Component)target).transform.rotation, "knockback_chain_initial_hit_vfx_missing"); return true; } SpawnVfx(knockbackChainDefinition.InitialHitVfx, 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_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_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_00ac: 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) KnockbackChainDefinition config = state.Config; if (state.CollateralHits >= config.MaxChainTargets) { return; } Vector3 centerPoint = source.GetCenterPoint(); int num = Physics.OverlapSphereNonAlloc(centerPoint, config.CollisionRadius, OverlapHits, GetCharacterMask(), (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val = OverlapHits[i]; OverlapHits[i] = null; if ((Object)(object)val == (Object)null) { continue; } Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(val); if (!IsValidCollisionTarget(state.Attacker, source, hitCharacter) || !state.CanHit(hitCharacter, config.HitCooldown)) { 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.CollateralHits < config.MaxChainTargets && num2 >= 0.05f) { AttachTracker(hitCharacter, state, pushForce, num2, val2); } if (state.CollateralHits >= config.MaxChainTargets) { break; } } } } private static bool ApplyCollisionHit(Character source, Character target, Collider collider, KnockbackChainState state, float pushForce, float chainPower, Vector3 direction) { //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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00f7: 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_0101: 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) //IL_0123: 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) KnockbackChainDefinition config = state.Config; 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); int collateralHits = state.CollateralHits; state.MarkHit(target); bool flag = ShouldSpawnCollisionDistanceEffects(config, state.Attacker, val2.m_point); IsApplyingChainDamage = true; try { target.Damage(val2); } finally { IsApplyingChainDamage = false; } if (flag) { Quaternion rotation = Quaternion.LookRotation(direction, Vector3.up); SpawnVfx(ResolveCollisionVfx(config, collateralHits), val2.m_point, rotation, "knockback_chain_collision_vfx_missing"); SpawnVfx(config.CollisionSfx, val2.m_point, rotation, "knockback_chain_collision_sfx_missing"); } return true; } private static bool ShouldSpawnCollisionDistanceEffects(KnockbackChainDefinition config, Character attacker, Vector3 point) { //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_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_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) float num = Mathf.Max(0f, config.CollisionVfxMinDistanceFromPlayer); if (num <= 0f) { return true; } if ((Object)(object)attacker == (Object)null) { return false; } Vector3 centerPoint = attacker.GetCenterPoint(); Vector3 val = point - centerPoint; return ((Vector3)(ref val)).sqrMagnitude >= num * num; } private static string ResolveCollisionVfx(KnockbackChainDefinition config, int collisionIndex) { if (collisionIndex <= 0) { return config.FirstCollisionVfx; } if (collisionIndex != 1) { return config.LaterCollisionVfx; } return config.SecondCollisionVfx; } 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 (SecondaryAttackManager.TryMarkCompatibilityWarningReported(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 readonly Dictionary _lastHitTimes = new Dictionary(); public Character Attacker { get; } public HitData SourceHit { get; } public KnockbackChainDefinition Config { get; } public int CollateralHits { get; private set; } public KnockbackChainState(Character attacker, HitData sourceHit, KnockbackChainDefinition config) { Attacker = attacker; SourceHit = sourceHit.Clone(); Config = config; } public bool CanHit(Character target, float hitCooldown) { if ((Object)(object)target == (Object)null || CollateralHits >= Config.MaxChainTargets) { return false; } float num = Mathf.Max(0.01f, hitCooldown); if (_lastHitTimes.TryGetValue(target, out var value)) { return Time.time - value >= num; } return true; } public void MarkHit(Character target) { _lastHitTimes[target] = Time.time; CollateralHits++; } } internal sealed class KnockbackChainTracker : MonoBehaviour { 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_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_0082: 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_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_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_009b: 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_00c5: 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_00ca: 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_00f9: 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_0148: 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; } KnockbackChainDefinition config = _state.Config; if (Time.time - _startTime > config.Duration || _state.CollateralHits >= config.MaxChainTargets) { 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)) < config.MinSpeed)) { 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 MinAirTime = 0.2f; private const float LandingTimeout = 3f; private const float DefaultLandingAreaRadius = 0.75f; 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) 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, out var _)) { 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, launchSlamDefinition.LandingAreaRadiusFactor, launchSlamDefinition.LandingAreaRadiusMax, launchSlamDefinition.Vfx, launchSlamDefinition.VfxRotationOffset, launchSlamDefinition.Sfx, 0.2f, 3f); } } 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, float landingAreaRadiusFactor, float landingAreaRadiusMax, string landingVfx, Vector3 landingVfxRotationOffset, string landingSfx) { //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_0042: 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) //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_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_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_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_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) 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, landingAreaRadiusFactor, landingAreaRadiusMax); 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(landingVfx, position, rotation, "launch_slam_landing_vfx_missing"); SecondaryAttackNamedEffectSystem.Create(landingSfx, position, val, "launch_slam_landing_sfx_missing"); IsApplyingLandingDamage = true; try { foreach (Character item in list) { ApplyLandingHit(item, attacker, sourceHit, landingDamage, position); } } finally { IsApplyingLandingDamage = false; } } 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 radiusFactor, float radiusMax) { float num = Mathf.Max(0f, radiusFactor); float num2 = Mathf.Max(0f, radiusMax); if (num <= 0f || num2 <= 0f) { return 0f; } float num3 = ResolveCharacterFootprintRadius(target); return Mathf.Min(num2, num3 * num); } 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 Character? _target; private Character? _attacker; private Rigidbody? _body; private HitData? _sourceHit; private DamageTypes _landingDamage; private float _targetApexHeight; private float _landingAreaRadiusFactor; private float _landingAreaRadiusMax; private string _landingVfx = ""; private Vector3 _landingVfxRotationOffset = Vector3.zero; private string _landingSfx = ""; private float _minAirTime; private float _landingTimeout; 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, float landingAreaRadiusFactor, float landingAreaRadiusMax, string landingVfx, Vector3 landingVfxRotationOffset, string landingSfx, float minAirTime, float landingTimeout) { //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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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); _landingAreaRadiusFactor = landingAreaRadiusFactor; _landingAreaRadiusMax = landingAreaRadiusMax; _landingVfx = landingVfx.Trim(); _landingVfxRotationOffset = landingVfxRotationOffset; _landingSfx = landingSfx.Trim(); _minAirTime = minAirTime; _landingTimeout = landingTimeout; _startTime = Time.time; _startHeight = ((Component)target).transform.position.y; _hasBeenAirborne = false; IgnoreOtherCharacterCollisions(target); ((Behaviour)this).enabled = true; } } private void Update() { //IL_006c: 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_00e1: 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 > _landingTimeout) { 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 < _minAirTime) && flag) { LaunchSlamSystem.ApplyLandingDamage(_target, _attacker, _sourceHit, _landingDamage, _landingAreaRadiusFactor, _landingAreaRadiusMax, _landingVfx, _landingVfxRotationOffset, _landingSfx); 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 FarmingSkillOverrideTypeName = "SecondaryAttacks.FarmingSkillOverrideSystem"; private static readonly IDisposable NoopScope = new NoopDisposable(); private static bool _resolved; private static MethodInfo? _suppressRangePickupMethod; private static MethodInfo? _isForagingTargetMethod; 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); } private static void Resolve() { if (!_resolved) { _resolved = true; Type? obj = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly assembly) => string.Equals(assembly.GetName().Name, "Groundwork", StringComparison.OrdinalIgnoreCase))?.GetType("SecondaryAttacks.FarmingSkillOverrideSystem"); _suppressRangePickupMethod = obj?.GetMethod("SuppressRangePickup", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _isForagingTargetMethod = obj?.GetMethod("IsForagingTarget", 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 MagicPluginCompat { private sealed class FloatConfigAccessor { private readonly float _fallback; internal ConfigEntry? Entry { get; set; } internal float Value => Entry?.Value ?? _fallback; internal FloatConfigAccessor(ConfigEntry? entry, float fallback) { Entry = entry; _fallback = fallback; } } internal const string MagicPluginGuid = "blacks7ar.MagicPlugin"; private const string MagicPluginTypeName = "MagicPlugin.Plugin"; private const string ConfigSetupTypeName = "MagicPlugin.Functions.ConfigSetup"; private static readonly FloatConfigAccessor MagicVelocity = new FloatConfigAccessor(null, 2f); private static readonly FloatConfigAccessor MagicAccuracy = new FloatConfigAccessor(null, 0f); private static readonly FloatConfigAccessor SurtlingStaffAoe = new FloatConfigAccessor(null, 3f); private static readonly FloatConfigAccessor EikthyrStaffAoe = new FloatConfigAccessor(null, 3f); private static readonly FloatConfigAccessor LightningStaffAoe = new FloatConfigAccessor(null, 3f); private static readonly FloatConfigAccessor PoisonStaffAoe = new FloatConfigAccessor(null, 3f); private static readonly FloatConfigAccessor ArcticStaffAoe = new FloatConfigAccessor(null, 3f); private static readonly FloatConfigAccessor ModersAoeRange = new FloatConfigAccessor(null, 5f); private static readonly FloatConfigAccessor ModersChopDamage = new FloatConfigAccessor(null, 50f); private static readonly FloatConfigAccessor ModersFrostDamage = new FloatConfigAccessor(null, 120f); private static readonly FloatConfigAccessor ModersPickaxeDamage = new FloatConfigAccessor(null, 50f); private static readonly FloatConfigAccessor ModersPierceDamage = new FloatConfigAccessor(null, 40f); private static readonly FloatConfigAccessor YagluthAoeRange = new FloatConfigAccessor(null, 5f); private static readonly FloatConfigAccessor YagluthBluntDamage = new FloatConfigAccessor(null, 40f); private static readonly FloatConfigAccessor YagluthChopDamage = new FloatConfigAccessor(null, 50f); private static readonly FloatConfigAccessor YagluthFireDamage = new FloatConfigAccessor(null, 120f); private static readonly FloatConfigAccessor YagluthPickaxeDamage = new FloatConfigAccessor(null, 50f); private const string PlayerPatchTypeName = "MagicPlugin.Patches.PlayerPatch"; private static bool _projectilePatchInstalled; private static bool _teleportPatchInstalled; private static bool _reportedRuntimeError; private static bool _reportedTeleportRuntimeError; internal static void TryInstall(Harmony harmony) { if (Chainloader.PluginInfos.TryGetValue("blacks7ar.MagicPlugin", out var value)) { TryInstallProjectilePatch(harmony, value); TryInstallTeleportPatch(harmony, value); } } private static void TryInstallProjectilePatch(Harmony harmony, PluginInfo pluginInfo) { //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_01ff: Expected O, but got Unknown if (_projectilePatchInstalled) { return; } MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Attack), "FireProjectileBurst", (Type[])null, (Type[])null); if (methodInfo == null) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)"MagicPlugin compatibility skipped: Attack.FireProjectileBurst was not found."); return; } int num = CountMagicPluginPrefixes(methodInfo); if (num == 0) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)"MagicPlugin compatibility skipped: MagicPlugin FireProjectileBurst prefix was not found."); return; } Assembly obj = ((object)pluginInfo.Instance)?.GetType().Assembly ?? FindAssemblyWithType("MagicPlugin.Plugin"); Type type = obj?.GetType("MagicPlugin.Plugin", throwOnError: false); Type type2 = obj?.GetType("MagicPlugin.Functions.ConfigSetup", throwOnError: false); if (type == null || type2 == null) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)"MagicPlugin compatibility skipped: MagicPlugin config types were not found."); return; } UpdateAccessor(type, "_magicVelocity", MagicVelocity); UpdateAccessor(type, "_magciAccuracy", MagicAccuracy); UpdateAccessor(type2, "_surtlingStaffAOE", SurtlingStaffAoe); UpdateAccessor(type2, "_eikthyrStaffAOE", EikthyrStaffAoe); UpdateAccessor(type2, "_lightningStaffAOE", LightningStaffAoe); UpdateAccessor(type2, "_poisonStaffAOE", PoisonStaffAoe); UpdateAccessor(type2, "_arcticStaffAOE", ArcticStaffAoe); UpdateAccessor(type2, "_modersAoeRange", ModersAoeRange); UpdateAccessor(type2, "_modersChopDamage", ModersChopDamage); UpdateAccessor(type2, "_modersFrostDamage", ModersFrostDamage); UpdateAccessor(type2, "_modersPickaxeDamage", ModersPickaxeDamage); UpdateAccessor(type2, "_modersPierceDamage", ModersPierceDamage); UpdateAccessor(type2, "_yagluthAoeRange", YagluthAoeRange); UpdateAccessor(type2, "_yagluthBluntDamage", YagluthBluntDamage); UpdateAccessor(type2, "_yagluthChopDamage", YagluthChopDamage); UpdateAccessor(type2, "_yagluthFireDamage", YagluthFireDamage); UpdateAccessor(type2, "_yagluthPickaxeDamage", YagluthPickaxeDamage); harmony.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)1, "blacks7ar.MagicPlugin"); HarmonyMethod val = new HarmonyMethod(typeof(MagicPluginCompat), "FireProjectileBurstPrefix", (Type[])null) { priority = 400 }; harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _projectilePatchInstalled = true; SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"Installed MagicPlugin FireProjectileBurst compatibility patch for MagicPlugin {pluginInfo.Metadata.Version}; removed {num} event-registering prefix(es)."); } private static void TryInstallTeleportPatch(Harmony harmony, PluginInfo pluginInfo) { //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_00e3: Expected O, but got Unknown if (_teleportPatchInstalled) { return; } MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "TeleportTo", new Type[3] { typeof(Vector3), typeof(Quaternion), typeof(bool) }, (Type[])null) ?? AccessTools.DeclaredMethod(typeof(Player), "TeleportTo", (Type[])null, (Type[])null); if (methodInfo == null) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)"MagicPlugin teleport compatibility skipped: Player.TeleportTo was not found."); return; } List list = FindMagicPluginTeleportPostfixes(methodInfo); if (list.Count == 0) { return; } foreach (MethodInfo item in list) { harmony.Unpatch((MethodBase)methodInfo, item); } HarmonyMethod val = new HarmonyMethod(typeof(MagicPluginCompat), "TeleportToPostfix", (Type[])null) { priority = 400 }; harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _teleportPatchInstalled = true; SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"Installed MagicPlugin safe teleport compatibility patch for MagicPlugin {pluginInfo.Metadata.Version}; removed {list.Count} unsafe TeleportTo postfix(es)."); } private static bool FireProjectileBurstPrefix(Attack __instance) { try { ApplyFireProjectileBurstAdjustments(__instance); } catch (Exception ex) { if (!_reportedRuntimeError) { _reportedRuntimeError = true; SecondaryAttacksPlugin.ModLogger.LogWarning((object)("MagicPlugin compatibility failed while applying projectile adjustments: " + ex.Message)); } } return true; } private static void ApplyFireProjectileBurstAdjustments(Attack attack) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 if (attack == null) { return; } Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val == null) { return; } SharedData val2 = attack.m_weapon?.m_shared; if (val2 == null || (int)val2.m_skillType != 9) { return; } string text = val2.m_name ?? ""; if (text.EndsWith("scepter") || text.Contains("flamestaff") || text.Contains("thunderstaff")) { return; } string ammoType = val2.m_ammoType; if ((ammoType != null && ammoType.Contains("ammo_spells")) || ((Object)(object)val2.m_secondaryAttack?.m_attackProjectile != (Object)null && ((Object)val2.m_secondaryAttack.m_attackProjectile).name == "BDS_DvergerStaffFire_clusterbomb_projectile")) { return; } if (text == "$bmp_arcticstaff" || text == "$item_stafficeshards") { attack.m_projectileVel *= 1.2f; attack.m_projectileVelMin *= 1.2f; attack.m_projectileAccuracy *= 0f; attack.m_projectileAccuracyMin *= 0f; } else { attack.m_projectileVel *= MagicVelocity.Value; attack.m_projectileVelMin *= MagicVelocity.Value; attack.m_projectileAccuracy *= MagicAccuracy.Value; attack.m_projectileAccuracyMin *= MagicAccuracy.Value; } GameObject attackProjectile = attack.m_attackProjectile; if (!((Object)(object)attackProjectile == (Object)null)) { string obj = ((Object)attackProjectile).name ?? ""; string text2 = obj.ToLowerInvariant(); float skillFactor = ((Character)val).GetSkillFactor((SkillType)9); if (text2.Contains("surtlingstaff")) { ApplyProjectileAoe(attackProjectile, SurtlingStaffAoe, skillFactor); } if (text2.Contains("eikthyrsstaff")) { ApplyProjectileAoe(attackProjectile, EikthyrStaffAoe, skillFactor); } if (text2.Contains("lightningstaff")) { ApplyProjectileAoe(attackProjectile, LightningStaffAoe, skillFactor); } if (text2.Contains("poisonstaff")) { ApplyProjectileAoe(attackProjectile, PoisonStaffAoe, skillFactor); } if (text2.Contains("arcticstaff")) { ApplyProjectileAoe(attackProjectile, ArcticStaffAoe, skillFactor); } if (obj == "bmp_modersheritage_projectile") { ApplyModersHeritage(attackProjectile, skillFactor); } if (obj == "bmp_yagluthsheritage_projectile") { ApplyYagluthsHeritage(attackProjectile, skillFactor); } } } private static void ApplyProjectileAoe(GameObject projectilePrefab, FloatConfigAccessor aoeConfig, float elementalSkillFactor) { Projectile component = projectilePrefab.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_aoe = aoeConfig.Value + 2f * elementalSkillFactor; } } private static void ApplyModersHeritage(GameObject projectilePrefab, float elementalSkillFactor) { Projectile val = TryGetSpawnedProjectile(projectilePrefab); if (!((Object)(object)val == (Object)null)) { val.m_aoe = ModersAoeRange.Value + 2f * elementalSkillFactor; val.m_damage.m_chop = ModersChopDamage.Value; val.m_damage.m_frost = ModersFrostDamage.Value + ModersFrostDamage.Value * elementalSkillFactor; val.m_damage.m_pickaxe = ModersPickaxeDamage.Value; val.m_damage.m_pierce = ModersPierceDamage.Value; } } private static void ApplyYagluthsHeritage(GameObject projectilePrefab, float elementalSkillFactor) { Projectile val = TryGetSpawnedProjectile(projectilePrefab); if (!((Object)(object)val == (Object)null)) { val.m_aoe = YagluthAoeRange.Value + 2f * elementalSkillFactor; val.m_damage.m_blunt = YagluthBluntDamage.Value; val.m_damage.m_chop = YagluthChopDamage.Value; val.m_damage.m_fire = YagluthFireDamage.Value + YagluthFireDamage.Value * elementalSkillFactor; val.m_damage.m_pickaxe = YagluthPickaxeDamage.Value; } } private static Projectile? TryGetSpawnedProjectile(GameObject projectilePrefab) { GameObject obj = projectilePrefab.GetComponent()?.m_spawnOnHit; SpawnAbility val = ((obj != null) ? obj.GetComponent() : null); if (val?.m_spawnPrefab == null || val.m_spawnPrefab.Length == 0) { return null; } GameObject obj2 = val.m_spawnPrefab[0]; if (obj2 == null) { return null; } return obj2.GetComponent(); } private static void TeleportToPostfix(Player __instance, ref Vector3 pos, ref Quaternion rot) { //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) try { TeleportFollowingMagicSummons(__instance, pos, rot); } catch (Exception ex) { if (!_reportedTeleportRuntimeError) { _reportedTeleportRuntimeError = true; SecondaryAttacksPlugin.ModLogger.LogWarning((object)("MagicPlugin teleport compatibility failed while moving followed summons: " + ex.Message)); } } } private static void TeleportFollowingMagicSummons(Player player, Vector3 pos, Quaternion rot) { //IL_001b: 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_0052: 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 ((Object)(object)player == (Object)null) { return; } GameObject gameObject = ((Component)player).gameObject; if ((Object)(object)gameObject == (Object)null) { return; } Vector3 position = pos + ((Component)player).transform.forward; foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsFollowedMagicSummon(allCharacter, gameObject)) { Transform transform = ((Component)allCharacter).transform; transform.position = position; transform.rotation = rot; } } } private static bool IsFollowedMagicSummon(Character character, GameObject owner) { if ((Object)(object)character == (Object)null || (Object)(object)((Component)character).gameObject == (Object)null) { return false; } ZNetView nview = character.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } if (!(((Object)((Component)character).gameObject).name ?? "").StartsWith("BMP_", StringComparison.Ordinal) || !character.IsTamed()) { return false; } Tameable component = ((Component)character).GetComponent(); MonsterAI val = (((Object)(object)component != (Object)null) ? component.m_monsterAI : null); return (Object)(object)(((Object)(object)val != (Object)null) ? val.GetFollowTarget() : null) == (Object)(object)owner; } private static int CountMagicPluginPrefixes(MethodBase original) { Patches patchInfo = Harmony.GetPatchInfo(original); if (patchInfo == null) { return 0; } int num = 0; foreach (Patch prefix in patchInfo.Prefixes) { if (prefix.owner == "blacks7ar.MagicPlugin") { num++; } } return num; } private static List FindMagicPluginTeleportPostfixes(MethodBase original) { List list = new List(); Patches patchInfo = Harmony.GetPatchInfo(original); if (patchInfo == null) { return list; } foreach (Patch postfix in patchInfo.Postfixes) { if (postfix.owner == "blacks7ar.MagicPlugin" && postfix.PatchMethod.DeclaringType?.FullName == "MagicPlugin.Patches.PlayerPatch" && postfix.PatchMethod.Name.Contains("TeleportTo_Postfix")) { list.Add(postfix.PatchMethod); } } return list; } private static Assembly? FindAssemblyWithType(string fullTypeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetType(fullTypeName, throwOnError: false) != null) { return assembly; } } return null; } private static void UpdateAccessor(Type ownerType, string fieldName, FloatConfigAccessor accessor) { if (AccessTools.Field(ownerType, fieldName)?.GetValue(null) is ConfigEntry entry) { accessor.Entry = entry; } } } internal static class SecondaryAttackCompat { internal const string MagicPluginGuid = "blacks7ar.MagicPlugin"; internal static void TryInstallStartupHooks(Harmony harmony) { MagicPluginCompat.TryInstall(harmony); } internal static void TryInstallWorldApplyHooks() { } } 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 EntryId { get; } internal string ItemPrefabName { get; } internal MagicSummonQualityPreset Preset { get; } internal int MaxQuality { get; } internal string GroupId { get; } internal QualityRule(string entryId, string itemPrefabName, MagicSummonQualityPreset preset, int maxQuality) { EntryId = entryId; 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 bool Enabled => true; 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 (!Enabled || _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 void ApplyToZNetScene(ZNetScene scene, IReadOnlyDictionary summonConfigs) { if ((Object)(object)scene == (Object)null) { return; } RebuildCoreRules(summonConfigs, ObjectDB.instance); RulesBySpawnAbilityPrefab.Clear(); if (!Enabled || _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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if ((Object)(object)spawnAbility == (Object)null || item == null || !Enabled || !TryResolveRule(spawnAbility, item, out QualityRule rule)) { 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)summoned == (Object)null || !Enabled) { 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_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_00a6: 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 (!Enabled || 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); RulesByItemPrefab.Clear(); ActiveGroupIds.Clear(); if (!Enabled) { return; } 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(value.EntryId, itemPrefabName, value.QualityPreset, Mathf.Clamp(value.MaxQuality, 1, 10))); } } return list; } private static void AppendGlobalQualityRules(List rules, ObjectDB? objectDb) { MagicSummonQualityPreset configuredGlobalPreset = GetConfiguredGlobalPreset(); if (configuredGlobalPreset == MagicSummonQualityPreset.None || objectDb?.m_items == null) { return; } HashSet hashSet = new HashSet(rules.Select((QualityRule rule) => rule.ItemPrefabName), 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())) { rules.Add(new QualityRule(((Object)item).name, ((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) { //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 (!TryResolveSpawnAbilityFromAttack(val.m_attack, out SpawnAbility spawnAbility, out string targetName)) { return TryResolveSpawnAbilityFromAttack(val.m_secondaryAttack, out spawnAbility, out targetName); } return true; } 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; } } } private static bool TryResolveRule(SpawnAbility spawnAbility, 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; } string prefabName = Utils.GetPrefabName(((Component)spawnAbility).gameObject); if (!string.IsNullOrWhiteSpace(prefabName) && RulesBySpawnAbilityPrefab.TryGetValue(prefabName, out rule)) { return true; } string text3 = (((Object)(object)((Component)spawnAbility).gameObject != (Object)null) ? ((Object)((Component)spawnAbility).gameObject).name : ""); if (!string.IsNullOrWhiteSpace(text3) && RulesBySpawnAbilityPrefab.TryGetValue(text3, 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; } if (!TryResolveSpawnAbilityFromAttack(val2.m_itemData.m_shared.m_attack, out spawnAbility, out targetName)) { return TryResolveSpawnAbilityFromAttack(val2.m_itemData.m_shared.m_secondaryAttack, out spawnAbility, out targetName); } return true; } 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 bool TryResolveSpawnAbilityFromAttack(Attack? attack, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; if ((Object)(object)attack?.m_attackProjectile != (Object)null) { return TryResolveSpawnAbilityFromPrefab(attack.m_attackProjectile, out spawnAbility, out targetName); } return false; } private static bool TryResolveSpawnAbilityFromPrefab(GameObject prefab, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = prefab.GetComponent(); if ((Object)(object)spawnAbility != (Object)null) { targetName = ((Object)prefab).name; return true; } GameObject val = prefab.GetComponent()?.m_spawnOnHit; spawnAbility = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)spawnAbility != (Object)null) { targetName = ((Object)val).name; return true; } targetName = ((Object)prefab).name; return false; } 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 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 { private sealed class CharacterCooldownState { public Dictionary ReadyAtByPreset { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary DurationByPreset { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary IconByPreset { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public List UpdateKeys { get; } = new List(); public HashSet HudClearedStatusByPreset { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); } private sealed class CooldownStatusRegistration { public string PresetName { get; } public string StatusEffectName { get; } public string IconPrefabName { get; } public string DisplayNameToken { get; } public CooldownStatusRegistration(string presetName, string iconPrefabName, string displayNameToken) { PresetName = presetName; IconPrefabName = iconPrefabName; DisplayNameToken = displayNameToken; StatusEffectName = "SecondaryAttacks_Cooldown_" + presetName; } } private static readonly ConditionalWeakTable Cooldowns = new ConditionalWeakTable(); private static readonly Dictionary StatusesByPreset = BuildStatusMap(); internal static void RegisterStatusEffects(ObjectDB objectDb) { if ((Object)(object)objectDb == (Object)null) { return; } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (CooldownStatusRegistration registration in StatusesByPreset.Values) { if (hashSet.Add(registration.StatusEffectName) && !objectDb.m_StatusEffects.Exists((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == registration.StatusEffectName)) { MeleePresetCooldownStatusEffect meleePresetCooldownStatusEffect = ScriptableObject.CreateInstance(); meleePresetCooldownStatusEffect.Initialize(registration.StatusEffectName, registration.DisplayNameToken, "$sa_tooltip_secondary_recharging", ResolveIcon(objectDb, registration.IconPrefabName)); objectDb.m_StatusEffects.Add((StatusEffect)(object)meleePresetCooldownStatusEffect); } } } internal static bool TryConsume(Character attacker, ItemData? weapon, string presetName, MeleePresetCooldownDefinition cooldown, out float finalCooldown) { finalCooldown = 0f; if ((Object)(object)attacker == (Object)null) { return true; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { ClearCooldown(attacker, presetName); return true; } if (cooldown == null) { return true; } float num = Mathf.Max(0f, cooldown.Cooldown); if (num <= 0f) { return true; } if (weapon == null) { weapon = ResolveCurrentWeapon(attacker); } float num2 = Mathf.Clamp01(ResolveCooldownSkillLevel(attacker, weapon, cooldown.CooldownSkill) / 100f) * Mathf.Clamp01(cooldown.CooldownReductionFactor); finalCooldown = Mathf.Max(0f, num * (1f - num2)); if (finalCooldown <= 0f) { return true; } string text = (string.IsNullOrWhiteSpace(presetName) ? "unknown" : presetName.Trim()); CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); double num3 = SecondaryAttackManager.GetNetworkTimeSeconds(); if (value.ReadyAtByPreset.TryGetValue(text, out var value2) && num3 < value2) { SyncCooldownStatusTtl(attacker, weapon, text, (float)Math.Max(0.0, value2 - num3)); return false; } SyncCooldownStatusTtl(attacker, weapon, text, 0f); ClearCooldownState(value, text); value.ReadyAtByPreset[text] = num3 + (double)finalCooldown; value.DurationByPreset[text] = finalCooldown; value.IconByPreset[text] = ResolveWeaponIcon(weapon) ?? ResolveRegisteredIcon(text); value.HudClearedStatusByPreset.Remove(text); ApplyCooldownStatus(attacker, weapon, text, finalCooldown); return true; } internal static void UpdateActiveCooldowns(Player player) { if ((Object)(object)player == (Object)null) { return; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns((Character?)(object)player)) { ClearAllCooldowns((Character)(object)player); } else { if (!Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { return; } double num = SecondaryAttackManager.GetNetworkTimeSeconds(); ItemData weapon = ResolveCurrentWeapon((Character)(object)player); bool flag = SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects((Character?)(object)player); if (!flag) { value.HudClearedStatusByPreset.Clear(); } List updateKeys = value.UpdateKeys; updateKeys.Clear(); foreach (string key in value.ReadyAtByPreset.Keys) { updateKeys.Add(key); } foreach (string item in updateKeys) { if (!value.ReadyAtByPreset.TryGetValue(item, out var value2) || value2 <= num) { if (!flag) { SyncCooldownStatusTtl((Character)(object)player, weapon, item, 0f); } ClearCooldownState(value, item); continue; } float num2 = (float)Math.Max(0.0, value2 - num); if (flag) { if (value.HudClearedStatusByPreset.Add(item)) { ClearCooldownStatus((Character)(object)player, item); } continue; } SyncCooldownStatusTtl((Character)(object)player, weapon, item, num2); if (num2 <= 0f) { ClearCooldownState(value, item); } } updateKeys.Clear(); } } internal static bool IsReady(Character attacker, ItemData? weapon, string presetName, MeleePresetCooldownDefinition cooldown) { if ((Object)(object)attacker == (Object)null) { return true; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { ClearCooldown(attacker, presetName); return true; } if (cooldown == null || Mathf.Max(0f, cooldown.Cooldown) <= 0f) { return true; } string text = (string.IsNullOrWhiteSpace(presetName) ? "unknown" : presetName.Trim()); CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); double num = SecondaryAttackManager.GetNetworkTimeSeconds(); if (value.ReadyAtByPreset.TryGetValue(text, out var value2) && num < value2) { SyncCooldownStatusTtl(attacker, weapon, text, (float)Math.Max(0.0, value2 - num)); return false; } SyncCooldownStatusTtl(attacker, weapon, text, 0f); ClearCooldownState(value, text); return true; } internal static bool IsCooldownActive(Character attacker, ItemData? weapon, string presetName, out float remaining) { remaining = 0f; if ((Object)(object)attacker == (Object)null) { return false; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { ClearCooldown(attacker, presetName); return false; } if (!Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { return false; } string text = (string.IsNullOrWhiteSpace(presetName) ? "unknown" : presetName.Trim()); double num = SecondaryAttackManager.GetNetworkTimeSeconds(); if (value.ReadyAtByPreset.TryGetValue(text, out var value2) && num < value2) { remaining = (float)Math.Max(0.0, value2 - num); if (weapon == null) { weapon = ResolveCurrentWeapon(attacker); } SyncCooldownStatusTtl(attacker, weapon, text, remaining); return true; } if (value.ReadyAtByPreset.ContainsKey(text)) { if (weapon == null) { weapon = ResolveCurrentWeapon(attacker); } SyncCooldownStatusTtl(attacker, weapon, text, 0f); ClearCooldownState(value, text); } return false; } internal static void CollectHudEntries(Player player, List entries) { if ((Object)(object)player == (Object)null || entries == null) { return; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns((Character?)(object)player)) { ClearAllCooldowns((Character)(object)player); } else { if (!Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { return; } double num = SecondaryAttackManager.GetNetworkTimeSeconds(); ItemData weapon = ResolveCurrentWeapon((Character)(object)player); bool flag = SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects((Character?)(object)player); List updateKeys = value.UpdateKeys; updateKeys.Clear(); foreach (string key in value.ReadyAtByPreset.Keys) { updateKeys.Add(key); } foreach (string item in updateKeys) { if (!value.ReadyAtByPreset.TryGetValue(item, out var value2) || value2 <= num) { if (!flag) { SyncCooldownStatusTtl((Character)(object)player, weapon, item, 0f); } ClearCooldownState(value, item); } else { float num2 = (float)Math.Max(0.0, value2 - num); float value3; float duration = (value.DurationByPreset.TryGetValue(item, out value3) ? value3 : num2); Sprite value4; Sprite icon = (Sprite)(value.IconByPreset.TryGetValue(item, out value4) ? ((object)value4) : ((object)(ResolveWeaponIcon(weapon) ?? ResolveRegisteredIcon(item)))); entries.Add(new SecondaryCooldownHudSystem.Entry(icon, num2, duration)); } } updateKeys.Clear(); } } internal static string DescribeState(Character attacker, ItemData? weapon, string presetName, MeleePresetCooldownDefinition cooldown) { if ((Object)(object)attacker == (Object)null) { return "attacker="; } if (cooldown == null) { return "cooldown="; } float num = Mathf.Max(0f, cooldown.Cooldown); string text = (string.IsNullOrWhiteSpace(presetName) ? "unknown" : presetName.Trim()); if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { return $"key={text} baseCooldown={num:0.###} ready=true reason=admin-bypass"; } if (num <= 0f) { return $"key={text} baseCooldown={num:0.###} ready=true reason=no-cooldown"; } if (weapon == null) { weapon = ResolveCurrentWeapon(attacker); } float num2 = ResolveCooldownSkillLevel(attacker, weapon, cooldown.CooldownSkill); float num3 = Mathf.Clamp01(num2 / 100f) * Mathf.Clamp01(cooldown.CooldownReductionFactor); float num4 = Mathf.Max(0f, num * (1f - num3)); CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); double num5 = SecondaryAttackManager.GetNetworkTimeSeconds(); double value2; double num6 = (value.ReadyAtByPreset.TryGetValue(text, out value2) ? Math.Max(0.0, value2 - num5) : 0.0); float ttl; float num7 = (TryGetActiveCooldownStatusTtl(attacker, text, out ttl) ? ttl : 0f); bool flag = num6 <= 0.0; return $"key={text} baseCooldown={num:0.###} finalCooldown={num4:0.###} skill={num2:0.###} reduction={num3:0.###} tableRemaining={num6:0.###} statusTtl={num7:0.###} ready={flag}"; } private static Dictionary BuildStatusMap() { CooldownStatusRegistration[] obj = new CooldownStatusRegistration[14] { new CooldownStatusRegistration("sneakAmbush", "KnifeWood", "$sa_status_sneak_ambush_cooldown"), new CooldownStatusRegistration("cleavingThrust", "THSwordWood", "$sa_status_cleaving_thrust_cooldown"), new CooldownStatusRegistration("impactBurst", "Battleaxe", "$sa_status_impact_burst_cooldown"), new CooldownStatusRegistration("boomerang", "AxeBronze", "$sa_status_boomerang_cooldown"), new CooldownStatusRegistration("spinningSweep", "AtgeirWood", "$sa_status_spinning_sweep_cooldown"), new CooldownStatusRegistration("launchSlam", "MaceWood", "$sa_status_launch_slam_cooldown"), new CooldownStatusRegistration("knockbackChain", "FistBjornClaw", "$sa_status_knockback_chain_cooldown"), new CooldownStatusRegistration("aftershock", "SledgeWood", "$sa_status_aftershock_cooldown"), new CooldownStatusRegistration("riftTrail", "SwordWood", "$sa_status_rift_trail_cooldown"), new CooldownStatusRegistration("fractureLine", "PickaxeAntler", "$sa_status_fracture_line_cooldown"), new CooldownStatusRegistration("harvestSweep", "Scythe", "$sa_status_harvest_sweep_cooldown"), new CooldownStatusRegistration("spearRain", "SpearWood", "$sa_status_spear_rain_cooldown"), new CooldownStatusRegistration("summonEmpower", "StaffSkeleton", "$sa_status_summon_empower_cooldown"), new CooldownStatusRegistration("shieldConvert", "StaffShield", "$sa_status_shield_convert_cooldown") }; Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); CooldownStatusRegistration[] array = obj; foreach (CooldownStatusRegistration cooldownStatusRegistration in array) { dictionary[cooldownStatusRegistration.PresetName] = cooldownStatusRegistration; } return dictionary; } private static void ApplyCooldownStatus(Character attacker, ItemData? weapon, string presetName, float cooldown) { if ((Object)(object)attacker == (Object)null || cooldown <= 0f || !StatusesByPreset.TryGetValue(presetName, out CooldownStatusRegistration value)) { return; } if (SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects(attacker)) { ClearCooldownStatus(attacker, presetName); return; } SEMan sEMan = attacker.GetSEMan(); if (sEMan != null) { int stableHashCode = StringExtensionMethods.GetStableHashCode(value.StatusEffectName); sEMan.AddStatusEffect(stableHashCode, true, 0, 0f); StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode); if (statusEffect != null) { statusEffect.m_ttl = cooldown; statusEffect.m_icon = ResolveWeaponIcon(weapon) ?? statusEffect.m_icon; } } } private static bool TryGetActiveCooldownStatusTtl(Character attacker, string presetName, out float ttl) { if (!TryGetActiveCooldownStatus(attacker, presetName, out StatusEffect statusEffect)) { ttl = 0f; return false; } ttl = Mathf.Max(0f, statusEffect.GetRemaningTime()); return ttl > 0f; } private static void SyncCooldownStatusTtl(Character attacker, ItemData? weapon, string presetName, float remaining) { remaining = Mathf.Max(0f, remaining); StatusEffect statusEffect; if (SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects(attacker)) { ClearCooldownStatus(attacker, presetName); } else if (TryGetActiveCooldownStatus(attacker, presetName, out statusEffect)) { statusEffect.m_ttl = statusEffect.m_time + remaining; } else if (remaining > 0f) { ApplyCooldownStatus(attacker, weapon, presetName, remaining); } } private static void ClearCooldownState(CharacterCooldownState state, string presetName) { state.ReadyAtByPreset.Remove(presetName); state.DurationByPreset.Remove(presetName); state.IconByPreset.Remove(presetName); state.HudClearedStatusByPreset.Remove(presetName); } private static void ClearCooldown(Character attacker, string presetName) { string presetName2 = (string.IsNullOrWhiteSpace(presetName) ? "unknown" : presetName.Trim()); if (Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { ClearCooldownState(value, presetName2); } ClearCooldownStatus(attacker, presetName2); } private static void ClearAllCooldowns(Character attacker) { if (Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { value.ReadyAtByPreset.Clear(); value.DurationByPreset.Clear(); value.IconByPreset.Clear(); value.HudClearedStatusByPreset.Clear(); } foreach (string key in StatusesByPreset.Keys) { ClearCooldownStatus(attacker, key); } } private static void ClearCooldownStatus(Character attacker, string presetName) { if (!((Object)(object)attacker == (Object)null) && StatusesByPreset.TryGetValue(presetName, out CooldownStatusRegistration value)) { SEMan sEMan = attacker.GetSEMan(); StatusEffect val = ((sEMan != null) ? sEMan.GetStatusEffect(StringExtensionMethods.GetStableHashCode(value.StatusEffectName)) : null); if (val != null) { val.m_ttl = val.m_time; } } } private static bool TryGetActiveCooldownStatus(Character attacker, string presetName, out StatusEffect? statusEffect) { statusEffect = null; if ((Object)(object)attacker == (Object)null || !StatusesByPreset.TryGetValue(presetName, out CooldownStatusRegistration value)) { return false; } SEMan sEMan = attacker.GetSEMan(); if (sEMan == null) { return false; } int stableHashCode = StringExtensionMethods.GetStableHashCode(value.StatusEffectName); statusEffect = sEMan.GetStatusEffect(stableHashCode); if ((Object)(object)statusEffect != (Object)null) { return statusEffect.GetRemaningTime() > 0f; } return false; } 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(string presetName) { if ((Object)(object)ObjectDB.instance == (Object)null || !StatusesByPreset.TryGetValue(presetName, out CooldownStatusRegistration value)) { return null; } return ResolveIcon(ObjectDB.instance, value.IconPrefabName); } 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 sealed class MeleePresetCooldownStatusEffect : StatusEffect { public void Initialize(string statusName, string displayName, string tooltip, Sprite? icon) { ((Object)this).name = statusName; base.m_name = displayName; base.m_tooltip = tooltip; base.m_icon = icon; base.m_ttl = 0f; } } 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 string WeaponPrefabName { get; } private int ProjectileIndex { get; } private int ProjectileCount { get; } private string TargetName { get; } private int MarkerId { get; } private float CreatedAt { get; } private Vector3 SpawnPoint { get; } private Vector3 InitialVelocity { get; } private Vector3 FallbackTargetPoint { get; } public SpearRainFollowupProjectileState(string weaponPrefabName, int projectileIndex, int projectileCount, string targetName, int markerId, float createdAt, Vector3 spawnPoint, Vector3 initialVelocity, Vector3 fallbackTargetPoint) { //IL_0034: 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_003c: 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_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) WeaponPrefabName = weaponPrefabName; ProjectileIndex = projectileIndex; ProjectileCount = projectileCount; TargetName = targetName; MarkerId = markerId; CreatedAt = createdAt; SpawnPoint = spawnPoint; InitialVelocity = initialVelocity; FallbackTargetPoint = fallbackTargetPoint; } public string Describe(Projectile projectile) { //IL_0061: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) return $"weapon={WeaponPrefabName} index={ProjectileIndex + 1}/{ProjectileCount} target={TargetName} marker={MarkerId} age={Time.time - CreatedAt:0.###}s spawn={FormatVector(SpawnPoint)} velocity={FormatVector(InitialVelocity)} fallback={FormatVector(FallbackTargetPoint)} current={FormatVector(((Component)projectile).transform.position)}"; } } 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) { TryDescribeSpearRainFollowupProjectile(projectile, out string _); 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, "MeleeProjectileHitCascadeSystem.RegisterOnProjectileHitSource"); 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, weapon); } } } internal static bool HasPendingSpearRain(Character owner, ItemData? weapon) { 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, string reason) { 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_00c1: 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_00c6: 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_011f: 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_012d: 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_0102: 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_0104: 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) if (water || (Object)(object)projectile == (Object)null || (Object)(object)collider == (Object)null || !OnProjectileHitSources.TryGetValue(projectile, out OnProjectileHitSourceState value) || value.Triggered) { if ((Object)(object)projectile != (Object)null) { OnProjectileHitSources.TryGetValue(projectile, out OnProjectileHitSourceState _); if (TryDescribeSpearRainFollowupProjectile(projectile, out string description)) { _ = " followup=[" + description + "]"; } } return; } Character hitCharacter = ProjectileRuntimeSystem.GetHitCharacter(collider); if ((value.Config.TriggerOnCharactersOnly && (Object)(object)hitCharacter == (Object)null) || ((Object)(object)hitCharacter != (Object)null && !IsValidTarget(value.Owner, hitCharacter))) { return; } value.Triggered = true; OnProjectileHitSources.Remove(projectile); ReleasePendingSpearRain(projectile, "triggered"); Vector3 val = (((Object)(object)hitCharacter != (Object)null) ? hitCharacter.GetCenterPoint() : hitPoint); GameObject val2 = Projectile.FindHitObject(collider); if (value.Config.Preset.Equals("impactBurst", StringComparison.OrdinalIgnoreCase)) { TryGrantOnProjectileHitAdrenaline(value, hitCharacter); if (!((Object)(object)hitCharacter != (Object)null) && val2 != null) { val2.GetComponent(); } TriggerImpactBurst(value, val, ResolveImpactBurstVfxPoint(hitPoint, val), hitCharacter, val2, normal); } else if (TryConsumeSpearRainCooldown(value)) { 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_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_0047: 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_01e4: 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_014d: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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) MeleeOnProjectileHitDefinition config = state.Config; SecondaryAttackPerformanceLog.Start(); int num = 0; int num2 = 0; try { if (config.Radius <= 0f || (config.DamageFactor <= 0f && config.PushFactor <= 0f)) { return; } SecondaryAttackPerformanceLog.Start(); try { PlayImpactBurstVfx(config, state, impactPoint, vfxPoint, normal); } finally { } ImpactBurstTargets.Clear(); ImpactBurstTargetIds.Clear(); float radiusSqr = config.Radius * config.Radius; SecondaryAttackPerformanceLog.Start(); try { num = Physics.OverlapSphereNonAlloc(impactPoint, config.Radius, ImpactBurstColliders, config.IncludeDestructibles ? GetImpactBurstMask() : GetCharacterMask(), (QueryTriggerInteraction)1); } finally { } _ = 96; SecondaryAttackPerformanceLog.Start(); try { for (int i = 0; i < num; i++) { Collider val = ImpactBurstColliders[i]; ImpactBurstColliders[i] = null; 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 && !TryResolveImpactBurstSkipReason(state, config, directTarget, directHitObject, val2, hitCharacter, val4, out string _) && 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)); _ = ImpactBurstTargets.Count; } finally { } SecondaryAttackPerformanceLog.Start(); try { foreach (ImpactBurstTarget impactBurstTarget in ImpactBurstTargets) { if (ApplyImpactBurstHit(state, impactBurstTarget, impactPoint, normal)) { num2++; } } } finally { } ImpactBurstTargets.Clear(); ImpactBurstTargetIds.Clear(); } finally { } } private static void PlayImpactBurstVfx(MeleeOnProjectileHitDefinition config, OnProjectileHitSourceState state, Vector3 impactPoint, Vector3 vfxPoint, Vector3 normal) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_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_002f: 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 TryResolveImpactBurstSkipReason(OnProjectileHitSourceState state, MeleeOnProjectileHitDefinition config, Character? directTarget, GameObject? directHitObject, GameObject hitObject, Character? character, IDestructible destructible, out string reason) { //IL_00f9: 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_00ff: 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_010e: Invalid comparison between Unknown and I4 reason = string.Empty; 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))) { reason = "direct target excluded"; return true; } if ((Object)(object)character != (Object)null) { if (IsValidTarget(state.Owner, character)) { return false; } reason = $"invalid character dead={character.IsDead()} enemy={BaseAI.IsEnemy(state.Owner, character)}"; return true; } if (!config.IncludeDestructibles) { reason = "destructibles disabled"; return true; } ItemData weapon = state.Weapon; if (weapon != null && weapon.m_shared?.m_tamedOnly == true) { reason = "weapon is tamedOnly"; return true; } DestructibleType destructibleType = destructible.GetDestructibleType(); if ((int)destructibleType == 0) { reason = "destructible type is None"; return true; } if ((int)destructibleType == 4) { reason = "destructible type is Character without Character target"; return true; } return false; } private static bool ApplyImpactBurstHit(OnProjectileHitSourceState state, ImpactBurstTarget target, Vector3 impactPoint, Vector3 normal) { //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_0105: 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_0117: 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_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) HitData val = state.BaseHitData.Clone(); SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, null, state.Weapon, "MeleeProjectileHitCascadeSystem.ImpactBurst"); 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 false; } if ((Object)(object)target.Character != (Object)null && val.m_dodgeable && target.Character.IsDodgeInvincible()) { Character? character = target.Character; Player val2 = (Player)(object)((character is Player) ? character : null); if (val2 != null) { val2.HitWhileDodging(); } return false; } 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); IsApplyingImpactBurstDamage = true; try { target.Destructible.Damage(val); if ((Object)(object)target.Character != (Object)null && BaseAI.IsEnemy(state.Owner, target.Character)) { TryGrantOnProjectileHitAdrenaline(state, target.Character); } } finally { IsApplyingImpactBurstDamage = false; } return true; } 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 bool TryConsumeSpearRainCooldown(OnProjectileHitSourceState state) { if (MeleePresetCooldownSystem.TryConsume(state.Owner, state.Weapon, "spearRain", state.Config.PresetCooldown, out var _)) { return true; } return false; } 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_0074: 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_008b: 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_009e: 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_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_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_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_00e3: 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_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) SecondaryAttackPerformanceLog.Start(); int num = 0; try { 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; } num++; SpawnFollowupProjectile(state, i, spawnPoint, launchVelocity, targetMarker, targetPoint, gravity, visualContext); } } finally { } } private static void SpawnFollowupProjectile(OnProjectileHitSourceState state, int projectileIndex, 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_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_0043: 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_00ac: 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) 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, state, projectileIndex, spawnPoint, launchVelocity, targetMarker, fallbackTargetPoint); 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); } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogDebug(string message) { } private static string DescribeImpactBurstTarget(ImpactBurstTarget target) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) return $"character={DescribeCharacter(target.Character)} destructible={DescribeDestructible(target.Destructible)} collider={DescribeCollider(target.Collider)} distance={target.Distance:0.###} point={FormatVector(target.Point)}"; } private static string DescribeCharacter(Character? character) { if ((Object)(object)character == (Object)null) { return ""; } return $"{((Object)character).name}(dead={character.IsDead()}, object={DescribeGameObject(((Component)character).gameObject)})"; } private static string DescribeDestructible(IDestructible? destructible) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (destructible == null) { return ""; } Component val = (Component)(object)((destructible is Component) ? destructible : null); string arg = ((val != null) ? DescribeGameObject(val.gameObject) : ""); return $"{((object)destructible).GetType().Name}(type={destructible.GetDestructibleType()}, object={arg})"; } private static string DescribeCollider(Collider? collider) { if ((Object)(object)collider == (Object)null) { return ""; } return ((Object)collider).name + "(layer=" + DescribeLayer(((Component)collider).gameObject.layer) + ", object=" + DescribeGameObject(((Component)collider).gameObject) + ")"; } private static string DescribeGameObject(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return ""; } return ((Object)gameObject).name + "(layer=" + DescribeLayer(gameObject.layer) + ")"; } private static string DescribeLayer(int layer) { string text = LayerMask.LayerToName(layer); if (!string.IsNullOrEmpty(text)) { return $"{layer}:{text}"; } return layer.ToString(); } internal static string FormatVector(Vector3 value) { return ((Vector3)(ref value)).ToString("F2"); } internal static bool TryDescribeSpearRainFollowupProjectile(Projectile? projectile, out string description) { if ((Object)(object)projectile != (Object)null && SpearRainFollowupProjectiles.TryGetValue(projectile, out SpearRainFollowupProjectileState value)) { description = value.Describe(projectile); return true; } description = ""; return false; } internal static void DestroySpearRainFollowupAfterHit(Projectile? projectile, Collider? collider, Vector3 hitPoint, bool water, Vector3 normal) { if (!((Object)(object)projectile == (Object)null) && SpearRainFollowupProjectiles.TryGetValue(projectile, out SpearRainFollowupProjectileState value)) { value.Describe(projectile); SpearRainFollowupProjectiles.Remove(projectile); SecondaryAttackPerformanceLog.Start(); ProjectileRuntimeSystem.DestroyProjectileObject(((Component)projectile).gameObject); } } private static string DescribeSpearRainFollowupProjectile(Projectile projectile) { if (!TryDescribeSpearRainFollowupProjectile(projectile, out string description)) { return ""; } return description; } private static void RegisterSpearRainFollowupProjectile(Projectile projectile, OnProjectileHitSourceState state, int projectileIndex, Vector3 spawnPoint, Vector3 launchVelocity, SpearRainTargetMarker? targetMarker, Vector3 fallbackTargetPoint) { //IL_004f: 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_0052: Unknown result type (might be due to invalid IL or missing references) SpearRainFollowupProjectiles.Remove(projectile); SpearRainFollowupProjectileState value = new SpearRainFollowupProjectileState(state.WeaponPrefabName, projectileIndex, state.Config.Count, ((Object)(object)targetMarker != (Object)null) ? targetMarker.DebugTargetName : "", ((Object)(object)targetMarker != (Object)null) ? targetMarker.DebugId : 0, Time.time, spawnPoint, launchVelocity, fallbackTargetPoint); SpearRainFollowupProjectiles.Add(projectile, value); } private static string DescribeSpearRainMarker(SpearRainTargetMarker? marker) { if (!((Object)(object)marker != (Object)null)) { return ""; } return marker.DebugDescription; } private static HitData BuildFollowupHitData(OnProjectileHitSourceState state) { HitData val = state.BaseHitData.Clone(); SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, null, state.Weapon, "MeleeProjectileHitCascadeSystem.FollowupProjectile"); 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, ItemData weapon) { 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, string reason) { if (!((Object)(object)projectile == (Object)null)) { ((Component)projectile).GetComponent()?.Release(reason); } } 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; } private static Vector3 CalculateBallisticVelocity(Vector3 spawnPoint, Vector3 targetPoint, float gravity, float flightTime) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_0035: Unknown result type (might be due to invalid IL or missing references) flightTime = Mathf.Max(0.1f, 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("reinitialized"); _owner = owner; _active = true; MeleeProjectileHitCascadeSystem.AddPendingSpearRain(owner); } internal void Release(string reason) { if (_active) { _active = false; Character owner = _owner; _owner = null; if ((Object)(object)owner != (Object)null) { MeleeProjectileHitCascadeSystem.RemovePendingSpearRain(owner, reason); } } } private void OnDestroy() { Release("destroyed"); } } internal sealed class SpearRainTargetMarker : MonoBehaviour { private Character? _target; private Vector3 _lastKnownPoint; private float _expiresAt; private bool _loggedDeathFreeze; internal bool IsValid => Time.time <= _expiresAt; internal Vector3 CurrentPoint => _lastKnownPoint; internal int DebugId => ((Object)this).GetInstanceID(); internal string DebugTargetName { get { if (!((Object)(object)_target != (Object)null)) { return ""; } return ((Object)_target).name; } } internal string DebugDescription { get { //IL_003f: Unknown result type (might be due to invalid IL or missing references) object[] obj = new object[5] { DebugId, DebugTargetName, null, null, null }; Character? target = _target; obj[2] = target != null && target.IsDead(); obj[3] = MeleeProjectileHitCascadeSystem.FormatVector(_lastKnownPoint); obj[4] = _expiresAt - Time.time; return string.Format("id={0} target={1} dead={2} point={3} expiresIn={4:0.###}", obj); } } 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); _loggedDeathFreeze = false; } 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(); } else if ((Object)(object)_target != (Object)null && !_loggedDeathFreeze) { _loggedDeathFreeze = true; } 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00f7: 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_0107: 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 = CalculateBallisticVelocity(((Component)this).transform.position, targetPoint, _gravity, flightTime); 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); } } private static Vector3 CalculateBallisticVelocity(Vector3 spawnPoint, Vector3 targetPoint, float gravity, float flightTime) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_0035: Unknown result type (might be due to invalid IL or missing references) flightTime = Mathf.Max(0.05f, flightTime); Vector3 val = Vector3.down * gravity; return (targetPoint - spawnPoint - val * (0.5f * flightTime * flightTime)) / flightTime; } } internal static class MeleeBoomerangProjectileSystem { private readonly struct PendingReturnAutoEquip { public Humanoid Owner { get; } public ItemData Item { get; } public string ItemName { get; } public int DueFrame { get; } public PendingReturnAutoEquip(Humanoid owner, ItemData item, string itemName, int dueFrame) { Owner = owner; Item = item; ItemName = itemName; 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) { return; } SecondaryAttackPerformanceLog.Start(); try { (((Component)projectile).GetComponent() ?? ((Component)projectile).gameObject.AddComponent()).Configure(projectile, attack, definition.Boomerang); } finally { } } 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) { SecondaryAttackPerformanceLog.Start(); pendingReturnAutoEquip.Owner.EquipItem(pendingReturnAutoEquip.Item, true); } } } } internal static void QueueReturnAutoEquip(Humanoid owner, ItemData item, string itemName) { 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, itemName, 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_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_0171: 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_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: 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_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0204: 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_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: 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_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) _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 num = Mathf.Max(0.5f, definition.MaxDistance); SecondaryAttackPerformanceLog.Start(); float num2 = num; int hitCount = 0; int validHitCount = 0; try { num2 = ResolveMaxReach(projectile, val, num, out hitCount, out validHitCount); } finally { } _longRadius = Mathf.Max(0.1f, num2 * 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) HitData? originalHitData = ProjectileAccess.GetOriginalHitData(_projectile); HitData val = ((originalHitData != null) ? originalHitData.Clone() : null); if (val == null) { return false; } SecondaryAttackProjectileToolTierSystem.ApplyToHitData(val, _projectile, ProjectileAccess.GetWeapon(_projectile), "MeleeBoomerangProjectileSystem.ApplyHit"); 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, out int hitCount, out int validHitCount) { //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_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_0058: Unknown result type (might be due to invalid IL or missing references) EnsureRayMask(); hitCount = Physics.RaycastNonAlloc(((Component)projectile).transform.position, aimDirection, s_reachRaycastHits, maxDistance, s_rayMaskSolids, (QueryTriggerInteraction)1); validHitCount = 0; if (hitCount == 0) { return maxDistance; } Character owner = ProjectileAccess.GetOwner(projectile); float num = maxDistance; bool flag = false; for (int i = 0; i < hitCount; i++) { RaycastHit val = s_reachRaycastHits[i]; s_reachRaycastHits[i] = default(RaycastHit); 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)) { continue; } 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))) { if (((RaycastHit)(ref val)).distance < num) { num = ((RaycastHit)(ref val)).distance; flag = true; } validHitCount++; } } if (!flag) { return maxDistance; } return Mathf.Clamp(num, 0.5f, maxDistance); } 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_00fc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_projectile == (Object)null) { return false; } SecondaryAttackPerformanceLog.Start(); ItemData spawnItem = _projectile.m_spawnItem; object obj; if (spawnItem == null) { obj = null; } else { GameObject dropPrefab = spawnItem.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if (obj == null) { obj = spawnItem?.m_shared?.m_name ?? ""; } string itemName = (string)obj; try { SecondaryAttackPerformanceLog.Start(); 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; SecondaryAttackPerformanceLog.Start(); 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) { SecondaryAttackPerformanceLog.Start(); MeleeBoomerangProjectileSystem.QueueReturnAutoEquip(owner, spawnItem, itemName); } SecondaryAttackPerformanceLog.Start(); ProjectileRuntimeSystem.DestroyProjectileObject(((Component)_projectile).gameObject); ((Behaviour)this).enabled = false; return true; } finally { } } private void CompleteReturnAtCurrentPosition() { //IL_0029: 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_0046: 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); SecondaryAttackPerformanceLog.Start(); projectile.OnHit((Collider)null, ((Component)this).transform.position, false, Vector3.up); ((Behaviour)this).enabled = false; } } } internal static class RangedSecondaryCooldownSystem { private sealed class CharacterCooldownState { public Dictionary ReadyAtByWeaponKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary DurationByWeaponKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary IconByWeaponKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public List UpdateKeys { get; } = new List(); public bool HudClearedStatus { get; set; } } private const string StatusEffectName = "SecondaryAttacks_Cooldown_rangedSecondary"; private const string FallbackIconPrefabName = "Bow"; private static readonly ConditionalWeakTable Cooldowns = new ConditionalWeakTable(); internal static void RegisterStatusEffect(ObjectDB objectDb) { if (!((Object)(object)objectDb == (Object)null) && !objectDb.m_StatusEffects.Exists((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == "SecondaryAttacks_Cooldown_rangedSecondary")) { MeleePresetCooldownStatusEffect meleePresetCooldownStatusEffect = ScriptableObject.CreateInstance(); meleePresetCooldownStatusEffect.Initialize("SecondaryAttacks_Cooldown_rangedSecondary", "$sa_status_secondary_cooldown", "$sa_tooltip_secondary_recharging", ResolveIcon(objectDb, "Bow")); objectDb.m_StatusEffects.Add((StatusEffect)(object)meleePresetCooldownStatusEffect); } } 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, weapon, 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, attack.m_weapon, 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); } internal static void UpdateActiveCooldowns(Player player) { if ((Object)(object)player == (Object)null) { return; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns((Character?)(object)player)) { ClearAllCooldowns((Character)(object)player); return; } if (!Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { if (!SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects((Character?)(object)player)) { ClearCooldownStatus((Character)(object)player); } return; } double num = SecondaryAttackManager.GetNetworkTimeSeconds(); bool flag = SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects((Character?)(object)player); List updateKeys = value.UpdateKeys; updateKeys.Clear(); foreach (var (item, num3) in value.ReadyAtByWeaponKey) { if (num3 <= num) { updateKeys.Add(item); } } foreach (string item2 in updateKeys) { value.ReadyAtByWeaponKey.Remove(item2); value.DurationByWeaponKey.Remove(item2); value.IconByWeaponKey.Remove(item2); } updateKeys.Clear(); if (flag) { if (value.ReadyAtByWeaponKey.Count > 0 && !value.HudClearedStatus) { ClearCooldownStatus((Character)(object)player); value.HudClearedStatus = true; } else if (value.ReadyAtByWeaponKey.Count == 0) { value.HudClearedStatus = false; } } else { value.HudClearedStatus = false; ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); string text2 = ResolveWeaponKey(currentWeapon); if (!string.IsNullOrWhiteSpace(text2) && value.ReadyAtByWeaponKey.TryGetValue(text2, out var value2) && value2 > num) { SyncCooldownStatusTtl((Character)(object)player, currentWeapon, (float)Math.Max(0.0, value2 - num)); } else { ClearCooldownStatus((Character)(object)player); } } } private static bool CanUse(Character attacker, ItemData? weapon, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attacker == (Object)null) { return true; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { ClearAllCooldowns(attacker); return true; } if (behavior == null || Mathf.Max(0f, behavior.Cooldown) <= 0f) { return true; } string text = ResolveWeaponKey(weapon); if (string.IsNullOrWhiteSpace(text)) { return true; } CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); double num = SecondaryAttackManager.GetNetworkTimeSeconds(); if (!value.ReadyAtByWeaponKey.TryGetValue(text, out var value2) || value2 <= num) { value.ReadyAtByWeaponKey.Remove(text); value.DurationByWeaponKey.Remove(text); value.IconByWeaponKey.Remove(text); SyncCooldownStatusTtl(attacker, weapon, 0f); return true; } SyncCooldownStatusTtl(attacker, weapon, (float)Math.Max(0.0, value2 - num)); return false; } private static bool StartCooldown(Character attacker, ItemData? weapon, ProjectileSecondaryBehavior behavior) { if ((Object)(object)attacker == (Object)null) { return true; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns(attacker)) { ClearAllCooldowns(attacker); return true; } if (behavior == null) { return true; } float num = Mathf.Max(0f, behavior.Cooldown); if (num <= 0f) { return true; } string text = ResolveWeaponKey(weapon); if (string.IsNullOrWhiteSpace(text)) { return true; } float num2 = Mathf.Clamp01(ResolveCooldownSkillLevel(attacker, weapon) / 100f) * Mathf.Clamp01(behavior.CooldownReductionFactor); float num3 = Mathf.Max(0f, num * (1f - num2)); if (num3 <= 0f) { return true; } CharacterCooldownState value = Cooldowns.GetValue(attacker, (Character _) => new CharacterCooldownState()); value.ReadyAtByWeaponKey[text] = SecondaryAttackManager.GetNetworkTimeSeconds() + num3; value.DurationByWeaponKey[text] = num3; value.IconByWeaponKey[text] = ResolveWeaponIcon(weapon) ?? ResolveRegisteredIcon(); value.HudClearedStatus = false; if (SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects(attacker)) { ClearCooldownStatus(attacker); value.HudClearedStatus = true; } else { SyncCooldownStatusTtl(attacker, weapon, num3); } return true; } internal static void CollectHudEntries(Player player, List entries) { if ((Object)(object)player == (Object)null || entries == null) { return; } if (SecondaryAttackAdminAccessSystem.ShouldBypassPresetCooldowns((Character?)(object)player)) { ClearAllCooldowns((Character)(object)player); } else { if (!Cooldowns.TryGetValue((Character)(object)player, out CharacterCooldownState value)) { return; } double num = SecondaryAttackManager.GetNetworkTimeSeconds(); List updateKeys = value.UpdateKeys; updateKeys.Clear(); foreach (string key in value.ReadyAtByWeaponKey.Keys) { updateKeys.Add(key); } foreach (string item in updateKeys) { if (!value.ReadyAtByWeaponKey.TryGetValue(item, out var value2) || value2 <= num) { value.ReadyAtByWeaponKey.Remove(item); value.DurationByWeaponKey.Remove(item); value.IconByWeaponKey.Remove(item); } else { float num2 = (float)Math.Max(0.0, value2 - num); float value3; float duration = (value.DurationByWeaponKey.TryGetValue(item, out value3) ? value3 : num2); Sprite value4; Sprite icon = (Sprite)(value.IconByWeaponKey.TryGetValue(item, out value4) ? ((object)value4) : ((object)ResolveRegisteredIcon())); entries.Add(new SecondaryCooldownHudSystem.Entry(icon, num2, duration)); } } updateKeys.Clear(); } } private static void SyncCooldownStatusTtl(Character attacker, ItemData? weapon, float remaining) { if ((Object)(object)attacker == (Object)null) { return; } if (SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects(attacker)) { ClearCooldownStatus(attacker); return; } remaining = Mathf.Max(0f, remaining); SEMan sEMan = attacker.GetSEMan(); if (sEMan == null) { return; } int stableHashCode = StringExtensionMethods.GetStableHashCode("SecondaryAttacks_Cooldown_rangedSecondary"); StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode); if (statusEffect != null) { statusEffect.m_ttl = statusEffect.m_time + remaining; if (remaining > 0f) { statusEffect.m_icon = ResolveWeaponIcon(weapon) ?? statusEffect.m_icon; } } else if (!(remaining <= 0f)) { sEMan.AddStatusEffect(stableHashCode, true, 0, 0f); StatusEffect statusEffect2 = sEMan.GetStatusEffect(stableHashCode); if (statusEffect2 != null) { statusEffect2.m_ttl = remaining; statusEffect2.m_icon = ResolveWeaponIcon(weapon) ?? statusEffect2.m_icon; } } } private static void ClearCooldownStatus(Character attacker) { if (!((Object)(object)attacker == (Object)null)) { SEMan sEMan = attacker.GetSEMan(); StatusEffect val = ((sEMan != null) ? sEMan.GetStatusEffect(StringExtensionMethods.GetStableHashCode("SecondaryAttacks_Cooldown_rangedSecondary")) : null); if (val != null) { val.m_ttl = val.m_time; } } } private static void ClearAllCooldowns(Character attacker) { if (Cooldowns.TryGetValue(attacker, out CharacterCooldownState value)) { value.ReadyAtByWeaponKey.Clear(); value.DurationByWeaponKey.Clear(); value.IconByWeaponKey.Clear(); value.HudClearedStatus = false; } ClearCooldownStatus(attacker); } private static string ResolveWeaponKey(ItemData? weapon) { if (!((Object)(object)weapon?.m_dropPrefab != (Object)null)) { return ""; } return ((Object)weapon.m_dropPrefab).name; } 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 MaxSlots = 10; private const int Columns = 5; private const int Rows = 2; private const float SlotSize = 34f; private const float SlotSpacing = 4f; 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 bool ShouldSuppressStatusEffects => IsEnabled; internal static bool ShouldSuppressCooldownStatusEffects(Character? character) { if (IsEnabled && (Object)(object)character != (Object)null) { return (Object)(object)character == (Object)(object)Player.m_localPlayer; } return false; } 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); MeleePresetCooldownSystem.CollectHudEntries(player, Entries); RangedSecondaryCooldownSystem.CollectHudEntries(player, Entries); Entries.Sort(delegate(Entry left, Entry right) { int num = left.SortOrder.CompareTo(right.SortOrder); return (num == 0) ? left.Remaining.CompareTo(right.Remaining) : num; }); bool flag2 = flag || Entries.Count > 0; RootObject.SetActive(flag2); if (flag2) { ApplyConfiguredTransform(); if (flag) { RootObject.transform.SetAsLastSibling(); } UpdateSlots(flag); } } 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_004a: Expected O, but got Unknown //IL_0082: 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_00aa: 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_00f0: 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) if ((Object)(object)RootObject != (Object)null && (Object)(object)RootRect != (Object)null && AreSlotsValid()) { return; } 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, 0.5f); float num = 186f; float num2 = 72f; RootRect.sizeDelta = new Vector2(num, num2); 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; for (int i = 0; i < 10; i++) { Slots.Add(CreateSlot(val.transform, i)); } 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_00ac: 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 * Mathf.Clamp(SecondaryAttacksPlugin.SecondaryCooldownHudScale.Value, 1f, 2f); } } private static void UpdateSlots(bool previewMode) { if (!AreSlotsValid()) { ResetHudReferences(); EnsureHud(); if ((Object)(object)RootObject == (Object)null || (Object)(object)RootRect == (Object)null || Slots.Count != 10) { return; } } int num = Mathf.Min(Entries.Count, 10); for (int i = 0; i < Slots.Count; i++) { CooldownSlot cooldownSlot = Slots[i]; if (!cooldownSlot.IsValid) { continue; } bool flag = i < num; 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() { if (Slots.Count != 10) { return false; } 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() || !((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) && ((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() || !((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; } } 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(SneakVisibilitySystem.CrouchSpeedState __state) { SneakVisibilitySystem.RestoreCrouchSpeed(__state); } } internal static class SneakAmbushChargeSystem { internal readonly struct Snapshot { internal static readonly Snapshot Empty = new Snapshot(0f, 1f); internal float ChargeSeconds { get; } internal float MaxSeconds { get; } internal float ChargeFraction => Mathf.Clamp01(ChargeSeconds / MaxSeconds); internal Snapshot(float chargeSeconds, float maxSeconds) { ChargeSeconds = chargeSeconds; MaxSeconds = Mathf.Max(0.001f, maxSeconds); } } 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 bool HudClearedStatus { get; set; } public void ClearDisplay() { MaxSeconds = 0f; Display = false; } public void ClearPendingAttack() { PendingAttackChargeSeconds = 0f; HasPendingAttackCharge = false; } public void Clear() { ChargeSeconds = 0f; HudClearedStatus = false; ClearPendingAttack(); ClearDisplay(); } } private const string StatusEffectName = "SecondaryAttacks_SneakAmbushCharge"; private const string FallbackIconPrefabName = "KnifeWood"; private const float ChargeDecayPerSecond = 1f; private static readonly ConditionalWeakTable States = new ConditionalWeakTable(); private static int StatusHash => StringExtensionMethods.GetStableHashCode("SecondaryAttacks_SneakAmbushCharge"); internal static void RegisterStatusEffect(ObjectDB objectDb) { if (!((Object)(object)objectDb == (Object)null) && !objectDb.m_StatusEffects.Exists((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == "SecondaryAttacks_SneakAmbushCharge")) { SneakAmbushChargeStatusEffect sneakAmbushChargeStatusEffect = ScriptableObject.CreateInstance(); sneakAmbushChargeStatusEffect.Initialize(ResolveIcon(objectDb, "KnifeWood")); objectDb.m_StatusEffects.Add((StatusEffect)(object)sneakAmbushChargeStatusEffect); } } 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 weapon, out SneakAmbushDefinition sneakAmbush)) { value.Clear(); RemoveStatus(player); return; } if (flag || MeleePresetCooldownSystem.IsCooldownActive((Character)(object)player, weapon, "sneakAmbush", out var _)) { value.ChargeSeconds = 0f; value.ClearDisplay(); RemoveStatus(player); return; } float num = Mathf.Max(0f, sneakAmbush.ChargeMaxSeconds); if (num <= 0f) { value.Clear(); RemoveStatus(player); return; } float num2 = Mathf.Max(0f, Time.deltaTime); bool flag2 = ((Character)player).IsCrouching(); 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; if (value.Display) { ApplyStatus(player, weapon, value); } else { RemoveStatus(player); } } 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(); RemoveStatus(player); } } 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(); RemoveStatus(player); } } internal static bool TryGetSnapshot(out Snapshot snapshot) { snapshot = Snapshot.Empty; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !States.TryGetValue(localPlayer, out SneakAmbushChargeState value) || !value.Display || value.MaxSeconds <= 0f) { return false; } snapshot = new Snapshot(Mathf.Clamp(value.ChargeSeconds, 0f, value.MaxSeconds), value.MaxSeconds); return true; } 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 void ApplyStatus(Player player, ItemData? weapon, SneakAmbushChargeState state) { if (SecondaryCooldownHudSystem.ShouldSuppressCooldownStatusEffects((Character?)(object)player)) { if (!state.HudClearedStatus) { RemoveStatus(player); state.HudClearedStatus = true; } return; } state.HudClearedStatus = false; SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { if ((Object)(object)sEMan.GetStatusEffect(StatusHash) == (Object)null) { sEMan.AddStatusEffect(StatusHash, true, 0, 0f); } StatusEffect statusEffect = sEMan.GetStatusEffect(StatusHash); if (statusEffect != null) { statusEffect.m_icon = ResolveWeaponIcon(weapon) ?? statusEffect.m_icon; } } } private static void RemoveStatus(Player player) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveStatusEffect(StatusHash, true); } } 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 sealed class SneakAmbushChargeStatusEffect : StatusEffect { public void Initialize(Sprite? icon) { ((Object)this).name = "SecondaryAttacks_SneakAmbushCharge"; base.m_name = "$sa_status_sneak_ambush_charge"; base.m_category = "SecondaryAttacks"; base.m_tooltip = "$sa_tooltip_sneak_ambush_charge"; base.m_icon = icon; base.m_startMessage = ""; base.m_stopMessage = ""; } public override bool CanAdd(Character character) { return (Object)(object)character == (Object)(object)Player.m_localPlayer; } public override bool IsDone() { SneakAmbushChargeSystem.Snapshot snapshot; return !SneakAmbushChargeSystem.TryGetSnapshot(out snapshot); } public override string GetIconText() { if (!SneakAmbushChargeSystem.TryGetSnapshot(out var snapshot)) { return ""; } return $"{Mathf.RoundToInt(snapshot.ChargeFraction * 100f)}%"; } public override string GetTooltipString() { if (!SneakAmbushChargeSystem.TryGetSnapshot(out var snapshot)) { return ""; } return SecondaryAttackLocalization.Format("$sa_tooltip_sneak_ambush_charge_progress", "Prepared: {0} / {1}s\nHigher Sneak skill charges faster.", snapshot.ChargeSeconds.ToString("0.0", CultureInfo.InvariantCulture), snapshot.MaxSeconds.ToString("0.0", CultureInfo.InvariantCulture)); } } 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"; 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_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_00df: 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_00c7: 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, out var _)) { 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)); } } 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 (SecondaryAttackManager.TryMarkCompatibilityWarningReported("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_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_004f: Unknown result type (might be due to invalid IL or missing references) bool flag = senseBlockDuration > 0f; if (flag) { EnsureUpdater(); } float num = Time.time + senseBlockDuration; Vector3 position = ((Component)player).transform.position; float num2 = Mathf.Max(0f, range); num2 *= num2; foreach (Character allCharacter in Character.GetAllCharacters()) { if (!TryGetAffectedMonster(player, allCharacter, position, num2, out MonsterAI monsterAI)) { continue; } ClearMonsterAwareness(allCharacter, monsterAI, backstabResetSeconds); if (flag) { if (SenseBlocks.TryGetValue(allCharacter, out SmokeSenseBlockState value)) { value.HiddenPlayer = player; value.Until = Mathf.Max(value.Until, num); } else { SenseBlocks[allCharacter] = 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 void ClearMonsterAwareness(Character character, MonsterAI monsterAI, float backstabResetSeconds) { //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_0085: Unknown result type (might be due to invalid IL or missing references) monsterAI.SetTarget((Character)null); ((BaseAI)monsterAI).m_alerted = 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()) { ZDO zDO = nview.GetZDO(); zDO.Set("target", ZDOID.None); if (nview.IsOwner()) { zDO.Set("alerted", 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; } bool flag = IsDebugLoggingEnabled(); 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); return true; } if (!MeleePresetCooldownSystem.TryConsume((Character)(object)val, attack.m_weapon, "spinningSweep", spinningSweep.PresetCooldown, out var _)) { return false; } if ((Object)(object)spinningSweepController == (Object)null) { spinningSweepController = ((Component)val).gameObject.AddComponent(); } spinningSweepController.Begin(attack, definition, spinningSweep); 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) { bool result = ((Character)humanoid).StartAttack((Character)null, true); IsDebugLoggingEnabled(); return result; } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogDebug(string message) { } internal static bool IsDebugLoggingEnabled() { return false; } internal static string DescribeWeapon(ItemData? 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 = weapon?.m_shared?.m_name ?? ""; } return (string)obj; } } 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 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 _speedApplied; private bool _initialLoopStartApplied; private bool _loopRearmed = true; private Attack? _skillRaiseAttack; 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); 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) { bool cancelArmed = _cancelArmed; _cancelArmed = Time.frameCount > _startedFrame + 1; _lastSecondaryHold = false; if (!cancelArmed) { _ = _cancelArmed; } } else { bool flag = !_lastSecondaryHold; _lastSecondaryHold = true; if (_cancelArmed && flag) { StopAfterCurrentAttack(); } } } private void UpdatePrimaryCancelInput(bool primaryAttackHold) { if (!primaryAttackHold) { bool primaryCancelArmed = _primaryCancelArmed; _primaryCancelArmed = Time.frameCount > _startedFrame + 1; _lastPrimaryHold = false; if (!primaryCancelArmed) { _ = _primaryCancelArmed; } } else { bool flag = !_lastPrimaryHold; _lastPrimaryHold = true; if (_primaryCancelArmed && flag) { StopAfterCurrentAttack(); } } } internal void StopAfterCurrentAttack() { _stopRequested = true; } private void Update() { bool flag = SpinningSweepSystem.IsDebugLoggingEnabled(); 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; string reason; if (currentAttack != null && ((Character)_humanoid).InAttack()) { if (currentAttack == _currentAttack) { ApplyMovementFactors(currentAttack, _spinningSweep); if (ShouldKeepLooping()) { TryUpdateSeamlessLoop(currentAttack); } } else if (flag) { _ = Time.frameCount % 10; } } else if (_stopRequested || !MatchesWeapon(_humanoid.GetCurrentWeapon())) { Object.Destroy((Object)(object)this); } else if (Time.time < _nextRepeatTime || ((Character)_humanoid).IsStaggering() || ((Character)_humanoid).InAttack()) { if (flag) { _ = Time.frameCount % 20; } } else if (!CanPayNextAttackCost(_weapon, out reason)) { 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_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_0074: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (_spinningSweep == null || (Object)(object)_humanoid == (Object)null || _weapon == null) { return false; } bool flag = SpinningSweepSystem.IsDebugLoggingEnabled(); 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)) { if (flag) { _ = Time.frameCount % 10; } 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) { if (flag) { _ = Time.frameCount % 10; } return false; } if (!CanPayNextAttackCost(_weapon, out string _)) { _stopRequested = true; return false; } PayNextAttackCost(activeAttack); _lastLoopFrame = Time.frameCount; SeekToLoopStart(attackAnimatorState); _loopRearmed = false; return true; } private bool TryRearmLoop(AnimatorStateInfo state, float loopStart, float loopEnd) { bool flag = SpinningSweepSystem.IsDebugLoggingEnabled(); if (_loopRearmed) { return true; } if (((AnimatorStateInfo)(ref state)).normalizedTime < loopEnd) { _loopRearmed = true; return true; } if (flag) { _ = Time.frameCount % 10; } return false; } private void SeekToLoopStart(AnimatorStateInfo state) { if (!((Object)(object)_animator == (Object)null) && _spinningSweep != null) { int num = ((_loopStateHash != 0) ? _loopStateHash : ((AnimatorStateInfo)(ref state)).fullPathHash); if (num != 0) { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); _animator.Play(num, 0, _spinningSweep.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 static string DescribeState(AnimatorStateInfo state) { return $"stateHash={((AnimatorStateInfo)(ref state)).fullPathHash} tagHash={((AnimatorStateInfo)(ref state)).tagHash} normalized={((AnimatorStateInfo)(ref state)).normalizedTime:0.###} length={((AnimatorStateInfo)(ref state)).length:0.###} speed={((AnimatorStateInfo)(ref state)).speed:0.###}"; } private bool CanPayNextAttackCost(ItemData weapon, out string reason) { reason = ""; SharedData shared = weapon.m_shared; Attack val = shared?.m_secondaryAttack; if (val == null) { reason = "secondary attack is null"; return false; } float num = Mathf.Max(0f, shared.m_useDurabilityDrain * (_definition?.DurabilityFactor ?? 1f)); if (num > 0f && weapon.m_durability + 0.001f < num) { reason = $"durability {weapon.m_durability:0.###} < {num:0.###}"; return false; } float num2 = Mathf.Max(0f, val.m_attackStamina); if (num2 > 0f && !((Character)_humanoid).HaveStamina(num2)) { reason = $"stamina cost={num2:0.###}"; return false; } float num3 = Mathf.Max(0f, val.m_attackEitr); if (num3 > 0f && !((Character)_humanoid).HaveEitr(num3)) { reason = $"eitr cost={num3:0.###}"; return false; } float num4 = Mathf.Max(0f, val.m_attackHealth); if (num4 > 0f && !((Character)_humanoid).HaveHealth(num4) && val.m_attackHealthLowBlockUse) { reason = $"health cost={num4:0.###}"; return false; } reason = "ok"; 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 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() { SweepTrailResetSystem.ClearWeaponTrails(_currentAttack); RestoreSkillRaiseFactor(); RestoreAnimationSpeed(); } } internal static class SweepTrailResetSystem { 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? 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) { GameObject rightItemInstance = GetRightItemInstance(attack); if (!((Object)(object)rightItemInstance == (Object)null)) { MeleeWeaponTrail[] componentsInChildren = rightItemInstance.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ClearTrail(componentsInChildren[i]); } } } private static void ClearTrail(MeleeWeaponTrail trail) { //IL_008d: 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 (trailListFields[i]?.GetValue(trail) is IList list) { list.Clear(); } } object? obj = TrailMeshField?.GetValue(trail); Mesh val = (Mesh)((obj is Mesh) ? obj : null); if (val != null) { val.Clear(); } TrailEmitTimeField?.SetValue(trail, 0f); if (TrailLastPositionField != null) { TrailLastPositionField.SetValue(trail, ResolveCurrentTrailPosition(trail)); } } private static Vector3 ResolveCurrentTrailPosition(MeleeWeaponTrail trail) { //IL_0022: 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_004f: Unknown result type (might be due to invalid IL or missing references) object? obj = TrailTipField?.GetValue(trail); Transform val = (Transform)((obj is Transform) ? obj : null); if ((Object)(object)val != (Object)null) { return val.position; } object? obj2 = TrailBaseField?.GetValue(trail); Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if (!((Object)(object)val2 != (Object)null)) { return ((Component)trail).transform.position; } return val2.position; } private static GameObject? GetRightItemInstance(Attack? attack) { if ((Object)(object)attack?.m_character == (Object)null) { return null; } object? value = AttackVisEquipmentField.GetValue(attack); VisEquipment val = (VisEquipment)((value is VisEquipment) ? value : null); GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null) { return val2; } val = ((Component)attack.m_character).GetComponent(); if (!((Object)(object)val != (Object)null)) { return null; } object? value2 = VisRightItemInstanceField.GetValue(val); return (GameObject?)((value2 is GameObject) ? value2 : null); } } internal static class SummonQualityHudSystem { private sealed class HudLevelGroup { internal int Level { get; } internal GameObject Group { get; } internal Transform GuiTransform { get; } internal bool OwnsGroup { get; } internal HudLevelGroup(int level, GameObject group, Transform guiTransform, bool ownsGroup) { Level = level; Group = group; GuiTransform = guiTransform; OwnsGroup = ownsGroup; } } 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; } 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 = $"level_{level}"; Transform val = transform.Find(text); if ((Object)(object)val != (Object)null) { ActiveGroups[instanceId] = new HudLevelGroup(level, ((Component)val).gameObject, transform, ownsGroup: false); return ((Component)val).gameObject; } GameObject val2 = Object.Instantiate(((Component)level3).gameObject, transform); ((Object)val2).name = text; val2.SetActive(false); if (!TryAddExtraStars(val2.transform, starCount)) { Object.Destroy((Object)(object)val2); return null; } ActiveGroups[instanceId] = new HudLevelGroup(level, val2, transform, ownsGroup: true); 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 (value.OwnsGroup && (Object)(object)value.Group != (Object)null) { Object.Destroy((Object)(object)value.Group); } ActiveGroups.Remove(instanceId); } } private static void HideAllOwnedGroups() { foreach (HudLevelGroup value in ActiveGroups.Values) { if (value.OwnsGroup && (Object)(object)value.Group != (Object)null) { value.Group.SetActive(false); } } } } internal static class SweepHitStopSystem { internal static bool TryGetSuppression(Character? character, out string presetName) { presetName = ""; if ((Object)(object)character == (Object)null) { return false; } SpinningSweepController component = ((Component)character).GetComponent(); if (component != null && component.SuppressesHitStop) { presetName = "spinningSweep"; return true; } HarvestSweepController component2 = ((Component)character).GetComponent(); if (component2 != null && component2.SuppressesHitStop) { presetName = "harvestSweep"; return true; } return false; } internal static bool IsDebugLoggingEnabled(string presetName) { if (!(presetName == "spinningSweep")) { if (presetName == "harvestSweep") { return HarvestSweepSystem.IsDebugLoggingEnabled(); } return false; } return SpinningSweepSystem.IsDebugLoggingEnabled(); } internal static bool ShouldSuppress(Character? character, float duration) { if (duration <= 0f || !TryGetSuppression(character, out string presetName)) { return false; } if (IsDebugLoggingEnabled(presetName)) { string text = (((Object)(object)character != (Object)null) ? ((Object)character).name : ""); SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"[SweepHitStop] suppressed source=Character.FreezeFrame preset={presetName} character={text} duration={duration:0.###} frame={Time.frameCount}."); } return true; } 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_0098: 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_00ef: 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_0114: 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_0128: 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.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 void HideAll() { foreach (TextMeshProUGUI value in ActiveTexts.Values) { if ((Object)(object)value != (Object)null) { ((Component)value).gameObject.SetActive(false); } } } 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; private bool _registeredAsyncWork; 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); SecondaryAttackManager.RegisterAsyncSecondaryWork(_owner); _registeredAsyncWork = true; } private void OnDestroy() { ConsumeReloadIfNeeded(); if (_attack != null) { ActiveBurstFireControllers.Remove(_attack); } if (_registeredAsyncWork) { SecondaryAttackManager.UnregisterAsyncSecondaryWork(_owner); _registeredAsyncWork = false; } } 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)) { _nextShotAt = Time.time + _interval; _remainingShots--; ReplayFireAnimation(); FireSingleBurstFireShot(_attack, _definition); if (_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 val.GetCurrentWeapon() == _attack.m_weapon; } return true; } 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) { _reloadConsumed = true; ClearDeferredBurstFireReloadReset(_attack); if (!((Object)(object)_attack.m_character == (Object)null) && _attack.m_requiresReload) { bool active = SecondaryAttackManager.BeginReloadStateConsumption(_attack); _attack.m_character.ResetLoadedWeapon(); SecondaryAttackManager.EndReloadStateConsumption(_attack, active); } } } } 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; private bool _registeredAsyncWork; 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; SecondaryAttackManager.RegisterAsyncSecondaryWork(_owner); _registeredAsyncWork = true; } private void OnDestroy() { if (_registeredAsyncWork) { SecondaryAttackManager.UnregisterAsyncSecondaryWork(_owner); _registeredAsyncWork = false; } } 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 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; private bool _registeredAsyncWork; 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_012c: 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_018b: 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_01ab: Unknown result type (might be due to invalid IL or missing references) _attack = attack; _owner = owner; _weapon = weapon; _ammo = ammo; _projectile = projectile; _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); SecondaryAttackManager.RegisterAsyncSecondaryWork(_owner); _registeredAsyncWork = true; } private void OnDestroy() { if (_registeredAsyncWork) { SecondaryAttackManager.UnregisterAsyncSecondaryWork(_owner); _registeredAsyncWork = false; } } private void FixedUpdate() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (_released) { 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.GetCenterPoint()); } 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(Vector3 targetPoint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //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_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_005a: 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_0083: 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_00ab: 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) Vector3 val = targetPoint - ((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); _released = true; Object.Destroy((Object)(object)this); } } private static int AimRayMask; private static int ShieldChargeCollisionMask; private static int ShieldChargeImpactMask; private const float MeteorFallbackRange = 32f; private const float PiercingShotHitCooldown = 0.25f; private const float SentinelTargetScanInterval = 0.15f; private const float SentinelTargetScanJitter = 0.03f; 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 true; } ProjectileSecondaryBehavior projectileSecondaryBehavior = (ProjectileSecondaryBehavior)definition.Behavior; if (!OverchargedBombSystem.TryConsumeStackCost(attack, projectileSecondaryBehavior)) { attack.m_consumeItem = false; return true; } 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 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 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 TryHandleBurstPreset(Attack attack, SecondaryAttackDefinition definition, SecondaryAttackPreset preset) { switch (preset) { case SecondaryAttackPreset.Barrage: FireBarrage(attack, definition); return true; case SecondaryAttackPreset.Volley: return FireVolley(attack, definition); case SecondaryAttackPreset.Piercing: FirePiercingShot(attack, definition); return true; case SecondaryAttackPreset.Scatter: FireScatterRicochet(attack, definition); return true; case SecondaryAttackPreset.Spiral: FireSpiralBurst(attack, definition); return true; case SecondaryAttackPreset.Sentinel: FireSentinel(attack, definition); return true; case SecondaryAttackPreset.Meteor: FireMeteor(attack, definition); return true; case SecondaryAttackPreset.Burst: FireBurstFire(attack, definition); return true; case SecondaryAttackPreset.StickyDetonator: FireStickyDetonator(attack, definition); return true; case SecondaryAttackPreset.OverchargedBomb: return FireOverchargedBomb(attack, definition); default: return 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_0004: 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_0011: Unknown result type (might be due to invalid IL or missing references) if (!TryHandleScatterRicochetProjectileHit(projectile, collider, hitPoint, water, 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 (SecondaryAttackManager.TryMarkCompatibilityIssueReported(text + "|" + text2 + "|" + reason)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping custom secondary for " + text + ": " + reason)); } } internal static void FireBarrage(Attack attack, SecondaryAttackDefinition definition) { //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_0061: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_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_0081: 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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return; } 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; } for (int i = 0; i < projectileSecondaryBehavior.ProjectileCount; i++) { SpawnBarrageShot(attack, launchData, projectileSecondaryBehavior, val, aimDirection, horizontalRight, i); } } 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_0037: 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_0072: 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_005f: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_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_00c1: 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_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; Vector3 val = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref val, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); if (!TryResolveVolleyTargetPoint(attack, definition, launchData, 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 void FirePiercingShot(Attack attack, SecondaryAttackDefinition definition) { //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_004a: 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_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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return; } 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); } } } 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_013b: 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_0142: 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_014b: 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), "ProjectileRuntimeSystem.ApplyPiercingShotHit"); 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 void FireScatterRicochet(Attack attack, SecondaryAttackDefinition definition) { //IL_0026: 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_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_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_0042: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { 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)) { RegisterScatterRicochetProjectile(projectileComponent, attack, launchData, (ProjectileSecondaryBehavior)definition.Behavior, speed); } } } 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, bool water, 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 string DescribeProjectileForScatterDebug(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return ""; } string text = (((Object)(object)projectile.m_spawnOnHit != (Object)null) ? ((Object)projectile.m_spawnOnHit).name : ""); return ((Object)projectile).name + $" aoe={projectile.m_aoe:0.###}" + $" ttl={projectile.m_ttl:0.###}" + $" gravity={projectile.m_gravity:0.###}" + $" drag={projectile.m_drag:0.###}" + $" rayRadius={projectile.m_rayRadius:0.###}" + $" doOwnerRaytest={projectile.m_doOwnerRaytest}" + $" canHitWater={projectile.m_canHitWater}" + " spawnOnHit=" + text + $" spawnOnTtl={projectile.m_spawnOnTtl}" + $" stayStatic={projectile.m_stayAfterHitStatic}" + $" stayDynamic={projectile.m_stayAfterHitDynamic}" + $" bounce={projectile.m_bounce}"; } private static string DescribeColliderForScatterDebug(Collider collider) { if ((Object)(object)collider == (Object)null) { return ""; } GameObject gameObject = ((Component)collider).gameObject; string arg = LayerMask.LayerToName(gameObject.layer); string text = (((Object)(object)collider.attachedRigidbody != (Object)null) ? ((Object)collider.attachedRigidbody).name : ""); return ((Object)gameObject).name + $" layer={gameObject.layer}:{arg}" + " tag=" + gameObject.tag + " rigidbody=" + text; } 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 void FireSpiralBurst(Attack attack, SecondaryAttackDefinition definition) { //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_0061: 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_004e: 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_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_0094: 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_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_00a8: 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; } 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; } for (int i = 0; i < num; i++) { SpawnSpiralBurstProjectile(attack, launchData, projectileSecondaryBehavior, val, aimDirection, right, up, i, num); } } internal static void FireSentinel(Attack attack, SecondaryAttackDefinition definition) { //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_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_004e: 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_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) //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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return; } 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; } } } internal static void FireMeteor(Attack attack, SecondaryAttackDefinition definition) { //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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_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_0064: 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_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_00a6: 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; } 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; } for (int i = 0; i < projectileSecondaryBehavior.ProjectileCount; i++) { SpawnMeteor(attack, launchData, projectileSecondaryBehavior, targetPoint, horizontalForward, horizontalRight); } } 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 void FireBurstFire(Attack attack, SecondaryAttackDefinition definition) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) { int num = Mathf.Max(1, projectileSecondaryBehavior.ProjectileCount); if (FireSingleBurstFireShot(attack, definition) && num > 1) { DeferredBurstFireReloadResets.Add(attack); new GameObject("SecondaryAttacks_" + GetPresetName(projectileSecondaryBehavior.Preset)).AddComponent().Initialize(attack, definition, num - 1); } } } 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); } ProjectileLaunchData projectileLaunchData = new ProjectileLaunchData(attackProjectile, ammoItem, num, num2, num3, num4, num5, num6, configuredDamageFactor, configuredSkillRaiseFactor, configuredAdrenalineFactor, attack.m_randomVelocity && !attack.m_bowDraw); DumpRuntimeLaunchProfile(attack, projectileLaunchData); return projectileLaunchData; } 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; } private static string FormatVector3(Vector3 value) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return $"({value.x:0.###},{value.y:0.###},{value.z:0.###})"; } 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) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (preset != SecondaryAttackPreset.Volley) { return true; } Vector3 originPoint = default(Vector3); Vector3 aimDirection = default(Vector3); attack.GetProjectileSpawnPoint(ref originPoint, ref aimDirection); aimDirection = ApplyLaunchAngle(attack, aimDirection); ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!launchData.IsValid) { return false; } Vector3 targetPoint; return TryResolveVolleyTargetPoint(attack, definition, launchData, originPoint, aimDirection, out targetPoint); } private static bool TryResolveVolleyTargetPoint(Attack attack, SecondaryAttackDefinition definition, ProjectileLaunchData launchData, 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; } internal static int GetShieldChargeCollisionMask() { if (ShieldChargeCollisionMask == 0) { ShieldChargeCollisionMask = LayerMask.GetMask(new string[7] { "Default", "static_solid", "Default_small", "piece", "terrain", "blocker", "vehicle" }); } return ShieldChargeCollisionMask; } internal static int GetShieldChargeImpactMask() { if (ShieldChargeImpactMask == 0) { ShieldChargeImpactMask = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "terrain", "blocker", "vehicle", "character", "character_net", "character_ghost", "hitbox", "character_noenv" }); } return ShieldChargeImpactMask; } 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); } } } private static void DumpRuntimeLaunchProfile(Attack attack, ProjectileLaunchData launchData) { 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; } string text = (string)obj; if (SecondaryAttackManager.ShouldDumpRuntimeProfileForProjectileRuntime(text) && SecondaryAttackManager.TryMarkRuntimeDumpReported("launch|" + text)) { GameObject? projectilePrefab = launchData.ProjectilePrefab; Projectile val = ((projectilePrefab != null) ? projectilePrefab.GetComponent() : null); ManualLogSource modLogger = SecondaryAttacksPlugin.ModLogger; string[] obj2 = new string[19] { "[RuntimeDump] ", text, " launch payload=", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; GameObject? projectilePrefab2 = launchData.ProjectilePrefab; obj2[3] = ((projectilePrefab2 != null) ? ((Object)projectilePrefab2).name : null) ?? ""; obj2[4] = $" speed={launchData.ProjectileVelocity}"; obj2[5] = $" speedMin={launchData.ProjectileVelocityMin}"; obj2[6] = $" accuracy={launchData.ProjectileAccuracy}"; obj2[7] = $" accuracyMin={launchData.ProjectileAccuracyMin}"; obj2[8] = $" useRandomVelocity={launchData.UseRandomVelocity}"; obj2[9] = $" damageFactor={launchData.DamageFactor}"; obj2[10] = $" configuredDamageFactor={launchData.ConfiguredDamageFactor}"; obj2[11] = $" configuredSkillRaiseFactor={launchData.ConfiguredSkillRaiseFactor}"; obj2[12] = $" configuredAdrenalineFactor={launchData.ConfiguredAdrenalineFactor}"; obj2[13] = $" hitNoise={launchData.AttackHitNoise}"; obj2[14] = $" gravity={val?.m_gravity ?? 0f}"; obj2[15] = $" drag={val?.m_drag ?? 0f}"; obj2[16] = $" ttl={val?.m_ttl ?? 0f}"; obj2[17] = $" rayRadius={val?.m_rayRadius ?? 0f}"; obj2[18] = $" aoe={val?.m_aoe ?? 0f}"; modLogger.LogInfo((object)string.Concat(obj2)); } } internal static void FireStickyDetonator(Attack attack, SecondaryAttackDefinition definition) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_00bd: 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_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_00ce: 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) ProjectileLaunchData launchData = CreateLaunchData(attack, definition); if (!TryGetProjectilePayload(attack, definition, launchData, out Projectile _)) { return; } 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); } } } } 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) && ScaledAoes.Length == 0 && 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 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.0.1")] [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 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 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("Blood Magic skill raise amount per actual consumed health. 0 disables this custom health-cost skill gain and keeps the vanilla Blood Magic skill gain behavior. Example: consuming 160 health and 0.01 factor awards 1.6 raise amount.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.1f), new object[1] { new ConfigurationManagerAttributes { Order = 640 } })); } } 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 SecondaryCooldownHudScale; internal ConfigEntry SecondaryCooldownHudPositionX; internal ConfigEntry SecondaryCooldownHudPositionY; internal void Bind(SecondaryAttacksPlugin plugin) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown SecondaryCooldownHudEnabled = plugin.config("3 - UI", "Secondary Cooldown HUD Enabled", Toggle.On, "If on, secondary attack cooldowns are shown in a dedicated HUD block instead of status effect icons. While this HUD is on, secondary cooldown center messages are suppressed.", synchronizedSetting: false); SecondaryCooldownHudScale = plugin.config("3 - UI", "Secondary Cooldown HUD Scale", 2f, new ConfigDescription("Client-side scale for the secondary cooldown HUD block.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 2f), Array.Empty()), synchronizedSetting: false); SecondaryCooldownHudPositionX = plugin.config("3 - UI", "Secondary Cooldown HUD Position X", 0.6f, 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; } private class AcceptableShortcuts : AcceptableValueBase { public AcceptableShortcuts() : base(typeof(KeyboardShortcut)) { } public override object Clamp(object value) { return value; } public override bool IsValid(object value) { return true; } public override string ToDescriptionString() { return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes); } } internal const string ModName = "SecondaryAttacks"; internal const string ModVersion = "1.0.1"; 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; internal static string ConnectionError = ""; 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.0.1", MinimumRequiredVersion = "1.0.1" }; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private string? _lastConfigFileText; private bool _suppressWorldApplySettingChange; private const long RELOAD_DELAY = 10000000L; 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 SecondaryCooldownHudScale => Settings.Ui.SecondaryCooldownHudScale; internal static ConfigEntry SecondaryCooldownHudPositionX => Settings.Ui.SecondaryCooldownHudPositionX; internal static ConfigEntry SecondaryCooldownHudPositionY => Settings.Ui.SecondaryCooldownHudPositionY; internal static ConfigEntry AdminNoPresetCooldowns => Settings.Admin.AdminNoPresetCooldowns; public void Awake() { SecondaryAttackLocalization.Load(); bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; Settings.Bind(this); RegisterWorldApplySettingHandlers(); _serverConfigLocked = Settings.General.LockConfiguration; ConfigSync.AddLockingConfigEntry(_serverConfigLocked); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SecondaryAttackCompat.TryInstallStartupHooks(_harmony); SecondaryAttackFacade.Initialize(); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { UnregisterWorldApplySettingHandlers(); SecondaryAttackFacade.Dispose(); SaveWithRespectToConfigSet(); _watcher?.Dispose(); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { ModLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { string b = File.ReadAllText(ConfigFileFullPath); if (string.Equals(_lastConfigFileText, b, StringComparison.Ordinal)) { return; } _suppressWorldApplySettingChange = true; try { SaveWithRespectToConfigSet(reload: true); } finally { _suppressWorldApplySettingChange = false; } SecondaryAttackFacade.RequestCurrentWorldReapply(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); ModLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { ModLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private static string? ReadFileTextIfExists(string path) { if (!File.Exists(path)) { return null; } return File.ReadAllText(path); } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } 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? AmmoField = AccessTools.Field(typeof(Projectile), "m_ammo"); 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? StatusEffectHashField = AccessTools.Field(typeof(Projectile), "m_statusEffectHash"); 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 ItemData? GetAmmo(Projectile projectile) { object? obj = AmmoField?.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 int GetStatusEffectHash(Projectile projectile) { object obj = StatusEffectHashField?.GetValue(projectile); if (obj is int) { return (int)obj; } return 0; } 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 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 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 _registered; private bool _activated; private bool _durabilityDrained; private bool _finished; private bool _visualCreated; private bool _visualCreationFinalized; 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)); _registered = true; TryResolveTrailSamplers(); SampleTrails(); SecondaryAttackManager.RegisterAsyncSecondaryWork((Character?)(object)attack.m_character); ((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(); 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(); return true; } 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; } GameObject rightItemInstance = GetRightItemInstance(Attack); if ((Object)(object)rightItemInstance == (Object)null) { return; } MeleeWeaponTrail[] componentsInChildren = rightItemInstance.GetComponentsInChildren(true); foreach (MeleeWeaponTrail obj in componentsInChildren) { object? value = TrailBaseField.GetValue(obj); Transform val = (Transform)((value is Transform) ? value : null); object? value2 = TrailTipField.GetValue(obj); Transform val2 = (Transform)((value2 is Transform) ? value2 : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { object? value3 = TrailMaterialField.GetValue(obj); Material material = (Material)((value3 is Material) ? value3 : null); Color[] colors = TrailColorsField.GetValue(obj) as Color[]; float[] sizes = TrailSizesField.GetValue(obj) as float[]; float lifeTime = ((TrailLifeTimeField.GetValue(obj) is float num) ? num : 0.2f); int subdivisions = ((TrailSubdivisionsField.GetValue(obj) is int num2) ? num2 : 0); _trailSamplers.Add(new TrailSampler(val, val2, 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(EvaluateColor(trailSampler.Colors, normalizedAge), tint, alphaFactor); float num3 = Mathf.Max(0.01f, EvaluateSize(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 static Color EvaluateColor(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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_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 = 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 EvaluateSize(float[]? sizes, float normalizedAge) { if (sizes == null || sizes.Length == 0) { return 1f; } if (sizes.Length == 1) { return sizes[0]; } float num = 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); } 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); if (_registered) { SecondaryAttackManager.UnregisterAsyncSecondaryWork((Character?)(object)Attack?.m_character); _registered = false; } } 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(); } } private const bool HitThroughWalls = false; private const int MaxTrailSamples = 64; private const float MinTrailSampleDistance = 0.025f; private const int MaxTrailSubdivisions = 8; private const float PostTriggerVisualCaptureSeconds = 0.18f; private const float VisualFadeSeconds = 1f; private const float FanStepDegrees = 4f; 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 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(); } } 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(); 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(); } HitTargets.Clear(); } 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 GameObject? GetRightItemInstance(Attack attack) { object? value = AttackVisEquipmentField.GetValue(attack); VisEquipment val = (VisEquipment)((value is VisEquipment) ? value : null); GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null) { return val2; } val = (((Object)(object)attack.m_character != (Object)null) ? ((Component)attack.m_character).GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return null; } object? value2 = VisRightItemInstanceField.GetValue(val); return (GameObject?)((value2 is GameObject) ? value2 : null); } private static Color ResolveVisualTint(RiftTrailDefinition riftTrail) { //IL_0023: 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) Color result = default(Color); if (!string.IsNullOrWhiteSpace(riftTrail.VisualTint) && ColorUtility.TryParseHtmlString(riftTrail.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; } } internal sealed class SecondaryAttackCompiledSnapshot { public static readonly SecondaryAttackCompiledSnapshot Empty = new SecondaryAttackCompiledSnapshot(0, new NormalizedSecondaryAttackConfigFile()); public int SnapshotId { get; } public NormalizedSecondaryAttackConfigFile Config { get; } public IReadOnlyDictionary Weapons => Config.Weapons; public IReadOnlyDictionary GlobalRangedPresets => Config.GlobalRangedPresets; public IReadOnlyDictionary GlobalBloodMagicPresets => Config.GlobalBloodMagicPresets; public NormalizedWeaponConfig? GlobalMeleeFallback => Config.GlobalMeleeFallback; public IReadOnlyDictionary Effects => Config.Effects; public IReadOnlyDictionary MagicSummons => Config.MagicSummons; public SecondaryAttackCompiledSnapshot(int snapshotId, NormalizedSecondaryAttackConfigFile config) { SnapshotId = snapshotId; Config = config ?? throw new ArgumentNullException("config"); } } 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 IReadOnlyDictionary Effects => CompiledSnapshot.Effects; 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 SecondaryAttackConfigClone { internal static T Shallow(T source) where T : class, new() { T val = new T(); PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.CanRead && propertyInfo.CanWrite && propertyInfo.GetIndexParameters().Length == 0) { propertyInfo.SetValue(val, propertyInfo.GetValue(source, null), null); } } return val; } } internal enum SecondaryAttackYamlDomainId { Ranged, Melee, BloodMagic } internal sealed class SecondaryAttackYamlDomain { public SecondaryAttackYamlDomainId Id { get; } public string FileName { get; } public string FilePath { get; } public string SyncedIdentifier { get; } public Func GetDefaultContents { get; } internal SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId id, string fileName, string filePath, string syncedIdentifier, Func getDefaultContents) { Id = id; FileName = fileName; FilePath = filePath; SyncedIdentifier = syncedIdentifier; 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"; private const string SyncedRangedYamlIdentifier = "secondary_attack_ranged_yaml"; private const string SyncedMeleeYamlIdentifier = "secondary_attack_melee_yaml"; private const string SyncedBloodMagicYamlIdentifier = "secondary_attack_blood_magic_yaml"; internal const long ReloadDelayTicks = 10000000L; 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, "secondary_attack_ranged_yaml", () => SecondaryAttackDefaultYamlResources.Load("SecondaryAttacks.Ranged.yml")), new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.Melee, "SecondaryAttacks.Melee.yml", MeleeYamlFilePath, "secondary_attack_melee_yaml", () => SecondaryAttackDefaultYamlResources.Load("SecondaryAttacks.Melee.yml")), new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.BloodMagic, "SecondaryAttacks.BloodMagic.yml", BloodMagicYamlFilePath, "secondary_attack_blood_magic_yaml", () => 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 IReadOnlyDictionary All => _texts; public SecondaryAttackYamlTexts(IReadOnlyDictionary texts) { _texts = new Dictionary(texts); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { _texts.TryAdd(domain.Id, string.Empty); } } public string Get(SecondaryAttackYamlDomainId id) { if (!_texts.TryGetValue(id, out string value)) { return string.Empty; } return value; } public string GetContentFingerprint() { 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 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); public IReadOnlyDictionary Effects { 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 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, Effects = new Dictionary(StringComparer.OrdinalIgnoreCase) }; 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; } hasRootMapping = parser.Current is MappingStart; return true; } } 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 static class SecondaryAttackLocalization { internal const string StatusSecondaryCooldown = "$sa_status_secondary_cooldown"; internal const string StatusSneakAmbushCooldown = "$sa_status_sneak_ambush_cooldown"; internal const string StatusCleavingThrustCooldown = "$sa_status_cleaving_thrust_cooldown"; internal const string StatusImpactBurstCooldown = "$sa_status_impact_burst_cooldown"; internal const string StatusBoomerangCooldown = "$sa_status_boomerang_cooldown"; internal const string StatusSpinningSweepCooldown = "$sa_status_spinning_sweep_cooldown"; internal const string StatusLaunchSlamCooldown = "$sa_status_launch_slam_cooldown"; internal const string StatusKnockbackChainCooldown = "$sa_status_knockback_chain_cooldown"; internal const string StatusAftershockCooldown = "$sa_status_aftershock_cooldown"; internal const string StatusRiftTrailCooldown = "$sa_status_rift_trail_cooldown"; internal const string StatusFractureLineCooldown = "$sa_status_fracture_line_cooldown"; internal const string StatusHarvestSweepCooldown = "$sa_status_harvest_sweep_cooldown"; internal const string StatusSpearRainCooldown = "$sa_status_spear_rain_cooldown"; internal const string StatusSummonEmpowerCooldown = "$sa_status_summon_empower_cooldown"; internal const string StatusShieldConvertCooldown = "$sa_status_shield_convert_cooldown"; internal const string StatusSneakAmbushCharge = "$sa_status_sneak_ambush_charge"; internal const string TooltipSecondaryRecharging = "$sa_tooltip_secondary_recharging"; internal const string TooltipSneakAmbushCharge = "$sa_tooltip_sneak_ambush_charge"; internal const string TooltipSneakAmbushChargeProgress = "$sa_tooltip_sneak_ambush_charge_progress"; internal const string HintDetonate = "$sa_hint_detonate"; internal const string HudEmpower = "$sa_hud_empower"; internal const string SummonNameFormat = "$sa_summon_name_format"; 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 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) { SecondaryAttackDefinition definition = activeAttack.Definition; if (definition.Behavior is ProjectileSecondaryBehavior projectileSecondaryBehavior) { return projectileSecondaryBehavior.AdrenalineFactor; } return ResolveDefinitionFactor(definition); } internal static float ResolveDefinitionFactor(SecondaryAttackDefinition definition) { 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 { private const string AdminProbePrefix = "secondaryattacks_admintest_"; private const float AdminProbeRetrySeconds = 5f; private const float AdminProbeTimeoutSeconds = 3f; private static ZNet? _adminProbeZNet; private static long _adminProbePlayerId; private static string _adminProbeToken = ""; private static bool _adminProbePending; private static bool? _adminProbeVerified; private static float _adminProbeNextTime; private static float _adminProbeDeadline; private static int _adminAccessFrame = -1; private static bool _adminAccess; internal static void Update() { if (!IsAdminNoPresetCooldownEnabled()) { ResetServerAdminProbe(); } else { PrimeServerAdminProbe(); } } internal static bool ShouldBypassPresetCooldowns(Character? character) { if (IsAdminNoPresetCooldownEnabled()) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { return HasCachedServerAdminAccess(); } } return false; } internal static bool HandleServerAdminProbeRemotePrint(string text) { if (!_adminProbePending) { return false; } if (string.Equals(text, "Unbanning user " + _adminProbeToken, StringComparison.Ordinal)) { MarkServerAdminProbeSuccess(ZNet.instance); return true; } if (string.Equals(text, "You are not admin", StringComparison.Ordinal)) { _adminProbePending = false; _adminProbeVerified = false; _adminProbeNextTime = Time.realtimeSinceStartup + 5f; return true; } return false; } private static bool IsAdminNoPresetCooldownEnabled() { return SecondaryAttacksPlugin.AdminNoPresetCooldowns.Value == SecondaryAttacksPlugin.Toggle.On; } private static bool HasCachedServerAdminAccess() { int frameCount = Time.frameCount; if (_adminAccessFrame == frameCount) { return _adminAccess; } _adminAccessFrame = frameCount; _adminAccess = HasServerAdminAccess(); return _adminAccess; } private static bool HasServerAdminAccess() { if ((Object)(object)ZNet.instance == (Object)null) { return true; } if (ZNet.instance.IsServer() || ZNet.instance.LocalPlayerIsAdminOrHost()) { MarkServerAdminProbeSuccess(ZNet.instance); return true; } UpdateServerAdminProbeState(); if (_adminProbeVerified == true) { return true; } StartServerAdminProbe(force: false); return false; } private static void PrimeServerAdminProbe() { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { ResetServerAdminProbe(); return; } UpdateServerAdminProbeState(); if (!_adminProbeVerified.HasValue) { StartServerAdminProbe(force: false); } } private static void UpdateServerAdminProbeState() { ZNet instance = ZNet.instance; long localPlayerId = GetLocalPlayerId(); if (_adminProbeZNet != instance || _adminProbePlayerId != localPlayerId) { ResetServerAdminProbe(); _adminProbeZNet = instance; _adminProbePlayerId = localPlayerId; _adminProbeToken = ((localPlayerId > 0) ? ("secondaryattacks_admintest_" + localPlayerId.ToString(CultureInfo.InvariantCulture)) : ""); } if (_adminProbePending && Time.realtimeSinceStartup > _adminProbeDeadline) { _adminProbePending = false; _adminProbeVerified = false; _adminProbeNextTime = Time.realtimeSinceStartup + 5f; } } private static void StartServerAdminProbe(bool force) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.IsServer()) { return; } UpdateServerAdminProbeState(); if (_adminProbePending || string.IsNullOrWhiteSpace(_adminProbeToken)) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!force && realtimeSinceStartup < _adminProbeNextTime) { return; } try { _adminProbePending = true; _adminProbeDeadline = realtimeSinceStartup + 3f; _adminProbeNextTime = realtimeSinceStartup + 5f; instance.Unban(_adminProbeToken); } catch (Exception ex) { _adminProbePending = false; _adminProbeVerified = false; _adminProbeNextTime = Time.realtimeSinceStartup + 5f; SecondaryAttacksPlugin.ModLogger.LogDebug((object)("Admin cooldown bypass probe failed: " + ex.Message)); } } private static void MarkServerAdminProbeSuccess(ZNet? znet) { _adminProbeZNet = znet; _adminProbePlayerId = GetLocalPlayerId(); _adminProbePending = false; _adminProbeVerified = true; _adminProbeNextTime = Time.realtimeSinceStartup + 5f; } private static void ResetServerAdminProbe() { _adminProbeZNet = null; _adminProbePlayerId = 0L; _adminProbeToken = ""; _adminProbePending = false; _adminProbeVerified = null; _adminProbeNextTime = 0f; _adminProbeDeadline = 0f; _adminAccessFrame = -1; _adminAccess = false; } private static long GetLocalPlayerId() { Game instance = Game.instance; long? obj; if (instance == null) { obj = null; } else { PlayerProfile playerProfile = instance.GetPlayerProfile(); obj = ((playerProfile != null) ? new long?(playerProfile.GetPlayerID()) : ((long?)null)); } long? num = obj; long valueOrDefault = num.GetValueOrDefault(); if (valueOrDefault != 0L) { return valueOrDefault; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return 0L; } return localPlayer.GetPlayerID(); } } [HarmonyPatch(typeof(ZNet), "RPC_RemotePrint")] internal static class SecondaryAttackAdminProbeRemotePrintPatch { private static bool Prefix(string text) { return !SecondaryAttackAdminAccessSystem.HandleServerAdminProbeRemotePrint(text); } } 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 (SecondaryAttackManager.TryMarkCompatibilityWarningReported("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 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) { rawConfig.Preset = presetName; rawConfig.Enabled = null; globalRangedPresets[presetName] = FromRangedRaw(rawConfig); } } private static void AddGlobalBloodMagicPreset(Dictionary globalBloodMagicPresets, string presetName, BloodMagicWeaponConfig? rawConfig) { if (rawConfig != null) { rawConfig.Preset = presetName; globalBloodMagicPresets[presetName] = FromBloodMagicRaw(rawConfig); } } 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 = NormalizeSelectedImpactBurst(raw.ImpactBurst, 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 = NormalizeSelectedBoomerang(raw.Boomerang, 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 = NormalizeSelectedSpinningSweep(raw.SpinningSweep, 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) ? NormalizeSelectedSneakAmbush(sneakAmbush, 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 = NormalizeSelectedCleavingThrust(raw.CleavingThrust, 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 = NormalizeSelectedLaunchSlam(raw.LaunchSlam, 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 = NormalizeSelectedKnockbackChain(raw.KnockbackChain, 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 = NormalizeSelectedAftershock(raw.Aftershock, 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 = NormalizeSelectedRiftTrail(raw.RiftTrail, 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 = NormalizeSelectedFractureLine(raw.FractureLine, 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; } if (text.Equals("sneakAmbush", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.SneakAmbush; return true; } if (text.Equals("cleavingThrust", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.CleavingThrust; return true; } if (text.Equals("spearRain", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.SpearRain; return true; } if (text.Equals("impactBurst", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.ImpactBurst; return true; } if (text.Equals("boomerang", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.Boomerang; return true; } if (text.Equals("spinningSweep", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.SpinningSweep; return true; } if (text.Equals("launchSlam", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.LaunchSlam; return true; } if (text.Equals("knockbackChain", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.KnockbackChain; return true; } if (text.Equals("aftershock", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.Aftershock; return true; } if (text.Equals("riftTrail", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.RiftTrail; return true; } if (text.Equals("fractureLine", StringComparison.OrdinalIgnoreCase)) { preset = MeleeSpecialPreset.FractureLine; return true; } 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 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", _ => "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) + ".")); } } private static NormalizedSneakAmbushConfig? NormalizeSelectedSneakAmbush(SneakAmbushConfig? rawSneakAmbush, NormalizedSneakAmbushConfig? fallback) { return NormalizeSneakAmbush(rawSneakAmbush ?? new SneakAmbushConfig(), fallback); } private static NormalizedCleavingThrustConfig? NormalizeSelectedCleavingThrust(CleavingThrustConfig? rawCleavingThrust, NormalizedCleavingThrustConfig? fallback) { return NormalizeCleavingThrust(rawCleavingThrust ?? new CleavingThrustConfig(), fallback); } private static NormalizedLaunchSlamConfig? NormalizeSelectedLaunchSlam(LaunchSlamConfig? rawLaunchSlam, NormalizedLaunchSlamConfig? fallback) { return NormalizeLaunchSlam(rawLaunchSlam ?? new LaunchSlamConfig(), fallback); } private static NormalizedKnockbackChainConfig? NormalizeSelectedKnockbackChain(KnockbackChainConfig? rawKnockbackChain, NormalizedKnockbackChainConfig? fallback) { return NormalizeKnockbackChain(rawKnockbackChain ?? new KnockbackChainConfig(), fallback); } private static NormalizedAftershockConfig? NormalizeSelectedAftershock(AftershockConfig? rawAftershock, NormalizedAftershockConfig? fallback) { return NormalizeAftershock(rawAftershock ?? new AftershockConfig(), fallback); } private static NormalizedRiftTrailConfig? NormalizeSelectedRiftTrail(RiftTrailConfig? rawRiftTrail, NormalizedRiftTrailConfig? fallback) { return NormalizeRiftTrail(rawRiftTrail ?? new RiftTrailConfig(), fallback); } private static NormalizedFractureLineConfig? NormalizeSelectedFractureLine(FractureLineConfig? rawFractureLine, NormalizedFractureLineConfig? fallback) { return NormalizeFractureLine(rawFractureLine ?? new FractureLineConfig(), fallback); } private static NormalizedImpactBurstConfig? NormalizeSelectedImpactBurst(ImpactBurstConfig? rawImpactBurst, NormalizedImpactBurstConfig? fallback) { return NormalizeImpactBurst(rawImpactBurst ?? new ImpactBurstConfig(), fallback); } private static NormalizedBoomerangConfig? NormalizeSelectedBoomerang(BoomerangConfig? rawBoomerang, NormalizedBoomerangConfig? fallback) { return NormalizeBoomerang(rawBoomerang ?? new BoomerangConfig(), fallback); } private static NormalizedSpinningSweepConfig? NormalizeSelectedSpinningSweep(SpinningSweepConfig? rawSpinningSweep, NormalizedSpinningSweepConfig? fallback) { return NormalizeSpinningSweep(rawSpinningSweep ?? new SpinningSweepConfig(), fallback); } private static NormalizedSecondaryModeConfig CreateImpactBurstSecondary(NormalizedImpactBurstConfig impactBurst) { //IL_00af: 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, CooldownFallback = "originalSecondary", 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() }; } private 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() }; } private static NormalizedSecondaryModeConfig CreateSpinningSweepSecondary(NormalizedSpinningSweepConfig spinningSweep) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = 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) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = fallback ?? new NormalizedSecondaryModeConfig(); string preset = rawRanged.Preset?.Trim() ?? 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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 : ""); } 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), CooldownFallback = "originalSecondary", 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), CooldownFallback = "originalSecondary", 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), CooldownFallback = "originalSecondary", 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), 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), Vfx = baseConfig.Vfx, VfxRotationOffset = baseConfig.VfxRotationOffset, Sfx = baseConfig.Sfx }); } 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) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = fallback ?? new NormalizedSecondaryModeConfig(); NormalizedSummonEmpowerSecondaryConfig normalizedSummonEmpowerSecondaryConfig = fallback?.SummonEmpower ?? new NormalizedSummonEmpowerSecondaryConfig(); NormalizedShieldConvertSecondaryConfig normalizedShieldConvertSecondaryConfig = fallback?.ShieldConvert ?? new NormalizedShieldConvertSecondaryConfig(); string type = rawBloodMagic.Preset?.Trim() ?? 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 enum YamlAuthorityMode { LocalFiles, SyncedOnly } private static readonly object ReloadLock = new object(); private static FileSystemWatcher? _watcher; private static readonly Dictionary> SyncedYamlValues = new Dictionary>(); private static SecondaryAttackCompiledSnapshot _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; private static SecondaryAttackCompiledSnapshot? _pendingCompiledSnapshot; private static SecondaryAttackAppliedWorldSnapshot _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; private static DateTime _lastYamlReloadTime; 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 SecondaryAttackCompiledSnapshot CurrentCompiledSnapshot => _currentCompiledSnapshot; internal static SecondaryAttackAppliedWorldSnapshot CurrentAppliedWorldSnapshot => _currentAppliedWorldSnapshot; public static void Initialize() { SecondaryAttackConfigLoader.EnsureLocalFilesExist(); InitializeSyncedYamlValues(); RefreshYamlAuthorityMode(force: true); } public static void Dispose() { DisposeSyncedYamlValues(); _watcher?.Dispose(); _watcher = null; } public static void ApplyToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings); } internal static void TryApplyPendingConfig() { RefreshYamlAuthorityMode(); if (!CommitPendingConfig(force: false, applyToObjectDbImmediately: true)) { CommitPendingWorldReapply(force: false); } } internal static void RequestCurrentWorldReapply() { lock (ReloadLock) { StageWorldReapply(); } } internal static void ApplyPendingConfigToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); bool num = CommitPendingConfig(force: true, applyToObjectDbImmediately: false); ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings); if (num) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); } } internal static void ApplyPendingConfigToZNetScene(ZNetScene scene, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); bool num = CommitPendingConfig(force: true, applyToObjectDbImmediately: false); ApplyCompiledSnapshotToZNetScene(scene, _currentCompiledSnapshot, emitMissingWarnings); if ((Object)(object)ObjectDB.instance != (Object)null) { ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings, applyZNetScene: false); } if (num) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); } } private static void SetupWatcher() { if (_watcher == null) { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); _watcher = new FileSystemWatcher(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath, "SecondaryAttacks.*.yml"); _watcher.Changed += OnYamlFileChanged; _watcher.Created += OnYamlFileChanged; _watcher.Renamed += OnYamlFileChanged; _watcher.IncludeSubdirectories = false; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } } private static void OnYamlFileChanged(object sender, FileSystemEventArgs e) { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return; } DateTime now = DateTime.Now; if (now.Ticks - _lastYamlReloadTime.Ticks < 10000000) { return; } lock (ReloadLock) { ReloadLocalYaml(); _lastYamlReloadTime = now; } } private static void ReloadLocalYaml() { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return; } SecondaryAttackConfigLoader.EnsureLocalFilesExist(); SecondaryAttackYamlTexts secondaryAttackYamlTexts = SecondaryAttackConfigLoader.ReadLocalYamlTexts(); if (SyncedYamlValues.Count == SecondaryAttackYamlDomainRegistry.Domains.Count) { _suppressSyncedYamlChanged = true; try { foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { SyncedYamlValues[domain.Id].AssignLocalValue(secondaryAttackYamlTexts.Get(domain.Id)); } } finally { _suppressSyncedYamlChanged = false; } } ApplyYamlTexts(secondaryAttackYamlTexts); } private static void OnSyncedYamlChanged() { if (!_suppressSyncedYamlChanged) { ApplyYamlTexts(ReadSyncedYamlTexts()); } } private static void RefreshYamlAuthorityMode(bool force = false) { YamlAuthorityMode yamlAuthorityMode = DetermineYamlAuthorityMode(); if (!force && yamlAuthorityMode == _yamlAuthorityMode) { return; } _yamlAuthorityMode = yamlAuthorityMode; switch (yamlAuthorityMode) { case YamlAuthorityMode.LocalFiles: SetupWatcher(); ReloadLocalYaml(); SecondaryAttacksPlugin.ModLogger.LogInfo((object)"SecondaryAttacks YAML authority mode: LocalFiles."); break; case YamlAuthorityMode.SyncedOnly: DisposeWatcher(); if (AnySyncedYamlHasValue()) { ApplyYamlTexts(ReadSyncedYamlTexts()); } else { _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; _hasPendingWorldReapply = false; _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; _currentYamlFingerprint = string.Empty; _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; if ((Object)(object)ZNetScene.instance != (Object)null) { ApplyCompiledSnapshotToZNetScene(ZNetScene.instance, _currentCompiledSnapshot, emitMissingWarnings: true); } SecondaryAttackManager.RefreshLocalPlayerRuntimeWeaponDefinitions(); } 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 InitializeSyncedYamlValues() { DisposeSyncedYamlValues(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { CustomSyncedValue customSyncedValue = new CustomSyncedValue(SecondaryAttacksPlugin.ConfigSync, domain.SyncedIdentifier, ""); customSyncedValue.ValueChanged += OnSyncedYamlChanged; SyncedYamlValues[domain.Id] = customSyncedValue; } } private static void DisposeSyncedYamlValues() { foreach (CustomSyncedValue value in SyncedYamlValues.Values) { value.ValueChanged -= OnSyncedYamlChanged; } SyncedYamlValues.Clear(); } private static SecondaryAttackYamlTexts ReadSyncedYamlTexts() { Dictionary dictionary = new Dictionary(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { dictionary[domain.Id] = (SyncedYamlValues.TryGetValue(domain.Id, out CustomSyncedValue value) ? value.Value : string.Empty); } return new SecondaryAttackYamlTexts(dictionary); } private static bool AnySyncedYamlHasValue() { return SyncedYamlValues.Values.Any((CustomSyncedValue syncedValue) => !string.IsNullOrEmpty(syncedValue.Value)); } private static void DisposeWatcher() { if (_watcher != null) { _watcher.Dispose(); _watcher = null; } } private static void ApplyYamlTexts(SecondaryAttackYamlTexts yamlTexts) { string contentFingerprint = yamlTexts.GetContentFingerprint(); if (!string.Equals(_currentYamlFingerprint, contentFingerprint, StringComparison.Ordinal) && (!_hasPendingConfig || !string.Equals(_pendingYamlFingerprint, contentFingerprint, StringComparison.Ordinal)) && SecondaryAttackConfigLoader.TryCompileSnapshot(_nextSnapshotId++, yamlTexts, out SecondaryAttackCompiledSnapshot snapshot)) { StageConfig(snapshot, contentFingerprint); } } private static void StageConfig(SecondaryAttackCompiledSnapshot snapshot, string fingerprint) { _pendingCompiledSnapshot = snapshot; _pendingYamlFingerprint = fingerprint; _hasPendingConfig = true; CommitPendingConfig(force: true, applyToObjectDbImmediately: true); } private static void StageWorldReapply() { _hasPendingWorldReapply = true; CommitPendingWorldReapply(force: true); } private static bool CommitPendingConfig(bool force, bool applyToObjectDbImmediately) { if (!_hasPendingConfig || _pendingCompiledSnapshot == null) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } _currentCompiledSnapshot = _pendingCompiledSnapshot; _currentYamlFingerprint = _pendingYamlFingerprint ?? _currentYamlFingerprint; _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; if (applyToObjectDbImmediately && (Object)(object)ObjectDB.instance != (Object)null) { ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings: true); } SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); return true; } private static bool CommitPendingWorldReapply(bool force) { if (!_hasPendingWorldReapply) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings: true); SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Applied staged world-apply config changes."); return true; } private static void ApplyCompiledSnapshotToObjectDb(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings, bool applyZNetScene = true) { _hasPendingWorldReapply = false; if (applyZNetScene && (Object)(object)ZNetScene.instance != (Object)null) { ApplyCompiledSnapshotToZNetScene(ZNetScene.instance, compiledSnapshot, emitMissingWarnings); } _currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, compiledSnapshot, emitMissingWarnings); SecondaryAttackManager.RefreshLocalPlayerRuntimeWeaponDefinitions(); } private static void ApplyCompiledSnapshotToZNetScene(ZNetScene scene, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { SecondaryAttackWorldApplyContributors.ApplyToZNetScene(scene, compiledSnapshot, emitMissingWarnings); } private static bool CanApplyPendingConfigNow() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } if (((Humanoid)localPlayer).m_currentAttack != null) { return false; } return !SecondaryAttackManager.HasActiveAsyncSecondaryWorkForFacade((Character?)(object)localPlayer); } } internal static class SecondaryAttackConfigCompiler { public static SecondaryAttackCompiledSnapshot Compile(int snapshotId, SecondaryAttackParsedYaml parsedYaml) { return Compile(snapshotId, parsedYaml.Ranged, parsedYaml.Melee, parsedYaml.BloodMagic, parsedYaml.Effects); } public static SecondaryAttackCompiledSnapshot Compile(int snapshotId, IReadOnlyDictionary parsedRanged, IReadOnlyDictionary parsedMelee, IReadOnlyDictionary parsedBloodMagic, IReadOnlyDictionary parsedEffects) { return new SecondaryAttackCompiledSnapshot(snapshotId, SecondaryAttackNormalizedConfigFacade.FromParsed(parsedRanged, parsedMelee, parsedBloodMagic, parsedEffects)); } } internal readonly struct SecondaryAttackDefinitionBuildContext { public ObjectDB ObjectDb { get; } public IReadOnlyDictionary EffectConfigs { get; } public bool EmitMissingWarnings { get; } public SecondaryAttackDefinitionBuildContext(ObjectDB objectDb, IReadOnlyDictionary effectConfigs, bool emitMissingWarnings) { ObjectDb = objectDb; EffectConfigs = effectConfigs; EmitMissingWarnings = emitMissingWarnings; } } internal static class SecondaryAttackDefinitionCompiler { private readonly struct DefinitionFeatures { public bool HasEffectConfig { 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 bool WantsSecondaryOverride { get; } public float SummonEmpowerMoveSpeedFactor { get; } public float SummonEmpowerAttackSpeedFactor { get; } public DefinitionFeatures(bool hasEffectConfig, bool hasSecondaryConfig, string secondaryType, bool usesSummonEmpower, bool usesShieldConvert, bool usesAftershock, bool usesFractureLine, bool hasCustomPayload, bool hasCopiedSecondary, bool wantsSecondaryOverride, float summonEmpowerMoveSpeedFactor, float summonEmpowerAttackSpeedFactor) { HasEffectConfig = hasEffectConfig; HasSecondaryConfig = hasSecondaryConfig; SecondaryType = secondaryType; UsesSummonEmpower = usesSummonEmpower; UsesShieldConvert = usesShieldConvert; UsesAftershock = usesAftershock; UsesFractureLine = usesFractureLine; HasCustomPayload = hasCustomPayload; HasCopiedSecondary = hasCopiedSecondary; WantsSecondaryOverride = wantsSecondaryOverride; SummonEmpowerMoveSpeedFactor = summonEmpowerMoveSpeedFactor; SummonEmpowerAttackSpeedFactor = summonEmpowerAttackSpeedFactor; } } 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; } List list = ResolveConfiguredWeaponEffects(prefabName, buildContext.EffectConfigs); DefinitionFeatures features = AnalyzeDefinitionFeatures(weaponConfig, list); if (ShouldIgnoreConfiguredEffectsForProjectilePrimary(val.m_attack, features)) { if (list.Count > 0 && SecondaryAttackManager.TryMarkCompatibilityIssueReported("ignored_projectile_primary_effects:" + prefabName)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Ignoring configured effects on " + prefabName + ": projectile-primary secondary weapons do not support effect-only assignments together with secondary overrides.")); } list = new List(); } DefinitionFeatures features2 = AnalyzeDefinitionFeatures(weaponConfig, list); LogStaffDefinitionCreation(prefabName, val, features2); DefinitionValidationResult definitionValidationResult = ValidateDefinitionRequest(prefabName, val, weaponConfig, features2); switch (definitionValidationResult.Disposition) { case DefinitionValidationDisposition.EffectOnly: definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig, list); return true; case DefinitionValidationDisposition.Skip: return false; default: return TryCreateValidatedDefinition(buildContext, prefabName, val, definitionValidationResult.PrimaryAttack, weaponConfig, list, features2, out definition); } } internal static bool HasConfiguredWeaponEffects(string prefabName, IReadOnlyDictionary effectConfigs) { return false; } private static bool ShouldIgnoreConfiguredEffectsForProjectilePrimary(Attack? primaryAttack, DefinitionFeatures features) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (features.WantsSecondaryOverride && primaryAttack != null) { if ((int)primaryAttack.m_attackType != 2) { return (Object)(object)primaryAttack.m_attackProjectile != (Object)null; } return true; } return false; } 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 List ResolveConfiguredWeaponEffects(string prefabName, IReadOnlyDictionary effectConfigs) { return new List(); } private static DefinitionFeatures AnalyzeDefinitionFeatures(NormalizedWeaponConfig weaponConfig, List configuredEffects) { int hasEffectConfig; if (configuredEffects.Count <= 0) { 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) { NormalizedAftershockConfig? aftershock = weaponConfig.Aftershock; if (aftershock == null || !aftershock.Enabled) { NormalizedRiftTrailConfig? riftTrail = weaponConfig.RiftTrail; if (riftTrail == null || !riftTrail.Enabled) { NormalizedFractureLineConfig? fractureLine = weaponConfig.FractureLine; if (fractureLine == null || !fractureLine.Enabled) { NormalizedImpactBurstConfig? impactBurst = weaponConfig.ImpactBurst; if (impactBurst == null || !impactBurst.Enabled) { NormalizedBoomerangConfig? boomerang = weaponConfig.Boomerang; if (boomerang == null || !boomerang.Enabled) { NormalizedSpinningSweepConfig? spinningSweep = weaponConfig.SpinningSweep; if (spinningSweep == null || !spinningSweep.Enabled) { hasEffectConfig = ((weaponConfig.HarvestSweep?.Enabled ?? false) ? 1 : 0); goto IL_00f5; } } } } } } } } } } } hasEffectConfig = 1; goto IL_00f5; IL_00f5: bool flag = 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"; bool wantsSecondaryOverride = flag; float configuredSummonEmpowerMoveSpeedFactor = SecondaryAttackManager.GetConfiguredSummonEmpowerMoveSpeedFactor(weaponConfig); float configuredSummonEmpowerAttackSpeedFactor = SecondaryAttackManager.GetConfiguredSummonEmpowerAttackSpeedFactor(weaponConfig); return new DefinitionFeatures((byte)hasEffectConfig != 0, flag, text, usesSummonEmpower, usesShieldConvert, usesAftershock, usesFractureLine, hasCustomPayload, hasCopiedSecondary, wantsSecondaryOverride, configuredSummonEmpowerMoveSpeedFactor, configuredSummonEmpowerAttackSpeedFactor); } private static void LogStaffDefinitionCreation(string prefabName, SharedData sharedData, DefinitionFeatures features) { if (!string.Equals(prefabName, "StaffSkeleton", StringComparison.OrdinalIgnoreCase) && !string.Equals(prefabName, "StaffRedTroll", StringComparison.OrdinalIgnoreCase)) { string.Equals(prefabName, "StaffShield", StringComparison.OrdinalIgnoreCase); } } private static bool TryCreateValidatedDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, DefinitionFeatures features, out SecondaryAttackDefinition? definition) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 definition = null; if (features.UsesSummonEmpower) { return SecondaryAttackManager.TryCreateSummonEmpowerDefinition(prefabName, sharedData, primaryAttack, weaponConfig, configuredEffects, out definition); } if (features.UsesShieldConvert) { return SecondaryAttackManager.TryCreateShieldConvertDefinition(prefabName, sharedData, primaryAttack, weaponConfig, configuredEffects, 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.HasEffectConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig, configuredEffects); return true; } return false; } return SecondaryAttackManager.TryCreateCustomPayloadDefinition(prefabName, sharedData, primaryAttack, weaponConfig, configuredEffects, out definition); } if (features.UsesAftershock) { return SecondaryAttackManager.TryCreateAftershockDefinition(buildContext, prefabName, sharedData, primaryAttack, weaponConfig, configuredEffects, out definition); } if (features.UsesFractureLine) { return SecondaryAttackManager.TryCreateFractureLineDefinition(buildContext, prefabName, primaryAttack, weaponConfig, configuredEffects, 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.HasEffectConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig, configuredEffects); return true; } return false; } return SecondaryAttackManager.TryCreateSecondaryOverrideDefinition(prefabName, sourcePrefabName, primaryAttack, sourceAttack, weaponConfig, configuredEffects, out definition); } if (features.HasEffectConfig) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig, configuredEffects); 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.WantsSecondaryOverride) { if (features.HasEffectConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } if (!features.HasSecondaryConfig) { if (features.HasEffectConfig) { 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.HasEffectConfig) { 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.HasEffectConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } if (features.UsesSummonEmpower && features.UsesShieldConvert) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": summon empower and shield convert cannot be used together on the same weapon.")); if (!features.HasEffectConfig) { 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.HasEffectConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } return new DefinitionValidationResult(DefinitionValidationDisposition.Continue, attack); } } internal sealed class NormalizedSecondaryAttackConfigFile { public Dictionary Effects { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary MagicSummons { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); 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 SecondaryAttackNormalizedConfigFacade { internal static NormalizedSecondaryAttackConfigFile FromParsed(IReadOnlyDictionary ranged, IReadOnlyDictionary melee, IReadOnlyDictionary bloodMagic, IReadOnlyDictionary effects) { SecondaryAttackWeaponNormalizationResult secondaryAttackWeaponNormalizationResult = SecondaryAttackWeaponConfigNormalizer.Normalize(ranged, melee, bloodMagic); return new NormalizedSecondaryAttackConfigFile { Weapons = secondaryAttackWeaponNormalizationResult.Weapons, GlobalRangedPresets = secondaryAttackWeaponNormalizationResult.GlobalRangedPresets, GlobalBloodMagicPresets = secondaryAttackWeaponNormalizationResult.GlobalBloodMagicPresets, GlobalMeleeFallback = secondaryAttackWeaponNormalizationResult.GlobalMeleeFallback, Effects = NormalizeEffects(effects), MagicSummons = SecondaryAttackMagicSummonNormalizer.Normalize(bloodMagic) }; } private static Dictionary NormalizeEffects(IReadOnlyDictionary effects) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var (text2, effectBehaviorConfig2) in effects) { if (!string.IsNullOrWhiteSpace(text2) && effectBehaviorConfig2 != null) { dictionary[text2.Trim()] = effectBehaviorConfig2; } } return dictionary; } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 string Vfx { get; set; } = "vfx_archerytarget_bullseye"; public Vector3 VfxRotationOffset { get; set; } = new Vector3(90f, 0f, 0f); public string Sfx { get; set; } = "sfx_sledge_hit"; public static NormalizedLaunchSlamConfig CreateDefault() { return new NormalizedLaunchSlamConfig(); } public NormalizedLaunchSlamConfig Clone() { return SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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 string CooldownFallback { get; set; } = "originalSecondary"; 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 SecondaryAttackConfigClone.Shallow(this); } } 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 string CooldownFallback { get; set; } = "originalSecondary"; 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 SecondaryAttackConfigClone.Shallow(this); } } 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 string CooldownFallback { get; set; } = "originalSecondary"; 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 SecondaryAttackConfigClone.Shallow(this); } } 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 SecondaryAttackConfigClone.Shallow(this); } } 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; } } internal sealed class NormalizedProjectileBehaviorConfig { public NormalizedProjectileSecondaryConfig? Ranged { get; set; } public NormalizedMeleeOnProjectileHitConfig? OnProjectileHit { get; set; } public NormalizedBoomerangConfig? Boomerang { get; set; } } 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 string? CooldownFallback { 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 string? CooldownFallback { 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 string? CooldownFallback { 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? 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? TrailScaleFactor { 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 sealed class EffectBehaviorConfig { public string Type { get; set; } = ""; public int? Value { get; set; } public string Prefab { get; set; } = ""; public string Trigger { get; set; } = "anyHit"; public int? StacksRequired { get; set; } public float StackWindow { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? LightningDamage { get; set; } public float? Radius { get; set; } public float? Ttl { get; set; } public float? HitInterval { get; set; } public float ProcChance { get; set; } = 100f; public string DamageType { get; set; } = ""; public string Modifier { get; set; } = "normal"; public ScalarValueConfig Damage { get; set; } = new ScalarValueConfig(); public ScalarValueConfig Heal { get; set; } = new ScalarValueConfig(); public ScalarValueConfig StaminaRestore { get; set; } = new ScalarValueConfig(); public float MoveSpeedMultiplier { get; set; } = 1f; public float HealthThresholdPercent { get; set; } = 25f; public float DamageMultiplier { get; set; } = 1f; public bool ConsumeOnModify { get; set; } public Dictionary? Prefabs { get; set; } } internal sealed class EffectBehaviorOverrideConfig { public string? Type { get; set; } public int? Value { get; set; } public string? Prefab { get; set; } public string? Trigger { get; set; } public int? StacksRequired { get; set; } public float? StackWindow { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? LightningDamage { get; set; } public float? Radius { get; set; } public float? Ttl { get; set; } public float? HitInterval { get; set; } public float? ProcChance { get; set; } public string? DamageType { get; set; } public string? Modifier { get; set; } public ScalarValueOverrideConfig? Damage { get; set; } public ScalarValueOverrideConfig? Heal { get; set; } public ScalarValueOverrideConfig? StaminaRestore { get; set; } public float? MoveSpeedMultiplier { get; set; } public float? HealthThresholdPercent { get; set; } public float? DamageMultiplier { get; set; } public bool? ConsumeOnModify { get; set; } } internal sealed class ScalarValueConfig { public string Mode { get; set; } = "fixed"; public float Value { get; set; } } internal sealed class ScalarValueOverrideConfig { public string? Mode { get; set; } public float? Value { 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 WeaponEffectType { Dot, ResistanceShred, Adrenaline, Haste, Vampirism, Execute, StaggerChance, BurstDamage, Bleeding, Bash, Piercing, Executioner, Decapitator, Smasher, Juggernaut } internal enum WeaponEffectTrigger { AnyHit, MainHit, SecondaryHit } internal enum ScalarValueMode { Fixed, TargetMaxHealthPercent, SelfMaxHealthPercent, SelfMaxStaminaPercent } 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 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 { private List _configuredEffects = new List(); private WeaponEffectRuntimeCache? _effectRuntimeCache; 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 string GuardAttackAnimation { 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 List ConfiguredEffects { get { return _configuredEffects; } set { _configuredEffects = value ?? new List(); _effectRuntimeCache = null; } } internal WeaponEffectRuntimeCache EffectRuntimeCache => _effectRuntimeCache ?? (_effectRuntimeCache = WeaponEffectRuntimeCache.Build(_configuredEffects)); 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 string CooldownFallback { get; set; } = "originalSecondary"; 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 CooldownFallback { get; set; } = "originalSecondary"; 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 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; public float LandingAreaRadiusFactor { get; set; } = 1.5f; public float LandingAreaRadiusMax { get; set; } = 4f; public string Vfx { get; set; } = "vfx_archerytarget_bullseye"; public Vector3 VfxRotationOffset { get; set; } = new Vector3(90f, 0f, 0f); public string Sfx { get; set; } = "sfx_sledge_hit"; } 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 CollisionRadius { get; set; } = 2f; public float ChainDecay { get; set; } = 0.75f; public int MaxChainTargets { get; set; } = 5; public float Duration { get; set; } = 1.5f; public float MinSpeed { get; set; } = 0.5f; public float HitCooldown { get; set; } = 0.2f; public string InitialHitVfx { get; set; } = "vfx_archerytarget_bullseye"; public string FirstCollisionVfx { get; set; } = "vfx_HitSparks"; public string SecondCollisionVfx { get; set; } = "vfx_HitSparks"; public string LaterCollisionVfx { get; set; } = "vfx_HitSparks"; public string CollisionSfx { get; set; } = "sfx_club_hit"; public float CollisionVfxMinDistanceFromPlayer { get; set; } } 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 CooldownSkill { get; set; } = "weapon"; public float CooldownReductionFactor { get; set; } = 0.5f; } internal static class ProjectilePresetCooldownFallback { internal const string OriginalSecondary = "originalSecondary"; internal const string CopiedSecondary = "copiedSecondary"; internal static string Normalize(string? raw, string fallback = "originalSecondary") { string text = raw?.Trim() ?? ""; if (text.Length == 0) { if (!IsSupported(fallback)) { return "originalSecondary"; } return fallback; } if (text.Equals("originalSecondary", StringComparison.OrdinalIgnoreCase)) { return "originalSecondary"; } if (text.Equals("copiedSecondary", StringComparison.OrdinalIgnoreCase)) { return "copiedSecondary"; } if (!IsSupported(fallback)) { return "originalSecondary"; } return fallback; } internal static bool IsCopiedSecondary(string? value) { return Normalize(value).Equals("copiedSecondary", StringComparison.Ordinal); } internal static bool IsOriginalSecondary(string? value) { return Normalize(value).Equals("originalSecondary", StringComparison.Ordinal); } 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; } private static bool IsSupported(string? value) { if (value != null) { if (!value.Equals("originalSecondary", StringComparison.Ordinal)) { return value.Equals("copiedSecondary", StringComparison.Ordinal); } return true; } return false; } } 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 sealed class ConfiguredWeaponEffectDefinition { public string Id { get; set; } = ""; public string StatusEffectName { get; set; } = ""; public WeaponEffectType Type { get; set; } public WeaponEffectTrigger Trigger { get; set; } public int StacksRequired { get; set; } = 1; public float StackWindow { get; set; } public float Duration { get; set; } public float TickInterval { get; set; } = 1f; public float ProcChance { get; set; } = 100f; public DamageType DamageType { get; set; } = (DamageType)1024; public DamageModifier Modifier { get; set; } public ScalarValueMode DamageMode { get; set; } public float DamageValue { get; set; } public float ValuePercent { get; set; } public ScalarValueMode HealMode { get; set; } public float HealValue { get; set; } public ScalarValueMode StaminaRestoreMode { get; set; } public float StaminaRestoreValue { get; set; } public float MoveSpeedMultiplier { get; set; } = 1f; public float HealthThresholdPercent { get; set; } = 25f; public float DamageMultiplier { get; set; } = 1f; public bool ConsumeOnModify { get; set; } internal bool UsesActualDamagePostfix { get { WeaponEffectType type = Type; if ((uint)(type - 2) <= 2u || (uint)(type - 8) <= 6u) { return true; } return false; } } } internal sealed class WeaponEffectRuntimeCache { internal static readonly WeaponEffectRuntimeCache Empty = new WeaponEffectRuntimeCache(Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); internal ConfiguredWeaponEffectDefinition[] PrimaryImmediateEffects { get; } internal ConfiguredWeaponEffectDefinition[] PrimaryPostDamageEffects { get; } internal ConfiguredWeaponEffectDefinition[] SecondaryImmediateEffects { get; } internal ConfiguredWeaponEffectDefinition[] SecondaryPostDamageEffects { get; } internal bool HasEffects { get { if (PrimaryImmediateEffects.Length == 0 && PrimaryPostDamageEffects.Length == 0 && SecondaryImmediateEffects.Length == 0) { return SecondaryPostDamageEffects.Length != 0; } return true; } } private WeaponEffectRuntimeCache(ConfiguredWeaponEffectDefinition[] primaryImmediateEffects, ConfiguredWeaponEffectDefinition[] primaryPostDamageEffects, ConfiguredWeaponEffectDefinition[] secondaryImmediateEffects, ConfiguredWeaponEffectDefinition[] secondaryPostDamageEffects) { PrimaryImmediateEffects = primaryImmediateEffects; PrimaryPostDamageEffects = primaryPostDamageEffects; SecondaryImmediateEffects = secondaryImmediateEffects; SecondaryPostDamageEffects = secondaryPostDamageEffects; } internal ConfiguredWeaponEffectDefinition[] GetImmediateEffects(bool secondaryAttack) { if (!secondaryAttack) { return PrimaryImmediateEffects; } return SecondaryImmediateEffects; } internal ConfiguredWeaponEffectDefinition[] GetPostDamageEffects(bool secondaryAttack) { if (!secondaryAttack) { return PrimaryPostDamageEffects; } return SecondaryPostDamageEffects; } internal static WeaponEffectRuntimeCache Build(IReadOnlyList effects) { if (effects.Count == 0) { return Empty; } List list = new List(); List list2 = new List(); List list3 = new List(); List list4 = new List(); AddTriggerEffects(effects, secondaryAttack: false, list, list2); AddTriggerEffects(effects, secondaryAttack: true, list3, list4); if (list.Count == 0 && list2.Count == 0 && list3.Count == 0 && list4.Count == 0) { return Empty; } return new WeaponEffectRuntimeCache((list.Count > 0) ? list.ToArray() : Array.Empty(), (list2.Count > 0) ? list2.ToArray() : Array.Empty(), (list3.Count > 0) ? list3.ToArray() : Array.Empty(), (list4.Count > 0) ? list4.ToArray() : Array.Empty()); } private static void AddTriggerEffects(IReadOnlyList effects, bool secondaryAttack, List immediateEffects, List postDamageEffects) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ConfiguredWeaponEffectDefinition effect in effects) { if (hashSet.Add(effect.Id) && MatchesTrigger(effect.Trigger, secondaryAttack)) { if (effect.UsesActualDamagePostfix) { postDamageEffects.Add(effect); } else { immediateEffects.Add(effect); } } } } private static bool MatchesTrigger(WeaponEffectTrigger trigger, bool secondaryAttack) { return trigger switch { WeaponEffectTrigger.AnyHit => true, WeaponEffectTrigger.MainHit => !secondaryAttack, WeaponEffectTrigger.SecondaryHit => secondaryAttack, _ => false, }; } } 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 { 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; } } 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 AsyncSecondaryActivityState { public int ActiveCount { get; set; } } private static readonly Dictionary ReloadConsumptionWeapons = new Dictionary(); private static readonly MethodInfo MemberwiseCloneMethod = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null); 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 CharacterRpcComponentName = "SecondaryAttacks_CharacterRpc"; private const string ApplySummonEmpowerRpcName = "SecondaryAttacks_ApplySummonEmpower"; private const string ConvertShieldToHealRpcName = "SecondaryAttacks_ConvertShieldToHeal"; private const string StaffRapidFireAnimation = "staff_rapidfire"; private static readonly ConditionalWeakTable BowSecondaryStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable AsyncSecondaryActivityStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable RuntimeWeaponDefinitionStates = new ConditionalWeakTable(); private static readonly HashSet ReportedRuntimeDumps = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly SortedSet PlayerAnimatorTriggers = new SortedSet(StringComparer.Ordinal); private static bool _animatorDumpWritten; private static bool _customAnimationDumpWritten; internal static string ApplySummonEmpowerRpcNameForStaffRuntime => "SecondaryAttacks_ApplySummonEmpower"; internal static string ConvertShieldToHealRpcNameForStaffRuntime => "SecondaryAttacks_ConvertShieldToHeal"; internal static string SummonEmpowerExpiryZdoKeyForStaffRuntime => "SecondaryAttacks_SummonEmpowerExpiry"; internal static string SummonEmpowerMoveSpeedBonusZdoKeyForStaffRuntime => "SecondaryAttacks_SummonEmpowerMoveSpeedBonus"; internal static string SummonEmpowerAttackCooldownReductionZdoKeyForStaffRuntime => "SecondaryAttacks_SummonEmpowerAttackCooldownReduction"; internal static string ShieldRemainingDisplayZdoKeyForStaffRuntime => "SecondaryAttacks_ShieldRemainingDisplay"; internal static string ShieldDisplayExpiryZdoKeyForStaffRuntime => "SecondaryAttacks_ShieldDisplayExpiry"; internal static float GetConfiguredSummonEmpowerMoveSpeedFactor(NormalizedWeaponConfig weaponConfig) { return weaponConfig.Secondary?.SummonEmpower.MoveSpeedFactor ?? 1f; } internal static float GetConfiguredSummonEmpowerAttackSpeedFactor(NormalizedWeaponConfig weaponConfig) { return weaponConfig.Secondary?.SummonEmpower.AttackSpeedFactor ?? 1f; } 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) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) 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), Vfx = launchSlam.Vfx.Trim(), VfxRotationOffset = launchSlam.VfxRotationOffset, Sfx = launchSlam.Sfx.Trim() }; } 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_005f: 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), CooldownFallback = "originalSecondary", 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), 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 int ResolveAmmoConsumption(int configuredValue, bool usesAmmo, SecondaryAttackPreset preset, int projectileCount) { 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 (preset == SecondaryAttackPreset.Burst && configuredValue < 0) { return Mathf.Max(1, projectileCount); } if (configuredValue >= 0) { return Mathf.Max(0, configuredValue); } return 1; } } internal static SecondaryAttackDefinition CreateEffectOnlyDefinition(string prefabName, NormalizedWeaponConfig weaponConfig, List configuredEffects) { return 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), RiftTrail = CreateRiftTrailDefinition(weaponConfig), FractureLine = CreateFractureLineDefinition(weaponConfig), HarvestSweep = CreateHarvestSweepDefinition(weaponConfig), Boomerang = CreateBoomerangDefinition(weaponConfig), SpinningSweep = CreateSpinningSweepDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; } public static bool BeginProjectileHitContext(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_0004: Unknown result type (might be due to invalid IL or missing references) return SecondaryAttackRuntimeFacade.BeginProjectileHitContext(projectile, collider, hitPoint, water, normal); } public static void EndProjectileHitContext(bool active) { SecondaryAttackRuntimeFacade.EndProjectileHitContext(active); } public static bool TryGetProjectileHitAttackContext(out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition? definition, out bool disableCurrentAttackFallback) { return SecondaryAttackRuntimeFacade.TryGetProjectileHitAttackContext(out weaponPrefabName, out secondaryAttack, out definition, out disableCurrentAttackFallback); } private static int GetCurrentSnapshotId() { return SecondaryAttackFacade.CurrentCompiledSnapshot.SnapshotId; } internal static void ResetWorldApplyTransientState() { } internal static bool TryMarkCompatibilityWarningReported(string warningKey) { return SecondaryAttackWarningLog.TryMarkWarning(warningKey); } internal static bool TryMarkCompatibilityIssueReported(string issueKey) { return SecondaryAttackWarningLog.TryMarkIssue(issueKey); } 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 bool BeginReloadStateConsumption(Attack attack) { if ((Object)(object)attack?.m_character == (Object)null || attack.m_weapon == null || !attack.m_requiresReload) { return false; } ReloadConsumptionWeapons[attack.m_character] = attack.m_weapon; return true; } internal static void EndReloadStateConsumption(Attack attack, bool active) { if (active && !((Object)(object)attack?.m_character == (Object)null)) { ReloadConsumptionWeapons.Remove(attack.m_character); } } 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 TryCreateDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, ItemDrop itemDrop, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { return SecondaryAttackDefinitionCompiler.TryCreateDefinition(buildContext, prefabName, itemDrop, weaponConfig, out definition); } 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 float GetNetworkTimeSeconds() { if (!((Object)(object)ZNet.instance != (Object)null)) { return Time.time; } return (float)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) { PlayTriggeredAttackEffects(attack, 1f); } 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, ResolveActiveAttackDurabilityFactor(activeAttack)); 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 float ResolveActiveAttackDurabilityFactor(ActiveSecondaryAttack activeAttack) { return activeAttack.Definition.DurabilityFactor; } internal static Character? GetSeManCharacter(SEMan seMan) { return ShieldAccess.GetSeManCharacter(seMan); } internal static float ClosestSegmentProgress(Vector3 start, Vector3 end, 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_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_0025: Unknown result type (might be due to invalid IL or missing references) Vector3 val = end - start; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude <= Mathf.Epsilon) { return 0f; } return Mathf.Clamp01(Vector3.Dot(point - start, val) / sqrMagnitude); } 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 void RegisterAsyncSecondaryWork(Character? owner) { if (!((Object)(object)owner == (Object)null)) { AsyncSecondaryActivityStates.GetValue(owner, (Character _) => new AsyncSecondaryActivityState()).ActiveCount++; } } internal static void UnregisterAsyncSecondaryWork(Character? owner) { if (!((Object)(object)owner == (Object)null) && AsyncSecondaryActivityStates.TryGetValue(owner, out AsyncSecondaryActivityState value)) { value.ActiveCount = Mathf.Max(0, value.ActiveCount - 1); } } private static bool HasActiveAsyncSecondaryWork(Character? owner) { if ((Object)(object)owner != (Object)null && AsyncSecondaryActivityStates.TryGetValue(owner, out AsyncSecondaryActivityState value)) { return value.ActiveCount > 0; } return false; } internal static bool HasActiveAsyncSecondaryWorkForFacade(Character? owner) { return HasActiveAsyncSecondaryWork(owner); } internal static bool TryCreateCustomPayloadDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, 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, projectileCount); 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; secondaryAttackDefinition.SneakAmbush = CreateSneakAmbushDefinition(weaponConfig); secondaryAttackDefinition.CleavingThrust = CreateCleavingThrustDefinition(weaponConfig); secondaryAttackDefinition.LaunchSlam = CreateLaunchSlamDefinition(weaponConfig); secondaryAttackDefinition.KnockbackChain = CreateKnockbackChainDefinition(weaponConfig); secondaryAttackDefinition.Aftershock = CreateAftershockDefinition(weaponConfig); secondaryAttackDefinition.RiftTrail = CreateRiftTrailDefinition(weaponConfig); secondaryAttackDefinition.FractureLine = CreateFractureLineDefinition(weaponConfig); secondaryAttackDefinition.HarvestSweep = CreateHarvestSweepDefinition(weaponConfig); secondaryAttackDefinition.OnProjectileHit = CreateMeleeOnProjectileHitDefinition(prefabName, weaponConfig.Secondary?.OnProjectileHit); secondaryAttackDefinition.Boomerang = CreateBoomerangDefinition(weaponConfig); secondaryAttackDefinition.SpinningSweep = CreateSpinningSweepDefinition(weaponConfig); secondaryAttackDefinition.ConfiguredEffects = configuredEffects; definition = secondaryAttackDefinition; 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, List configuredEffects, 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), RiftTrail = CreateRiftTrailDefinition(weaponConfig), FractureLine = CreateFractureLineDefinition(weaponConfig), HarvestSweep = CreateHarvestSweepDefinition(weaponConfig), OnProjectileHit = CreateMeleeOnProjectileHitDefinition(prefabName, weaponConfig.Secondary?.OnProjectileHit), Boomerang = CreateBoomerangDefinition(weaponConfig), SpinningSweep = CreateSpinningSweepDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateAftershockDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, 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) && configuredEffects.Count <= 0) { return false; } } } } } } } } definition = CreateEffectOnlyDefinition(prefabName, weaponConfig, configuredEffects); 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), RiftTrail = CreateRiftTrailDefinition(weaponConfig), FractureLine = CreateFractureLineDefinition(weaponConfig), HarvestSweep = CreateHarvestSweepDefinition(weaponConfig), OnProjectileHit = CreateMeleeOnProjectileHitDefinition(prefabName, weaponConfig.Secondary?.OnProjectileHit), Boomerang = CreateBoomerangDefinition(weaponConfig), SpinningSweep = CreateSpinningSweepDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateFractureLineDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, 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) && configuredEffects.Count <= 0) { return false; } } } } } } } definition = CreateEffectOnlyDefinition(prefabName, weaponConfig, configuredEffects); 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), RiftTrail = CreateRiftTrailDefinition(weaponConfig), FractureLine = CreateFractureLineDefinition(weaponConfig), HarvestSweep = CreateHarvestSweepDefinition(weaponConfig), OnProjectileHit = CreateMeleeOnProjectileHitDefinition(prefabName, weaponConfig.Secondary?.OnProjectileHit), Boomerang = CreateBoomerangDefinition(weaponConfig), SpinningSweep = CreateSpinningSweepDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; ApplyAttackResourceScaling(definition, primaryAttack, GetSelectedMeleeResourceMultiplier(weaponConfig)); return true; } private static MeleeOnProjectileHitDefinition? CreateMeleeOnProjectileHitDefinition(string prefabName, NormalizedMeleeOnProjectileHitConfig? config) { //IL_00ef: 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), CooldownFallback = config.CooldownFallback, 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 EnsureRuntimeWeaponDefinitionApplied(ItemData? weapon) { SecondaryAttackRuntimeWeaponRebind.Apply(weapon, SecondaryAttackFacade.CurrentAppliedWorldSnapshot); } internal static void RefreshLocalPlayerRuntimeWeaponDefinitions() { SecondaryAttackRuntimeWeaponRebind.RefreshLocalPlayerInventory(SecondaryAttackFacade.CurrentAppliedWorldSnapshot); } 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; } private static bool ShouldDumpRuntimeProfile(string prefabName) { if (!string.Equals(prefabName, "StaffDeathcallerPoisonDO", StringComparison.OrdinalIgnoreCase)) { return string.Equals(prefabName, "StaffStormcallerShockerDO", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool ShouldDumpRuntimeProfileForProjectileRuntime(string prefabName) { return ShouldDumpRuntimeProfile(prefabName); } internal static bool TryMarkRuntimeDumpReported(string dumpKey) { return ReportedRuntimeDumps.Add(dumpKey); } internal static void DumpRuntimeWeaponProfile(string weaponPrefabName, SharedData sharedData) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_06aa: Unknown result type (might be due to invalid IL or missing references) //IL_0862: Unknown result type (might be due to invalid IL or missing references) if (!ShouldDumpRuntimeProfile(weaponPrefabName)) { return; } string item = "weapon|" + weaponPrefabName; if (!ReportedRuntimeDumps.Add(item)) { return; } Attack attack = sharedData.m_attack; if (attack == null) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)("[RuntimeDump] " + weaponPrefabName + " primary attack is .")); return; } GameObject attackProjectile = attack.m_attackProjectile; Projectile val = (((Object)(object)attackProjectile != (Object)null) ? attackProjectile.GetComponent() : null); Aoe val2 = (((Object)(object)attackProjectile != (Object)null) ? attackProjectile.GetComponent() : null); string text = (((Object)(object)val != (Object)null) ? "Projectile" : (((Object)(object)val2 != (Object)null) ? "Aoe" : (((Object)(object)attackProjectile != (Object)null && attackProjectile.GetComponent() != null) ? "IProjectileOnly" : "None"))); SecondaryAttacksPlugin.ModLogger.LogInfo((object)("[RuntimeDump] " + weaponPrefabName + " primary" + $" type={attack.m_attackType}" + " projectile=" + (((attackProjectile != null) ? ((Object)attackProjectile).name : null) ?? "") + " payloadType=" + text + $" projectiles={attack.m_projectiles}" + $" bursts={attack.m_projectileBursts}" + $" burstInterval={attack.m_burstInterval}" + $" vel={attack.m_projectileVel}" + $" velMin={attack.m_projectileVelMin}" + $" randomVelocity={attack.m_randomVelocity}" + $" accuracy={attack.m_projectileAccuracy}" + $" accuracyMin={attack.m_projectileAccuracyMin}" + $" launchAngle={attack.m_launchAngle}" + $" destroyPrevious={attack.m_destroyPreviousProjectile}" + $" requiresReload={attack.m_requiresReload}" + $" attackHitNoise={attack.m_attackHitNoise}")); SecondaryAttacksPlugin.ModLogger.LogInfo((object)("[RuntimeDump] " + weaponPrefabName + " payloadComponents=" + DescribeComponents(attackProjectile))); if ((Object)(object)val != (Object)null) { ManualLogSource modLogger = SecondaryAttacksPlugin.ModLogger; string[] obj = new string[20] { "[RuntimeDump] ", weaponPrefabName, " payload", $" gravity={val.m_gravity}", $" drag={val.m_drag}", $" ttl={val.m_ttl}", $" rayRadius={val.m_rayRadius}", $" aoe={val.m_aoe}", $" attackForce={val.m_attackForce}", $" hitNoise={val.m_hitNoise}", $" canHitWater={val.m_canHitWater}", $" blockable={val.m_blockable}", $" dodgeable={val.m_dodgeable}", $" stayStatic={val.m_stayAfterHitStatic}", $" stayDynamic={val.m_stayAfterHitDynamic}", $" stayTTL={val.m_stayTTL}", $" attachToRigidBody={val.m_attachToRigidBody}", " spawnOnHit=", null, null }; GameObject spawnOnHit = val.m_spawnOnHit; obj[18] = ((spawnOnHit != null) ? ((Object)spawnOnHit).name : null) ?? ""; obj[19] = $" spawnOnHitChance={val.m_spawnOnHitChance}"; modLogger.LogInfo((object)string.Concat(obj)); ManualLogSource modLogger2 = SecondaryAttacksPlugin.ModLogger; string[] obj2 = new string[48] { "[RuntimeDump] ", weaponPrefabName, " payloadFlags", $" type={val.m_type}", $" adrenaline={val.m_adrenaline}", $" backstabBonus={val.m_backstabBonus}", " statusEffect=", val.m_statusEffect, $" doOwnerRaytest={val.m_doOwnerRaytest}", $" attachToClosestBone={val.m_attachToClosestBone}", $" attachPenetration={val.m_attachPenetration}", " hideOnHit=", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; GameObject hideOnHit = val.m_hideOnHit; obj2[12] = ((hideOnHit != null) ? ((Object)hideOnHit).name : null) ?? ""; obj2[13] = $" stopEmittersOnHit={val.m_stopEmittersOnHit}"; obj2[14] = $" bounce={val.m_bounce}"; obj2[15] = $" bounceOnWater={val.m_bounceOnWater}"; obj2[16] = $" bouncePower={val.m_bouncePower}"; obj2[17] = $" bounceRoughness={val.m_bounceRoughness}"; obj2[18] = $" maxBounces={val.m_maxBounces}"; obj2[19] = $" minBounceVel={val.m_minBounceVel}"; obj2[20] = $" respawnItemOnHit={val.m_respawnItemOnHit}"; obj2[21] = $" spawnOnTtl={val.m_spawnOnTtl}"; obj2[22] = $" spawnCount={val.m_spawnCount}"; obj2[23] = $" randomSpawnOnHitCount={val.m_randomSpawnOnHitCount}"; obj2[24] = $" randomSpawnSkipLava={val.m_randomSpawnSkipLava}"; obj2[25] = $" showBreakMessage={val.m_showBreakMessage}"; obj2[26] = $" staticHitOnly={val.m_staticHitOnly}"; obj2[27] = $" groundHitOnly={val.m_groundHitOnly}"; obj2[28] = $" spawnOffset={val.m_spawnOffset}"; obj2[29] = $" copyProjectileRotation={val.m_copyProjectileRotation}"; obj2[30] = $" spawnRandomRotation={val.m_spawnRandomRotation}"; obj2[31] = $" spawnFacingRotation={val.m_spawnFacingRotation}"; obj2[32] = $" spawnProjectileNewVelocity={val.m_spawnProjectileNewVelocity}"; obj2[33] = $" spawnProjectileMinVel={val.m_spawnProjectileMinVel}"; obj2[34] = $" spawnProjectileMaxVel={val.m_spawnProjectileMaxVel}"; obj2[35] = $" spawnProjectileRandomDir={val.m_spawnProjectileRandomDir}"; obj2[36] = $" spawnProjectileHemisphereDir={val.m_spawnProjectileHemisphereDir}"; obj2[37] = $" projectilesInheritHitData={val.m_projectilesInheritHitData}"; obj2[38] = $" onlySpawnedProjectilesDealDamage={val.m_onlySpawnedProjectilesDealDamage}"; obj2[39] = $" divideDamageBetweenProjectiles={val.m_divideDamageBetweenProjectiles}"; obj2[40] = $" rotateVisual={val.m_rotateVisual}"; obj2[41] = $" rotateVisualY={val.m_rotateVisualY}"; obj2[42] = $" rotateVisualZ={val.m_rotateVisualZ}"; obj2[43] = " visual="; GameObject visual = val.m_visual; obj2[44] = ((visual != null) ? ((Object)visual).name : null) ?? ""; obj2[45] = $" canChangeVisuals={val.m_canChangeVisuals}"; obj2[46] = $" skill={val.m_skill}"; obj2[47] = $" raiseSkillAmount={val.m_raiseSkillAmount}"; modLogger2.LogInfo((object)string.Concat(obj2)); SecondaryAttacksPlugin.ModLogger.LogInfo((object)("[RuntimeDump] " + weaponPrefabName + " payloadHierarchy=" + DescribeHierarchy(attackProjectile))); } } private static string DescribeComponents(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return ""; } Component[] components = gameObject.GetComponents(); if (components.Length == 0) { return ""; } return string.Join(", ", components.Select(delegate(Component component) { object obj; if (!((Object)(object)component == (Object)null)) { obj = ((object)component).GetType().FullName; if (obj == null) { return ((object)component).GetType().Name; } } else { obj = ""; } return (string)obj; })); } private static string DescribeHierarchy(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return ""; } List list = new List(); CollectHierarchyDescriptions(gameObject.transform, string.Empty, list); if (list.Count != 0) { return string.Join(" | ", list); } return ""; } private static void CollectHierarchyDescriptions(Transform node, string parentPath, List nodes) { string text = (string.IsNullOrEmpty(parentPath) ? ((Object)node).name : (parentPath + "/" + ((Object)node).name)); Component[] components = ((Component)node).GetComponents(); string text2 = string.Join(", ", from component in components where (Object)(object)component != (Object)null && !(component is Transform) select ((object)component).GetType().FullName ?? ((object)component).GetType().Name); nodes.Add(string.IsNullOrEmpty(text2) ? text : (text + "[" + text2 + "]")); for (int num = 0; num < node.childCount; num++) { CollectHierarchyDescriptions(node.GetChild(num), text, nodes); } } internal static bool TryCreateSummonEmpowerDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; ApplyAttackResourceScaling(definition, primaryAttack, GetNormalizedResourceMultiplier(weaponConfig)); return true; } internal static bool TryCreateShieldConvertDefinition(string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, List configuredEffects, 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)), SneakAmbush = CreateSneakAmbushDefinition(weaponConfig), CleavingThrust = CreateCleavingThrustDefinition(weaponConfig), LaunchSlam = CreateLaunchSlamDefinition(weaponConfig), KnockbackChain = CreateKnockbackChainDefinition(weaponConfig), Aftershock = CreateAftershockDefinition(weaponConfig), ConfiguredEffects = configuredEffects }; 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; } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogStaffDebug(string message) { } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogRangedDebug(string message) { } internal static string DescribeItemForRangedDebug(ItemData? item) { return SecondaryAttackRuntimeFacade.DescribeItemForRangedDebug(item); } internal static string DescribeAttackForRangedDebug(Attack? attack) { return SecondaryAttackRuntimeFacade.DescribeAttackForRangedDebug(attack); } public static bool TryGetDefinition(ItemData weapon, out SecondaryAttackDefinition definition) { return SecondaryAttackRuntimeFacade.TryGetDefinition(weapon, out definition); } public static bool TryGetDefinition(string weaponPrefabName, out SecondaryAttackDefinition definition) { return SecondaryAttackRuntimeFacade.TryGetDefinition(weaponPrefabName, out definition); } public static bool TryGetCurrentWeaponDefinition(out SecondaryAttackDefinition definition, out bool secondaryAttack) { return SecondaryAttackRuntimeFacade.TryGetCurrentWeaponDefinition(out definition, out secondaryAttack); } public static bool ShouldHandleBowDraw(ItemData weapon) { return SecondaryAttackRuntimeFacade.ShouldHandleBowDraw(weapon); } public static bool CanStartConfiguredSecondary(Humanoid humanoid, ItemData weapon) { return SecondaryAttackRuntimeFacade.CanStartConfiguredSecondary(humanoid, weapon); } public static void RegisterActiveAttack(Attack attack, ItemData weapon) { SecondaryAttackRuntimeFacade.RegisterActiveAttack(attack, weapon); } public static bool TryHandleCustomAttackTrigger(Attack attack) { return SecondaryAttackRuntimeFacade.TryHandleCustomAttackTrigger(attack); } public static bool TryHandleCustomProjectileBurst(Attack attack) { return SecondaryAttackRuntimeFacade.TryHandleCustomProjectileBurst(attack); } 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 (!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 || !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 text = presetText.Trim(); foreach (SecondaryAttackPreset value in Enum.GetValues(typeof(SecondaryAttackPreset))) { if (ProjectileRuntimeSystem.GetPresetName(value) == text) { preset = value; return true; } } preset = SecondaryAttackPreset.Barrage; 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; } return itemDrop.m_itemData.m_shared.m_secondaryAttack ?? 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 dropPrefab2 = itemDrop.m_itemData.m_dropPrefab; obj2 = ((dropPrefab2 != null) ? ((Object)dropPrefab2).name : null) ?? ((Object)itemDrop).name; } string sourcePrefabName2 = (string)obj2; if (TryResolveFractureLineSourceAttack(objectDb, sourcePrefabName2, out Attack sourceAttack2, out reason)) { return sourceAttack2; } return itemDrop.m_itemData.m_shared.m_secondaryAttack ?? 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 dropPrefab3 = itemDrop.m_itemData.m_dropPrefab; obj3 = ((dropPrefab3 != null) ? ((Object)dropPrefab3).name : null) ?? ((Object)itemDrop).name; } string sourcePrefabName3 = (string)obj3; if (TryResolveSecondarySourceAttack(objectDb, sourcePrefabName3, out Attack sourceAttack3, out reason)) { return sourceAttack3; } return itemDrop.m_itemData.m_shared.m_secondaryAttack; } 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 = val.m_itemData.m_shared.m_secondaryAttack; 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 secondaryAttack = val.m_itemData.m_shared.m_secondaryAttack; if (IsValidAftershockSourceAttack(secondaryAttack)) { sourceAttack = secondaryAttack; 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 secondaryAttack = val.m_itemData.m_shared.m_secondaryAttack; if (IsValidFractureLineSourceAttack(secondaryAttack)) { sourceAttack = secondaryAttack; 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 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; } internal static bool TryResolveWeaponEffectDefinition(string effectId, EffectBehaviorConfig effectConfig, out ConfiguredWeaponEffectDefinition? definition, out string reason) { return WeaponEffectDefinitionCompiler.TryResolve(effectId, effectConfig, out definition, out reason); } 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, ProjectileOnHitState state) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_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(state.DirectHitContext); if (state.RuntimeContext) { MeleeProjectileHitCascadeSystem.TryTrigger(projectile, collider, hitPoint, water, normal); } SecondaryAttackRuntimeFacade.EndProjectileHitContext(state.RuntimeContext); ProjectileRuntimeSystem.EndScatterRicochetDamageScale(state.ScatterRicochetDamageScope); MeleeProjectileHitCascadeSystem.DestroySpearRainFollowupAfterHit(projectile, collider, hitPoint, water, normal); } internal static void PlayerUpdatePostfix(Player player, bool primaryAttackHold, bool secondaryAttackHold, bool secondaryAttackPressed, ref bool blocking) { if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { SecondaryAttackFacade.TryApplyPendingConfig(); SecondaryAttackAdminAccessSystem.Update(); MeleeBoomerangProjectileSystem.UpdateDeferredReturnAutoEquips(player); SecondaryAttackRuntimeFacade.TryUpdateSecondaryProjectileHoldRepeat(player, secondaryAttackHold); MeleePresetCooldownSystem.UpdateActiveCooldowns(player); RangedSecondaryCooldownSystem.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 void PlayerUpdatePlacementGhostPostfix(Player player) { } internal static bool PlayerTryPlacePiecePrefix(Player player, Piece piece, ref bool result) { return true; } internal static void PlayerTryPlacePiecePostfix(Player player, Piece piece, bool result) { } 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(state); return false; } return true; } internal static void AttackFireProjectileBurstPostfix(CopiedThrowProjectileVisualSystem.BurstScope state) { CopiedThrowProjectileVisualSystem.EndBurst(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, string source) { 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), source); } } internal static void ApplyToHitData(HitData? hit, Projectile? projectile, ItemData? weapon, string source) { if (hit != null) { short num = ResolveToolTier(projectile, weapon); byte b = ResolveItemWorldLevel(projectile, weapon); if (num > hit.m_toolTier) { _ = hit.m_toolTier; hit.m_toolTier = num; } if (b > hit.m_itemWorldLevel) { _ = 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; } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] private static void LogDebug(string message) { } } 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 string OriginalCooldownFallbackPresetName { get; } internal bool IsOriginalCooldownFallback => !string.IsNullOrWhiteSpace(OriginalCooldownFallbackPresetName); internal StartAttackState(SecondaryAttackManager.ReloadSecondaryResourceCostContext reloadCostContext, ItemData? cooldownFallbackWeapon = null, Attack? configuredSecondaryAttack = null, bool skipActiveRegistration = false, string originalCooldownFallbackPresetName = "") { ReloadCostContext = reloadCostContext; CooldownFallbackWeapon = cooldownFallbackWeapon; ConfiguredSecondaryAttack = configuredSecondaryAttack; SkipActiveRegistration = skipActiveRegistration; OriginalCooldownFallbackPresetName = originalCooldownFallbackPresetName; } internal void RestoreCooldownFallbackSecondary() { if (CooldownFallbackWeapon?.m_shared != null && ConfiguredSecondaryAttack != null) { CooldownFallbackWeapon.m_shared.m_secondaryAttack = ConfiguredSecondaryAttack; } } } private sealed class ProjectilePresetCooldownFallbackState { internal string PresetName { get; } internal ProjectilePresetCooldownFallbackState(string presetName) { PresetName = presetName; } } private sealed class ProjectilePresetCooldownConsumedState { internal string PresetName { get; } internal ProjectilePresetCooldownConsumedState(string presetName) { PresetName = presetName; } } private sealed class ProjectilePresetOriginalCooldownFallbackState { internal string PresetName { get; } internal ProjectilePresetOriginalCooldownFallbackState(string presetName) { PresetName = presetName; } } private static readonly ConditionalWeakTable ProjectilePresetCooldownFallbackAttacks = new ConditionalWeakTable(); 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, ItemData leftItem, ItemData rightItem, 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) { SecondaryAttackAdrenalineSystem.EndConfiguredSecondaryStart(humanoid); if (secondaryAttack) { state.RestoreCooldownFallbackSecondary(); } if (secondaryAttack && result && humanoid.m_currentAttack != null) { if (state.IsOriginalCooldownFallback) { MarkProjectilePresetOriginalCooldownFallback(humanoid.m_currentAttack, state.OriginalCooldownFallbackPresetName); } 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); } } } 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, out string _)) { return true; } ClearProjectilePresetCooldownFallback(attack); if (ProjectilePresetCooldownConsumedAttacks.TryGetValue(attack, out ProjectilePresetCooldownConsumedState _)) { return true; } SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(attack.m_weapon); if (!TryResolveProjectilePresetCooldown(attack.m_weapon, out string presetName2, out MeleePresetCooldownDefinition cooldown, out SecondaryAttackDefinition definition)) { return true; } if (IsSpearRainPreset(presetName2)) { bool num = MeleePresetCooldownSystem.IsReady((Character)(object)attack.m_character, attack.m_weapon, presetName2, cooldown); bool flag = MeleeProjectileHitCascadeSystem.HasPendingSpearRain((Character)(object)attack.m_character, attack.m_weapon); if (num && !flag) { return true; } attack.Stop(); return false; } if (MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, presetName2, cooldown, out var _)) { MarkProjectilePresetCooldownConsumed(attack, presetName2); return true; } if (ProjectilePresetCooldownFallback.IsCopiedSecondary(ResolveProjectilePresetCooldownFallback(definition))) { MarkProjectilePresetCooldownFallback(attack, presetName2); return true; } attack.Stop(); return false; } internal static bool ShouldSkipProjectilePresetEffectsForCooldown(Attack? attack, out string presetName) { presetName = ""; if (attack == null || !ProjectilePresetCooldownFallbackAttacks.TryGetValue(attack, out ProjectilePresetCooldownFallbackState value)) { return false; } presetName = value.PresetName; return true; } internal static bool IsProjectilePresetOriginalCooldownFallback(Attack? attack, out string presetName) { presetName = ""; if (attack == null || !ProjectilePresetOriginalCooldownFallbackAttacks.TryGetValue(attack, out ProjectilePresetOriginalCooldownFallbackState value)) { return false; } presetName = value.PresetName; return true; } 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)) { LogCooldownStartProbeResolveMiss(currentWeapon); return false; } string value = ResolveProjectilePresetCooldownFallback(definition); bool num = MeleePresetCooldownSystem.IsReady((Character)(object)humanoid, currentWeapon, presetName, cooldown); bool flag = IsSpearRainPreset(presetName) && MeleeProjectileHitCascadeSystem.HasPendingSpearRain((Character)(object)humanoid, currentWeapon); if (num && !flag) { if (ProjectilePresetCooldownFallback.UsesDynamicOriginalSecondary(definition)) { return TryPrepareDynamicProjectilePresetSecondary(currentWeapon, definition, presetName, ref result, out state, out runOriginal); } return false; } if (ProjectilePresetCooldownFallback.IsCopiedSecondary(value)) { return false; } if (ProjectilePresetCooldownFallback.UsesDynamicOriginalSecondary(definition) && TryPrepareDynamicOriginalSecondaryFallback(currentWeapon, definition, presetName, out state)) { return true; } if (!TryResolveOriginalSecondaryAttack(currentWeapon, definition, secondaryBeforeRuntimeApply, definition.ConfiguredSecondaryAttack ?? currentWeapon.m_shared?.m_secondaryAttack, out Attack originalSecondaryAttack, out string _)) { 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, presetName); return true; } private static bool TryPrepareDynamicProjectilePresetSecondary(ItemData currentWeapon, SecondaryAttackDefinition definition, string presetName, 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, string presetName, 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, presetName); return true; } private static void LogCooldownStartProbeResolveMiss(ItemData currentWeapon) { if (!((Object)(object)currentWeapon?.m_dropPrefab == (Object)null)) { SecondaryAttackRuntimeFacade.TryGetDefinition(currentWeapon, out SecondaryAttackDefinition _); } } 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 string ResolveProjectilePresetCooldownFallback(SecondaryAttackDefinition definition) { return "originalSecondary"; } private static bool IsSpearRainPreset(string presetName) { return presetName.Equals("spearRain", StringComparison.OrdinalIgnoreCase); } private static void MarkProjectilePresetCooldownFallback(Attack attack, string presetName) { ProjectilePresetCooldownFallbackAttacks.Remove(attack); ProjectilePresetCooldownFallbackAttacks.Add(attack, new ProjectilePresetCooldownFallbackState(presetName)); } private static void MarkProjectilePresetCooldownConsumed(Attack attack, string presetName) { ProjectilePresetCooldownConsumedAttacks.Remove(attack); ProjectilePresetCooldownConsumedAttacks.Add(attack, new ProjectilePresetCooldownConsumedState(presetName)); } private static void MarkProjectilePresetOriginalCooldownFallback(Attack attack, string presetName) { ProjectilePresetOriginalCooldownFallbackAttacks.Remove(attack); ProjectilePresetOriginalCooldownFallbackAttacks.Add(attack, new ProjectilePresetOriginalCooldownFallbackState(presetName)); } private static void ClearProjectilePresetCooldownFallback(Attack attack) { ProjectilePresetCooldownFallbackAttacks.Remove(attack); } private static bool TryResolveOriginalSecondaryAttack(ItemData currentWeapon, SecondaryAttackDefinition definition, Attack? secondaryBeforeRuntimeApply, Attack? configuredSecondaryAttack, out Attack? originalSecondaryAttack, out string source) { originalSecondaryAttack = null; source = ""; string copiedSourcePrefab; bool rejectCopiedCarrier = ShouldRejectMatchingCopiedCarrier(currentWeapon, definition, out copiedSourcePrefab); if (HasUsableSecondaryAttack(secondaryBeforeRuntimeApply)) { if (!ShouldSkipOriginalSecondaryCandidate(secondaryBeforeRuntimeApply, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = SecondaryAttackManager.CloneAttack(secondaryBeforeRuntimeApply); source = "runtimeBeforeApply"; return true; } LogSkippedOriginalSecondaryCandidate(currentWeapon, "runtimeBeforeApply", copiedSourcePrefab, secondaryBeforeRuntimeApply, configuredSecondaryAttack); } if (HasUsableSecondaryAttack(definition.CooldownFallbackSecondaryAttack)) { if (!ShouldSkipOriginalSecondaryCandidate(definition.CooldownFallbackSecondaryAttack, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = SecondaryAttackManager.CloneAttack(definition.CooldownFallbackSecondaryAttack); source = "definition"; return true; } LogSkippedOriginalSecondaryCandidate(currentWeapon, "definition", copiedSourcePrefab, definition.CooldownFallbackSecondaryAttack, configuredSecondaryAttack); } 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)) { if (!ShouldSkipOriginalSecondaryCandidate(attack, configuredSecondaryAttack, rejectCopiedCarrier)) { originalSecondaryAttack = attack; source = "objectDb"; return true; } LogSkippedOriginalSecondaryCandidate(currentWeapon, "objectDb", copiedSourcePrefab, attack, configuredSecondaryAttack); } return false; } private static bool ShouldRejectMatchingCopiedCarrier(ItemData currentWeapon, SecondaryAttackDefinition definition, out string copiedSourcePrefab) { copiedSourcePrefab = ""; if ((Object)(object)currentWeapon?.m_dropPrefab == (Object)null || !(definition.Behavior is CopiedSecondaryBehavior copiedSecondaryBehavior) || string.IsNullOrWhiteSpace(copiedSecondaryBehavior.SourcePrefabName)) { return false; } copiedSourcePrefab = copiedSecondaryBehavior.SourcePrefabName.Trim(); return !string.Equals(copiedSourcePrefab, ((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 void LogSkippedOriginalSecondaryCandidate(ItemData? currentWeapon, string source, string copiedSourcePrefab, Attack? candidate, Attack? configuredSecondaryAttack) { } private static bool HasUsableSecondaryAttack(Attack? attack) { if (attack != null) { return !string.IsNullOrWhiteSpace(attack.m_attackAnimation); } return false; } private static string DescribeAttack(Attack? attack) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (attack == null) { return ""; } object[] obj = new object[6] { attack.m_attackAnimation ?? "", attack.m_attackType, null, null, null, null }; GameObject attackProjectile = attack.m_attackProjectile; obj[2] = ((attackProjectile != null) ? ((Object)attackProjectile).name : null) ?? ""; obj[3] = attack.m_consumeItem; obj[4] = attack.m_projectiles; obj[5] = attack.m_projectileBursts; return string.Format("animation={0}, type={1}, projectile={2}, consumeItem={3}, projectiles={4}, bursts={5}", obj); } [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] private static void LogStaffStartDebugIfNeeded(Humanoid humanoid, bool result) { ItemData currentWeapon = humanoid.GetCurrentWeapon(); SecondaryAttackManager.EnsureRuntimeWeaponDefinitionApplied(currentWeapon); if (currentWeapon != null && SecondaryAttackRuntimeFacade.TryGetDefinition(currentWeapon, out SecondaryAttackDefinition definition) && definition.BehaviorType != SecondaryAttackBehaviorType.SummonEmpower) { _ = definition.BehaviorType; _ = 3; } } 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_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_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, 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, __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(OverchargedBombSystem.ProjectileHitScaleState __state) { OverchargedBombSystem.EndProjectileHit(__state); } } [HarmonyPatch(typeof(Destructible), "Damage")] internal static class DestructibleDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "Destructible.Damage"); } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "MineRock.Damage"); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5DamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "MineRock5.Damage"); } } [HarmonyPatch(typeof(TreeBase), "Damage")] internal static class TreeBaseDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "TreeBase.Damage"); } } [HarmonyPatch(typeof(TreeLog), "Damage")] internal static class TreeLogDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "TreeLog.Damage"); } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class WearNTearDamageProjectileToolTierPatch { private static void Prefix(HitData hit) { SecondaryAttackProjectileToolTierSystem.ApplyCurrentProjectileHitToolTierIfNeeded(hit, "WearNTear.Damage"); } } [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, bool ___m_secondaryAttack, ref bool ___m_blocking) { SecondaryAttackHarmonyDispatch.PlayerUpdatePostfix(__instance, ___m_attackHold, ___m_secondaryAttackHold, ___m_secondaryAttack, 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(Player), "UpdatePlacementGhost")] internal static class PlayerUpdatePlacementGhostPatch { private static void Postfix(Player __instance) { SecondaryAttackHarmonyDispatch.PlayerUpdatePlacementGhostPostfix(__instance); } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class PlayerTryPlacePiecePatch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { return SecondaryAttackHarmonyDispatch.PlayerTryPlacePiecePrefix(__instance, piece, ref __result); } private static void Postfix(Player __instance, Piece piece, bool __result) { SecondaryAttackHarmonyDispatch.PlayerTryPlacePiecePostfix(__instance, piece, __result); } } [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 = SecondaryAttackManager.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 = SecondaryAttackManager.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) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (BloodMagicSkillGainSystem.ShouldBlockBloodMagicRaise(skillType)) { return false; } HarvestSweepSystem.ApplySkillRaiseFactor(skillType, ref factor); return true; } } [HarmonyPatch(typeof(SEMan), "ApplyStatusEffectSpeedMods")] internal static class SEManApplyStatusEffectSpeedModsPatch { private static void Postfix(SEMan __instance, ref float speed) { Character seManCharacter = SecondaryAttackManager.GetSeManCharacter(__instance); if (!((Object)(object)seManCharacter == (Object)null) && StaffRuntimeSystem.TryGetSummonEmpower(seManCharacter, out var moveSpeedFactor, out var _, out var _) && !Mathf.Approximately(moveSpeedFactor, 1f)) { speed *= moveSpeedFactor; } } } [HarmonyPatch(typeof(Humanoid), "GetTimeSinceLastAttack")] internal static class HumanoidGetTimeSinceLastAttackPatch { private static void Postfix(Humanoid __instance, ref float __result) { if (StaffRuntimeSystem.TryGetSummonEmpower((Character)(object)__instance, out var _, out var attackSpeedFactor, out var _) && !Mathf.Approximately(attackSpeedFactor, 1f)) { __result *= Mathf.Max(0.05f, attackSpeedFactor); } } } [HarmonyPatch(typeof(Attack), "Start")] internal static class AttackStartCooldownAdjustmentPatch { private static void Prefix(Attack __instance, out float __state) { __state = __instance?.m_attackUseAdrenaline ?? 0f; if (__instance != null && (SecondaryAttackAdrenalineSystem.ShouldSuppressAttackUseAdrenaline(__instance) || SecondaryAttackAdrenalineSystem.TryBeginAttackUseAdrenalineProjectileHitConversion(__instance))) { __instance.m_attackUseAdrenaline = 0f; } } private static void Postfix(Attack __instance, bool __result, float __state) { if (__instance != null) { __instance.m_attackUseAdrenaline = __state; if (__result && !((Object)(object)__instance.m_character == (Object)null) && __instance.m_weapon != null && StaffRuntimeSystem.TryGetSummonEmpower((Character)(object)__instance.m_character, out var _, out var attackSpeedFactor, out var _) && !Mathf.Approximately(attackSpeedFactor, 1f)) { float num = 1f - 1f / Mathf.Max(0.05f, attackSpeedFactor); ItemData weapon = __instance.m_weapon; weapon.m_lastAttackTime -= __instance.m_weapon.m_shared.m_aiAttackInterval * num; } } } } [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, ItemData ___m_leftItem, ItemData ___m_rightItem, out SecondaryAttackStartAttackDispatch.StartAttackState __state) { return SecondaryAttackStartAttackDispatch.Prefix(__instance, secondaryAttack, ref __result, ___m_leftItem, ___m_rightItem, out __state); } private static void Postfix(Humanoid __instance, bool secondaryAttack, bool __result, SecondaryAttackStartAttackDispatch.StartAttackState __state) { SecondaryAttackStartAttackDispatch.Postfix(__instance, secondaryAttack, __result, __state); } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class AttackOnAttackTriggerPatch { private readonly struct AttackOnAttackTriggerState { internal bool ReloadState { get; } internal AttackOnAttackTriggerState(bool reloadState) { ReloadState = reloadState; } } [HarmonyPriority(800)] private static bool Prefix(Attack __instance, out AttackOnAttackTriggerState __state) { bool reloadState = SecondaryAttackManager.BeginReloadStateConsumption(__instance); __state = new AttackOnAttackTriggerState(reloadState); return !SecondaryAttackRuntimeFacade.TryHandleCustomAttackTrigger(__instance); } private static void Postfix(Attack __instance, AttackOnAttackTriggerState __state) { SecondaryAttackManager.EndReloadStateConsumption(__instance, __state.ReloadState); SecondaryAttackRuntimeFacade.TryTriggerRiftTrailAfterAttack(__instance); SecondaryAttackRuntimeFacade.TryTriggerFractureLineAfterAttack(__instance); } } [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(SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(__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(SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(__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(SecondaryAttackManager.SecondaryAttackDurabilityAdjustmentState __state) { SecondaryAttackManager.EndSecondaryAttackDurabilityAdjustment(__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(CopiedThrowProjectileVisualSystem.BurstScope __state) { SecondaryAttackHarmonyDispatch.AttackFireProjectileBurstPostfix(__state); } } internal static class SecondaryAttackNetworkCompat { [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterAndSendVersionCheckPatch { private static void Prefix(ZNetPeer peer) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown peer.m_rpc.Register(VersionCheckRpcName, (Action)HandleVersionCheck); SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Invoking version check"); ZPackage val = new ZPackage(); val.Write("1.0.1"); peer.m_rpc.Invoke(VersionCheckRpcName, new object[1] { val }); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] private static class ShowConnectionErrorPatch { private static void Postfix(FejdStartup __instance) { if (__instance.m_connectionFailedPanel.activeSelf) { __instance.m_connectionFailedError.fontSizeMax = 25f; __instance.m_connectionFailedError.fontSizeMin = 15f; TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + SecondaryAttacksPlugin.ConnectionError; } } } private static string VersionCheckRpcName => "SecondaryAttacks_VersionCheck"; public static void HandleVersionCheck(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); SecondaryAttacksPlugin.ModLogger.LogInfo((object)("Version check, local: 1.0.1, remote: " + text)); if (text != "1.0.1") { SecondaryAttacksPlugin.ConnectionError = "SecondaryAttacks Installed: 1.0.1\n Needed: " + text; if (ZNet.instance.IsServer()) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") has incompatible version, disconnecting...")); rpc.Invoke("Error", new object[1] { 3 }); } } else if (!ZNet.instance.IsServer()) { SecondaryAttacksPlugin.ModLogger.LogInfo((object)"Received same version from server."); } } } internal static class SecondaryAttackObjectDbStateStore { private sealed class OriginalWeaponState { public Attack OriginalSecondaryAttack { get; } public StatusEffect? OriginalEquipStatusEffect { get; } public bool OriginalBuildBlockCharges { get; } public int OriginalMaxBlockCharges { get; } public float OriginalBlockChargeDecayTime { get; } public float OriginalBlockChargeBlockingDecayFactor { get; } public OriginalWeaponState(Attack originalSecondaryAttack, StatusEffect? originalEquipStatusEffect, bool originalBuildBlockCharges, int originalMaxBlockCharges, float originalBlockChargeDecayTime, float originalBlockChargeBlockingDecayFactor) { OriginalSecondaryAttack = originalSecondaryAttack; OriginalEquipStatusEffect = originalEquipStatusEffect; OriginalBuildBlockCharges = originalBuildBlockCharges; OriginalMaxBlockCharges = originalMaxBlockCharges; OriginalBlockChargeDecayTime = originalBlockChargeDecayTime; OriginalBlockChargeBlockingDecayFactor = originalBlockChargeBlockingDecayFactor; } } private static readonly ConditionalWeakTable> Snapshots = new ConditionalWeakTable>(); public static void Capture(ObjectDB objectDb) { Dictionary value = Snapshots.GetValue(objectDb, (ObjectDB _) => new Dictionary(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)) { SharedData shared = component.m_itemData.m_shared; if (!value.ContainsKey(((Object)item).name)) { value[((Object)item).name] = new OriginalWeaponState(SecondaryAttackManager.CloneAttack(shared.m_secondaryAttack), shared.m_equipStatusEffect, shared.m_buildBlockCharges, shared.m_maxBlockCharges, shared.m_blockChargeDecayTime, shared.m_blockChargeBlockingDecayMult); } } } } 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)) { SharedData shared = component.m_itemData.m_shared; shared.m_secondaryAttack = SecondaryAttackManager.CloneAttack(value2.OriginalSecondaryAttack); shared.m_equipStatusEffect = value2.OriginalEquipStatusEffect; shared.m_buildBlockCharges = value2.OriginalBuildBlockCharges; shared.m_maxBlockCharges = value2.OriginalMaxBlockCharges; shared.m_blockChargeDecayTime = value2.OriginalBlockChargeDecayTime; shared.m_blockChargeBlockingDecayMult = value2.OriginalBlockChargeBlockingDecayFactor; } } } } 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.OriginalSecondaryAttack); return true; } } internal static class SecondaryAttackPerformanceLog { internal const bool Enabled = false; internal static Stopwatch? Start() { return null; } [Conditional("SECONDARY_ATTACKS_PERF_LOGGING")] internal static void Stop(Stopwatch? stopwatch, string scope, string details) { } [Conditional("SECONDARY_ATTACKS_PERF_LOGGING")] internal static void Stop(Stopwatch? stopwatch, string scope, Func details) { } } internal static class StaffRuntimeSystem { internal const string StaffTargetEffectRpcName = "SecondaryAttacks_SpawnStaffTargetEffect"; 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; } float finalCooldown; return MeleePresetCooldownSystem.TryConsume((Character)(object)attack.m_character, attack.m_weapon, presetName, cooldown, out finalCooldown); } 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, weapon, 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_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_0080: 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 = SecondaryAttackManager.GetNetworkTimeSeconds() + Mathf.Max(0.1f, summonEmpowerSecondaryBehavior.Duration); Vector3 centerPoint = ((Character)val).GetCenterPoint(); int num = 0; foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsMatchingSummonEmpowerTarget(val, allCharacter, definition, centerPoint) && TryApplySummonEmpower(allCharacter, expiry, moveSpeedFactor, attackSpeedFactor)) { num++; } } } 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_0049: 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(); int num = 0; foreach (Character allCharacter in Character.GetAllCharacters()) { if (IsValidShieldConvertTarget(val, allCharacter, shieldConvertSecondaryBehavior.Radius, centerPoint) && TryConvertShieldToHeal(allCharacter, shieldConvertSecondaryBehavior.HealFactor, shieldConvertSecondaryBehavior.ShieldStatusEffectHash)) { num++; } } } 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 bool TryApplySummonEmpower(Character target, float expiry, float moveSpeedFactor, float attackSpeedFactor) { if ((Object)(object)target == (Object)null) { return false; } if (SecondaryAttackManager.HasCharacterAuthority(target)) { ApplySummonEmpowerState(target, expiry, moveSpeedFactor, attackSpeedFactor); return true; } if (!SecondaryAttackManager.TryGetCharacterZdo(target, out ZNetView nview, out ZDO _)) { return false; } nview.InvokeRPC(SecondaryAttackManager.ApplySummonEmpowerRpcNameForStaffRuntime, new object[3] { expiry, moveSpeedFactor, attackSpeedFactor }); return true; } private static bool TryConvertShieldToHeal(Character target, float healFactor, int shieldStatusEffectHash) { if ((Object)(object)target == (Object)null) { return false; } if (SecondaryAttackManager.HasCharacterAuthority(target)) { return ApplyShieldConvertToCharacter(target, healFactor, shieldStatusEffectHash); } if (!SecondaryAttackManager.TryGetCharacterZdo(target, out ZNetView nview, out ZDO _)) { return false; } nview.InvokeRPC(SecondaryAttackManager.ConvertShieldToHealRpcNameForStaffRuntime, new object[2] { healFactor, shieldStatusEffectHash }); return true; } internal static void ApplySummonEmpowerState(Character character, float expiry, float moveSpeedFactor, float attackSpeedFactor) { if (SecondaryAttackManager.TryGetCharacterZdo(character, out ZNetView _, out ZDO zdo)) { zdo.Set(SecondaryAttackManager.SummonEmpowerExpiryZdoKeyForStaffRuntime, Mathf.Max(0f, expiry)); zdo.Set(SecondaryAttackManager.SummonEmpowerMoveSpeedBonusZdoKeyForStaffRuntime, Mathf.Max(0.05f, moveSpeedFactor)); zdo.Set(SecondaryAttackManager.SummonEmpowerAttackCooldownReductionZdoKeyForStaffRuntime, 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 networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); if (SecondaryAttackManager.TryGetShieldRemaining(character, 0, out SE_Shield _, out float remaining, out float remainingTime)) { zdo.Set(SecondaryAttackManager.ShieldRemainingDisplayZdoKeyForStaffRuntime, Mathf.Max(0f, remaining)); zdo.Set(SecondaryAttackManager.ShieldDisplayExpiryZdoKeyForStaffRuntime, networkTimeSeconds + Mathf.Max(0f, remainingTime)); OverheadStatusUiManager.RefreshTrackedCharacter(character); } else { zdo.Set(SecondaryAttackManager.ShieldRemainingDisplayZdoKeyForStaffRuntime, 0f); zdo.Set(SecondaryAttackManager.ShieldDisplayExpiryZdoKeyForStaffRuntime, 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 networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); float num = zdo.GetFloat(SecondaryAttackManager.SummonEmpowerExpiryZdoKeyForStaffRuntime, 0f); if (num <= networkTimeSeconds) { zdo.Set(SecondaryAttackManager.SummonEmpowerExpiryZdoKeyForStaffRuntime, 0f); zdo.Set(SecondaryAttackManager.SummonEmpowerMoveSpeedBonusZdoKeyForStaffRuntime, 0f); zdo.Set(SecondaryAttackManager.SummonEmpowerAttackCooldownReductionZdoKeyForStaffRuntime, 0f); return false; } moveSpeedFactor = Mathf.Max(0.05f, zdo.GetFloat(SecondaryAttackManager.SummonEmpowerMoveSpeedBonusZdoKeyForStaffRuntime, 1f)); attackSpeedFactor = Mathf.Max(0.05f, zdo.GetFloat(SecondaryAttackManager.SummonEmpowerAttackCooldownReductionZdoKeyForStaffRuntime, 1f)); remainingTime = num - networkTimeSeconds; return remainingTime > 0f; } 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 networkTimeSeconds = SecondaryAttackManager.GetNetworkTimeSeconds(); if (zdo.GetFloat(SecondaryAttackManager.ShieldDisplayExpiryZdoKeyForStaffRuntime, 0f) <= networkTimeSeconds) { return false; } remaining = Mathf.Max(0f, zdo.GetFloat(SecondaryAttackManager.ShieldRemainingDisplayZdoKeyForStaffRuntime, 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) { return; } 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); value.ConfiguredSecondaryAttack = SecondaryAttackManager.CloneAttack(val); weapon.m_shared.m_secondaryAttack = (Attack)(ProjectilePresetCooldownFallback.UsesDynamicOriginalSecondary(value) ? ((object)SecondaryAttackManager.CloneAttack(value.CooldownFallbackSecondaryAttack ?? component.m_itemData?.m_shared?.m_secondaryAttack)) : ((object)val)); if (value.BehaviorType != SecondaryAttackBehaviorType.SummonEmpower && value.BehaviorType != SecondaryAttackBehaviorType.ShieldConvert) { } } 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 RemoveActiveAttack(Attack attack) { ActiveAttacks.Remove(attack); } 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 static class SecondaryAttackRuntimeFacade { internal const string ReloadLoadedCustomDataKey = "SecondaryAttacks.ReloadLoaded"; private const string StaffRapidFireAnimation = "staff_rapidfire"; private const float FallbackHoldRepeatInterval = 0.2f; [Conditional("SECONDARY_ATTACKS_DEBUG_LOGGING")] internal static void LogRangedDebug(string message) { } internal static string DescribeItemForRangedDebug(ItemData? item) { if (item == null) { return ""; } string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); string text2 = item.m_shared?.m_name ?? ""; string text3 = ((item.m_shared != null) ? ((object)Unsafe.As(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString() : ""); string text4 = ((item.m_shared != null) ? item.m_shared.m_ammoType : ""); string value; string text5 = ((item.m_customData != null && item.m_customData.TryGetValue("SecondaryAttacks.ReloadLoaded", out value)) ? value : "0"); return $"{text}/{text2} type={text3} stack={item.m_stack} quality={item.m_quality} durability={item.m_durability:0.###} ammoType={text4} loadedData={text5}"; } internal static string DescribeAttackForRangedDebug(Attack? attack) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (attack == null) { return ""; } Humanoid character = attack.m_character; Player val = (Player)(object)((character is Player) ? character : null); object obj; if (val == null || attack.m_weapon == null) { Humanoid character2 = attack.m_character; obj = ((character2 != null) ? character2.IsWeaponLoaded().ToString() : null) ?? ""; } else { obj = (val.m_weaponLoaded == attack.m_weapon).ToString(); } string text = (string)obj; object[] obj2 = new object[12] { attack.m_attackAnimation ?? "", attack.m_attackType, null, null, null, null, null, null, null, null, null, null }; GameObject attackProjectile = attack.m_attackProjectile; obj2[2] = ((attackProjectile != null) ? ((Object)attackProjectile).name : null) ?? ""; obj2[3] = attack.m_consumeItem; obj2[4] = attack.m_requiresReload; obj2[5] = text; obj2[6] = attack.m_projectiles; obj2[7] = attack.m_projectileBursts; obj2[8] = attack.m_perBurstResourceUsage; obj2[9] = DescribeItemForRangedDebug(attack.m_weapon); obj2[10] = DescribeItemForRangedDebug(attack.m_ammoItem); obj2[11] = DescribeItemForRangedDebug(attack.m_lastUsedAmmo); return string.Format("animation={0} type={1} projectile={2} consumeItem={3} requiresReload={4} loaded={5} projectiles={6} bursts={7} perBurst={8} weapon=[{9}] ammo=[{10}] lastAmmo=[{11}]", obj2); } 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 TryGetCurrentWeaponDefinition(out SecondaryAttackDefinition definition, out bool secondaryAttack) { definition = null; secondaryAttack = false; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { 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; return SecondaryAttackFacade.CurrentAppliedWorldSnapshot.DefinitionsByPrefabName.TryGetValue(((Object)currentAttack.m_weapon.m_dropPrefab).name, 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; } } if (projectileSecondaryBehavior.AmmoConsumption <= 0 || string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType)) { return true; } if (humanoid.GetInventory() != null && CountAmmo(humanoid.GetInventory(), weapon.m_shared.m_ammoType) >= projectileSecondaryBehavior.AmmoConsumption) { return true; } ((Character)humanoid).Message((MessageType)2, "$msg_outof " + weapon.m_shared.m_ammoType, 0, (Sprite)null); return false; } 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 : SecondaryAttackAdrenalineSystem.ResolveDefinitionFactor(definition)); 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); } GreatSwordSkillScalingSystem.ApplyTrailScaleForActiveDefinition(attack, definition); if (num) { ActiveSecondaryAttack activeAttack = new ActiveSecondaryAttack(definition); SecondaryAttackRuntimeContext.SetActiveAttack(attack, activeAttack); SecondaryAttackAdrenalineSystem.Reset(attack); if (definition.CleavingThrust != null) { GreatSwordSkillScalingSystem.ApplyTrailScaleForActiveDefinition(attack, definition); } if (definition.RiftTrail != null) { RiftTrailSystem.BeginSampling(attack, definition); } if (definition.BehaviorType == SecondaryAttackBehaviorType.CopiedSecondary && definition.OnProjectileHit == null) { _ = definition.Boomerang; } } } 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; } float finalCooldown; 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, out finalCooldown)) { 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, out finalCooldown)) { 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; } TriggerConfiguredAttack(attack, activeAttack); 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, out var _)) { 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, out var _)) { activeAttack.Triggered = true; FractureLineSystem.Trigger(attack, activeAttack.Definition); } } private static bool TriggerCleavingThrust(Attack attack, ActiveSecondaryAttack activeAttack) { if (!ConsumeConfiguredAmmo(attack, activeAttack.Definition)) { return false; } activeAttack.Triggered = true; GreatSwordSkillScalingSystem.ApplyCleavingThrustTrailScaleForTriggeredAttack(attack, activeAttack.Definition); 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 ((projectileTriggered && !TryConsumeRepeatedProjectileStartResources(currentAttack)) || !TriggerConfiguredAttack(currentAttack, activeAttack)) { currentAttack.Stop(); } } } private static bool TriggerConfiguredAttack(Attack attack, ActiveSecondaryAttack activeAttack) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected I4, but got Unknown if (!ConsumeConfiguredAmmo(attack, activeAttack.Definition)) { return false; } 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; } ApplyAttackTriggerSideEffects(attack); return true; } 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 if (activeAttack.Definition.BehaviorType == SecondaryAttackBehaviorType.Projectile && (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; } float attackStamina = attack.GetAttackStamina(); if (attackStamina > 0f) { if (!((Character)attack.m_character).HaveStamina(attackStamina)) { if (((Character)attack.m_character).IsPlayer()) { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } return false; } ((Character)attack.m_character).UseStamina(attackStamina); } float attackEitr = attack.GetAttackEitr(); if (attackEitr > 0f) { if (!((Character)attack.m_character).HaveEitr(attackEitr)) { return false; } ((Character)attack.m_character).UseEitr(attackEitr); } float attackHealth = attack.GetAttackHealth(); if (attackHealth > 0f) { if (!((Character)attack.m_character).HaveHealth(attackHealth) && attack.m_attackHealthLowBlockUse) { if (((Character)attack.m_character).IsPlayer()) { Hud instance2 = Hud.instance; if (instance2 != null) { instance2.FlashHealthBar(); } } return false; } ((Character)attack.m_character).UseHealth(Mathf.Min(((Character)attack.m_character).GetHealth() - 1f, attackHealth)); } return true; } 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); if (!RangedSecondaryCooldownSystem.CanUse(attack, projectileSecondaryBehavior)) { if (!flag) { attack.Stop(); } return true; } if (!ProjectileRuntimeSystem.CanStartBurstPreset(attack, activeAttack.Definition, projectileSecondaryBehavior.Preset)) { attack.Stop(); return true; } if (!ConsumePerBurstResourcesIfNeeded(attack)) { return true; } bool num = ProjectileRuntimeSystem.TryHandleBurstPreset(attack, activeAttack.Definition, projectileSecondaryBehavior.Preset); if (num) { RangedSecondaryCooldownSystem.StartCooldown(attack, projectileSecondaryBehavior); } return num; } private static bool ConsumeConfiguredAmmo(Attack attack, SecondaryAttackDefinition definition) { attack.m_ammoItem = null; attack.m_lastUsedAmmo = null; ProjectileSecondaryBehavior projectileSecondaryBehavior = definition.Behavior as ProjectileSecondaryBehavior; if (string.IsNullOrWhiteSpace(attack.m_weapon.m_shared.m_ammoType)) { return true; } int num = CountAmmo(attack.m_character.GetInventory(), attack.m_weapon.m_shared.m_ammoType); ItemData val = Attack.FindAmmo(attack.m_character, attack.m_weapon); if (val != null && !IsAmmoItemForType(val, attack.m_weapon.m_shared.m_ammoType)) { val = ((IEnumerable)attack.m_character.GetInventory().GetAllItems()).FirstOrDefault((Func)((ItemData item) => IsAmmoItemForType(item, attack.m_weapon.m_shared.m_ammoType))); } if (val == null) { ((Character)attack.m_character).Message((MessageType)2, "$msg_outof " + attack.m_weapon.m_shared.m_ammoType, 0, (Sprite)null); return false; } attack.m_ammoItem = val; attack.m_lastUsedAmmo = val; if (projectileSecondaryBehavior == null || projectileSecondaryBehavior.AmmoConsumption <= 0) { return true; } if (num < projectileSecondaryBehavior.AmmoConsumption) { ((Character)attack.m_character).Message((MessageType)2, "$msg_outof " + attack.m_weapon.m_shared.m_ammoType, 0, (Sprite)null); return false; } RemoveAmmo(attack.m_character.GetInventory(), attack.m_weapon.m_shared.m_ammoType, projectileSecondaryBehavior.AmmoConsumption); CountAmmo(attack.m_character.GetInventory(), attack.m_weapon.m_shared.m_ammoType); return true; } private static int CountAmmo(Inventory inventory, string ammoType) { return (from item in inventory.GetAllItems() where IsAmmoItemForType(item, ammoType) select item).Sum((ItemData item) => item.m_stack); } private static void RemoveAmmo(Inventory inventory, string ammoType, int amount) { foreach (ItemData item in (from item in inventory.GetAllItems() where IsAmmoItemForType(item, ammoType) select item).ToList()) { if (amount <= 0) { break; } int num = Mathf.Min(item.m_stack, amount); inventory.RemoveItem(item, num); amount -= num; } } 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; } float attackStamina = attack.GetAttackStamina(); if (attackStamina > 0f) { if (!((Character)attack.m_character).HaveStamina(attackStamina)) { attack.Stop(); return false; } ((Character)attack.m_character).UseStamina(attackStamina); } float attackEitr = attack.GetAttackEitr(); if (attackEitr > 0f) { if (!((Character)attack.m_character).HaveEitr(attackEitr)) { attack.Stop(); return false; } ((Character)attack.m_character).UseEitr(attackEitr); } float attackHealth = attack.GetAttackHealth(); if (attackHealth > 0f) { if (!((Character)attack.m_character).HaveHealth(attackHealth) && attack.m_attackHealthLowBlockUse) { attack.Stop(); return false; } ((Character)attack.m_character).UseHealth(Mathf.Min(((Character)attack.m_character).GetHealth() - 1f, attackHealth)); } return true; } } internal sealed class ActiveSecondaryAttack { public SecondaryAttackDefinition Definition { get; } public bool Triggered { get; set; } public bool ProjectileTriggered { 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 SecondaryAttackWorldApplyContributors { internal static void BeforeDefinitions(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { CaptureOriginalObjectDbState(objectDb); RestoreOriginalObjectDbState(objectDb); ApplyObjectDbPreDefinitionSystems(objectDb, compiledSnapshot); } internal static void AfterDefinitions(ObjectDB objectDb, SecondaryAttackAppliedWorldSnapshot appliedWorldSnapshot, bool emitMissingWarnings) { WeaponEffectManager.ApplyToObjectDb(objectDb, appliedWorldSnapshot.DefinitionsByPrefabName, appliedWorldSnapshot.Effects); MagicSummonQualityPresetSystem.ApplyToObjectDb(objectDb, appliedWorldSnapshot.CompiledSnapshot.MagicSummons); } internal static void ApplyToZNetScene(ZNetScene scene, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { SummonPrefabOverrideSystem.RestoreScene(scene); SummonPrefabOverrideSystem.Apply(scene, compiledSnapshot.MagicSummons, compiledSnapshot.SnapshotId, emitMissingWarnings); MagicSummonQualityPresetSystem.ApplyToZNetScene(scene, compiledSnapshot.MagicSummons); } private static void CaptureOriginalObjectDbState(ObjectDB objectDb) { SecondaryAttackObjectDbStateStore.Capture(objectDb); } private static void RestoreOriginalObjectDbState(ObjectDB objectDb) { SecondaryAttackObjectDbStateStore.Restore(objectDb); MagicSummonQualityPresetSystem.RestoreObjectDb(objectDb); } private static void ApplyObjectDbPreDefinitionSystems(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot) { SecondaryAttackCompat.TryInstallWorldApplyHooks(); } } internal static class SecondaryAttackWorldApplySystem { private const string FixedThrowCarrierPrefab = "SpearFlint"; private static int _nextApplyRevision = 1; public static SecondaryAttackAppliedWorldSnapshot Apply(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { if ((Object)(object)objectDb == (Object)null) { return SecondaryAttackAppliedWorldSnapshot.Empty; } SecondaryAttackWorldApplyContributors.BeforeDefinitions(objectDb, compiledSnapshot, emitMissingWarnings); SecondaryAttackManager.ResetWorldApplyTransientState(); SecondaryAttackDefinitionBuildContext buildContext = new SecondaryAttackDefinitionBuildContext(objectDb, compiledSnapshot.Effects, emitMissingWarnings); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 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; } SecondaryAttackManager.DumpRuntimeWeaponProfile(((Object)item).name, component.m_itemData.m_shared); bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; compiledSnapshot.Weapons.TryGetValue(((Object)item).name, out NormalizedWeaponConfig value); if (value != null) { seenConfiguredPrefabs.Add(((Object)item).name); if (value.Enabled && !value.UseAutomaticFallback) { ApplyDefaultMeleeFallbacksIfNeeded(component, value, compiledSnapshot.GlobalMeleeFallback); } else { value = null; } } if (value == null) { NormalizedWeaponConfig fallback2; NormalizedWeaponConfig defaultMeleeFallback; 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 defaultMeleeFallback)) { value = defaultMeleeFallback; flag = true; } else { if (!SecondaryAttackDefinitionCompiler.HasConfiguredWeaponEffects(((Object)item).name, compiledSnapshot.Effects)) { continue; } value = new NormalizedWeaponConfig(); flag4 = true; } } if (!SecondaryAttackDefinitionCompiler.TryCreateDefinition(buildContext, ((Object)item).name, component, value, out SecondaryAttackDefinition definition)) { continue; } SecondaryAttackDefinition secondaryAttackDefinition = definition; 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 (!ProjectilePresetCooldownFallback.UsesDynamicOriginalSecondary(secondaryAttackDefinition)) { component.m_itemData.m_shared.m_secondaryAttack = val; } } num++; if (flag2) { num2++; } if (flag3) { num3++; } if (flag) { num4++; } if (flag4) { num5++; } } SecondaryAttackAppliedWorldSnapshot secondaryAttackAppliedWorldSnapshot = new SecondaryAttackAppliedWorldSnapshot(compiledSnapshot, dictionary, _nextApplyRevision++); foreach (string item2 in compiledSnapshot.Weapons.Keys.Where((string key) => !seenConfiguredPrefabs.Contains(key))) { if (emitMissingWarnings && SecondaryAttackManager.TryMarkCompatibilityWarningReported("missing_objectdb_prefab:" + item2)) { SecondaryAttacksPlugin.ModLogger.LogWarning((object)("Configured prefab '" + item2 + "' was not found in ObjectDB.")); } } SecondaryAttackWorldApplyContributors.AfterDefinitions(objectDb, secondaryAttackAppliedWorldSnapshot, emitMissingWarnings); SecondaryAttacksPlugin.ModLogger.LogInfo((object)$"Applied {num} secondary attack definition(s), including {num2} global ranged fallback definition(s), {num3} global blood magic fallback definition(s), {num4} global melee fallback definition(s), and {num5} effect-only definition(s)."); return secondaryAttackAppliedWorldSnapshot; } private static bool TryCreateDefaultBloodMagicFallback(string prefabName, ItemDrop itemDrop, IReadOnlyDictionary globalBloodMagicPresets, out NormalizedWeaponConfig? fallback) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 fallback = null; if (globalBloodMagicPresets.Count == 0) { return false; } SharedData val = itemDrop.m_itemData?.m_shared; Attack val2 = val?.m_attack; if (val == null || val2 == null || (int)val.m_skillType != 10) { return false; } if (IsDefaultShieldConvertWeapon(prefabName) && globalBloodMagicPresets.TryGetValue("shieldConvert", out NormalizedWeaponConfig value)) { fallback = value; return true; } if (IsDefaultSummonEmpowerWeapon(val2) && globalBloodMagicPresets.TryGetValue("summonEmpower", out NormalizedWeaponConfig value2)) { fallback = value2; return true; } return false; } private static bool IsDefaultShieldConvertWeapon(string prefabName) { return string.Equals(prefabName, "StaffShield", StringComparison.OrdinalIgnoreCase); } private 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; } 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) { //IL_0030: 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_0067: Invalid comparison between Unknown and I4 //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Invalid comparison between Unknown and I4 //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Invalid comparison between Unknown and I4 //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Invalid comparison between Unknown and I4 //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Invalid comparison between Unknown and I4 presetName = ""; SharedData val = itemDrop.m_itemData?.m_shared; Attack val2 = val?.m_attack; if (val == null || val2 == null) { return false; } if (IsAmmoItemType(val.m_itemType) || string.IsNullOrWhiteSpace(val2.m_attackAnimation)) { return false; } if (TryResolveBombPresetName(val2, out presetName)) { return !string.IsNullOrWhiteSpace(presetName); } if ((int)val.m_skillType == 9) { string a = val2.m_attackAnimation ?? ""; if (string.Equals(a, "staff_fireball", StringComparison.OrdinalIgnoreCase)) { return TryGetRangedPresetName(SecondaryAttacksPlugin.FireballStaffPreset.Value, out presetName); } if (string.Equals(a, "staff_rapidfire", StringComparison.OrdinalIgnoreCase)) { return TryGetRangedPresetName(SecondaryAttacksPlugin.RapidStaffPreset.Value, out presetName); } if (string.Equals(a, "staff_lightningshot", StringComparison.OrdinalIgnoreCase)) { return TryGetRangedPresetName(SecondaryAttacksPlugin.LightningStaffPreset.Value, out presetName); } } if ((int)val.m_skillType == 14 || (val2.m_requiresReload && (int)val2.m_attackType == 2 && !string.IsNullOrWhiteSpace(val.m_ammoType))) { return TryGetRangedPresetName(SecondaryAttacksPlugin.CrossbowPreset.Value, out presetName); } if ((int)val.m_itemType == 4 || (int)val.m_skillType == 8) { return TryGetRangedPresetName(SecondaryAttacksPlugin.BowPreset.Value, out presetName); } 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; } private static bool TryResolveBombPresetName(Attack primaryAttack, out string presetName) { presetName = ""; if (!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 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; } 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 ShouldApplyGlobalMeleeFallback(ItemDrop itemDrop, NormalizedWeaponConfig? globalMeleeFallback) { NormalizedWeaponConfig defaultMeleeFallback; return TryCreateDefaultMeleeFallback(itemDrop, globalMeleeFallback, out defaultMeleeFallback); } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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; } private 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 TryCreateDefaultMeleeFallback(ItemDrop itemDrop, NormalizedWeaponConfig? globalMeleeFallback, out NormalizedWeaponConfig? defaultMeleeFallback) { defaultMeleeFallback = null; if (globalMeleeFallback == null || !globalMeleeFallback.Enabled) { return false; } NormalizedSneakAmbushConfig? sneakAmbush = globalMeleeFallback.SneakAmbush; NormalizedSneakAmbushConfig normalizedSneakAmbushConfig = ((sneakAmbush != null && sneakAmbush.Enabled && IsDefaultSneakAmbushWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.SneakAmbush.Clone() : null); NormalizedCleavingThrustConfig? cleavingThrust = globalMeleeFallback.CleavingThrust; NormalizedCleavingThrustConfig normalizedCleavingThrustConfig = ((cleavingThrust != null && cleavingThrust.Enabled && IsDefaultCleavingThrustWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.CleavingThrust.Clone() : null); NormalizedRiftTrailConfig? riftTrail = globalMeleeFallback.RiftTrail; NormalizedRiftTrailConfig normalizedRiftTrailConfig = ((riftTrail != null && riftTrail.Enabled && IsDefaultRiftTrailWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.RiftTrail.Clone() : null); NormalizedLaunchSlamConfig? launchSlam = globalMeleeFallback.LaunchSlam; NormalizedLaunchSlamConfig normalizedLaunchSlamConfig = ((launchSlam != null && launchSlam.Enabled && IsDefaultLaunchSlamWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.LaunchSlam.Clone() : null); NormalizedKnockbackChainConfig? knockbackChain = globalMeleeFallback.KnockbackChain; NormalizedKnockbackChainConfig normalizedKnockbackChainConfig = ((knockbackChain != null && knockbackChain.Enabled && IsDefaultKnockbackChainWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.KnockbackChain.Clone() : null); NormalizedAftershockConfig? aftershock = globalMeleeFallback.Aftershock; NormalizedAftershockConfig normalizedAftershockConfig = ((aftershock != null && aftershock.Enabled && IsDefaultAftershockWeapon(itemDrop)) ? globalMeleeFallback.Aftershock.Clone() : null); NormalizedHarvestSweepConfig? harvestSweep = globalMeleeFallback.HarvestSweep; NormalizedHarvestSweepConfig normalizedHarvestSweepConfig = ((harvestSweep != null && harvestSweep.Enabled && IsDefaultHarvestSweepWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.HarvestSweep.Clone() : null); NormalizedSpinningSweepConfig? spinningSweep = globalMeleeFallback.SpinningSweep; NormalizedSpinningSweepConfig normalizedSpinningSweepConfig = ((spinningSweep != null && spinningSweep.Enabled && IsDefaultSpinningSweepWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.SpinningSweep.Clone() : null); NormalizedFractureLineConfig? fractureLine = globalMeleeFallback.FractureLine; NormalizedFractureLineConfig normalizedFractureLineConfig = ((fractureLine != null && fractureLine.Enabled && IsDefaultFractureLineWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.FractureLine.Clone() : null); NormalizedMeleeOnProjectileHitConfig? spearRain = globalMeleeFallback.SpearRain; NormalizedMeleeOnProjectileHitConfig normalizedMeleeOnProjectileHitConfig = ((spearRain != null && spearRain.Enabled && IsDefaultSpearRainWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.SpearRain.Clone() : null); NormalizedImpactBurstConfig? impactBurst = globalMeleeFallback.ImpactBurst; NormalizedImpactBurstConfig normalizedImpactBurstConfig = ((impactBurst != null && impactBurst.Enabled && IsDefaultImpactBurstWeapon(itemDrop)) ? globalMeleeFallback.ImpactBurst.Clone() : null); NormalizedBoomerangConfig? boomerang = globalMeleeFallback.Boomerang; NormalizedBoomerangConfig normalizedBoomerangConfig = ((boomerang != null && boomerang.Enabled && IsDefaultBoomerangWeapon(itemDrop.m_itemData?.m_shared)) ? globalMeleeFallback.Boomerang.Clone() : null); if (normalizedSneakAmbushConfig == null && normalizedCleavingThrustConfig == null && normalizedRiftTrailConfig == null && normalizedLaunchSlamConfig == null && normalizedKnockbackChainConfig == null && normalizedAftershockConfig == null && normalizedHarvestSweepConfig == null && normalizedFractureLineConfig == null && normalizedMeleeOnProjectileHitConfig == null && normalizedImpactBurstConfig == null && normalizedBoomerangConfig == null && normalizedSpinningSweepConfig == null) { return false; } defaultMeleeFallback = new NormalizedWeaponConfig { Secondary = CreateDefaultMeleeSecondary(globalMeleeFallback.Secondary, normalizedAftershockConfig, normalizedFractureLineConfig, normalizedMeleeOnProjectileHitConfig, normalizedImpactBurstConfig, normalizedBoomerangConfig, normalizedSpinningSweepConfig, normalizedHarvestSweepConfig), SneakAmbush = normalizedSneakAmbushConfig, CleavingThrust = normalizedCleavingThrustConfig, RiftTrail = normalizedRiftTrailConfig, LaunchSlam = normalizedLaunchSlamConfig, KnockbackChain = normalizedKnockbackChainConfig, Aftershock = normalizedAftershockConfig, HarvestSweep = normalizedHarvestSweepConfig, FractureLine = normalizedFractureLineConfig, SpearRain = normalizedMeleeOnProjectileHitConfig, ImpactBurst = normalizedImpactBurstConfig, Boomerang = normalizedBoomerangConfig, SpinningSweep = normalizedSpinningSweepConfig, MeleePreset = ResolveDefaultMeleePreset(normalizedSneakAmbushConfig, normalizedCleavingThrustConfig, normalizedRiftTrailConfig, normalizedLaunchSlamConfig, normalizedKnockbackChainConfig, normalizedAftershockConfig, normalizedFractureLineConfig, normalizedMeleeOnProjectileHitConfig, normalizedImpactBurstConfig, normalizedBoomerangConfig, normalizedSpinningSweepConfig), HasExplicitMeleePreset = false }; return true; } private static NormalizedSecondaryModeConfig? CreateDefaultMeleeSecondary(NormalizedSecondaryModeConfig? source, NormalizedAftershockConfig? aftershock, NormalizedFractureLineConfig? fractureLine, NormalizedMeleeOnProjectileHitConfig? spearRain, NormalizedImpactBurstConfig? impactBurst, NormalizedBoomerangConfig? boomerang, NormalizedSpinningSweepConfig? spinningSweep, NormalizedHarvestSweepConfig? harvestSweep) { if (aftershock != null) { return CloneAftershockSecondary(source); } if (fractureLine != null) { return CreateFractureLineSecondary(source); } if (spearRain != null) { return CreateSpearRainSecondary(source, spearRain); } if (impactBurst != null) { return CreateImpactBurstSecondary(impactBurst); } if (boomerang != null) { return CreateBoomerangSecondary(boomerang); } if (spinningSweep != null) { return CreateSpinningSweepSecondary(spinningSweep); } if (harvestSweep == null) { return null; } return CreateHarvestSweepSecondary(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 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, CopyFrom = "SpearFlint", OnProjectileHit = new NormalizedMeleeOnProjectileHitConfig { Preset = "impactBurst", Cooldown = impactBurst.Cooldown, CooldownReductionFactor = impactBurst.CooldownReductionFactor, CooldownFallback = "originalSecondary", 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 }, Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private static NormalizedSecondaryModeConfig CreateBoomerangSecondary(NormalizedBoomerangConfig boomerang) { return new NormalizedSecondaryModeConfig { Type = "copy", Animation = boomerang.Animation, ResourceMultiplier = boomerang.ResourceMultiplier, OutputMultiplier = 1f, DurabilityFactor = boomerang.DurabilityFactor, CopyFrom = "SpearFlint", Projectile = new NormalizedProjectileSecondaryConfig(), SummonEmpower = new NormalizedSummonEmpowerSecondaryConfig(), ShieldConvert = new NormalizedShieldConvertSecondaryConfig() }; } private 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, CopyFrom = "", 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 MeleeSpecialPreset ResolveDefaultMeleePreset(NormalizedSneakAmbushConfig? sneakAmbush, NormalizedCleavingThrustConfig? cleavingThrust, NormalizedRiftTrailConfig? riftTrail, NormalizedLaunchSlamConfig? launchSlam, NormalizedKnockbackChainConfig? knockbackChain, NormalizedAftershockConfig? aftershock, NormalizedFractureLineConfig? fractureLine, NormalizedMeleeOnProjectileHitConfig? spearRain, NormalizedImpactBurstConfig? impactBurst, NormalizedBoomerangConfig? boomerang, NormalizedSpinningSweepConfig? spinningSweep) { if (spearRain != null && spearRain.Enabled) { return MeleeSpecialPreset.SpearRain; } if (impactBurst != null) { return MeleeSpecialPreset.ImpactBurst; } if (boomerang != null) { return MeleeSpecialPreset.Boomerang; } if (spinningSweep != null) { return MeleeSpecialPreset.SpinningSweep; } if (cleavingThrust != null) { return MeleeSpecialPreset.CleavingThrust; } if (riftTrail != null) { return MeleeSpecialPreset.RiftTrail; } if (launchSlam != null) { return MeleeSpecialPreset.LaunchSlam; } if (knockbackChain != null) { return MeleeSpecialPreset.KnockbackChain; } if (aftershock != null) { return MeleeSpecialPreset.Aftershock; } if (fractureLine != null) { return MeleeSpecialPreset.FractureLine; } if (sneakAmbush == null) { return MeleeSpecialPreset.None; } return MeleeSpecialPreset.SneakAmbush; } private static void ApplyDefaultMeleeFallbacksIfNeeded(ItemDrop itemDrop, NormalizedWeaponConfig weaponConfig, NormalizedWeaponConfig? globalMeleeFallback) { if (!weaponConfig.Enabled || !TryCreateDefaultMeleeFallback(itemDrop, globalMeleeFallback, out NormalizedWeaponConfig defaultMeleeFallback) || defaultMeleeFallback == null) { return; } bool flag = weaponConfig.HarvestSweep == null && defaultMeleeFallback.HarvestSweep != null; NormalizedWeaponConfig normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.HarvestSweep == null) { NormalizedHarvestSweepConfig normalizedHarvestSweepConfig = (normalizedWeaponConfig.HarvestSweep = defaultMeleeFallback.HarvestSweep); } if (flag || IsDefaultHarvestSweepWeapon(itemDrop.m_itemData?.m_shared)) { NormalizedHarvestSweepConfig? harvestSweep2 = weaponConfig.HarvestSweep; if (harvestSweep2 != null && harvestSweep2.Enabled && weaponConfig.Secondary == null) { weaponConfig.Secondary = CreateHarvestSweepSecondary(weaponConfig.HarvestSweep); } } if (!weaponConfig.HasExplicitMeleePreset && weaponConfig.MeleePreset == MeleeSpecialPreset.None) { bool num = weaponConfig.SneakAmbush == null && defaultMeleeFallback.SneakAmbush != null; bool flag2 = weaponConfig.CleavingThrust == null && defaultMeleeFallback.CleavingThrust != null; bool flag3 = weaponConfig.RiftTrail == null && defaultMeleeFallback.RiftTrail != null; bool flag4 = weaponConfig.LaunchSlam == null && defaultMeleeFallback.LaunchSlam != null; bool flag5 = weaponConfig.KnockbackChain == null && defaultMeleeFallback.KnockbackChain != null; bool flag6 = weaponConfig.Aftershock == null && defaultMeleeFallback.Aftershock != null; bool flag7 = weaponConfig.FractureLine == null && defaultMeleeFallback.FractureLine != null; bool flag8 = weaponConfig.SpearRain == null && (defaultMeleeFallback.SpearRain?.Enabled ?? false); bool flag9 = weaponConfig.ImpactBurst == null && defaultMeleeFallback.ImpactBurst != null; bool flag10 = weaponConfig.Boomerang == null && defaultMeleeFallback.Boomerang != null; bool flag11 = weaponConfig.SpinningSweep == null && defaultMeleeFallback.SpinningSweep != null; normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.SneakAmbush == null) { NormalizedSneakAmbushConfig normalizedSneakAmbushConfig = (normalizedWeaponConfig.SneakAmbush = defaultMeleeFallback.SneakAmbush); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.CleavingThrust == null) { NormalizedCleavingThrustConfig normalizedCleavingThrustConfig = (normalizedWeaponConfig.CleavingThrust = defaultMeleeFallback.CleavingThrust); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.RiftTrail == null) { NormalizedRiftTrailConfig normalizedRiftTrailConfig = (normalizedWeaponConfig.RiftTrail = defaultMeleeFallback.RiftTrail); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.LaunchSlam == null) { NormalizedLaunchSlamConfig normalizedLaunchSlamConfig = (normalizedWeaponConfig.LaunchSlam = defaultMeleeFallback.LaunchSlam); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.KnockbackChain == null) { NormalizedKnockbackChainConfig normalizedKnockbackChainConfig = (normalizedWeaponConfig.KnockbackChain = defaultMeleeFallback.KnockbackChain); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.Aftershock == null) { NormalizedAftershockConfig normalizedAftershockConfig = (normalizedWeaponConfig.Aftershock = defaultMeleeFallback.Aftershock); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.FractureLine == null) { NormalizedFractureLineConfig normalizedFractureLineConfig = (normalizedWeaponConfig.FractureLine = defaultMeleeFallback.FractureLine); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.SpearRain == null) { NormalizedMeleeOnProjectileHitConfig normalizedMeleeOnProjectileHitConfig = (normalizedWeaponConfig.SpearRain = defaultMeleeFallback.SpearRain); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.ImpactBurst == null) { NormalizedImpactBurstConfig normalizedImpactBurstConfig = (normalizedWeaponConfig.ImpactBurst = defaultMeleeFallback.ImpactBurst); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.Boomerang == null) { NormalizedBoomerangConfig normalizedBoomerangConfig = (normalizedWeaponConfig.Boomerang = defaultMeleeFallback.Boomerang); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.SpinningSweep == null) { NormalizedSpinningSweepConfig normalizedSpinningSweepConfig = (normalizedWeaponConfig.SpinningSweep = defaultMeleeFallback.SpinningSweep); } normalizedWeaponConfig = weaponConfig; if (normalizedWeaponConfig.Secondary == null) { NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = (normalizedWeaponConfig.Secondary = CreateDefaultMeleeSecondary(defaultMeleeFallback.Secondary, flag6 ? defaultMeleeFallback.Aftershock : null, flag7 ? defaultMeleeFallback.FractureLine : null, flag8 ? defaultMeleeFallback.SpearRain : null, flag9 ? defaultMeleeFallback.ImpactBurst : null, flag10 ? defaultMeleeFallback.Boomerang : null, flag11 ? defaultMeleeFallback.SpinningSweep : null, null)); } if (num || flag2 || flag3 || flag4 || flag5 || flag6 || flag7 || flag8 || flag9 || flag10 || flag11 || flag) { weaponConfig.MeleePreset = defaultMeleeFallback.MeleePreset; } } } } 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 SummonPrefabOverrideSystem { private sealed class SceneState { internal int LastAppliedSnapshotId { get; set; } 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 RestoreScene(ZNetScene scene) { if (!((Object)(object)scene == (Object)null)) { RestoreOriginalSpawnPrefabs(SceneStates.GetValue(scene, (ZNetScene _) => new SceneState())); } } internal static void Apply(ZNetScene scene, IReadOnlyDictionary overrides, int snapshotId, 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) { value.LastAppliedSnapshotId = snapshotId; 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++; } } value.LastAppliedSnapshotId = snapshotId; 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(); } } } 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 (TryResolveSpawnAbilityFromAttack(val2.m_itemData.m_shared.m_attack, out spawnAbility, out targetName) || TryResolveSpawnAbilityFromAttack(val2.m_itemData.m_shared.m_secondaryAttack, 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 bool TryResolveSpawnAbilityFromAttack(Attack? attack, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = null; targetName = ""; if ((Object)(object)attack?.m_attackProjectile != (Object)null) { return TryResolveSpawnAbilityFromPrefab(attack.m_attackProjectile, out spawnAbility, out targetName); } return false; } private static bool TryResolveSpawnAbilityFromPrefab(GameObject prefab, out SpawnAbility? spawnAbility, out string targetName) { spawnAbility = prefab.GetComponent(); if ((Object)(object)spawnAbility != (Object)null) { targetName = ((Object)prefab).name; return true; } GameObject val = prefab.GetComponent()?.m_spawnOnHit; spawnAbility = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)spawnAbility != (Object)null) { targetName = ((Object)val).name; return true; } targetName = ((Object)prefab).name; return false; } 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_0085: 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; } if (summon.Health.HasValue && summon.Health.Value > 0f) { component.m_health = summon.Health.Value; } 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 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; } 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) ProjectilePrefab = projectilePrefab; Owner = owner; Weapon = weapon; Ammo = ammo; HitNoise = hitNoise; HitData = hitData; Definition = definition; Lifetime = lifetime; AoeRadiusFactor = aoeRadiusFactor; } } 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; } GameObject val3 = (((Object)(object)collider != (Object)null) ? Projectile.FindHitObject(collider) : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val3 != (Object)(object)((Component)projectile).gameObject && !val3.isStatic) { ((Component)projectile).transform.SetParent(val3.transform, true); } 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; 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_00a9: 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) 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; _detonating = true; try { component.OnHit((Collider)null, state.HitPoint, false, state.HitNormal); } finally { _detonating = false; } } 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 (!WeaponEffectManager.IsApplyingGeneratedEffectDamage && !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; } } internal static class WeaponEffectDefinitionCompiler { internal static bool HasPrefabAssignment(EffectBehaviorConfig effectConfig, string prefabName) { if (effectConfig.Prefabs == null || string.IsNullOrWhiteSpace(prefabName)) { return false; } return effectConfig.Prefabs.Keys.Any((string configuredPrefabName) => string.Equals(configuredPrefabName?.Trim(), prefabName, StringComparison.OrdinalIgnoreCase)); } internal static bool TryResolveForPrefab(string effectId, EffectBehaviorConfig effectConfig, string prefabName, out ConfiguredWeaponEffectDefinition? definition, out string reason) { definition = null; reason = ""; if (!TryGetPrefabOverride(effectConfig, prefabName, out EffectBehaviorOverrideConfig prefabOverride)) { return false; } bool num = HasOverrideValues(prefabOverride); string effectId2 = (num ? (effectId + "_" + prefabName) : effectId); EffectBehaviorConfig effectConfig2 = (num ? MergePrefabOverride(effectConfig, prefabOverride) : effectConfig); if (!TryResolve(effectId2, effectConfig2, out definition, out reason)) { return false; } definition.Id = effectId; return true; } internal static bool TryResolve(string effectId, EffectBehaviorConfig effectConfig, out ConfiguredWeaponEffectDefinition? definition, out string reason) { //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) definition = null; reason = ""; string text = (string.IsNullOrWhiteSpace(effectConfig.Type) ? effectId : effectConfig.Type); if (!TryParseWeaponEffectType(text, out var effectType)) { reason = "unknown effect type '" + text + "'."; return false; } if (!TryParseWeaponEffectTrigger(effectConfig.Trigger, out var trigger)) { reason = "unknown trigger '" + effectConfig.Trigger + "'."; return false; } string text2 = (string.IsNullOrWhiteSpace(effectConfig.DamageType) ? GetDefaultDamageType(effectType) : effectConfig.DamageType); if (!TryParseHitDamageType(text2, out var damageType)) { reason = "unknown damageType '" + text2 + "'."; return false; } if (!TryParseDamageModifier(effectConfig.Modifier, out var modifier)) { reason = "unknown modifier '" + effectConfig.Modifier + "'."; return false; } if (!TryParseScalarValueMode(effectConfig.Damage.Mode, out var mode)) { reason = "unknown damage mode '" + effectConfig.Damage.Mode + "'."; return false; } if (!TryParseScalarValueMode(effectConfig.Heal.Mode, out var mode2)) { reason = "unknown heal mode '" + effectConfig.Heal.Mode + "'."; return false; } if (!TryParseScalarValueMode(effectConfig.StaminaRestore.Mode, out var mode3)) { reason = "unknown staminaRestore mode '" + effectConfig.StaminaRestore.Mode + "'."; return false; } definition = new ConfiguredWeaponEffectDefinition { Id = effectId, StatusEffectName = (UsesRuntimeStatusEffect(effectType) ? GetRuntimeEffectStatusName(effectId, effectType) : ""), Type = effectType, Trigger = trigger, StacksRequired = Mathf.Max(1, effectConfig.StacksRequired ?? GetDefaultStacksRequired(effectType)), StackWindow = ResolveStackWindow(effectConfig, effectType), Duration = ResolveDuration(effectConfig, effectType), TickInterval = Mathf.Max(0.01f, effectConfig.TickInterval ?? 1f), ProcChance = Mathf.Clamp(effectConfig.ProcChance, 0f, 100f), DamageType = damageType, Modifier = modifier, DamageMode = mode, DamageValue = Mathf.Max(0f, effectConfig.Damage.Value), ValuePercent = Mathf.Max(0f, effectConfig.Value.HasValue ? ((float)effectConfig.Value.Value) : GetDefaultValuePercent(effectType)), HealMode = mode2, HealValue = Mathf.Max(0f, effectConfig.Heal.Value), StaminaRestoreMode = mode3, StaminaRestoreValue = Mathf.Max(0f, effectConfig.StaminaRestore.Value), MoveSpeedMultiplier = ResolveMoveSpeedMultiplier(effectConfig, effectType), HealthThresholdPercent = Mathf.Clamp(effectConfig.HealthThresholdPercent, 0f, 100f), DamageMultiplier = Mathf.Max(0f, effectConfig.DamageMultiplier), ConsumeOnModify = effectConfig.ConsumeOnModify }; return true; } private static bool TryGetPrefabOverride(EffectBehaviorConfig effectConfig, string prefabName, out EffectBehaviorOverrideConfig? prefabOverride) { prefabOverride = null; if (effectConfig.Prefabs == null) { return false; } foreach (var (text2, effectBehaviorOverrideConfig2) in effectConfig.Prefabs) { if (string.Equals(text2?.Trim(), prefabName, StringComparison.OrdinalIgnoreCase)) { prefabOverride = effectBehaviorOverrideConfig2 ?? new EffectBehaviorOverrideConfig(); return true; } } return false; } private static bool HasOverrideValues(EffectBehaviorOverrideConfig? prefabOverride) { if (prefabOverride != null) { if (string.IsNullOrWhiteSpace(prefabOverride.Type) && !prefabOverride.Value.HasValue && string.IsNullOrWhiteSpace(prefabOverride.Trigger) && !prefabOverride.StacksRequired.HasValue && !prefabOverride.StackWindow.HasValue && !prefabOverride.Duration.HasValue && !prefabOverride.TickInterval.HasValue && !prefabOverride.DamageFactor.HasValue && !prefabOverride.ProcChance.HasValue && string.IsNullOrWhiteSpace(prefabOverride.DamageType) && string.IsNullOrWhiteSpace(prefabOverride.Modifier) && !HasScalarOverrideValues(prefabOverride.Damage) && !HasScalarOverrideValues(prefabOverride.Heal) && !HasScalarOverrideValues(prefabOverride.StaminaRestore) && !prefabOverride.MoveSpeedMultiplier.HasValue && !prefabOverride.HealthThresholdPercent.HasValue && !prefabOverride.DamageMultiplier.HasValue) { return prefabOverride.ConsumeOnModify.HasValue; } return true; } return false; } private static bool HasScalarOverrideValues(ScalarValueOverrideConfig? overrideValue) { if (overrideValue != null) { if (string.IsNullOrWhiteSpace(overrideValue.Mode)) { return overrideValue.Value.HasValue; } return true; } return false; } private static EffectBehaviorConfig MergePrefabOverride(EffectBehaviorConfig baseConfig, EffectBehaviorOverrideConfig prefabOverride) { return new EffectBehaviorConfig { Type = ((!string.IsNullOrWhiteSpace(prefabOverride.Type)) ? prefabOverride.Type : baseConfig.Type), Value = (prefabOverride.Value ?? baseConfig.Value), Trigger = ((!string.IsNullOrWhiteSpace(prefabOverride.Trigger)) ? prefabOverride.Trigger : baseConfig.Trigger), StacksRequired = (prefabOverride.StacksRequired ?? baseConfig.StacksRequired), StackWindow = (prefabOverride.StackWindow ?? baseConfig.StackWindow), Duration = (prefabOverride.Duration ?? baseConfig.Duration), TickInterval = (prefabOverride.TickInterval ?? baseConfig.TickInterval), DamageFactor = (prefabOverride.DamageFactor ?? baseConfig.DamageFactor), ProcChance = (prefabOverride.ProcChance ?? baseConfig.ProcChance), DamageType = ((!string.IsNullOrWhiteSpace(prefabOverride.DamageType)) ? prefabOverride.DamageType : baseConfig.DamageType), Modifier = ((!string.IsNullOrWhiteSpace(prefabOverride.Modifier)) ? prefabOverride.Modifier : baseConfig.Modifier), Damage = MergeScalarValue(baseConfig.Damage, prefabOverride.Damage), Heal = MergeScalarValue(baseConfig.Heal, prefabOverride.Heal), StaminaRestore = MergeScalarValue(baseConfig.StaminaRestore, prefabOverride.StaminaRestore), MoveSpeedMultiplier = (prefabOverride.MoveSpeedMultiplier ?? baseConfig.MoveSpeedMultiplier), HealthThresholdPercent = (prefabOverride.HealthThresholdPercent ?? baseConfig.HealthThresholdPercent), DamageMultiplier = (prefabOverride.DamageMultiplier ?? baseConfig.DamageMultiplier), ConsumeOnModify = (prefabOverride.ConsumeOnModify ?? baseConfig.ConsumeOnModify) }; } private static ScalarValueConfig MergeScalarValue(ScalarValueConfig baseValue, ScalarValueOverrideConfig? overrideValue) { if (overrideValue == null) { return new ScalarValueConfig { Mode = baseValue.Mode, Value = baseValue.Value }; } return new ScalarValueConfig { Mode = ((!string.IsNullOrWhiteSpace(overrideValue.Mode)) ? overrideValue.Mode : baseValue.Mode), Value = (overrideValue.Value ?? baseValue.Value) }; } private static bool UsesRuntimeStatusEffect(WeaponEffectType effectType) { if ((uint)effectType <= 1u || effectType == WeaponEffectType.Haste) { return true; } return false; } private static string GetRuntimeEffectStatusName(string effectId, WeaponEffectType effectType) { return "SecondaryAttacks_" + SanitizeInternalId(effectId) + "_" + SanitizeInternalId(effectType.ToString()); } private static bool TryParseWeaponEffectType(string effectTypeText, out WeaponEffectType effectType) { switch (effectTypeText.Trim().ToLowerInvariant()) { case "dot": effectType = WeaponEffectType.Dot; return true; case "resistanceShred": case "resistanceshred": effectType = WeaponEffectType.ResistanceShred; return true; case "adrenaline": effectType = WeaponEffectType.Adrenaline; return true; case "haste": effectType = WeaponEffectType.Haste; return true; case "vampirism": effectType = WeaponEffectType.Vampirism; return true; case "execute": effectType = WeaponEffectType.Execute; return true; case "executioner": effectType = WeaponEffectType.Executioner; return true; case "staggerChance": case "staggerchance": effectType = WeaponEffectType.StaggerChance; return true; case "burstDamage": case "burstdamage": effectType = WeaponEffectType.BurstDamage; return true; case "bleeding": effectType = WeaponEffectType.Bleeding; return true; case "bash": effectType = WeaponEffectType.Bash; return true; case "piercing": effectType = WeaponEffectType.Piercing; return true; case "decapitator": effectType = WeaponEffectType.Decapitator; return true; case "smasher": effectType = WeaponEffectType.Smasher; return true; case "juggernaut": effectType = WeaponEffectType.Juggernaut; return true; default: effectType = WeaponEffectType.Dot; return false; } } private static int GetDefaultStacksRequired(WeaponEffectType effectType) { return effectType switch { WeaponEffectType.Adrenaline => 5, WeaponEffectType.Haste => 6, WeaponEffectType.Vampirism => 5, WeaponEffectType.Bleeding => 4, WeaponEffectType.Bash => 4, WeaponEffectType.Piercing => 4, WeaponEffectType.Decapitator => 4, WeaponEffectType.Smasher => 4, WeaponEffectType.Juggernaut => 4, _ => 1, }; } private static float ResolveStackWindow(EffectBehaviorConfig effectConfig, WeaponEffectType effectType) { float num = Mathf.Max(0f, effectConfig.StackWindow); if (num > 0f) { return num; } if (GetDefaultStacksRequired(effectType) <= 1) { return 0f; } return 10f; } private static float ResolveDuration(EffectBehaviorConfig effectConfig, WeaponEffectType effectType) { if (effectConfig.Duration.HasValue) { return Mathf.Max(0f, effectConfig.Duration.Value); } return effectType switch { WeaponEffectType.Haste => 6f, WeaponEffectType.Bleeding => 4f, _ => 0f, }; } private static float GetDefaultValuePercent(WeaponEffectType effectType) { return effectType switch { WeaponEffectType.Adrenaline => 10f, WeaponEffectType.Haste => 20f, WeaponEffectType.Vampirism => 10f, WeaponEffectType.Bleeding => 50f, WeaponEffectType.Bash => 50f, WeaponEffectType.Piercing => 50f, WeaponEffectType.Executioner => 50f, _ => 0f, }; } private static float ResolveMoveSpeedMultiplier(EffectBehaviorConfig effectConfig, WeaponEffectType effectType) { if (effectType == WeaponEffectType.Haste && Mathf.Approximately(effectConfig.MoveSpeedMultiplier, 1f)) { float num = Mathf.Max(0f, effectConfig.Value.HasValue ? ((float)effectConfig.Value.Value) : GetDefaultValuePercent(effectType)); return 1f + num * 0.01f; } return Mathf.Max(0f, effectConfig.MoveSpeedMultiplier); } private static string GetDefaultDamageType(WeaponEffectType effectType) { return effectType switch { WeaponEffectType.Bleeding => "slash", WeaponEffectType.Piercing => "pierce", WeaponEffectType.Decapitator => "slash", WeaponEffectType.Smasher => "blunt", WeaponEffectType.Juggernaut => "pierce", _ => "", }; } private static bool TryParseWeaponEffectTrigger(string triggerText, out WeaponEffectTrigger trigger) { switch (triggerText.Trim()) { case "anyHit": trigger = WeaponEffectTrigger.AnyHit; return true; case "mainHit": trigger = WeaponEffectTrigger.MainHit; return true; case "secondaryHit": trigger = WeaponEffectTrigger.SecondaryHit; return true; default: trigger = WeaponEffectTrigger.AnyHit; return false; } } private static bool TryParseScalarValueMode(string modeText, out ScalarValueMode mode) { switch (modeText.Trim().ToLowerInvariant()) { case "fixed": mode = ScalarValueMode.Fixed; return true; case "targetmaxhealthpercent": mode = ScalarValueMode.TargetMaxHealthPercent; return true; case "selfmaxhealthpercent": mode = ScalarValueMode.SelfMaxHealthPercent; return true; case "selfmaxstaminapercent": mode = ScalarValueMode.SelfMaxStaminaPercent; return true; default: mode = ScalarValueMode.Fixed; return false; } } private static bool TryParseHitDamageType(string damageTypeText, out DamageType damageType) { switch (damageTypeText.Trim()) { case "damage": case "": damageType = (DamageType)1024; return true; case "blunt": damageType = (DamageType)1; return true; case "slash": damageType = (DamageType)2; return true; case "pierce": damageType = (DamageType)4; return true; case "chop": damageType = (DamageType)8; return true; case "pickaxe": damageType = (DamageType)16; return true; case "fire": damageType = (DamageType)32; return true; case "frost": damageType = (DamageType)64; return true; case "lightning": damageType = (DamageType)128; return true; case "poison": damageType = (DamageType)256; return true; case "spirit": damageType = (DamageType)512; return true; default: damageType = (DamageType)0; return false; } } private static bool TryParseDamageModifier(string modifierText, out DamageModifier modifier) { switch (modifierText.Trim()) { case "normal": modifier = (DamageModifier)0; return true; case "slightlyresistant": modifier = (DamageModifier)7; return true; case "resistant": modifier = (DamageModifier)1; return true; case "veryresistant": modifier = (DamageModifier)5; return true; case "slightlyweak": modifier = (DamageModifier)8; return true; case "weak": modifier = (DamageModifier)2; return true; case "veryweak": modifier = (DamageModifier)6; return true; case "immune": modifier = (DamageModifier)3; return true; case "ignore": modifier = (DamageModifier)4; return true; default: modifier = (DamageModifier)0; return false; } } private static string SanitizeInternalId(string? value) { if (!string.IsNullOrWhiteSpace(value)) { return new string(value.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant(); } return string.Empty; } } internal static class WeaponEffectManager { private sealed class EffectStackState { public int Count { get; set; } public float ExpirationTime { get; set; } } private const string GeneratedStatusPrefix = "SecondaryAttacks_"; private static readonly ConditionalWeakTable> StackStates = new ConditionalWeakTable>(); private static bool _isApplyingEffectDamage; internal static bool IsApplyingGeneratedEffectDamage => _isApplyingEffectDamage; public static void ApplyToObjectDb(ObjectDB objectDb, IReadOnlyDictionary definitions, IReadOnlyDictionary effectConfigs) { RemoveGeneratedStatuses(objectDb); MeleePresetCooldownSystem.RegisterStatusEffects(objectDb); SneakAmbushChargeSystem.RegisterStatusEffect(objectDb); RangedSecondaryCooldownSystem.RegisterStatusEffect(objectDb); RegisterRuntimeStatuses(objectDb, definitions.Values); } public static WeaponEffectDamageState? TryHandleCharacterDamagePrefix(Character target, ref HitData hit) { //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_0042: Expected O, but got Unknown //IL_0042: Expected O, but got Unknown if (_isApplyingEffectDamage || LaunchSlamSystem.IsApplyingLandingDamage || KnockbackChainSystem.IsApplyingChainDamage || MeleeProjectileHitCascadeSystem.IsApplyingImpactBurstDamage) { return null; } if (!DirectWeaponHitContextSystem.ShouldCountWeaponEffectHit) { return null; } if ((Object)hit.GetAttacker() != (Object)Player.m_localPlayer) { return null; } if (!TryGetEffectiveAttackContext(out Player localPlayer, out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition definition)) { return null; } if (definition?.SneakAmbush != null) { SneakAmbushSystem.TryTriggerForSecondaryHit(localPlayer, target, secondaryAttack, definition); } List list = null; if (definition != null) { WeaponEffectRuntimeCache effectRuntimeCache = definition.EffectRuntimeCache; ConfiguredWeaponEffectDefinition[] immediateEffects = effectRuntimeCache.GetImmediateEffects(secondaryAttack); foreach (ConfiguredWeaponEffectDefinition effect in immediateEffects) { ApplyConfiguredEffect(localPlayer, target, weaponPrefabName, effect, ref hit); } ConfiguredWeaponEffectDefinition[] postDamageEffects = effectRuntimeCache.GetPostDamageEffects(secondaryAttack); immediateEffects = postDamageEffects; foreach (ConfiguredWeaponEffectDefinition configuredWeaponEffectDefinition in immediateEffects) { if (TryPreparePostDamageEffect(localPlayer, target, weaponPrefabName, configuredWeaponEffectDefinition)) { if (list == null) { list = new List(postDamageEffects.Length); } list.Add(configuredWeaponEffectDefinition); } } } if (!KnockbackChainSystem.TryApplyForSecondaryHit(localPlayer, target, secondaryAttack, definition, ref hit)) { LaunchSlamSystem.TryApplyForSecondaryHit(localPlayer, target, secondaryAttack, definition, ref hit); } if (list == null || list.Count <= 0) { return null; } return new WeaponEffectDamageState(localPlayer, target, hit.Clone(), target.GetHealth(), list); } public static void TryHandleCharacterDamagePostfix(WeaponEffectDamageState? state) { if (state == null || (Object)(object)state.Target == (Object)null || (Object)(object)state.Attacker == (Object)null) { return; } float num = Mathf.Max(0f, state.HealthBefore - state.Target.GetHealth()); if (num <= 0f) { return; } foreach (ConfiguredWeaponEffectDefinition effect in state.Effects) { ApplyPostDamageEffect(state.Attacker, state.Target, state.SourceHit, effect, num); } } internal static float ResolveScalarValue(ScalarValueMode mode, float value, Character self, Character target) { switch (mode) { case ScalarValueMode.Fixed: return value; case ScalarValueMode.TargetMaxHealthPercent: return target.GetMaxHealth() * value * 0.01f; case ScalarValueMode.SelfMaxHealthPercent: return self.GetMaxHealth() * value * 0.01f; case ScalarValueMode.SelfMaxStaminaPercent: { Player val = (Player)(object)((self is Player) ? self : null); return (val != null) ? (((Character)val).GetMaxStamina() * value * 0.01f) : 0f; } default: return value; } } internal static void SetDamageValue(ref DamageTypes damageTypes, DamageType damageType, float value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0009: 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: Expected I4, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)damageType <= 32) { if ((int)damageType <= 8) { switch (damageType - 1) { default: if ((int)damageType != 8) { break; } damageTypes.m_chop = value; return; case 0: damageTypes.m_blunt = value; return; case 1: damageTypes.m_slash = value; return; case 3: damageTypes.m_pierce = value; return; case 2: break; } } else { if ((int)damageType == 16) { damageTypes.m_pickaxe = value; return; } if ((int)damageType == 32) { damageTypes.m_fire = value; return; } } } else if ((int)damageType <= 128) { if ((int)damageType == 64) { damageTypes.m_frost = value; return; } if ((int)damageType == 128) { damageTypes.m_lightning = value; return; } } else { if ((int)damageType == 256) { damageTypes.m_poison = value; return; } if ((int)damageType == 512) { damageTypes.m_spirit = value; return; } } damageTypes.m_damage = value; } private static bool TryGetEffectiveAttackContext(out Player localPlayer, out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition? definition) { localPlayer = Player.m_localPlayer; weaponPrefabName = string.Empty; secondaryAttack = false; definition = null; if ((Object)(object)localPlayer == (Object)null) { return false; } if (SecondaryAttackRuntimeFacade.TryGetProjectileHitAttackContext(out weaponPrefabName, out secondaryAttack, out definition, out var disableCurrentAttackFallback)) { return true; } if (disableCurrentAttackFallback) { return false; } return TryGetCurrentAttackContext(out localPlayer, out weaponPrefabName, out secondaryAttack, out definition); } private static bool TryGetCurrentAttackContext(out Player localPlayer, out string weaponPrefabName, out bool secondaryAttack, out SecondaryAttackDefinition? definition) { localPlayer = Player.m_localPlayer; weaponPrefabName = string.Empty; secondaryAttack = false; definition = null; if ((Object)(object)localPlayer == (Object)null) { return false; } Attack currentAttack = ((Humanoid)localPlayer).m_currentAttack; if ((Object)(object)currentAttack?.m_weapon?.m_dropPrefab == (Object)null) { return false; } weaponPrefabName = ((Object)currentAttack.m_weapon.m_dropPrefab).name; secondaryAttack = ((Humanoid)localPlayer).m_currentAttackIsSecondary; SecondaryAttackRuntimeFacade.TryGetDefinition(currentAttack.m_weapon, out definition); return true; } private static void RemoveGeneratedStatuses(ObjectDB objectDb) { objectDb.m_StatusEffects.RemoveAll((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name.StartsWith("SecondaryAttacks_", StringComparison.Ordinal)); foreach (GameObject item in objectDb.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent() : null); StatusEffect val2 = val?.m_itemData?.m_shared?.m_equipStatusEffect; if ((Object)(object)val2 != (Object)null && ((Object)val2).name.StartsWith("SecondaryAttacks_equip_", StringComparison.Ordinal)) { val.m_itemData.m_shared.m_equipStatusEffect = null; } } } private static void RegisterRuntimeStatuses(ObjectDB objectDb, IEnumerable definitions) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (ConfiguredWeaponEffectDefinition item in from effect in definitions.SelectMany((SecondaryAttackDefinition definition) => definition.ConfiguredEffects) where !string.IsNullOrWhiteSpace(effect.StatusEffectName) select effect) { dictionary.TryAdd(item.StatusEffectName, item); } foreach (KeyValuePair item2 in dictionary) { item2.Deconstruct(out var _, out var value); StatusEffect val = CreateRuntimeStatusEffect(value); if ((Object)(object)val != (Object)null) { objectDb.m_StatusEffects.Add(val); } } } private static void ApplyConfiguredEquipStatuses(ObjectDB objectDb, IReadOnlyDictionary definitions) { foreach (GameObject item in objectDb.m_items) { if (!((Object)(object)item == (Object)null)) { ItemDrop component = item.GetComponent(); if (!((Object)(object)component == (Object)null) && definitions.TryGetValue(((Object)item).name, out SecondaryAttackDefinition value) && value.ConfiguredEffects.Count != 0 && !((Object)(object)component.m_itemData.m_shared.m_equipStatusEffect != (Object)null)) { component.m_itemData.m_shared.m_equipStatusEffect = CreateEquipStatusEffect(((Object)item).name, value); } } } } private static StatusEffect? CreateRuntimeStatusEffect(ConfiguredWeaponEffectDefinition effect) { return (StatusEffect?)(effect.Type switch { WeaponEffectType.Dot => CreateConfiguredStatusEffect(effect), WeaponEffectType.ResistanceShred => CreateConfiguredStatusEffect(effect), WeaponEffectType.Haste => CreateConfiguredStatusEffect(effect), _ => null, }); } private static T CreateConfiguredStatusEffect(ConfiguredWeaponEffectDefinition effect) where T : ConfiguredRuntimeStatusEffectBase { T val = ScriptableObject.CreateInstance(); val.Initialize(effect); return val; } private static StatusEffect CreateEquipStatusEffect(string weaponPrefabName, SecondaryAttackDefinition definition) { ConfiguredEquipStatusEffect configuredEquipStatusEffect = ScriptableObject.CreateInstance(); configuredEquipStatusEffect.Initialize(label: string.Join(", ", definition.ConfiguredEffects.Select((ConfiguredWeaponEffectDefinition effect) => effect.Id)), tooltip: string.Join("\n", definition.ConfiguredEffects.Select((ConfiguredWeaponEffectDefinition effect) => effect.Id)), statusName: "SecondaryAttacks_equip_" + NormalizeKey(weaponPrefabName)); return (StatusEffect)(object)configuredEquipStatusEffect; } private static string NormalizeKey(string? value) { if (!string.IsNullOrWhiteSpace(value)) { return new string(value.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant(); } return string.Empty; } private static void ApplyConfiguredEffect(Player attacker, Character target, string weaponPrefabName, ConfiguredWeaponEffectDefinition effect, ref HitData hit) { switch (effect.Type) { case WeaponEffectType.Execute: ApplyExecuteEffect(target, effect, ref hit); return; case WeaponEffectType.StaggerChance: ApplyStaggerChanceEffect(target, effect); return; } if (TryConsumeEffectStack(target, effect, weaponPrefabName) && RollProc(effect.ProcChance)) { switch (effect.Type) { case WeaponEffectType.Adrenaline: ((Character)attacker).AddStamina(ResolveScalarValue(effect.StaminaRestoreMode, effect.StaminaRestoreValue, (Character)(object)attacker, target)); break; case WeaponEffectType.Haste: ((Character)attacker).m_seman.AddStatusEffect(StringExtensionMethods.GetStableHashCode(effect.StatusEffectName), true, 0, 0f); break; case WeaponEffectType.Vampirism: ((Character)attacker).Heal(ResolveScalarValue(effect.HealMode, effect.HealValue, (Character)(object)attacker, target), true); break; case WeaponEffectType.Dot: target.m_seman.AddStatusEffect(StringExtensionMethods.GetStableHashCode(effect.StatusEffectName), true, 0, 0f); break; case WeaponEffectType.ResistanceShred: target.m_seman.AddStatusEffect(StringExtensionMethods.GetStableHashCode(effect.StatusEffectName), true, 0, 0f); break; case WeaponEffectType.BurstDamage: ApplyBurstDamage(target, effect); break; case WeaponEffectType.Execute: case WeaponEffectType.StaggerChance: break; } } } private static void ApplyExecuteEffect(Character target, ConfiguredWeaponEffectDefinition effect, ref HitData hit) { float maxHealth = target.GetMaxHealth(); if (!(maxHealth <= 0f) && !(target.GetHealth() / maxHealth > effect.HealthThresholdPercent * 0.01f) && RollProc(effect.ProcChance)) { hit.ApplyModifier(effect.DamageMultiplier); } } private static void ApplyStaggerChanceEffect(Character target, ConfiguredWeaponEffectDefinition effect) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (RollProc(effect.ProcChance)) { target.Stagger(Vector3.zero); } } private static void ApplyBurstDamage(Character target, ConfiguredWeaponEffectDefinition effect) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_002a: 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) float num = ResolveScalarValue(effect.DamageMode, effect.DamageValue, target, target); if (!(num <= 0f)) { HitData val = new HitData(); SetDamageValue(ref val.m_damage, effect.DamageType, num); val.m_point = ((Component)target).transform.position; target.Damage(val); } } private static bool TryPreparePostDamageEffect(Player attacker, Character target, string weaponPrefabName, ConfiguredWeaponEffectDefinition effect) { if (effect.Type == WeaponEffectType.Executioner && !IsTargetBelowExecutionThreshold(target, effect)) { return false; } if (TryConsumeEffectStack(target, effect, weaponPrefabName)) { return RollProc(effect.ProcChance); } return false; } private static bool IsTargetBelowExecutionThreshold(Character target, ConfiguredWeaponEffectDefinition effect) { float maxHealth = target.GetMaxHealth(); if (maxHealth > 0f) { return target.GetHealth() / maxHealth <= effect.HealthThresholdPercent * 0.01f; } return false; } private static void ApplyPostDamageEffect(Player attacker, Character target, HitData sourceHit, ConfiguredWeaponEffectDefinition effect, float actualDamage) { //IL_00c5: 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) switch (effect.Type) { case WeaponEffectType.Adrenaline: ((Character)attacker).AddStamina(actualDamage * effect.ValuePercent * 0.01f); break; case WeaponEffectType.Haste: ((Character)attacker).m_seman.AddStatusEffect(StringExtensionMethods.GetStableHashCode(effect.StatusEffectName), true, 0, 0f); break; case WeaponEffectType.Vampirism: ((Character)attacker).Heal(actualDamage * effect.ValuePercent * 0.01f, true); break; case WeaponEffectType.Bleeding: WeaponEffectBleedingController.Apply(target, attacker, sourceHit, effect, actualDamage * effect.ValuePercent * 0.01f); break; case WeaponEffectType.Bash: ApplyBashStagger(attacker, target, sourceHit, actualDamage * effect.ValuePercent * 0.01f); break; case WeaponEffectType.Piercing: ApplyEffectDamage(attacker, target, sourceHit, effect.DamageType, actualDamage * effect.ValuePercent * 0.01f); break; case WeaponEffectType.Executioner: ApplyEffectDamage(attacker, target, sourceHit, effect.DamageType, actualDamage * effect.ValuePercent * 0.01f); break; case WeaponEffectType.Decapitator: case WeaponEffectType.Smasher: case WeaponEffectType.Juggernaut: ApplyWeaknessBonusDamage(attacker, target, sourceHit, effect, actualDamage); break; case WeaponEffectType.Execute: case WeaponEffectType.StaggerChance: case WeaponEffectType.BurstDamage: break; } } internal static void ApplyEffectDamage(Player attacker, Character target, HitData sourceHit, DamageType damageType, float damage) { //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_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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //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_003a: 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_0050: 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_005e: 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_0070: 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_007d: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) if (damage <= 0f || target.IsDead()) { return; } HitData val = new HitData { m_point = sourceHit.m_point, m_dir = sourceHit.m_dir, m_pushForce = 0f, m_backstabBonus = 1f, m_staggerMultiplier = 0f, m_dodgeable = false, m_blockable = false, m_skill = (SkillType)0, m_skillRaiseAmount = 0f, m_hitType = sourceHit.m_hitType }; val.SetAttacker((Character)(object)attacker); SetDamageValue(ref val.m_damage, damageType, damage); _isApplyingEffectDamage = true; try { target.Damage(val); } finally { _isApplyingEffectDamage = false; } } private static void ApplyBashStagger(Player attacker, Character target, HitData sourceHit, float staggerDamage) { //IL_001e: 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_0040: Unknown result type (might be due to invalid IL or missing references) if (!(staggerDamage <= 0f) && !target.IsDead()) { HitData val = sourceHit.Clone(); val.m_damage = default(DamageTypes); val.m_skill = (SkillType)0; val.m_skillRaiseAmount = 0f; val.SetAttacker((Character)(object)attacker); target.AddStaggerDamage(staggerDamage, sourceHit.m_dir, val); } } private static void ApplyWeaknessBonusDamage(Player attacker, Character target, HitData sourceHit, ConfiguredWeaponEffectDefinition effect, float actualDamage) { //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_0039: Unknown result type (might be due to invalid IL or missing references) float damageModifierMultiplier = GetDamageModifierMultiplier(target.GetDamageModifier(effect.DamageType)); float damageModifierMultiplier2 = GetDamageModifierMultiplier((DamageModifier)2); if (!(damageModifierMultiplier <= 0f) && !(damageModifierMultiplier >= damageModifierMultiplier2)) { float damage = actualDamage * (damageModifierMultiplier2 / damageModifierMultiplier - 1f) / damageModifierMultiplier; ApplyEffectDamage(attacker, target, sourceHit, effect.DamageType, damage); } } private static float GetDamageModifierMultiplier(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown return (int)modifier switch { 3 => 0f, 5 => 0.25f, 1 => 0.5f, 7 => 0.75f, 0 => 1f, 4 => 1f, 8 => 1.5f, 2 => 2f, 6 => 4f, _ => 1f, }; } private static bool TryConsumeEffectStack(Character recipient, ConfiguredWeaponEffectDefinition effect, string weaponPrefabName) { if (effect.StacksRequired <= 1) { return true; } string key = weaponPrefabName + ":" + effect.Id; Dictionary orCreateValue = StackStates.GetOrCreateValue(recipient); if (!orCreateValue.TryGetValue(key, out var value) || (value.ExpirationTime > 0f && Time.time > value.ExpirationTime)) { value = new EffectStackState(); } value.Count++; value.ExpirationTime = ((effect.StackWindow > 0f) ? (Time.time + effect.StackWindow) : 0f); if (value.Count < effect.StacksRequired) { orCreateValue[key] = value; return false; } orCreateValue.Remove(key); return true; } private static bool RollProc(float procChance) { if (!(procChance >= 100f)) { return Random.Range(0f, 100f) < procChance; } return true; } internal static void AppendConfiguredEffectTooltip(ItemData item, ref string tooltip) { if ((Object)(object)item?.m_dropPrefab == (Object)null || string.IsNullOrWhiteSpace(((Object)item.m_dropPrefab).name) || !SecondaryAttackFacade.CurrentAppliedWorldSnapshot.DefinitionsByPrefabName.TryGetValue(((Object)item.m_dropPrefab).name, out SecondaryAttackDefinition value) || value.ConfiguredEffects.Count == 0) { return; } List list = new List(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ConfiguredWeaponEffectDefinition configuredEffect in value.ConfiguredEffects) { if (hashSet.Add(configuredEffect.Id)) { string text = BuildTooltipLine(configuredEffect); if (!string.IsNullOrWhiteSpace(text)) { list.Add(text); } } } if (list.Count != 0) { string text2 = string.Join("\n", list); tooltip = (string.IsNullOrWhiteSpace(tooltip) ? text2 : (tooltip + "\n\n" + text2)); } } private static string BuildTooltipLine(ConfiguredWeaponEffectDefinition effect) { //IL_0158: Unknown result type (might be due to invalid IL or missing references) string text = ((effect.StacksRequired <= 1) ? "Hits" : $"Every {effect.StacksRequired} hits on the same target"); string text2 = FormatPercent(effect.ValuePercent); string text3 = FormatSeconds(effect.Duration); return effect.Type switch { WeaponEffectType.Adrenaline => text + " restore " + text2 + " damage as stamina.", WeaponEffectType.Haste => text + " grant +" + text2 + " movement speed for " + text3 + ".", WeaponEffectType.Vampirism => text + " restore " + text2 + " damage as health.", WeaponEffectType.Bleeding => text + " deal " + text2 + " damage as bleeding over " + text3 + ".", WeaponEffectType.Bash => text + " deal " + text2 + " damage as stagger.", WeaponEffectType.Piercing => text + " deal " + text2 + " bonus " + FormatDamageType(effect.DamageType) + " damage.", WeaponEffectType.Executioner => "Hits against targets below " + FormatPercent(effect.HealthThresholdPercent) + " health deal " + text2 + " bonus damage.", WeaponEffectType.Decapitator => text + " deal bonus slash damage.", WeaponEffectType.Smasher => text + " deal bonus blunt damage.", WeaponEffectType.Juggernaut => text + " deal bonus pierce damage.", _ => "", }; } private static string FormatPercent(float value) { if (!Mathf.Approximately(value, Mathf.Round(value))) { return $"{value:0.##}%"; } return $"{Mathf.RoundToInt(value)}%"; } private static string FormatSeconds(float value) { if (!Mathf.Approximately(value, Mathf.Round(value))) { return $"{value:0.##}s"; } return $"{Mathf.RoundToInt(value)}s"; } private static string FormatDamageType(DamageType damageType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0009: 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: Expected I4, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)damageType <= 32) { if ((int)damageType <= 8) { switch (damageType - 1) { default: if ((int)damageType != 8) { break; } return "chop"; case 0: return "blunt"; case 1: return "slash"; case 3: return "pierce"; case 2: break; } } else { if ((int)damageType == 16) { return "pickaxe"; } if ((int)damageType == 32) { return "fire"; } } } else if ((int)damageType <= 128) { if ((int)damageType == 64) { return "frost"; } if ((int)damageType == 128) { return "lightning"; } } else { if ((int)damageType == 256) { return "poison"; } if ((int)damageType == 512) { return "spirit"; } } return "damage"; } } internal sealed class WeaponEffectDamageState { public Player Attacker { get; } public Character Target { get; } public HitData SourceHit { get; } public float HealthBefore { get; } public List Effects { get; } public WeaponEffectDamageState(Player attacker, Character target, HitData sourceHit, float healthBefore, List effects) { Attacker = attacker; Target = target; SourceHit = sourceHit; HealthBefore = healthBefore; Effects = effects; } } internal sealed class WeaponEffectBleedingController : MonoBehaviour { private struct BleedInstance { public Player Attacker { get; } public HitData SourceHit { get; } public DamageType DamageType { get; } public float TickDamage { get; } public float Interval { get; } public float Timer { get; set; } public int RemainingTicks { get; set; } public BleedInstance(Player attacker, HitData sourceHit, DamageType damageType, float tickDamage, float interval, int remainingTicks) { //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) Attacker = attacker; SourceHit = sourceHit; DamageType = damageType; TickDamage = tickDamage; Interval = interval; Timer = interval; RemainingTicks = remainingTicks; } } private readonly List _instances = new List(); internal static void Apply(Character target, Player attacker, HitData sourceHit, ConfiguredWeaponEffectDefinition effect, float totalDamage) { if (!(totalDamage <= 0f) && !target.IsDead()) { (((Component)target).GetComponent() ?? ((Component)target).gameObject.AddComponent()).Add(attacker, sourceHit, effect, totalDamage); } } private void Add(Player attacker, HitData sourceHit, ConfiguredWeaponEffectDefinition effect, float totalDamage) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.01f, effect.TickInterval); int num2 = Mathf.Max(1, Mathf.CeilToInt(Mathf.Max(num, effect.Duration) / num)); _instances.Add(new BleedInstance(attacker, sourceHit.Clone(), effect.DamageType, totalDamage / (float)num2, num, num2)); } private void Update() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) Character component = ((Component)this).GetComponent(); if ((Object)(object)component == (Object)null || component.IsDead()) { Object.Destroy((Object)(object)this); return; } float deltaTime = Time.deltaTime; for (int num = _instances.Count - 1; num >= 0; num--) { BleedInstance value = _instances[num]; value.Timer -= deltaTime; if (value.Timer <= 0f) { value.Timer += value.Interval; value.RemainingTicks--; WeaponEffectManager.ApplyEffectDamage(value.Attacker, component, value.SourceHit, value.DamageType, value.TickDamage); } if (value.RemainingTicks <= 0) { _instances.RemoveAt(num); } else { _instances[num] = value; } } if (_instances.Count == 0) { Object.Destroy((Object)(object)this); } } } internal abstract class ConfiguredRuntimeStatusEffectBase : StatusEffect { protected ConfiguredWeaponEffectDefinition Effect; public void Initialize(ConfiguredWeaponEffectDefinition effect) { Effect = effect; ((Object)this).name = effect.StatusEffectName; base.m_name = effect.Id; base.m_tooltip = effect.Id; base.m_ttl = effect.Duration; } } internal sealed class ConfiguredDotStatusEffect : ConfiguredRuntimeStatusEffectBase { private float _tickTimer; public override void UpdateStatusEffect(float dt) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0077: 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) ((StatusEffect)this).UpdateStatusEffect(dt); _tickTimer += dt; if (!(_tickTimer < Effect.TickInterval)) { _tickTimer = 0f; float num = WeaponEffectManager.ResolveScalarValue(Effect.DamageMode, Effect.DamageValue, ((StatusEffect)this).m_character, ((StatusEffect)this).m_character); if (!(num <= 0f)) { HitData val = new HitData(); WeaponEffectManager.SetDamageValue(ref val.m_damage, Effect.DamageType, num); val.m_point = ((Component)((StatusEffect)this).m_character).transform.position; ((StatusEffect)this).m_character.Damage(val); } } } } internal sealed class ConfiguredResistanceShredStatusEffect : ConfiguredRuntimeStatusEffectBase { public override void ModifyDamageMods(ref DamageModifiers modifiers) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_001c: 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_0034: Expected I4, but got Unknown //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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Invalid comparison between Unknown and I4 //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 //IL_00ec: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //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_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_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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_014b: 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_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_00ff: 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_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) ((StatusEffect)this).ModifyDamageMods(ref modifiers); DamageType damageType = Effect.DamageType; if ((int)damageType <= 32) { if ((int)damageType <= 8) { switch (damageType - 1) { default: if ((int)damageType == 8) { modifiers.m_chop = Effect.Modifier; } break; case 0: modifiers.m_blunt = Effect.Modifier; break; case 1: modifiers.m_slash = Effect.Modifier; break; case 3: modifiers.m_pierce = Effect.Modifier; break; case 2: break; } } else if ((int)damageType != 16) { if ((int)damageType == 32) { modifiers.m_fire = Effect.Modifier; } } else { modifiers.m_pickaxe = Effect.Modifier; } } else if ((int)damageType <= 128) { if ((int)damageType != 64) { if ((int)damageType == 128) { modifiers.m_lightning = Effect.Modifier; } } else { modifiers.m_frost = Effect.Modifier; } } else if ((int)damageType != 256) { if ((int)damageType == 512) { modifiers.m_spirit = Effect.Modifier; } } else { modifiers.m_poison = Effect.Modifier; } if (Effect.ConsumeOnModify) { ((StatusEffect)this).m_ttl = 0.1f; } } } internal sealed class ConfiguredHasteStatusEffect : ConfiguredRuntimeStatusEffectBase { public override void ModifySpeed(float baseSpeed, ref float speed, Character character, Vector3 dir) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) ((StatusEffect)this).ModifySpeed(baseSpeed, ref speed, character, dir); speed *= Effect.MoveSpeedMultiplier; } } internal sealed class ConfiguredEquipStatusEffect : StatusEffect { public void Initialize(string statusName, string label, string tooltip) { ((Object)this).name = statusName; base.m_name = label; base.m_tooltip = tooltip; base.m_ttl = 0f; } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamageWeaponEffectPatch { private static void Prefix(Character __instance, ref HitData hit, out WeaponEffectDamageState? __state) { __state = WeaponEffectManager.TryHandleCharacterDamagePrefix(__instance, ref hit); } private static void Postfix(WeaponEffectDamageState? __state) { WeaponEffectManager.TryHandleCharacterDamagePostfix(__state); } } } 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; } } }