Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MoreStats v1.1.0
MoreStats.dll
Decompiled a year agousing 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 MonoMod.RuntimeDetour; 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.1.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.1.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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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); ILHook val = new ILHook((MethodBase)typeof(CharacterMaster).GetMethod("get_luck", (BindingFlags)(-1)), new Manipulator(ModifyLuck)); } } private static void ModifyLuck(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.GotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchRet(x) }); val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Func<float, CharacterMaster, float>>((Func<float, CharacterMaster, float>)delegate(float baseLuck, CharacterMaster master) { if ((Object)(object)master == (Object)null || !master.hasBody) { return baseLuck; } CharacterBody body = master.GetBody(); MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body); float num = baseLuck + (float)moreStatsFromBody.luckAdd; float num2 = num % 1f; return (!(num2 >= float.Epsilon) || !Util.CheckRoll(num2 * 100f, 0f, (CharacterMaster)null)) ? ((float)Mathf.FloorToInt(num)) : ((float)Mathf.CeilToInt(num)); }); } 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); CustomStats.ResetStats(); CustomStats.luckAdd = StatMods.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; } }