Decompiled source of CustomAttributes v1.1.0

plugins/CustomAttributes.dll

Decompiled 7 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OutwardModTemplate")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OutwardModTemplate")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c5450fe0-edcf-483f-b9ea-4b1ef9d36da7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace CustomAttributes;

[DisallowMultipleComponent]
public class CustomAttributeComponent : MonoBehaviour
{
	public float safetyCritHit;

	public float safetyCritHeal;

	public float safetyAvoidance;

	public static Dictionary<string, float> ManualCritRate = new Dictionary<string, float>();

	public static Dictionary<string, float> ManualCritHeal = new Dictionary<string, float>();

	public static Dictionary<string, float> ManualCritScaling = new Dictionary<string, float>();

	public static Dictionary<string, float> ManualAvoidance = new Dictionary<string, float>();

	public static Dictionary<string, float> ManualLeech = new Dictionary<string, float>();

	public Character parentPlayer { get; private set; }

	public virtual void Awake()
	{
		parentPlayer = ((Component)this).GetComponent<Character>();
	}

	public void ManualCritRateStore(string modID, float value)
	{
		if (ManualCritRate.ContainsKey(modID))
		{
			ManualCritRate.Remove(modID);
		}
		ManualCritRate.Add(modID, value);
	}

	public float GetManualCritRate()
	{
		float num = 0f;
		foreach (KeyValuePair<string, float> item in ManualCritRate)
		{
			num += item.Value;
		}
		return num;
	}

	public float CalculateFinalCritRate(float additionalCrit = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return 0f + (GetManualCritRate() + CustomAttributeHelper.GetPassiveCritRate(parentPlayer) + CustomAttributeHelper.GetStatusCritRate(parentPlayer) + additionalCrit);
	}

	public bool ShouldCrit()
	{
		if (!CritAllowed())
		{
			return false;
		}
		float num = CalculateFinalCritRate();
		if (num >= 1f)
		{
			if ((float)Random.Range(1, 101) <= num + safetyCritHit)
			{
				safetyCritHit = 0f;
				return true;
			}
			float num2 = num * 0.1f;
			safetyCritHit += num2;
		}
		return false;
	}

	public bool CritAllowed()
	{
		foreach (KeyValuePair<string, float> statusCritCanceler in CustomAttributeHelper.StatusCritCancelers)
		{
			if (statusCritCanceler.Key != null && parentPlayer.StatusEffectMngr.HasStatusEffect(statusCritCanceler.Key))
			{
				return false;
			}
		}
		return true;
	}

	public void ManualCritHealStore(string modID, float value)
	{
		if (ManualCritHeal.ContainsKey(modID))
		{
			ManualCritHeal.Remove(modID);
		}
		ManualCritHeal.Add(modID, value);
	}

	public float GetManualCritHeal()
	{
		float num = 0f;
		foreach (KeyValuePair<string, float> item in ManualCritHeal)
		{
			num += item.Value;
		}
		return num;
	}

	public float CalculateFinalCritHealRate(float additionalCritHeal = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return 0f + (GetManualCritHeal() + CustomAttributeHelper.GetPassiveCritHeal(parentPlayer) + CustomAttributeHelper.GetStatusCritHeal(parentPlayer) + additionalCritHeal);
	}

	public bool ShouldCritHeal(bool smallHeal = false)
	{
		float num = CalculateFinalCritHealRate();
		if (smallHeal)
		{
			num *= 0.5f;
		}
		if (num >= 1f)
		{
			if ((float)Random.Range(1, 101) <= num + safetyCritHeal)
			{
				safetyCritHeal = 0f;
				return true;
			}
			float num2 = num * 0.1f;
			safetyCritHeal += num2;
		}
		return false;
	}

	public void ManualCritScalingStore(string modID, float value)
	{
		if (ManualCritScaling.ContainsKey(modID))
		{
			ManualCritScaling.Remove(modID);
		}
		ManualCritScaling.Add(modID, value);
	}

