Decompiled source of MeleeBuff v2.2.4

plugins\MeleeBuff.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MeleeBuff")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MeleeBuff")]
[assembly: AssemblyTitle("MeleeBuff")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 MeleeBuff
{
	[BepInPlugin("com.leona.meleebuff", "Melee Buff", "2.2.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class RageBuildDamageFrameState
		{
			public int LastFrame;
		}

		private sealed class RageIncomingHalfFrameState
		{
			public int LastFrame;
		}

		[HarmonyPatch(typeof(Character), "Update")]
		public static class CharacterRageHarmonyPatch
		{
			private sealed class RageEdgeState
			{
				public bool WasEligible;
			}

			private static readonly ConditionalWeakTable<Character, RageEdgeState> EligibleEdge = new ConditionalWeakTable<Character, RageEdgeState>();

			[HarmonyPostfix]
			public static void Postfix(Character __instance)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					bool flag = CharacterUtils.IsBuffEligible(__instance);
					if (DumpReflectionEnabled != null && DumpReflectionEnabled.Value)
					{
						ReflectionDumper.TryDumpOnce(__instance);
					}
					if (!EligibleEdge.TryGetValue(__instance, out var value))
					{
						value = new RageEdgeState();
						EligibleEdge.Add(__instance, value);
					}
					if (flag && !value.WasEligible)
					{
						RageBurntHealth.ResetGreyHealth(__instance);
					}
					value.WasEligible = flag;
					float deltaTime = Time.deltaTime;
					PassiveBuffEffects.Tick(__instance, flag, deltaTime);
					if (flag)
					{
						RageManaDrain.ForceZeroMana(__instance);
					}
				}
			}
		}

		[HarmonyPatch]
		public static class WeaponDamageRageHarmonyPatch
		{
			private const int MaxBuildDamageDiagSamples = int.MaxValue;

			private static int _buildDamageDiagCount;

			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(WeaponDamage), "BuildDamage", new Type[5]
				{
					typeof(Weapon),
					typeof(Character),
					typeof(bool),
					typeof(DamageList).MakeByRefType(),
					typeof(float).MakeByRefType()
				}, (Type[])null);
			}

			[HarmonyPostfix]
			public static void Postfix(Weapon _weapon, Character _targetCharacter, bool _isSkillOrShield, ref DamageList _list, ref float _knockback)
			{
				bool flag = Interlocked.Increment(ref _buildDamageDiagCount) <= int.MaxValue;
				if ((Object)(object)_weapon == (Object)null)
				{
					if (flag)
					{
						ModLog.LogWarning((object)"[MeleeBuff][BuildDamage] _weapon is null");
					}
					return;
				}
				Character ownerCharacter = ((EffectSynchronizer)_weapon).OwnerCharacter;
				bool flag2 = (Object)(object)ownerCharacter != (Object)null && CharacterUtils.IsPlayer(ownerCharacter);
				bool flag3 = (Object)(object)ownerCharacter != (Object)null && CharacterUtils.HasRageStatus(ownerCharacter);
				bool flag4 = CharacterUtils.IsBuffEligible(ownerCharacter);
				int num = -1;
				float num2 = -1f;
				if (_list != null)
				{
					num2 = _list.TotalDamage;
					num = ((_list.List != null) ? _list.List.Count : (-1));
				}
				float num3 = num2;
				float num4 = -1f;
				float num5 = -1f;
				string text = null;
				bool flag5 = false;
				if (flag4)
				{
					List<DamageType> list = _list.List;
					if (list != null && list.Count > 0 && list[0] != null)
					{
						num4 = list[0].Damage;
						text = ((object)list[0]).GetType().FullName;
						flag5 = ((object)list[0]).GetType().IsValueType;
					}
					int num6 = DamageUtils.AddHundredPercentToDamageList(_list);
					num3 = _list.TotalDamage;
					MarkBuildDamageApplied(ownerCharacter);
					List<DamageType> list2 = _list.List;
					if (list2 != null && list2.Count > 0 && list2[0] != null)
					{
						num5 = list2[0].Damage;
					}
					if (flag)
					{
						ModLog.LogInfo((object)("[MeleeBuff][BuildDamage][diagRow0] " + $"scaledRows={num6} beforeTotal={num2} afterTotal={num3} " + $"row0Before={num4} row0After={num5} row0Type={text} row0IsValueType={flag5}"));
					}
				}
				else if (flag)
				{
					ModLog.LogInfo((object)("[MeleeBuff][BuildDamage] eligible=false => no scaling " + $"listCount={num} totalDamage={num2}"));
				}
				if (flag)
				{
					ModLog.LogInfo((object)("[MeleeBuff][BuildDamage] " + $"weapon={((Object)_weapon).name} skillOrShield={_isSkillOrShield} " + "targetChar=" + (((object)_targetCharacter)?.GetType().Name ?? "null") + " " + string.Format("attackerNull={0} attackerType={1} ", (Object)(object)ownerCharacter == (Object)null, ((object)ownerCharacter)?.GetType().Name ?? "null") + $"isPlayer={flag2} hasRageStatus={flag3} eligible={flag4} " + $"listNull={_list == null} listCount={num} totalDamage={num2} " + $"afterTotalDamage={num3} row0Before={num4} row0After={num5} row0Type={text}"));
				}
			}
		}

		[HarmonyPatch]
		public static class WeaponDamageDamageMultRageHarmonyPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(WeaponDamage), "DamageMult", new Type[2]
				{
					typeof(Character),
					typeof(bool)
				}, (Type[])null);
			}

			[HarmonyPostfix]
			public static void Postfix(Character _targetCharacter, bool _isSkill, ref float __result)
			{
				ModLog.LogInfo((object)("[MeleeBuff][DamageMult][start] targetChar=" + (((object)_targetCharacter)?.GetType().Name ?? "null") + " " + $"isSkill={_isSkill} beforeMult={__result}"));
				if (!((Object)(object)_targetCharacter == (Object)null) && CharacterUtils.IsBuffEligible(_targetCharacter) && !WasBuildDamageAppliedThisFrame(_targetCharacter))
				{
					float num = __result;
					__result = num + num;
					ModLog.LogInfo((object)("[MeleeBuff][DamageMult] " + $"targetCharacter={((object)_targetCharacter).GetType().Name} isSkill={_isSkill} beforeMult={num} afterMult={__result}"));
				}
			}
		}

		[HarmonyPatch]
		public static class CharacterDamageReceiveLoggingHarmonyPatch
		{
			[HarmonyPatch]
			public static class ProcessDamageReductionPatch
			{
				public static MethodBase TargetMethod()
				{
					return TargetMethodProcessDamageReduction();
				}

				[HarmonyPrefix]
				public static void Prefix(Character __instance, Weapon __0, DamageList __1, bool __2, out float __state)
				{
					__state = ((__1 != null) ? __1.TotalDamage : (-1f));
					Character val = (((Object)(object)__0 != (Object)null) ? ((EffectSynchronizer)__0).OwnerCharacter : null);
					bool flag = (Object)(object)val != (Object)null && CharacterUtils.IsBuffEligible(val);
					bool flag2 = __2;
					bool flag3 = flag && !flag2 && !WasBuildDamageAppliedThisFrame(val);
					int num = 0;
					if (flag3 && __1 != null)
					{
						num = DamageUtils.AddHundredPercentToDamageList(__1);
						__state = ((__1 != null) ? __1.TotalDamage : __state);
						MarkBuildDamageApplied(val);
					}
					bool flag4 = (Object)(object)__instance != (Object)null && CharacterUtils.IsBuffEligible(__instance);
					ModLog.LogInfo((object)("[MeleeBuff][ProcessDamageReduction][before] " + string.Format("weapon={0} eligibleInstance={1} eligibleAttacker={2} ", ((Object)(object)__0 == (Object)null) ? "null" : ((Object)__0).name, flag4, flag) + $"flag={__2} shouldDoubleBasics={flag3} scaledRows={num} total={__state}"));
				}

				[HarmonyPostfix]
				public static void Postfix(Character __instance, Weapon __0, DamageList __1, bool __2, float __state)
				{
					if ((Object)(object)__instance != (Object)null && CharacterUtils.IsBuffEligible(__instance) && !WasIncomingHalfAppliedThisFrame(__instance))
					{
						List<DamageType> list = ((__1 != null) ? __1.List : null);
						if (list != null)
						{
							for (int i = 0; i < list.Count; i++)
							{
								DamageType val = list[i];
								if (val != null)
								{
									val.Damage *= 0.5f;
								}
							}
						}
						MarkIncomingHalfApplied(__instance);
					}
					float num = ((__1 != null) ? __1.TotalDamage : (-1f));
					ModLog.LogInfo((object)("[MeleeBuff][ProcessDamageReduction][after] " + string.Format("weapon={0} flag={1} totalBefore={2} totalAfter={3}", ((Object)(object)__0 == (Object)null) ? "null" : ((Object)__0).name, __2, __state, num)));
				}
			}

			[HarmonyPatch]
			public static class OnReceiveHitPatch
			{
				public static MethodBase TargetMethod()
				{
					return TargetMethodOnReceiveHit();
				}

				[HarmonyPrefix]
				public static void Prefix(Weapon __0, float __1, DamageList __2, Vector3 __3, Vector3 __4, float __5, float __6, Character __7, float __8)
				{
					bool flag = (Object)(object)__7 != (Object)null && CharacterUtils.IsBuffEligible(__7);
					float num = ((__2 != null) ? __2.TotalDamage : (-1f));
					int num2 = ((((__2 != null) ? __2.List : null) != null) ? __2.List.Count : (-1));
					ModLog.LogInfo((object)("[MeleeBuff][OnReceiveHit] " + string.Format("attacker={0} eligible={1} ", ((Object)(object)__7 == (Object)null) ? "null" : ((object)__7).GetType().Name, flag) + string.Format("weapon={0} total={1} listCount={2} f0={3} f1={4} f2={5} f3={6}", ((Object)(object)__0 == (Object)null) ? "null" : ((Object)__0).name, num, num2, __1, __5, __6, __8)));
				}
			}

			private static MethodBase TargetMethodReceiveHitDamageList()
			{
				return AccessTools.Method(typeof(Character), "ReceiveHit", new Type[9]
				{
					typeof(Object),
					typeof(DamageList),
					typeof(Vector3),
					typeof(Vector3),
					typeof(float),
					typeof(float),
					typeof(Character),
					typeof(float),
					typeof(bool)
				}, (Type[])null);
			}

			private static MethodBase TargetMethodProcessDamageReduction()
			{
				return AccessTools.Method(typeof(Character), "ProcessDamageReduction", new Type[3]
				{
					typeof(Weapon),
					typeof(DamageList),
					typeof(bool)
				}, (Type[])null);
			}

			private static MethodBase TargetMethodOnReceiveHit()
			{
				return AccessTools.Method(typeof(Character), "OnReceiveHit", new Type[9]
				{
					typeof(Weapon),
					typeof(float),
					typeof(DamageList),
					typeof(Vector3),
					typeof(Vector3),
					typeof(float),
					typeof(float),
					typeof(Character),
					typeof(float)
				}, (Type[])null);
			}

			public static MethodBase TargetMethod()
			{
				return TargetMethodReceiveHitDamageList();
			}

			[HarmonyPrefix]
			public static void Prefix(Object __0, DamageList __1, Vector3 __2, Vector3 __3, float __4, float __5, Character __6, float __7, bool __8, out float __state)
			{
				__state = ((__1 != null) ? __1.TotalDamage : (-1f));
				bool flag = (Object)(object)__6 != (Object)null && CharacterUtils.IsBuffEligible(__6);
				if (__0 != (Object)null)
				{
					_ = ((object)__0).GetType().Name.IndexOf("Weapon", StringComparison.OrdinalIgnoreCase) >= 0;
				}
				else
					_ = 0;
				int num = 0;
				float num2 = __state;
				int num3 = ((((__1 != null) ? __1.List : null) != null) ? __1.List.Count : (-1));
				float num4 = -1f;
				if (((__1 != null) ? __1.List : null) != null && __1.List.Count > 0 && __1.List[0] != null)
				{
					num4 = __1.List[0].Damage;
				}
				ModLog.LogInfo((object)("[MeleeBuff][ReceiveHitPrefix] " + string.Format("attacker={0} eligible={1} ", ((Object)(object)__6 == (Object)null) ? "null" : ((object)__6).GetType().Name, flag) + string.Format("hitInventory={0} sourceType={1} ", __8, (__0 == (Object)null) ? "null" : ((object)__0).GetType().Name) + $"totalBefore={__state} listCount={num3} row0={num4} " + $"rageDoubledRows={num} totalAfterDoubling={num2}"));
			}

			[HarmonyPostfix]
			public static void Postfix(Object __0, DamageList __1, Vector3 __2, Vector3 __3, float __4, float __5, Character __6, float __7, bool __8, DamageList __result, float __state)
			{
				float num = ((__result != null) ? __result.TotalDamage : (-1f));
				ModLog.LogInfo((object)("[MeleeBuff][ReceiveHitPostfix] " + string.Format("attacker={0} hitInventory={1} ", ((Object)(object)__6 == (Object)null) ? "null" : ((object)__6).GetType().Name, __8) + $"totalBefore={__state} totalAfter={num}"));
			}
		}

		private static class CharacterUtils
		{
			private static readonly string[] RageStatusIdentifiers = new string[2] { "Rage", "Rage Amplified" };

			public static bool IsCharacterLike(object obj)
			{
				if (obj == null)
				{
					return false;
				}
				string name = obj.GetType().Name;
				if (name.IndexOf("Character", StringComparison.OrdinalIgnoreCase) < 0)
				{
					return name.IndexOf("CharacterStats", StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return true;
			}

			public static bool IsPlayer(object obj)
			{
				if (obj == null)
				{
					return false;
				}
				if (obj.GetType().Name.IndexOf("Player", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
				bool value;
				return TryGetBoolMember(obj, "IsLocalPlayer", out value) && value;
			}

			public static bool IsBuffEligible(object obj)
			{
				if (!IsPlayer(obj) || GetMaxMana(obj) >= 50f)
				{
					return false;
				}
				return HasRageStatus(obj);
			}

			public static float GetMaxMana(object obj)
			{
				if (obj == null)
				{
					return 0f;
				}
				object memberValue = ReflectionUtils.GetMemberValue(obj, "Stats");
				if (memberValue == null)
				{
					return 0f;
				}
				if (!ConvertNumeric(ReflectionUtils.GetMemberValue(memberValue, "MaxMana"), out var value))
				{
					return 0f;
				}
				return value;
			}

			private static bool ConvertNumeric(object raw, out float value)
			{
				value = 0f;
				if (raw == null)
				{
					return false;
				}
				if (raw is float num)
				{
					value = num;
					return true;
				}
				if (raw is double num2)
				{
					value = (float)num2;
					return true;
				}
				if (raw is int num3)
				{
					value = num3;
					return true;
				}
				if (raw is uint num4)
				{
					value = num4;
					return true;
				}
				if (raw is long num5)
				{
					value = num5;
					return true;
				}
				if (raw is ulong num6)
				{
					value = ((num6 > int.MaxValue) ? float.MaxValue : ((float)num6));
					return true;
				}
				if (raw is short num7)
				{
					value = num7;
					return true;
				}
				if (raw is ushort num8)
				{
					value = (int)num8;
					return true;
				}
				if (raw is byte b)
				{
					value = (int)b;
					return true;
				}
				if (raw is sbyte b2)
				{
					value = b2;
					return true;
				}
				if (raw is decimal num9)
				{
					value = (float)num9;
					return true;
				}
				if (raw is string s)
				{
					return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
				}
				return false;
			}

			public static bool HasRageStatus(object characterLike)
			{
				object statusEffectManager = GetStatusEffectManager(characterLike);
				if (statusEffectManager == null)
				{
					return false;
				}
				string[] rageStatusIdentifiers = RageStatusIdentifiers;
				foreach (string identifier in rageStatusIdentifiers)
				{
					if (ManagerKnowsStatus(statusEffectManager, identifier))
					{
						return true;
					}
				}
				return false;
			}

			private static object GetStatusEffectManager(object characterLike)
			{
				return ReflectionUtils.GetMemberValue(characterLike, "StatusEffectMngr");
			}

			private static bool ManagerKnowsStatus(object manager, string identifier)
			{
				object obj = ReflectionUtils.InvokeMethodWithArgs(manager, "HasStatusEffect", identifier);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
				return false;
			}

			private static bool TryGetBoolMember(object obj, string name, out bool value)
			{
				value = false;
				if (ReflectionUtils.GetMemberValue(obj, name) is bool flag)
				{
					value = flag;
					return true;
				}
				return false;
			}
		}

		private static class RageManaDrain
		{
			public static void ForceZeroMana(object character)
			{
				if (character != null)
				{
					object memberValue = ReflectionUtils.GetMemberValue(character, "Stats");
					if (memberValue != null)
					{
						ReflectionUtils.SetMemberValue(memberValue, "m_mana", 0f);
						ReflectionUtils.SetMemberValue(memberValue, "m_burntMana", 0f);
					}
				}
			}
		}

		private static class RageBurntHealth
		{
			public static void ResetGreyHealth(Character character)
			{
				if (!((Object)(object)character == (Object)null))
				{
					CharacterStats stats = character.Stats;
					if ((Object)(object)stats != (Object)null)
					{
						TryRestoreAllBurnt(stats);
					}
					PlayerCharacterStats playerStats = character.PlayerStats;
					if ((Object)(object)playerStats != (Object)null && playerStats != stats)
					{
						TryRestoreAllBurnt((CharacterStats)(object)playerStats);
					}
				}
			}

			private static void TryRestoreAllBurnt(CharacterStats stats)
			{
				float burntHealth = stats.BurntHealth;
				if (!(burntHealth <= 0.0001f))
				{
					stats.RestoreBurntHealth(burntHealth, true);
					burntHealth = stats.BurntHealth;
					if (burntHealth > 0.0001f)
					{
						stats.RestoreBurntHealth(burntHealth, false);
					}
				}
			}
		}

		private static class DamageUtils
		{
			public static int AddHundredPercentToDamageList(DamageList damageList)
			{
				if (damageList == null)
				{
					return 0;
				}
				List<DamageType> list = damageList.List;
				if (list == null)
				{
					return 0;
				}
				int num = 0;
				for (int i = 0; i < list.Count; i++)
				{
					DamageType val = list[i];
					if (val != null)
					{
						val.Damage += val.Damage;
						num++;
					}
				}
				return num;
			}
		}

		private static class PassiveBuffEffects
		{
			private delegate bool TryFindFloatStatMember(object character, out object target, out string memberName, out float baseline);

			private sealed class FloatStatSnap
			{
				public object Target;

				public string MemberName;

				public float Baseline;
			}

			private const float RegenPerSecondWhileRage = 1f;

			private const float StaminaRegenMultiplier = 1.25f;

			private static readonly Dictionary<int, FloatStatSnap> StaminaRegenSnaps = new Dictionary<int, FloatStatSnap>();

			public static void Tick(object character, bool eligible, float deltaTime)
			{
				if (character == null)
				{
					return;
				}
				int hashCode = character.GetHashCode();
				if (!eligible)
				{
					RestoreFloatStat(StaminaRegenSnaps, hashCode);
					return;
				}
				if (deltaTime > 0f && deltaTime < 2f)
				{
					ApplySignedHealthPerSecond(character, 1f * deltaTime);
				}
				ApplyStaminaRegenBuff(character, hashCode);
			}

			private static void RestoreFloatStat(Dictionary<int, FloatStatSnap> dict, int id)
			{
				if (dict.TryGetValue(id, out var value))
				{
					if (value.Target != null && !string.IsNullOrEmpty(value.MemberName))
					{
						ReflectionUtils.SetMemberValue(value.Target, value.MemberName, ConvertNumeric(value.Target, value.MemberName, value.Baseline));
					}
					dict.Remove(id);
				}
			}

			private static void ApplySignedHealthPerSecond(object character, float signedAmount)
			{
				if (signedAmount == 0f)
				{
					return;
				}
				if (signedAmount > 0f)
				{
					if (!TryModifyHealthViaFields(character, signedAmount))
					{
						TryHealViaMethods(character, signedAmount);
					}
				}
				else
				{
					TryModifyHealthViaFields(character, signedAmount);
				}
			}

			private static bool TryHealViaMethods(object character, float amount)
			{
				string[] array = new string[6] { "AddHealth", "Heal", "RestoreHealth", "ReceiveHeal", "ChangeHealth", "AddVitality" };
				foreach (string methodName in array)
				{
					if (ReflectionUtils.TryInvokeMethodWithOneNumericArg(character, methodName, amount))
					{
						return true;
					}
				}
				return false;
			}

			private static bool TryModifyHealthViaFields(object character, float delta)
			{
				if (delta == 0f)
				{
					return false;
				}
				object memberValue = ReflectionUtils.GetMemberValue(character, "Stats");
				if (memberValue == null)
				{
					return false;
				}
				if (!TryReadNumeric(memberValue, "CurrentHealth", out var value) || value < 0f)
				{
					return false;
				}
				if (!TryReadNumeric(memberValue, "MaxHealth", out var value2) || value2 <= 0f)
				{
					return false;
				}
				float num = value + delta;
				if (delta > 0f)
				{
					if (value >= value2)
					{
						return false;
					}
					num = Mathf.Min(num, value2);
				}
				else
				{
					num = Mathf.Max(0f, num);
				}
				return ReflectionUtils.SetMemberValue(memberValue, "CurrentHealth", ConvertNumeric(memberValue, "CurrentHealth", num));
			}

			private static object ConvertNumeric(object target, string memberName, float value)
			{
				Type type = target.GetType();
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					if (property.PropertyType == typeof(int))
					{
						return (int)value;
					}
					if (property.PropertyType == typeof(float))
					{
						return value;
					}
					if (property.PropertyType == typeof(double))
					{
						return (double)value;
					}
				}
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					if (field.FieldType == typeof(int))
					{
						return (int)value;
					}
					if (field.FieldType == typeof(float))
					{
						return value;
					}
					if (field.FieldType == typeof(double))
					{
						return (double)value;
					}
				}
				return value;
			}

			private static bool TryReadNumeric(object target, string memberName, out float value)
			{
				value = 0f;
				object memberValue = ReflectionUtils.GetMemberValue(target, memberName);
				if (memberValue == null)
				{
					return false;
				}
				if (memberValue is float num)
				{
					value = num;
					return true;
				}
				if (memberValue is int num2)
				{
					value = num2;
					return true;
				}
				if (memberValue is double num3)
				{
					value = (float)num3;
					return true;
				}
				return float.TryParse(memberValue.ToString(), out value);
			}

			private static void ApplyStaminaRegenBuff(object character, int id)
			{
				ApplyScaledFloatStat(character, id, StaminaRegenSnaps, TryFindStaminaRegenMember, 1.25f);
			}

			private static void ApplyScaledFloatStat(object character, int id, Dictionary<int, FloatStatSnap> dict, TryFindFloatStatMember finder, float multiplier)
			{
				if (!dict.TryGetValue(id, out var value) || value.Target == null)
				{
					if (!finder(character, out var target, out var memberName, out var baseline))
					{
						return;
					}
					value = (dict[id] = new FloatStatSnap
					{
						Target = target,
						MemberName = memberName,
						Baseline = baseline
					});
				}
				float value2 = value.Baseline * multiplier;
				ReflectionUtils.SetMemberValue(value.Target, value.MemberName, ConvertNumeric(value.Target, value.MemberName, value2));
			}

			private static bool TryFindStaminaRegenMember(object character, out object target, out string memberName, out float baseline)
			{
				target = null;
				memberName = null;
				baseline = 0f;
				object memberValue = ReflectionUtils.GetMemberValue(character, "Stats");
				if (memberValue == null)
				{
					return false;
				}
				if (!TryReadNumeric(memberValue, "StaminaRegen", out var value) || value < 0f)
				{
					return false;
				}
				Type type = memberValue.GetType();
				PropertyInfo property = type.GetProperty("StaminaRegen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					if (!property.CanWrite)
					{
						return false;
					}
				}
				else
				{
					FieldInfo field = type.GetField("StaminaRegen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || field.IsInitOnly)
					{
						return false;
					}
				}
				target = memberValue;
				memberName = "StaminaRegen";
				baseline = value;
				return true;
			}
		}

		private static class ReflectionDumper
		{
			private static readonly string[] StatsCandidates = new string[3] { "Stats", "CharacterStats", "m_stats" };

			private static readonly string[] OwnerCandidates = new string[3] { "OwnerCharacter", "Owner", "m_owner" };

			private static readonly string[] BoolPlayerCandidates = new string[4] { "IsPlayer", "IsLocalPlayer", "IsPlayerControlled", "OwnerPlayer" };

			private static readonly string[] StatusEffectManagerCandidates = new string[4] { "StatusEffectMngr", "StatusEffectManager", "m_statusEffectMngr", "m_statusEffectManager" };

			private static readonly string[] MaxManaCandidates = new string[16]
			{
				"MaxMana", "MaximumMana", "BaseMaxMana", "ActiveMaxMana", "ManaPoint", "ManaPoints", "ManaCap", "MaxManaPoints", "m_maxMana", "m_maximumMana",
				"m_baseMaxMana", "m_activeMaxMana", "m_manaPoint", "m_manaPoints", "m_manaCap", "m_maxManaPoints"
			};

			private static readonly string[] RageStatusIdentifiers = new string[2] { "Rage", "Rage Amplified" };

			private static readonly string[] CurrentManaCandidates = new string[4] { "CurrentMana", "Mana", "m_mana", "m_currentMana" };

			private static readonly string[] BurntManaCandidates = new string[2] { "BurntMana", "m_burntMana" };

			private static readonly string[] MoveSpeedCandidates = new string[9] { "MovementSpeed", "MoveSpeed", "m_movementSpeed", "m_moveSpeed", "WalkSpeed", "BaseMoveSpeed", "m_walkSpeed", "Speed", "MoveModifier" };

			private static readonly string[] StaminaRegenCandidates = new string[13]
			{
				"StaminaRegen", "m_staminaRegen", "StaminaRegeneration", "StaminaRecovery", "StaminaRecoverRate", "PassiveStaminaRegen", "StaminaRegenRate", "BaseStaminaRegen", "StaminaGainPerSecond", "m_staminaRecovery",
				"StaminaRecharge", "StaminaRegenModifier", "BonusStaminaRegen"
			};

			private static readonly string[] CurHealthCandidates = new string[7] { "CurrentHealth", "Health", "HP", "m_currentHealth", "m_health", "Vitality", "m_vitality" };

			private static readonly string[] MaxHealthCandidates = new string[5] { "MaxHealth", "MaximumHealth", "m_maxHealth", "MaxVitality", "m_maxVitality" };

			private static readonly string[] HealMethodCandidates = new string[6] { "AddHealth", "Heal", "RestoreHealth", "ReceiveHeal", "ChangeHealth", "AddVitality" };

			public static void TryDumpOnce(Character character)
			{
				if (!((Object)(object)character == (Object)null) && Interlocked.Exchange(ref _reflectionDumpRan, 1) == 0)
				{
					Dump(character);
				}
			}

			private static void Dump(object character)
			{
				try
				{
					ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][begin] characterType=" + character.GetType().FullName));
					object obj = null;
					string text = null;
					string[] statsCandidates = StatsCandidates;
					Type memberType;
					bool isProperty;
					foreach (string text2 in statsCandidates)
					{
						if (TryReadMemberValue(character, text2, out var value, out memberType, out isProperty, out var _) && value != null)
						{
							obj = value;
							text = text2;
							break;
						}
					}
					ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][statsRoot] chosen=" + (text ?? "null") + " statsRootType=" + ((obj == null) ? "null" : obj.GetType().FullName)));
					statsCandidates = BoolPlayerCandidates;
					foreach (string text3 in statsCandidates)
					{
						if (TryReadMemberValue(character, text3, out var value2, out var memberType2, out isProperty, out var error2))
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][boolPlayer] " + text3 + " found memberType=" + (memberType2?.FullName ?? "null") + " value=" + FormatValue(value2) + " err=" + (error2 ?? "none")));
						}
						else
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][boolPlayer] " + text3 + " NOTFOUND"));
						}
					}
					statsCandidates = OwnerCandidates;
					foreach (string text4 in statsCandidates)
					{
						if (TryReadMemberValue(character, text4, out var value3, out var memberType3, out isProperty, out var error3) && value3 != null)
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][owner] " + text4 + " valueType=" + (memberType3?.FullName ?? "null") + " valueTypeActual=" + value3.GetType().FullName + " err=" + (error3 ?? "none")));
						}
						else if (TryHasMember(character, text4, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, out memberType))
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][owner] " + text4 + " FOUND but value=null err=" + (error3 ?? "none")));
						}
						else
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][owner] " + text4 + " NOTFOUND"));
						}
					}
					object obj2 = null;
					string text5 = null;
					Type type = null;
					statsCandidates = StatusEffectManagerCandidates;
					foreach (string text6 in statsCandidates)
					{
						if (TryReadMemberValue(character, text6, out var value4, out var memberType4, out isProperty, out var error4))
						{
							if (text5 == null)
							{
								text5 = text6;
								type = memberType4;
							}
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][statusManager] candidate=" + text6 + " memberType=" + (memberType4?.FullName ?? "null") + " value=" + ((value4 == null) ? "null" : value4.GetType().FullName) + " valueFormatted=" + FormatValue(value4) + " err=" + (error4 ?? "none")));
							if (obj2 == null && value4 != null)
							{
								obj2 = value4;
							}
						}
						else
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][statusManager] candidate=" + text6 + " NOTFOUND"));
						}
					}
					ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][statusManager] chosen=" + (text5 ?? "null") + " managerValueType=" + ((obj2 == null) ? "null" : obj2.GetType().FullName) + " managerDeclaredType=" + ((type == null) ? "null" : type.FullName)));
					if (obj2 != null)
					{
						DumpMethodOverloads(obj2, "HasStatusEffect", null);
						DumpMethodOverloads(obj2, "HasStatus", null);
						statsCandidates = RageStatusIdentifiers;
						foreach (string text7 in statsCandidates)
						{
							ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][rageStatusId] identifier=\"" + text7 + "\""));
						}
					}
					else
					{
						DumpMethodOverloadsOnType(type, "HasStatusEffect", null);
						DumpMethodOverloadsOnType(type, "HasStatus", null);
					}
					bool flag = false;
					string text8 = null;
					object[] array = new object[2] { character, obj };
					foreach (object obj3 in array)
					{
						if (obj3 == null)
						{
							continue;
						}
						statsCandidates = MaxManaCandidates;
						int num = 0;
						string text9;
						string error5;
						float value6;
						while (num < statsCandidates.Length)
						{
							text9 = statsCandidates[num];
							if (!TryReadMemberValue(obj3, text9, out var value5, out memberType, out isProperty, out error5) || !TryConvertNumeric(value5, out value6))
							{
								num++;
								continue;
							}
							goto IL_049e;
						}
						continue;
						IL_049e:
						flag = true;
						text8 = ((obj3 == character) ? "character" : "statsRoot");
						ModLog.LogInfo((object)string.Format("[MeleeBuff][ReflectionDump][maxMana] found root={0} member={1} value={2} err={3}", text8, text9, value6, error5 ?? "none"));
						break;
					}
					if (!flag)
					{
						ModLog.LogInfo((object)"[MeleeBuff][ReflectionDump][maxMana] no numeric member found in candidates");
					}
					array = new object[2] { character, obj };
					foreach (object obj4 in array)
					{
						if (obj4 != null)
						{
							string text10 = ((obj4 == character) ? "character" : "statsRoot");
							statsCandidates = CurrentManaCandidates;
							foreach (string memberName in statsCandidates)
							{
								DumpNumericProbe(obj4, text10, memberName);
							}
							statsCandidates = BurntManaCandidates;
							foreach (string memberName2 in statsCandidates)
							{
								DumpNumericProbe(obj4, text10, memberName2);
							}
							DumpCandidateWritableNumeric(obj4, text10 + ".currentMana", CurrentManaCandidates, (float _) => true);
							DumpCandidateWritableNumeric(obj4, text10 + ".burntMana", BurntManaCandidates, (float _) => true);
						}
					}
					object obj5 = obj ?? character;
					string text11 = ((obj == null) ? "character" : "statsRoot");
					ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][passiveStats] using=" + text11 + " type=" + obj5.GetType().FullName));
					DumpCandidateWritableNumeric(obj5, "MoveSpeed", MoveSpeedCandidates, (float v) => v > 0.001f);
					DumpCandidateWritableNumeric(obj5, "StaminaRegen", StaminaRegenCandidates, (float v) => v >= 0f);
					if (obj5 != character)
					{
						DumpCandidateWritableNumeric(character, "MoveSpeed", MoveSpeedCandidates, (float v) => v > 0.001f);
						DumpCandidateWritableNumeric(character, "StaminaRegen", StaminaRegenCandidates, (float v) => v >= 0f);
					}
					DumpCandidateWritableNumeric(obj5, "healthCur", CurHealthCandidates, (float v) => v >= 0f);
					statsCandidates = MaxHealthCandidates;
					foreach (string memberName3 in statsCandidates)
					{
						DumpNumericProbe(obj5, "healthStats", memberName3);
					}
					DumpOneNumericMethodOverloads(character, HealMethodCandidates);
					DumpMethodOverloads(character, "SetMana", new Type[2]
					{
						typeof(float),
						typeof(int)
					});
					ModLog.LogInfo((object)"[MeleeBuff][ReflectionDump][end]");
				}
				catch (Exception ex)
				{
					ModLog.LogError((object)("[MeleeBuff][ReflectionDump][error] " + ex.GetType().Name + ": " + ex.Message));
				}
			}

			private static bool TryHasMember(object instance, string memberName, BindingFlags flags, out Type memberType)
			{
				memberType = null;
				if (instance == null || string.IsNullOrWhiteSpace(memberName))
				{
					return false;
				}
				Type type = instance.GetType();
				PropertyInfo property = type.GetProperty(memberName, flags);
				if (property != null)
				{
					memberType = property.PropertyType;
					return true;
				}
				FieldInfo field = type.GetField(memberName, flags);
				if (field != null)
				{
					memberType = field.FieldType;
					return true;
				}
				return false;
			}

			private static void DumpMethodOverloads(object instance, string methodName, Type[] desiredParamTypes)
			{
				if (instance == null)
				{
					return;
				}
				Type type = instance.GetType();
				MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					where string.Equals(m.Name, methodName, StringComparison.Ordinal)
					select m).ToArray();
				ModLog.LogInfo((object)$"[MeleeBuff][ReflectionDump][method] {type.FullName}.{methodName} overloads={array.Length}");
				if (array.Length == 0)
				{
					return;
				}
				MethodInfo[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					ParameterInfo[] parameters = array2[i].GetParameters();
					string arg = string.Join(",", parameters.Select((ParameterInfo p) => p.ParameterType.FullName));
					bool flag = desiredParamTypes == null || (parameters.Length == 1 && UnityEngineExtensions.Contains<Type>(desiredParamTypes, parameters[0].ParameterType));
					ModLog.LogInfo((object)$"[MeleeBuff][ReflectionDump][method]   overload params=[{arg}] matchesDesired={flag}");
				}
			}

			private static void DumpMethodOverloadsOnType(Type type, string methodName, Type[] desiredParamTypes)
			{
				if (type == null)
				{
					return;
				}
				MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					where string.Equals(m.Name, methodName, StringComparison.Ordinal)
					select m).ToArray();
				ModLog.LogInfo((object)$"[MeleeBuff][ReflectionDump][methodOnType] {type.FullName}.{methodName} overloads={array.Length}");
				if (array.Length == 0)
				{
					return;
				}
				MethodInfo[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					ParameterInfo[] parameters = array2[i].GetParameters();
					string arg = string.Join(",", parameters.Select((ParameterInfo p) => p.ParameterType.FullName));
					bool flag = desiredParamTypes == null || (parameters.Length == 1 && UnityEngineExtensions.Contains<Type>(desiredParamTypes, parameters[0].ParameterType));
					ModLog.LogInfo((object)$"[MeleeBuff][ReflectionDump][methodOnType]   overload params=[{arg}] matchesDesired={flag}");
				}
			}

			private static void DumpOneNumericMethodOverloads(object instance, string[] methodNames)
			{
				Type type = instance?.GetType();
				if (type == null)
				{
					return;
				}
				foreach (string name in methodNames)
				{
					MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
						where string.Equals(m.Name, name, StringComparison.Ordinal)
						select m).ToArray();
					if (array.Length == 0)
					{
						ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][healMethod] " + name + " NOTFOUND"));
						continue;
					}
					ModLog.LogInfo((object)$"[MeleeBuff][ReflectionDump][healMethod] {name} overloads={array.Length}");
					MethodInfo[] array2 = array;
					for (int j = 0; j < array2.Length; j++)
					{
						ParameterInfo[] parameters = array2[j].GetParameters();
						string text = string.Join(",", parameters.Select((ParameterInfo p) => p.ParameterType.FullName));
						ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][healMethod]   params=[" + text + "]"));
					}
				}
			}

			private static void DumpCandidateWritableNumeric(object root, string label, string[] candidates, Func<float, bool> accept)
			{
				if (root == null)
				{
					return;
				}
				Type type = root.GetType();
				bool flag = false;
				foreach (string text in candidates)
				{
					if (TryReadMemberValue(root, text, out var value, out var memberType, out var isProperty, out var error) && TryConvertNumeric(value, out var value2) && accept(value2))
					{
						flag = true;
						bool flag2 = false;
						if (isProperty)
						{
							PropertyInfo property = type.GetProperty(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							flag2 = property != null && property.CanWrite;
						}
						else
						{
							FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							flag2 = field != null && !field.IsInitOnly;
						}
						ModLog.LogInfo((object)string.Format("[MeleeBuff][ReflectionDump][{0}] found rootType={1} member={2} memberType={3} value={4} canWrite={5} err={6}", label, type.FullName, text, memberType?.FullName ?? "null", value2, flag2, error ?? "none"));
					}
				}
				if (!flag)
				{
					ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][" + label + "] no writable numeric member met acceptance thresholds on type=" + type.FullName));
				}
			}

			private static void DumpNumericProbe(object root, string rootLabel, string memberName)
			{
				if (root != null)
				{
					float value2;
					if (!TryReadMemberValue(root, memberName, out var value, out var memberType, out var _, out var error))
					{
						ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][numericProbe] root=" + rootLabel + " member=" + memberName + " NOTFOUND"));
					}
					else if (TryConvertNumeric(value, out value2))
					{
						ModLog.LogInfo((object)string.Format("[MeleeBuff][ReflectionDump][numericProbe] root={0} member={1} value={2} memberType={3} err={4}", rootLabel, memberName, value2, memberType?.FullName ?? "null", error ?? "none"));
					}
					else
					{
						ModLog.LogInfo((object)("[MeleeBuff][ReflectionDump][numericProbe] root=" + rootLabel + " member=" + memberName + " valueType=" + (value?.GetType().FullName ?? "null") + " memberType=" + (memberType?.FullName ?? "null") + " err=" + (error ?? "none")));
					}
				}
			}

			private static bool TryConvertNumeric(object raw, out float value)
			{
				value = 0f;
				if (raw == null)
				{
					return false;
				}
				if (raw is float num)
				{
					value = num;
					return true;
				}
				if (raw is double num2)
				{
					value = (float)num2;
					return true;
				}
				if (raw is int num3)
				{
					value = num3;
					return true;
				}
				if (raw is uint num4)
				{
					value = num4;
					return true;
				}
				if (raw is long num5)
				{
					value = num5;
					return true;
				}
				if (raw is ulong num6)
				{
					value = ((num6 > int.MaxValue) ? float.MaxValue : ((float)num6));
					return true;
				}
				if (raw is short num7)
				{
					value = num7;
					return true;
				}
				if (raw is ushort num8)
				{
					value = (int)num8;
					return true;
				}
				if (raw is byte b)
				{
					value = (int)b;
					return true;
				}
				if (raw is sbyte b2)
				{
					value = b2;
					return true;
				}
				if (raw is decimal num9)
				{
					value = (float)num9;
					return true;
				}
				if (raw is string s)
				{
					return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
				}
				return false;
			}

			private static bool TryReadMemberValue(object instance, string memberName, out object value, out Type memberType, out bool isProperty, out string error)
			{
				value = null;
				memberType = null;
				isProperty = false;
				error = null;
				if (instance == null || string.IsNullOrWhiteSpace(memberName))
				{
					return false;
				}
				Type type = instance.GetType();
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					isProperty = true;
					memberType = property.PropertyType;
					if (!property.CanRead)
					{
						error = "property !CanRead";
						return true;
					}
					try
					{
						value = property.GetValue(instance, null);
					}
					catch (Exception ex)
					{
						error = ex.GetType().Name + ":" + ex.Message;
					}
					return true;
				}
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					isProperty = false;
					memberType = field.FieldType;
					try
					{
						value = field.GetValue(instance);
					}
					catch (Exception ex2)
					{
						error = ex2.GetType().Name + ":" + ex2.Message;
					}
					return true;
				}
				return false;
			}

			private static string FormatValue(object value)
			{
				if (value == null)
				{
					return "null";
				}
				try
				{
					if (value is float num)
					{
						return num.ToString(CultureInfo.InvariantCulture);
					}
					if (value is double num2)
					{
						return num2.ToString(CultureInfo.InvariantCulture);
					}
					string text = value as string;
					if (text != null)
					{
						if (text.Length > 120)
						{
							text = text.Substring(0, 120) + "...";
						}
						return "\"" + text + "\"";
					}
					_ = value.GetType().IsEnum;
					return value.ToString();
				}
				catch
				{
					return value.GetType().FullName;
				}
			}
		}

		private static class ReflectionUtils
		{
			public static object GetMemberValue(object instance, string memberName)
			{
				if (instance == null || string.IsNullOrWhiteSpace(memberName))
				{
					return null;
				}
				Type type = instance.GetType();
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					if (!property.CanRead)
					{
						return null;
					}
					if (property.GetIndexParameters().Length != 0)
					{
						return null;
					}
					return property.GetValue(instance, null);
				}
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(instance);
				}
				return null;
			}

			public static bool SetMemberValue(object instance, string memberName, object value)
			{
				if (instance == null || string.IsNullOrWhiteSpace(memberName))
				{
					return false;
				}
				Type type = instance.GetType();
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite)
				{
					if (property.GetIndexParameters().Length != 0)
					{
						return false;
					}
					property.SetValue(instance, value, null);
					return true;
				}
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					if (field.IsInitOnly)
					{
						return false;
					}
					field.SetValue(instance, value);
					return true;
				}
				return false;
			}

			public static bool TryInvokeMethodWithOneNumericArg(object instance, string methodName, float amount)
			{
				if (instance == null || string.IsNullOrWhiteSpace(methodName))
				{
					return false;
				}
				MethodInfo[] methods = instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal))
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1)
					{
						Type parameterType = parameters[0].ParameterType;
						if (parameterType == typeof(float))
						{
							methodInfo.Invoke(instance, new object[1] { amount });
							return true;
						}
						if (parameterType == typeof(double))
						{
							methodInfo.Invoke(instance, new object[1] { (double)amount });
							return true;
						}
						if (parameterType == typeof(int))
						{
							methodInfo.Invoke(instance, new object[1] { (int)amount });
							return true;
						}
					}
				}
				return false;
			}

			public static object InvokeMethod(object instance, string methodName)
			{
				if (instance == null || string.IsNullOrWhiteSpace(methodName))
				{
					return null;
				}
				MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method == null)
				{
					return null;
				}
				return method.Invoke(instance, null);
			}

			public static object InvokeMethodWithArgs(object instance, string methodName, params object[] args)
			{
				if (instance == null || string.IsNullOrWhiteSpace(methodName))
				{
					return null;
				}
				MethodInfo[] array = (from m in instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					where string.Equals(m.Name, methodName, StringComparison.Ordinal)
					select m).ToArray();
				foreach (MethodInfo methodInfo in array)
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == ((args != null) ? args.Length : 0) && TryBuildArgumentsForMethod(parameters, args, out var convertedArgs))
					{
						return methodInfo.Invoke(instance, convertedArgs);
					}
				}
				return null;
			}

			private static bool TryBuildArgumentsForMethod(ParameterInfo[] parameters, object[] args, out object[] convertedArgs)
			{
				convertedArgs = null;
				if (args == null)
				{
					args = Array.Empty<object>();
				}
				object[] array = new object[parameters.Length];
				for (int i = 0; i < parameters.Length; i++)
				{
					object obj = args[i];
					Type parameterType = parameters[i].ParameterType;
					if (obj == null)
					{
						if (parameterType.IsValueType && Nullable.GetUnderlyingType(parameterType) == null)
						{
							return false;
						}
						array[i] = null;
						continue;
					}
					Type type = obj.GetType();
					if (parameterType.IsAssignableFrom(type))
					{
						array[i] = obj;
						continue;
					}
					if (parameterType == typeof(float))
					{
						if (obj is float num)
						{
							array[i] = num;
							continue;
						}
						if (obj is double num2)
						{
							array[i] = (float)num2;
							continue;
						}
						if (obj is int num3)
						{
							array[i] = num3;
							continue;
						}
						if (obj is long num4)
						{
							array[i] = num4;
							continue;
						}
					}
					if (parameterType == typeof(double))
					{
						if (obj is double num5)
						{
							array[i] = num5;
							continue;
						}
						if (obj is float num6)
						{
							array[i] = (double)num6;
							continue;
						}
						if (obj is int num7)
						{
							array[i] = num7;
							continue;
						}
						if (obj is long num8)
						{
							array[i] = (double)num8;
							continue;
						}
					}
					if (parameterType == typeof(int))
					{
						if (obj is int num9)
						{
							array[i] = num9;
							continue;
						}
						if (obj is float num10)
						{
							array[i] = (int)num10;
							continue;
						}
						if (obj is double num11)
						{
							array[i] = (int)num11;
							continue;
						}
						if (obj is long num12)
						{
							array[i] = (int)num12;
							continue;
						}
					}
					return false;
				}
				convertedArgs = array;
				return true;
			}
		}

		public const string PluginGuid = "com.leona.meleebuff";

		public const string PluginName = "Melee Buff";

		public const string PluginVersion = "2.2.4";

		private static ConfigEntry<bool> DumpReflectionEnabled;

		private static int _reflectionDumpRan;

		private Harmony _harmony;

		private static readonly ConditionalWeakTable<Character, RageBuildDamageFrameState> BuildDamageFrameApplied = new ConditionalWeakTable<Character, RageBuildDamageFrameState>();

		private static readonly ConditionalWeakTable<Character, RageIncomingHalfFrameState> IncomingHalfFrameApplied = new ConditionalWeakTable<Character, RageIncomingHalfFrameState>();

		internal static ManualLogSource ModLog { get; private set; }

		private void Awake()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			ModLog = ((BaseUnityPlugin)this).Logger;
			DumpReflectionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DumpReflectionEnabled", false, "Dumps live reflection member/method candidates once during runtime (for removing fallbacks/try blocks).");
			LogWeaponDamageBuildDamageTarget();
			_harmony = new Harmony("com.leona.meleebuff");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Melee Buff 2.2.4: Harmony PatchAll. Rage / Rage Amplified: +100% weapon damage, mana clamp, +1 HP/s, burnt (grey) health cleared on Rage start.");
		}

		private static void LogWeaponDamageBuildDamageTarget()
		{
			Type[] array = new Type[5]
			{
				typeof(Weapon),
				typeof(Character),
				typeof(bool),
				typeof(DamageList).MakeByRefType(),
				typeof(float).MakeByRefType()
			};
			MethodBase methodBase = AccessTools.Method(typeof(WeaponDamage), "BuildDamage", array, (Type[])null);
			if (methodBase == null)
			{
				ModLog.LogError((object)"[MeleeBuff] WeaponDamage.BuildDamage(Weapon, Character, bool, ref DamageList, ref float) NOT FOUND — damage postfix will not apply. Candidates:");
				MethodInfo[] methods = typeof(WeaponDamage).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo.Name != "BuildDamage"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						ModLog.LogInfo((object)string.Format("[MeleeBuff]   {0}  // {1}", methodInfo, string.Join(", ", parameters.Select((ParameterInfo p) => p.ParameterType.Name))));
					}
				}
			}
			else
			{
				ModLog.LogInfo((object)("[MeleeBuff] BuildDamage Harmony target OK: " + methodBase.DeclaringType?.FullName + "." + methodBase.Name));
			}
		}

		private static void MarkBuildDamageApplied(Character attacker)
		{
			if (!((Object)(object)attacker == (Object)null))
			{
				if (!BuildDamageFrameApplied.TryGetValue(attacker, out var value))
				{
					value = new RageBuildDamageFrameState();
					BuildDamageFrameApplied.Add(attacker, value);
				}
				value.LastFrame = Time.frameCount;
			}
		}

		private static bool WasBuildDamageAppliedThisFrame(Character attacker)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return false;
			}
			if (BuildDamageFrameApplied.TryGetValue(attacker, out var value))
			{
				return value.LastFrame == Time.frameCount;
			}
			return false;
		}

		private static void MarkIncomingHalfApplied(Character receiver)
		{
			if (!((Object)(object)receiver == (Object)null))
			{
				if (!IncomingHalfFrameApplied.TryGetValue(receiver, out var value))
				{
					value = new RageIncomingHalfFrameState();
					IncomingHalfFrameApplied.Add(receiver, value);
				}
				value.LastFrame = Time.frameCount;
			}
		}

		private static bool WasIncomingHalfAppliedThisFrame(Character receiver)
		{
			if ((Object)(object)receiver == (Object)null)
			{
				return false;
			}
			if (IncomingHalfFrameApplied.TryGetValue(receiver, out var value))
			{
				return value.LastFrame == Time.frameCount;
			}
			return false;
		}
	}
}