Decompiled source of SeamstressConfigurable v1.2.0

plugins/SeamstressConfigurable/SeamstressConfigurable.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LocalTweaks;
using Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SeamstressConfigurable")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+7160b64eeafe5fb80b3af84262d34aef175bf07a")]
[assembly: AssemblyProduct("SeamstressConfigurable")]
[assembly: AssemblyTitle("SeamstressConfigurable")]
[assembly: AssemblyVersion("1.2.0.0")]
[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 LocalTweaks
{
	internal static class PatchTools
	{
		internal static MethodInfo Method(string type, string name)
		{
			Type type2 = AccessTools.TypeByName(type);
			if (!(type2 == null))
			{
				return AccessTools.DeclaredMethod(type2, name, (Type[])null, (Type[])null) ?? throw new MissingMethodException(type, name);
			}
			throw new TypeLoadException(type);
		}

		internal static IEnumerable<CodeInstruction> ReplaceFloat(IEnumerable<CodeInstruction> source, float value, MethodInfo replacement)
		{
			List<CodeInstruction> list = source.ToList();
			List<CodeInstruction> list2 = list.Where((CodeInstruction x) => x.opcode == OpCodes.Ldc_R4 && x.operand is float num && Math.Abs(num - value) < 1E-06f).ToList();
			if (list2.Count != 1)
			{
				throw new InvalidOperationException($"Expected one {value} constant, found {list2.Count}.");
			}
			list2[0].opcode = OpCodes.Call;
			list2[0].operand = replacement;
			return list;
		}
	}
	internal static class TweakSettings
	{
		internal static ConfigEntry<int> Slider(ConfigFile file, string section, string name, int value, int min, int max, string description, string guid, string modName)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			ConfigEntry<int> obj = file.Bind<int>(section, name, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), Array.Empty<object>()));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(obj, new IntSliderConfig
			{
				min = min,
				max = max
			}), guid, modName);
			return obj;
		}

		internal static ConfigEntry<bool> Checkbox(ConfigFile file, string section, string name, bool value, string description, string guid, string modName)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			ConfigEntry<bool> obj = file.Bind<bool>(section, name, value, description);
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(obj), guid, modName);
			return obj;
		}
	}
}
namespace SeamstressConfigurable
{
	internal static class DamageSettings
	{
		private const string ConfigType = "SeamstressMod.Seamstress.Content.SeamstressConfig";

		private const string States = "SeamstressMod.Seamstress.SkillStates.";

		private const string Components = "SeamstressMod.Seamstress.Components.";

		private static readonly Harmony harmony = new Harmony("com.youssef.SeamstressConfigurable.damage");

		private static readonly ConfigEntry<int>[] values = new ConfigEntry<int>[10];

		private static readonly Dictionary<MethodBase, int> expectedReads = new Dictionary<MethodBase, int>();

		private static readonly Dictionary<string, int> fields = new Dictionary<string, int>
		{
			["trimDamageCoefficient"] = 0,
			["trimThirdDamageCoefficient"] = 1,
			["scissorSlashDamageCoefficient"] = 2,
			["flurryDamageCoefficient"] = 4,
			["telekinesisDamageCoefficient"] = 7,
			["scissorDamageCoefficient"] = 8
		};

		private static Type trim;

		private static Type flurry;

		private static Type scissorImpact;

		private static FieldInfo swingIndex;

