Decompiled source of MagicStaffTweakerClientOnly v2.2.5

MagicStaffTweaker.Core.dll

Decompiled 14 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MagicStaffTweaker.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MagicStaffTweaker.Core")]
[assembly: AssemblyTitle("MagicStaffTweaker.Core")]
[assembly: AssemblyVersion("1.0.0.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;
		}
	}
}
namespace MagicStaffTweaker.Core
{
	public static class Modifier
	{
		public static float Apply(float vanilla, ModifierMode mode, float value)
		{
			return mode switch
			{
				ModifierMode.Vanilla => vanilla, 
				ModifierMode.Add => vanilla + value, 
				ModifierMode.Multiply => vanilla * value, 
				ModifierMode.Override => value, 
				_ => vanilla, 
			};
		}

		public static int Apply(int vanilla, ModifierMode mode, int value)
		{
			return mode switch
			{
				ModifierMode.Vanilla => vanilla, 
				ModifierMode.Add => vanilla + value, 
				ModifierMode.Multiply => (int)MathF.Round(vanilla * value), 
				ModifierMode.Override => value, 
				_ => vanilla, 
			};
		}
	}
	public sealed class StaffDefinition
	{
		public string Prefab { get; }

		public string DisplayName { get; }

		public StaffMagicType MagicType { get; }

		public StaffFeatures Features { get; }

		public StaffDefinition(string prefab, string displayName, StaffMagicType magicType, StaffFeatures features)
		{
			Prefab = prefab;
			DisplayName = displayName;
			MagicType = magicType;
			Features = features;
		}
	}
	[Flags]
	public enum StaffFeatures
	{
		None = 0,
		Projectile = 1,
		Explosion = 2,
		Summon = 4,
		Shield = 8,
		Area = 0x10,
		Chain = 0x20,
		SecondaryAttack = 0x40,
		BloodMagic = 0x80,
		ElementalMagic = 0x100
	}
	public enum StaffMagicType
	{
		Elemental,
		Blood
	}
	public enum ModifierMode
	{
		Vanilla,
		Add,
		Multiply,
		Override
	}
	public static class VanillaStaffs
	{
		public static readonly IReadOnlyList<StaffDefinition> All = new StaffDefinition[12]
		{
			new StaffDefinition("StaffFireball", "Staff of Embers", StaffMagicType.Elemental, StaffFeatures.Projectile | StaffFeatures.Explosion | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffIceShards", "Staff of Frost", StaffMagicType.Elemental, StaffFeatures.Projectile | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffClusterbomb", "Staff of Fracturing", StaffMagicType.Elemental, StaffFeatures.Projectile | StaffFeatures.Explosion | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffGreenRoots", "Staff of the Wild", StaffMagicType.Elemental, StaffFeatures.Projectile | StaffFeatures.Summon | StaffFeatures.Area | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffLightning", "Dundr", StaffMagicType.Elemental, StaffFeatures.Projectile | StaffFeatures.Chain | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffOrbofAhri", "Echo Spike", StaffMagicType.Blood, StaffFeatures.Projectile | StaffFeatures.Explosion | StaffFeatures.BloodMagic),
			new StaffDefinition("StaffSpiritCaller", "Spirit Caller", StaffMagicType.Blood, StaffFeatures.Projectile | StaffFeatures.Summon | StaffFeatures.BloodMagic),
			new StaffDefinition("StaffFrostOrbs", "Northern Vengeance", StaffMagicType.Blood, StaffFeatures.Projectile | StaffFeatures.Explosion | StaffFeatures.BloodMagic),
			new StaffDefinition("StaffThunderBlood", "Lightning Strike", StaffMagicType.Elemental, StaffFeatures.Area | StaffFeatures.ElementalMagic),
			new StaffDefinition("StaffShield", "Staff of Protection", StaffMagicType.Blood, StaffFeatures.Shield | StaffFeatures.Area | StaffFeatures.BloodMagic),
			new StaffDefinition("StaffSkeleton", "Dead Raiser", StaffMagicType.Blood, StaffFeatures.Summon | StaffFeatures.BloodMagic),
			new StaffDefinition("StaffRedTroll", "Trollstav", StaffMagicType.Blood, StaffFeatures.Summon | StaffFeatures.BloodMagic)
		};
	}
}

MagicStaffTweaker.dll

Decompiled 14 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Extensions;
using Jotunn.Managers;
using MagicStaffTweaker.Core;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MagicStaffTweaker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.2.5.0")]
[assembly: AssemblyInformationalVersion("2.2.5")]
[assembly: AssemblyProduct("MagicStaffTweaker")]
[assembly: AssemblyTitle("MagicStaffTweaker")]
[assembly: AssemblyVersion("2.2.5.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;
		}
	}
}
namespace MagicStaffTweaker
{
	public static class ConfigPersistence
	{
		public static string Key(string section, string key)
		{
			return section + "\n" + key;
		}

