Decompiled source of MoreStats v1.0.0

MoreStats.dll

Decompiled 8 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using IL.RoR2;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using On.EntityStates;
using On.RoR2;
using RoR2;
using RoR2.Projectile;
using RoR2BepInExPack.Utilities;
using UnityEngine;

[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 = "")]
[assembly: AssemblyCompany("MoreStats")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MoreStats")]
[assembly: AssemblyTitle("MoreStats")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace MoreStats;

[BepInPlugin("com.RiskOfBrainrot.MoreStats", "MoreStats", "1.0.0")]
public class MoreStatsPlugin : BaseUnityPlugin
{
	public const string guid = "com.RiskOfBrainrot.MoreStats";

	public const string teamName = "RiskOfBrainrot";

	public const string modName = "MoreStats";

	public const string version = "1.0.0";

	public const float BaseShieldRechargeDelay = 7f;

	public const float MinShieldRechargeDelay = 1f;

	public static PluginInfo PInfo { get; private set; }

	private void Awake()
	{
		StatHooks.Init();
		OnHit.Init();
		OnJump.Init();
	}
}
public static class OnHit
{
	public delegate void HitHookEventHandler(CharacterBody attackerBody, DamageInfo damageInfo, CharacterBody victimBody);

	private static bool initialized;

	public static event HitHookEventHandler GetHitBehavior;

	internal static void Init()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		if (!initialized)
		{
			initialized = true;
			GlobalEventManager.ProcessHitEnemy += new hook_ProcessHitEnemy(GlobalEventManager_OnHitEnemy);
		}
	}

	private static void GlobalEventManager_OnHitEnemy(orig_ProcessHitEnemy orig, GlobalEventManager self, DamageInfo damageInfo, GameObject victim)
	{
		if (Object.op_Implicit((Object)(object)damageInfo.attacker) && damageInfo.procCoefficient > 0f)
		{
			CharacterBody val = null;
			CharacterBody victimBody = null;
			if (damageInfo.attacker.TryGetComponent<CharacterBody>(ref val) && victim.TryGetComponent<CharacterBody>(ref victimBody))
			{
				CharacterMaster master = val.master;
				if ((Object)(object)master != (Object)null)
				{
					DoIgniteOnHit(damageInfo, victim, val, victimBody);
					OnHit.GetHitBehavior?.Invoke(val, damageInfo, victimBody);
				}
			}
		}
		orig.Invoke(self, damageInfo, victim);
	}

	private static void DoIgniteOnHit(DamageInfo damageInfo, GameObject victim, CharacterBody attackerBody, CharacterBody victimBody)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		Inventory inventory = victimBody.inventory;
		uint? maxStacksFromAttacker = null;
		if (Object.op_Implicit((Object)(object)damageInfo?.inflictor))
		{
			ProjectileDamage component = damageInfo.inflictor.GetComponent<ProjectileDamage>();
			if (Object.op_Implicit((Object)(object)component) && component.useDotMaxStacksFromAttacker)
			{
				maxStacksFromAttacker = component.dotMaxStacksFromAttacker;
			}
		}
		float burnChance = StatHooks.GetMoreStatsFromBody(attackerBody).burnChance;
		if (burnChance > 0f && Util.CheckRoll(burnChance, attackerBody.master))
		{
			InflictDotInfo val = default(InflictDotInfo);
			val.attackerObject = damageInfo.attacker;
			val.victimObject = victim;
			val.totalDamage = damageInfo.damage * 0.5f;
			val.damageMultiplier = 1f;
			val.dotIndex = (DotIndex)1;
			val.maxStacksFromAttacker = maxStacksFromAttacker;
			InflictDotInfo val2 = val;
			StrengthenBurnUtils.CheckDotForUpgrade(inventory, ref val2);
			DotController.InflictDot(ref val2);
		}
	}
}
public static class OnJump
{
	public delegate void OnJumpHandler(CharacterMotor sender, CharacterBody body, ref float verticalBonus);

