Decompiled source of Edge v0.2.13

plugins/Edge/Edge.Core.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using ExtraPotions.Edge.Config;
using ExtraPotions.Edge.Conversion;
using ExtraPotions.Edge.Integration;
using ExtraPotions.Edge.Secondaries;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Edge")]
[assembly: InternalsVisibleTo("Edge.Core.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExtraPotions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")]
[assembly: AssemblyFileVersion("0.2.13.0")]
[assembly: AssemblyInformationalVersion("0.2.13+source.FB84B287D1209D44")]
[assembly: AssemblyProduct("Edge.Core")]
[assembly: AssemblyTitle("Edge.Core")]
[assembly: AssemblyVersion("0.2.13.0")]
[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;
		}
	}
}
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";
	}
}
namespace ExtraPotions.Edge
{
	public static class EdgeCore
	{
		public const string PluginGuid = "com.meadworks.edge";

		public const string ProductName = "Edge";

		public const string ProductVersion = "0.2.13";

		public const string Family = "Fang";

		public const string TokenNamespace = "edge";

		public const string TokenPrefix = "meadworks_edge_";

		public const string SupportedGameVersion = "1.0.17";
	}
}
namespace ExtraPotions.Edge.Slope
{
	public readonly struct AttackShape : IEquatable<AttackShape>
	{
		public float Height { get; }

		public float Offset { get; }

		public float MaxYAngle { get; }

		public AttackShape(float height, float offset, float maxYAngle)
		{
			Height = height;
			Offset = offset;
			MaxYAngle = maxYAngle;
		}

		public bool Equals(AttackShape other)
		{
			if (Height.Equals(other.Height) && Offset.Equals(other.Offset))
			{
				return MaxYAngle.Equals(other.MaxYAngle);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is AttackShape other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (Height, Offset, MaxYAngle).GetHashCode();
		}

		public override string ToString()
		{
			return "height " + Height + ", offset " + Offset + ", max pitch " + MaxYAngle;
		}
	}
	public readonly struct SlopeFacts
	{
		public bool LocalPlayer { get; }

		public bool Melee { get; }

		public float GroundSlopeDegrees { get; }

		public float? TargetVerticalOffset { get; }

		public SlopeFacts(bool localPlayer, bool melee, float groundSlopeDegrees, float? targetVerticalOffset)
		{
			LocalPlayer = localPlayer;
			Melee = melee;
			GroundSlopeDegrees = groundSlopeDegrees;
			TargetVerticalOffset = targetVerticalOffset;
		}
	}
	public readonly struct SlopeSettings
	{
		public static SlopeSettings Defaults => new SlopeSettings(EdgeSettings.Slope.Mode.Default, EdgeSettings.Slope.AttackHeight.Default, EdgeSettings.Slope.AttackOffset.Default, EdgeSettings.Slope.AdaptiveSlopeThreshold.Default, EdgeSettings.Slope.AdaptiveMaxPitch.Default);

		public SlopeMode Mode { get; }

		public float Height { get; }

		public float Offset { get; }

		public float SlopeThreshold { get; }

		public float MaxPitch { get; }

		public SlopeSettings(SlopeMode mode, float height, float offset, float slopeThreshold, float maxPitch)
		{
			Mode = EdgeSettings.Slope.Mode.Normalize(mode);
			Height = EdgeSettings.Slope.AttackHeight.Normalize(height);
			Offset = EdgeSettings.Slope.AttackOffset.Normalize(offset);
			SlopeThreshold = EdgeSettings.Slope.AdaptiveSlopeThreshold.Normalize(slopeThreshold);
			MaxPitch = EdgeSettings.Slope.AdaptiveMaxPitch.Normalize(maxPitch);
		}
	}
	public static class SlopeAssist
	{
		public const float TargetTriggerHeight = 0.5f;

		public static AttackShape? Decide(SlopeSettings settings, SlopeFacts facts, AttackShape vanilla)
		{
			if (settings.Mode == SlopeMode.Off || !facts.LocalPlayer || !facts.Melee)
			{
				return null;
			}
			float maxYAngle = vanilla.MaxYAngle;
			if (settings.Mode == SlopeMode.Adaptive && Triggered(settings, facts))
			{
				maxYAngle = Math.Max(vanilla.MaxYAngle, settings.MaxPitch);
			}
			return new AttackShape(settings.Height, settings.Offset, maxYAngle);
		}

		private static bool Triggered(SlopeSettings settings, SlopeFacts facts)
		{
			float groundSlopeDegrees = facts.GroundSlopeDegrees;
			if (!float.IsNaN(groundSlopeDegrees) && !float.IsInfinity(groundSlopeDegrees) && groundSlopeDegrees >= settings.SlopeThreshold)
			{
				return true;
			}
			float? targetVerticalOffset = facts.TargetVerticalOffset;
			if (targetVerticalOffset.HasValue && !float.IsNaN(targetVerticalOffset.Value) && !float.IsInfinity(targetVerticalOffset.Value))
			{
				return Math.Abs(targetVerticalOffset.Value) >= 0.5f;
			}
			return false;
		}
	}
	public sealed class SlopeRestore
	{
		private AttackShape saved;

		public bool Pending { get; private set; }

		public void Save(AttackShape original)
		{
			saved = original;
			Pending = true;
		}

		public AttackShape Take()
		{
			if (!Pending)
			{
				throw new InvalidOperationException("No saved attack values.");
			}
			Pending = false;
			return saved;
		}
	}
}
namespace ExtraPotions.Edge.Secondaries
{
	public static class CompendiumPage
	{
		public const string TitleKey = "compendium_title";

		public const string IntroKey = "compendium_intro";

		public const string QuickstepNameKey = "compendium_quickstep_name";

		public const string QuickstepDescriptionKey = "compendium_quickstep_description";

		public static readonly string[] Keys = new string[8]
		{
			"compendium_title",
			"compendium_intro",
			"compendium_quickstep_name",
			"compendium_quickstep_description",
			GroupKey(PresetGroup.Ranged),
			GroupKey(PresetGroup.Melee),
			GroupKey(PresetGroup.BloodMagic),
			GroupKey(PresetGroup.SummonQuality)
		};

		public static string GroupKey(PresetGroup group)
		{
			return group switch
			{
				PresetGroup.Ranged => "compendium_group_ranged", 
				PresetGroup.Melee => "compendium_group_melee", 
				PresetGroup.BloodMagic => "compendium_group_blood_magic", 
				_ => "compendium_group_summon_quality", 
			};
		}

		public static string Text(Func<string, string> text, bool quickstep)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<color=#D6D6D6>").Append(text("compendium_intro")).Append("</color>\n\n");
			if (quickstep)
			{
				stringBuilder.Append(Heading(text("compendium_quickstep_name"))).Append(text("compendium_quickstep_description")).Append("\n\n\n");
			}
			PresetGroup? presetGroup = null;
			foreach (PresetInfo entry in PresetCatalog.Entries)
			{
				if (presetGroup != entry.Group)
				{
					if (presetGroup.HasValue)
					{
						stringBuilder.Append('\n');
					}
					stringBuilder.Append(Heading(text(GroupKey(entry.Group))));
					presetGroup = entry.Group;
				}
				stringBuilder.Append("<color=orange><b>").Append(text(entry.NameKey)).Append("</b></color>\n")
					.Append(text(entry.DescriptionKey))
					.Append("\n\n");
			}
			return stringBuilder.ToString().TrimEnd();
		}

		private static string Heading(string words)
		{
			return "<color=#FFD27A><b>" + words + "</b></color>\n\n";
		}
	}
	public enum CooldownPanelPlace
	{
		Hidden,
		Hud,
		InventoryPreview
	}
	public static class CooldownPanelPlacement
	{
		public static CooldownPanelPlace Place(bool windowOpen, bool inventoryOpen, bool inventoryCovered)
		{
			if (!windowOpen)
			{
				if (inventoryOpen)
				{
					if (!inventoryCovered)
					{
						return CooldownPanelPlace.InventoryPreview;
					}
					return CooldownPanelPlace.Hidden;
				}
				return CooldownPanelPlace.Hud;
			}
			return CooldownPanelPlace.Hidden;
		}
	}
	public static class Cooldowns
	{
		public static float Final(float baseCooldown, float skillLevel, float reductionFactor, bool noCooldowns)
		{
			if (noCooldowns)
			{
				return 0f;
			}
			float num = Clamp01(skillLevel / 100f) * Clamp01(reductionFactor);
			return Math.Max(0f, Math.Max(0f, baseCooldown) * (1f - num));
		}

		public static string Group(string? explicitGroup, string? automaticFamilyGroup, string domain, string? presetKey)
		{
			if (!string.IsNullOrWhiteSpace(explicitGroup))
			{
				return explicitGroup.Trim();
			}
			if (!string.IsNullOrWhiteSpace(automaticFamilyGroup))
			{
				return automaticFamilyGroup.Trim();
			}
			return PresetFallback(domain, presetKey);
		}

		public static string PresetFallback(string? domain, string? presetKey)
		{
			string text = Token(domain);
			string text2 = Token(presetKey);
			if (text.Length != 0 && text2.Length != 0)
			{
				return "preset:" + text + ":" + text2;
			}
			return "";
		}