		internal static void Install(ConfigFile config, ManualLogSource logger)
		{
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Expected O, but got Unknown
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Expected O, but got Unknown
			try
			{
				trim = AccessTools.TypeByName("SeamstressMod.Seamstress.SkillStates.Trim");
				flurry = AccessTools.TypeByName("SeamstressMod.Seamstress.SkillStates.Flurry");
				scissorImpact = AccessTools.TypeByName("SeamstressMod.Seamstress.Components.ScissorImpact");
				swingIndex = AccessTools.Field(trim, "swingIndex");
				if (trim == null || flurry == null || scissorImpact == null || swingIndex == null)
				{
					throw new TypeLoadException("The Seamstress attack types no longer match.");
				}
				Bind(config, 0, "Trim", "Claw damage percent", "trimDamageCoefficient", "Damage of each normal bare-hand hit.");
				Bind(config, 1, "Trim", "Third claw hit damage percent", "trimThirdDamageCoefficient", "Damage of the third bare-hand hit in the combo.");
				Bind(config, 2, "Trim", "Scissor damage percent", "scissorSlashDamageCoefficient", "Extra scissor hit damage when that hand has a scissor.");
				Bind(config, 3, "Trim", "Third scissor hit damage percent", "scissorSlashDamageCoefficient", "Extra scissor hit damage on the third attack, when both scissors are present.");
				Bind(config, 4, "Flurry", "Claw damage percent", "flurryDamageCoefficient", "Damage of each bare-hand hit.");
				Bind(config, 5, "Flurry", "Scissor damage percent", "scissorSlashDamageCoefficient", "Extra scissor hit damage when that hand has a scissor.");
				Bind(config, 6, "Clip", "Damage per snip percent", "clipDamageCoefficient", "Damage per snip. The two starting snips and extra snips from needles are unchanged.");
				Bind(config, 7, "Planar Manipulation", "Base damage percent", "telekinesisDamageCoefficient", "Base coefficient for held and thrown impacts. Needle, velocity and victim-health terms and caps are unchanged; zero does not remove separate health-based damage.");
				Bind(config, 8, "Skewer", "Initial hit damage percent", "scissorDamageCoefficient", "Damage of the impact blast when the fired scissor first lands.");
				Bind(config, 9, "Skewer", "Symbiotic pickup damage percent", "scissorPickupDamageCoefficient", "Damage of the slash when the scissor is picked up or detonates. Independent of the initial hit.");
				PatchRead("SeamstressMod.Seamstress.SkillStates.Trim", "OnEnter", 2);
				PatchRead("SeamstressMod.Seamstress.SkillStates.Flurry", "OnEnter", 1);
				PatchRead("SeamstressMod.Modules.BaseStates.BaseMeleeAttack", "FixedUpdate", 1);
				PatchRead("SeamstressMod.Seamstress.SkillStates.Telekinesis", "FixedUpdate", 4);
				PatchRead("SeamstressMod.Seamstress.Components.DetonateOnImpactThrownTelekinesis", "FixedUpdate", 2);
				PatchRead("SeamstressMod.Seamstress.Components.ScissorImpact", "OnProjectileImpact", 1);
				harmony.Patch((MethodBase)PatchTools.Method("SeamstressMod.Seamstress.SkillStates.Clip", "OnEnter"), new HarmonyMethod(typeof(DamageSettings), "ChangeClipDamage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(ProjectileExplosion), "DetonateServer", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DamageSettings), "ChangePickupDamage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				logger.LogInfo((object)"Primary, secondary and Skewer damage settings loaded.");
			}
			catch (Exception arg)
			{
				Uninstall();
				logger.LogError((object)$"Damage settings were not applied. Existing health and Retaliate settings are unaffected.\n{arg}");
			}
		}

		internal static void Uninstall()
		{
			harmony.UnpatchSelf();
			expectedReads.Clear();
		}

		private static void Bind(ConfigFile config, int index, string section, string name, string fieldName, string description)
		{
			int num = (int)Math.Round((float)((ConfigEntryBase)((AccessTools.Field(AccessTools.TypeByName("SeamstressMod.Seamstress.Content.SeamstressConfig"), fieldName)?.GetValue(null) as ConfigEntry<float>) ?? throw new MissingFieldException("SeamstressMod.Seamstress.Content.SeamstressConfig", fieldName))).DefaultValue * 100f);
			values[index] = TweakSettings.Slider(config, section, name, num, 0, Math.Max(2000, num), description + " Uses percent of base damage, not a bonus percent. Applies to the next attack or damage calculation.", "com.youssef.SeamstressConfigurable", "Seamstress Configurable");
		}

		private static void PatchRead(string type, string method, int count)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			MethodInfo methodInfo = PatchTools.Method(type, method);
			expectedReads.Add(methodInfo, count);
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DamageSettings), "ChangeDamageReads", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void ChangeClipDamage(ref float ___damageCoefficient)
		{
			___damageCoefficient = (float)values[6].Value / 100f;
		}

		private static float DamageValue(float original, int index, object state)
		{
			if (index == 2)
			{
				if (trim.IsInstanceOfType(state))
				{
					index = (((int)swingIndex.GetValue(state) == 2) ? 3 : 2);
				}
				else
				{
					if (!flurry.IsInstanceOfType(state))
					{
						return original;
					}
					index = 5;
				}
			}
			return (float)values[index].Value / 100f;
		}

		private static float PickupValue(float original, ProjectileExplosion explosion)
		{
			if (!Object.op_Implicit((Object)(object)((Component)explosion).GetComponent(scissorImpact)))
			{
				return original;
			}
			return (float)values[9].Value / 100f;
		}

		private static IEnumerable<CodeInstruction> ChangeDamageReads(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			for (int i = 0; i < list.Count - 1; i++)
			{
				if (!(list[i].opcode != OpCodes.Ldsfld) && list[i].operand is FieldInfo fieldInfo && !(fieldInfo.DeclaringType?.FullName != "SeamstressMod.Seamstress.Content.SeamstressConfig") && fields.TryGetValue(fieldInfo.Name, out var value) && list[i + 1].operand is MethodInfo methodInfo && !(methodInfo.Name != "get_Value") && !(methodInfo.ReturnType != typeof(float)))
				{
					list.InsertRange(i + 2, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3]
					{
						new CodeInstruction(OpCodes.Ldc_I4, (object)value),
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(DamageSettings), "DamageValue", (Type[])null, (Type[])null))
					});
					i += 4;
					num++;
				}
			}
			if (!expectedReads.TryGetValue(__originalMethod, out var value2) || num != value2)
			{
				throw new InvalidOperationException($"Unexpected damage reads in {__originalMethod.DeclaringType?.Name}.{__originalMethod.Name}: {num}.");
			}
			return list;
		}

		private static IEnumerable<CodeInstruction> ChangePickupDamage(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			for (int i = 0; i < list.Count - 2; i++)
			{
				if (!(list[i].opcode != OpCodes.Ldfld) && list[i].operand is FieldInfo fieldInfo && !(fieldInfo.DeclaringType != typeof(ProjectileExplosion)) && !(fieldInfo.Name != "blastDamageCoefficient") && !(list[i + 1].opcode != OpCodes.Mul) && !(list[i + 2].opcode != OpCodes.Stfld) && list[i + 2].operand is FieldInfo fieldInfo2 && !(fieldInfo2.Name != "baseDamage"))
				{
					list.InsertRange(i + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(DamageSettings), "PickupValue", (Type[])null, (Type[])null))
					});
					i += 2;
					num++;
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException($"Expected one pickup damage calculation, found {num}.");
			}
			return list;
		}
	}
	[BepInPlugin("com.youssef.SeamstressConfigurable", "Seamstress Configurable", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.youssef.SeamstressConfigurable";

		public const string Name = "Seamstress Configurable";

		private static ConfigEntry<int> lifesteal;

		private static ConfigEntry<int> skewerCost;

		private static ConfigEntry<bool> cleanRetaliate;

		private static FieldInfo insatiable;

		private static FieldInfo needles;

		private static FieldInfo selfBleed;

		private readonly Harmony harmony = new Harmony("com.youssef.SeamstressConfigurable");

		private void Awake()
		{
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected O, but got Unknown
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Expected O, but got Unknown
			lifesteal = TweakSettings.Slider(((BaseUnityPlugin)this).Config, "It Hungers", "Lifesteal percent", 15, 1, 25, "Maximum lifesteal coefficient. Still scales with missing health and the hit's proc coefficient. Applies immediately.", "com.youssef.SeamstressConfigurable", "Seamstress Configurable");
			cleanRetaliate = TweakSettings.Checkbox(((BaseUnityPlugin)this).Config, "Retaliate", "No self bleed or needle gain", value: true, "While Retaliate's Insatiable is active, prevent its self-bleed damage and needle gains. Keep barrier conversion, duration and activation unchanged. Applies immediately.", "com.youssef.SeamstressConfigurable", "Seamstress Configurable");
			skewerCost = TweakSettings.Slider(((BaseUnityPlugin)this).Config, "Skewer", "Health cost percent", 15, 0, 25, "Percent of full combined health spent when firing Skewer. Applies on the next shot.", "com.youssef.SeamstressConfigurable", "Seamstress Configurable");
			try
			{
				Type type = AccessTools.TypeByName("SeamstressMod.Seamstress.Content.SeamstressBuffs");
				insatiable = AccessTools.Field(type, "SeamstressInsatiableBuff");
				needles = AccessTools.Field(type, "Needles");
				selfBleed = AccessTools.Field(AccessTools.TypeByName("SeamstressMod.Seamstress.Content.SeamstressDots"), "SeamstressSelfBleed");
				if (insatiable == null || needles == null || selfBleed == null)
				{
					throw new MissingFieldException("Seamstress buff or self-bleed fields were not found.");
				}
				harmony.Patch((MethodBase)PatchTools.Method("SeamstressMod.Seamstress.Components.SeamstressBaseDamageController", "ApplyLifesteal"), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ChangeLifesteal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)PatchTools.Method("SeamstressMod.Seamstress.SkillStates.FireScissor", "OnEnter"), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ChangeSkewerCost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(HealthComponent), "TakeDamage", new Type[1] { typeof(DamageInfo) }, (Type[])null), new HarmonyMethod(typeof(Plugin), "AllowDamage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)PatchTools.Method("SeamstressMod.Seamstress.SkillStates.HeartStandBy", "HandleBleed"), new HarmonyMethod(typeof(Plugin), "AllowHeartBleed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterBody), "AddBuff", new Type[1] { typeof(BuffDef) }, (Type[])null), new HarmonyMethod(typeof(Plugin), "AllowBuff", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Seamstress tweaks loaded.");
			}
			catch (Exception arg)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)$"Seamstress tweaks were not applied. The installed Seamstress version may have changed.\n{arg}");
			}
			DamageSettings.Install(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			SkewerTracking.Install(((BaseUnityPlugin)this).Logger);
		}

		private void OnDestroy()
		{
			SkewerTracking.Uninstall();
			DamageSettings.Uninstall();
			harmony.UnpatchSelf();
		}

		private static float LifestealFraction()
		{
			return (float)lifesteal.Value / 100f;
		}

		private static float SkewerFraction()
		{
			return (float)skewerCost.Value / 100f;
		}

		private static IEnumerable<CodeInstruction> ChangeLifesteal(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			List<int> list2 = new List<int>();
			for (int i = 0; i < list.Count - 1; i++)
			{
				if (list[i].opcode == OpCodes.Ldsfld && list[i].operand is FieldInfo { Name: "passiveLifeSteal" } && list[i + 1].operand is MethodInfo { Name: "get_Value" } methodInfo && methodInfo.ReturnType == typeof(float))
				{
					list2.Add(i);
				}
			}
			if (list2.Count != 1)
			{
				throw new InvalidOperationException("The It Hungers lifesteal read no longer matches.");
			}
			int num = list2[0];
			list[num].opcode = OpCodes.Nop;
			list[num].operand = null;
			list[num + 1].opcode = OpCodes.Call;
			list[num + 1].operand = AccessTools.Method(typeof(Plugin), "LifestealFraction", (Type[])null, (Type[])null);
			return list;
		}

		private static IEnumerable<CodeInstruction> ChangeSkewerCost(IEnumerable<CodeInstruction> instructions)
		{
			return PatchTools.ReplaceFloat(instructions, 0.15f, AccessTools.Method(typeof(Plugin), "SkewerFraction", (Type[])null, (Type[])null));
		}

		private static bool IsRetaliateInsatiable(CharacterBody body)
		{
			if (!cleanRetaliate.Value || !Object.op_Implicit((Object)(object)body))
			{
				return false;
			}
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.utility : null);
			object? value = insatiable.GetValue(null);
			BuffDef val2 = (BuffDef)((value is BuffDef) ? value : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.skillDef) && val.skillDef.skillNameToken == "KENKO_SEAMSTRESS_UTILITY_PARRY_NAME" && Object.op_Implicit((Object)(object)val2))
			{
				return body.HasBuff(val2);
			}
			return false;
		}

		private static bool AllowDamage(HealthComponent __instance, DamageInfo damageInfo)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (damageInfo.dotIndex == (DotIndex)selfBleed.GetValue(null))
			{
				return !IsRetaliateInsatiable(__instance.body);
			}
			return true;
		}

		private static bool AllowHeartBleed(CharacterBody ___ownerBody)
		{
			return !IsRetaliateInsatiable(___ownerBody);
		}

		private static bool AllowBuff(CharacterBody __instance, BuffDef buffDef)
		{
			object? value = needles.GetValue(null);
			if (!((Object)(object)buffDef != (Object)((value is BuffDef) ? value : null)))
			{
				return !IsRetaliateInsatiable(__instance);
			}
			return true;
		}
	}
	internal static class SkewerTracking
	{
		private static readonly Harmony harmony = new Harmony("com.youssef.SeamstressConfigurable.tracking");

		private static Type scissorImpact;

		internal static void Install(ManualLogSource logger)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			try
			{
				scissorImpact = AccessTools.TypeByName("SeamstressMod.Seamstress.Components.ScissorImpact");
				MethodInfo methodInfo = PatchTools.Method("SeamstressMod.Seamstress.Components.ScissorImpact", "TrySticking");
				if (methodInfo.ReturnType != typeof(bool))
				{
					throw new MissingMethodException("Skewer's sticking method no longer matches.");
				}
				harmony.Patch((MethodBase)AccessTools.Method(typeof(ProjectileController), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(SkewerTracking), "TrackEnemies", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SkewerTracking), "StopAfterLanding", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				logger.LogInfo((object)"Skewer projectile tracking loaded.");
			}
			catch (Exception arg)
			{
				harmony.UnpatchSelf();
				logger.LogError((object)$"Skewer tracking was not applied.\n{arg}");
			}
		}

		internal static void Uninstall()
		{
			harmony.UnpatchSelf();
		}

		private static void TrackEnemies(ProjectileController __instance)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_0114: 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_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || __instance.isPrediction || !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent(scissorImpact)))
			{
				return;
			}
			ProjectileDirectionalTargetFinder component = ((Component)__instance).GetComponent<ProjectileDirectionalTargetFinder>();
			ProjectileSteerTowardTarget component2 = ((Component)__instance).GetComponent<ProjectileSteerTowardTarget>();
			ProjectileTargetComponent component3 = ((Component)__instance).GetComponent<ProjectileTargetComponent>();
			if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component3))
			{
				component.lookRange = 60f;
				component.lookCone = 60f;
				component.targetSearchInterval = 0.5f;
				component.onlySearchIfNoTarget = true;
				component.allowTargetLoss = false;
				component.testLoS = true;
				component.ignoreAir = false;
				component.flierAltitudeTolerance = float.PositiveInfinity;
				((Behaviour)component).enabled = true;
				component2.yAxisOnly = false;
				component2.rotationSpeed = 250f;
				((Behaviour)component2).enabled = true;
				TeamMask allButNeutral = TeamMask.allButNeutral;
				((TeamMask)(ref allButNeutral)).RemoveTeam(TeamComponent.GetObjectTeam(__instance.owner));
				BullseyeSearch val = new BullseyeSearch
				{
					searchOrigin = ((Component)__instance).transform.position,
					searchDirection = ((Component)__instance).transform.forward,
					maxDistanceFilter = 60f,
					maxAngleFilter = 30f,
					teamMaskFilter = allButNeutral,
					filterByLoS = true,
					sortMode = (SortMode)2
				};
				val.RefreshCandidates();
				HurtBox val2 = val.GetResults().FirstOrDefault((Func<HurtBox, bool>)((HurtBox hit) => Object.op_Implicit((Object)(object)hit.healthComponent) && hit.healthComponent.alive));
				if (Object.op_Implicit((Object)(object)val2))
				{
					component3.target = ((Component)val2).transform;
				}
			}
		}

		private static void StopAfterLanding(Component __instance, bool __result)
		{
			if (NetworkServer.active && __result)
			{
				ProjectileSteerTowardTarget component = __instance.GetComponent<ProjectileSteerTowardTarget>();
				if (Object.op_Implicit((Object)(object)component))
				{
					((Behaviour)component).enabled = false;
				}
			}
		}
	}
}