	private static bool initialized;

	public static event OnJumpHandler OnJumpEvent;

	public static bool IsDoubleJump(CharacterMotor motor, CharacterBody body)
	{
		int maxJumpCount = body.maxJumpCount;
		int baseJumpCount = body.baseJumpCount;
		int num = motor.jumpCount + 1;
		if (num > baseJumpCount)
		{
			return true;
		}
		return false;
	}

	public static bool IsBaseJump(CharacterMotor motor, CharacterBody body)
	{
		int maxJumpCount = body.maxJumpCount;
		int baseJumpCount = body.baseJumpCount;
		int num = motor.jumpCount + 1;
		if (num <= baseJumpCount)
		{
			return true;
		}
		return false;
	}

	public static bool IsLastJump(CharacterMotor motor, CharacterBody body)
	{
		int maxJumpCount = body.maxJumpCount;
		int baseJumpCount = body.baseJumpCount;
		int num = motor.jumpCount + 1;
		if (num == maxJumpCount)
		{
			return true;
		}
		return false;
	}

	internal static void Init()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		if (!initialized)
		{
			initialized = true;
			GenericCharacterMain.ApplyJumpVelocity += new hook_ApplyJumpVelocity(CharacterMain_JumpVelocity);
		}
	}

	private static void CharacterMain_JumpVelocity(orig_ApplyJumpVelocity orig, CharacterMotor characterMotor, CharacterBody characterBody, float horizontalBonus, float verticalBonus, bool vault)
	{
		OnJump.OnJumpEvent?.Invoke(characterMotor, characterBody, ref verticalBonus);
		orig.Invoke(characterMotor, characterBody, horizontalBonus, verticalBonus, vault);
	}
}
public static class StatHooks
{
	public class MoreStatHookEventArgs
	{
		public int barrierFreezeCount = 0;

		public float barrierDecayIncreaseMultiplier = 1f;

		public float barrierDecayDecreaseDivisor = 1f;

		public float barrierGenPerSecondFlat = 0f;

		public float barrierDecayPerSecondFlat = 0f;

		public float barrierBaseStaticDecayRateMaxHealthTime = 30f;

		public float barrierBaseDynamicDecayRateHalfLife = 0f;

		public int jumpCountAdd = 0;

		public int featherJumpCountBase = 1;

		public int featherJumpCountStack = 1;

		public float burnChanceOnHit = 0f;

		public float shieldDelayIncreaseInSeconds = 0f;

		public float shieldDelayMultiplier = 1f;

		public int luckAdd = 0;

		public float selfExecutionThresholdAdd = 0f;

		public float barrierDecayMultiplier
		{
			get
			{
				if (barrierDecayDecreaseDivisor <= 0f || barrierDecayIncreaseMultiplier <= 0f)
				{
					return 0f;
				}
				return barrierDecayIncreaseMultiplier / barrierDecayDecreaseDivisor;
			}
		}

		public float selfExecutionThresholdBase { get; private set; } = float.NegativeInfinity;


		public float ModifyBaseExecutionThreshold(float newThreshold, bool condition)
		{
			if (newThreshold <= 0f || selfExecutionThresholdBase >= 1f)
			{
				return selfExecutionThresholdBase;
			}
			if (condition && newThreshold > selfExecutionThresholdBase)
			{
				selfExecutionThresholdBase = newThreshold;
			}
			return selfExecutionThresholdBase;
		}
	}

	public delegate void MoreStatHookEventHandler(CharacterBody sender, MoreStatHookEventArgs args);

	private static bool initialized = false;

	public static FixedConditionalWeakTable<CharacterBody, MoreStatCoefficients> characterCustomStats = new FixedConditionalWeakTable<CharacterBody, MoreStatCoefficients>();

	private static MoreStatHookEventArgs StatMods;

	private static MoreStatCoefficients CustomStats;

