Decompiled source of CorpseBloomRebornHC v1.0.0

CorpseBloomRebornHC.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using On.RoR2;
using On.RoR2.UI;
using RoR2;
using RoR2.ContentManagement;
using RoR2.UI;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Serestielle")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CorpseBloomRebornHC")]
[assembly: AssemblyTitle("CorpseBloomRebornHC")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Serestielle.CorpseBloomRebornHC
{
	[BepInPlugin("com.Serestielle.CorpseBloomRebornHC", "CorpseBloomRebornHC", "1.0.0")]
	[BepInIncompatibility("com.TPDespair.CorpseBloomReborn")]
	public class CorpseBloomRebornHCPlugin : BaseUnityPlugin
	{
		private class Content : IContentPackProvider
		{
			private readonly ContentPack contentPack = new ContentPack();

			public string identifier => "CorpseBloomRebornHCContent";

			public IEnumerator LoadStaticContentAsync(LoadStaticContentAsyncArgs args)
			{
				ReserveBuff = ScriptableObject.CreateInstance<BuffDef>();
				((Object)ReserveBuff).name = "CBReserve";
				ReserveBuff.buffColor = new Color(0.65f, 0.35f, 1f);
				ReserveBuff.canStack = true;
				ReserveBuff.isHidden = Configuration.HideReserveBuff.Value;
				ReserveBuff.iconSprite = LegacyResourcesAPI.Load<BuffDef>("BuffDefs/MedkitHeal")?.iconSprite;
				contentPack.buffDefs.Add((BuffDef[])(object)new BuffDef[1] { ReserveBuff });
				args.ReportProgress(1f);
				yield break;
			}

			public IEnumerator GenerateContentPackAsync(GetContentPackAsyncArgs args)
			{
				ContentPack.Copy(contentPack, args.output);
				args.ReportProgress(1f);
				yield break;
			}

			public IEnumerator FinalizeAsync(FinalizeAsyncArgs args)
			{
				args.ReportProgress(1f);
				yield break;
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action<CharacterBody> <0>__OnInventoryChanged;

			public static hook_Heal <1>__InterceptHeal;

			public static Action <2>__SetLanguageStrings;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static CollectContentPackProvidersDelegate <>9__5_0;

			public static hook_Awake <>9__5_1;

			internal void <Awake>b__5_0(AddContentPackProviderDelegate add)
			{
				add.Invoke((IContentPackProvider)(object)new Content());
			}

			internal void <Awake>b__5_1(orig_Awake orig, HealthBar self)
			{
				orig.Invoke(self);
				if (Object.op_Implicit((Object)(object)self.barContainer))
				{
					((Component)self).gameObject.AddComponent<ReserveBar>().healthBar = self;
				}
			}
		}

		public const string ModVer = "1.0.0";

		public const string ModName = "CorpseBloomRebornHC";

		public const string ModGuid = "com.Serestielle.CorpseBloomRebornHC";

		internal static BuffDef ReserveBuff;

		private static Hook healHook;

		public void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			RoR2Application.isModded = true;
			NetworkModCompatibilityHelper.networkModList = NetworkModCompatibilityHelper.networkModList.Append("com.Serestielle.CorpseBloomRebornHC:1.0.0");
			Configuration.Init(((BaseUnityPlugin)this).Config);
			object obj = <>c.<>9__5_0;
			if (obj == null)
			{
				CollectContentPackProvidersDelegate val = delegate(AddContentPackProviderDelegate add)
				{
					add.Invoke((IContentPackProvider)(object)new Content());
				};
				<>c.<>9__5_0 = val;
				obj = (object)val;
			}
			ContentManager.collectContentPackProviders += (CollectContentPackProvidersDelegate)obj;
			CharacterBody.onBodyInventoryChangedGlobal += ReserveBehavior.OnInventoryChanged;
			MethodInfo? method = typeof(HealthComponent).GetMethod("Heal");
			object obj2 = <>O.<1>__InterceptHeal;
			if (obj2 == null)
			{
				hook_Heal val2 = InterceptHeal;
				<>O.<1>__InterceptHeal = val2;
				obj2 = (object)val2;
			}
			healHook = new Hook((MethodBase)method, (Delegate)obj2, new HookConfig
			{
				Priority = 10000
			});
			object obj3 = <>c.<>9__5_1;
			if (obj3 == null)
			{
				hook_Awake val3 = delegate(orig_Awake orig, HealthBar self)
				{
					orig.Invoke(self);
					if (Object.op_Implicit((Object)(object)self.barContainer))
					{
						((Component)self).gameObject.AddComponent<ReserveBar>().healthBar = self;
					}
				};
				<>c.<>9__5_1 = val3;
				obj3 = (object)val3;
			}
			HealthBar.Awake += (hook_Awake)obj3;
			Language.onCurrentLanguageChanged += SetLanguageStrings;
			if (Language.currentLanguage != null)
			{
				SetLanguageStrings();
			}
		}

		private static float InterceptHeal(orig_Heal orig, HealthComponent self, float amount, ProcChainMask procChainMask, bool nonRegen)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			ReserveBehavior reserveBehavior = default(ReserveBehavior);
			if (nonRegen && NetworkServer.active && !((ProcChainMask)(ref procChainMask)).HasProc((ProcType)13) && !((ProcChainMask)(ref procChainMask)).HasProc((ProcType)31) && ((Component)self).TryGetComponent<ReserveBehavior>(ref reserveBehavior))
			{
				if (self.alive && amount > 0f && !self.body.HasBuff(Buffs.HealingDisabled))
				{
					reserveBehavior.AddReserve(amount);
				}
				return 0f;
			}
			return orig.Invoke(self, amount, procChainMask, nonRegen);
		}

		private static void SetLanguageStrings()
		{
			Language.currentLanguage.SetStringByToken("ITEM_REPEATHEAL_PICKUP", GetPickup());
			Language.currentLanguage.SetStringByToken("ITEM_REPEATHEAL_DESC", GetDesc());
		}

		private static string GetPickup()
		{
			if (Configuration.HealBeforeReserve.Value)
			{
				return "Gain extra healing as reserve.";
			}
			if (Configuration.HealWhenReserveFull.Value)
			{
				return "Store healing to heal over time.";
			}
			return "All healing is applied over time.";
		}

		private static string Stack(float value)
		{
			if (value == 0f)
			{
				return "";
			}
			return string.Format(" <style=cStack>({0}{1:0.##}% per stack)</style>", (value > 0f) ? "+" : "", value * 100f);
		}

		private static string GetDesc()
		{
			string pickup = GetPickup();
			pickup += $"\nGain <style=cIsHealing>{Configuration.BaseHealthReserve.Value * 100f:0.##}%</style>{Stack(Configuration.AddedHealthReserve.Value)}";
			pickup += " of your <style=cIsHealing>maximum health</style> as <style=cIsHealing>maximum reserve</style>.";
			pickup += $"\nStore <style=cIsHealing>{Configuration.BaseAbsorbMult.Value * 100f:0.##}%</style>{Stack(Configuration.AddedAbsorbMult.Value)}";
			pickup += " of healing as <style=cIsHealing>reserve</style>.";
			pickup += $"\nCan <style=cIsHealing>heal</style> for <style=cIsHealing>{Configuration.BaseMaxUsageRate.Value * 100f:0.##}%</style>{Stack(Configuration.StackMaxUsageRate.Value)}";
			pickup += " of your <style=cIsHealing>maximum health</style> every second from <style=cIsHealing>reserve</style>.";
			if (Configuration.VanillaUsageBehavior.Value)
			{
				pickup += "\n<style=cStack>(Additional stacks further reduce healing rate)</style>";
			}
			float value = Configuration.BaseExportMult.Value;
			float value2 = Configuration.AddedExportMult.Value;
			if (value != 1f || value2 != 0f)
			{
				string text = ((value2 != 0f) ? ((!(value2 > 0f)) ? ((value > 1f) ? "cIsDamage" : "cDeath") : ((value >= 1f) ? "cIsHealing" : "cIsDamage")) : ((value > 1f) ? "cIsHealing" : "cDeath"));
				pickup += string.Format("\n\n<style={0}>{1}{2:0.##}%</style>{3} Healing Multiplier.", text, (value >= 1f) ? "+" : "", (value - 1f) * 100f, Stack(value2));
			}
			return pickup;
		}
	}
	public static class Configuration
	{
		public static ConfigEntry<bool> HideReserveBuff;

		public static ConfigEntry<bool> HealBeforeReserve;

		public static ConfigEntry<bool> HealWhenReserveFull;

		public static ConfigEntry<float> BaseAbsorbMult;

		public static ConfigEntry<float> AddedAbsorbMult;

		public static ConfigEntry<float> BaseExportMult;

		public static ConfigEntry<float> AddedExportMult;

		public static ConfigEntry<int> AegisInteraction;

		public static ConfigEntry<bool> RestoreRejuvBehavior;

		public static ConfigEntry<float> BaseHealthReserve;

		public static ConfigEntry<float> AddedHealthReserve;

		public static ConfigEntry<bool> VanillaUsageBehavior;

		public static ConfigEntry<float> BaseMaxUsageRate;

		public static ConfigEntry<float> StackMaxUsageRate;

		public static ConfigEntry<float> BaseMinUsageRate;

		public static ConfigEntry<float> StackMinUsageRate;

		internal static void Init(ConfigFile Config)
		{
			HideReserveBuff = Config.Bind<bool>("General", "HideReserveBuff", true, "Set CBReserve buff as hidden, preventing it from being drawn on your active buffs bar.");
			HealBeforeReserve = Config.Bind<bool>("General", "HealBeforeReserve", false, "If incoming healing should apply to health before going into reserve.");
			HealWhenReserveFull = Config.Bind<bool>("General", "HealWhenReserveFull", false, "If incoming healing should apply to health after reserve is full.");
			BaseAbsorbMult = Config.Bind<float>("General", "BaseAbsorbMult", 2f, "Reserve absorption. Base effectiveness of healing going into reserve.");
			AddedAbsorbMult = Config.Bind<float>("General", "AddedAbsorbMult", 1f, "Increased absorption effect per stack.");
			BaseExportMult = Config.Bind<float>("General", "BaseExportMult", 1f, "Base effectiveness of healing coming out of reserve.");
			AddedExportMult = Config.Bind<float>("General", "AddedExportMult", 0f, "Increased healing effect per stack.");
			AegisInteraction = Config.Bind<int>("General", "AegisInteraction", 1, "How does Aegis affect usage rate. 0 = No Effect, 1 = MaxUsage at MinBarrier / MinUsage at MaxBarrier, 2 = Always MaxUsage. Chooses highest between this and amount going to health (amount going to health is highest between MinUsage and HealMissingHealth capped at MaxUsage).");
			RestoreRejuvBehavior = Config.Bind<bool>("General", "RestoreRejuvBehavior", false, "Most healing multipliers used to apply twice when used with Corpsebloom. Set to true to restore this behavior for Rejuvenation Rack.");
			BaseHealthReserve = Config.Bind<float>("General", "BaseHealthReserve", 1f, "Base reserve gained from health.");
			AddedHealthReserve = Config.Bind<float>("General", "AddedHealthReserve", 0.5f, "Added reserve gained from health per stack.");
			VanillaUsageBehavior = Config.Bind<bool>("General", "VanillaUsageBehavior", false, "Scale reserve outputs by 1 / stack count.");
			BaseMaxUsageRate = Config.Bind<float>("General", "BaseMaxUsageRate", 0.1f, "Base maximum healing output from reserve per second.");
			StackMaxUsageRate = Config.Bind<float>("General", "StackMaxUsageRate", 0f, "Stack maximum healing output from reserve per second.");
			BaseMinUsageRate = Config.Bind<float>("General", "BaseMinUsageRate", 0.05f, "Base minimum healing output from reserve per second. Set to 0 to disable reserve decay.");
			StackMinUsageRate = Config.Bind<float>("General", "StackMinUsageRate", 0f, "Stack minimum healing output from reserve per second.");
		}
	}
	public class ReserveBar : MonoBehaviour
	{
		private const float Height = 0.32f;

		public HealthBar healthBar;

		private RectTransform bar;

		private void LateUpdate()
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			HealthComponent source = healthBar.source;
			if (Object.op_Implicit((Object)(object)source) && Object.op_Implicit((Object)(object)source.body))
			{
				int buffCount = source.body.GetBuffCount(CorpseBloomRebornHCPlugin.ReserveBuff);
				if (buffCount > 0)
				{
					num = (float)buffCount / 100f * source.fullHealth / source.fullCombinedHealth / source.body.cursePenalty;
				}
			}
			if (num <= 0f)
			{
				if (Object.op_Implicit((Object)(object)bar) && ((Component)bar).gameObject.activeSelf)
				{
					((Component)bar).gameObject.SetActive(false);
				}
				return;
			}
			if (!Object.op_Implicit((Object)(object)bar))
			{
				bar = CreateBar();
			}
			if (!((Component)bar).gameObject.activeSelf)
			{
				((Component)bar).gameObject.SetActive(true);
			}
			bar.anchorMax = new Vector2(Mathf.Min(num, 1f), 0.32f);
			if (((Transform)bar).GetSiblingIndex() != ((Transform)bar).parent.childCount - 1)
			{
				((Transform)bar).SetAsLastSibling();
			}
		}

		private RectTransform CreateBar()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject("CorpseBloomReserve", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			}).transform;
			((Transform)val).SetParent((Transform)(object)healthBar.barContainer, false);
			val.anchorMin = Vector2.zero;
			val.anchorMax = new Vector2(0f, 0.32f);
			Vector2 offsetMin = (val.offsetMax = Vector2.zero);
			val.offsetMin = offsetMin;
			Image component = ((Component)val).GetComponent<Image>();
			((Graphic)component).color = new Color(0.625f, 0.25f, 1f, 0.65f);
			((Graphic)component).raycastTarget = false;
			return val;
		}
	}
	public class ReserveBehavior : MonoBehaviour
	{
		private const float Interval = 0.25f;

		public float reserve;

		private CharacterBody body;

		private HealthComponent healthComponent;

		private float timer = 0.25f;

		private float healthMult;

		private float maxRate;

		private float minRate;

		private float absorbMult;

		private float exportMult;

		private int aegisState;

		private float Maximum => Mathf.Max(1f, healthComponent.fullHealth * healthMult);

		internal static void OnInventoryChanged(CharacterBody body)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active)
			{
				return;
			}
			int num = (Object.op_Implicit((Object)(object)body.inventory) ? body.inventory.GetItemCountEffective(Items.RepeatHeal) : 0);
			ReserveBehavior reserveBehavior = ((Component)body).GetComponent<ReserveBehavior>();
			if (num > 0)
			{
				if (!Object.op_Implicit((Object)(object)reserveBehavior))
				{
					reserveBehavior = ((Component)body).gameObject.AddComponent<ReserveBehavior>();
				}
				reserveBehavior.Recalculate(body, num);
			}
			else if (Object.op_Implicit((Object)(object)reserveBehavior))
			{
				Object.Destroy((Object)(object)reserveBehavior);
				body.SetBuffCount(CorpseBloomRebornHCPlugin.ReserveBuff.buffIndex, 0);
			}
		}

		private void Recalculate(CharacterBody body, int count)
		{
			this.body = body;
			healthComponent = body.healthComponent;
			Inventory inventory = body.inventory;
			int num = count - 1;
			healthMult = Mathf.Max(0.1f, Configuration.BaseHealthReserve.Value + Configuration.AddedHealthReserve.Value * (float)num);
			minRate = Mathf.Max(0f, Configuration.BaseMinUsageRate.Value + Configuration.StackMinUsageRate.Value * (float)num);
			maxRate = Mathf.Max(new float[3]
			{
				0.01f,
				minRate,
				Configuration.BaseMaxUsageRate.Value + Configuration.StackMaxUsageRate.Value * (float)num
			});
			if (Configuration.VanillaUsageBehavior.Value)
			{
				minRate /= count;
				maxRate = Mathf.Max(0.01f, maxRate / (float)count);
			}
			absorbMult = Mathf.Max(0.1f, Configuration.BaseAbsorbMult.Value + Configuration.AddedAbsorbMult.Value * (float)num);
			exportMult = Mathf.Max(0.1f, Configuration.BaseExportMult.Value + Configuration.AddedExportMult.Value * (float)num);
			if (Configuration.RestoreRejuvBehavior.Value)
			{
				absorbMult *= 1 + inventory.GetItemCountEffective(Items.IncreaseHealing);
			}
			aegisState = ((inventory.GetItemCountEffective(Items.BarrierOnOverHeal) > 0) ? Configuration.AegisInteraction.Value : 0);
		}

		public void AddReserve(float amount)
		{
			float num = 0f;
			if (Configuration.HealBeforeReserve.Value)
			{
				num = Mathf.Min(amount, (healthComponent.fullHealth - healthComponent.health) / exportMult);
				amount -= num;
			}
			float maximum = Maximum;
			float num2 = Mathf.Min(amount, Mathf.Max(0f, maximum - reserve) / absorbMult);
			amount -= num2;
			if (Configuration.HealWhenReserveFull.Value)
			{
				num += amount;
			}
			reserve = Mathf.Min(reserve + num2 * absorbMult, maximum);
			if (num > 0f)
			{
				HealFromReserve(num);
			}
		}

		private void HealFromReserve(float amount)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			ProcChainMask val = default(ProcChainMask);
			((ProcChainMask)(ref val)).AddProc((ProcType)13);
			healthComponent.Heal(amount * exportMult, val, true);
		}

		private void FixedUpdate()
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)healthComponent) || !healthComponent.alive)
			{
				return;
			}
			timer -= Time.fixedDeltaTime;
			if (!(timer > 0f))
			{
				timer = 0.25f;
				float maximum = Maximum;
				reserve = Mathf.Min(reserve, maximum);
				if (!Paused())
				{
					float num = HealAmount();
					reserve -= num;
					HealFromReserve(num);
				}
				int num2 = ((reserve > 0f) ? Mathf.Clamp(Mathf.RoundToInt(reserve / maximum * 100f), 1, 100) : 0);
				if (body.GetBuffCount(CorpseBloomRebornHCPlugin.ReserveBuff) != num2)
				{
					body.SetBuffCount(CorpseBloomRebornHCPlugin.ReserveBuff.buffIndex, num2);
				}
			}
		}

		private bool Paused()
		{
			if (reserve <= 0f || body.HasBuff(Buffs.HealingDisabled))
			{
				return true;
			}
			if (minRate > 0f || aegisState > 0)
			{
				return false;
			}
			return healthComponent.health >= healthComponent.fullHealth;
		}

		private float HealAmount()
		{
			float fullHealth = healthComponent.fullHealth;
			float num = fullHealth * maxRate * 0.25f;
			if (aegisState >= 2)
			{
				return Mathf.Min(reserve, num);
			}
			float num2 = fullHealth * minRate * 0.25f;
			float num3 = Mathf.Max(num2, (fullHealth - healthComponent.health) / exportMult);
			if (aegisState == 1)
			{
				num3 = Mathf.Max(num3, Mathf.Lerp(num, num2, healthComponent.barrier / healthComponent.fullBarrier));
			}
			return Mathf.Min(new float[3] { reserve, num, num3 });
		}
	}
}