Decompiled source of GrappleHookSkill v1.0.1

GrappleHookSkill.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ConditionalConfigSync;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("GrappleHookSkill")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("GrappleHookSkill")]
[assembly: AssemblyTitle("GrappleHookSkill")]
[assembly: AssemblyVersion("1.0.1.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 GrappleHookSkill
{
	[BepInPlugin("com.dreich.grapplehookskill", "Grapple Hook Skill", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class GrappleHookSkillPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.grapplehookskill";

		public const string Name = "Grapple Hook Skill";

		public const string Version = "1.0.1";

		internal const string ConfigSyncGuid = "_shudnal.ConditionalConfigSync";

		internal static ManualLogSource Log;

		internal static bool ConfigSyncAvailable;

		private static ConfigEntry<bool> ConfigLockedEntry;

		private static ConfigEntry<bool> EnabledEntry;

		private static ConfigEntry<float> MinReloadMultiplierEntry;

		private static ConfigEntry<float> SkillGainPerShotEntry;

		internal static bool Enabled => EnabledEntry.Value;

		internal static float MinReloadMultiplier => MinReloadMultiplierEntry.Value;

		internal static float SkillGainPerShot => SkillGainPerShotEntry.Value;

		private void Awake()
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigSyncAvailable = Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.Initialize("com.dreich.grapplehookskill", "Grapple Hook Skill", "1.0.1");
			}
			else
			{
				Log.LogInfo((object)"ConditionalConfigSync not found; configuration will not be synced from the server.");
			}
			ConfigLockedEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock Configuration", true, "If on, the configuration is locked and can be changed by server admins only. Requires ConditionalConfigSync.");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddLockingConfigEntry(ConfigLockedEntry);
			}
			EnabledEntry = BindShared("General", "Enabled", defaultValue: true, "Turns the Grapple Hook skill on or off: skill gain and the faster reload.");
			MinReloadMultiplierEntry = BindShared("Reload", "MinReloadMultiplier", 0.5f, "Reload time multiplier at skill level 100. Level 0 is always the normal reload time. 0.5 matches the crossbow (half the time at level 100); 0.2 would match the bow's draw time.");
			SkillGainPerShotEntry = BindShared("Skill", "SkillGainPerShot", 1f, "Skill experience gained each time you fire the grappling hook. Higher levels faster.");
			new Harmony("com.dreich.grapplehookskill").PatchAll();
		}

		private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, string description)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description);
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddConfigEntry<T>(val, serverControlled: true);
			}
			return val;
		}
	}
	internal static class ConfigSyncCompat
	{
		private static object configSync;

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void Initialize(string guid, string name, string version)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			configSync = (object)new ConfigSync(guid)
			{
				DisplayName = name,
				CurrentVersion = version,
				MinimumRequiredVersion = version,
				ModRequired = false,
				ModRequirementMode = (ModRequirementMode)1
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddLockingConfigEntry(ConfigEntry<bool> entry)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddLockingConfigEntry<bool>(entry);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddConfigEntry<T>(ConfigEntry<T> entry, bool serverControlled)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddConfigEntry<T>(entry, (ConfigSyncMode)((!serverControlled) ? 2 : 0));
		}
	}
	internal static class GrappleSkill
	{
		public const string SkillName = "Grapple Hook";

		private const string SkillDescription = "Skill with the grappling hook. The higher your level, the faster you reload it.";

		public static readonly SkillType Type = (SkillType)StableId("com.dreich.grapplehookskill.GrappleHook");

		private static readonly Dictionary<string, bool> HookCache = new Dictionary<string, bool>();

		private static readonly MethodInfo AddWordMethod = typeof(Localization).GetMethod("AddWord", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
		{
			typeof(string),
			typeof(string)
		}, null);

		private static readonly FieldInfo TranslationsField = FindTranslationsField();

		private static bool warned;

		private static string WordKey => "skill_" + (int)Type;

		private static int StableId(string text)
		{
			int num = 5381;
			foreach (char c in text)
			{
				num = num * 33 + c;
			}
			return (num & 0x7FFFFFFF) | 0x100;
		}

		private static FieldInfo FindTranslationsField()
		{
			FieldInfo fieldInfo = null;
			FieldInfo[] fields = typeof(Localization).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo2 in fields)
			{
				if (!(fieldInfo2.FieldType != typeof(Dictionary<string, string>)))
				{
					if (fieldInfo2.Name == "m_translations")
					{
						return fieldInfo2;
					}
					if (fieldInfo == null)
					{
						fieldInfo = fieldInfo2;
					}
				}
			}
			return fieldInfo;
		}

		public static void EnsureWord()
		{
			try
			{
				Localization instance = Localization.instance;
				if (instance != null)
				{
					if (AddWordMethod != null)
					{
						AddWordMethod.Invoke(instance, new object[2] { WordKey, "Grapple Hook" });
					}
					else if (TranslationsField != null && TranslationsField.GetValue(instance) is Dictionary<string, string> dictionary)
					{
						dictionary[WordKey] = "Grapple Hook";
					}
					else if (!warned)
					{
						warned = true;
						GrappleHookSkillPlugin.Log.LogWarning((object)("Could not find a way to add the skill name; it will show as $" + WordKey + "."));
					}
				}
			}
			catch (Exception ex)
			{
				GrappleHookSkillPlugin.Log.LogWarning((object)("Could not add skill name: " + ex.Message));
			}
		}

		public static void Register(Skills skills)
		{
			//IL_002b: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_0072: Invalid comparison between Unknown and I4
			if ((Object)(object)skills == (Object)null || skills.m_skills == null)
			{
				return;
			}
			Sprite val = null;
			foreach (SkillDef skill in skills.m_skills)
			{
				if (skill.m_skill == Type)
				{
					EnsureWord();
					return;
				}
				if ((int)skill.m_skill == 14 && (Object)(object)skill.m_icon != (Object)null)
				{
					val = skill.m_icon;
				}
				else if ((Object)(object)val == (Object)null && (int)skill.m_skill == 8)
				{
					val = skill.m_icon;
				}
			}
			SkillDef val2 = new SkillDef();
			val2.m_skill = Type;
			val2.m_icon = val;
			val2.m_description = "Skill with the grappling hook. The higher your level, the faster you reload it.";
			val2.m_increseStep = 1f;
			skills.m_skills.Add(val2);
			EnsureWord();
		}

		public static bool IsGrappleHook(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			string key = item.m_shared.m_name ?? "";
			if (HookCache.TryGetValue(key, out var value))
			{
				return value;
			}
			value = Detect(item);
			HookCache[key] = value;
			return value;
		}

		private static bool Detect(ItemData item)
		{
			try
			{
				GameObject attackProjectile = item.m_shared.m_attack.m_attackProjectile;
				if ((Object)(object)attackProjectile != (Object)null)
				{
					Projectile component = attackProjectile.GetComponent<Projectile>();
					if ((Object)(object)component != (Object)null && (Object)(object)component.m_spawnOnHit != (Object)null && (Object)(object)component.m_spawnOnHit.GetComponent<GrapplingPoint>() != (Object)null)
					{
						return true;
					}
				}
			}
			catch (Exception)
			{
			}
			string name = item.m_shared.m_name;
			if (name != null)
			{
				return name.IndexOf("grapp", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "Awake")]
	internal static class SkillsAwakePatch
	{
		private static void Postfix(Skills __instance)
		{
			GrappleSkill.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Skills), "IsSkillValid")]
	internal static class IsSkillValidPatch
	{
		private static void Postfix(SkillType __0, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (__0 == GrappleSkill.Type)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(SkillsDialog), "Setup")]
	internal static class SkillsDialogPatch
	{
		private static void Prefix()
		{
			GrappleSkill.EnsureWord();
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class RaiseSkillPatch
	{
		private static void Prefix(SkillType __0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (__0 == GrappleSkill.Type)
			{
				GrappleSkill.EnsureWord();
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetWeaponLoadingTime")]
	internal static class ReloadTimePatch
	{
		private static void Postfix(ItemData __instance, ref float __result)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (GrappleHookSkillPlugin.Enabled && __instance.m_shared != null && __instance.m_shared.m_attack != null && __instance.m_shared.m_attack.m_requiresReload && GrappleSkill.IsGrappleHook(__instance))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null))
				{
					float reloadTime = __instance.m_shared.m_attack.m_reloadTime;
					float skillFactor = ((Character)localPlayer).GetSkillFactor(GrappleSkill.Type);
					__result = Mathf.Lerp(reloadTime, reloadTime * GrappleHookSkillPlugin.MinReloadMultiplier, skillFactor);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
	internal static class FirePatch
	{
		private static readonly FieldInfo CharacterField = AccessTools.Field(typeof(Attack), "m_character");

		private static readonly FieldInfo WeaponField = AccessTools.Field(typeof(Attack), "m_weapon");

		private static void Postfix(Attack __instance)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (GrappleHookSkillPlugin.Enabled && !(CharacterField == null) && !(WeaponField == null))
			{
				object? value = CharacterField.GetValue(__instance);
				Character val = (Character)((value is Character) ? value : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && GrappleSkill.IsGrappleHook((ItemData)/*isinst with value type is only supported in some contexts*/))
				{
					val.RaiseSkill(GrappleSkill.Type, GrappleHookSkillPlugin.SkillGainPerShot);
				}
			}
		}
	}
}