	public static event MoreStatHookEventHandler GetMoreStatCoefficients;

	public static MoreStatCoefficients GetMoreStatsFromBody(CharacterBody body)
	{
		if ((Object)(object)body == (Object)null)
		{
			return null;
		}
		return characterCustomStats.GetOrCreateValue(body);
	}

	internal static void Init()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Expected O, but got Unknown
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Expected O, but got Unknown
		if (!initialized)
		{
			initialized = true;
			CharacterBody.RecalculateStats += new Manipulator(RecalculateMoreStats);
			CharacterBody.UpdateOutOfCombatAndDanger += new hook_UpdateOutOfCombatAndDanger(UpdateDangerMoreStats);
			CharacterMaster.OnInventoryChanged += new hook_OnInventoryChanged(UpdateMoreLuckStat);
			HealthComponent.ServerFixedUpdate += new Manipulator(HookHealthComponentUpdate);
			HealthComponent.TakeDamageProcess += new Manipulator(InterceptExecutionThreshold);
			HealthComponent.GetHealthBarValues += new hook_GetHealthBarValues(DisplayExecutionThreshold);
		}
	}

	private static void RecalculateMoreStats(ILContext il)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		ILCursor val = new ILCursor(il);
		val.Emit(OpCodes.Ldarg_0);
		val.EmitDelegate<Action<CharacterBody>>((Action<CharacterBody>)GetStatMods);
		val.Emit(OpCodes.Ldarg_0);
		val.EmitDelegate<Action<CharacterBody>>((Action<CharacterBody>)delegate(CharacterBody body)
		{
			CustomStats = characterCustomStats.GetOrCreateValue(body);
			if ((Object)(object)body.master != (Object)null)
			{
				CharacterMaster master = body.master;
				master.luck -= (float)CustomStats.luckAdd;
			}
			CustomStats.ResetStats();
			CustomStats.luckAdd = StatMods.luckAdd;
			if ((Object)(object)body.master != (Object)null)
			{
				CharacterMaster master2 = body.master;
				master2.luck += (float)CustomStats.luckAdd;
			}
			CustomStats.shieldRechargeDelay = (7f + StatMods.shieldDelayIncreaseInSeconds) * StatMods.shieldDelayMultiplier;
			UpdateShieldRechargeReady(body, CustomStats);
			CustomStats.selfExecutionThresholdAdd = StatMods.selfExecutionThresholdAdd;
			CustomStats.selfExecutionThresholdBase = StatMods.selfExecutionThresholdBase;
		});
		ProcessBarrierDecayRate_RecalcStats(val);
		ProcessMaxJumpCount(val);
	}

	private static void GetStatMods(CharacterBody body)
	{
		StatMods = new MoreStatHookEventArgs();
		if (StatHooks.GetMoreStatCoefficients != null)
		{
			StatHooks.GetMoreStatCoefficients(body, StatMods);
		}
	}

	private static void HookHealthComponentUpdate(ILContext il)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		ILCursor c = new ILCursor(il);
		ModifyShieldRechargeReady(c);
		ModifyBarrierDecayRate_ServerFixedUpdate(c);
	}

	private static void ProcessBarrierDecayRate_RecalcStats(ILCursor c)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		c.Index = 0;
		c.GotoNext((MoveType)0, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "set_barrierDecayRate")
		});
		float num3 = default(float);
		c.GotoPrev((MoveType)2, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, ref num3)
		});
		c.Remove();
		c.Emit(OpCodes.Ldarg_0);
		c.EmitDelegate<Func<float, float, CharacterBody, float>>((Func<float, float, CharacterBody, float>)delegate(float maxBarrier, float decayTime, CharacterBody body)
		{
			float num = 0f;
			bool flag = StatMods.barrierFreezeCount > 0;
			float num2 = ((StatMods.barrierDecayDecreaseDivisor > 0f) ? (StatMods.barrierDecayIncreaseMultiplier / StatMods.barrierDecayDecreaseDivisor) : 0f);
			CustomStats.barrierDecayFrozen = StatMods.barrierFreezeCount > 0;
			CustomStats.barrierDecayDynamicHalfLife = ((num2 > 0f) ? (StatMods.barrierBaseDynamicDecayRateHalfLife / num2) : 0f);
			if (!flag && num2 > 0f)
			{
				num = StatMods.barrierDecayPerSecondFlat;
				if (StatMods.barrierBaseStaticDecayRateMaxHealthTime > 0f)
				{
					num += maxBarrier / StatMods.barrierBaseStaticDecayRateMaxHealthTime;
				}
				num *= num2;
			}
			return num - StatMods.barrierGenPerSecondFlat;
		});
	}

	private static void ModifyBarrierDecayRate_ServerFixedUpdate(ILCursor c)
	{
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		c.Index = 0;
		float num2 = default(float);
		c.GotoNext((MoveType)2, new Func<Instruction, bool>[2]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdfld<HealthComponent>(x, "barrier"),
			(Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, ref num2)
		});
		c.Emit(OpCodes.Ldarg_0);
		c.EmitDelegate<Func<float, HealthComponent, float>>((Func<float, HealthComponent, float>)delegate(float minBarrier, HealthComponent healthComponent)
		{
			CharacterBody body2 = healthComponent.body;
			if (Object.op_Implicit((Object)(object)body2) && body2.barrierDecayRate < 0f)
			{
				minBarrier += body2.barrierDecayRate;
			}
			return minBarrier;
		});
		c.GotoNext((MoveType)0, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "get_barrierDecayRate")
		});
		c.Remove();
		c.Emit(OpCodes.Ldarg_0);
		c.EmitDelegate<Func<CharacterBody, HealthComponent, float>>((Func<CharacterBody, HealthComponent, float>)delegate(CharacterBody body, HealthComponent healthComponent)
		{
			float num = body.barrierDecayRate;
			MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body);
			if (moreStatsFromBody == null)
			{
				return num;
			}
			if (!moreStatsFromBody.barrierDecayFrozen && moreStatsFromBody.barrierDecayDynamicHalfLife > 0f)
			{
				num += Mathf.Max(1f - moreStatsFromBody.barrierGenRate, healthComponent.barrier * Mathf.Log(2f) / moreStatsFromBody.barrierDecayDynamicHalfLife);
			}
			return num;
		});
	}

	private static void ProcessMaxJumpCount(ILCursor c)
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		c.Index = 0;
		int featherCountLoc = 0;
		c.GotoNext((MoveType)2, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdsfld(x, "RoR2.RoR2Content/Items", "Feather")
		});
		c.GotoNext((MoveType)2, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref featherCountLoc)
		});
		if (c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterBody>(x, "baseJumpCount"),
			(Instruction x) => ILPatternMatchingExt.MatchLdloc(x, featherCountLoc)
		}))
		{
			c.Emit(OpCodes.Ldarg_0);
			c.EmitDelegate<Func<int, CharacterBody, int>>((Func<int, CharacterBody, int>)delegate(int featherCount, CharacterBody self)
			{
				int num = 0;
				MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self);
				if (featherCount > 0)
				{
					num += StatMods.featherJumpCountBase + StatMods.featherJumpCountStack * (featherCount - 1);
				}
				return num + StatMods.jumpCountAdd;
			});
		}
		else
		{
			Debug.LogError((object)"MORE STATS JUMP COUNT HOOK FAILED");
		}
	}

	private static void UpdateMoreLuckStat(orig_OnInventoryChanged orig, CharacterMaster self)
	{
		CharacterBody body = self.GetBody();
		if (Object.op_Implicit((Object)(object)body))
		{
			MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body);
			moreStatsFromBody.luckAdd = 0;
		}
		orig.Invoke(self);
	}

	private static void UpdateDangerMoreStats(orig_UpdateOutOfCombatAndDanger orig, CharacterBody self)
	{
		orig.Invoke(self);
		MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self);
		UpdateShieldRechargeReady(self, moreStatsFromBody);
	}

	private static void UpdateShieldRechargeReady(CharacterBody body, MoreStatCoefficients stats)
	{
		bool flag = body.outOfDangerStopwatch >= stats.shieldRechargeDelay;
		if (stats.shieldRechargeReady != flag)
		{
			stats.shieldRechargeReady = flag;
			body.statsDirty = true;
		}
	}

	private static void ModifyShieldRechargeReady(ILCursor c)
	{
		c.Index = 0;
		c.GotoNext((MoveType)2, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "get_maxShield")
		});
		c.GotoNext((MoveType)0, new Func<Instruction, bool>[1]
		{
			(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "get_outOfDanger")
		});
		c.Remove();
		c.EmitDelegate<Func<CharacterBody, bool>>((Func<CharacterBody, bool>)delegate(CharacterBody body)
		{
			MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body);
			return moreStatsFromBody.shieldRechargeReady;
		});
	}

	private static void InterceptExecutionThreshold(ILContext il)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: 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)
		ILCursor val = new ILCursor(il);
		int thresholdPosition = 0;
		val.GotoNext((MoveType)2, new Func<Instruction, bool>[2]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, float.NegativeInfinity),
			(Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref thresholdPosition)
		});
		val.GotoNext((MoveType)0, new Func<Instruction, bool>[2]
		{
			(Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0),
			(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<HealthComponent>(x, "get_isInFrozenState")
		});
		val.Emit(OpCodes.Ldloc, thresholdPosition);
		val.Emit(OpCodes.Ldarg, 0);
		val.EmitDelegate<Func<float, HealthComponent, float>>((Func<float, HealthComponent, float>)((float currentThreshold, HealthComponent hc) => RecalculateExecutionThreshold(currentThreshold, hc)));
		val.Emit(OpCodes.Stloc, thresholdPosition);
	}

	private static float RecalculateExecutionThreshold(float currentThreshold, HealthComponent healthComponent, float mult = 1f)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		CharacterBody body = healthComponent.body;
		if ((Object)(object)body != (Object)null && !((Enum)body.bodyFlags).HasFlag((Enum)(object)(BodyFlags)16))
		{
			MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body);
			float num = Mathf.Max(currentThreshold, moreStatsFromBody.selfExecutionThresholdBase * mult);
			return num + moreStatsFromBody.selfExecutionThresholdAdd;
		}
		return currentThreshold;
	}

	private static HealthBarValues DisplayExecutionThreshold(orig_GetHealthBarValues orig, HealthComponent self)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: 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_0043: 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)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		HealthBarValues val = orig.Invoke(self);
		val.cullFraction = Mathf.Clamp01(RecalculateExecutionThreshold(val.cullFraction, self, Mathf.Clamp01(1f - (1f - 1f / self.body.cursePenalty))));
		return val;
	}
}
public class MoreStatCoefficients
{
	public bool barrierDecayFrozen = false;

	public float barrierDecayDynamicHalfLife = 0f;

	public float barrierGenRate = 0f;

	public float burnChance = 0f;

	public bool shieldRechargeReady = true;

	public float shieldRechargeDelay = 7f;

	public float selfExecutionThresholdAdd = 0f;

	public float selfExecutionThresholdBase = float.NegativeInfinity;

	public int luckAdd = 0;

	public void ResetStats()
	{
		barrierDecayFrozen = false;
		barrierDecayDynamicHalfLife = 0f;
		barrierGenRate = 0f;
		burnChance = 0f;
		shieldRechargeReady = true;
		shieldRechargeDelay = 7f;
		selfExecutionThresholdAdd = 0f;
		selfExecutionThresholdBase = 0f;
		luckAdd = 0;
	}
}