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;
}
}