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.4.0
MoreStats.dll
Decompiled 4 hours 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 EntityStates; using IL.EntityStates; using IL.RoR2; using Mono.Cecil.Cil; using MonoMod.Cil; using On.EntityStates; using On.RoR2; using RoR2; using RoR2BepInExPack.Utilities; using UnityEngine; using UnityEngine.Networking; [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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: UnverifiableCode] namespace MoreStats; public static class BaseStats { public static bool ApplyShieldConversionHook = false; public static float PerfectedHealthBonus = 0.25f; public static float TranscendenceHealthBonusBase = 0.5f; public static float TranscendenceHealthBonusStack = 0.25f; public static float TranscendenceShieldConversionFractionBase = 1f; public static float TranscendenceShieldConversionFractionStack = 0f; public static float PerfectedShieldConversionFraction = 1f; public static float OverloadingShieldConversionFraction = 0.5f; public static float BaseShieldDelaySeconds = 7f; public static float MinShieldDelaySeconds = 1f; public static float BarrierLowDecayFactor = 0.5f; public static float BarrierHighDecayFactor = 3f; public static float BarrierDecayStaticMaxHealthTime = 30f; public static int FeatherJumpCountBase = 1; public static int FeatherJumpCountStack = 1; public static bool IncludeStrangeScrapInScrapTotal = true; } [BepInPlugin("com.RiskOfBrainrot.MoreStats", "MoreStats", "1.3.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.3.0"; public static PluginInfo PInfo { get; private set; } private void Awake() { StatHooks.Init(); OnHit.Init(); JumpAPI.Init(); } public static void DebugBreakpoint(string methodName, int breakpointNumber = -1) { string text = "MoreStats: " + methodName + " IL hook failed!"; if (breakpointNumber >= 0) { text += $" (breakpoint {breakpointNumber})"; } Debug.LogError((object)text); } } 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_000e: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = attackerBody.inventory; if (!((Enum)damageInfo.damageType.damageType).HasFlag((Enum)(object)(DamageType)128)) { float burnChance = StatHooks.GetMoreStatsFromBody(attackerBody).burnChance; if (burnChance > 0f) { ref DamageType damageType = ref damageInfo.damageType.damageType; damageType = (DamageType)((uint)damageType | 0x80u); } } } } public static class JumpAPI { public class JumpSynergyInfo { public bool isConditionalJump = false; public bool didJumpIgnoreRequirements = false; public bool isAirJump = false; public JumpSynergyInfo(bool isConditionalJump, bool didJumpIgnoreRequirements) { this.isConditionalJump = isConditionalJump; this.didJumpIgnoreRequirements = didJumpIgnoreRequirements; } } public delegate bool ConditionalJumpHandler(CharacterMotor sender, bool jumpIgnoredRequirements); public delegate void OnJumpSynergyHandler(CharacterMotor sender, JumpSynergyInfo jumpSynergyInfo); private static bool initialized; private static float verticalBonus; private static float horizontalBonus; private static bool didConditionalJump; private static bool jumpInputReceived; private static bool ignoredRequirements; public static event ConditionalJumpHandler OnConditionalJumpUrgent; public static event ConditionalJumpHandler OnConditionalJumpPriority; public static event ConditionalJumpHandler OnConditionalJumpLast; public static event OnJumpSynergyHandler OnJumpEvent; public static bool IsDoubleJump(CharacterMotor motor) { CharacterBody body = motor.body; int maxJumpCount = body.maxJumpCount; int baseJumpCount = body.baseJumpCount; int jumpCount = motor.jumpCount; if (jumpCount >= baseJumpCount) { return true; } return false; } public static bool IsBaseJump(CharacterMotor motor, bool mustBeGrounded = false) { CharacterBody body = motor.body; int maxJumpCount = body.maxJumpCount; int baseJumpCount = body.baseJumpCount; int jumpCount = motor.jumpCount; if (mustBeGrounded) { return jumpCount == 0; } if (jumpCount < baseJumpCount) { return true; } return false; } public static bool IsLastJump(CharacterMotor motor) { CharacterBody body = motor.body; int maxJumpCount = body.maxJumpCount; int baseJumpCount = body.baseJumpCount; int jumpCount = motor.jumpCount; if (jumpCount >= maxJumpCount - 1) { return true; } return false; } public static void ProposeJumpPowerBonusForCurrentJump(float vBonus = 0f, float hBonus = 0f) { if (verticalBonus <= vBonus) { verticalBonus = vBonus; } if (horizontalBonus <= hBonus) { horizontalBonus = hBonus; } } 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.ProcessJump_bool += new Manipulator(InsertConditionalJumps); } } private static void InsertConditionalJump(orig_ProcessJump_bool orig, GenericCharacterMain self, bool ignoreRequirements) { orig.Invoke(self, ignoreRequirements); } private static void InsertConditionalJumps(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ILCursor val = new ILCursor(il); val.Index = 0; InsertConditionalJumpProc(val); val.Index = 0; InsertJumpBonus(val); } private static void InsertJumpBonus(ILCursor c) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) int num = default(int); if (!c.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<GenericCharacterMain>(x, "ApplyJumpVelocity") }) || !c.TryGotoPrev((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num) })) { MoreStatsPlugin.DebugBreakpoint("InsertJumpBonus", 0); return; } int index = c.Index; c.Index = index + 1; c.EmitDelegate<Func<float, float>>((Func<float, float>)((float hBonusIn) => AddJumpBonus(hBonusIn, horizontalBonus))); index = c.Index; c.Index = index + 1; c.EmitDelegate<Func<float, float>>((Func<float, float>)((float vBonusIn) => AddJumpBonus(vBonusIn, verticalBonus))); c.GotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<GenericCharacterMain>(x, "ApplyJumpVelocity") }); c.Emit(OpCodes.Ldarg_0); c.EmitDelegate<Action<GenericCharacterMain>>((Action<GenericCharacterMain>)delegate(GenericCharacterMain self) { JumpAPI.OnJumpEvent?.Invoke(((EntityState)self).characterMotor, new JumpSynergyInfo(didConditionalJump, ignoredRequirements)); }); static float AddJumpBonus(float bonusIn, float bonusBonus) { return bonusIn + bonusBonus; } } private static void InsertConditionalJumpProc(ILCursor c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) c.Emit(OpCodes.Ldarg_0); c.Emit(OpCodes.Ldarg_1); c.EmitDelegate<Action<GenericCharacterMain, bool>>((Action<GenericCharacterMain, bool>)delegate(GenericCharacterMain self, bool ignoreRequirements) { verticalBonus = 0f; horizontalBonus = 0f; didConditionalJump = false; jumpInputReceived = self.jumpInputReceived; ignoredRequirements = ignoreRequirements; if (jumpInputReceived && ((BaseCharacterMain)self).hasCharacterMotor && ((EntityState)self).characterMotor.jumpCount > 0 && NetworkServer.active && GetConditionalJump(self, ignoreRequirements)) { didConditionalJump = true; } }); int num = default(int); if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<GenericCharacterMain>(x, "jumpInputReceived") }) || !c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num) })) { MoreStatsPlugin.DebugBreakpoint("InsertConditionalJumpProc", 1); return; } c.Emit(OpCodes.Ldarg_0); c.EmitDelegate<Func<bool, GenericCharacterMain, bool>>((Func<bool, GenericCharacterMain, bool>)((bool canJump, GenericCharacterMain characterMain) => (jumpInputReceived && (canJump || didConditionalJump)) ? true : false)); if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterMotor>(x, "jumpCount"), (Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 1) })) { MoreStatsPlugin.DebugBreakpoint("InsertConditionalJumpProc", 2); return; } c.EmitDelegate<Func<int, int>>((Func<int, int>)((int jumpCountToAdd) => (!didConditionalJump) ? jumpCountToAdd : 0)); } private static bool GetConditionalJump(GenericCharacterMain self, bool jumpIgnoredRequirements) { if (InvokeJumpSequentially(JumpAPI.OnConditionalJumpUrgent, onlyOneJump: false)) { return true; } if (((EntityState)self).characterMotor.jumpCount < ((EntityState)self).characterBody.baseJumpCount) { return false; } if (InvokeJumpSequentially(JumpAPI.OnConditionalJumpPriority, onlyOneJump: true)) { return true; } if (((EntityState)self).characterMotor.jumpCount < ((EntityState)self).characterBody.maxJumpCount) { return false; } if (InvokeJumpSequentially(JumpAPI.OnConditionalJumpLast, onlyOneJump: true)) { return true; } return false; bool InvokeJumpSequentially(ConditionalJumpHandler jumpEvent, bool onlyOneJump) { if (jumpEvent == null) { return false; } Delegate[] invocationList = jumpEvent.GetInvocationList(); if (invocationList == null || invocationList.Length == 0) { return false; } bool result = false; Delegate[] array = invocationList; for (int i = 0; i < array.Length; i++) { ConditionalJumpHandler conditionalJumpHandler = (ConditionalJumpHandler)array[i]; if (conditionalJumpHandler(((EntityState)self).characterMotor, jumpIgnoredRequirements)) { result = true; if (onlyOneJump) { break; } } } return result; } } } public class MoreStatCoefficients { public bool barrierDecayFrozen = false; public float barrierGenRate = 0f; public float barrierDrainRate = 0f; public float barrierDecayMult = 1f; public float barrierDecayDynamicHalfLife = 0f; public float luckFromBody = 0f; public float luckFromMaster = 0f; public float burnChance = 0f; public bool shieldRechargeReady = true; public float shieldRechargeDelay = BaseStats.BaseShieldDelaySeconds; public float healthToShieldConversionReversed = 0f; public float selfExecutionThresholdAdd = 0f; public float selfExecutionThresholdBase = float.NegativeInfinity; public float healingMult = 1f; public int bodyScrapWhiteCount = 0; public int bodyScrapGreenCount = 0; public int bodyScrapRedCount = 0; public int bodyScrapYellowCount = 0; public int maxHealthGateCount = 0; public float lowestCombinedHealthFraction = 1f; public bool preventHitStun = false; public float hitStunThresholdScale = 1f; internal float GetNextThresholdHealthFraction() { if (maxHealthGateCount == 0) { return -1f; } int num = maxHealthGateCount + 1; return FloorOrSubAndMult(lowestCombinedHealthFraction, num) / (float)num; static float FloorOrSubAndMult(float value, float multiplier) { return Mathf.Ceil(value * multiplier) - 1f; } } internal float GetHealthFractionSize() { if (maxHealthGateCount == 0) { return -1f; } return 1 / (maxHealthGateCount + 1); } internal void ResetStats() { barrierDecayFrozen = false; barrierDecayDynamicHalfLife = 0f; barrierGenRate = 0f; luckFromBody = 0f; burnChance = 0f; shieldRechargeReady = true; shieldRechargeDelay = BaseStats.BaseShieldDelaySeconds; selfExecutionThresholdAdd = 0f; selfExecutionThresholdBase = 0f; healingMult = 1f; bodyScrapWhiteCount = 0; bodyScrapGreenCount = 0; bodyScrapRedCount = 0; bodyScrapYellowCount = 0; maxHealthGateCount = 0; } } public static class StatHooks { public class MoreStatHookEventArgs { public int barrierFreezeCount = 0; public float barrierDecayRatePercentIncreaseMult = 1f; public float barrierDecayRatePercentDecreaseDiv = 1f; public float barrierGenerationRateAddPostMult = 0f; public float barrierDecayRateAddPreMult = 0f; public int jumpCountAdd = 0; public float burnChanceOnHit = 0f; public float shieldHealthConversionFractionReversedMult = 1f; public float shieldDelaySecondsIncreaseAddPreMult = 0f; public float shieldDelaySecondsIncreaseAddPostMult = 0f; public float shieldDelayPercentIncreaseMult = 1f; public float shieldDelayPercentDecreaseDiv = 1f; public float luckAdd = 0f; public float selfExecutionThresholdAdd = 0f; public float healingPercentIncreaseMult = 1f; public int scrapWhiteCountAdd = 0; public int scrapGreenCountAdd = 0; public int scrapRedCountAdd = 0; public int scrapYellowCountAdd = 0; public int preventHitStunCount = 0; public float hitStunThresholdScaleMult = 1f; public float barrierDecayMultiplier { get { if (barrierDecayRatePercentDecreaseDiv <= 0f || barrierDecayRatePercentIncreaseMult <= 0f) { return 0f; } return barrierDecayRatePercentIncreaseMult / barrierDecayRatePercentDecreaseDiv; } } public float shieldDelayMultiplier { get { if (shieldDelayPercentDecreaseDiv <= 0f || shieldDelayPercentIncreaseMult <= 0f) { return 0f; } return shieldDelayPercentIncreaseMult / shieldDelayPercentDecreaseDiv; } } public float selfExecutionThresholdBase { get; private set; } = float.NegativeInfinity; public int maxHealthGateCount { get; private set; } = 0; public float ModifyBaseExecutionThreshold(float newThreshold, bool condition) { if (newThreshold <= 0f || selfExecutionThresholdBase >= 1f) { return selfExecutionThresholdBase; } if (condition && newThreshold > selfExecutionThresholdBase) { selfExecutionThresholdBase = newThreshold; } return selfExecutionThresholdBase; } public int ModifyHealthGateCount(int newCount, bool condition = true) { if (newCount <= 0 || selfExecutionThresholdBase >= 1f) { return maxHealthGateCount; } if (condition && newCount > maxHealthGateCount) { maxHealthGateCount = newCount; } return maxHealthGateCount; } } public delegate void MoreStatHookEventHandler(CharacterBody sender, MoreStatHookEventArgs args); public delegate void HealthGateTriggeredEventHandler(CharacterBody sender); 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 event HealthGateTriggeredEventHandler OnBodyHealthGateTriggeredGlobal; 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown if (!initialized) { initialized = true; CharacterBody.RecalculateStats += new Manipulator(RecalculateMoreStats); CharacterBody.UpdateOutOfCombatAndDanger += new hook_UpdateOutOfCombatAndDanger(UpdateDangerMoreStats); Util.CheckRoll_float_float_CharacterMaster += new hook_CheckRoll_float_float_CharacterMaster(RoundLuckInCheckRoll); HealthComponent.ServerFixedUpdate += new Manipulator(HookHealthComponentUpdate); HealthComponent.GetBarrierDecayRate += new Manipulator(HookBarrierDecayRate); HealthComponent.TakeDamageProcess += new Manipulator(InterceptExecutionThreshold); HealthComponent.GetHealthBarValues += new hook_GetHealthBarValues(DisplayExecutionThreshold); HealthComponent.Heal += new Manipulator(ModifyHealing); HealthComponent.TakeDamageProcess += new Manipulator(InsertHealthGates); GlobalEventManager.onServerDamageDealt += RecordHealthFractionServer; GlobalEventManager.onClientDamageNotified += RecordHealthFractionClient; CharacterBody.onBodyStartGlobal += RecordHealthFractionBody; SetStateOnHurt.GetShouldHitStun += new hook_GetShouldHitStun(OverrideHitStun); DrifterTrashToTreasureController.OnInventoryChanged += new hook_OnInventoryChanged(DrifterUpdateScrapCounts); } } private static bool OverrideHitStun(orig_GetShouldHitStun orig, SetStateOnHurt self, HealthComponent healthComponent, float trueDamage) { if (Object.op_Implicit((Object)(object)healthComponent.body)) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(healthComponent.body); if (moreStatsFromBody.preventHitStun) { return false; } trueDamage *= moreStatsFromBody.hitStunThresholdScale; } return orig.Invoke(self, healthComponent, trueDamage); } 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); ModifyBarrierDecayRate_ServerFixedUpdate(c); ModifyShieldRechargeReady(c); } private static void ModifyHealing(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int num = default(int); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 1), (Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref num) })) { MoreStatsPlugin.DebugBreakpoint("ModifyHealing"); return; } int index = val.Index; val.Index = index + 1; val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Func<float, HealthComponent, float>>((Func<float, HealthComponent, float>)delegate(float healAmt, HealthComponent healthComponent) { CharacterBody body = healthComponent.body; MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body); return (moreStatsFromBody.healingMult > 0f) ? (healAmt * moreStatsFromBody.healingMult) : healAmt; }); val.Emit(OpCodes.Starg, 1); val.Emit(OpCodes.Ldarg_1); } 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 = GetMoreStatsFromBody(body); if (Object.op_Implicit((Object)(object)body.master)) { CharacterMaster master = body.master; master.luck -= CustomStats.luckFromBody; } CustomStats.ResetStats(); if (Object.op_Implicit((Object)(object)body.master)) { CharacterMaster master2 = body.master; master2.luck += StatMods.luckAdd; CustomStats.luckFromBody = StatMods.luckAdd; } CustomStats.burnChance = StatMods.burnChanceOnHit; CustomStats.healingMult = StatMods.healingPercentIncreaseMult; CustomStats.barrierDecayFrozen = StatMods.barrierFreezeCount > 0; CustomStats.barrierDrainRate = StatMods.barrierDecayRateAddPreMult; CustomStats.barrierDecayMult = StatMods.barrierDecayMultiplier; CustomStats.barrierGenRate = StatMods.barrierGenerationRateAddPostMult; float num = (BaseStats.BaseShieldDelaySeconds + StatMods.shieldDelaySecondsIncreaseAddPreMult) * StatMods.shieldDelayMultiplier + StatMods.shieldDelaySecondsIncreaseAddPostMult; CustomStats.shieldRechargeDelay = Mathf.Max(BaseStats.MinShieldDelaySeconds, num); UpdateShieldRechargeReady(body, CustomStats); CustomStats.healthToShieldConversionReversed = 1f - Mathf.Clamp01(StatMods.shieldHealthConversionFractionReversedMult); CustomStats.selfExecutionThresholdAdd = StatMods.selfExecutionThresholdAdd; CustomStats.selfExecutionThresholdBase = StatMods.selfExecutionThresholdBase; CustomStats.maxHealthGateCount = StatMods.maxHealthGateCount; CustomStats.hitStunThresholdScale = StatMods.hitStunThresholdScaleMult; CustomStats.preventHitStun = StatMods.preventHitStunCount > 0; }); ProcessLuck(val); ProcessMaxJumpCount(val); ProcessScrapCounts(val); if (BaseStats.ApplyShieldConversionHook) { ProcessShieldConversion(val); } } private static void GetStatMods(CharacterBody body) { StatMods = new MoreStatHookEventArgs(); if (StatHooks.GetMoreStatCoefficients != null) { StatHooks.GetMoreStatCoefficients(body, StatMods); } } private static void ProcessLuck(ILCursor c) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) c.Index = 0; if (!c.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterMaster>(x, "set_luck") })) { MoreStatsPlugin.DebugBreakpoint("ProcessLuck"); return; } c.EmitDelegate<Func<float>>((Func<float>)(() => StatMods.luckAdd)); c.Emit(OpCodes.Add); } private static void HookBarrierDecayRate(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); float num = default(float); if (val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, ref num) })) { val.Remove(); val.Remove(); val.Emit(OpCodes.Ldc_R4, BaseStats.BarrierLowDecayFactor); val.Emit(OpCodes.Ldc_R4, BaseStats.BarrierHighDecayFactor); } else { Debug.LogError((object)"MORE STATS DYNAMIC BARRIER DECAY HOOK FAILED!!!!"); } if (val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchDiv(x) })) { val.Remove(); val.Emit(OpCodes.Ldc_R4, BaseStats.BarrierDecayStaticMaxHealthTime); } else { Debug.LogError((object)"MORE STATS STATIC BARRIER DECAY HOOK FAILED!!!!"); } } private static void ModifyBarrierDecayRate_ServerFixedUpdate(ILCursor c) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) c.Index = 0; if (c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<HealthComponent>(x, "GetBarrierDecayRate") })) { c.Emit(OpCodes.Ldarg_0); c.EmitDelegate<Func<float, HealthComponent, float>>((Func<float, HealthComponent, float>)delegate(float barrierDecayRatePerSecond, HealthComponent healthComponent) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(healthComponent.body); if (moreStatsFromBody == null) { return barrierDecayRatePerSecond; } if (!moreStatsFromBody.barrierDecayFrozen) { barrierDecayRatePerSecond += moreStatsFromBody.barrierDrainRate; barrierDecayRatePerSecond *= moreStatsFromBody.barrierDecayMult; } else { barrierDecayRatePerSecond = 0f; } barrierDecayRatePerSecond -= moreStatsFromBody.barrierGenRate; return barrierDecayRatePerSecond; }); } else { Debug.LogError((object)"MORE STATS BARRIER DECAY HOOK FAILED!!!!"); } } private static void ProcessMaxJumpCount(ILCursor c) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) c.Index = 0; int featherCountLoc = 0; if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdsfld(x, "RoR2.RoR2Content/Items", "Feather") }) || !c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref featherCountLoc) }) || !c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterBody>(x, "baseJumpCount"), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, featherCountLoc) })) { MoreStatsPlugin.DebugBreakpoint("ProcessMaxJumpCount"); return; } 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 += BaseStats.FeatherJumpCountBase + BaseStats.FeatherJumpCountStack * (featherCount - 1); } num += StatMods.jumpCountAdd; return Mathf.Max(num, 0); }); } private static bool RoundLuckInCheckRoll(orig_CheckRoll_float_float_CharacterMaster orig, float percentChance, float luck, CharacterMaster effectOriginMaster) { float num = luck % 1f; if (num < 0f) { num += 1f; } luck = ((!(num > float.Epsilon) || !Util.CheckRoll(num * 100f, 0f, (CharacterMaster)null)) ? ((float)Math.Floor(luck)) : ((float)Math.Ceiling(luck))); return orig.Invoke(percentChance, luck, effectOriginMaster); } 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; if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "get_maxShield") }) || !c.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "get_outOfDanger") })) { MoreStatsPlugin.DebugBreakpoint("ModifyShieldRechargeReady"); return; } c.Remove(); c.EmitDelegate<Func<CharacterBody, bool>>((Func<CharacterBody, bool>)delegate(CharacterBody body) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(body); return moreStatsFromBody.shieldRechargeReady; }); } private static void ProcessShieldConversion(ILCursor c) { //IL_0232: Unknown result type (might be due to invalid IL or missing references) c.Index = 0; ILLabel val = c.DefineLabel(); int localShieldTotalLoc = 74; if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterBody>(x, "levelMaxShield") }) || !c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref localShieldTotalLoc) })) { MoreStatsPlugin.DebugBreakpoint("ProcessShieldConversion", 1); return; } int num = default(int); if (!c.TryGotoNext((MoveType)0, new Func<Instruction, bool>[3] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<CharacterBody>(x, "set_maxShield") })) { MoreStatsPlugin.DebugBreakpoint("ProcessShieldConversion", 2); return; } c.MarkLabel(val); int index = c.Index; c.Index = index + 1; c.EmitDelegate<Func<CharacterBody, float, float>>((Func<CharacterBody, float, float>)ConvertHealthToShield); ILLabel val2 = default(ILLabel); if (!c.TryGotoPrev((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchBrfalse(x, ref val2) })) { MoreStatsPlugin.DebugBreakpoint("ProcessShieldConversion", 3); return; } c.EmitDelegate<Func<bool, bool>>((Func<bool, bool>)((bool _) => false)); if (!c.TryGotoPrev((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchDup(x), (Instruction x) => ILPatternMatchingExt.MatchBrfalse(x, ref val2) })) { MoreStatsPlugin.DebugBreakpoint("ProcessShieldConversion", 4); return; } c.Remove(); c.Emit(OpCodes.Ldc_I4_0); } public static float ConvertHealthToShield(CharacterBody self, float maxShield) { float healthToShieldConversionReversed = CustomStats.healthToShieldConversionReversed; int transCount = self.inventory.GetItemCountEffective(Items.ShieldOnly); bool isTrans = transCount > 0; bool isPerfected = self.HasBuff(Buffs.AffixLunar); bool flag = self.HasBuff(Buffs.AffixBlue); healthToShieldConversionReversed *= 1f - GetTransConversion(BaseStats.TranscendenceShieldConversionFractionBase, BaseStats.TranscendenceShieldConversionFractionStack, BaseStats.PerfectedShieldConversionFraction); healthToShieldConversionReversed *= 1f - (flag ? BaseStats.OverloadingShieldConversionFraction : 0f); float num = GetTransConversion(BaseStats.TranscendenceHealthBonusBase, BaseStats.TranscendenceHealthBonusStack, BaseStats.PerfectedHealthBonus); self.maxHealth *= num; float num2 = self.maxHealth * (1f - healthToShieldConversionReversed); self.maxHealth = ((healthToShieldConversionReversed == 0f) ? 1f : (self.maxHealth - num2)); return maxShield + num2; float GetTransConversion(float transBase, float transStack, float perfected) { if (!isTrans && !isPerfected) { return 0f; } if (isTrans != isPerfected) { if (isPerfected) { return perfected; } return transBase + transStack * (float)(transCount - 1); } return Mathf.Max(transBase, perfected) + transStack * (float)(transCount - 1); } } 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int thresholdPosition = 0; if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, float.NegativeInfinity), (Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref thresholdPosition) }) || !val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0), (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<HealthComponent>(x, "get_isInFrozenState") })) { MoreStatsPlugin.DebugBreakpoint("InterceptExecutionThreshold"); return; } 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; } private static void ProcessScrapCounts(ILCursor c) { ProcessScrapCount("ScrapWhite", delegate(int scrapCount, CharacterBody self) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self); moreStatsFromBody.bodyScrapWhiteCount = scrapCount + StatMods.scrapWhiteCountAdd; if (BaseStats.IncludeStrangeScrapInScrapTotal) { moreStatsFromBody.bodyScrapWhiteCount += self.inventory.GetItemCountEffective(Items.ScrapWhiteSuppressed); } return moreStatsFromBody.bodyScrapWhiteCount; }); ProcessScrapCount("ScrapGreen", delegate(int scrapCount, CharacterBody self) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self); moreStatsFromBody.bodyScrapGreenCount = scrapCount + StatMods.scrapGreenCountAdd; if (BaseStats.IncludeStrangeScrapInScrapTotal) { moreStatsFromBody.bodyScrapGreenCount += self.inventory.GetItemCountEffective(Items.ScrapGreenSuppressed); } return moreStatsFromBody.bodyScrapGreenCount; }); ProcessScrapCount("ScrapRed", delegate(int scrapCount, CharacterBody self) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self); moreStatsFromBody.bodyScrapRedCount = scrapCount + StatMods.scrapRedCountAdd; if (BaseStats.IncludeStrangeScrapInScrapTotal) { moreStatsFromBody.bodyScrapRedCount += self.inventory.GetItemCountEffective(Items.ScrapRedSuppressed); } return moreStatsFromBody.bodyScrapRedCount; }); ProcessScrapCount("ScrapYellow", delegate(int scrapCount, CharacterBody self) { MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self); moreStatsFromBody.bodyScrapYellowCount = scrapCount + StatMods.scrapYellowCountAdd; return moreStatsFromBody.bodyScrapYellowCount; }); void ProcessScrapCount(string scrapName, Func<int, CharacterBody, int> callback) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) c.Index = 0; if (!c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdsfld(x, "RoR2.RoR2Content/Items", scrapName) }) || !c.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt<Inventory>(x, "GetItemCountEffective") })) { MoreStatsPlugin.DebugBreakpoint("ProcessScrapCount/" + scrapName); } else { c.Emit(OpCodes.Ldarg_0); c.EmitDelegate<Func<int, CharacterBody, int>>(callback); } } } private static void DrifterUpdateScrapCounts(orig_OnInventoryChanged orig, DrifterTrashToTreasureController self) { //IL_0018: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self.body); self.body.SetBuffCount(Buffs.TrashToTreasureWhite.buffIndex, moreStatsFromBody.bodyScrapWhiteCount); self.body.SetBuffCount(Buffs.TrashToTreasureGreen.buffIndex, moreStatsFromBody.bodyScrapGreenCount); self.body.SetBuffCount(Buffs.TrashToTreasureRed.buffIndex, moreStatsFromBody.bodyScrapRedCount); self.body.SetBuffCount(Buffs.TrashToTreasureYellow.buffIndex, moreStatsFromBody.bodyScrapYellowCount); } private static void InsertHealthGates(ILContext il) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int localDamageLoc = 10; ILLabel jumpTo = val.DefineLabel(); ILLabel val2 = val.DefineLabel(); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<DamageInfo>(x, "damage"), (Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref localDamageLoc) }) || !val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchLdfld<ItemCounts>(x, "minHealthPercentage") }) || !val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchBle(x, ref jumpTo) })) { MoreStatsPlugin.DebugBreakpoint("InsertHealthGates", 1); return; } val.GotoLabel(jumpTo, (MoveType)2, false); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldloc, localDamageLoc); val.EmitDelegate<Func<HealthComponent, float, float>>((Func<HealthComponent, float, float>)delegate(HealthComponent self, float damageIn) { if (self.itemCounts.minHealthPercentage > 0 || (Object)(object)self.body == (Object)null) { return damageIn; } MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self.body); if (moreStatsFromBody.maxHealthGateCount <= 0) { return damageIn; } float nextThresholdHealthFraction = moreStatsFromBody.GetNextThresholdHealthFraction(); if (nextThresholdHealthFraction < moreStatsFromBody.GetHealthFractionSize() || nextThresholdHealthFraction <= 0.02f) { return damageIn; } float num = self.combinedHealth - self.fullCombinedHealth * nextThresholdHealthFraction; if (damageIn < num) { return damageIn; } RecordBodyLowestHealth(moreStatsFromBody, nextThresholdHealthFraction - 0.01f); if (StatHooks.OnBodyHealthGateTriggeredGlobal != null) { StatHooks.OnBodyHealthGateTriggeredGlobal(self.body); } return num; }); val.Emit(OpCodes.Stloc, localDamageLoc); } private static void DisplayHealthGates(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchStfld<HealthBarValues>(x, "ospFraction") })) { MoreStatsPlugin.DebugBreakpoint("DisplayHealthGates"); return; } val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Func<float, HealthComponent, float>>((Func<float, HealthComponent, float>)delegate(float ospFractionIn, HealthComponent self) { float num = GetNewFraction(); Debug.Log((object)num); return num; float GetNewFraction() { if ((Object)(object)self.body == (Object)null) { return ospFractionIn; } MoreStatCoefficients moreStatsFromBody = GetMoreStatsFromBody(self.body); float nextThresholdHealthFraction = moreStatsFromBody.GetNextThresholdHealthFraction(); if (nextThresholdHealthFraction < moreStatsFromBody.GetHealthFractionSize()) { return ospFractionIn; } float num2 = nextThresholdHealthFraction * (1f - 1f / self.body.cursePenalty); if (num2 < ospFractionIn) { return ospFractionIn; } return nextThresholdHealthFraction; } }); } private static void RecordHealthFractionBody(CharacterBody obj) { RecordBodyLowestHealth(obj); } private static void RecordHealthFractionClient(DamageDealtMessage damageDealtMessage) { CharacterBody victimBody = default(CharacterBody); if (Object.op_Implicit((Object)(object)damageDealtMessage.victim) && damageDealtMessage.victim.TryGetComponent<CharacterBody>(ref victimBody)) { RecordBodyLowestHealth(victimBody); } } private static void RecordHealthFractionServer(DamageReport damageReport) { RecordBodyLowestHealth(damageReport.victimBody); } private static void RecordBodyLowestHealth(CharacterBody victimBody) { if (!((Object)(object)victimBody == (Object)null)) { RecordBodyLowestHealth(GetMoreStatsFromBody(victimBody), victimBody.healthComponent.combinedHealthFraction); } } private static void RecordBodyLowestHealth(MoreStatCoefficients statCoefficients, float healthFraction) { if (statCoefficients != null) { statCoefficients.lowestCombinedHealthFraction = Mathf.Min(statCoefficients.lowestCombinedHealthFraction, healthFraction); } } }