Decompiled source of WeeWeeShotgunTweaks v1.0.4

BepInEx/plugins/WeeWeeShotgunTweaks/WeeWeeShotgunTweaks.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("WeeWeeShotgunTweaks")]
[assembly: AssemblyDescription("Lethal Company shotgun and friendly-fire tweaks")]
[assembly: AssemblyCompany("WeeWee")]
[assembly: AssemblyProduct("WeeWeeShotgunTweaks")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyVersion("1.0.4.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 WeeWeeShotgunTweaks
{
	[BepInPlugin("LilWeeWee.WeeWeeShotgunTweaks", "Wee Wee Shotgun Tweaks", "1.0.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "LilWeeWee.WeeWeeShotgunTweaks";

		public const string PluginName = "Wee Wee Shotgun Tweaks";

		public const string PluginVersion = "1.0.4";

		internal static ModConfig Settings { get; private set; }

		private void Awake()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			new Harmony("LilWeeWee.WeeWeeShotgunTweaks").PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Wee Wee Shotgun Tweaks 1.0.4 loaded.");
		}

		private void Update()
		{
			TemporaryRootClear.Tick();
		}
	}
	internal sealed class ModConfig
	{
		internal ConfigEntry<bool> InfiniteShotgunAmmo { get; }

		internal ConfigEntry<bool> DisableShotgunMisfire { get; }

		internal ConfigEntry<bool> DisableIncomingFriendlyFire { get; }

		internal ConfigEntry<bool> DisableOutgoingFriendlyFire { get; }

		internal ConfigEntry<int> ShotgunDamage { get; }

		internal ConfigEntry<int> ShovelDamage { get; }

		internal ConfigEntry<bool> ClearCadaverRoots { get; }

		internal ConfigEntry<float> RootClearDuration { get; }

		internal ConfigEntry<bool> ClearSpiderWebs { get; }

		internal ConfigEntry<float> SpiderWebClearRange { get; }

		internal ConfigEntry<float> SpiderWebClearAngle { get; }

		internal ModConfig(ConfigFile config)
		{
			InfiniteShotgunAmmo = config.Bind<bool>("Shotgun", "InfiniteShotgunAmmo", true, "Prevents player-held shotguns from consuming shells.");
			DisableShotgunMisfire = config.Bind<bool>("Shotgun", "DisableShotgunMisfire", true, "Prevents dropped player shotguns from firing by themselves.");
			ShotgunDamage = config.Bind<int>("Shotgun", "ShotgunDamage", -1, "Custom shotgun damage. -1 keeps the game's normal damage value.");
			DisableIncomingFriendlyFire = config.Bind<bool>("Friendly Fire", "DisableIncomingFriendlyFire", true, "Blocks damage dealt to you by another player.");
			DisableOutgoingFriendlyFire = config.Bind<bool>("Friendly Fire", "DisableOutgoingFriendlyFire", true, "Blocks your weapon damage against other players.");
			ShovelDamage = config.Bind<int>("Shovel", "ShovelDamage", 2, "Damage force used by shovels and signs. Vanilla is normally 1.");
			ClearCadaverRoots = config.Bind<bool>("Cadaver Growth", "TemporarilyClearRoots", true, "Player-fired shotgun blasts temporarily hide Cadaver Growth roots so players can see through them.");
			RootClearDuration = config.Bind<float>("Cadaver Growth", "RootClearDuration", 5f, "Seconds Cadaver Growth roots stay visually cleared after a player fires a shotgun.");
			ClearSpiderWebs = config.Bind<bool>("Spider Webs", "ShotgunsClearSpiderWebs", true, "Allows player-fired shotgun blasts to permanently break spider webs in their path.");
			SpiderWebClearRange = config.Bind<float>("Spider Webs", "SpiderWebClearRange", 15f, "Maximum distance in metres at which a shotgun blast can break spider webs.");
			SpiderWebClearAngle = config.Bind<float>("Spider Webs", "SpiderWebClearAngle", 70f, "Total forward cone angle in degrees used when detecting spider webs hit by a shotgun blast.");
		}
	}
	internal static class SpiderWebClear
	{
		internal static void TryClear(ShotgunItem shotgun, Vector3 shotPosition, Vector3 shotForward)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Settings.ClearSpiderWebs.Value || (Object)(object)shotgun == (Object)null || ((GrabbableObject)shotgun).isHeldByEnemy)
			{
				return;
			}
			PlayerControllerB localPlayer = Helpers.LocalPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)((GrabbableObject)shotgun).playerHeldBy != (Object)(object)localPlayer)
			{
				return;
			}
			float num = Mathf.Clamp(Plugin.Settings.SpiderWebClearRange.Value, 1f, 50f);
			float num2 = Mathf.Clamp(Plugin.Settings.SpiderWebClearAngle.Value, 5f, 180f) * 0.5f;
			SandSpiderWebTrap[] array = Object.FindObjectsOfType<SandSpiderWebTrap>();
			foreach (SandSpiderWebTrap val in array)
			{
				if (!((Object)(object)val == (Object)null) && !val.webHasBeenBroken)
				{
					Vector3 val2 = (((Object)(object)val.centerOfWeb != (Object)null) ? val.centerOfWeb.position : ((Component)val).transform.position) - shotPosition;
					float magnitude = ((Vector3)(ref val2)).magnitude;
					if (!(magnitude > num) && !(magnitude <= 0.01f) && !(Vector3.Angle(shotForward, val2) > num2))
					{
						val.Hit(1, ((Vector3)(ref val2)).normalized, localPlayer, true, -1);
					}
				}
			}
		}
	}
	internal static class TemporaryRootClear
	{
		private static readonly Dictionary<BatchAllMeshChildren, bool> BatchStates = new Dictionary<BatchAllMeshChildren, bool>();

		private static readonly Dictionary<MeshRenderer, bool> RendererStates = new Dictionary<MeshRenderer, bool>();

		private static float _clearUntil;

		private static float _nextRefresh;

		private static bool _active;

		internal static void Trigger()
		{
			if (Plugin.Settings.ClearCadaverRoots.Value)
			{
				float num = Mathf.Clamp(Plugin.Settings.RootClearDuration.Value, 0.25f, 60f);
				_clearUntil = Mathf.Max(_clearUntil, Time.time + num);
				_active = true;
				HideCurrentRoots();
			}
		}

		internal static void Tick()
		{
			if (_active)
			{
				if (Time.time >= _clearUntil)
				{
					RestoreRoots();
				}
				else if (Time.time >= _nextRefresh)
				{
					_nextRefresh = Time.time + 0.25f;
					HideCurrentRoots();
				}
			}
		}

		private static void HideCurrentRoots()
		{
			CadaverGrowthAI[] array = Object.FindObjectsOfType<CadaverGrowthAI>();
			foreach (CadaverGrowthAI val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (val.plantBatchers != null)
				{
					BatchAllMeshChildren[] plantBatchers = val.plantBatchers;
					foreach (BatchAllMeshChildren val2 in plantBatchers)
					{
						if (!((Object)(object)val2 == (Object)null))
						{
							if (!BatchStates.ContainsKey(val2))
							{
								BatchStates.Add(val2, ((Behaviour)val2).enabled);
							}
							((Behaviour)val2).enabled = false;
						}
					}
				}
				if (val.GrowthTiles == null)
				{
					continue;
				}
				foreach (TileWithGrowth growthTile in val.GrowthTiles)
				{
					if (growthTile == null || (Object)(object)growthTile.plantsContainer == (Object)null)
					{
						continue;
					}
					MeshRenderer[] componentsInChildren = growthTile.plantsContainer.GetComponentsInChildren<MeshRenderer>(true);
					foreach (MeshRenderer val3 in componentsInChildren)
					{
						if (!((Object)(object)val3 == (Object)null))
						{
							if (!RendererStates.ContainsKey(val3))
							{
								RendererStates.Add(val3, ((Renderer)val3).enabled);
							}
							((Renderer)val3).enabled = false;
						}
					}
				}
			}
		}

		private static void RestoreRoots()
		{
			foreach (KeyValuePair<BatchAllMeshChildren, bool> batchState in BatchStates)
			{
				if ((Object)(object)batchState.Key != (Object)null)
				{
					((Behaviour)batchState.Key).enabled = batchState.Value;
				}
			}
			foreach (KeyValuePair<MeshRenderer, bool> rendererState in RendererStates)
			{
				if ((Object)(object)rendererState.Key != (Object)null)
				{
					((Renderer)rendererState.Key).enabled = rendererState.Value;
				}
			}
			BatchStates.Clear();
			RendererStates.Clear();
			_active = false;
			_clearUntil = 0f;
			_nextRefresh = 0f;
		}
	}
	internal static class Helpers
	{
		[ThreadStatic]
		private static int _enemyShotDepth;

		internal static PlayerControllerB LocalPlayer
		{
			get
			{
				if (!((Object)(object)GameNetworkManager.Instance != (Object)null))
				{
					return null;
				}
				return GameNetworkManager.Instance.localPlayerController;
			}
		}

		internal static bool EnemyShotInProgress => _enemyShotDepth > 0;

		internal static void BeginEnemyShot(ShotgunItem shotgun)
		{
			if ((Object)(object)shotgun != (Object)null && ((GrabbableObject)shotgun).isHeldByEnemy)
			{
				_enemyShotDepth++;
			}
		}

		internal static void EndEnemyShot(ShotgunItem shotgun)
		{
			if ((Object)(object)shotgun != (Object)null && ((GrabbableObject)shotgun).isHeldByEnemy && _enemyShotDepth > 0)
			{
				_enemyShotDepth--;
			}
		}

		internal static bool IsWeaponCause(CauseOfDeath cause)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)cause != 1)
			{
				return (int)cause == 7;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ShotgunItem), "ShootGun")]
	internal static class ShotgunShootPatch
	{
		[HarmonyPrefix]
		private static bool PreventDroppedMisfire(ShotgunItem __instance)
		{
			Helpers.BeginEnemyShot(__instance);
			if (!Plugin.Settings.DisableShotgunMisfire.Value)
			{
				return true;
			}
			if (!((GrabbableObject)__instance).isHeld)
			{
				return ((GrabbableObject)__instance).isHeldByEnemy;
			}
			return true;
		}

		[HarmonyFinalizer]
		private static void FinishEnemyShot(ShotgunItem __instance)
		{
			Helpers.EndEnemyShot(__instance);
		}

		[HarmonyPostfix]
		private static void ApplyShotgunEnvironmentEffects(ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward)
		{
			//IL_001f: 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)
			if ((Object)(object)__instance != (Object)null && ((GrabbableObject)__instance).isHeld && !((GrabbableObject)__instance).isHeldByEnemy)
			{
				TemporaryRootClear.Trigger();
				SpiderWebClear.TryClear(__instance, shotgunPosition, shotgunForward);
			}
		}

		internal static bool PreCheckFriendly(ShotgunItem shotgun)
		{
			if (!Plugin.Settings.DisableOutgoingFriendlyFire.Value)
			{
				return false;
			}
			return !((GrabbableObject)shotgun).isHeldByEnemy;
		}

		internal static int GetShotgunDamage(int originalDamage)
		{
			int value = Plugin.Settings.ShotgunDamage.Value;
			if (value >= 0)
			{
				return value;
			}
			return originalDamage;
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ShootGunTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			bool injectedFriendlyFireCheck = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (!injectedFriendlyFireCheck && instruction.opcode == OpCodes.Stloc_0)
				{
					yield return instruction;
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ShotgunShootPatch), "PreCheckFriendly", (Type[])null, (Type[])null));
					yield return new CodeInstruction(OpCodes.Stloc_0, (object)null);
					injectedFriendlyFireCheck = true;
				}
				else if (instruction.opcode == OpCodes.Ldc_I4_5)
				{
					yield return instruction;
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ShotgunShootPatch), "GetShotgunDamage", (Type[])null, (Type[])null));
				}
				else
				{
					yield return instruction;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ShotgunItem), "Update")]
	internal static class ShotgunUpdatePatch
	{
		[HarmonyPostfix]
		private static void UpdatePostfix(ShotgunItem __instance, ref float ___misfireTimer, ref bool ___hasHitGroundWithSafetyOff, ref int ___shellsLoaded)
		{
			if (!((Object)(object)__instance == (Object)null) && !((GrabbableObject)__instance).isHeldByEnemy)
			{
				if (Plugin.Settings.DisableShotgunMisfire.Value)
				{
					___misfireTimer = 30f;
					___hasHitGroundWithSafetyOff = false;
				}
				if (Plugin.Settings.InfiniteShotgunAmmo.Value && ___shellsLoaded < 2)
				{
					___shellsLoaded = 2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")]
	internal static class PlayerDamagePatch
	{
		[HarmonyPrefix]
		private static bool DamagePlayerPrefix(PlayerControllerB __instance, CauseOfDeath causeOfDeath, bool callRPC)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (Helpers.EnemyShotInProgress)
			{
				return true;
			}
			if (!Helpers.IsWeaponCause(causeOfDeath))
			{
				return true;
			}
			PlayerControllerB localPlayer = Helpers.LocalPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			if ((Object)(object)__instance == (Object)(object)localPlayer && Plugin.Settings.DisableIncomingFriendlyFire.Value)
			{
				return false;
			}
			if ((Object)(object)__instance != (Object)(object)localPlayer && callRPC && Plugin.Settings.DisableOutgoingFriendlyFire.Value)
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientClientRpc")]
	internal static class IncomingNetworkDamagePatch
	{
		[HarmonyPrefix]
		private static bool IncomingPrefix(PlayerControllerB __instance)
		{
			PlayerControllerB localPlayer = Helpers.LocalPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)localPlayer))
			{
				return !Plugin.Settings.DisableIncomingFriendlyFire.Value;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientServerRpc")]
	internal static class OutgoingNetworkDamagePatch
	{
		[HarmonyPrefix]
		private static bool OutgoingPrefix(int playerWhoHit)
		{
			PlayerControllerB localPlayer = Helpers.LocalPlayer;
			if ((Object)(object)localPlayer == (Object)null || !Plugin.Settings.DisableOutgoingFriendlyFire.Value)
			{
				return true;
			}
			return playerWhoHit != (int)localPlayer.playerClientId;
		}
	}
	[HarmonyPatch(typeof(Shovel), "HitShovel")]
	internal static class ShovelPatch
	{
		[HarmonyPrefix]
		private static void HitShovelPrefix(ref int ___shovelHitForce)
		{
			int value = Plugin.Settings.ShovelDamage.Value;
			if (value >= 0)
			{
				___shovelHitForce = value;
			}
		}
	}
}