Decompiled source of RexEnhancements v1.0.1

RexEnhancements.dll

Decompiled an hour ago
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using EntityStates.Treebot;
using EntityStates.Treebot.Weapon;
using On.EntityStates.Treebot;
using On.EntityStates.Treebot.Weapon;
using On.RoR2;
using R2API;
using RoR2;
using RoR2.Projectile;
using RoR2.Skills;
using UnityEngine;
using UnityEngine.AddressableAssets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RexEnhancements")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+0a0c35b111d7a92f4e395054c60dd8e097f717b0")]
[assembly: AssemblyProduct("RexEnhancements")]
[assembly: AssemblyTitle("RexEnhancements")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RexEnhancements;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("com.Sagittariod.RexEnhancements", "Rex Enhancements", "1.0.0")]
public class RexEnhancementsPlugin : BaseUnityPlugin
{
	public class MortarHitTracker : MonoBehaviour
	{
		public int hits = 0;
	}

	public class ConvalescenceController : MonoBehaviour
	{
		public CharacterBody body;

		public BuffDef buffDef;

		public float remainingTime = 0f;

		private void Awake()
		{
			body = ((Component)this).GetComponent<CharacterBody>();
		}

		public void AddStack()
		{
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)buffDef))
			{
				int buffCount = body.GetBuffCount(buffDef);
				float num = 9f * Mathf.Pow(0.815f, (float)buffCount);
				remainingTime += num;
				UpdateBuffStacks();
			}
		}

		private void FixedUpdate()
		{
			if (remainingTime > 0f)
			{
				remainingTime -= Time.fixedDeltaTime;
				if (remainingTime <= 0f)
				{
					remainingTime = 0f;
					ClearBuffs();
					Object.Destroy((Object)(object)this);
				}
				else
				{
					UpdateBuffStacks();
				}
			}
		}

		private void UpdateBuffStacks()
		{
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)buffDef))
			{
				int stacksForTime = GetStacksForTime(remainingTime);
				int i;
				for (i = body.GetBuffCount(buffDef); i < stacksForTime; i++)
				{
					body.AddBuff(buffDef);
				}
				while (i > stacksForTime)
				{
					body.RemoveBuff(buffDef);
					i--;
				}
			}
		}

		private void ClearBuffs()
		{
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)buffDef))
			{
				for (int num = body.GetBuffCount(buffDef); num > 0; num--)
				{
					body.RemoveBuff(buffDef);
				}
			}
		}

		public static int GetStacksForTime(float t)
		{
			if (t <= 0f)
			{
				return 0;
			}
			int num = 0;
			float num2 = 0f;
			float num3 = 10f;
			while (t > num2)
			{
				num++;
				num2 += num3;
				num3 *= 0.815f;
				if (num > 100)
				{
					break;
				}
			}
			return num;
		}
	}

	public static ConfigEntry<float> maxBonusDamage;

	public static ConfigEntry<float> maxBonusArmor;

	public static ConfigEntry<bool> disperseCooldownReduction;

	public static ConfigEntry<float> brambleVolleyDamage;

	public static ConfigEntry<float> disperseCooldown;

	public static ConfigEntry<bool> harvestEndlagReduction;

	public static ConfigEntry<float> injectProcCoefficient;

	public static ConfigEntry<float> injectDamage;

	public static ConfigEntry<bool> enableNewPassive;

	public static ConfigEntry<bool> enableConvalescenceBuff;

	public static BuffDef curseCleanserBuff;

	public void Awake()
	{
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Expected O, but got Unknown
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Expected O, but got Unknown
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Expected O, but got Unknown
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Expected O, but got Unknown
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Expected O, but got Unknown
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		enableNewPassive = ((BaseUnityPlugin)this).Config.Bind<bool>("Passive", "Enable New Passive", true, "If true, grants a new passive which increases armor and damage based on missing health");
		maxBonusDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Passive", "Max Bonus Damage", 0.3f, "Maximum percentage damage increase granted when at minimum health");
		maxBonusArmor = ((BaseUnityPlugin)this).Config.Bind<float>("Passive", "Max Bonus Armor", 50f, "Maximum armor increase granted when at minimum health");
		harvestEndlagReduction = ((BaseUnityPlugin)this).Config.Bind<bool>("Existing Skills", "Enable Faster Harvest", true, "If true, halves the endlag of DIRECTIVE: HARVEST");
		injectProcCoefficient = ((BaseUnityPlugin)this).Config.Bind<float>("Existing Skills", "DIRECTIVE - Inject Proc Coefficient", 0.8f, "Proc coefficient for DIRECTIVE: INJECT");
		brambleVolleyDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Skills - Utility", "Bramble Volley Damage", 10f, "Damage coefficient for Bramble Volley");
		disperseCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Skills - Utility", "Directive Disperse Cooldown", 4.5f, "Base cooldown in seconds for Directive: DISPERSE");
		injectDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Existing Skills", "DIRECTIVE - Inject Damage", 0.9f, "Damage coefficient for DIRECTIVE: INJECT");
		enableConvalescenceBuff = ((BaseUnityPlugin)this).Config.Bind<bool>("Existing Skills", "Enable Convalescence Buff", true, "If true, specific abilities grant the Convalescence buff which temporarily reduce Curse");
		CharacterBody.RecalculateStats += new hook_RecalculateStats(CharacterBody_RecalculateStats);
		TreebotFireFruitSeed.OnEnter += new hook_OnEnter(TreebotFireFruitSeed_OnEnter);
		FireSyringe.OnEnter += new hook_OnEnter(FireSyringe_OnEnter);
		GlobalEventManager.OnHitEnemy += new hook_OnHitEnemy(GlobalEventManager_OnHitEnemy);
		FirePlantSonicBoom.OnEnter += new hook_OnEnter(FirePlantSonicBoom_OnEnter);
		Addressables.LoadAssetAsync<SkillDef>((object)"RoR2/Base/Treebot/TreebotBodySonicBoom.asset").WaitForCompletion().baseRechargeInterval = disperseCooldown.Value;
		ModifyPrefabs();
		UpdateMiscLanguage();
		UpdateInjectDescription();
		UpdateBrambleVolleyDescription();
		SetupConvalescenceKeyword();
		SetupBuff();
	}

	private void UpdateInjectDescription()
	{
		string text = "TREEBOT_PRIMARY_DESCRIPTION";
		string text2 = $"Fire 3 syringes for <style=cIsDamage>3x{injectDamage.Value * 100f}% damage</style>. The last syringe <style=cIsDamage>Weakens</style> and <style=cIsHealing>heals for 60% of damage dealt</style>.";
		LanguageAPI.Add(text, text2);
	}

	private void UpdateBrambleVolleyDescription()
	{
		string text = "TREEBOT_UTILITY_ALT1_DESCRIPTION";
		string text2 = $"<style=cIsHealth>20% HP</style>. Fire a <style=cIsUtility>Sonic Boom</style> that damages enemies for <style=cIsDamage>{brambleVolleyDamage.Value * 100f}% damage</style>. <style=cIsHealing>Heals for every target hit</style>.";
		LanguageAPI.Add(text, text2);
	}

	private void UpdateMiscLanguage()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		string text = "TREEBOT_PASSIVE_NAME";
		string text2 = "TREEBOT_PASSIVE_DESCRIPTION";
		string text3 = "TREEBOT_SPECIAL_DESCRIPTION";
		string text4 = "TREEBOT_SPECIAL_ALT1_DESCRIPTION";
		string text5 = "TREEBOT_SECONDARY_DESCRIPTION";
		string text6 = "TREEBOT_SECONDARY_ALT1_DESCRIPTION";
		GameObject val = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/Treebot/TreebotBody.prefab").WaitForCompletion();
		SkillLocator component = val.GetComponent<SkillLocator>();
		if (enableNewPassive.Value)
		{
			LanguageAPI.Add(text, "Adaptive Plating");
			LanguageAPI.Add(text2, $"Gain up to <style=cIsUtility>50 armor</style> and deal up to <style=cIsDamage>+{maxBonusDamage.Value * 100f}% damage</style> based on your <style=cIsHealth>missing health</style>.");
			if ((Object)(object)component != (Object)null)
			{
				component.passiveSkill.keywordToken = string.Empty;
			}
		}
		else
		{
			LanguageAPI.Add(text, "Natural Toxins");
			LanguageAPI.Add(text2, "Certain attacks <style=cIsDamage>Weaken</style> enemies hit, reducing their movement speed, armor, and damage.");
		}
		if (enableConvalescenceBuff.Value)
		{
			LanguageAPI.Add(text3, "<style=cIsHealth>25% HP</style>. Fire a flower that <style=cIsDamage>roots</style> for <style=cIsDamage>200% damage</style>. <style=cIsHealing>Heals for every target hit</style>. <style=cIsUtility>Direct hits grant Convalescence.</style>");
			LanguageAPI.Add(text4, "Fire a bolt that deals <style=cIsDamage>330% damage</style> and <style=cIsDamage>injects</style> an enemy. On death, drop multiple <style=cIsHealing>healing fruits</style> that heal for <style=cIsHealing>25% HP</style>. <style=cIsUtility>Direct hits grant Convalescence.</style>");
			LanguageAPI.Add(text6, "Launch a series of seed bullets into the sky, raining down for <style=cIsDamage>450% damage per second</style>. <style=cIsUtility>Every 4 hits grants Convalescence.</style>");
			LanguageAPI.Add(text5, "<style=cIsHealth>15% HP</style>. Launch a mortar into the sky for <style=cIsDamage>450% damage</style>. <style=cIsUtility>Each hit grants Convalescence.</style>");
		}
		else
		{
			LanguageAPI.Add(text3, "<style=cIsHealth>25% HP</style>. Fire a flower that <style=cIsDamage>roots</style> for <style=cIsDamage>200% damage</style>. <style=cIsHealing>Heals for every target hit</style>.");
			LanguageAPI.Add(text4, "Fire a bolt that deals <style=cIsDamage>330% damage</style> and <style=cIsDamage>injects</style> an enemy. On death, drop multiple <style=cIsHealing>healing fruits</style> that heal for <style=cIsHealing>25% HP</style>.");
			LanguageAPI.Add(text6, "Launch a series of seed bullets into the sky, raining down for <style=cIsDamage>450% damage per second</style>.");
			LanguageAPI.Add(text5, "<style=cIsHealth>15% HP</style>. Launch a mortar into the sky for <style=cIsDamage>450% damage</style>.");
		}
	}

	private void SetupConvalescenceKeyword()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		string keywordToken = "KEYWORD_CONVALESCENCE";
		if (!enableConvalescenceBuff.Value)
		{
			return;
		}
		LanguageAPI.Add(keywordToken, "<style=cKeywordName>Convalescence</style><style=cSub>Reduces current <style=cIsHealth>Curse</style> by 15% per stack. Higher stacks have shorter durations.</style>");
		GameObject val = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/Treebot/TreebotBody.prefab").WaitForCompletion();
		SkillLocator component = val.GetComponent<SkillLocator>();
		Action<SkillFamily> action = delegate(SkillFamily family)
		{
			//IL_000f: 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)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Variant[] variants = family.variants;
			foreach (Variant val2 in variants)
			{
				SkillDef skillDef = val2.skillDef;
				if ((Object)(object)skillDef != (Object)null && !skillDef.keywordTokens.Contains(keywordToken))
				{
					skillDef.keywordTokens = skillDef.keywordTokens.Append(keywordToken).ToArray();
				}
			}
		};
		if ((Object)(object)component != (Object)null)
		{
			action(component.secondary.skillFamily);
			action(component.special.skillFamily);
		}
	}

	private void SetupBuff()
	{
		curseCleanserBuff = ScriptableObject.CreateInstance<BuffDef>();
		((Object)curseCleanserBuff).name = "Convalescence";
		curseCleanserBuff.canStack = true;
		curseCleanserBuff.isDebuff = false;
		curseCleanserBuff.isHidden = false;
		curseCleanserBuff.iconSprite = LoadSpriteFromManifest("Convalescence.png");
		ContentAddition.AddBuffDef(curseCleanserBuff);
	}

	private void GlobalEventManager_OnHitEnemy(orig_OnHitEnemy orig, GlobalEventManager self, DamageInfo damageInfo, GameObject victim)
	{
		orig.Invoke(self, damageInfo, victim);
		if (!enableConvalescenceBuff.Value || !((Object)(object)damageInfo.attacker != (Object)null))
		{
			return;
		}
		CharacterBody component = damageInfo.attacker.GetComponent<CharacterBody>();
		if (!((Object)(object)component != (Object)null) || !(component.baseNameToken == "TREEBOT_BODY_NAME"))
		{
			return;
		}
		bool flag = false;
		if ((Object)(object)damageInfo.inflictor != (Object)null)
		{
			string name = ((Object)damageInfo.inflictor).name;
			if (name.Contains("TreebotFruitSeed") || name.Contains("TreebotMortar2") || name.Contains("TreebotFlower"))
			{
				flag = true;
			}
			else if (name.Contains("TreebotMortar"))
			{
				MortarHitTracker mortarHitTracker = ((Component)component).GetComponent<MortarHitTracker>();
				if ((Object)(object)mortarHitTracker == (Object)null)
				{
					mortarHitTracker = ((Component)component).gameObject.AddComponent<MortarHitTracker>();
				}
				mortarHitTracker.hits++;
				if (mortarHitTracker.hits >= 4)
				{
					flag = true;
					mortarHitTracker.hits = 0;
				}
			}
		}
		if (flag)
		{
			ConvalescenceController convalescenceController = ((Component)component).GetComponent<ConvalescenceController>();
			if ((Object)(object)convalescenceController == (Object)null)
			{
				convalescenceController = ((Component)component).gameObject.AddComponent<ConvalescenceController>();
				convalescenceController.buffDef = curseCleanserBuff;
			}
			convalescenceController.AddStack();
		}
	}

	private Sprite LoadSpriteFromManifest(string fileName)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		string name = "RexEnhancements." + fileName;
		using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
		if (stream != null)
		{
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(2, 2);
			ImageConversion.LoadImage(val, array);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
		return null;
	}

	private void CharacterBody_RecalculateStats(orig_RecalculateStats orig, CharacterBody self)
	{
		orig.Invoke(self);
		if (self.baseNameToken == "TREEBOT_BODY_NAME" && (Object)(object)self.healthComponent != (Object)null)
		{
			if (enableNewPassive.Value && self.healthComponent.fullHealth > 0f)
			{
				float num = self.healthComponent.health / self.healthComponent.fullHealth;
				float num2 = 1f + maxBonusDamage.Value * (1f - num);
				float num3 = maxBonusArmor.Value * (1f - num);
				typeof(CharacterBody).GetProperty("damage").SetValue(self, self.damage * num2);
				typeof(CharacterBody).GetProperty("armor").SetValue(self, self.armor + num3);
			}
			int buffCount = self.GetBuffCount(curseCleanserBuff);
			if (buffCount > 0 && self.cursePenalty > 1f && enableConvalescenceBuff.Value)
			{
				float cursePenalty = self.cursePenalty;
				float num4 = self.maxHealth * cursePenalty;
				float num5 = num4 - self.maxHealth;
				float num6 = num5 * Mathf.Pow(0.85f, (float)buffCount);
				float num7 = num4 - num6;
				float num8 = num4 / num7;
				typeof(CharacterBody).GetProperty("maxHealth").SetValue(self, num7);
				typeof(CharacterBody).GetProperty("cursePenalty").SetValue(self, num8);
			}
		}
	}

	private void TreebotFireFruitSeed_OnEnter(orig_OnEnter orig, TreebotFireFruitSeed self)
	{
		if (harvestEndlagReduction.Value)
		{
			self.baseDuration = 0.5f;
		}
		orig.Invoke(self);
	}

	private void FirePlantSonicBoom_OnEnter(orig_OnEnter orig, FirePlantSonicBoom self)
	{
		FirePlantSonicBoom.damageCoefficient = brambleVolleyDamage.Value;
		orig.Invoke(self);
	}

	private void FireSyringe_OnEnter(orig_OnEnter orig, FireSyringe self)
	{
		FireSyringe.damageCoefficient = injectDamage.Value;
		orig.Invoke(self);
	}

	private void ModifyPrefabs()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//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)
		GameObject val = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/Treebot/SyringeProjectile.prefab").WaitForCompletion();
		if ((Object)(object)val != (Object)null)
		{
			val.GetComponent<ProjectileController>().procCoefficient = injectProcCoefficient.Value;
		}
		GameObject val2 = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/Treebot/SyringeProjectileHealing.prefab").WaitForCompletion();
		if ((Object)(object)val2 != (Object)null)
		{
			val2.GetComponent<ProjectileController>().procCoefficient = injectProcCoefficient.Value;
		}
	}
}