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;
}
}
}