		public static HashSet<string> ReadKeys(string text)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string section = string.Empty;
			using StringReader stringReader = new StringReader(text);
			string text2;
			while ((text2 = stringReader.ReadLine()) != null)
			{
				text2 = text2.Trim();
				if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))
				{
					section = text2.Substring(1, text2.Length - 2);
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					hashSet.Add(Key(section, text2.Substring(0, num).Trim()));
				}
			}
			return hashSet;
		}
	}
	public static class DamageTuning
	{
		public static float SpellFactor(ModifierMode mode, float value)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			float num = Modifier.Apply(1f, mode, value);
			if (!float.IsNaN(num) && !float.IsInfinity(num) && !(num < 0f))
			{
				return num;
			}
			return 1f;
		}

		public static bool ValidHealth(float value)
		{
			if (value > 0f)
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		public static bool ValidCooldown(float value)
		{
			if (value >= 0f)
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	public static class HealthDefaults
	{
		private static readonly FieldInfo? DefaultField = typeof(ConfigEntryBase).GetField("<DefaultValue>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

		public static void Apply(ConfigEntry<float> entry, float vanilla)
		{
			if (DamageTuning.ValidHealth(vanilla))
			{
				DefaultField?.SetValue(entry, vanilla);
				if (!DamageTuning.ValidHealth(entry.Value))
				{
					entry.Value = vanilla;
				}
			}
		}
	}
	[BepInPlugin("com.magicstafftweaker.client", "Magic Staff Tweaker", "2.2.5")]
	[BepInDependency("com.jotunn.jotunn", "2.30.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Attack), "FireProjectileBurst", new Type[] { })]
		private static class LocalSummonBurstPatch
		{
			private static void Prefix(Attack __instance, Humanoid ___m_character, ItemData ___m_weapon, out int? __state)
			{
				__state = null;
				if (TryGetLocalStaffConfig((Character)(object)___m_character, ___m_weapon, out StaffConfig cfg) && cfg.SummonsPerCast.IntendedVisible)
				{
					__state = __instance.m_projectiles;
					__instance.m_projectiles = 1;
				}
			}

			private static void Finalizer(Attack __instance, int? __state)
			{
				if (__state.HasValue)
				{
					__instance.m_projectiles = __state.Value;
				}
			}
		}

		[HarmonyPatch(typeof(SpawnAbility), "Setup", new Type[]
		{
			typeof(Character),
			typeof(Vector3),
			typeof(float),
			typeof(HitData),
			typeof(ItemData),
			typeof(ItemData)
		})]
		private static class LocalSummonSetupPatch
		{
			private static void Prefix(SpawnAbility __instance, Character owner, ItemData item)
			{
				if (TryGetLocalStaffConfig(owner, item, out StaffConfig cfg) && cfg.SummonsPerCast.IntendedVisible)
				{
					SummonControls.ApplyToInstance(__instance, cfg.SummonsPerCast.Value.Value, cfg.IgnoreDeadRaiserLevelLimit.Value.Value && ((Object)item.m_dropPrefab).name == "StaffSkeleton");
				}
			}
		}

		[HarmonyPatch(typeof(Tameable), "UnsummonMaxInstances", new Type[] { typeof(int) })]
		private static class LocalDeadRaiserLimitPatch
		{
			private static bool Prefix(Character ___m_character, MonsterAI ___m_monsterAI, ZNetView ___m_nview)
			{
				Plugin instance = _instance;
				if ((Object)(object)instance == (Object)null || !instance._configs.TryGetValue("StaffSkeleton", out StaffConfig value) || !value.Enabled.Value || !value.IgnoreDeadRaiserLevelLimit.Value.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)___m_character == (Object)null || !IsDeadRaiserSummon(___m_character) || (Object)(object)___m_nview == (Object)null || !___m_nview.IsValid() || !___m_nview.IsOwner() || (Object)(object)___m_monsterAI == (Object)null)
				{
					return true;
				}
				GameObject followTarget = ___m_monsterAI.GetFollowTarget();
				return (Object)(object)((followTarget != null) ? followTarget.GetComponent<Player>() : null) != (Object)(object)Player.m_localPlayer;
			}
		}

		[HarmonyPatch(typeof(Attack), "ModifyDamage", new Type[]
		{
			typeof(HitData),
			typeof(float)
		})]
		private static class LocalSpellDamagePatch
		{
			private static void Postfix(HitData hitData, Humanoid ___m_character, ItemData ___m_weapon)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				if (hitData != null && TryGetLocalSpellConfig((Character)(object)___m_character, ___m_weapon, out StaffConfig cfg))
				{
					((DamageTypes)(ref hitData.m_damage)).Modify(DamageTuning.SpellFactor(cfg.DamageMultiplier.Mode.Value, cfg.DamageMultiplier.Value.Value));
				}
			}
		}

		private sealed class LocalAoeFactor
		{
			public float Value = 1f;
		}

		[HarmonyPatch(typeof(Aoe), "Setup", new Type[]
		{
			typeof(Character),
			typeof(Vector3),
			typeof(float),
			typeof(HitData),
			typeof(ItemData),
			typeof(ItemData)
		})]
		private static class LocalAoeSetupPatch
		{
			private static void Postfix(Aoe __instance, Character owner, HitData hitData, ItemData item)
			{
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				LocalAoeFactor value = LocalAoeFactors.GetValue(__instance, (Aoe _) => new LocalAoeFactor());
				value.Value = 1f;
				if ((!__instance.m_useAttackSettings || hitData == null) && TryGetLocalSpellConfig(owner, item, out StaffConfig cfg))
				{
					value.Value = DamageTuning.SpellFactor(cfg.DamageMultiplier.Mode.Value, cfg.DamageMultiplier.Value.Value);
				}
			}
		}

		[HarmonyPatch(typeof(Aoe), "GetDamage", new Type[] { typeof(int) })]
		private static class LocalAoeDamagePatch
		{
			private static void Postfix(Aoe __instance, ref DamageTypes __result)
			{
				if (LocalAoeFactors.TryGetValue(__instance, out LocalAoeFactor value))
				{
					((DamageTypes)(ref __result)).Modify(value.Value);
				}
			}
		}

		private sealed class SummonWeaponDamageBaseline
		{
			public readonly DamageTypes Damages;

			public readonly DamageTypes DamagesPerLevel;

			public SummonWeaponDamageBaseline(ItemData item)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				Damages = item.m_shared.m_damages;
				DamagesPerLevel = item.m_shared.m_damagesPerLevel;
			}
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class SummonFinalDamagePatch
		{
			private static void Prefix(HitData hit)
			{
				Plugin instance = _instance;
				if ((Object)(object)instance == (Object)null || hit == null)
				{
					return;
				}
				Character attacker = hit.GetAttacker();
				if ((Object)(object)attacker == (Object)null)
				{
					return;
				}
				string summonStaffPrefab = GetSummonStaffPrefab(attacker);
				if (summonStaffPrefab == null || !instance._configs.TryGetValue(summonStaffPrefab, out StaffConfig value))
				{
					return;
				}
				try
				{
					ApplyFinalSummonDamage(hit, value);
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[MagicStaffTweaker] Final summon damage patch failed: {arg}");
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		private static class SummonWeaponDamagePatch
		{
			private static void Postfix(Humanoid __instance, ItemData item, bool __result)
			{
				if (!__result || item == null || (Object)(object)__instance == (Object)null || !IsDeadRaiserCreature(__instance))
				{
					return;
				}
				Plugin instance = _instance;
				if ((Object)(object)instance == (Object)null || !instance._configs.TryGetValue("StaffSkeleton", out StaffConfig value))
				{
					return;
				}
				try
				{
					ApplySummonWeaponDamage(item, value);
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[MagicStaffTweaker] Summon weapon damage patch failed: {arg}");
				}
			}

			private static bool IsDeadRaiserCreature(Humanoid humanoid)
			{
				string name = ((Object)((Component)humanoid).gameObject).name;
				if (!name.Contains("Skeleton_Friendly", StringComparison.Ordinal))
				{
					return name.Contains("SkeletonArcher_Friendly", StringComparison.Ordinal);
				}
				return true;
			}
		}

		private sealed class StaffBaseline
		{
			public float MaxDurability;

			public float DurabilityPerLevel;

			public bool UseDurability;

			public float MovementModifier;

			public float BlockPower;

			public float BlockPowerPerLevel;

			public float DeflectionForce;

			public float DeflectionForcePerLevel;

			public float TimedBlockBonus;

			public float BlockStaminaUseFlatValue;

			public float BlockStaminaUseModifier;

			public AttackBaseline PrimaryAttack = new AttackBaseline();

			public AttackBaseline SecondaryAttack = new AttackBaseline();

			public static StaffBaseline Capture(GameObject prefab)
			{
				StaffBaseline staffBaseline = new StaffBaseline();
				SharedData val = prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
				if (val == null)
				{
					return staffBaseline;
				}
				staffBaseline.MaxDurability = GetFloat(val, "m_maxDurability");
				staffBaseline.DurabilityPerLevel = GetFloat(val, "m_durabilityPerLevel");
				staffBaseline.UseDurability = GetBool(val, "m_useDurability");
				staffBaseline.MovementModifier = GetFloat(val, "m_movementModifier");
				staffBaseline.BlockPower = GetFloat(val, "m_blockPower");
				staffBaseline.BlockPowerPerLevel = GetFloat(val, "m_blockPowerPerLevel");
				staffBaseline.DeflectionForce = GetFloat(val, "m_deflectionForce");
				staffBaseline.DeflectionForcePerLevel = GetFloat(val, "m_deflectionForcePerLevel");
				staffBaseline.TimedBlockBonus = GetFloat(val, "m_timedBlockBonus");
				staffBaseline.BlockStaminaUseFlatValue = GetFloat(val, "m_blockStaminaUseFlatValue");
				staffBaseline.BlockStaminaUseModifier = GetFloat(val, "m_blockStaminaUseModifier");
				staffBaseline.PrimaryAttack = AttackBaseline.Capture(val.m_attack, prefab);
				staffBaseline.SecondaryAttack = AttackBaseline.Capture(val.m_secondaryAttack, prefab);
				return staffBaseline;
			}

			public void Restore(GameObject prefab)
			{
				SharedData val = prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
				if (val != null)
				{
					SetField(val, "m_maxDurability", MaxDurability);
					SetField(val, "m_durabilityPerLevel", DurabilityPerLevel);
					SetField(val, "m_useDurability", UseDurability);
					SetField(val, "m_movementModifier", MovementModifier);
					SetField(val, "m_blockPower", BlockPower);
					SetField(val, "m_blockPowerPerLevel", BlockPowerPerLevel);
					SetField(val, "m_deflectionForce", DeflectionForce);
					SetField(val, "m_deflectionForcePerLevel", DeflectionForcePerLevel);
					SetField(val, "m_timedBlockBonus", TimedBlockBonus);
					SetField(val, "m_blockStaminaUseFlatValue", BlockStaminaUseFlatValue);
					SetField(val, "m_blockStaminaUseModifier", BlockStaminaUseModifier);
					PrimaryAttack.Restore(val.m_attack);
					SecondaryAttack.Restore(val.m_secondaryAttack);
				}
			}

			private static float GetFloat(object target, string name)
			{
				object field = GetField(target, name, 0f);
				if (field is float)
				{
					return (float)field;
				}
				return 0f;
			}

			private static bool GetBool(object target, string name)
			{
				object field = GetField(target, name, false);
				bool flag = default(bool);
				int num;
				if (field is bool)
				{
					flag = (bool)field;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}

			private static object? GetField(object target, string name, object fallback)
			{
				return target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) ?? fallback;
			}
		}

		private sealed class AttackBaseline
		{
			public float AttackEitr;

			public float AttackHealthPercentage;

			public float AttackCooldown;

			public float AttackForce;

			public float AttackForceForward;

			public float ProjectileAccuracyMin;

			public float ProjectileVelocity;

			public int Projectiles;

			public float AttackDrawTime;

			public float AttackDrawPercentage;

			public float AttackStamina;

			public float AttackStaminaModifier;

			public float StatusEffectChance;

			public float AttackHealth;

			public float HealthReturnHit;

			public float AttackEitrAdd;

			public float ChainChance;

			public float ChainChancePerTarget;

			public int ChainMaxTargets;

			public float ChainDelay;

			public float ChainStartChance;

			public float ChainStartChanceFalloff;

			public float ChainStartDelay;

			public int ChainMinTargets;

			public float ChainAttackMaxTime;

			public int AttackChainLevels;

			public readonly List<GraphFieldBaseline> GraphFields = new List<GraphFieldBaseline>();

			public readonly List<GraphFieldBaseline> SummonGraphFields = new List<GraphFieldBaseline>();

			public static AttackBaseline Capture(Attack? attack, GameObject staffPrefab = null)
			{
				AttackBaseline attackBaseline = new AttackBaseline();
				if (attack == null)
				{
					return attackBaseline;
				}
				attackBaseline.AttackEitr = GetFloat(attack, "m_attackEitr");
				attackBaseline.AttackHealthPercentage = GetFloat(attack, "m_attackHealthPercentage");
				attackBaseline.AttackCooldown = GetFloat(attack, "m_attackCooldown");
				attackBaseline.AttackForce = GetFloat(attack, "m_attackForce");
				attackBaseline.AttackForceForward = GetFloat(attack, "m_attackForceForward");
				attackBaseline.ProjectileAccuracyMin = GetFloat(attack, "m_projectileAccuracyMin");
				attackBaseline.ProjectileVelocity = GetFloat(attack, "m_projectileVel");
				attackBaseline.Projectiles = GetInt(attack, "m_projectiles");
				attackBaseline.AttackDrawTime = GetFloat(attack, "m_attackDrawTime");
				attackBaseline.AttackDrawPercentage = GetFloat(attack, "m_attackDrawPercentage");
				attackBaseline.AttackStamina = GetFloat(attack, "m_attackStamina");
				attackBaseline.AttackStaminaModifier = GetFloat(attack, "m_attackStaminaModifier");
				attackBaseline.StatusEffectChance = GetFloat(attack, "m_attackStatusEffectChance");
				attackBaseline.AttackHealth = GetFloat(attack, "m_attackHealth");
				attackBaseline.HealthReturnHit = GetFloat(attack, "m_attackHealthReturnHit");
				attackBaseline.AttackEitrAdd = GetFloat(attack, "m_attackEitrAdd");
				attackBaseline.ChainChance = GetFloat(attack, "m_chainChance");
				attackBaseline.ChainChancePerTarget = GetFloat(attack, "m_chainChancePerTarget");
				attackBaseline.ChainMaxTargets = GetInt(attack, "m_chainMaxTargets");
				attackBaseline.ChainDelay = GetFloat(attack, "m_chainDelay");
				attackBaseline.ChainStartChance = GetFloat(attack, "m_chainStartChance");
				attackBaseline.ChainStartChanceFalloff = GetFloat(attack, "m_chainStartChanceFalloff");
				attackBaseline.ChainStartDelay = GetFloat(attack, "m_chainStartDelay");
				attackBaseline.ChainMinTargets = GetInt(attack, "m_chainMinTargets");
				attackBaseline.ChainAttackMaxTime = GetFloat(attack, "m_chainAttackMaxTime");
				attackBaseline.AttackChainLevels = GetInt(attack, "m_attackChainLevels");
				attackBaseline.GraphFields.AddRange(CaptureGraphFields(attack, staffPrefab));
				attackBaseline.SummonGraphFields.AddRange(CaptureSummonGraphFields(attack));
				return attackBaseline;
			}

			public void Restore(Attack? attack)
			{
				if (attack != null)
				{
					SetField(attack, "m_attackEitr", AttackEitr);
					SetField(attack, "m_attackHealthPercentage", AttackHealthPercentage);
					SetField(attack, "m_attackCooldown", AttackCooldown);
					SetField(attack, "m_attackForce", AttackForce);
					SetField(attack, "m_attackForceForward", AttackForceForward);
					SetField(attack, "m_projectileAccuracyMin", ProjectileAccuracyMin);
					SetField(attack, "m_projectileVel", ProjectileVelocity);
					SetField(attack, "m_projectiles", Projectiles);
					SetField(attack, "m_attackDrawTime", AttackDrawTime);
					SetField(attack, "m_attackDrawPercentage", AttackDrawPercentage);
					SetField(attack, "m_attackStamina", AttackStamina);
					SetField(attack, "m_attackStaminaModifier", AttackStaminaModifier);
					SetField(attack, "m_attackStatusEffectChance", StatusEffectChance);
					SetField(attack, "m_attackHealth", AttackHealth);
					SetField(attack, "m_attackHealthReturnHit", HealthReturnHit);
					SetField(attack, "m_attackEitrAdd", AttackEitrAdd);
					SetField(attack, "m_chainChance", ChainChance);
					SetField(attack, "m_chainChancePerTarget", ChainChancePerTarget);
					SetField(attack, "m_chainMaxTargets", ChainMaxTargets);
					SetField(attack, "m_chainDelay", ChainDelay);
					SetField(attack, "m_chainStartChance", ChainStartChance);
					SetField(attack, "m_chainStartChanceFalloff", ChainStartChanceFalloff);
					SetField(attack, "m_chainStartDelay", ChainStartDelay);
					SetField(attack, "m_chainMinTargets", ChainMinTargets);
					SetField(attack, "m_chainAttackMaxTime", ChainAttackMaxTime);
					SetField(attack, "m_attackChainLevels", AttackChainLevels);
				}
			}

			private static List<GraphFieldBaseline> CaptureSummonGraphFields(Attack attack)
			{
				List<GraphFieldBaseline> list = new List<GraphFieldBaseline>();
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				Queue<(GameObject, int)> queue = new Queue<(GameObject, int)>();
				HashSet<int> hashSet2 = new HashSet<int>();
				FieldInfo[] fields = ((object)attack).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType))
					{
						object? value = fieldInfo.GetValue(attack);
						GameObject val = (GameObject)((value is GameObject) ? value : null);
						if (val != null && (Object)(object)val != (Object)null)
						{
							queue.Enqueue((val, 0));
						}
					}
				}
				while (queue.Count > 0)
				{
					var (val2, num) = queue.Dequeue();
					if ((Object)(object)val2 == (Object)null || !hashSet2.Add(((Object)val2).GetInstanceID()))
					{
						continue;
					}
					Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
					foreach (Component val3 in componentsInChildren)
					{
						if ((Object)(object)val3 == (Object)null)
						{
							continue;
						}
						string name = ((object)val3).GetType().Name;
						if (name != "Character" && name != "Humanoid" && name != "Attack" && name != "SpawnAbility")
						{
							continue;
						}
						fields = ((object)val3).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo2 in fields)
						{
							if (!IsSummonField(name, fieldInfo2.Name) || !IsNumeric(fieldInfo2.FieldType))
							{
								continue;
							}
							object value2 = fieldInfo2.GetValue(val3);
							if (value2 != null)
							{
								string item = ((Object)val3).GetInstanceID() + ":" + fieldInfo2.Name;
								if (hashSet.Add(item))
								{
									list.Add(new GraphFieldBaseline(val3, fieldInfo2, value2));
								}
							}
						}
					}
					if (num >= 3)
					{
						continue;
					}
					componentsInChildren = val2.GetComponentsInChildren<Component>(true);
					foreach (Component val4 in componentsInChildren)
					{
						if ((Object)(object)val4 == (Object)null)
						{
							continue;
						}
						fields = ((object)val4).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo3 in fields)
						{
							if (typeof(GameObject).IsAssignableFrom(fieldInfo3.FieldType))
							{
								object? value3 = fieldInfo3.GetValue(val4);
								GameObject val5 = (GameObject)((value3 is GameObject) ? value3 : null);
								if (val5 != null && (Object)(object)val5 != (Object)null)
								{
									queue.Enqueue((val5, num + 1));
								}
							}
							else
							{
								if (!fieldInfo3.FieldType.IsArray || !typeof(GameObject).IsAssignableFrom(fieldInfo3.FieldType.GetElementType()) || !(fieldInfo3.GetValue(val4) is Array array))
								{
									continue;
								}
								foreach (object item2 in array)
								{
									GameObject val6 = (GameObject)((item2 is GameObject) ? item2 : null);
									if (val6 != null && (Object)(object)val6 != (Object)null)
									{
										queue.Enqueue((val6, num + 1));
									}
								}
							}
						}
					}
				}
				return list;
			}

			private static bool IsSummonField(string typeName, string fieldName)
			{
				switch (typeName)
				{
				case "Character":
					return fieldName == "m_health";
				case "Humanoid":
					if (!(fieldName == "m_health") && !(fieldName == "m_walkSpeed"))
					{
						return fieldName == "m_runSpeed";
					}
					return true;
				case "Attack":
					if (!(fieldName == "m_damageMultiplier"))
					{
						return fieldName == "m_attackCooldown";
					}
					return true;
				case "SpawnAbility":
					return fieldName == "m_maxSpawns";
				default:
					return false;
				}
			}

			private static List<GraphFieldBaseline> CaptureGraphFields(Attack attack, GameObject staffPrefab)
			{
				List<GraphFieldBaseline> list = new List<GraphFieldBaseline>();
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				Queue<(GameObject, int)> queue = new Queue<(GameObject, int)>();
				HashSet<int> hashSet2 = new HashSet<int>();
				if ((Object)(object)staffPrefab != (Object)null)
				{
					queue.Enqueue((staffPrefab, 0));
				}
				FieldInfo[] fields = ((object)attack).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType))
					{
						object? value = fieldInfo.GetValue(attack);
						GameObject val = (GameObject)((value is GameObject) ? value : null);
						if (val != null && (Object)(object)val != (Object)null)
						{
							queue.Enqueue((val, 0));
						}
					}
				}
				while (queue.Count > 0)
				{
					var (val2, num) = queue.Dequeue();
					if ((Object)(object)val2 == (Object)null || !hashSet2.Add(((Object)val2).GetInstanceID()))
					{
						continue;
					}
					Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
					foreach (Component val3 in componentsInChildren)
					{
						if ((Object)(object)val3 == (Object)null)
						{
							continue;
						}
						string name = ((object)val3).GetType().Name;
						if ((name != "Projectile" && name != "Aoe" && name != "EffectArea" && name != "StatusEffect" && name != "SE_Shield" && name != "ShieldDome") || (name == "Attack" && (object)val3 == attack))
						{
							continue;
						}
						fields = ((object)val3).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo2 in fields)
						{
							if (!IsGraphField(name, fieldInfo2.Name) || !IsNumeric(fieldInfo2.FieldType))
							{
								continue;
							}
							object value2 = fieldInfo2.GetValue(val3);
							if (value2 != null)
							{
								string item = ((Object)val3).GetInstanceID() + ":" + fieldInfo2.Name;
								if (hashSet.Add(item))
								{
									list.Add(new GraphFieldBaseline(val3, fieldInfo2, value2));
								}
							}
						}
					}
					if (num >= 2)
					{
						continue;
					}
					componentsInChildren = val2.GetComponentsInChildren<Component>(true);
					foreach (Component val4 in componentsInChildren)
					{
						if ((Object)(object)val4 == (Object)null)
						{
							continue;
						}
						fields = ((object)val4).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo3 in fields)
						{
							if (typeof(GameObject).IsAssignableFrom(fieldInfo3.FieldType))
							{
								object? value3 = fieldInfo3.GetValue(val4);
								GameObject val5 = (GameObject)((value3 is GameObject) ? value3 : null);
								if (val5 != null && (Object)(object)val5 != (Object)null)
								{
									queue.Enqueue((val5, num + 1));
								}
							}
							else
							{
								if (!fieldInfo3.FieldType.IsArray || !typeof(GameObject).IsAssignableFrom(fieldInfo3.FieldType.GetElementType()) || !(fieldInfo3.GetValue(val4) is Array array))
								{
									continue;
								}
								foreach (object item2 in array)
								{
									GameObject val6 = (GameObject)((item2 is GameObject) ? item2 : null);
									if (val6 != null && (Object)(object)val6 != (Object)null)
									{
										queue.Enqueue((val6, num + 1));
									}
								}
							}
						}
					}
				}
				return list;
			}

			private static bool IsGraphField(string typeName, string fieldName)
			{
				switch (typeName)
				{
				case "Projectile":
					switch (fieldName)
					{
					default:
						return fieldName == "m_forceStagger";
					case "m_ttl":
					case "m_ttlMax":
					case "m_projectileVelocity":
					case "m_projectileVelocityMax":
					case "m_maxBounces":
					case "m_damageMultiplier":
					case "m_damage":
					case "m_damagePerHit":
					case "m_force":
						return true;
					}
				case "Aoe":
				case "EffectArea":
					switch (fieldName)
					{
					default:
						return fieldName == "m_damagePerHit";
					case "m_radius":
					case "m_force":
					case "m_forceStagger":
					case "m_damage":
						return true;
					}
				case "StatusEffect":
					if (!(fieldName == "m_duration"))
					{
						return fieldName == "m_ttl";
					}
					return true;
				case "SE_Shield":
					if (!(fieldName == "m_ttl") && !(fieldName == "m_absorbDamage"))
					{
						return fieldName == "m_absorbDamagePerSkillLevel";
					}
					return true;
				case "ShieldDome":
					if (!(fieldName == "m_wrapSphereRadius"))
					{
						return fieldName == "m_reflectionHeight";
					}
					return true;
				default:
					return false;
				}
			}

			private static bool IsNumeric(Type type)
			{
				if (!(type == typeof(float)) && !(type == typeof(double)) && !(type == typeof(int)))
				{
					return type == typeof(long);
				}
				return true;
			}

			private static float GetFloat(object target, string name)
			{
				object field = GetField(target, name, 0f);
				if (field is float)
				{
					return (float)field;
				}
				return 0f;
			}

			private static int GetInt(object target, string name)
			{
				object field = GetField(target, name, 0);
				if (field is int)
				{
					return (int)field;
				}
				return 0;
			}

			private static object? GetField(object target, string name, object fallback)
			{
				return target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) ?? fallback;
			}
		}

		private sealed class GraphFieldBaseline
		{
			public readonly Component Target;

			public readonly FieldInfo Field;

			public readonly object VanillaValue;

			public string ComponentTypeName
			{
				get
				{
					if (!(Target is Character) || !(FieldName == "m_health"))
					{
						return ((object)Target).GetType().Name;
					}
					return "Character";
				}
			}

			public string FieldName => Field.Name;

			public GraphFieldBaseline(Component target, FieldInfo field, object vanillaValue)
			{
				Target = target;
				Field = field;
				VanillaValue = vanillaValue;
			}

			public void Restore()
			{
				Field.SetValue(Target, VanillaValue);
			}
		}

		private sealed class StaffConfig
		{
			internal interface ISettingGroup
			{
				ConfigEntry<ModifierMode> Mode { get; }

				ConfigEntryBase Value { get; }

				void SubscribeValueChanged(Action handler);
			}

			internal sealed class TuningFloat : ISettingGroup
			{
				public ConfigEntry<ModifierMode> Mode { get; }

				public ConfigEntry<float> Value { get; }

				ConfigEntryBase ISettingGroup.Value => (ConfigEntryBase)(object)Value;

				public bool Uninitialized { get; set; }

				public ConfigurationManagerAttributes? Ui { get; }

				public string UiDescription { get; }

				public bool IntendedVisible { get; }

				public TuningFloat(ConfigEntry<ModifierMode> mode, ConfigEntry<float> value, bool uninitialized = false, ConfigurationManagerAttributes? ui = null, string uiDescription = "", bool intendedVisible = false)
				{
					Mode = mode;
					Value = value;
					Uninitialized = uninitialized;
					Ui = ui;
					UiDescription = uiDescription;
					IntendedVisible = intendedVisible;
				}

				public void SubscribeValueChanged(Action handler)
				{
					Value.SettingChanged += delegate
					{
						handler();
					};
				}
			}

			internal sealed class TuningInt : ISettingGroup
			{
				public ConfigEntry<ModifierMode> Mode { get; }

				public ConfigEntry<int> Value { get; }

				ConfigEntryBase ISettingGroup.Value => (ConfigEntryBase)(object)Value;

				public bool Uninitialized { get; set; }

				public ConfigurationManagerAttributes? Ui { get; }

				public string UiDescription { get; }

				public bool IntendedVisible { get; }

				public TuningInt(ConfigEntry<ModifierMode> mode, ConfigEntry<int> value, bool uninitialized = false, ConfigurationManagerAttributes? ui = null, string uiDescription = "", bool intendedVisible = false)
				{
					Mode = mode;
					Value = value;
					Uninitialized = uninitialized;
					Ui = ui;
					UiDescription = uiDescription;
					IntendedVisible = intendedVisible;
				}

				public void SubscribeValueChanged(Action handler)
				{
					Value.SettingChanged += delegate
					{
						handler();
					};
				}
			}

			internal sealed class TuningBool : ISettingGroup
			{
				public ConfigEntry<ModifierMode> Mode { get; }

				public ConfigEntry<bool> Value { get; }

				ConfigEntryBase ISettingGroup.Value => (ConfigEntryBase)(object)Value;

				public TuningBool(ConfigEntry<ModifierMode> mode, ConfigEntry<bool> value)
				{
					Mode = mode;
					Value = value;
				}

				public void SubscribeValueChanged(Action handler)
				{
					Value.SettingChanged += delegate
					{
						handler();
					};
				}
			}

			internal sealed class ConfigurationManagerAttributes
			{
				public bool? Browsable { get; set; }

				public bool? IsAdvanced { get; set; }

				public string? Description { get; set; }
			}

			private static readonly Dictionary<string, HashSet<string>> VisibleSettings = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal)
			{
				["Staff of Embers"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Projectile Speed", "Explosion / AOE Radius", "Projectile Damage Multiplier" },
				["Staff of Frost"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Projectile Speed", "Projectile Count", "Status Effect Duration", "Projectile Damage Multiplier" },
				["Staff of Fracturing"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Projectile Speed", "Projectile Count", "Explosion / AOE Radius", "Projectile Damage Multiplier" },
				["Staff of the Wild"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Summon Health", "Summon Damage Multiplier", "Summon Attack Cooldown", "Summon Max Spawns", "Summons Per Cast" },
				["Dundr"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Projectile Count", "Projectile Speed", "Projectile Accuracy Min", "Projectile Damage Multiplier" },
				["Echo Spike"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Projectile Speed", "Projectile Max Bounces", "Projectile Damage Multiplier" },
				["Spirit Caller"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Health Cost Percent", "Cooldown", "Summon Health", "Summon Damage Multiplier", "Summon Attack Cooldown", "Summon Max Spawns", "Summons Per Cast" },
				["Northern Vengeance"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Health Cost Percent", "Cooldown", "Ally Application Radius", "Vengeance Reflection Height", "Projectile Damage Multiplier" },
				["Lightning Strike"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Cooldown", "Explosion / AOE Radius", "Projectile Damage Multiplier" },
				["Staff of Protection"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Health Cost Percent", "Cooldown", "Shield Absorption", "Shield Absorption Per Blood Magic Level", "Status Effect Duration" },
				["Dead Raiser"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Health Cost Percent", "Cooldown", "Summon Max Spawns", "Summon Health", "Summon Melee Damage Multiplier", "Summon Archer Damage Multiplier", "Summon Attack Cooldown", "Summons Per Cast", "Ignore Dead Raiser Level Limit" },
				["Trollstav"] = new HashSet<string>(StringComparer.Ordinal) { "Eitr Cost", "Health Cost Percent", "Cooldown", "Summon Max Spawns", "Summon Health", "Summon Damage Multiplier", "Summon Attack Cooldown", "Summons Per Cast" }
			};

			private static readonly Dictionary<string, float> VanillaEitrCosts = new Dictionary<string, float>(StringComparer.Ordinal)
			{
				["Staff of Embers"] = 35f,
				["Staff of Frost"] = 5f,
				["Staff of Fracturing"] = 30f,
				["Staff of the Wild"] = 55f,
				["Dundr"] = 25f,
				["Echo Spike"] = 25f,
				["Spirit Caller"] = 100f,
				["Northern Vengeance"] = 100f,
				["Lightning Strike"] = 40f,
				["Staff of Protection"] = 60f,
				["Dead Raiser"] = 100f,
				["Trollstav"] = 120f
			};

			private static readonly Dictionary<string, float> VanillaHealthCosts = new Dictionary<string, float>(StringComparer.Ordinal)
			{
				["Spirit Caller"] = 40f,
				["Northern Vengeance"] = 40f,
				["Staff of Protection"] = 40f,
				["Dead Raiser"] = 40f,
				["Trollstav"] = 60f
			};

			public string DisplayName { get; }

			public ConfigEntry<bool> Enabled { get; }

			internal TuningFloat Durability { get; }

			internal TuningFloat DurabilityPerLevel { get; }

			internal TuningBool UseDurability { get; }

			internal TuningFloat MovementModifier { get; }

			internal TuningFloat BlockPower { get; }

			internal TuningFloat BlockPowerPerLevel { get; }

			internal TuningFloat DeflectionForce { get; }

			internal TuningFloat DeflectionForcePerLevel { get; }

			internal TuningFloat TimedBlockBonus { get; }

			internal TuningFloat BlockStaminaUseFlatValue { get; }

			internal TuningFloat BlockStaminaUseModifier { get; }

			internal TuningFloat EitrCost { get; }

			internal TuningFloat HealthCostPercent { get; }

			internal TuningFloat Cooldown { get; }

			internal TuningFloat AttackForce { get; }

			internal TuningFloat AttackForceForward { get; }

			internal TuningFloat ProjectileSpeed { get; }

			internal TuningInt ProjectileCount { get; }

			internal TuningFloat DrawTime { get; }

			internal TuningFloat DrawPercentage { get; }

			internal TuningFloat AttackStamina { get; }

			internal TuningFloat AttackStaminaModifier { get; }

			internal TuningFloat StatusEffectChance { get; }

			internal TuningFloat HealthCost { get; }

			internal TuningFloat HealthReturnHit { get; }

			internal TuningFloat EitrAdd { get; }

			internal TuningFloat ProjectileAccuracy { get; }

			internal TuningFloat ProjectileAccuracyMin { get; }

			internal TuningFloat ProjectileLifetime { get; }

			internal TuningFloat ProjectileVelocityMax { get; }

			internal TuningFloat ProjectileVelocity { get; }

			internal TuningFloat ProjectileVelocityMin { get; }

			internal TuningInt ProjectileMaxBounces { get; }

			internal TuningFloat ProjectileSpawnDistance { get; }

			internal TuningFloat SpawnRadius { get; }

			internal TuningFloat RaiseRadius { get; }

			internal TuningFloat SpawnOnHitChance { get; }

			internal TuningFloat SpawnCount { get; }

			internal TuningFloat SpawnInterval { get; }

			internal TuningFloat SpawnDelay { get; }

			internal TuningFloat SpawnForce { get; }

			internal TuningFloat SpiritDamageLeft { get; }

			internal TuningFloat SpiritDamagePerHit { get; }

			internal TuningFloat ExplosionRadius { get; }

			internal TuningFloat Knockback { get; }

			internal TuningFloat StatusEffectDuration { get; }

			internal TuningFloat ShieldAbsorption { get; }

			internal TuningFloat ShieldAbsorptionPerBloodMagicLevel { get; }

			internal TuningFloat VengeanceSphereRadius { get; }

			internal TuningFloat VengeanceReflectionHeight { get; }

			internal TuningFloat SummonHealth { get; }

			internal TuningInt SummonsPerCast { get; }

			internal TuningBool IgnoreDeadRaiserLevelLimit { get; }

			internal TuningFloat SummonWalkSpeed { get; }

			internal TuningFloat SummonRunSpeed { get; }

			internal TuningFloat SummonMeleeDamageMultiplier { get; }

			internal TuningFloat SummonArcherDamageMultiplier { get; }

			internal TuningFloat SummonDamagePerLevelMultiplier { get; }

			internal TuningFloat SummonDamageMultiplier { get; }

			internal TuningFloat SummonFinalDamageMultiplier { get; }

			internal TuningFloat SummonAttackCooldown { get; }

			internal TuningInt SummonMaxSpawns { get; }

			internal TuningFloat DamageMultiplier { get; }

			internal TuningFloat Damage { get; }

			internal TuningFloat DamagePerHit { get; }

			internal TuningFloat ChainChance { get; }

			internal TuningFloat ChainChancePerTarget { get; }

			internal TuningInt ChainMaxTargets { get; }

			internal TuningFloat ChainDelay { get; }

			internal TuningFloat ChainStartChance { get; }

			internal TuningFloat ChainStartChanceFalloff { get; }

			internal TuningFloat ChainStartDelay { get; }

			internal TuningInt ChainMinTargets { get; }

			internal TuningFloat ChainAttackMaxTime { get; }

			internal TuningInt AttackChainLevels { get; }

			public event Action? Changed;

			public StaffConfig(ConfigFile config, StaffDefinition staff)
			{
				//IL_018b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0191: Invalid comparison between Unknown and I4
				DisplayName = staff.DisplayName;
				string displayName = staff.DisplayName;
				Enabled = Bind(config, displayName, "Enabled", defaultValue: true, "Enable modifications for this staff.");
				Durability = TuneFloat(config, displayName, "Durability", 0f, "Value used by the selected modifier mode. Vanilla mode ignores this value.");
				DurabilityPerLevel = TuneFloat(config, displayName, "Durability Per Level", 0f, "Durability gained per quality level.");
				UseDurability = TuneBool(config, displayName, "Use Durability", defaultValue: true, "Whether the staff consumes durability.");
				MovementModifier = TuneFloat(config, displayName, "Movement Modifier", 0f, "Movement modifier applied while equipped.");
				BlockPower = TuneFloat(config, displayName, "Block Power", 0f, "Base block power while using the staff.");
				BlockPowerPerLevel = TuneFloat(config, displayName, "Block Power Per Level", 0f, "Block power gained per quality level.");
				DeflectionForce = TuneFloat(config, displayName, "Deflection Force", 0f, "Base deflection force while blocking.");
				DeflectionForcePerLevel = TuneFloat(config, displayName, "Deflection Force Per Level", 0f, "Deflection force gained per quality level.");
				TimedBlockBonus = TuneFloat(config, displayName, "Timed Block Bonus", 0f, "Timed block/parry multiplier.");
				BlockStaminaUseFlatValue = TuneFloat(config, displayName, "Block Stamina Use Flat Value", 0f, "Flat stamina use while blocking.");
				BlockStaminaUseModifier = TuneFloat(config, displayName, "Block Stamina Use Modifier", 0f, "Multiplier applied to block stamina use.");
				EitrCost = TuneFloat(config, displayName, "Eitr Cost", 0f, "Eitr consumed by the attack.");
				HealthCostPercent = TuneFloat(config, displayName, "Health Cost Percent", 0f, ((int)staff.MagicType == 1) ? "Percentage of current health consumed per cast." : "Percentage of health consumed per cast.");
				Cooldown = TuneFloat(config, displayName, "Cooldown", 0f, "Attack cooldown.");
				DrawTime = TuneFloat(config, displayName, "Draw Time", 0f, "Attack charge/draw time.");
				DrawPercentage = TuneFloat(config, displayName, "Draw Percentage", 0f, "Attack draw percentage.");
				AttackStamina = TuneFloat(config, displayName, "Attack Stamina", 0f, "Attack stamina cost.");
				AttackStaminaModifier = TuneFloat(config, displayName, "Attack Stamina Modifier", 0f, "Attack stamina modifier.");
				HealthCost = TuneFloat(config, displayName, "Health Cost", 0f, "Flat health cost.");
				HealthReturnHit = TuneFloat(config, displayName, "Health Return Per Hit", 0f, "Health returned on hit.");
				EitrAdd = TuneFloat(config, displayName, "Eitr Added", 0f, "Flat Eitr added by the attack.");
				AttackForce = TuneFloat(config, displayName, "Attack Force", 0f, "Attack force / stagger force.");
				AttackForceForward = TuneFloat(config, displayName, "Attack Force Forward", 0f, "Forward push applied by the cast.");
				StatusEffectChance = TuneFloat(config, displayName, "Status Effect Chance", 0f, "Chance to apply the attack status effect.");
				ProjectileSpeed = TuneFloat(config, displayName, "Projectile Speed", 0f, "Primary projectile velocity.");
				ProjectileCount = TuneInt(config, displayName, "Projectile Count", 0, "Number of projectiles fired.");
				ProjectileAccuracy = TuneFloat(config, displayName, "Projectile Accuracy", -1f, "Projectile spread/accuracy.");
				ProjectileAccuracyMin = TuneFloat(config, displayName, "Projectile Accuracy Min", -1f, "Minimum spread used when firing projectiles. Lower values generally produce a tighter spread.");
				ProjectileLifetime = TuneFloat(config, displayName, "Projectile Lifetime", -1f, "Projectile stay/TTL value.");
				ProjectileVelocityMax = TuneFloat(config, displayName, "Projectile Velocity Max", -1f, "Maximum projectile velocity.");
				ProjectileVelocity = TuneFloat(config, displayName, "Projectile Velocity", -1f, "Travel speed of the spell projectile.");
				ProjectileVelocityMin = TuneFloat(config, displayName, "Projectile Velocity Min", -1f, "Minimum projectile velocity.");
				ProjectileMaxBounces = TuneInt(config, displayName, "Projectile Max Bounces", -1, "Maximum number of times the projectile can bounce to another target.");
				ProjectileSpawnDistance = TuneFloat(config, displayName, "Projectile Spawn Distance", -1f, "Projectile spawn distance.");
				SpawnRadius = TuneFloat(config, displayName, "Spawn Radius", -1f, "Summon/spawn radius.");
				RaiseRadius = TuneFloat(config, displayName, "Raise Radius", -1f, "Dead Raiser / raise effect radius.");
				SpawnOnHitChance = TuneFloat(config, displayName, "Spawn On Hit Chance", -1f, "Chance to spawn an entity/effect on hit.");
				SpawnCount = TuneFloat(config, displayName, "Spawn Count", -1f, "Number of spawned entities/effects.");
				SpawnInterval = TuneFloat(config, displayName, "Spawn Interval", -1f, "Time between spawns.");
				SpawnDelay = TuneFloat(config, displayName, "Spawn Delay", -1f, "Delay before spawning.");
				SpawnForce = TuneFloat(config, displayName, "Spawn Force", -1f, "Force applied to spawned objects.");
				SpiritDamageLeft = TuneFloat(config, displayName, "Spirit Damage Left", -1f, "Remaining spirit damage pool.");
				SpiritDamagePerHit = TuneFloat(config, displayName, "Spirit Damage Per Hit", -1f, "Spirit damage consumed/dealt per hit.");
				ExplosionRadius = TuneFloat(config, displayName, "Explosion / AOE Radius", -1f, "Radius of the spell's area-of-effect or explosion.");
				Knockback = TuneFloat(config, displayName, "Knockback", -1f, "Knockback or stagger force produced by the spell.");
				StatusEffectDuration = TuneFloat(config, displayName, "Status Effect Duration", -1f, "Duration of status effects attached to the staff attack.");
				ShieldAbsorption = TuneFloat(config, displayName, "Shield Absorption", -1f, "Base damage the Staff of Protection barrier can absorb.");
				ShieldAbsorptionPerBloodMagicLevel = TuneFloat(config, displayName, "Shield Absorption Per Blood Magic Level", -1f, "Additional barrier absorption per Blood Magic level. Vanilla is 5 per level.");
				VengeanceSphereRadius = TuneFloat(config, displayName, "Ally Application Radius", -1f, "Radius of Northern Vengeance's protective vengeance sphere.");
				VengeanceReflectionHeight = TuneFloat(config, displayName, "Vengeance Reflection Height", -1f, "Vertical reach used by Northern Vengeance when reflecting incoming attacks.");
				SummonHealth = TuneFloat(config, displayName, "Summon Health", -1f, "Base health of creatures summoned by this staff.");
				SummonsPerCast = TuneInt(config, displayName, "Summons Per Cast", 1, "Number of creatures attempted by one local cast. Normal active-summon limits still apply unless bypassed for Dead Raiser.");
				IgnoreDeadRaiserLevelLimit = TuneBool(config, displayName, "Ignore Dead Raiser Level Limit", defaultValue: false, "Bypass the staff-quality/Blood Magic summon-count limit for your locally owned skeletons. Retains skeleton strength and star scaling. Turning this off restores normal count enforcement.");
				SummonWalkSpeed = TuneFloat(config, displayName, "Summon Walk Speed", -1f, "Walking speed of creatures summoned by this staff.");
				SummonRunSpeed = TuneFloat(config, displayName, "Summon Run Speed", -1f, "Running speed of creatures summoned by this staff.");
				SummonMeleeDamageMultiplier = TuneFloat(config, displayName, "Summon Melee Damage Multiplier", -1f, "Multiplier applied to Dead Raiser melee summon damage.");
				SummonArcherDamageMultiplier = TuneFloat(config, displayName, "Summon Archer Damage Multiplier", -1f, "Multiplier applied to Dead Raiser archer summon damage.");
				SummonDamagePerLevelMultiplier = TuneFloat(config, displayName, "Summon Damage Per Level Multiplier", -1f, "Multiplier applied to summon weapon m_damagesPerLevel values. This is weapon-quality scaling, not Blood Magic skill scaling.");
				SummonDamageMultiplier = TuneFloat(config, displayName, "Summon Attack Damage Multiplier", -1f, "Multiplier applied to the summon's attack damage.");
				SummonFinalDamageMultiplier = TuneFloat(config, displayName, "Summon Damage Multiplier", -1f, "Multiplier applied to damage dealt by summons created by this staff.");
				SummonAttackCooldown = TuneFloat(config, displayName, "Summon Attack Cooldown", -1f, "Time between attacks for creatures summoned by this staff.");
				SummonMaxSpawns = TuneInt(config, displayName, "Summon Max Spawns", -1, "Maximum number of creatures this staff can keep summoned at once. Blood Magic skill scaling remains unchanged.");
				DamageMultiplier = TuneFloat(config, displayName, "Projectile Damage Multiplier", -1f, "Multiplier applied to this staff's projectile or spell damage.");
				Damage = TuneFloat(config, displayName, "Direct Damage", -1f, "Direct damage dealt by the spell when this staff uses a flat damage value.");
				DamagePerHit = TuneFloat(config, displayName, "Damage Per Hit", -1f, "Damage dealt each time the spell hits a target.");
				ChainChance = TuneFloat(config, displayName, "Chain Chance", -1f, "Chance for a chain attack where the staff Attack exposes it.");
				ChainChancePerTarget = TuneFloat(config, displayName, "Chain Chance Per Target", -1f, "Per-target chain chance adjustment.");
				ChainMaxTargets = TuneInt(config, displayName, "Chain Max Targets", -1, "Maximum chain targets.");
				ChainDelay = TuneFloat(config, displayName, "Chain Delay", -1f, "Delay between chain targets.");
				ChainStartChance = TuneFloat(config, displayName, "Chain Start Chance", -1f, "Initial chain-start chance.");
				ChainStartChanceFalloff = TuneFloat(config, displayName, "Chain Start Chance Falloff", -1f, "Chain-start chance falloff.");
				ChainStartDelay = TuneFloat(config, displayName, "Chain Start Delay", -1f, "Delay before chain begins.");
				ChainMinTargets = TuneInt(config, displayName, "Chain Min Targets", -1, "Minimum number of targets required by the chain attack when the vanilla field exists.");
				ChainAttackMaxTime = TuneFloat(config, displayName, "Chain Attack Max Time", -1f, "Maximum time window for the chain attack when the vanilla field exists.");
				AttackChainLevels = TuneInt(config, displayName, "Attack Chain Levels", -1, "Number of chain levels supported by the attack when the vanilla field exists.");
				Enabled.SettingChanged += delegate
				{
					this.Changed?.Invoke();
				};
				foreach (ISettingGroup tuning in GetTunings())
				{
					tuning.Mode.SettingChanged += delegate
					{
						this.Changed?.Invoke();
					};
					tuning.SubscribeValueChanged(delegate
					{
						this.Changed?.Invoke();
					});
				}
			}

			private IEnumerable<ConfigEntryBase> GetEntries()
			{
				yield return (ConfigEntryBase)(object)Enabled;
				foreach (ISettingGroup tuning in GetTunings())
				{
					yield return (ConfigEntryBase)(object)tuning.Mode;
					yield return tuning.Value;
				}
			}

			private IEnumerable<ISettingGroup> GetTunings()
			{
				yield return Durability;
				yield return DurabilityPerLevel;
				yield return UseDurability;
				yield return MovementModifier;
				yield return BlockPower;
				yield return BlockPowerPerLevel;
				yield return DeflectionForce;
				yield return DeflectionForcePerLevel;
				yield return TimedBlockBonus;
				yield return BlockStaminaUseFlatValue;
				yield return BlockStaminaUseModifier;
				yield return EitrCost;
				yield return HealthCostPercent;
				yield return Cooldown;
				yield return AttackForce;
				yield return AttackForceForward;
				yield return ProjectileSpeed;
				yield return ProjectileCount;
				yield return DrawTime;
				yield return DrawPercentage;
				yield return AttackStamina;
				yield return AttackStaminaModifier;
				yield return StatusEffectChance;
				yield return HealthCost;
				yield return HealthReturnHit;
				yield return EitrAdd;
				yield return ProjectileAccuracy;
				yield return ProjectileAccuracyMin;
				yield return ProjectileLifetime;
				yield return ProjectileVelocityMax;
				yield return ProjectileVelocity;
				yield return ProjectileVelocityMin;
				yield return ProjectileMaxBounces;
				yield return ProjectileSpawnDistance;
				yield return SpawnRadius;
				yield return RaiseRadius;
				yield return SpawnOnHitChance;
				yield return SpawnCount;
				yield return SpawnInterval;
				yield return SpawnDelay;
				yield return SpawnForce;
				yield return SpiritDamageLeft;
				yield return SpiritDamagePerHit;
				yield return ExplosionRadius;
				yield return Knockback;
				yield return StatusEffectDuration;
				yield return ShieldAbsorption;
				yield return ShieldAbsorptionPerBloodMagicLevel;
				yield return VengeanceSphereRadius;
				yield return VengeanceReflectionHeight;
				yield return SummonHealth;
				yield return SummonWalkSpeed;
				yield return SummonRunSpeed;
				yield return SummonsPerCast;
				yield return IgnoreDeadRaiserLevelLimit;
				yield return SummonMeleeDamageMultiplier;
				yield return SummonArcherDamageMultiplier;
				yield return SummonDamagePerLevelMultiplier;
				yield return SummonDamageMultiplier;
				yield return SummonFinalDamageMultiplier;
				yield return SummonAttackCooldown;
				yield return SummonMaxSpawns;
				yield return DamageMultiplier;
				yield return Damage;
				yield return DamagePerHit;
				yield return ChainChance;
				yield return ChainChancePerTarget;
				yield return ChainMaxTargets;
				yield return ChainDelay;
				yield return ChainStartChance;
				yield return ChainStartChanceFalloff;
				yield return ChainStartDelay;
				yield return ChainMinTargets;
				yield return ChainAttackMaxTime;
				yield return AttackChainLevels;
			}

			private static bool IsVisible(string section, string key)
			{
				if (VisibleSettings.TryGetValue(section, out HashSet<string> value))
				{
					return value.Contains(key);
				}
				return false;
			}

			private static AcceptableValueBase? RangeFor(string key)
			{
				if (key == "Health Cost Percent")
				{
					return (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 100f);
				}
				if (key.Contains("Chance", StringComparison.Ordinal))
				{
					return (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 1f);
				}
				if (key.Contains("Multiplier", StringComparison.Ordinal))
				{
					return (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 5f);
				}
				return null;
			}

			public void InitializeVanillaValues(StaffBaseline baseline, bool forceReset)
			{
				Init(EitrCost, VanillaEitrCosts.TryGetValue(DisplayName, out var value) ? value : baseline.PrimaryAttack.AttackEitr, forceReset);
				Init(HealthCostPercent, VanillaHealthCosts.TryGetValue(DisplayName, out var value2) ? value2 : (baseline.PrimaryAttack.AttackHealthPercentage * 100f), forceReset);
				Init(Cooldown, baseline.PrimaryAttack.AttackCooldown, forceReset);
				Init(ProjectileSpeed, baseline.PrimaryAttack.ProjectileVelocity, forceReset);
				Init(ProjectileCount, (DisplayName == "Dundr" || DisplayName == "Staff of Fracturing") ? 12 : baseline.PrimaryAttack.Projectiles, forceReset);
				Init(ProjectileAccuracyMin, baseline.PrimaryAttack.ProjectileAccuracyMin, forceReset);
				InitGraph(baseline.PrimaryAttack.GraphFields, "Aoe", "m_radius", ExplosionRadius, forceReset);
				InitGraph(baseline.PrimaryAttack.GraphFields, "EffectArea", "m_radius", ExplosionRadius, forceReset);
				InitGraph(baseline.PrimaryAttack.GraphFields, "StatusEffect", "m_duration", StatusEffectDuration, forceReset);
				if (DisplayName == "Staff of Protection")
				{
					Init(ShieldAbsorption, 200f, forceReset);
					Init(ShieldAbsorptionPerBloodMagicLevel, 5f, forceReset);
				}
				else
				{
					InitGraph(baseline.PrimaryAttack.GraphFields, "SE_Shield", "m_absorbDamage", ShieldAbsorption, forceReset);
					InitGraph(baseline.PrimaryAttack.GraphFields, "SE_Shield", "m_absorbDamagePerSkillLevel", ShieldAbsorptionPerBloodMagicLevel, forceReset);
				}
				InitGraph(baseline.PrimaryAttack.GraphFields, "ShieldDome", "m_wrapSphereRadius", VengeanceSphereRadius, forceReset);
				InitGraph(baseline.PrimaryAttack.GraphFields, "ShieldDome", "m_reflectionHeight", VengeanceReflectionHeight, forceReset);
				InitGraph(baseline.PrimaryAttack.GraphFields, "Projectile", "m_maxBounces", ProjectileMaxBounces, forceReset);
				Init(DamageMultiplier, 1f, forceReset);
				InitSummonHealth(baseline);
				InitGraph(baseline.PrimaryAttack.SummonGraphFields, "Attack", "m_attackCooldown", SummonAttackCooldown, forceReset);
				InitGraph(baseline.PrimaryAttack.SummonGraphFields, "SpawnAbility", "m_maxSpawns", SummonMaxSpawns, forceReset);
				Init(SummonMeleeDamageMultiplier, 1f, forceReset);
				Init(SummonArcherDamageMultiplier, 1f, forceReset);
				Init(SummonFinalDamageMultiplier, 1f, forceReset);
				if (DisplayName == "Trollstav")
				{
					Init(SummonMaxSpawns, 2, forceReset);
				}
			}

			private static void Init(TuningFloat setting, float vanilla, bool force = false)
			{
				if (vanilla < 0f)
				{
					if (setting.Ui != null)
					{
						setting.Ui.Browsable = false;
					}
					return;
				}
				if (force || setting.Uninitialized)
				{
					setting.Value.Value = vanilla;
					setting.Uninitialized = false;
				}
				if (setting.Ui != null)
				{
					setting.Ui.Browsable = setting.IntendedVisible;
					if (setting.IntendedVisible)
					{
						setting.Ui.Description = setting.UiDescription + " Vanilla: " + FormatVanilla(vanilla) + ".";
					}
				}
			}

			private void InitSummonHealth(StaffBaseline baseline)
			{
				List<GraphFieldBaseline>[] array = new List<GraphFieldBaseline>[2]
				{
					baseline.PrimaryAttack.SummonGraphFields,
					baseline.SecondaryAttack.SummonGraphFields
				};
				for (int i = 0; i < array.Length; i++)
				{
					foreach (GraphFieldBaseline item in array[i])
					{
						if (!(item.ComponentTypeName != "Character") && !(item.FieldName != "m_health"))
						{
							float? num = ReadNumeric(item.Field.FieldType, item.VanillaValue);
							if (num.HasValue && DamageTuning.ValidHealth(num.Value))
							{
								HealthDefaults.Apply(SummonHealth.Value, num.Value);
								Init(SummonHealth, num.Value);
								return;
							}
						}
					}
				}
				if (SummonHealth.Ui != null)
				{
					SummonHealth.Ui.Browsable = false;
				}
			}

			private static void Init(TuningInt setting, int vanilla, bool force = false)
			{
				if (vanilla < 0)
				{
					if (setting.Ui != null)
					{
						setting.Ui.Browsable = false;
					}
					return;
				}
				if (force || setting.Uninitialized)
				{
					setting.Value.Value = vanilla;
					setting.Mode.Value = (ModifierMode)3;
					setting.Uninitialized = false;
				}
				if (setting.Ui != null)
				{
					setting.Ui.Browsable = setting.IntendedVisible;
					if (setting.IntendedVisible)
					{
						setting.Ui.Description = setting.UiDescription + " Vanilla: " + vanilla + ".";
					}
				}
			}

			private static string FormatVanilla(float value)
			{
				if (!(MathF.Abs(value - MathF.Round(value)) < 0.0001f))
				{
					return value.ToString("0.##");
				}
				return ((int)MathF.Round(value)).ToString();
			}

			private static void InitGraph(List<GraphFieldBaseline> fields, string component, string field, TuningFloat setting, bool force = false)
			{
				foreach (GraphFieldBaseline field2 in fields)
				{
					if (field2.ComponentTypeName == component && field2.FieldName == field)
					{
						float? num = ReadNumeric(field2.Field.FieldType, field2.VanillaValue);
						if (num.HasValue)
						{
							Init(setting, num.Value, force);
						}
						return;
					}
				}
				if (setting.Ui != null)
				{
					setting.Ui.Browsable = false;
				}
			}

			private static void InitGraph(List<GraphFieldBaseline> fields, string component, string field, TuningInt setting, bool force = false)
			{
				foreach (GraphFieldBaseline field2 in fields)
				{
					if (field2.ComponentTypeName == component && field2.FieldName == field)
					{
						float? num = ReadNumeric(field2.Field.FieldType, field2.VanillaValue);
						if (num.HasValue)
						{
							Init(setting, (int)MathF.Round(num.Value), force);
						}
						return;
					}
				}
				if (setting.Ui != null)
				{
					setting.Ui.Browsable = false;
				}
			}

			private static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, string description)
			{
				return ConfigFileExtensions.BindConfigInOrder<T>(config, section, key, defaultValue, description, false, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			}

			private static TuningFloat TuneFloat(ConfigFile config, string section, string key, float defaultValue, string description)
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_005f: Expected O, but got Unknown
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_0116: Unknown result type (might be due to invalid IL or missing references)
				//IL_011d: Expected O, but got Unknown
				bool flag = IsVisible(section, key);
				ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
				{
					Browsable = false
				};
				ModifierMode val = (ModifierMode)(key.Contains("Multiplier", StringComparison.Ordinal) ? 2 : 3);
				ConfigEntry<ModifierMode> val2 = config.Bind<ModifierMode>("Internal / " + section, key + " Mode", val, new ConfigDescription("Internal modifier mode.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes }));
				val2.Value = val;
				AcceptableValueBase val3 = RangeFor(key);
				bool flag2 = _instance?._savedSettingKeys.Contains(ConfigPersistence.Key(section, key)) ?? false;
				float num = ((val3 == null) ? defaultValue : ((key == "Health Cost Percent") ? Mathf.Clamp(defaultValue, 0f, 100f) : (key.Contains("Multiplier", StringComparison.Ordinal) ? Mathf.Clamp(defaultValue, 0f, 5f) : Mathf.Clamp(defaultValue, 0f, 1f))));
				ConfigurationManagerAttributes configurationManagerAttributes2 = (flag ? new ConfigurationManagerAttributes
				{
					Browsable = true,
					Description = description
				} : configurationManagerAttributes);
				ConfigDescription val4 = new ConfigDescription(description, val3, new object[1] { configurationManagerAttributes2 });
				ConfigEntry<float> value = config.Bind<float>(section, key, num, val4);
				return new TuningFloat(val2, value, !flag2, configurationManagerAttributes2, description, flag);
			}

			private static TuningInt TuneInt(ConfigFile config, string section, string key, int defaultValue, string description)
			{
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Expected O, but got Unknown
				//IL_015c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0163: Expected O, but got Unknown
				bool flag = IsVisible(section, key);
				ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
				{
					Browsable = false
				};
				ConfigEntry<ModifierMode> val = config.Bind<ModifierMode>("Internal / " + section, key + " Mode", (ModifierMode)3, new ConfigDescription("Internal modifier mode.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes }));
				val.Value = (ModifierMode)3;
				AcceptableValueBase val2 = null;
				switch (key)
				{
				case "Projectile Count":
					val2 = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50);
					break;
				case "Summons Per Cast":
					val2 = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20);
					break;
				case "Summon Max Spawns":
					val2 = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20);
					break;
				case "Projectile Max Bounces":
					val2 = (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20);
					break;
				}
				bool flag2 = _instance?._savedSettingKeys.Contains(ConfigPersistence.Key(section, key)) ?? false;
				int num = key switch
				{
					"Projectile Max Bounces" => Math.Max(0, Math.Min(20, defaultValue)), 
					"Summon Max Spawns" => Math.Max(1, Math.Min(20, defaultValue)), 
					"Projectile Count" => Math.Max(1, Math.Min(50, defaultValue)), 
					_ => defaultValue, 
				};
				ConfigurationManagerAttributes configurationManagerAttributes2 = (flag ? new ConfigurationManagerAttributes
				{
					Browsable = true,
					Description = description
				} : configurationManagerAttributes);
				ConfigDescription val3 = new ConfigDescription(description, val2, new object[1] { configurationManagerAttributes2 });
				ConfigEntry<int> value = config.Bind<int>(section, key, num, val3);
				return new TuningInt(val, value, !flag2, configurationManagerAttributes2, description, flag);
			}

			private static TuningBool TuneBool(ConfigFile config, string section, string key, bool defaultValue, string description)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Expected O, but got Unknown
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_0093: Expected O, but got Unknown
				ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
				{
					Browsable = false
				};
				ConfigEntry<ModifierMode> mode = config.Bind<ModifierMode>("Internal / " + section, key + " Mode", (ModifierMode)3, new ConfigDescription("Internal modifier mode.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes }));
				ConfigurationManagerAttributes configurationManagerAttributes2 = ((key == "Ignore Dead Raiser Level Limit" && IsVisible(section, key)) ? new ConfigurationManagerAttributes
				{
					Browsable = true,
					Description = description
				} : configurationManagerAttributes);
				ConfigEntry<bool> value = config.Bind<bool>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { configurationManagerAttributes2 }));
				return new TuningBool(mode, value);
			}
		}

		public const string Guid = "com.magicstafftweaker.client";

		public const string Name = "Magic Staff Tweaker";

		public const string Version = "2.2.5";

		private readonly Dictionary<string, StaffConfig> _configs = new Dictionary<string, StaffConfig>();

		private readonly Dictionary<string, StaffBaseline> _baselines = new Dictionary<string, StaffBaseline>();

		private bool _vanillaPrefabsReady;

		private ConfigEntry<bool>? _logStaffGraph;

		private ConfigEntry<int>? _uiDefaultsRevision;

		private Harmony? _harmony;

		private static Plugin? _instance;

		private HashSet<string> _savedSettingKeys = new HashSet<string>(StringComparer.Ordinal);

		private static readonly ConditionalWeakTable<Aoe, LocalAoeFactor> LocalAoeFactors = new ConditionalWeakTable<Aoe, LocalAoeFactor>();

		private static readonly ConditionalWeakTable<SharedData, SummonWeaponDamageBaseline> SummonWeaponBaselines = new ConditionalWeakTable<SharedData, SummonWeaponDamageBaseline>();

		private void Awake()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			_instance = this;
			_savedSettingKeys = ConfigPersistence.ReadKeys(File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath) ? File.ReadAllText(((BaseUnityPlugin)this).Config.ConfigFilePath) : string.Empty);
			_logStaffGraph = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Log Staff Attack Graph", false, "Log the relevant component/field graph for each vanilla staff when prefabs become available.");
			StaffConfig.ConfigurationManagerAttributes configurationManagerAttributes = new StaffConfig.ConfigurationManagerAttributes
			{
				Browsable = false
			};
			_uiDefaultsRevision = ((BaseUnityPlugin)this).Config.Bind<int>("Internal", "UI Vanilla Defaults Revision", 0, new ConfigDescription("Internal migration marker for player-facing vanilla defaults.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes }));
			foreach (StaffDefinition item in VanillaStaffs.All)
			{
				StaffConfig staffConfig = new StaffConfig(((BaseUnityPlugin)this).Config, item);
				_configs[item.Prefab] = staffConfig;
				staffConfig.Changed += ApplyAll;
			}
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable;
			_harmony = new Harmony("com.magicstafftweaker.client");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. {2} vanilla staffs registered.", "Magic Staff Tweaker", "2.2.5", VanillaStaffs.All.Count));
		}

		private void OnDestroy()
		{
			_instance = null;
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			foreach (StaffConfig value in _configs.Values)
			{
				value.Changed -= ApplyAll;
			}
		}

		private void OnVanillaPrefabsAvailable()
		{
			_vanillaPrefabsReady = true;
			ConfigEntry<bool>? logStaffGraph = _logStaffGraph;
			if (logStaffGraph != null && logStaffGraph.Value)
			{
				DumpStaffGraphs();
			}
			CaptureBaselines();
			ApplyAll();
		}

		private void DumpStaffGraphs()
		{
			foreach (StaffDefinition item in VanillaStaffs.All)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(item.Prefab);
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[Graph] " + item.DisplayName + " (" + item.Prefab + ")"));
				Component[] componentsInChildren = prefab.GetComponentsInChildren<Component>(true);
				foreach (Component val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					string name = ((object)val).GetType().Name;
					if (name != "ItemDrop" && name != "Attack" && name != "Projectile" && name != "Aoe" && name != "EffectArea" && name != "StatusEffect" && name != "SE_Shield")
					{
						continue;
					}
					FieldInfo[] fields = ((object)val).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						if (fieldInfo.Name.StartsWith("m_", StringComparison.Ordinal))
						{
							object value = fieldInfo.GetValue(val);
							if (value == null || value is string || value.GetType().IsPrimitive || value is Enum)
							{
								((BaseUnityPlugin)this).Logger.LogInfo((object)$"  {name}.{fieldInfo.Name} = {value}");
							}
						}
					}
				}
			}
		}

		private void CaptureBaselines()
		{
			if (!_vanillaPrefabsReady)
			{
				return;
			}
			bool flag = (_uiDefaultsRevision?.Value ?? 0) < 214;
			foreach (StaffDefinition item in VanillaStaffs.All)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(item.Prefab);
				if ((Object)(object)prefab == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find vanilla staff prefab '" + item.Prefab + "'."));
					continue;
				}
				_baselines[item.Prefab] = StaffBaseline.Capture(prefab);
				CaptureKnownSummonHealth(item.Prefab, _baselines[item.Prefab]);
				_configs[item.Prefab].InitializeVanillaValues(_baselines[item.Prefab], forceReset: false);
			}
			if (flag && _uiDefaultsRevision != null)
			{
				_uiDefaultsRevision.Value = 214;
				((BaseUnityPlugin)this).Config.Save();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Initialized missing configuration defaults while preserving saved values.");
			}
		}

		private void ApplyAll()
		{
			if (!_vanillaPrefabsReady)
			{
				return;
			}
			foreach (StaffDefinition item in VanillaStaffs.All)
			{
				if (_baselines.TryGetValue(item.Prefab, out StaffBaseline value))
				{
					GameObject prefab = PrefabManager.Instance.GetPrefab(item.Prefab);
					if (!((Object)(object)prefab == (Object)null))
					{
						ApplyStaff(prefab, value, _configs[item.Prefab]);
					}
				}
			}
		}

		private static void CaptureKnownSummonHealth(string staffPrefab, StaffBaseline baseline)
		{
			object obj = staffPrefab switch
			{
				"StaffSkeleton" => new string[2] { "Skeleton_Friendly", "SkeletonArcher_Friendly" }, 
				"StaffRedTroll" => new string[1] { "Troll_Summoned" }, 
				"StaffGreenRoots" => new string[1] { "staff_greenroots_tentaroot" }, 
				"StaffSpiritCaller" => new string[4] { "Boar_spiritcaller", "Wolf_spiritcaller", "Moose_spiritcaller", "Bjorn_spiritcaller" }, 
				_ => Array.Empty<string>(), 
			};
			List<GraphFieldBaseline> summonGraphFields = baseline.PrimaryAttack.SummonGraphFields;
			string[] array = (string[])obj;
			foreach (string text in array)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(text);
				Character creature = ((prefab != null) ? prefab.GetComponent<Character>() : null);
				if (!((Object)(object)creature == (Object)null))
				{
					FieldInfo fieldInfo = FindField(creature, "m_health");
					if (!(fieldInfo == null) && !summonGraphFields.Exists((GraphFieldBaseline f) => (object)f.Target == creature && f.FieldName == "m_health") && !baseline.SecondaryAttack.SummonGraphFields.Exists((GraphFieldBaseline f) => (object)f.Target == creature && f.FieldName == "m_health"))
					{
						summonGraphFields.Add(new GraphFieldBaseline((Component)(object)creature, fieldInfo, fieldInfo.GetValue(creature)));
					}
				}
			}
		}

		private void ApplyStaff(GameObject prefab, StaffBaseline baseline, StaffConfig cfg)
		{
			if (!cfg.Enabled.Value)
			{
				baseline.Restore(prefab);
				return;
			}
			ItemDrop component = prefab.GetComponent<ItemDrop>();
			if (component?.m_itemData?.m_shared == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)(cfg.DisplayName + ": no ItemDrop/SharedData found."));
				return;
			}
			SharedData shared = component.m_itemData.m_shared;
			ApplyAttack(shared.m_attack, baseline.PrimaryAttack, cfg);
			ApplyAttack(shared.m_secondaryAttack, baseline.SecondaryAttack, cfg);
			ApplyKnownAttackFields(prefab, shared.m_attack, cfg);
			ApplyKnownAttackFields(prefab, shared.m_secondaryAttack, cfg);
			ApplySpecialStaffGraph(prefab, shared.m_attack, baseline.PrimaryAttack, cfg);
			ApplySpecialStaffGraph(prefab, shared.m_secondaryAttack, baseline.SecondaryAttack, cfg);
			ApplySummonGraph(baseline.PrimaryAttack, cfg);
			ApplySummonGraph(baseline.SecondaryAttack, cfg);
		}

		private static void ApplyAttack(Attack? attack, AttackBaseline baseline, StaffConfig cfg)
		{
			if (attack != null)
			{
				SetFloat(attack, "m_attackEitr", baseline.AttackEitr, cfg.EitrCost);
				SetPercentFloat(attack, "m_attackHealthPercentage", baseline.AttackHealthPercentage, cfg.HealthCostPercent);
				SetFloat(attack, "m_attackCooldown", baseline.AttackCooldown, cfg.Cooldown);
				SetFloat(attack, "m_attackForce", baseline.AttackForce, cfg.AttackForce);
				SetFloat(attack, "m_attackForceForward", baseline.AttackForceForward, cfg.AttackForceForward);
				SetFloat(attack, "m_projectileVel", baseline.ProjectileVelocity, cfg.ProjectileSpeed);
				SetFloat(attack, "m_projectileAccuracyMin", baseline.ProjectileAccuracyMin, cfg.ProjectileAccuracyMin);
				SetInt(attack, "m_projectiles", baseline.Projectiles, cfg.ProjectileCount);
				SetFloat(attack, "m_attackDrawTime", baseline.AttackDrawTime, cfg.DrawTime);
				SetFloat(attack, "m_attackDrawPercentage", baseline.AttackDrawPercentage, cfg.DrawPercentage);
				SetFloat(attack, "m_attackStamina", baseline.AttackStamina, cfg.AttackStamina);
				SetFloat(attack, "m_attackStaminaModifier", baseline.AttackStaminaModifier, cfg.AttackStaminaModifier);
				SetFloat(attack, "m_attackStatusEffectChance", baseline.StatusEffectChance, cfg.StatusEffectChance);
				SetFloat(attack, "m_attackHealth", baseline.AttackHealth, cfg.HealthCost);
				SetFloat(attack, "m_attackHealthReturnHit", baseline.HealthReturnHit, cfg.HealthReturnHit);
				SetFloat(attack, "m_attackEitrAdd", baseline.AttackEitrAdd, cfg.EitrAdd);
				SetFloat(attack, "m_chainChance", baseline.ChainChance, cfg.ChainChance);
				SetFloat(attack, "m_chainChancePerTarget", baseline.ChainChancePerTarget, cfg.ChainChancePerTarget);
				SetInt(attack, "m_chainMaxTargets", baseline.ChainMaxTargets, cfg.ChainMaxTargets);
				SetFloat(attack, "m_chainDelay", baseline.ChainDelay, cfg.ChainDelay);
				SetFloat(attack, "m_chainStartChance", baseline.ChainStartChance, cfg.ChainStartChance);
				SetFloat(attack, "m_chainStartChanceFalloff", baseline.ChainStartChanceFalloff, cfg.ChainStartChanceFalloff);
				SetFloat(attack, "m_chainStartDelay", baseline.ChainStartDelay, cfg.ChainStartDelay);
				SetInt(attack, "m_chainMinTargets", baseline.ChainMinTargets, cfg.ChainMinTargets);
				SetFloat(attack, "m_chainAttackMaxTime", baseline.ChainAttackMaxTime, cfg.ChainAttackMaxTime);
				SetInt(attack, "m_attackChainLevels", baseline.AttackChainLevels, cfg.AttackChainLevels);
			}
		}

		private static void ApplySummonGraph(AttackBaseline baseline, StaffConfig cfg)
		{
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "Character" }, "m_health", cfg.SummonHealth);
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "Humanoid" }, "m_walkSpeed", cfg.SummonWalkSpeed);
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "Humanoid" }, "m_runSpeed", cfg.SummonRunSpeed);
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "Attack" }, "m_damageMultiplier", cfg.SummonDamageMultiplier);
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "Attack" }, "m_attackCooldown", cfg.SummonAttackCooldown);
			ApplyGraphField(baseline.SummonGraphFields, new string[1] { "SpawnAbility" }, "m_maxSpawns", cfg.SummonMaxSpawns);
		}

		private static bool TryGetLocalStaffConfig(Character owner, ItemData item, out StaffConfig cfg)
		{
			cfg = null;
			Plugin instance = _instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)owner == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)owner != (Object)(object)Player.m_localPlayer || (Object)(object)item?.m_dropPrefab == (Object)null)
			{
				return false;
			}
			if (instance._configs.TryGetValue(((Object)item.m_dropPrefab).name, out cfg))
			{
				return cfg.Enabled.Value;
			}
			return false;
		}

		private static bool TryGetLocalSpellConfig(Character owner, ItemData item, out StaffConfig cfg)
		{
			if (TryGetLocalStaffConfig(owner, item, out cfg))
			{
				return cfg.DamageMultiplier.IntendedVisible;
			}
			return false;
		}

		private static string? GetSummonStaffPrefab(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return null;
			}
			string name = ((Object)((Component)character).gameObject).name;
			if (name.Contains("Skeleton_Friendly", StringComparison.Ordinal) || name.Contains("SkeletonArcher_Friendly", StringComparison.Ordinal))
			{
				return "StaffSkeleton";
			}
			if (name.Contains("Troll_Summoned", StringComparison.Ordinal))
			{
				return "StaffRedTroll";
			}
			if (name.Contains("staff_greenroots_tentaroot", StringComparison.Ordinal))
			{
				return "StaffGreenRoots";
			}
			if (name.Contains("Boar_spiritcaller", StringComparison.Ordinal) || name.Contains("Wolf_spiritcaller", StringComparison.Ordinal) || name.Contains("Moose_spiritcaller", StringComparison.Ordinal) || name.Contains("Bjorn_spiritcaller", StringComparison.Ordinal))
			{
				return "StaffSpiritCaller";
			}
			return null;
		}

		private static bool IsDeadRaiserSummon(Character character)
		{
			return GetSummonStaffPrefab(character) == "StaffSkeleton";
		}

		private static void ApplyFinalSummonDamage(HitData hit, StaffConfig cfg)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (hit != null && cfg.Enabled.Value && !((Object)(object)hit.GetAttacker() == (Object)null))
			{
				StaffConfig.TuningFloat summonFinalDamageMultiplier = cfg.SummonFinalDamageMultiplier;
				if (summonFinalDamageMultiplier.IntendedVisible && (int)summonFinalDamageMultiplier.Mode.Value != 0)
				{
					float num = DamageTuning.SpellFactor(summonFinalDamageMultiplier.Mode.Value, summonFinalDamageMultiplier.Value.Value);
					hit.m_damage.m_damage *= num;
					hit.m_damage.m_blunt *= num;
					hit.m_damage.m_slash *= num;
					hit.m_damage.m_pierce *= num;
					hit.m_damage.m_chop *= num;
					hit.m_damage.m_pickaxe *= num;
					hit.m_damage.m_fire *= num;
					hit.m_damage.m_frost *= num;
					hit.m_damage.m_lightning *= num;
					hit.m_damage.m_poison *= num;
					hit.m_damage.m_spirit *= num;
				}
			}
		}

		private static void ApplySummonWeaponDamage(ItemData item, StaffConfig cfg)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Invalid comparison between Unknown and I4
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared != null)
			{
				SummonWeaponDamageBaseline value = SummonWeaponBaselines.GetValue(item.m_shared, (SharedData _) => new SummonWeaponDamageBaseline(item));
				if (!cfg.Enabled.Value)
				{
					item.m_shared.m_damages = value.Damages;
					item.m_shared.m_damagesPerLevel = value.DamagesPerLevel;
					return;
				}
				bool num = (int)item.m_shared.m_itemType == 4;
				DamageTypes damages = value.Damages;
				StaffConfig.TuningFloat setting = (num ? cfg.SummonArcherDamageMultiplier : cfg.SummonMeleeDamageMultiplier);
				ApplyDamageTypes(ref damages, setting);
				item.m_shared.m_damages = damages;
				DamageTypes damages2 = value.DamagesPerLevel;
				ApplyDamageTypes(ref damages2, cfg.SummonDamagePerLevelMultiplier);
				item.m_shared.m_damagesPerLevel = damages2;
			}
		}

		private static void ApplyDamageTypes(ref DamageTypes damages, StaffConfig.TuningFloat setting)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			if (setting.IntendedVisible)
			{
				damages.m_damage = Modifier.Apply(damages.m_damage, setting.Mode.Value, setting.Value.Value);
				damages.m_blunt = Modifier.Apply(damages.m_blunt, setting.Mode.Value, setting.Value.Value);
				damages.m_slash = Modifier.Apply(damages.m_slash, setting.Mode.Value, setting.Value.Value);
				damages.m_pierce = Modifier.Apply(damages.m_pierce, setting.Mode.Value, setting.Value.Value);
				damages.m_chop = Modifier.Apply(damages.m_chop, setting.Mode.Value, setting.Value.Value);
				damages.m_pickaxe = Modifier.Apply(damages.m_pickaxe, setting.Mode.Value, setting.Value.Value);
				damages.m_fire = Modifier.Apply(damages.m_fire, setting.Mode.Value, setting.Value.Value);
				damages.m_frost = Modifier.Apply(damages.m_frost, setting.Mode.Value, setting.Value.Value);
				damages.m_lightning = Modifier.Apply(damages.m_lightning, setting.Mode.Value, setting.Value.Value);
				damages.m_poison = Modifier.Apply(damages.m_poison, setting.Mode.Value, setting.Value.Value);
				damages.m_spirit = Modifier.Apply(damages.m_spirit, setting.Mode.Value, setting.Value.Value);
			}
		}

		private static void ApplySpecialStaffGraph(GameObject staffPrefab, Attack? attack, AttackBaseline baseline, StaffConfig cfg)
		{
			if (attack != null)
			{
				ApplyGraphField(baseline.GraphFields, new string[2] { "Aoe", "EffectArea" }, "m_radius", cfg.ExplosionRadius);
				ApplyGraphField(baseline.GraphFields, new string[3] { "Projectile", "Aoe", "EffectArea" }, "m_force", cfg.Knockback);
				ApplyGraphField(baseline.GraphFields, new string[3] { "Projectile", "Aoe", "EffectArea" }, "m_forceStagger", cfg.Knockback);
				ApplyGraphField(baseline.GraphFields, new string[1] { "StatusEffect" }, "m_duration", cfg.StatusEffectDuration);
				ApplyGraphField(baseline.GraphFields, new string[1] { "StatusEffect" }, "m_ttl", cfg.StatusEffectDuration);
				ApplyGraphField(baseline.GraphFields, new string[1] { "SE_Shield" }, "m_absorbDamage", cfg.ShieldAbsorption);
				ApplyGraphField(baseline.GraphFields, new string[1] { "SE_Shield" }, "m_absorbDamagePerSkillLevel", cfg.ShieldAbsorptionPerBloodMagicLevel);
				ApplyGraphField(baseline.GraphFields, new string[1] { "ShieldDome" }, "m_wrapSphereRadius", cfg.VengeanceSphereRadius);
				ApplyGraphField(baseline.GraphFields, new string[1] { "ShieldDome" }, "m_reflectionHeight", cfg.VengeanceReflectionHeight);
				ApplyGraphField(baseline.GraphFields, new string[1] { "Projectile" }, "m_ttl", cfg.ProjectileLifetime);
				ApplyGraphField(baseline.GraphFields, new string[1] { "Projectile" }, "m_ttlMax", cfg.ProjectileLifetime);
				ApplyGraphField(baseline.GraphFields, new string[1] { "Projectile" }, "m_projectileVelocity", cfg.ProjectileVelocity);
				ApplyGraphField(baseline.GraphFields, new string[1] { "Projectile" }, "m_projectileVelocityMax", cfg.ProjectileVelocityMax);
				ApplyGraphField(baseline.GraphFields, new string[1] { "Projectile" }, "m_maxBounces", cfg.ProjectileMaxBounces);
				ApplyGraphField(baseline.GraphFields, new string[3] { "Projectile", "Aoe", "EffectArea" }, "m_damage", cfg.Damage);
				ApplyGraphField(baseline.GraphFields, new string[3] { "Projectile", "Aoe", "EffectArea" }, "m_damagePerHit", cfg.DamagePerHit);
			}
		}

		private static void ApplyGraphField(List<GraphFieldBaseline> fields, string[] componentTypeNames, string fieldName, StaffConfig.TuningFloat entry)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (!entry.IntendedVisible)
			{
				return;
			}
			foreach (GraphFieldBaseline field in fields)
			{
				if (Array.IndexOf(componentTypeNames, field.ComponentTypeName) < 0 || !string.Equals(field.FieldName, fieldName, StringComparison.Ordinal))
				{
					continue;
				}
				if ((int)entry.Mode.Value == 0)
				{
					field.Restore();
					continue;
				}
				float? num = ReadNumeric(field.Field.FieldType, field.VanillaValue);
				if (num.HasValue)
				{
					float value = Modifier.Apply(num.Value, entry.Mode.Value, entry.Value.Value);
					object converted;
					if ((fieldName == "m_health" && !DamageTuning.ValidHealth(value)) || (fieldName == "m_attackCooldown" && !DamageTuning.ValidCooldown(value)))
					{
						field.Restore();
					}
					else if (TryConvertNumber(value, field.Field.FieldType, out converted))
					{
						field.Field.SetValue(field.Target, converted);
					}
				}
			}
		}

		private static void ApplyGraphField(List<GraphFieldBaseline> fields, string[] componentTypeNames, string fieldName, StaffConfig.TuningInt entry)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!entry.IntendedVisible)
			{
				return;
			}
			foreach (GraphFieldBaseline field in fields)
			{
				if (Array.IndexOf(componentTypeNames, field.ComponentTypeName) < 0 || !string.Equals(field.FieldName, fieldName, StringComparison.Ordinal))
				{
					continue;
				}
				if ((int)entry.Mode.Value == 0)
				{
					field.Restore();
					continue;
				}
				float? num = ReadNumeric(field.Field.FieldType, field.VanillaValue);
				if (num.HasValue && TryConvertNumber(Modifier.Apply((int)MathF.Round(num.Value), entry.Mode.Value, entry.Value.Value), field.Field.FieldType, out object converted))
				{
					field.Field.SetValue(field.Target, converted);
				}
			}
		}

		private static FieldInfo? FindField(object target, string fieldName)
		{
			return target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static float? ReadNumeric(Type fieldType, object? value)
		{
			if (value == null)
			{
				return null;
			}
			try
			{
				return Convert.ToSingle(value, CultureInfo.InvariantCulture);
			}
			catch
			{
				return null;
			}
		}

		private static bool TryConvertNumber(float value, Type targetType, out object converted)
		{
			try
			{
				if (targetType == typeof(float))
				{
					converted = value;
					return true;
				}
				if (targetType == typeof(double))
				{
					converted = (double)value;
					return true;
				}
				if (targetType == typeof(int))
				{
					converted = (int)MathF.Round(value);
					return true;
				}
				if (targetType == typeof(long))
				{
					converted = (long)MathF.Round(value);
					return true;
				}
				converted = Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
				return true;
			}
			catch
			{
				converted = null;
				return false;
			}
		}

		private static void ApplyKnownAttackFields(GameObject staffPrefab, Attack? attack, StaffConfig cfg)
		{
		}

		private static void SetPercentFloat(object target, string fieldName, float baseline, StaffConfig.TuningFloat entry)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!entry.IntendedVisible)
			{
				return;
			}
			FieldInfo fieldInfo = FindField(target, fieldName);
			if (!(fieldInfo == null))
			{
				if ((int)entry.Mode.Value == 0)
				{
					fieldInfo.SetValue(target, baseline);
				}
				else if ((int)entry.Mode.Value == 3)
				{
					fieldInfo.SetValue(target, entry.Value.Value);
				}
				else
				{
					fieldInfo.SetValue(target, Modifier.Apply(baseline, entry.Mode.Value, entry.Value.Value));
				}
			}
		}

		private static void SetFloat(object target, string fieldName, float baseline, StaffConfig.TuningFloat entry)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (entry.IntendedVisible)
			{
				float num = Modifier.Apply(baseline, entry.Mode.Value, entry.Value.Value);
				if ((int)entry.Mode.Value == 0)
				{
					num = baseline;
				}
				SetField(target, fieldName, num);
			}
		}

		private static void SetInt(object target, string fieldName, int baseline, StaffConfig.TuningInt entry)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (entry.IntendedVisible)
			{
				int num = Modifier.Apply(baseline, entry.Mode.Value, entry.Value.Value);
				if ((int)entry.Mode.Value == 0)
				{
					num = baseline;
				}
				SetField(target, fieldName, num);
			}
		}

		private static void SetBool(object target, string fieldName, bool baseline, StaffConfig.TuningBool entry)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			bool flag = entry.Value.Value;
			if ((int)entry.Mode.Value == 0)
			{
				flag = baseline;
			}
			SetField(target, fieldName, flag);
		}

		private static void SetField(object target, string fieldName, object value)
		{
			FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(field == null) && field.FieldType.IsAssignableFrom(value.GetType()))
			{
				field.SetValue(target, value);
			}
		}
	}
	public static class SummonControls
	{
		public static void ApplyToInstance(SpawnAbility spawn, int count, bool ignoreDeadRaiserLimit)
		{
			count = Math.Max(1, Math.Min(20, count));
			spawn.m_minToSpawn = count;
			spawn.m_maxToSpawn = count;
			if (ignoreDeadRaiserLimit)
			{
				spawn.m_maxSpawned = 0;
			}
		}
	}
}