	public float GetManualCritScaling()
	{
		float num = 0f;
		foreach (KeyValuePair<string, float> item in ManualCritScaling)
		{
			num += item.Value;
		}
		return num;
	}

	public float CalculateFinalScalingCrit(float extraScalingCrit = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return (CustomAttributeManager.BaseCritScaling + (GetManualCritScaling() + CustomAttributeHelper.GetPassiveCritScaling(parentPlayer) + CustomAttributeHelper.GetStatusCritScaling(parentPlayer) + extraScalingCrit)) / 100f + 1f;
	}

	public float ProcessCriticalScaling(float amount)
	{
		return CustomAttributeHelper.Rounder(amount * CalculateFinalScalingCrit());
	}

	public void ManualAvoidanceStore(string modID, float value)
	{
		if (ManualAvoidance.ContainsKey(modID))
		{
			ManualAvoidance.Remove(modID);
		}
		ManualAvoidance.Add(modID, value);
	}

	public float GetManualAvoidance()
	{
		float num = 0f;
		foreach (KeyValuePair<string, float> item in ManualAvoidance)
		{
			num += item.Value;
		}
		return num;
	}

	public float CalculateFinalAvoidance(float additionalAvoidance = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return 0f + (GetManualAvoidance() + CustomAttributeHelper.GetPassiveAvoidance(parentPlayer) + CustomAttributeHelper.GetStatusAvoidance(parentPlayer) + additionalAvoidance);
	}

	public bool ShouldAvoid()
	{
		if (!AvoidAllowed())
		{
			return false;
		}
		float num = CalculateFinalAvoidance();
		if (num >= 1f)
		{
			if ((float)Random.Range(1, 101) <= num + safetyAvoidance)
			{
				safetyAvoidance = 0f;
				return true;
			}
			float num2 = num * 0.1f;
			safetyAvoidance += num2;
		}
		return false;
	}

	public bool AvoidAllowed()
	{
		foreach (KeyValuePair<string, float> statusAvoidCanceler in CustomAttributeHelper.StatusAvoidCancelers)
		{
			if (statusAvoidCanceler.Key != null && parentPlayer.StatusEffectMngr.HasStatusEffect(statusAvoidCanceler.Key))
			{
				return false;
			}
		}
		return true;
	}

	public void ManualLeechStore(string modID, float value)
	{
		if (ManualLeech.ContainsKey(modID))
		{
			ManualLeech.Remove(modID);
		}
		ManualLeech.Add(modID, value);
	}

	public float GetManualLeech()
	{
		float num = 0f;
		foreach (KeyValuePair<string, float> item in ManualLeech)
		{
			num += item.Value;
		}
		return num;
	}

	public float CalculateFinalLeech(float additionalLeech = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return 0f + (GetManualLeech() + CustomAttributeHelper.GetPassiveLeech(parentPlayer) + CustomAttributeHelper.GetStatusLeech(parentPlayer) + additionalLeech);
	}

	public void DoLeech(float totalDamage)
	{
		float num = CalculateFinalLeech();
		if (num <= 0f)
		{
			return;
		}
		float num2 = CustomAttributeHelper.Rounder(num / 100f * totalDamage);
		float num3 = parentPlayer.Stats.CurrentStamina / parentPlayer.Stats.ActiveMaxStamina;
		float num4 = parentPlayer.Stats.CurrentMana / parentPlayer.Stats.ActiveMaxMana;
		if (num3 < num4 || parentPlayer.Stats.MaxMana <= 0f)
		{
			if (CustomAttributesBase.LeechNotif.Value)
			{
				parentPlayer.CharacterUI.ShowInfoNotification("Leech " + num2 + " to Stam");
			}
			parentPlayer.Stats.AffectStamina(num2);
		}
		else if (parentPlayer.Mana > 0f && num4 < num3)
		{
			if (CustomAttributesBase.LeechNotif.Value)
			{
				parentPlayer.CharacterUI.ShowInfoNotification("Leech " + num2 + " to Mana");
			}
			parentPlayer.Stats.RestaureMana((Tag[])(object)new Tag[3], num2);
		}
	}

