Decompiled source of CurseCatcher v0.3.2

CurseCatcher.dll

Decompiled 5 months ago
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using CurseCatcher.Properties;
using HG;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 CurseCatcher.Properties
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class Resources
	{
		private static ResourceManager resourceMan;

		private static CultureInfo resourceCulture;

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static ResourceManager ResourceManager
		{
			get
			{
				if (resourceMan == null)
				{
					resourceMan = new ResourceManager("CurseCatcher.Properties.Resources", typeof(Resources).Assembly);
				}
				return resourceMan;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static CultureInfo Culture
		{
			get
			{
				return resourceCulture;
			}
			set
			{
				resourceCulture = value;
			}
		}

		internal static byte[] disabled => (byte[])ResourceManager.GetObject("disabled", resourceCulture);

		internal static byte[] enabled => (byte[])ResourceManager.GetObject("enabled", resourceCulture);

		internal Resources()
		{
		}
	}
}
namespace Local.Eclipse.CurseCatcher
{
	internal static class Artifact
	{
		private static ArtifactDef definition = null;

		private static int ruleIndex = -1;

		[HarmonyPatch(typeof(ArtifactCatalog), "SetArtifactDefs")]
		[HarmonyPostfix]
		private static void CreateArtifact()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			definition = new ArtifactDef
			{
				nameToken = "Artifact of Infliction",
				descriptionToken = "Only enemies inflict permanent damage.",
				smallIconSelectedSprite = Load(Resources.enabled),
				smallIconDeselectedSprite = Load(Resources.disabled),
				artifactIndex = (ArtifactIndex)ArtifactCatalog.artifactDefs.Length,
				cachedName = "Curse"
			};
			ArrayUtils.ArrayAppend<ArtifactDef>(ref ArtifactCatalog.artifactDefs, ref definition);
			Harmony instance = null;
			RunArtifactManager.onArtifactEnabledGlobal += (ArtifactStateChangeDelegate)delegate(RunArtifactManager _, ArtifactDef artifact)
			{
				if (instance == null && NetworkServer.active && (Object)(object)artifact == (Object)(object)definition)
				{
					instance = Harmony.CreateAndPatchAll(Plugin.instance.GetType(), (string)null);
				}
			};
			RunArtifactManager.onArtifactDisabledGlobal += (ArtifactStateChangeDelegate)delegate(RunArtifactManager _, ArtifactDef artifact)
			{
				if ((Object)(object)artifact == (Object)(object)definition)
				{
					Harmony obj = instance;
					if (obj != null)
					{
						obj.UnpatchSelf();
					}
					instance = null;
				}
			};
		}

		private static Sprite Load(byte[] imageData)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(512, 512, (TextureFormat)5, 4, false);
			ImageConversion.LoadImage(val, imageData);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2((float)(((Texture)val).width / 2), (float)(((Texture)val).height / 2)));
		}

		[HarmonyPatch(typeof(RuleCatalog), "Init")]
		[HarmonyFinalizer]
		private static void AddArtifact(Exception __exception)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (__exception != null)
			{
				throw __exception;
			}
			RuleDef val = RuleDef.FromArtifact(definition.artifactIndex);
			ruleIndex = RuleCatalog.allRuleDefs.Count;
			val.globalIndex = ruleIndex;
			RuleCatalog.allRuleDefs.Add(val);
			RuleCatalog.ruleDefsByGlobalName[val.globalName] = val;
			val.category = RuleCatalog.artifactRuleCategory;
			RuleCatalog.artifactRuleCategory.children.Add(val);
			for (int i = 0; i < val.choices.Count; i++)
			{
				RuleChoiceDef val2 = val.choices[i];
				val2.localIndex = i;
				val2.globalIndex = RuleCatalog.allChoicesDefs.Count;
				RuleCatalog.allChoicesDefs.Add(val2);
				RuleCatalog.ruleChoiceDefsByGlobalName[val2.globalName] = val2;
			}
		}

		[HarmonyPatch(typeof(RuleCatalog), "AddRule")]
		[HarmonyPrefix]
		private static bool SkipRule(RuleDef ruleDef)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			foreach (RuleChoiceDef choice in ruleDef.choices)
			{
				if (choice.artifactIndex == definition.artifactIndex)
				{
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(EclipseRun), "OverrideRuleChoices")]
		[HarmonyPostfix]
		private static void ShowInEclipse(RuleChoiceMask mustInclude, RuleChoiceMask mustExclude)
		{
			foreach (RuleChoiceDef item in (IEnumerable<RuleChoiceDef>)((ruleIndex >= 0) ? RuleCatalog.GetRuleDef(ruleIndex).choices : new List<RuleChoiceDef>()))
			{
				((SerializableBitArray)mustInclude)[item.globalIndex] = false;
				((SerializableBitArray)mustExclude)[item.globalIndex] = false;
			}
		}

		[HarmonyPatch(typeof(NetworkExtensions), "Write", new Type[]
		{
			typeof(NetworkWriter),
			typeof(RuleBook)
		})]
		[HarmonyPrefix]
		private static bool WriteRuleBook(NetworkWriter writer, RuleBook src)
		{
			for (int i = 0; i < src.ruleValues.Length; i++)
			{
				if (i != ruleIndex)
				{
					writer.Write(src.ruleValues[i]);
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(NetworkExtensions), "ReadRuleBook")]
		[HarmonyPrefix]
		private static bool ReadRuleBook(NetworkReader reader, RuleBook dest)
		{
			for (int i = 0; i < dest.ruleValues.Length; i++)
			{
				if (i == ruleIndex)
				{
					dest.ruleValues[i] = (byte)RuleCatalog.GetRuleDef(ruleIndex).defaultChoiceIndex;
				}
				else
				{
					dest.ruleValues[i] = reader.ReadByte();
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(RunArtifactManager), "OnDeserialize")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> LimitReadLength(IEnumerable<CodeInstruction> IL)
		{
			MethodInfo readBitArray = typeof(NetworkExtensions).GetMethod("ReadBitArray", new Type[2]
			{
				typeof(NetworkReader),
				typeof(bool[])
			});
			foreach (CodeInstruction item in IL)
			{
				if (CodeInstructionExtensions.Calls(item, readBitArray))
				{
					yield return Transpilers.EmitDelegate<Action<NetworkReader, bool[]>>((Action<NetworkReader, bool[]>)delegate(NetworkReader input, bool[] array)
					{
						if (definition == null)
						{
							NetworkExtensions.ReadBitArray(input, array);
						}
						else
						{
							NetworkExtensions.ReadBitArray(input, array, array.Length - 1);
						}
					});
				}
				else
				{
					yield return item;
				}
			}
		}
	}
	[BepInPlugin("local.eclipse.cursecatcher", "CurseCatcher", "0.3.2")]
	internal class Plugin : BaseUnityPlugin
	{
		internal interface IPatch
		{
			object GetDamageType(DamageInfo info);
		}

		private class Hopoo : IPatch
		{
			[HarmonyPatch(typeof(HealthComponent), "TakeDamage")]
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> IL)
			{
				return InsertCodeInstruction(IL);
			}

			public object GetDamageType(DamageInfo info)
			{
				return typeof(DamageInfo).GetField("damageType").GetValue(info);
			}
		}

		private class Gearbox : IPatch
		{
			[HarmonyPatch(typeof(HealthComponent), "TakeDamageProcess")]
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> IL)
			{
				return InsertCodeInstruction(IL);
			}

			public object GetDamageType(DamageInfo info)
			{
				//IL_0006: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Expected I4, but got Unknown
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				DamageType damageType = info.damageType.damageType;
				if ((int)damageType != 0)
				{
					return damageType;
				}
				return (uint)(int)info.damageType.damageTypeExtended;
			}
		}

		public const string identifier = "local.eclipse.cursecatcher";

		public const string version = "0.3.2";

		private static bool artifact;

		private static ConfigEntry<bool> self;

		private static ConfigEntry<bool> friendly;

		private static ConfigEntry<bool> fall;

		private static ConfigEntry<bool> interactable;

		private static ConfigEntry<bool> hazard;

		private static ConfigEntry<bool> log;

		internal static IPatch instance;

		protected void Awake()
		{
			self = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Self Damage", false, "Abilities that cost health to activate will curse the player if this setting is enabled.");
			friendly = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Friendly Fire", true, "Effects that damage allies may apply curse when enabled.");
			fall = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Fall Damage", true, "Inflict curse upon taking sufficient fall damage.");
			interactable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Interactable Cost", true, "Whether the debuff is triggered upon paying health costs.");
			hazard = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Neutral Hazards", true, "If neutral damage, such as stage hazards, can cause curse.");
			artifact = ((BaseUnityPlugin)this).Config.Bind<bool>("Other", "Artifact Enabled", false, "Used to toggle the above functionality. Selectable in the Eclipse lobby, but also applies the modifier to other game modes.").Value;
			log = ((BaseUnityPlugin)this).Config.Bind<bool>("Other", "Detailed Logging", false, "For troubleshooting purposes only.");
			if (artifact)
			{
				Harmony.CreateAndPatchAll(typeof(Artifact), (string)null);
				RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(CheckVersion));
				return;
			}
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, (Action)delegate
			{
				CheckVersion();
				Harmony.CreateAndPatchAll(instance.GetType(), (string)null);
			});
		}

		private void CheckVersion()
		{
			IPatch patch2;
			if (!Version.TryParse(RoR2Application.GetBuildId(), out Version result) || !(result < new Version(1, 3)))
			{
				IPatch patch = new Gearbox();
				patch2 = patch;
			}
			else
			{
				IPatch patch = new Hopoo();
				patch2 = patch;
			}
			instance = patch2;
		}

		private static IEnumerable<CodeInstruction> InsertCodeInstruction(IEnumerable<CodeInstruction> IL)
		{
			MethodInfo getSelectedDifficulty = typeof(Run).GetProperty("selectedDifficulty").GetMethod;
			bool found = false;
			CodeInstruction previousInstruction = null;
			Console.WriteLine("Installing hook for 'Eclipse 8' curse...");
			Label? label = default(Label?);
			foreach (CodeInstruction instruction in IL)
			{
				if (found && CodeInstructionExtensions.Branches(instruction, ref label))
				{
					found = false;
					if (CodeInstructionExtensions.LoadsConstant(previousInstruction, (Enum)(object)(DifficultyIndex)10))
					{
						Console.WriteLine("...instruction sequence found.");
						yield return new CodeInstruction(OpCodes.Pop, (object)null);
						yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null);
						yield return CodeInstruction.Call(typeof(Plugin), "ApplyCurse", (Type[])null, (Type[])null);
						yield return new CodeInstruction(OpCodes.Brfalse, instruction.operand);
						continue;
					}
				}
				else if (CodeInstructionExtensions.Calls(instruction, getSelectedDifficulty))
				{
					found = true;
				}
				previousInstruction = instruction;
				yield return instruction;
			}
		}

		public static bool ApplyCurse(DifficultyIndex difficulty, DamageInfo damageInfo)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_003f: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_00c0: 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)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Invalid comparison between Unknown and I4
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (!artifact && (int)difficulty < 10)
			{
				return false;
			}
			bool flag = true;
			if (log.Value)
			{
				PrintInfo(damageInfo);
			}
			if (damageInfo.attacker == null)
			{
				object damageType = instance.GetDamageType(damageInfo);
				if (damageType is DamageType val)
				{
					if ((int)val == 1 || (int)val == 3)
					{
						goto IL_006a;
					}
					if (((Enum)val).HasFlag((Enum)(object)(DamageType)2097152))
					{
						flag = fall.Value;
					}
				}
				else if (damageType is uint && (uint)damageType == 134217728)
				{
					goto IL_006a;
				}
			}
			else if (Object.op_Implicit((Object)(object)damageInfo.attacker))
			{
				TeamComponent component = damageInfo.attacker.GetComponent<TeamComponent>();
				if (Object.op_Implicit((Object)(object)component))
				{
					TeamIndex teamIndex = component.teamIndex;
					if ((int)teamIndex != 0)
					{
						if ((int)teamIndex == 1)
						{
							flag = friendly.Value;
						}
					}
					else
					{
						flag = hazard.Value;
					}
				}
				else if (damageInfo.attacker.GetComponent<IInteractable>() != null)
				{
					flag = interactable.Value;
				}
			}
			goto IL_0102;
			IL_0102:
			if (flag)
			{
				return true;
			}
			if (log.Value)
			{
				Console.WriteLine("...preventing curse.");
			}
			return false;
			IL_006a:
			flag = self.Value;
			goto IL_0102;
		}

		private static void PrintInfo(DamageInfo damage)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			string text = getName(damage.attacker);
			string text2 = getName(damage.inflictor);
			string text3 = "attacker: " + text;
			if (text2 != text && damage.inflictor != null)
			{
				text3 = text3 + " (" + text2 + ")";
			}
			if ((Object)(object)damage.attacker != (Object)null)
			{
				TeamComponent component = damage.attacker.GetComponent<TeamComponent>();
				if ((Object)(object)component != (Object)null)
				{
					string text4 = text3;
					TeamIndex teamIndex = component.teamIndex;
					text3 = text4 + ", team: " + ((object)(TeamIndex)(ref teamIndex)).ToString();
				}
			}
			string text5 = text3;
			DamageTypeCombo damageType = damage.damageType;
			text3 = text5 + ", type: " + ((object)(DamageTypeCombo)(ref damageType)).ToString();
			Console.WriteLine(text3);
			static string getName(GameObject obj)
			{
				string text6 = ((obj == null) ? "null" : (((Object)(object)obj == (Object)null) ? "destroyed" : ((((Object)obj).name != null) ? ((Object)obj).name : "unknown")));
				int num = text6.IndexOf('(');
				int num2 = text6.IndexOf(')') - num + 1;
				if (num >= 0 && num2 >= 0)
				{
					text6 = text6.Remove(num, num2);
				}
				return text6;
			}
		}
	}
}