		private static string Token(string? value)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return value.Trim().ToLowerInvariant();
			}
			return "";
		}

		private static float Clamp01(float v)
		{
			if (!float.IsNaN(v))
			{
				return Math.Min(1f, Math.Max(0f, v));
			}
			return 0f;
		}
	}
	public sealed class CooldownTable
	{
		private readonly Dictionary<string, (double ReadyAt, float Duration)> entries = new Dictionary<string, (double, float)>(StringComparer.Ordinal);

		public bool TryConsume(string? group, double now, float duration)
		{
			if (string.IsNullOrWhiteSpace(group))
			{
				return true;
			}
			string key = group.Trim();
			if (Active(key, now, out (double, float) _))
			{
				return false;
			}
			entries.Remove(key);
			float num = Math.Max(0f, duration);
			if (num <= 0f)
			{
				return true;
			}
			entries[key] = (now + (double)num, num);
			return true;
		}

		public bool IsReady(string? group, double now)
		{
			(double, float) entry;
			if (!string.IsNullOrWhiteSpace(group))
			{
				return !Active(group.Trim(), now, out entry);
			}
			return true;
		}

		public float Remaining(string? group, double now)
		{
			if (string.IsNullOrWhiteSpace(group) || !Active(group.Trim(), now, out (double, float) entry))
			{
				return 0f;
			}
			return (float)Math.Max(0.0, entry.Item1 - now);
		}

		public float Progress(string? group, double now)
		{
			if (string.IsNullOrWhiteSpace(group) || !Active(group.Trim(), now, out (double, float) entry) || entry.Item2 <= 0f)
			{
				return 1f;
			}
			return (float)Math.Min(1.0, Math.Max(0.0, 1.0 - (entry.Item1 - now) / (double)entry.Item2));
		}

		public void Clear()
		{
			entries.Clear();
		}

		private bool Active(string key, double now, out (double ReadyAt, float Duration) entry)
		{
			if (entries.TryGetValue(key, out entry))
			{
				return entry.ReadyAt > now;
			}
			return false;
		}
	}
	public static class OwnedCharacterWrite
	{
		public static bool Allowed(bool viewValid, bool isOwner)
		{
			return viewValid && isOwner;
		}
	}
	public enum PresetGroup
	{
		Ranged,
		Melee,
		BloodMagic,
		SummonQuality
	}
	public sealed class PresetInfo
	{
		public string Key { get; }

		public string TokenKey { get; }

		public PresetGroup Group { get; }

		public string IconPrefabName { get; }

		public string NameKey => "preset_" + TokenKey + "_name";

		public string DescriptionKey => "preset_" + TokenKey + "_description";

		internal PresetInfo(string key, string tokenKey, PresetGroup group, string iconPrefabName)
		{
			Key = key;
			TokenKey = tokenKey;
			Group = group;
			IconPrefabName = iconPrefabName;
		}

		public override string ToString()
		{
			return Key;
		}
	}
	public static class PresetCatalog
	{
		private static readonly Dictionary<string, PresetInfo> ByKey = BuildByKey();

		public static IReadOnlyList<PresetInfo> Entries { get; } = Array.AsReadOnly(new PresetInfo[26]
		{
			new PresetInfo("barrage", "barrage", PresetGroup.Ranged, "BowHuntsman"),
			new PresetInfo("volley", "volley", PresetGroup.Ranged, "CrossbowArbalest"),
			new PresetInfo("piercing", "piercing", PresetGroup.Ranged, "StaffLightning"),
			new PresetInfo("scatter", "scatter", PresetGroup.Ranged, "StaffClusterbomb"),
			new PresetInfo("spiral", "spiral", PresetGroup.Ranged, "StaffIceShards"),
			new PresetInfo("sentinel", "sentinel", PresetGroup.Ranged, "StaffFireball"),
			new PresetInfo("meteor", "meteor", PresetGroup.Ranged, "StaffFireball"),
			new PresetInfo("burst", "burst", PresetGroup.Ranged, "CrossbowArbalest"),
			new PresetInfo("stickyDetonator", "sticky_detonator", PresetGroup.Ranged, "BombOoze"),
			new PresetInfo("overchargedBomb", "overcharged_bomb", PresetGroup.Ranged, "BombBile"),
			new PresetInfo("sneakAmbush", "sneak_ambush", PresetGroup.Melee, "KnifeWood"),
			new PresetInfo("riftTrail", "rift_trail", PresetGroup.Melee, "SwordWood"),
			new PresetInfo("cleavingThrust", "cleaving_thrust", PresetGroup.Melee, "THSwordWood"),
			new PresetInfo("launchSlam", "launch_slam", PresetGroup.Melee, "MaceWood"),
			new PresetInfo("aftershock", "aftershock", PresetGroup.Melee, "SledgeWood"),
			new PresetInfo("knockbackChain", "knockback_chain", PresetGroup.Melee, "FistBjornClaw"),
			new PresetInfo("boomerang", "boomerang", PresetGroup.Melee, "AxeBronze"),
			new PresetInfo("impactBurst", "impact_burst", PresetGroup.Melee, "Battleaxe"),
			new PresetInfo("spearRain", "spear_rain", PresetGroup.Melee, "SpearWood"),
			new PresetInfo("spinningSweep", "spinning_sweep", PresetGroup.Melee, "AtgeirWood"),
			new PresetInfo("fractureLine", "fracture_line", PresetGroup.Melee, "PickaxeAntler"),
			new PresetInfo("harvestSweep", "harvest_sweep", PresetGroup.Melee, "Scythe"),
			new PresetInfo("summonEmpower", "summon_empower", PresetGroup.BloodMagic, "StaffSkeleton"),
			new PresetInfo("shieldConvert", "shield_convert", PresetGroup.BloodMagic, "StaffShield"),
			new PresetInfo("countByQuality", "count_by_quality", PresetGroup.SummonQuality, "StaffRedTroll"),
			new PresetInfo("levelByQuality", "level_by_quality", PresetGroup.SummonQuality, "StaffSkeleton")
		});

		public static bool TryGet(string? presetKey, out PresetInfo info)
		{
			return ByKey.TryGetValue(presetKey ?? "", out info);
		}

		public static string Canonicalize(string? presetKey)
		{
			if (string.IsNullOrWhiteSpace(presetKey))
			{
				return "";
			}
			string text = presetKey.Trim();
			if (!TryGet(text, out PresetInfo info))
			{
				return text;
			}
			return info.Key;
		}

		public static string Key(RangedPreset preset)
		{
			return preset switch
			{
				RangedPreset.Barrage => "barrage", 
				RangedPreset.Volley => "volley", 
				RangedPreset.Piercing => "piercing", 
				RangedPreset.Scatter => "scatter", 
				RangedPreset.Spiral => "spiral", 
				RangedPreset.Sentinel => "sentinel", 
				RangedPreset.Meteor => "meteor", 
				RangedPreset.Burst => "burst", 
				_ => "", 
			};
		}

		public static string Key(BombPreset preset)
		{
			return preset switch
			{
				BombPreset.StickyDetonator => "stickyDetonator", 
				BombPreset.OverchargedBomb => "overchargedBomb", 
				_ => "", 
			};
		}

		private static Dictionary<string, PresetInfo> BuildByKey()
		{
			Dictionary<string, PresetInfo> dictionary = new Dictionary<string, PresetInfo>(StringComparer.OrdinalIgnoreCase);
			foreach (PresetInfo entry in Entries)
			{
				dictionary[entry.Key] = entry;
			}
			return dictionary;
		}
	}
	public sealed class RangedChoices
	{
		public static RangedChoices Defaults { get; } = new RangedChoices(EdgeSettings.Ranged.OneHandedElementalPreset.Default, EdgeSettings.Ranged.FireballStaffPreset.Default, EdgeSettings.Ranged.RapidfireStaffPreset.Default, EdgeSettings.Ranged.LightningStaffPreset.Default, EdgeSettings.Ranged.BowPreset.Default, EdgeSettings.Ranged.CrossbowPreset.Default, EdgeSettings.Ranged.BombPreset.Default);

		public RangedPreset OneHandedElemental { get; }

		public RangedPreset FireballStaff { get; }

		public RangedPreset RapidStaff { get; }

		public RangedPreset LightningStaff { get; }

		public RangedPreset Bow { get; }

		public RangedPreset Crossbow { get; }

		public BombPreset Bomb { get; }

		public RangedChoices(RangedPreset oneHandedElemental, RangedPreset fireballStaff, RangedPreset rapidStaff, RangedPreset lightningStaff, RangedPreset bow, RangedPreset crossbow, BombPreset bomb)
		{
			OneHandedElemental = oneHandedElemental;
			FireballStaff = fireballStaff;
			RapidStaff = rapidStaff;
			LightningStaff = lightningStaff;
			Bow = bow;
			Crossbow = crossbow;
			Bomb = bomb;
		}

		public RangedChoices With(RangedPreset? oneHandedElemental = null, RangedPreset? fireballStaff = null, RangedPreset? rapidStaff = null, RangedPreset? lightningStaff = null, RangedPreset? bow = null, RangedPreset? crossbow = null, BombPreset? bomb = null)
		{
			return new RangedChoices(oneHandedElemental ?? OneHandedElemental, fireballStaff ?? FireballStaff, rapidStaff ?? RapidStaff, lightningStaff ?? LightningStaff, bow ?? Bow, crossbow ?? Crossbow, bomb ?? Bomb);
		}
	}
	public sealed class PrefabEntry
	{
		public string Prefab { get; }

		public string? Preset { get; }

		public bool Enabled { get; }

		public PrefabEntry(string prefab, string? preset, bool enabled)
		{
			Prefab = prefab ?? "";
			Preset = preset;
			Enabled = enabled;
		}
	}
	public enum ResolutionKind
	{
		EdgePreset,
		KeepOwn
	}
	public enum ResolutionReason
	{
		Automatic,
		PrefabEntry,
		PrefabNone,
		ForeignSecondary,
		GroupOff,
		NoFamily,
		ModuleOff
	}
	public readonly struct PresetResolution
	{
		public ResolutionKind Kind { get; }

		public string PresetKey { get; }

		public ResolutionReason Reason { get; }

		private PresetResolution(ResolutionKind kind, string presetKey, ResolutionReason reason)
		{
			Kind = kind;
			PresetKey = presetKey;
			Reason = reason;
		}

		internal static PresetResolution Preset(string key, ResolutionReason reason)
		{
			return new PresetResolution(ResolutionKind.EdgePreset, key, reason);
		}

		internal static PresetResolution Own(ResolutionReason reason)
		{
			return new PresetResolution(ResolutionKind.KeepOwn, "", reason);
		}

		public override string ToString()
		{
			if (Kind != ResolutionKind.EdgePreset)
			{
				return "own secondary (" + Reason.ToString() + ")";
			}
			return PresetKey + " (" + Reason.ToString() + ")";
		}
	}
	public static class PresetSelection
	{
		public const string NonePreset = "none";

		public static PresetResolution Resolve(WeaponFacts weapon, RangedChoices choices, IEnumerable<PrefabEntry> entries, bool foreignSecondary, bool moduleEnabled, ICollection<string>? warnings = null)
		{
			if (weapon == null)
			{
				throw new ArgumentNullException("weapon");
			}
			if (choices == null)
			{
				throw new ArgumentNullException("choices");
			}
			if (!moduleEnabled)
			{
				return PresetResolution.Own(ResolutionReason.ModuleOff);
			}
			List<PrefabEntry> list = (entries ?? Enumerable.Empty<PrefabEntry>()).Where((PrefabEntry e) => e != null && e.Enabled && string.Equals(e.Prefab, weapon.PrefabName, StringComparison.OrdinalIgnoreCase)).ToList();
			if (list.Count > 1)
			{
				warnings?.Add(weapon.PrefabName + ": " + list.Count + " enabled entries; the first is used.");
			}
			PrefabEntry prefabEntry = list.FirstOrDefault();
			if (prefabEntry != null && !string.IsNullOrWhiteSpace(prefabEntry.Preset))
			{
				string text = prefabEntry.Preset.Trim();
				if (string.Equals(text, "none", StringComparison.OrdinalIgnoreCase))
				{
					return PresetResolution.Own(ResolutionReason.PrefabNone);
				}
				if (PresetCatalog.TryGet(text, out PresetInfo info) && info.Group != PresetGroup.SummonQuality)
				{
					return PresetResolution.Preset(info.Key, ResolutionReason.PrefabEntry);
				}
				warnings?.Add(weapon.PrefabName + ": preset \"" + text + "\" is not a secondary attack preset; the automatic preset is used.");
			}
			if (foreignSecondary)
			{
				return PresetResolution.Own(ResolutionReason.ForeignSecondary);
			}
			return Automatic(weapon, choices);
		}

		private static PresetResolution Automatic(WeaponFacts weapon, RangedChoices choices)
		{
			switch (WeaponFamilies.Blood(weapon))
			{
			case BloodFamily.Summon:
				return PresetResolution.Preset("summonEmpower", ResolutionReason.Automatic);
			case BloodFamily.Shield:
				return PresetResolution.Preset("shieldConvert", ResolutionReason.Automatic);
			default:
			{
				RangedFamily rangedFamily = WeaponFamilies.Ranged(weapon);
				switch (rangedFamily)
				{
				case RangedFamily.Bomb:
				{
					string text3 = ((choices.Bomb != BombPreset.Auto) ? PresetCatalog.Key(choices.Bomb) : (weapon.BombHasArea ? "overchargedBomb" : "stickyDetonator"));
					if (text3.Length != 0)
					{
						return PresetResolution.Preset(text3, ResolutionReason.Automatic);
					}
					return PresetResolution.Own(ResolutionReason.GroupOff);
				}
				default:
				{
					string text2 = PresetCatalog.Key(Choice(rangedFamily, choices));
					if (text2.Length != 0)
					{
						return PresetResolution.Preset(text2, ResolutionReason.Automatic);
					}
					return PresetResolution.Own(ResolutionReason.GroupOff);
				}
				case RangedFamily.None:
				{
					string text = MeleeKey(WeaponFamilies.Melee(weapon));
					if (text.Length != 0)
					{
						return PresetResolution.Preset(text, ResolutionReason.Automatic);
					}
					return PresetResolution.Own(ResolutionReason.NoFamily);
				}
				}
			}
			}
		}

		private static RangedPreset Choice(RangedFamily family, RangedChoices choices)
		{
			return family switch
			{
				RangedFamily.OneHandedElemental => choices.OneHandedElemental, 
				RangedFamily.FireballStaff => choices.FireballStaff, 
				RangedFamily.RapidStaff => choices.RapidStaff, 
				RangedFamily.ReloadStaff => choices.LightningStaff, 
				RangedFamily.Bow => choices.Bow, 
				RangedFamily.Crossbow => choices.Crossbow, 
				_ => RangedPreset.Off, 
			};
		}

		public static string MeleeKey(MeleeFamily family)
		{
			return family switch
			{
				MeleeFamily.Knives => "sneakAmbush", 
				MeleeFamily.OneHandedSword => "riftTrail", 
				MeleeFamily.TwoHandedSword => "cleavingThrust", 
				MeleeFamily.OneHandedClub => "launchSlam", 
				MeleeFamily.Unarmed => "knockbackChain", 
				MeleeFamily.Sledge => "aftershock", 
				MeleeFamily.Battleaxe => "impactBurst", 
				MeleeFamily.Polearm => "spinningSweep", 
				MeleeFamily.Spear => "spearRain", 
				MeleeFamily.OneHandedAxe => "boomerang", 
				MeleeFamily.Pickaxe => "fractureLine", 
				MeleeFamily.Farming => "harvestSweep", 
				_ => "", 
			};
		}
	}
	public enum WeaponSkill
	{
		None,
		Swords,
		Knives,
		Clubs,
		Polearms,
		Spears,
		Blocking,
		Axes,
		Bows,
		ElementalMagic,
		BloodMagic,
		Unarmed,
		Pickaxes,
		WoodCutting,
		Crossbows,
		Farming,
		Other
	}
	public enum WeaponItemType
	{
		OneHandedWeapon,
		TwoHandedWeapon,
		TwoHandedWeaponLeft,
		Bow,
		Shield,
		Torch,
		Tool,
		Ammo,
		AmmoNonEquipable,
		Other
	}
	public enum AttackKind
	{
		None,
		Horizontal,
		Vertical,
		Projectile,
		Area,
		TriggerProjectile
	}
	public sealed class WeaponFacts
	{
		public string PrefabName { get; set; } = "";

		public WeaponSkill Skill { get; set; }

		public WeaponItemType ItemType { get; set; }

		public bool HasPrimary { get; set; }

		public string PrimaryAnimation { get; set; } = "";

		public AttackKind PrimaryAttackKind { get; set; }

		public bool PrimaryHasProjectile { get; set; }

		public bool PrimaryProjectileHasAoe { get; set; }

		public bool PrimaryProjectileHasSpawnAbility { get; set; }

		public bool PrimaryProjectileIsIProjectile { get; set; }

		public bool PrimarySpawnsSummon { get; set; }

		public bool PrimaryRequiresReload { get; set; }

		public string AmmoType { get; set; } = "";

		public bool HasSecondary { get; set; }

		public string SecondaryAnimation { get; set; } = "";

		public AttackKind SecondaryAttackKind { get; set; }

		public bool BombHasArea { get; set; }

		public WeaponFacts Clone()
		{
			return (WeaponFacts)MemberwiseClone();
		}

		public WeaponFacts With(Action<WeaponFacts> change)
		{
			if (change == null)
			{
				throw new ArgumentNullException("change");
			}
			WeaponFacts weaponFacts = Clone();
			change(weaponFacts);
			return weaponFacts;
		}
	}
	public enum BloodFamily
	{
		None,
		Summon,
		Shield
	}
	public enum RangedFamily
	{
		None,
		Bomb,
		FireballStaff,
		RapidStaff,
		ReloadStaff,
		Crossbow,
		Bow,
		OneHandedElemental
	}
	public enum MeleeFamily
	{
		None,
		Knives,
		TwoHandedSword,
		OneHandedSword,
		OneHandedClub,
		Unarmed,
		Sledge,
		Polearm,
		Farming,
		Pickaxe,
		Spear,
		Battleaxe,
		OneHandedAxe
	}
	public static class WeaponFamilies
	{
		public const string BloodSummonCooldownGroup = "family:blood:summon";

		public const string BloodShieldCooldownGroup = "family:blood:shield";

		public static string CooldownGroup(WeaponFacts weapon)
		{
			return Blood(weapon) switch
			{
				BloodFamily.Summon => "family:blood:summon", 
				BloodFamily.Shield => "family:blood:shield", 
				_ => Ranged(weapon) switch
				{
					RangedFamily.Bomb => "family:ranged:bomb", 
					RangedFamily.FireballStaff => "family:ranged:staff-fireball", 
					RangedFamily.RapidStaff => "family:ranged:staff-rapid", 
					RangedFamily.ReloadStaff => "family:ranged:staff-reload", 
					RangedFamily.Crossbow => "family:ranged:crossbow", 
					RangedFamily.Bow => "family:ranged:bow", 
					RangedFamily.OneHandedElemental => "family:ranged:magic-onehanded", 
					_ => Melee(weapon) switch
					{
						MeleeFamily.Knives => "family:melee:knives", 
						MeleeFamily.TwoHandedSword => "family:melee:sword-2h", 
						MeleeFamily.OneHandedSword => "family:melee:sword-1h", 
						MeleeFamily.OneHandedClub => "family:melee:club-1h", 
						MeleeFamily.Unarmed => "family:melee:unarmed", 
						MeleeFamily.Sledge => "family:melee:sledge", 
						MeleeFamily.Polearm => "family:melee:polearm", 
						MeleeFamily.Farming => "family:melee:farming", 
						MeleeFamily.Pickaxe => "family:melee:pickaxe", 
						MeleeFamily.Spear => "family:melee:spear", 
						MeleeFamily.Battleaxe => "family:melee:battleaxe", 
						MeleeFamily.OneHandedAxe => "family:melee:axe-1h", 
						_ => "", 
					}, 
				}, 
			};
		}

		public static BloodFamily Blood(WeaponFacts weapon)
		{
			if (weapon == null || !weapon.HasPrimary || weapon.Skill != WeaponSkill.BloodMagic)
			{
				return BloodFamily.None;
			}
			if (string.Equals(weapon.PrefabName, "StaffShield", StringComparison.OrdinalIgnoreCase))
			{
				return BloodFamily.Shield;
			}
			if (!weapon.PrimarySpawnsSummon)
			{
				return BloodFamily.None;
			}
			return BloodFamily.Summon;
		}

		public static RangedFamily Ranged(WeaponFacts weapon)
		{
			if (weapon == null || !weapon.HasPrimary || IsAmmo(weapon.ItemType) || string.IsNullOrWhiteSpace(weapon.PrimaryAnimation))
			{
				return RangedFamily.None;
			}
			if (IsBomb(weapon))
			{
				return RangedFamily.Bomb;
			}
			if (weapon.Skill == WeaponSkill.ElementalMagic)
			{
				if (weapon.ItemType == WeaponItemType.OneHandedWeapon)
				{
					if (!IsOneHandedElementalProjectile(weapon))
					{
						return RangedFamily.None;
					}
					return RangedFamily.OneHandedElemental;
				}
				return StaffFamily(weapon.PrimaryAnimation);
			}
			if (IsOffensiveBloodMagicStaff(weapon))
			{
				return StaffFamily(weapon.PrimaryAnimation);
			}
			if (weapon.Skill == WeaponSkill.Crossbows || (weapon.PrimaryRequiresReload && weapon.PrimaryAttackKind == AttackKind.Projectile && !string.IsNullOrWhiteSpace(weapon.AmmoType)))
			{
				return RangedFamily.Crossbow;
			}
			if (weapon.ItemType != WeaponItemType.Bow && weapon.Skill != WeaponSkill.Bows)
			{
				return RangedFamily.None;
			}
			return RangedFamily.Bow;
		}

		public static MeleeFamily Melee(WeaponFacts weapon)
		{
			if (weapon == null)
			{
				return MeleeFamily.None;
			}
			if (IsSpear(weapon))
			{
				return MeleeFamily.Spear;
			}
			if (IsBattleaxe(weapon))
			{
				return MeleeFamily.Battleaxe;
			}
			if (weapon.Skill == WeaponSkill.Axes && weapon.ItemType == WeaponItemType.OneHandedWeapon)
			{
				return MeleeFamily.OneHandedAxe;
			}
			if (weapon.Skill == WeaponSkill.Polearms && weapon.HasSecondary && Eq(weapon.SecondaryAnimation, "atgeir_secondary"))
			{
				return MeleeFamily.Polearm;
			}
			if (weapon.Skill == WeaponSkill.Swords && weapon.ItemType == WeaponItemType.TwoHandedWeapon)
			{
				return MeleeFamily.TwoHandedSword;
			}
			if (weapon.Skill == WeaponSkill.Swords && weapon.ItemType == WeaponItemType.OneHandedWeapon && weapon.HasSecondary && Eq(weapon.SecondaryAnimation, "sword_secondary"))
			{
				return MeleeFamily.OneHandedSword;
			}
			if (weapon.Skill == WeaponSkill.Clubs && weapon.ItemType == WeaponItemType.OneHandedWeapon)
			{
				return MeleeFamily.OneHandedClub;
			}
			if (weapon.Skill == WeaponSkill.Unarmed && weapon.HasSecondary && Eq(weapon.SecondaryAnimation, "unarmed_kick"))
			{
				return MeleeFamily.Unarmed;
			}
			if (IsSledge(weapon))
			{
				return MeleeFamily.Sledge;
			}
			if (weapon.Skill == WeaponSkill.Pickaxes)
			{
				return MeleeFamily.Pickaxe;
			}
			if (weapon.Skill == WeaponSkill.Knives)
			{
				return MeleeFamily.Knives;
			}
			if (weapon.Skill != WeaponSkill.Farming)
			{
				return MeleeFamily.None;
			}
			return MeleeFamily.Farming;
		}

		public static bool IsBomb(WeaponFacts weapon)
		{
			if (weapon.PrimaryHasProjectile && (weapon.PrimaryAttackKind == AttackKind.Projectile || weapon.PrimaryProjectileIsIProjectile))
			{
				return Eq(weapon.PrimaryAnimation, "throw_bomb");
			}
			return false;
		}

		public static bool IsOneHandedElementalProjectile(WeaponFacts weapon)
		{
			if (weapon.Skill == WeaponSkill.ElementalMagic && weapon.ItemType == WeaponItemType.OneHandedWeapon && weapon.HasPrimary && !string.IsNullOrWhiteSpace(weapon.PrimaryAnimation) && weapon.PrimaryAttackKind == AttackKind.Projectile)
			{
				return weapon.PrimaryHasProjectile;
			}
			return false;
		}

		public static bool IsOffensiveBloodMagicStaff(WeaponFacts weapon)
		{
			if (weapon.Skill == WeaponSkill.BloodMagic && (weapon.ItemType == WeaponItemType.TwoHandedWeapon || weapon.ItemType == WeaponItemType.TwoHandedWeaponLeft) && weapon.HasPrimary && weapon.PrimaryAttackKind == AttackKind.Projectile && StaffFamily(weapon.PrimaryAnimation) != RangedFamily.None && weapon.PrimaryHasProjectile && !weapon.PrimaryProjectileHasAoe)
			{
				return !weapon.PrimaryProjectileHasSpawnAbility;
			}
			return false;
		}

		private static RangedFamily StaffFamily(string? animation)
		{
			if (Eq(animation, "staff_fireball"))
			{
				return RangedFamily.FireballStaff;
			}
			if (Eq(animation, "staff_rapidfire"))
			{
				return RangedFamily.RapidStaff;
			}
			if (!Eq(animation, "staff_lightningshot"))
			{
				return RangedFamily.None;
			}
			return RangedFamily.ReloadStaff;
		}

		private static bool IsSpear(WeaponFacts weapon)
		{
			if (weapon.Skill == WeaponSkill.Spears && weapon.HasSecondary)
			{
				return !string.IsNullOrWhiteSpace(weapon.SecondaryAnimation);
			}
			return false;
		}

		private static bool IsSledge(WeaponFacts weapon)
		{
			if (weapon.Skill == WeaponSkill.Clubs && weapon.ItemType == WeaponItemType.TwoHandedWeapon && Contains(weapon.PrefabName, "Sledge"))
			{
				if (weapon.PrimaryAttackKind != AttackKind.Area)
				{
					return weapon.SecondaryAttackKind == AttackKind.Area;
				}
				return true;
			}
			return false;
		}

		private static bool IsBattleaxe(WeaponFacts weapon)
		{
			if (weapon.Skill != WeaponSkill.Axes || weapon.ItemType != WeaponItemType.TwoHandedWeapon)
			{
				return false;
			}
			if (Contains(weapon.PrefabName, "DualAxe"))
			{
				return false;
			}
			string text = (weapon.HasSecondary ? (weapon.SecondaryAnimation ?? "") : "");
			if (!Eq(text, "battleaxe_secondary"))
			{
				return false;
			}
			if (Contains(weapon.PrefabName, "Battleaxe"))
			{
				return true;
			}
			string text2 = weapon.PrimaryAnimation ?? "";
			if ((Contains(text2, "battleaxe") || Contains(text, "battleaxe")) && !Contains(text2, "dualaxe"))
			{
				return !Contains(text, "dualaxe");
			}
			return false;
		}

		private static bool IsAmmo(WeaponItemType type)
		{
			if (type != WeaponItemType.Ammo)
			{
				return type == WeaponItemType.AmmoNonEquipable;
			}
			return true;
		}

		private static bool Eq(string? a, string b)
		{
			return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
		}

		private static bool Contains(string? text, string part)
		{
			return (text ?? "").IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
}
namespace ExtraPotions.Edge.Secondaries.Adopted
{
	internal enum MeleeSpecialPreset
	{
		None,
		SneakAmbush,
		CleavingThrust,
		SpearRain,
		ImpactBurst,
		Boomerang,
		SpinningSweep,
		LaunchSlam,
		KnockbackChain,
		Aftershock,
		RiftTrail,
		FractureLine
	}
	internal sealed class SecondaryAttackCompiledSnapshot
	{
		public static readonly SecondaryAttackCompiledSnapshot Empty = new SecondaryAttackCompiledSnapshot(0, new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase), new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase), new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase), null, new Dictionary<string, NormalizedMagicSummonOverrideConfig>(StringComparer.OrdinalIgnoreCase));

		public int SnapshotId { get; }

		public IReadOnlyDictionary<string, NormalizedWeaponConfig> Weapons { get; }

		public IReadOnlyDictionary<string, NormalizedWeaponConfig> GlobalRangedPresets { get; }

		public IReadOnlyDictionary<string, NormalizedWeaponConfig> GlobalBloodMagicPresets { get; }

		public NormalizedWeaponConfig? GlobalMeleeFallback { get; }

		public IReadOnlyDictionary<string, NormalizedMagicSummonOverrideConfig> MagicSummons { get; }

		public SecondaryAttackCompiledSnapshot(int snapshotId, IReadOnlyDictionary<string, NormalizedWeaponConfig> weapons, IReadOnlyDictionary<string, NormalizedWeaponConfig> globalRangedPresets, IReadOnlyDictionary<string, NormalizedWeaponConfig> globalBloodMagicPresets, NormalizedWeaponConfig? globalMeleeFallback, IReadOnlyDictionary<string, NormalizedMagicSummonOverrideConfig> magicSummons)
		{
			SnapshotId = snapshotId;
			Weapons = CopyAsReadOnly(weapons);
			GlobalRangedPresets = CopyAsReadOnly(globalRangedPresets);
			GlobalBloodMagicPresets = CopyAsReadOnly(globalBloodMagicPresets);
			GlobalMeleeFallback = globalMeleeFallback?.Clone();
			MagicSummons = CopyAsReadOnly(magicSummons);
		}

		private static IReadOnlyDictionary<string, TValue> CopyAsReadOnly<TValue>(IReadOnlyDictionary<string, TValue> source)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			Dictionary<string, TValue> dictionary = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, TValue> item in source)
			{
				item.Deconstruct(out var key, out var value);
				string key2 = key;
				TValue value2 = value;
				dictionary[key2] = value2;
			}
			return new ReadOnlyDictionary<string, TValue>(dictionary);
		}
	}
	internal sealed class RangedWeaponConfig
	{
		public bool? Enabled { get; set; }

		public RangedWeaponConfig? Barrage { get; set; }

		public RangedWeaponConfig? Volley { get; set; }

		public RangedWeaponConfig? Piercing { get; set; }

		public RangedWeaponConfig? Scatter { get; set; }

		public RangedWeaponConfig? Spiral { get; set; }

		public RangedWeaponConfig? Sentinel { get; set; }

		public RangedWeaponConfig? Meteor { get; set; }

		public RangedWeaponConfig? Burst { get; set; }

		public RangedWeaponConfig? StickyDetonator { get; set; }

		public RangedWeaponConfig? OverchargedBomb { get; set; }

		public string? Animation { get; set; }

		public string? DetonateAnimation { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? DamageFactor { get; set; }

		public float? ProjectileSpeedFactor { get; set; }

		public float? ProjectileScaleFactor { get; set; }

		public string? Preset { get; set; }

		public int? Count { get; set; }

		public float? SpreadAngle { get; set; }

		public int? AmmoConsumption { get; set; }

		public float? VolleyRadius { get; set; }

		public float? VolleyArcAngleMin { get; set; }

		public float? VolleyArcAngleMax { get; set; }

		public float? VolleyMaxRange { get; set; }

		public float? Interval { get; set; }

		public float? HoldRepeatInterval { get; set; }

		public float? BarrageSpacing { get; set; }

		public float? MeteorRadius { get; set; }

		public float? PierceDamageDecay { get; set; }

		public float? SplitAngle { get; set; }

		public int? RicochetBounces { get; set; }

		public float? RicochetDecay { get; set; }

		public float? RicochetRoughness { get; set; }

		public float? SpiralRadius { get; set; }

		public float? SpiralTurns { get; set; }

		public float? DetectionRange { get; set; }

		public float? HoverDistance { get; set; }

		public float? HoverHeight { get; set; }

		public float? HoverElevationAngle { get; set; }

		public float? OrbitRadius { get; set; }

		public float? OrbitSpeed { get; set; }

		public float? Lifetime { get; set; }

		public float? AttackDelay { get; set; }

		public float? MeteorSpawnHeight { get; set; }

		public int? MaxCharges { get; set; }

		public float? AoeRadiusFactor { get; set; }
	}
	internal sealed class MeleeWeaponConfig
	{
		public bool? Enabled { get; set; }

		public string Preset { get; set; } = "";

		public string CopyFrom { get; set; } = "";

		public string Animation { get; set; } = "";

		public float ResourceMultiplier { get; set; } = 1f;

		public float OutputMultiplier { get; set; } = 1f;

		public float? DurabilityFactor { get; set; }

		public SneakAmbushConfig? SneakAmbush { get; set; }

		public CleavingThrustConfig? CleavingThrust { get; set; }

		public MeleeOnProjectileHitConfig? SpearRain { get; set; }

		public ImpactBurstConfig? ImpactBurst { get; set; }

		public BoomerangConfig? Boomerang { get; set; }

		public SpinningSweepConfig? SpinningSweep { get; set; }

		public LaunchSlamConfig? LaunchSlam { get; set; }

		public KnockbackChainConfig? KnockbackChain { get; set; }

		public AftershockConfig? Aftershock { get; set; }

		public RiftTrailConfig? RiftTrail { get; set; }

		public FractureLineConfig? FractureLine { get; set; }

		public HarvestSweepConfig? HarvestSweep { get; set; }
	}
	internal sealed class MeleeOnProjectileHitConfig
	{
		public bool? Enabled { get; set; }

		public string Preset { get; set; } = "";

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public string? ProjectileSpinAxis { get; set; }

		public string? ProjectileVisualRotationOffset { get; set; }

		public string? Vfx { get; set; }

		public int? Count { get; set; }

		public float? SpawnHeight { get; set; }

		public float? SpawnRadius { get; set; }

		public float? FlightTime { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }

		public float? Radius { get; set; }

		public bool? IncludeDirectTarget { get; set; }

		public bool? IncludeDestructibles { get; set; }

		public bool? TriggerOnCharactersOnly { get; set; }
	}
	internal sealed class ImpactBurstConfig
	{
		public bool? Enabled { get; set; }

		public string Animation { get; set; } = "";

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public string? ProjectileSpinAxis { get; set; }

		public string? ProjectileVisualRotationOffset { get; set; }

		public float? Radius { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }
	}
	internal sealed class BoomerangConfig
	{
		public bool? Enabled { get; set; }

		public string Animation { get; set; } = "";

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public string? ProjectileSpinAxis { get; set; }

		public string? ProjectileVisualRotationOffset { get; set; }

		public float? MaxDistance { get; set; }

		public float? CurveFactor { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }

		public float? HitDamageDecay { get; set; }
	}
	internal sealed class SpinningSweepConfig
	{
		public bool? Enabled { get; set; }

		public string Animation { get; set; } = "";

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }

		public float? LoopStart { get; set; }

		public float? LoopEnd { get; set; }

		public float? AnimationSpeed { get; set; }

		public float? MoveSpeedFactor { get; set; }

		public float? SkillRaiseFactor { get; set; }
	}
	internal sealed class SneakAmbushConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? ChargeMaxSeconds { get; set; }

		public float? ChargeSkillFactor { get; set; }

		public float? AggroResetRangePerChargeSecond { get; set; }

		public float? SenseBlockDurationPerChargeSecond { get; set; }

		public float? BackstabResetSecondsPerChargeSecond { get; set; }
	}
	internal sealed class CleavingThrustConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? RangeFactor { get; set; }

		public float? Angle { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }

		public bool? HitThroughWalls { get; set; }
	}
	internal sealed class LaunchSlamConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? LaunchHeight { get; set; }

		public float? DamageFactor { get; set; }
	}
	internal sealed class KnockbackChainConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? PushFactor { get; set; }

		public float? ChainDecay { get; set; }
	}
	internal sealed class AftershockConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public int? Waves { get; set; }

		public float? Interval { get; set; }

		public float? WaveDecay { get; set; }

		public float? ForwardStep { get; set; }

		public float? DurabilityFactor { get; set; }
	}
	internal sealed class RiftTrailConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? Duration { get; set; }

		public float? TickInterval { get; set; }

		public float? DamageFactor { get; set; }

		public float? PushFactor { get; set; }

		public float? Range { get; set; }

		public float? Angle { get; set; }

		public float? Width { get; set; }

		public bool? HitThroughWalls { get; set; }

		public bool? IncludeDestructibles { get; set; }

		public float? DurabilityFactor { get; set; }
	}
	internal sealed class FractureLineConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? Range { get; set; }

		public float? HitSpacing { get; set; }

		public float? Duration { get; set; }

		public float? TickInterval { get; set; }

		public float? DamageFactor { get; set; }

		public float? DurabilityFactor { get; set; }
	}
	internal sealed class HarvestSweepConfig
	{
		public bool? Enabled { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public string? Animation { get; set; }

		public float? LoopStart { get; set; }

		public float? LoopEnd { get; set; }

		public float? AnimationSpeed { get; set; }

		public float? MoveSpeedFactor { get; set; }

		public float? SkillRaiseFactor { get; set; }
	}
	internal sealed class BloodMagicWeaponConfig
	{
		public bool? Enabled { get; set; }

		public string? Preset { get; set; }

		public BloodMagicWeaponConfig? SummonEmpower { get; set; }

		public BloodMagicWeaponConfig? ShieldConvert { get; set; }

		public string? Animation { get; set; }

		public float? ResourceMultiplier { get; set; }

		public float? DurabilityFactor { get; set; }

		public float? Cooldown { get; set; }

		public float? CooldownReductionFactor { get; set; }

		public float? Radius { get; set; }

		public float? Duration { get; set; }

		public float? MoveSpeedFactor { get; set; }

		public float? AttackSpeedFactor { get; set; }

		public float? HealFactor { get; set; }

		public MagicSummonOverrideConfig? Summon { get; set; }
	}
	internal sealed class MagicSummonOverrideConfig
	{
		public string QualityPreset { get; set; } = "";

		public int? MaxQuality { get; set; }

		public int? LifetimeSeconds { get; set; }

		public List<MagicSummonCloneConfig>? 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 static class SecondaryAttackMagicSummonNormalizer
	{
		private const int MaxSpawnChoiceWeight = 100;

		internal static Dictionary<string, NormalizedMagicSummonOverrideConfig> Normalize(IReadOnlyDictionary<string, BloodMagicWeaponConfig> bloodMagic)
		{
			Dictionary<string, NormalizedMagicSummonOverrideConfig> dictionary = new Dictionary<string, NormalizedMagicSummonOverrideConfig>(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);
				int? lifetimeSeconds = NormalizeLifetimeSeconds(summon.LifetimeSeconds, text3);
				List<MagicSummonCloneConfig> obj = summon.SpawnChoices?.Where((MagicSummonCloneConfig magicSummonCloneConfig) => magicSummonCloneConfig != null).ToList() ?? new List<MagicSummonCloneConfig>();
				List<NormalizedMagicSummonCloneConfig> list = new List<NormalizedMagicSummonCloneConfig>();
				HashSet<string> hashSet = new HashSet<string>(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))
					{
						RuleDiagnostics.Warning("Skipping summon override '" + text3 + "' from Edge.BloodMagic.yml: sourcePrefab is required.");
						continue;
					}
					string text5 = TrimOrEmpty(item.ClonePrefab);
					if (string.IsNullOrWhiteSpace(text5))
					{
						text5 = "Edge_" + SanitizeMagicSummonId(text3) + "_" + SanitizeMagicSummonId(text4);
					}
					if (!hashSet.Add(text5))
					{
						RuleDiagnostics.Warning("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 || lifetimeSeconds.HasValue)
				{
					dictionary[text3] = new NormalizedMagicSummonOverrideConfig
					{
						EntryId = text3,
						QualityPreset = magicSummonQualityPreset,
						LifetimeSeconds = lifetimeSeconds,
						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;
			}
			RuleDiagnostics.Warning("Ignoring invalid qualityPreset '" + text + "' in summon override '" + entryId + "' from Edge.BloodMagic.yml. Valid values are countByQuality or levelByQuality.");
			return MagicSummonQualityPreset.None;
		}

		private static int? NormalizeLifetimeSeconds(int? rawLifetimeSeconds, string entryId)
		{
			if (!rawLifetimeSeconds.HasValue)
			{
				return null;
			}
			if (rawLifetimeSeconds.Value > 0)
			{
				return rawLifetimeSeconds.Value;
			}
			RuleDiagnostics.Warning(string.Format("Ignoring invalid lifetimeSeconds '{0}' in summon override '{1}' from {2}. The value must be a positive integer; the global Blood Magic summon lifetime will be used instead.", rawLifetimeSeconds.Value, entryId, "Edge.BloodMagic.yml"));
			return null;
		}

		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 enum MagicSummonQualityPreset
	{
		None,
		CountByQuality,
		LevelByQuality
	}
	internal sealed class NormalizedMagicSummonOverrideConfig
	{
		public string EntryId { get; set; } = "";

		public MagicSummonQualityPreset QualityPreset { get; set; }

		public int? LifetimeSeconds { get; set; }

		public bool HasQualityPreset => QualityPreset != MagicSummonQualityPreset.None;

		public List<NormalizedMagicSummonCloneConfig> Summons { get; set; } = new List<NormalizedMagicSummonCloneConfig>();
	}
	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 float Cooldown { get; set; } = 30f;

		public string CooldownSkill { get; set; } = "bloodMagic";

		public float CooldownReductionFactor { get; set; } = 0.5f;

		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 float Cooldown { get; set; } = 30f;

		public string CooldownSkill { get; set; } = "bloodMagic";

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float Radius { get; set; } = 7f;

		public float HealFactor { get; set; }
	}
	internal sealed class NormalizedSneakAmbushConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; } = 30f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public float ChargeMaxSeconds { get; set; } = 8f;

		public float ChargeSkillFactor { get; set; } = 2f;

		public float AggroResetRangePerChargeSecond { get; set; } = 1f;

		public float SenseBlockDurationPerChargeSecond { get; set; } = 0.25f;

		public float BackstabResetSecondsPerChargeSecond { get; set; } = 35f;

		public static NormalizedSneakAmbushConfig CreateDefault()
		{
			return new NormalizedSneakAmbushConfig();
		}

		public NormalizedSneakAmbushConfig Clone()
		{
			return (NormalizedSneakAmbushConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedCleavingThrustConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; }

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public float RangeFactor { get; set; } = 3f;

		public float Angle { get; set; } = 90f;

		public float DamageFactor { get; set; } = 1f;

		public float PushFactor { get; set; } = 6f;

		public static NormalizedCleavingThrustConfig CreateDefault()
		{
			return new NormalizedCleavingThrustConfig();
		}

		public NormalizedCleavingThrustConfig Clone()
		{
			return (NormalizedCleavingThrustConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedLaunchSlamConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; }

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public float LaunchHeight { get; set; } = 4f;

		public float DamageFactor { get; set; } = 1f;

		public static NormalizedLaunchSlamConfig CreateDefault()
		{
			return new NormalizedLaunchSlamConfig();
		}

		public NormalizedLaunchSlamConfig Clone()
		{
			return (NormalizedLaunchSlamConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedKnockbackChainConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; }

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public float PushFactor { get; set; } = 8f;

		public float ChainDecay { get; set; } = 0.75f;

		public static NormalizedKnockbackChainConfig CreateDefault()
		{
			return new NormalizedKnockbackChainConfig();
		}

		public NormalizedKnockbackChainConfig Clone()
		{
			return (NormalizedKnockbackChainConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedAftershockConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; }

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public int Waves { get; set; } = 3;

		public float Interval { get; set; } = 0.5f;

		public float WaveDecay { get; set; } = 0.2f;

		public float ForwardStep { get; set; } = 3f;

		public float DurabilityFactor { get; set; } = 1f;

		public static NormalizedAftershockConfig CreateDefault()
		{
			return new NormalizedAftershockConfig();
		}

		public NormalizedAftershockConfig Clone()
		{
			return (NormalizedAftershockConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedRiftTrailConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; }

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float Duration { get; set; } = 2f;

		public float TickInterval { get; set; } = 0.5f;

		public float DamageFactor { get; set; } = 0.25f;

		public float PushFactor { get; set; }

		public float Range { get; set; }

		public float Angle { get; set; }

		public float Width { get; set; }

		public float DurabilityFactor { get; set; } = 1f;

		public float VisualScaleFactor { get; set; } = 1.5f;

		public float VisualForwardOffset { get; set; } = 1.5f;

		public string VisualTint { get; set; } = "#ffffff";

		public float VisualAlphaFactor { get; set; } = 1f;

		public static NormalizedRiftTrailConfig CreateDefault()
		{
			return new NormalizedRiftTrailConfig();
		}

		public NormalizedRiftTrailConfig Clone()
		{
			return (NormalizedRiftTrailConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedFractureLineConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; } = 6f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float Range { get; set; } = 5f;

		public float HitSpacing { get; set; } = 0.75f;

		public float Duration { get; set; } = 1.5f;

		public float TickInterval { get; set; } = 0.3f;

		public float DamageFactor { get; set; } = 0.35f;

		public float DurabilityFactor { get; set; } = 1f;

		public static NormalizedFractureLineConfig CreateDefault()
		{
			return new NormalizedFractureLineConfig();
		}

		public NormalizedFractureLineConfig Clone()
		{
			return (NormalizedFractureLineConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedSpinningSweepConfig
	{
		public bool Enabled { get; set; } = true;

		public string Animation { get; set; } = "atgeir_secondary";

		public float Cooldown { get; set; } = 8f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public float DamageFactor { get; set; } = 0.75f;

		public float PushFactor { get; set; } = 0.5f;

		public float LoopStart { get; set; } = 0.4f;

		public float LoopEnd { get; set; } = 0.6f;

		public float AnimationSpeed { get; set; } = 1f;

		public float MoveSpeedFactor { get; set; } = 0.75f;

		public float SkillRaiseFactor { get; set; } = 0.25f;

		public static NormalizedSpinningSweepConfig CreateDefault()
		{
			return new NormalizedSpinningSweepConfig();
		}

		public NormalizedSpinningSweepConfig Clone()
		{
			return (NormalizedSpinningSweepConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedHarvestSweepConfig
	{
		public bool Enabled { get; set; } = true;

		public float Cooldown { get; set; } = 16f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1.5f;

		public float DurabilityFactor { get; set; } = 1.5f;

		public string Animation { get; set; } = "atgeir_secondary";

		public float LoopStart { get; set; } = 0.4f;

		public float LoopEnd { get; set; } = 0.6f;

		public float AnimationSpeed { get; set; } = 1f;

		public float MoveSpeedFactor { get; set; } = 0.75f;

		public float SkillRaiseFactor { get; set; } = 0.25f;

		public static NormalizedHarvestSweepConfig CreateDefault()
		{
			return new NormalizedHarvestSweepConfig();
		}

		public NormalizedHarvestSweepConfig Clone()
		{
			return (NormalizedHarvestSweepConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedMeleeOnProjectileHitConfig
	{
		public bool Enabled { get; set; } = true;

		public string Preset { get; set; } = "";

		public float Cooldown { get; set; } = 20f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public string ProjectileSpinAxis { get; set; } = "none";

		public RotationOffset ProjectileVisualRotationOffset { get; set; } = RotationOffset.Zero;

		public string Vfx { get; set; } = "";

		public int Count { get; set; } = 6;

		public float SpawnHeight { get; set; } = 10f;

		public float SpawnRadius { get; set; } = 10f;

		public float FlightTime { get; set; } = 1f;

		public float DamageFactor { get; set; } = 0.25f;

		public float PushFactor { get; set; } = 2f;

		public float Radius { get; set; } = 4f;

		public bool IncludeDirectTarget { get; set; }

		public bool IncludeDestructibles { get; set; }

		public bool TriggerOnCharactersOnly { get; set; } = true;

		public static NormalizedMeleeOnProjectileHitConfig CreateDefault()
		{
			return new NormalizedMeleeOnProjectileHitConfig
			{
				Preset = "spearRain"
			};
		}

		public NormalizedMeleeOnProjectileHitConfig Clone()
		{
			return (NormalizedMeleeOnProjectileHitConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedBoomerangConfig
	{
		public bool Enabled { get; set; } = true;

		public string Animation { get; set; } = "swing_axe2";

		public float Cooldown { get; set; } = 16f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1.5f;

		public float DurabilityFactor { get; set; } = 1f;

		public string ProjectileSpinAxis { get; set; } = "vertical";

		public RotationOffset ProjectileVisualRotationOffset { get; set; } = RotationOffset.Zero;

		public float MaxDistance { get; set; } = 12f;

		public float CurveFactor { get; set; } = 0.5f;

		public float DamageFactor { get; set; } = 1f;

		public float PushFactor { get; set; } = 1f;

		public float HitDamageDecay { get; set; } = 0.2f;

		public bool IncludeDestructibles { get; set; } = true;

		public static NormalizedBoomerangConfig CreateDefault()
		{
			return new NormalizedBoomerangConfig();
		}

		public NormalizedBoomerangConfig Clone()
		{
			return (NormalizedBoomerangConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedImpactBurstConfig
	{
		public bool Enabled { get; set; } = true;

		public string Animation { get; set; } = "battleaxe_attack1";

		public float Cooldown { get; set; } = 16f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float ResourceMultiplier { get; set; } = 1.5f;

		public float DurabilityFactor { get; set; } = 1f;

		public string ProjectileSpinAxis { get; set; } = "vertical";

		public RotationOffset ProjectileVisualRotationOffset { get; set; } = RotationOffset.Zero;

		public string Vfx { get; set; } = "vfx_archerytarget_bullseye_double";

		public float Radius { get; set; } = 4f;

		public float DamageFactor { get; set; } = 0.75f;

		public float PushFactor { get; set; } = 4f;

		public static NormalizedImpactBurstConfig CreateDefault()
		{
			return new NormalizedImpactBurstConfig();
		}

		public NormalizedImpactBurstConfig Clone()
		{
			return (NormalizedImpactBurstConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedProjectileSecondaryConfig
	{
		public string Preset { get; set; } = "";

		public float Cooldown { get; set; } = 8f;

		public float CooldownReductionFactor { get; set; } = 0.5f;

		public float DamageFactor { get; set; } = 1f;

		public float ProjectileSpeedFactor { get; set; } = 1f;

		public float ProjectileScaleFactor { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public int Count { get; set; } = 1;

		public float SpreadAngle { get; set; } = 24f;

		public int AmmoConsumption { get; set; } = -1;

		public float VolleyRadius { get; set; } = 4f;

		public float VolleyArcAngleMin { get; set; } = 35f;

		public float VolleyArcAngleMax { get; set; } = 65f;

		public float VolleyMaxRange { get; set; } = 64f;

		public float Interval { get; set; }

		public float HoldRepeatInterval { get; set; } = 0.2f;

		public float BarrageSpacing { get; set; } = 0.8f;

		public float MeteorRadius { get; set; }

		public float PierceDamageDecay { get; set; } = 0.25f;

		public float SplitAngle { get; set; } = 30f;

		public int RicochetBounces { get; set; } = 3;

		public float RicochetDecay { get; set; } = 0.2f;

		public float RicochetRoughness { get; set; } = 0.2f;

		public float SpiralRadius { get; set; } = 0.35f;

		public float SpiralTurns { get; set; } = 1.5f;

		public float SentinelDetectionRange { get; set; } = 25f;

		public float SentinelHoverDistance { get; set; } = 1.75f;

		public float SentinelHoverHeight { get; set; } = 1.6f;

		public float SentinelHoverElevationAngle { get; set; }

		public float SentinelOrbitRadius { get; set; } = 0.35f;

		public float SentinelOrbitSpeed { get; set; } = 120f;

		public float SentinelLifetime { get; set; } = 12f;

		public float SentinelAttackDelay { get; set; }

		public float MeteorSpawnHeight { get; set; } = 12f;

		public int MaxCharges { get; set; } = 6;

		public string DetonateAnimation { get; set; } = "emote_blowkiss";

		public float AoeRadiusFactor { get; set; } = 1f;

		public static NormalizedProjectileSecondaryConfig CreateDefault()
		{
			return new NormalizedProjectileSecondaryConfig();
		}

		public NormalizedProjectileSecondaryConfig Clone()
		{
			return (NormalizedProjectileSecondaryConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedWeaponConfig
	{
		public bool Enabled { get; set; } = true;

		public bool UseAutomaticFallback { get; set; }

		public NormalizedSecondaryModeConfig? Secondary { get; set; }

		public NormalizedSneakAmbushConfig? SneakAmbush { get; set; }

		public NormalizedCleavingThrustConfig? CleavingThrust { get; set; }

		public NormalizedLaunchSlamConfig? LaunchSlam { get; set; }

		public NormalizedKnockbackChainConfig? KnockbackChain { get; set; }

		public NormalizedAftershockConfig? Aftershock { get; set; }

		public NormalizedRiftTrailConfig? RiftTrail { get; set; }

		public NormalizedFractureLineConfig? FractureLine { get; set; }

		public NormalizedHarvestSweepConfig? HarvestSweep { get; set; }

		public NormalizedMeleeOnProjectileHitConfig? SpearRain { get; set; }

		public NormalizedImpactBurstConfig? ImpactBurst { get; set; }

		public NormalizedBoomerangConfig? Boomerang { get; set; }

		public NormalizedSpinningSweepConfig? SpinningSweep { get; set; }

		public MeleeSpecialPreset MeleePreset { get; set; }

		public bool HasExplicitMeleePreset { get; set; }

		public bool HasEnabledMeleeFeatureConfig
		{
			get
			{
				NormalizedSneakAmbushConfig? sneakAmbush = SneakAmbush;
				if (sneakAmbush == null || !sneakAmbush.Enabled)
				{
					NormalizedCleavingThrustConfig? cleavingThrust = CleavingThrust;
					if (cleavingThrust == null || !cleavingThrust.Enabled)
					{
						NormalizedLaunchSlamConfig? launchSlam = LaunchSlam;
						if (launchSlam == null || !launchSlam.Enabled)
						{
							NormalizedKnockbackChainConfig? knockbackChain = KnockbackChain;
							if (knockbackChain == null || !knockbackChain.Enabled)
							{
								NormalizedAftershockConfig? aftershock = Aftershock;
								if (aftershock == null || !aftershock.Enabled)
								{
									NormalizedRiftTrailConfig? riftTrail = RiftTrail;
									if (riftTrail == null || !riftTrail.Enabled)
									{
										NormalizedFractureLineConfig? fractureLine = FractureLine;
										if (fractureLine == null || !fractureLine.Enabled)
										{
											NormalizedImpactBurstConfig? impactBurst = ImpactBurst;
											if (impactBurst == null || !impactBurst.Enabled)
											{
												NormalizedBoomerangConfig? boomerang = Boomerang;
												if (boomerang == null || !boomerang.Enabled)
												{
													NormalizedSpinningSweepConfig? spinningSweep = SpinningSweep;
													if (spinningSweep == null || !spinningSweep.Enabled)
													{
														return HarvestSweep?.Enabled ?? false;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
				return true;
			}
		}

		public NormalizedWeaponConfig Clone()
		{
			return (NormalizedWeaponConfig)MemberwiseClone();
		}
	}
	internal sealed class NormalizedSecondaryModeConfig
	{
		public string Type { get; set; } = "";

		public string Animation { get; set; } = "";

		public float ResourceMultiplier { get; set; } = 1f;

		public float OutputMultiplier { get; set; } = 1f;

		public float DurabilityFactor { get; set; } = 1f;

		public NormalizedProjectileSecondaryConfig? Projectile { get; set; }

		public string CopyFrom { get; set; } = "";

		public NormalizedMeleeOnProjectileHitConfig? OnProjectileHit { get; set; }

		public NormalizedSummonEmpowerSecondaryConfig? SummonEmpower { get; set; }

		public NormalizedShieldConvertSecondaryConfig? ShieldConvert { get; set; }
	}
	internal static class SecondaryAttackWarningLog
	{
		private static readonly HashSet<string> ReportedWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> ReportedIssues = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static void Reset()
		{
			ReportedWarnings.Clear();
			ReportedIssues.Clear();
		}

		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))
			{
				RuleDiagnostics.Warning(message);
			}
		}
	}
	internal sealed class SecondaryAttackWeaponNormalizationResult
	{
		public Dictionary<string, NormalizedWeaponConfig> Weapons { get; set; } = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase);

		public Dictionary<string, NormalizedWeaponConfig> GlobalRangedPresets { get; set; } = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase);

		public Dictionary<string, NormalizedWeaponConfig> GlobalBloodMagicPresets { get; set; } = new Dictionary<string, NormalizedWeaponConfig>(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<string, RangedWeaponConfig> ranged, IReadOnlyDictionary<string, MeleeWeaponConfig> melee, IReadOnlyDictionary<string, BloodMagicWeaponConfig> bloodMagic)
		{
			Dictionary<string, NormalizedWeaponConfig> dictionary = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase);
			Dictionary<string, NormalizedWeaponConfig> globalRangedPresets = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase);
			Dictionary<string, NormalizedWeaponConfig> globalBloodMagicPresets = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase);
			RangedWeaponConfig rangedWeaponConfig = null;
			string key;
			RangedWeaponConfig value;
			foreach (KeyValuePair<string, RangedWeaponConfig> 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<string, RangedWeaponConfig> 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), "Edge.Ranged.yml");
					}
				}
			}
			MeleeWeaponConfig meleeWeaponConfig = null;
			MeleeWeaponConfig value2;
			foreach (KeyValuePair<string, MeleeWeaponConfig> 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<string, MeleeWeaponConfig> 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), "Edge.Melee.yml");
					}
				}
			}
			BloodMagicWeaponConfig bloodMagicWeaponConfig = null;
			BloodMagicWeaponConfig value3;
			foreach (KeyValuePair<string, BloodMagicWeaponConfig> 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<string, BloodMagicWeaponConfig> 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), "Edge.BloodMagic.yml");
					}
				}
			}
			return new SecondaryAttackWeaponNormalizationResult
			{
				Weapons = dictionary,
				GlobalRangedPresets = globalRangedPresets,
				GlobalBloodMagicPresets = globalBloodMagicPresets,
				GlobalMeleeFallback = normalizedWeaponConfig
			};
		}

		private static void AddGlobalRangedPreset(Dictionary<string, NormalizedWeaponConfig> globalRangedPresets, string presetName, RangedWeaponConfig? rawConfig)
		{
			if (rawConfig != null)
			{
				globalRangedPresets[presetName] = new NormalizedWeaponConfig
				{
					Enabled = true,
					Secondary = NormalizeRanged(rawConfig, null, presetName)
				};
			}
		}

		private static void AddGlobalBloodMagicPreset(Dictionary<string, NormalizedWeaponConfig> globalBloodMagicPresets, string presetName, BloodMagicWeaponConfig? rawConfig)
		{
			if (rawConfig != null)
			{
				globalBloodMagicPresets[presetName] = new NormalizedWeaponConfig
				{
					Enabled = (rawConfig.Enabled ?? true),
					Secondary = NormalizeBloodMagic(rawConfig, null, presetName)
				};
			}
		}

		private static NormalizedWeaponConfig? ResolveRangedPresetFallback(RangedWeaponConfig rawConfig, IReadOnlyDictionary<string, NormalizedWeaponConfig> 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<string, NormalizedWeaponConfig> 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<string, NormalizedWeaponConfig> normalizedWeapons, string prefabName, NormalizedWeaponConfig weaponConfig, string sourceFileName)
		{
			string text = prefabName.Trim();
			if (normalizedWeapons.ContainsKey(text))
			{
				RuleDiagnostics.Warning("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)
		{
			WarnAboutIgnoredMeleeOptions("Global", 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)
		{
			WarnAboutIgnoredMeleeOptions(prefabName, raw);
			MeleeSpecialPreset preset;
			bool flag = TryParseExplicitMeleePreset(prefabName, raw.Preset, out preset);
			if (!flag)
			{
				preset = ResolveImplicitMeleePreset(prefabName, raw);
			}
			SneakAmbushConfig sneakAmbush = raw.SneakAmbush;
			MeleeOnProjectileHitConfig spearRain = raw.SpearRain;
			object obj;
			if (preset != MeleeSpecialPreset.ImpactBurst)
			{
				ImpactBurstConfig? impactBurst = raw.ImpactBurst;
				obj = ((impactBurst != null && impactBurst.Enabled == false) ? NormalizeImpactBurst(raw.ImpactBurst, fallback?.ImpactBurst) : null);
			}
			else
			{
				obj = NormalizeImpactBurst(raw.ImpactBurst ?? new ImpactBurstConfig(), fallback?.ImpactBurst);
			}
			NormalizedImpactBurstConfig normalizedImpactBurstConfig = (NormalizedImpactBurstConfig)obj;
			object obj2;
			if (preset != MeleeSpecialPreset.Boomerang)
			{
				BoomerangConfig? boomerang = raw.Boomerang;
				obj2 = ((boomerang != null && boomerang.Enabled == false) ? NormalizeBoomerang(raw.Boomerang, fallback?.Boomerang) : null);
			}
			else
			{
				obj2 = NormalizeBoomerang(raw.Boomerang ?? new BoomerangConfig(), fallback?.Boomerang);
			}
			NormalizedBoomerangConfig normalizedBoomerangConfig = (NormalizedBoomerangConfig)obj2;
			object obj3;
			if (preset != MeleeSpecialPreset.SpinningSweep)
			{
				SpinningSweepConfig? spinningSweep = raw.SpinningSweep;
				obj3 = ((spinningSweep != null && spinningSweep.Enabled == false) ? NormalizeSpinningSweep(raw.SpinningSweep, fallback?.SpinningSweep) : null);
			}
			else
			{
				obj3 = NormalizeSpinningSweep(raw.SpinningSweep ?? new SpinningSweepConfig(), fallback?.SpinningSweep);
			}
			NormalizedSpinningSweepConfig normalizedSpinningSweepConfig = (NormalizedSpinningSweepConfig)obj3;
			if (flag)
			{
				LogIgnoredExplicitMeleePresetBlocks(prefabName, raw, preset);
			}
			MeleeOnProjectileHitConfig meleeOnProjectileHitConfig = ((preset == MeleeSpecialPreset.SpearRain) ? (spearRain ?? new MeleeOnProjectileHitConfig()) : null);
			NormalizedMeleeOnProjectileHitConfig fallbackOnProjectileHit = ((preset != MeleeSpecialPreset.SpearRain) ? null : fallback?.SpearRain);
			NormalizedSecondaryModeConfig secondary = ((preset == MeleeSpecialPreset.ImpactBurst && normalizedImpactBurstConfig != null) ? CreateImpactBurstSecondary(normalizedImpactBurstConfig) : ((preset == MeleeSpecialPreset.Boomerang && normalizedBoomerangConfig != null) ? CreateBoomerangSecondary(normalizedBoomerangConfig) : ((preset == MeleeSpecialPreset.SpinningSweep && normalizedSpinningSweepConfig != null) ? CreateSpinningSweepSecondary(normalizedSpinningSweepConfig) : (HasMeleeSecondaryConfig(raw, preset, meleeOnProjectileHitConfig) ? NormalizeMelee(raw, meleeOnProjectileHitConfig, preset == MeleeSpecialPreset.SpearRain, fallbackOnProjectileHit, ResolveMeleeSecondaryType(raw, preset)) : null))));
			NormalizedWeaponConfig obj4 = new NormalizedWeaponConfig
			{
				Enabled = (raw.Enabled ?? true),
				Secondary = secondary,
				SneakAmbush = ((preset == MeleeSpecialPreset.SneakAmbush) ? NormalizeSneakAmbush(sneakAmbush ?? new SneakAmbushConfig(), fallback?.SneakAmbush) : ((sneakAmbush != null && sneakAmbush.Enabled == false) ? NormalizeSneakAmbush(sneakAmbush, fallback?.SneakAmbush) : null))
			};
			object cleavingThrust2;
			if (preset != MeleeSpecialPreset.CleavingThrust)
			{
				CleavingThrustConfig? cleavingThrust = raw.CleavingThrust;
				cleavingThrust2 = ((cleavingThrust != null && cleavingThrust.Enabled == false) ? NormalizeCleavingThrust(raw.CleavingThrust, fallback?.CleavingThrust) : null);
			}
			else
			{
				cleavingThrust2 = NormalizeCleavingThrust(raw.CleavingThrust ?? new CleavingThrustConfig(), fallback?.CleavingThrust);
			}
			obj4.CleavingThrust = (NormalizedCleavingThrustConfig?)cleavingThrust2;
			object launchSlam2;
			if (preset != MeleeSpecialPreset.LaunchSlam)
			{
				LaunchSlamConfig? launchSlam = raw.LaunchSlam;
				launchSlam2 = ((launchSlam != null && launchSlam.Enabled == false) ? NormalizeLaunchSlam(raw.LaunchSlam, fallback?.LaunchSlam) : null);
			}
			else
			{
				launchSlam2 = NormalizeLaunchSlam(raw.LaunchSlam ?? new LaunchSlamConfig(), fallback?.LaunchSlam);
			}
			obj4.LaunchSlam = (NormalizedLaunchSlamConfig?)launchSlam2;
			object knockbackChain2;
			if (preset != MeleeSpecialPreset.KnockbackChain)
			{
				KnockbackChainConfig? knockbackChain = raw.KnockbackChain;
				knockbackChain2 = ((knockbackChain != null && knockbackChain.Enabled == false) ? NormalizeKnockbackChain(raw.KnockbackChain, fallback?.KnockbackChain) : null);
			}
			else
			{
				knockbackChain2 = NormalizeKnockbackChain(raw.KnockbackChain ?? new KnockbackChainConfig(), fallback?.KnockbackChain);
			}
			obj4.KnockbackChain = (NormalizedKnockbackChainConfig?)knockbackChain2;
			object aftershock2;
			if (preset != MeleeSpecialPreset.Aftershock)
			{
				AftershockConfig? aftershock = raw.Aftershock;
				aftershock2 = ((aftershock != null && aftershock.Enabled == false) ? NormalizeAftershock(raw.Aftershock, fallback?.Aftershock) : null);
			}
			else
			{
				aftershock2 = NormalizeAftershock(raw.Aftershock ?? new AftershockConfig(), fallback?.Aftershock);
			}
			obj4.Aftershock = (NormalizedAftershockConfig?)aftershock2;
			object riftTrail2;
			if (preset != MeleeSpecialPreset.RiftTrail)
			{
				RiftTrailConfig? riftTrail = raw.RiftTrail;
				riftTrail2 = ((riftTrail != null && riftTrail.Enabled == false) ? NormalizeRiftTrail(raw.RiftTrail, fallback?.RiftTrail) : null);
			}
			else
			{
				riftTrail2 = NormalizeRiftTrail(raw.RiftTrail ?? new RiftTrailConfig(), fallback?.RiftTrail);
			}
			obj4.RiftTrail = (NormalizedRiftTrailConfig?)riftTrail2;
			object fractureLine2;
			if (preset != MeleeSpecialPreset.FractureLine)
			{
				FractureLineConfig? fractureLine = raw.FractureLine;
				fractureLine2 = ((fractureLine != null && fractureLine.Enabled == false) ? NormalizeFractureLine(raw.FractureLine, fallback?.FractureLine) : null);
			}
			else
			{
				fractureLine2 = NormalizeFractureLine(raw.FractureLine ?? new FractureLineConfig(), fallback?.FractureLine);
			}
			obj4.FractureLine = (NormalizedFractureLineConfig?)fractureLine2;
			obj4.ImpactBurst = normalizedImpactBurstConfig;
			obj4.Boomerang = normalizedBoomerangConfig;
			obj4.SpinningSweep = normalizedSpinningSweepConfig;
			obj4.HarvestSweep = ((raw.HarvestSweep != null) ? NormalizeHarvestSweep(raw.HarvestSweep, fallback?.HarvestSweep) : null);
			obj4.MeleePreset = preset;
			obj4.HasExplicitMeleePreset = flag;
			return obj4;
		}

		private static void WarnAboutIgnoredMeleeOptions(string prefabName, MeleeWeaponConfig raw)
		{
			string text = (string.IsNullOrWhiteSpace(prefabName) ? "<unknown>" : prefabName.Trim());
			MeleeOnProjectileHitConfig? spearRain = raw.SpearRain;
			if (spearRain != null && spearRain.TriggerOnCharactersOnly.HasValue)
			{
				SecondaryAttackWarningLog.WarnOnce("ignored-melee-option:" + text + ":spearRain.triggerOnCharactersOnly", "Ignoring unsupported melee option '" + text + ".spearRain.triggerOnCharactersOnly': Spear Rain remains characters-only (triggerOnCharactersOnly is always true).");
			}
			CleavingThrustConfig? cleavingThrust = raw.CleavingThrust;
			if (cleavingThrust != null && cleavingThrust.HitThroughWalls.HasValue)
			{
				SecondaryAttackWarningLog.WarnOnce("ignored-melee-option:" + text + ":cleavingThrust.hitThroughWalls", "Ignoring unsupported melee option '" + text + ".cleavingThrust.hitThroughWalls': Cleaving Thrust remains blocked by the environment (hitThroughWalls is always false).");
			}
			RiftTrailConfig? riftTrail = raw.RiftTrail;
			if (riftTrail != null && riftTrail.HitThroughWalls.HasValue)
			{
				SecondaryAttackWarningLog.WarnOnce("ignored-melee-option:" + text + ":riftTrail.hitThroughWalls", "Ignoring unsupported melee option '" + text + ".riftTrail.hitThroughWalls': Rift Trail remains blocked by the environment (hitThroughWalls is always false).");
			}
			RiftTrailConfig? riftTrail2 = raw.RiftTrail;
			if (riftTrail2 != null && riftTrail2.IncludeDestructibles.HasValue)
			{
				SecondaryAttackWarningLog.WarnOnce("ignored-melee-option:" + text + ":riftTrail.includeDestructibles", "Ignoring unsupported melee option '" + text + ".riftTrail.includeDestructibles': Rift Trail remains characters-only (includeDestructibles is always false).");
			}
		}

		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;
			}
			RuleDiagnostics.Warning("Unknown melee preset '" + text + "' on " + prefabName + "; no melee special preset will be applied. Valid values: none, sneakAmbush, cleavingThrust, spearRain, impactBurst, boomerang, spinningSweep, launchSlam, knockbackChain, aftershock, riftTrail, fractureLine.");
			preset = MeleeSpecialPreset.None;
			return true;
		}

		private static bool TryParseMeleePreset(string rawPreset, out MeleeSpecialPreset preset)
		{
			string text = rawPreset.Trim();
			if (text.Equals("none", StringComparison.OrdinalIgnoreCase) || text.Equals("off", StringComparison.OrdinalIgnoreCase) || text.Equals("disabled", StringComparison.OrdinalIgnoreCase))
			{
				preset = MeleeSpecialPreset.None;
				return true;
			}
			preset = MeleeSpecialPreset.None;
			if (PresetCatalog.TryGet(text, out PresetInfo info) && info.Group == PresetGroup.Melee && Enum.TryParse<MeleeSpecialPreset>(info.Key, ignoreCase: true, out preset))
			{
				return preset != MeleeSpecialPreset.None;
			}
			return false;
		}

		private static MeleeSpecialPreset ResolveImplicitMeleePreset(string prefabName, MeleeWeaponConfig raw)
		{
			List<MeleeSpecialPreset> list = new List<MeleeSpecialPreset>();
			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)
			{
				RuleDiagnostics.Warning("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<MeleeSpecialPreset> 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<MeleeSpecialPreset> candidates, MeleeSpecialPreset selectedPreset)
		{
			List<string> list = new List<string>();
			foreach (MeleeSpecialPreset candidate in candidates)
			{
				if (candidate != selectedPreset)
				{
					list.Add(FormatMeleePreset(candidate));
				}
			}
			return string.Join(", ", list);
		}

		private static string FormatMeleePreset(MeleeSpecialPreset preset)
		{
			return PresetCatalog.Canonicalize(preset.ToString()) ?? "none";
		}

		private static void LogIgnoredExplicitMeleePresetBlocks(string prefabName, MeleeWeaponConfig raw, MeleeSpecialPreset selectedPreset)
		{
			List<string> list = new List<string>();
			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)
			{
				RuleDiagnostics.Warning("Ignoring melee preset block(s) " + string.Join(", ", list) + " on " + prefabName + " because preset is " + FormatMeleePreset(selectedPreset) + ".");
			}
		}

		internal static NormalizedSecondaryModeConfig CreateImpactBurstSecondary(NormalizedImpactBurstConfig impactBurst)
		{
			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,
					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
				}
			};
		}

		internal static NormalizedSecondaryModeConfig CreateBoomerangSecondary(NormalizedBoomerangConfig boomerang)
		{
			return new NormalizedSecondaryModeConfig
			{
				Type = "copy",
				Animation = boomerang.Animation,
				ResourceMultiplier = boomerang.ResourceMultiplier,
				OutputMultiplier = 1f,
				DurabilityFactor = boomerang.DurabilityFactor,
				CopyFrom = "SpearFlint"
			};
		}

		internal static NormalizedSecondaryModeConfig CreateSpinningSweepSecondary(NormalizedSpinningSweepConfig spinningSweep)
		{
			return new NormalizedSecondaryModeConfig
			{
				Type = "copy",
				Animation = (string.IsNullOrWhiteSpace(spinningSweep.Animation) ? "atgeir_secondary" : spinningSweep.Animation),
				ResourceMultiplier = spinningSweep.ResourceMultiplier,
				OutputMultiplier = 1f,
				DurabilityFactor = spinningSweep.DurabilityFactor,
				CopyFrom = ""
			};
		}

		private static NormalizedSecondaryModeConfig NormalizeRanged(RangedWeaponConfig rawRanged, NormalizedSecondaryModeConfig? fallback, string? presetOverride = null)
		{
			NormalizedSecondaryModeConfig normalizedSecondaryModeConfig = fallback ?? new NormalizedSecondaryModeConfig();
			NormalizedProjectileSecondaryConfig normalizedProjectileSecondaryConfig = fallback?.Projectile ?? new NormalizedProjectileSecondaryConfig();
			string preset = PresetCatalog.Canonicalize(presetOverride ?? rawRanged.Preset ?? normalizedProjectileSecondaryConfig.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, normalizedProjectileSecondaryConfig, preset),
				CopyFrom = ""
			};
		}

		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 =