	public float CalculateFinalSelfCrit(float additionalSelfCrit = 0f)
	{
		if (!parentPlayer.IsLocalPlayer)
		{
			return 0f;
		}
		return 0f + (CustomAttributeHelper.GetPassiveSelfCrit(parentPlayer) + CustomAttributeHelper.GetStatusSelfCrit(parentPlayer) + additionalSelfCrit);
	}

	public bool ShouldSelfCritFromAI()
	{
		float num = CalculateFinalSelfCrit();
		if (num >= 1f && (float)Random.Range(1, 101) <= num)
		{
			return true;
		}
		return false;
	}

	public float ProcessSelfCritical(float amount)
	{
		float num = CustomAttributeManager.AIcritScaling / 100f + 1f;
		return CustomAttributeHelper.Rounder(amount * num);
	}
}
public static class CustomAttributeHelper
{
	public static Dictionary<int, float> PassivesCritRate = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusCritRate = new Dictionary<string, float>();

	public static Dictionary<string, float> StatusCritCancelers = new Dictionary<string, float>();

	public static Dictionary<int, float> PassivesCritHeal = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusCritHeal = new Dictionary<string, float>();

	public static Dictionary<int, float> PassivesCritScaling = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusCritScaling = new Dictionary<string, float>();

	public static Dictionary<int, float> PassivesAvoidance = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusAvoidance = new Dictionary<string, float>();

	public static Dictionary<string, float> StatusAvoidCancelers = new Dictionary<string, float>();

	public static Dictionary<int, float> PassivesLeech = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusLeech = new Dictionary<string, float>();

	public static Dictionary<int, float> PassivesSelfCrit = new Dictionary<int, float>();

	public static Dictionary<string, float> StatusSelfCrit = new Dictionary<string, float>();

	public static float Rounder(float amount)
	{
		return (float)Math.Round(amount, 2);
	}

	public static void PassiveCritRateStore(int skillID, float value)
	{
		PassivesCritRate.Add(skillID, value);
	}

