Decompiled source of ProcChainNerf v1.0.0

ProcChainNerf.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HG.Reflection;
using IL.RoR2;
using IL.RoR2.Items;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using MonoMod.Cil;
using On.RoR2;
using RoR2;
using RoR2.Items;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ProcChainNerf")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+292282bf511bcd63c954762be9e8f52e83a13739")]
[assembly: AssemblyProduct("ProcChainNerf")]
[assembly: AssemblyTitle("ProcChainNerf")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ProcChainNerf
{
	internal static class Log
	{
		internal static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		internal static ManualLogSource GetSource()
		{
			return _logSource;
		}

		internal static void LogDebug(object data)
		{
			_logSource.LogDebug(data);
		}

		internal static void LogError(object data)
		{
			_logSource.LogError(data);
		}

		internal static void LogFatal(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void LogInfo(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void LogMessage(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void LogWarning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	[BepInPlugin("Def.ProcChainNerf", "ProcChainNerf", "1.0.0")]
	public class ProcChainNerfPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "Def.ProcChainNerf";

		public const string PluginAuthor = "Def";

		public const string PluginName = "ProcChainNerf";

		public const string PluginVersion = "1.0.0";

		public static ConfigEntry<bool> Enabled;

		public static readonly Dictionary<ProcItem, ConfigEntry<bool>> ItemEnabled = new Dictionary<ProcItem, ConfigEntry<bool>>();

		public void Awake()
		{
			Log.Init(((BaseUnityPlugin)this).Logger);
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Main", "Enabled", true, "When another item's hit triggers one of the items below, its extra stacks deal their damage based on your base damage, not on the damage of that hit. The first stack still counts the hit's total damage. Items triggered by your skills keep all of their stacks. Never more than vanilla.");
			ProcItems.Info[] all = ProcItems.All;
			foreach (ProcItems.Info info in all)
			{
				ItemEnabled[info.id] = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", info.name.Replace("'", ""), true, info.name + ": " + info.description);
			}
			ProcItems.Hook();
		}

		public static bool IsEnabled(ProcItem item)
		{
			if (Enabled.Value && ItemEnabled.TryGetValue(item, out var value))
			{
				return value.Value;
			}
			return false;
		}
	}
	public enum ProcItem
	{
		AtGMissile,
		PlasmaShrimp,
		RunaldsBand,
		KjarosBand,
		SingularityBand,
		MoltenPerforator,
		ChargedPerforator,
		LuminousShot,
		SauteedWorms
	}
	public static class ProcItems
	{
		public class Info
		{
			public ProcItem id;

			public string name;

			public string description;

			public float firstStack;

			public float extraStack;

			public string itemField;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Manipulator <0>__GlobalEventManager_ProcessHitEnemy;

			public static hook_ProcessHitEnemy <1>__GlobalEventManager_ProcessHitEnemy_Chain;

			public static Manipulator <2>__WyrmOnHitBehavior_TryFire;
		}

		public static readonly Info[] All = new Info[9]
		{
			new Info
			{
				id = ProcItem.AtGMissile,
				name = "AtG Missile Mk. 1",
				description = "missile for 300% TOTAL damage, extra stacks 300% base damage.",
				firstStack = 3f,
				extraStack = 3f,
				itemField = "Missile"
			},
			new Info
			{
				id = ProcItem.PlasmaShrimp,
				name = "Plasma Shrimp",
				description = "missiles for 40% TOTAL damage, extra stacks 40% base damage.",
				firstStack = 0.4f,
				extraStack = 0.4f,
				itemField = "MissileVoid"
			},
			new Info
			{
				id = ProcItem.RunaldsBand,
				name = "Runald's Band",
				description = "blast for 250% TOTAL damage, extra stacks 250% base damage.",
				firstStack = 2.5f,
				extraStack = 2.5f,
				itemField = "IceRing"
			},
			new Info
			{
				id = ProcItem.KjarosBand,
				name = "Kjaro's Band",
				description = "fire tornado for 300% TOTAL damage, extra stacks 300% base damage.",
				firstStack = 3f,
				extraStack = 3f,
				itemField = "FireRing"
			},
			new Info
			{
				id = ProcItem.SingularityBand,
				name = "Singularity Band",
				description = "black hole for 100% TOTAL damage, extra stacks 100% base damage.",
				firstStack = 1f,
				extraStack = 1f,
				itemField = "ElementalRingVoid"
			},
			new Info
			{
				id = ProcItem.MoltenPerforator,
				name = "Molten Perforator",
				description = "magma balls for 300% TOTAL damage, extra stacks 300% base damage.",
				firstStack = 3f,
				extraStack = 3f,
				itemField = "FireballsOnHit"
			},
			new Info
			{
				id = ProcItem.ChargedPerforator,
				name = "Charged Perforator",
				description = "lightning for 500% TOTAL damage, extra stacks 500% base damage.",
				firstStack = 5f,
				extraStack = 5f,
				itemField = "LightningStrikeOnHit"
			},
			new Info
			{
				id = ProcItem.LuminousShot,
				name = "Luminous Shot",
				description = "bonus for 175% TOTAL damage, extra stacks 50% base damage.",
				firstStack = 1.75f,
				extraStack = 0.5f,
				itemField = "IncreasePrimaryDamage"
			},
			new Info
			{
				id = ProcItem.SauteedWorms,
				name = "Sauteed Worms",
				description = "wyrm for 500% TOTAL damage per second, extra stacks 500% base damage.",
				firstStack = 5f,
				extraStack = 5f,
				itemField = "WyrmOnHit"
			}
		};

		private static readonly Stack<bool> itemHits = new Stack<bool>();

		private static int stickyBombIndex = -2;

		private static Info Get(ProcItem id)
		{
			return All[(int)id];
		}

		public static void Hook()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			object obj = <>O.<0>__GlobalEventManager_ProcessHitEnemy;
			if (obj == null)
			{
				Manipulator val = GlobalEventManager_ProcessHitEnemy;
				<>O.<0>__GlobalEventManager_ProcessHitEnemy = val;
				obj = (object)val;
			}
			GlobalEventManager.ProcessHitEnemy += (Manipulator)obj;
			object obj2 = <>O.<1>__GlobalEventManager_ProcessHitEnemy_Chain;
			if (obj2 == null)
			{
				hook_ProcessHitEnemy val2 = GlobalEventManager_ProcessHitEnemy_Chain;
				<>O.<1>__GlobalEventManager_ProcessHitEnemy_Chain = val2;
				obj2 = (object)val2;
			}
			GlobalEventManager.ProcessHitEnemy += (hook_ProcessHitEnemy)obj2;
			object obj3 = <>O.<2>__WyrmOnHitBehavior_TryFire;
			if (obj3 == null)
			{
				Manipulator val3 = WyrmOnHitBehavior_TryFire;
				<>O.<2>__WyrmOnHitBehavior_TryFire = val3;
				obj3 = (object)val3;
			}
			WyrmOnHitBehavior.TryFire += (Manipulator)obj3;
		}

		private static void GlobalEventManager_ProcessHitEnemy_Chain(orig_ProcessHitEnemy orig, GlobalEventManager self, DamageInfo damageInfo, GameObject victim)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			itemHits.Push(damageInfo != null && (damageInfo.procChainMask.mask != 0 || (int)damageInfo.damageColorIndex == 13 || IsStickyBomb(damageInfo.inflictor)));
			try
			{
				orig.Invoke(self, damageInfo, victim);
			}
			finally
			{
				itemHits.Pop();
			}
		}

		private static bool IsStickyBomb(GameObject inflictor)
		{
			ProjectileController val = default(ProjectileController);
			if ((Object)(object)inflictor == (Object)null || !inflictor.TryGetComponent<ProjectileController>(ref val))
			{
				return false;
			}
			if (stickyBombIndex == -2)
			{
				stickyBombIndex = ProjectileCatalog.FindProjectileIndex("StickyBomb");
			}
			if (stickyBombIndex >= 0)
			{
				return val.catalogIndex == stickyBombIndex;
			}
			return false;
		}

		public static float Lower(float vanilla, float hitDamage, float damageStat, Info item, int stacks)
		{
			if (stacks <= 1 || !NetworkServer.active || !ProcChainNerfPlugin.IsEnabled(item.id) || itemHits.Count == 0 || !itemHits.Peek())
			{
				return vanilla;
			}
			float num = item.firstStack * hitDamage + item.extraStack * (float)(stacks - 1) * damageStat;
			return Mathf.Min(vanilla, Mathf.Max(1f, num));
		}

		public static float OnHitProcDamage(float hitDamage, float damageStat, float coefficient, int item)
		{
			float vanilla = Util.OnHitProcDamage(hitDamage, damageStat, coefficient);
			Info info = Get((ProcItem)item);
			int stacks = Mathf.RoundToInt(coefficient / info.firstStack);
			return Lower(vanilla, hitDamage, damageStat, info, stacks);
		}

		public static float LuminousShot(float vanilla, DamageInfo damageInfo, CharacterBody body, int stacks)
		{
			if (!Object.op_Implicit((Object)(object)body))
			{
				return vanilla;
			}
			return Lower(vanilla, damageInfo.damage, body.damage, Get(ProcItem.LuminousShot), stacks);
		}

		public static float SauteedWorms(float vanilla, DamageInfo damageInfo, BaseItemBodyBehavior behavior)
		{
			if (!Object.op_Implicit((Object)(object)behavior) || !Object.op_Implicit((Object)(object)behavior.body))
			{
				return vanilla;
			}
			return Lower(vanilla, damageInfo.damage, behavior.body.damage, Get(ProcItem.SauteedWorms), behavior.stack);
		}

		private static void GlobalEventManager_ProcessHitEnemy(ILContext il)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, Info> dictionary = All.ToDictionary((Info item) => item.itemField);
			Dictionary<int, string> dictionary2 = new Dictionary<int, string>();
			HashSet<ProcItem> patched = new HashSet<ProcItem>();
			ILCursor val = new ILCursor(il);
			Collection<Instruction> instrs = il.Instrs;
			int num2 = default(int);
			for (int num = 0; num < instrs.Count; num++)
			{
				Instruction val2 = instrs[num];
				if (ILPatternMatchingExt.MatchCallvirt<Inventory>(val2, "GetItemCountEffective") && num > 0 && num + 1 < instrs.Count && instrs[num - 1].OpCode == OpCodes.Ldsfld)
				{
					object operand = instrs[num - 1].Operand;
					FieldReference val3 = (FieldReference)((operand is FieldReference) ? operand : null);
					if (val3 != null && ILPatternMatchingExt.MatchStloc(instrs[num + 1], ref num2))
					{
						dictionary2[num2] = ((MemberReference)val3).Name;
						if (((MemberReference)val3).Name == Get(ProcItem.LuminousShot).itemField)
						{
							PatchLuminousShot(il, num + 1, num2, patched);
						}
						continue;
					}
				}
				if (ILPatternMatchingExt.MatchCall(val2, typeof(Util), "OnHitProcDamage") && TryFindCountLocal(instrs, num, out var local) && dictionary2.TryGetValue(local, out var value) && dictionary.TryGetValue(value, out var value2))
				{
					val.Goto(num, (MoveType)0, false);
					val.Emit(OpCodes.Ldc_I4, (int)value2.id);
					val.Next.Operand = il.Import((MethodBase)typeof(ProcItems).GetMethod("OnHitProcDamage"));
					num++;
					patched.Add(value2.id);
				}
			}
			foreach (Info item in All.Where((Info item) => item.id != ProcItem.SauteedWorms && !patched.Contains(item.id)))
			{
				Log.LogWarning("Couldn't find " + item.name + "'s damage, it stays vanilla.");
			}
		}

		private static bool TryFindCountLocal(Collection<Instruction> instrs, int call, out int local)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			local = -1;
			int num = call - 1;
			int num2 = default(int);
			if (num >= 0 && ILPatternMatchingExt.MatchLdloc(instrs[num], ref num2))
			{
				num = -1;
				int num3 = call - 2;
				while (num3 >= 0 && num3 >= call - 30)
				{
					if (ILPatternMatchingExt.MatchStloc(instrs[num3], num2))
					{
						num = num3 - 1;
						break;
					}
					num3--;
				}
			}
			float num4 = default(float);
			if (num >= 3 && instrs[num].OpCode == OpCodes.Mul && instrs[num - 1].OpCode == OpCodes.Conv_R4 && ILPatternMatchingExt.MatchLdloc(instrs[num - 2], ref local))
			{
				return ILPatternMatchingExt.MatchLdcR4(instrs[num - 3], ref num4);
			}
			return false;
		}

		private static void PatchLuminousShot(ILContext il, int countStore, int countLocal, HashSet<ProcItem> patched)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			ILCursor val = new ILCursor(il);
			val.Goto(countStore + 1, (MoveType)0, false);
			int num = default(int);
			if (val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1]
			{
				(Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, 1.75f)
			}) && val.TryGotoNext(new Func<Instruction, bool>[1]
			{
				(Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref num)
			}))
			{
				val.Emit(OpCodes.Ldarg_1);
				val.Emit(OpCodes.Ldloc_0);
				val.Emit(OpCodes.Ldloc, countLocal);
				val.Emit(OpCodes.Call, (MethodBase)typeof(ProcItems).GetMethod("LuminousShot"));
				patched.Add(ProcItem.LuminousShot);
			}
		}

		private static void WyrmOnHitBehavior_TryFire(ILContext il)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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);
			if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[6]
			{
				(Instruction x) => ILPatternMatchingExt.MatchLdcR4(x, 5f),
				(Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0),
				(Instruction x) => ILPatternMatchingExt.MatchLdfld<BaseItemBodyBehavior>(x, "stack"),
				(Instruction x) => x.OpCode == OpCodes.Conv_R4,
				(Instruction x) => x.OpCode == OpCodes.Mul,
				(Instruction x) => x.OpCode == OpCodes.Mul
			}))
			{
				Log.LogWarning("Couldn't find " + Get(ProcItem.SauteedWorms).name + "' damage, it stays vanilla.");
				return;
			}
			val.Emit(OpCodes.Ldarg_1);
			val.Emit(OpCodes.Ldind_Ref);
			val.Emit(OpCodes.Ldarg_0);
			val.Emit(OpCodes.Call, (MethodBase)typeof(ProcItems).GetMethod("SauteedWorms"));
		}
	}
}