	public static float GetPassiveCritRate(Character player)
	{
		if (PassivesCritRate.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesCritRate)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusCritRateStore(string skillID, float value)
	{
		StatusCritRate.Add(skillID, value);
	}

	public static float GetStatusCritRate(Character player)
	{
		if (StatusCritRate.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusCritRate)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}

	public static void StatusCritCancelerStore(string skillID, float value = 0f)
	{
		StatusCritCancelers.Add(skillID, value);
	}

	public static void PassiveCritHealStore(int skillID, float value)
	{
		PassivesCritHeal.Add(skillID, value);
	}

	public static float GetPassiveCritHeal(Character player)
	{
		if (PassivesCritHeal.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesCritHeal)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusCritHealStore(string skillID, float value)
	{
		StatusCritHeal.Add(skillID, value);
	}

	public static float GetStatusCritHeal(Character player)
	{
		if (StatusCritHeal.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusCritHeal)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}

	public static void PassiveCritScalingStore(int skillID, float value)
	{
		PassivesCritScaling.Add(skillID, value);
	}

	public static float GetPassiveCritScaling(Character player)
	{
		if (PassivesCritScaling.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesCritScaling)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusCritScalingStore(string skillID, float value)
	{
		StatusCritScaling.Add(skillID, value);
	}

	public static float GetStatusCritScaling(Character player)
	{
		if (StatusCritScaling.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusCritScaling)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}

	public static void PassiveAvoidanceStore(int skillID, float value)
	{
		PassivesAvoidance.Add(skillID, value);
	}

	public static float GetPassiveAvoidance(Character player)
	{
		if (PassivesAvoidance.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesAvoidance)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusAvoidanceStore(string skillID, float value)
	{
		StatusAvoidance.Add(skillID, value);
	}

	public static float GetStatusAvoidance(Character player)
	{
		if (StatusAvoidance.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusAvoidance)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}

	public static void StatusAvoidCancelerStore(string skillID, float value = 0f)
	{
		StatusAvoidCancelers.Add(skillID, value);
	}

	public static void PassiveLeechStore(int skillID, float value)
	{
		PassivesLeech.Add(skillID, value);
	}

	public static float GetPassiveLeech(Character player)
	{
		if (PassivesLeech.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesLeech)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusLeechStore(string skillID, float value)
	{
		StatusLeech.Add(skillID, value);
	}

	public static float GetStatusLeech(Character player)
	{
		if (StatusLeech.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusLeech)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}

	public static void PassiveSelfCritStore(int skillID, float value)
	{
		PassivesSelfCrit.Add(skillID, value);
	}

	public static float GetPassiveSelfCrit(Character player)
	{
		if (PassivesSelfCrit.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<int, float> item in PassivesSelfCrit)
		{
			if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(item.Key))
			{
				num += item.Value;
			}
		}
		return num;
	}

	public static void StatusSelfCritStore(string skillID, float value)
	{
		StatusSelfCrit.Add(skillID, value);
	}

	public static float GetStatusSelfCrit(Character player)
	{
		if (StatusSelfCrit.Count <= 0)
		{
			return 0f;
		}
		float num = 0f;
		foreach (KeyValuePair<string, float> item in StatusSelfCrit)
		{
			if (item.Key != null && player.StatusEffectMngr.HasStatusEffect(item.Key))
			{
				num += (float)player.StatusEffectMngr.GetStatusLevel(item.Key) * item.Value;
			}
		}
		return num;
	}
}
public static class CustomAttributeManager
{
	[HarmonyPatch(typeof(Character), "ReceiveHit", new Type[]
	{
		typeof(Object),
		typeof(DamageList),
		typeof(Vector3),
		typeof(Vector3),
		typeof(float),
		typeof(float),
		typeof(Character),
		typeof(float),
		typeof(bool)
	})]
	public class Character_ReceiveHit
	{
		[HarmonyPrefix]
		public static void Prefix(Character __instance, Object _damageSource, ref DamageList _damage, Vector3 _hitDir, Vector3 _hitPoint, float _angle, float _angleDir, Character _dealerChar, ref float _knockBack, bool _hitInventory)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_dealerChar) || !Object.op_Implicit((Object)(object)__instance))
			{
				return;
			}
			if (_knockBack > 0f && _dealerChar.IsLocalPlayer && __instance.IsAI)
			{
				StatusEffect val = (StatusEffect)(object)((_damageSource is StatusEffect) ? _damageSource : null);
				CustomAttributeComponent component = ((Component)_dealerChar).GetComponent<CustomAttributeComponent>();
				if ((Object)(object)component == (Object)null)
				{
					Debug.LogError((object)"Hit AttComponent Null!");
				}
				else
				{
					if ((Object)(object)val != (Object)null)
					{
						return;
					}
					if (component.ShouldCrit())
					{
						foreach (DamageType item in _damage.List)
						{
							int num = _damage.IndexOf(item.Type);
							if (_damage[num] != null && _damage[num].Damage > 0f)
							{
								float damage = component.ProcessCriticalScaling(_damage[num].Damage);
								_damage[num].Damage = damage;
							}
						}
						if (CustomAttributesBase.CritNotifs.Value)
						{
							_dealerChar.CharacterUI.ShowInfoNotification("CRIT! " + (int)_damage.TotalDamage);
						}
						if (CustomAttributesBase.CritPopup.Value)
						{
							TextDisplayManager.RegisterAttEvent(__instance.CenterPosition + new Vector3(0f, 0.4f, 0f), "CRIT!", Color.red);
						}
						CustomAttributesBase.Instance.OnCharacterCritEvent?.Invoke(_dealerChar, __instance, _damage.TotalDamage);
					}
					component.DoLeech(_damage.TotalDamage);
				}
			}
			else
			{
				if (!(_knockBack > 0f) || !__instance.IsLocalPlayer || !_dealerChar.IsAI)
				{
					return;
				}
				CustomAttributeComponent component2 = ((Component)__instance).GetComponent<CustomAttributeComponent>();
				if ((Object)(object)component2 == (Object)null)
				{
					Debug.LogError((object)"Hit AttComponent Null!");
				}
				else if (component2.ShouldAvoid())
				{
					if (CustomAttributesBase.AvoidNotifs.Value)
					{
						__instance.CharacterUI.ShowInfoNotification("Miss! " + _damage.TotalDamage);
					}
					CustomAttributesBase.Instance.OnCharacterAvoidEvent?.Invoke(__instance, _dealerChar, _damage.TotalDamage);
					_damage.Clear();
					_knockBack = 0f;
					if (CustomAttributesBase.AvoidPopup.Value)
					{
						TextDisplayManager.RegisterAttEvent(__instance.CenterPosition + new Vector3(0f, 0.4f, 0f), "Miss!", Color.gray);
					}
				}
				else
				{
					if (!component2.ShouldSelfCritFromAI())
					{
						return;
					}
					foreach (DamageType item2 in _damage.List)
					{
						int num2 = _damage.IndexOf(item2.Type);
						if (_damage[num2] != null && _damage[num2].Damage > 0f)
						{
							float damage2 = component2.ProcessSelfCritical(_damage[num2].Damage);
							_damage[num2].Damage = damage2;
						}
					}
					if (CustomAttributesBase.CritNotifs.Value)
					{
						__instance.CharacterUI.ShowInfoNotification("Crushd!: " + (int)_damage.TotalDamage);
					}
					if (CustomAttributesBase.CritPopup.Value)
					{
						TextDisplayManager.RegisterAttEvent(__instance.CenterPosition + new Vector3(0f, 0.4f, 0f), "Crush!", Color.yellow);
					}
					CustomAttributesBase.Instance.OnCharacterSelfCritFromAI?.Invoke(__instance, _dealerChar, _damage.TotalDamage);
				}
			}
		}
	}

	[HarmonyPatch(typeof(CharacterStats), "AffectHealth")]
	public class CharacterStats_AffectHealth
	{
		[HarmonyPrefix]
		public static void Prefix(CharacterStats __instance, ref float _quantity)
		{
			if (_quantity <= 0f || !Object.op_Implicit((Object)(object)__instance) || !__instance.m_character.IsLocalPlayer)
			{
				return;
			}
			CustomAttributeComponent component = ((Component)__instance.m_character).GetComponent<CustomAttributeComponent>();
			if ((Object)(object)component == (Object)null)
			{
				Debug.LogError((object)"AffectHealth AttComponent Null!");
			}
			else if ((_quantity < 5f) ? component.ShouldCritHeal(smallHeal: true) : component.ShouldCritHeal())
			{
				float num = component.ProcessCriticalScaling(_quantity);
				CustomAttributesBase.Instance.OnCharacterCritHealEvent?.Invoke(__instance.m_character, _quantity);
				_quantity = num;
				if (CustomAttributesBase.CritHealNotifs.Value)
				{
					__instance.m_character.CharacterUI.ShowInfoNotification("CritHeal! " + num);
				}
			}
		}
	}

	public static float BaseCritScaling = 50f;

	public static float AIcritScaling = 50f;
}
[BepInPlugin("iggy.customattributes", "Custom Attributes", "1.0.0")]
public class CustomAttributesBase : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Character), "Awake")]
	public class CharacterAwakePatch
	{
		private static void Postfix(Character __instance)
		{
			if ((Object)(object)__instance != (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<CustomAttributeComponent>();
			}
		}
	}

	public const string GUID = "iggy.customattributes";

	public const string NAME = "Custom Attributes";

	public const string VERSION = "1.0.0";

	internal static ManualLogSource Log;

	public static ConfigEntry<bool> ExampleConfig;

	public static CustomAttributesBase Instance;

	public Action<Character, Character, float> OnCharacterCritEvent;

	public Action<Character, float> OnCharacterCritHealEvent;

	public Action<Character, Character, float> OnCharacterAvoidEvent;

	public Action<Character, Character, float> OnCharacterSelfCritFromAI;

	public static ConfigEntry<bool> CritNotifs;

	public static ConfigEntry<bool> CritHealNotifs;

	public static ConfigEntry<bool> AvoidNotifs;

	public static ConfigEntry<bool> LeechNotif;

	public static ConfigEntry<bool> CritPopup;

	public static ConfigEntry<bool> AvoidPopup;

	internal void Awake()
	{
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogMessage((object)"Custom Attributes 1.0.0 Loaded");
		Instance = this;
		CritNotifs = ((BaseUnityPlugin)this).Config.Bind<bool>("Trigger Notifications", "Critical Hit Notifications", false, "Disable to: Hide notifications about dealing or receiving a critical hit.");
		CritHealNotifs = ((BaseUnityPlugin)this).Config.Bind<bool>("Trigger Notifications", "Critical Heals Notifications", true, "Disable to: Hide notifications about your critical heals.");
		AvoidNotifs = ((BaseUnityPlugin)this).Config.Bind<bool>("Trigger Notifications", "Avoidance Notifications", false, "Disable to: Hide notifications about avoiding a hit.");
		LeechNotif = ((BaseUnityPlugin)this).Config.Bind<bool>("Trigger Notifications", "Leech Notifications", false, "Disable to: Hide notifications about the amount and resource leeched.");
		CritPopup = ((BaseUnityPlugin)this).Config.Bind<bool>("Flying Text", "Critical Hit Text", true, "Disable to: Hide flying text about dealing or receiving a critical hit.");
		AvoidPopup = ((BaseUnityPlugin)this).Config.Bind<bool>("Flying Text", "Avoidance text", true, "Disable to: Hide flying text about avoiding/missing a hit.");
		new Harmony("iggy.customattributes").PatchAll();
		TextDisplayManager.Init();
	}

	internal void Update()
	{
		TextDisplayManager.Update();
	}
}
public class TextInfo
{
	public Vector3 hitPos;

	public float hitTime;

	public Text[] refTexts = (Text[])(object)new Text[2];

	public TextInfo(Vector3 pos)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		hitPos = pos;
		hitTime = Time.time;
	}
}
public class TextDisplayManager
{
	private static Canvas Canvas;

	private static readonly List<TextInfo> AttEvent = new List<TextInfo>();

	private static readonly List<Text>[] TextPools = new List<Text>[2]
	{
		new List<Text>(),
		new List<Text>()
	};

	private static Font DefaultFont;

	public static void RegisterAttEvent(Vector3 position, string attEventText, Color attEventColor)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		TextInfo textInfo = new TextInfo(position);
		AttEvent.Add(textInfo);
		textInfo.refTexts = BorrowOrCreateTexts(attEventText, attEventColor);
	}

	internal static void Init()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("CustomAttCanvas");
		Object.DontDestroyOnLoad((Object)val);
		Canvas = val.AddComponent<Canvas>();
		Canvas.renderMode = (RenderMode)0;
		Canvas.sortingOrder = 999;
		Canvas.referencePixelsPerUnit = 100f;
		CanvasScaler obj = val.AddComponent<CanvasScaler>();
		obj.referenceResolution = new Vector2(1920f, 1080f);
		obj.screenMatchMode = (ScreenMatchMode)1;
		DefaultFont = Resources.FindObjectsOfTypeAll<Font>().First((Font it) => ((Object)it).name.Contains("Philosopher"));
	}

	internal static void Update()
	{
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		for (int num = AttEvent.Count - 1; num >= 0; num--)
		{
			TextInfo textInfo = AttEvent[num];
			if (Time.time - textInfo.hitTime > 1.5f)
			{
				ReturnTexts(textInfo);
				AttEvent.RemoveAt(num);
			}
			else
			{
				for (int i = 0; i < SplitScreenManager.Instance.LocalPlayerCount; i++)
				{
					Text val = textInfo.refTexts[i];
					SplitPlayer val2 = SplitScreenManager.Instance.LocalPlayers[i];
					int num2;
					if (!MenuManager.Instance.IsMapDisplayed)
					{
						MenuPanel currentMenu = val2.AssignedCharacter.CharacterUI.GetCurrentMenu();
						num2 = ((currentMenu != null && ((UIElement)currentMenu).IsDisplayed) ? 1 : 0);
					}
					else
					{
						num2 = 1;
					}
					Camera cameraScript = val2.CameraScript;
					if (num2 != 0 || Vector3.Distance(((Component)val2.AssignedCharacter).transform.position, textInfo.hitPos) > 50f)
					{
						((Component)val).gameObject.SetActive(false);
						continue;
					}
					Vector3 val3 = textInfo.hitPos + Vector3.up * (Time.time - textInfo.hitTime) * 0.5f;
					Vector3 screenPos = cameraScript.WorldToViewportPoint(val3);
					if (IsScreenPosVisible(ref screenPos, i))
					{
						((Component)val).transform.position = new Vector3(screenPos.x, screenPos.y, 0f);
						((Component)val).gameObject.SetActive(true);
					}
				}
			}
		}
	}

	internal static Text[] BorrowOrCreateTexts(string attEventText, Color attColor)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		Text[] array = (Text[])(object)new Text[2];
		for (int i = 0; i < 2; i++)
		{
			List<Text> list = TextPools[i];
			if (list.Any())
			{
				Text val = list.First();
				list.RemoveAt(0);
				((Component)val).gameObject.SetActive(true);
				array[i] = val;
				val.text = attEventText;
				((Graphic)val).color = attColor;
				continue;
			}
			GameObject val2 = new GameObject("Text");
			val2.transform.parent = ((Component)Canvas).transform;
			RectTransform obj = val2.AddComponent<RectTransform>();
			obj.pivot = new Vector2(0.5f, 0.5f);
			obj.anchorMin = obj.pivot;
			obj.anchorMax = obj.pivot;
			obj.sizeDelta = new Vector2(200f, 50f);
			Text val3 = val2.AddComponent<Text>();
			val3.text = attEventText;
			val3.font = DefaultFont;
			val3.fontSize = 20;
			val3.alignment = (TextAnchor)4;
			array[i] = val3;
			((Graphic)val3).color = attColor;
		}
		return array;
	}

	internal static void ReturnTexts(TextInfo attEvent)
	{
		for (int i = 0; i < 2; i++)
		{
			Text val = attEvent.refTexts[i];
			((Component)val).gameObject.SetActive(false);
			TextPools[i].Add(val);
		}
	}

	private static bool IsScreenPosVisible(ref Vector3 screenPos, int splitID)
	{
		float num = 0f;
		float num2 = 1080f;
		screenPos.x *= Relative(1920f);
		if (SplitScreenManager.Instance.LocalPlayerCount == 1)
		{
			screenPos.y *= Relative(1080f, height: true);
			if (screenPos.z > 0f && screenPos.x >= 0f && screenPos.x <= 1920f && screenPos.y >= num)
			{
				return screenPos.y <= num2;
			}
			return false;
		}
		screenPos.y *= Relative(540f, height: true);
		if (splitID == 0)
		{
			num = Relative(540f, height: true);
			screenPos.y += num;
			if (screenPos.z > 0f && screenPos.x >= 0f && screenPos.x <= Relative(1920f) && screenPos.y >= num)
			{
				return screenPos.y <= num2;
			}
			return false;
		}
		num2 = Relative(540f, height: true);
		if (screenPos.z > 0f && screenPos.x >= 0f && screenPos.x <= Relative(1920f) && screenPos.y >= num)
		{
			return screenPos.y <= num2;
		}
		return false;
	}

	public static float Relative(float offset, bool height = false)
	{
		return offset * (float)(height ? Screen.height : Screen.width) * 100f / (height ? 1080f : 1920f) * 0.01f;
	}
}