using System;
using System.Collections;
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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Hypick.BetterShotgun.NetcodePatcher;
using LethalCompanyInputUtils.Api;
using LethalLib.Modules;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Hypick.BetterShotgun")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.4.2.0")]
[assembly: AssemblyInformationalVersion("1.4.2+e83b16fce6e800144c50ae54885cc0f78d0adfbf")]
[assembly: AssemblyProduct("BetterShotgun")]
[assembly: AssemblyTitle("Hypick.BetterShotgun")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[module: NetcodePatchedAssembly]
internal class <Module>
{
static <Module>()
{
}
}
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 Hypick
{
[BepInPlugin("Hypick.BetterShotgun", "BetterShotgun", "1.4.2")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public static PluginConfig Config;
private readonly Harmony _harmony = new Harmony("Hypick.BetterShotgun");
private static Keybinds InputActionsInstance = new Keybinds();
private static AnimationClip ShotgunInspectAnimation;
private static AudioClip ShotgunInspectSFX;
private bool _isLoaded;
private static Plugin Instance { get; set; }
public static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;
private List<Item> AllItems => Resources.FindObjectsOfTypeAll<Item>().Concat(Object.FindObjectsByType<Item>((FindObjectsInactive)1, (FindObjectsSortMode)1)).ToList();
private Item Shotgun => ((IEnumerable<Item>)AllItems).FirstOrDefault((Func<Item, bool>)((Item item) => ((Object)item).name == "Shotgun"));
private Item ShotgunShell => ((IEnumerable<Item>)AllItems).FirstOrDefault((Func<Item, bool>)((Item item) => ((Object)item).name == "GunAmmo"));
public Plugin()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Expected O, but got Unknown
Instance = this;
}
private void Awake()
{
Config = new PluginConfig(((BaseUnityPlugin)this).Config);
SceneManager.sceneLoaded += OnSceneLoaded;
Log.LogInfo((object)"Applying patches...");
_harmony.PatchAll();
Log.LogInfo((object)"Patches applied");
SetupKeybindCallbacks();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Hypick.BetterShotgun is fully loaded!");
}
public void SetupKeybindCallbacks()
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
InputActionSetupExtensions.AddBinding(InputActionsInstance.ReloadKey, "<keyboard>/" + Config.ReloadKeybind, (string)null, (string)null, (string)null);
if (Config.ReloadKeybind.Replace("<keyboard>/", "") != "e")
{
Log.LogInfo((object)("Start ReloadKeybind with key " + InputActionRebindingExtensions.GetBindingDisplayString(InputActionsInstance.ReloadKey, (DisplayStringOptions)0, (string)null)));
InputActionsInstance.ReloadKey.performed += OnReloadKeyPressed;
}
}
public void OnReloadKeyPressed(CallbackContext context)
{
if (!((CallbackContext)(ref context)).performed || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null)
{
return;
}
PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController;
if (!((NetworkBehaviour)localPlayerController).IsOwner)
{
return;
}
GrabbableObject val = localPlayerController.ItemSlots[localPlayerController.currentItemSlot];
if ((Object)(object)val != (Object)null)
{
ShotgunItem val2 = (ShotgunItem)(object)((val is ShotgunItem) ? val : null);
if (val2 != null && !val2.isReloading)
{
val2.StartReloadGun();
}
}
}
public void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (!_isLoaded && !(((Scene)(ref scene)).name != "MainMenu"))
{
_isLoaded = true;
RegisterItem(Shotgun, "Shotgun", Config.ShotgunMaxDiscount, Config.ShotgunMinValue, Config.ShotgunMaxValue, Config.ShotgunWeight, Config.ShotgunPrice, Config.ShotgunRarity);
RegisterItem(ShotgunShell, "Shell", Config.ShellMaxDiscount, Config.ShellMinValue, Config.ShellMaxValue, 0, Config.ShellPrice, Config.ShellRarity);
}
}
private static void RegisterItem(Item item, string name, int maxDiscount, int minValue, int maxValue, int weight, int price, int rarity)
{
item.itemName = name;
item.highestSalePercentage = maxDiscount;
item.minValue = Mathf.Max(minValue, 0) * 100 / 40;
item.maxValue = Mathf.Max(maxValue, item.minValue) * 100 / 40;
item.weight = ((weight <= 9) ? ((float)(weight + 100) / 100f) : ((float)(weight + 99) / 100f));
if (price != -1)
{
Items.RegisterShopItem(item, price);
Log.LogInfo((object)(item.itemName + " added to the store"));
}
if (rarity != -1)
{
Items.RegisterScrap(item, rarity, (LevelTypes)(-1));
Log.LogInfo((object)(item.itemName + " added as scrap"));
}
Log.LogInfo((object)("Loaded " + item.itemName));
}
}
public static class Category
{
public const string Shotgun = "1 >> Shotgun << 1";
public const string ShotgunTweaks = "3 >> Shotgun Tweaks << 3";
public const string Shell = "2 >> Shell << 2";
}
public class PluginConfig
{
public int ShotgunPrice { get; }
public int ShotgunMaxDiscount { get; }
public int ShotgunMinValue { get; }
public int ShotgunMaxValue { get; }
public int ShotgunWeight { get; }
public int ShotgunRarity { get; }
public bool MisfireOff { get; }
public bool InfiniteAmmo { get; }
public string ReloadKeybind { get; }
public bool ShowAmmoCount { get; }
public bool AmmoCheckAnimation { get; }
public bool ReloadNoLimit { get; }
public bool DisableFriendlyFire { get; }
public bool SkipReloadAnimation { get; }
public int ShellPrice { get; }
public int ShellMaxDiscount { get; }
public int ShellMinValue { get; }
public int ShellMaxValue { get; }
public int ShellRarity { get; }
public PluginConfig(ConfigFile cfg)
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Expected O, but got Unknown
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Expected O, but got Unknown
//IL_0248: Unknown result type (might be due to invalid IL or missing references)
//IL_0252: Expected O, but got Unknown
ShotgunPrice = cfg.Bind<int>("1 >> Shotgun << 1", "Price", 700, "Cost of a shotgun in a store. (-1 = remove from sale)").Value;
ShotgunMaxDiscount = cfg.Bind<int>("1 >> Shotgun << 1", "MaxDiscount", 80, new ConfigDescription("Maximum discount percentage in store (vanilla = 80)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 90), Array.Empty<object>())).Value;
ShotgunMinValue = cfg.Bind<int>("1 >> Shotgun << 1", "MinValueScrap", 40, "Minimum scrap value (must be >= 0) (In the game, the value is scaled down, so it is calculated using the formula value * 100 / 40)").Value;
ShotgunMaxValue = cfg.Bind<int>("1 >> Shotgun << 1", "MaxValueScrap", 70, "Maximum scrap value (must be >= min value) (In the game, the value is scaled down, so it is calculated using the formula value * 100 / 40)").Value;
ShotgunWeight = cfg.Bind<int>("1 >> Shotgun << 1", "Weight", 16, new ConfigDescription("[BETA] Scrap weight", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())).Value;
ShotgunRarity = cfg.Bind<int>("1 >> Shotgun << 1", "Rarity", -1, "Rarity of shotgun spawn on moons (higher = more common). A shotgun will also appear in gifts. (-1 = disable)").Value;
MisfireOff = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "MisfireOff", true, "If set to true, disables shotgun misfire (vanilla = false)").Value;
InfiniteAmmo = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "InfiniteAmmo", false, "If set to true, the shotgun will have infinite ammo").Value;
ReloadKeybind = cfg.Bind<string>("3 >> Shotgun Tweaks << 3", "ReloadKeybind", "R", "Changes the reload key to the one you specify (vanilla = E)").Value;
ShowAmmoCount = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "ShowAmmoCount", true, "If set to true, the number of cartridges in the shotgun will be displayed in the upper right text").Value;
AmmoCheckAnimation = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "AmmoCheckAnimation", false, "[BETA] Enables animation of checking cartridges in a shotgun on the reload key (Does not work with InfiniteAmmo = true)").Value;
ReloadNoLimit = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "ReloadNoLimit", false, "The shotgun can be loaded with an infinite number of cartridges").Value;
DisableFriendlyFire = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "DisableFriendlyFire", false, "Turns off friendly fire").Value;
SkipReloadAnimation = cfg.Bind<bool>("3 >> Shotgun Tweaks << 3", "SkipReloadAnimation", false, "Skips shotgun reload animation").Value;
ShellPrice = cfg.Bind<int>("2 >> Shell << 2", "Price", 50, "Cost of a shotgun shell in a store. (-1 = remove from sale)").Value;
ShellMaxDiscount = cfg.Bind<int>("2 >> Shell << 2", "MaxDiscount", 80, new ConfigDescription("Maximum discount percentage in store (vanilla = 80)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 90), Array.Empty<object>())).Value;
ShellMinValue = cfg.Bind<int>("2 >> Shell << 2", "MinValueScrap", 15, "Minimum scrap value (must be >= 0) (In the game, the value is scaled down, so it is calculated using the formula value * 100 / 40)").Value;
ShellMaxValue = cfg.Bind<int>("2 >> Shell << 2", "MaxValueScrap", 25, "Maximum scrap value (must be >= min value) (In the game, the value is scaled down, so it is calculated using the formula value * 100 / 40)").Value;
ShellRarity = cfg.Bind<int>("2 >> Shell << 2", "Rarity", 2, "Rarity of shotgun shell spawns on moons (higher = more common). Shell will also appear in gifts. (-1 = disable)").Value;
}
}
public class Keybinds : LcInputActions
{
public InputAction ReloadKey => ((LcInputActions)this).Asset["Reload"];
public override void CreateInputActions(in InputActionMapBuilder builder)
{
builder.NewActionBinding().WithActionId("Reload").WithActionType((InputActionType)1)
.WithBindingName("ReloadShotgunKey")
.Finish();
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "Hypick.BetterShotgun";
public const string PLUGIN_NAME = "BetterShotgun";
public const string PLUGIN_VERSION = "1.4.2";
}
}
namespace Hypick.Patches
{
[HarmonyPatch(typeof(ShotgunItem))]
internal class ShotgunItemPatch
{
[HarmonyPatch("Update")]
[HarmonyPrefix]
public static void Update(ShotgunItem __instance)
{
if (Plugin.Config.MisfireOff && !__instance.safetyOn)
{
__instance.hasHitGroundWithSafetyOff = true;
__instance.misfireTimer = float.MaxValue;
}
}
[HarmonyPatch("ItemActivate")]
[HarmonyPrefix]
public static void ItemActivate(ShotgunItem __instance)
{
if (Plugin.Config.InfiniteAmmo)
{
__instance.shellsLoaded = int.MaxValue;
}
}
[HarmonyPatch("SetControlTipsForItem")]
[HarmonyPrefix]
public static void SetControlTipsForItem(ShotgunItem __instance)
{
((GrabbableObject)__instance).itemProperties.toolTips[1] = GetCustomTooltip(__instance);
}
[HarmonyPatch("SetSafetyControlTip")]
[HarmonyPrefix]
public static void SetSafetyControlTip(ShotgunItem __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
HUDManager.Instance.ChangeControlTip(2, GetCustomTooltip(__instance), false);
}
}
[HarmonyPatch("ReloadGunEffectsClientRpc")]
[HarmonyPatch("ShootGun")]
[HarmonyPostfix]
public static void UpdateControlTipsForItem(ShotgunItem __instance)
{
if (Plugin.Config.ShowAmmoCount)
{
__instance.SetSafetyControlTip();
}
}
private static string GetCustomTooltip(ShotgunItem item)
{
string text = (Plugin.Config.AmmoCheckAnimation ? "Reload / Check" : "Reload");
if (!Plugin.Config.ShowAmmoCount)
{
return text + ": [" + Plugin.Config.ReloadKeybind.ToUpper() + "]";
}
string arg = (Plugin.Config.ReloadNoLimit ? "∞" : "2");
string text2 = (Plugin.Config.InfiniteAmmo ? "∞" : $"{item.shellsLoaded}/{arg}");
return text + " (" + text2 + "): [" + Plugin.Config.ReloadKeybind.ToUpper() + "]";
}
[HarmonyPatch("ShootGun")]
[HarmonyPrefix]
[HarmonyPriority(600)]
public static void ShootGunPrefix(ShotgunItem __instance, out int __state)
{
__state = __instance.shellsLoaded;
}
[HarmonyPatch("ShootGun")]
[HarmonyPostfix]
public static void ShootGunPostfix(ShotgunItem __instance, ref int __state)
{
__instance.shellsLoaded = Mathf.Max(0, __state - 1);
__instance.SetSafetyControlTip();
}
[HarmonyPatch("reloadGunAnimation")]
[HarmonyPrefix]
public static bool ReloadGunAnimation(ShotgunItem __instance, ref IEnumerator __result)
{
__result = ReloadGunAnimationCustom(__instance);
return false;
}
[HarmonyPatch("ShootGun")]
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
bool found = false;
foreach (CodeInstruction instruction in instructions)
{
if (Plugin.Config.DisableFriendlyFire && !found && ((object)instruction).ToString().Contains("playerHeldBy"))
{
found = true;
yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ShotgunItemPatch), "CheckFriendly", (Type[])null, (Type[])null));
yield return new CodeInstruction(OpCodes.Stloc_0, (object)null);
}
yield return instruction;
}
}
private static bool CheckFriendly(ShotgunItem __instance)
{
return (Object)(object)((GrabbableObject)__instance).playerHeldBy != (Object)null;
}
[HarmonyPatch("ItemInteractLeftRight")]
[HarmonyPrefix]
public static bool ItemInteractLeftRight(ShotgunItem __instance, bool right)
{
if (Plugin.Config.ReloadKeybind.ToLower() != "e" && right)
{
return false;
}
if (!Plugin.Config.ReloadNoLimit || !right || __instance.isReloading)
{
return true;
}
__instance.StartReloadGun();
return false;
}
[HarmonyPatch("StartReloadGun")]
[HarmonyPrefix]
public static bool StartReloadGun(ShotgunItem __instance)
{
if ((Plugin.Config.AmmoCheckAnimation && !__instance.ReloadedGun()) || (Plugin.Config.AmmoCheckAnimation && !Plugin.Config.ReloadNoLimit && !Plugin.Config.InfiniteAmmo && __instance.shellsLoaded >= 2))
{
if (__instance.gunCoroutine != null)
{
((MonoBehaviour)__instance).StopCoroutine(__instance.gunCoroutine);
}
__instance.gunCoroutine = ((MonoBehaviour)__instance).StartCoroutine(CheckAmmoAnimation(__instance));
return false;
}
if (Plugin.Config.InfiniteAmmo && __instance.ReloadedGun())
{
return false;
}
return ((NetworkBehaviour)__instance).IsOwner;
}
private static IEnumerator CheckAmmoAnimation(ShotgunItem __instance)
{
__instance.isReloading = true;
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun", true);
((Renderer)__instance.shotgunShellLeft).enabled = __instance.shellsLoaded > 0;
((Renderer)__instance.shotgunShellRight).enabled = __instance.shellsLoaded > 1;
yield return (object)new WaitForSeconds(0.3f);
__instance.gunAnimator.SetBool("Reloading", true);
__instance.ReloadGunEffectsServerRpc(true);
yield return (object)new WaitForSeconds(2.35f);
__instance.gunAnimator.SetBool("Reloading", false);
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun", false);
__instance.isReloading = false;
__instance.ReloadGunEffectsServerRpc(false);
}
private static IEnumerator ReloadGunAnimationCustom(ShotgunItem __instance)
{
if (!Plugin.Config.SkipReloadAnimation)
{
__instance.isReloading = true;
if (__instance.shellsLoaded <= 0)
{
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun", true);
((Renderer)__instance.shotgunShellLeft).enabled = false;
((Renderer)__instance.shotgunShellRight).enabled = false;
}
else
{
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun2", true);
((Renderer)__instance.shotgunShellRight).enabled = false;
}
yield return (object)new WaitForSeconds(0.3f);
__instance.gunAudio.PlayOneShot(__instance.gunReloadSFX);
__instance.gunAnimator.SetBool("Reloading", true);
__instance.ReloadGunEffectsServerRpc(true);
yield return (object)new WaitForSeconds(0.95f);
((Renderer)__instance.shotgunShellInHand).enabled = true;
__instance.shotgunShellInHandTransform.SetParent(((GrabbableObject)__instance).playerHeldBy.leftHandItemTarget);
__instance.shotgunShellInHandTransform.localPosition = new Vector3(-0.0555f, 0.1469f, -0.0655f);
__instance.shotgunShellInHandTransform.localEulerAngles = new Vector3(-1.956f, 143.856f, -16.427f);
yield return (object)new WaitForSeconds(0.95f);
((GrabbableObject)__instance).playerHeldBy.DestroyItemInSlotAndSync(__instance.ammoSlotToUse);
__instance.ammoSlotToUse = -1;
if (Plugin.Config.ReloadNoLimit)
{
__instance.shellsLoaded++;
}
else
{
__instance.shellsLoaded = Mathf.Clamp(__instance.shellsLoaded + 1, 0, 2);
}
((Renderer)__instance.shotgunShellLeft).enabled = true;
if (__instance.shellsLoaded >= 2)
{
((Renderer)__instance.shotgunShellRight).enabled = true;
}
((Renderer)__instance.shotgunShellInHand).enabled = false;
__instance.shotgunShellInHandTransform.SetParent(((Component)__instance).transform);
yield return (object)new WaitForSeconds(0.45f);
__instance.gunAudio.PlayOneShot(__instance.gunReloadFinishSFX);
__instance.gunAnimator.SetBool("Reloading", false);
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun", false);
((GrabbableObject)__instance).playerHeldBy.playerBodyAnimator.SetBool("ReloadShotgun2", false);
__instance.isReloading = false;
__instance.ReloadGunEffectsServerRpc(false);
}
else
{
Plugin.Log.LogInfo((object)"Skip reload animation");
__instance.isReloading = true;
((GrabbableObject)__instance).playerHeldBy.DestroyItemInSlotAndSync(__instance.ammoSlotToUse);
__instance.ammoSlotToUse = -1;
if (Plugin.Config.ReloadNoLimit)
{
__instance.shellsLoaded++;
}
else
{
__instance.shellsLoaded = Mathf.Clamp(__instance.shellsLoaded + 1, 0, 2);
}
((Renderer)__instance.shotgunShellLeft).enabled = true;
if (__instance.shellsLoaded >= 2)
{
((Renderer)__instance.shotgunShellRight).enabled = true;
}
((Renderer)__instance.shotgunShellInHand).enabled = false;
__instance.shotgunShellInHandTransform.SetParent(((Component)__instance).transform);
__instance.isReloading = false;
__instance.SetSafetyControlTip();
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}
namespace Hypick.BetterShotgun.NetcodePatcher
{
[AttributeUsage(AttributeTargets.Module)]
internal class NetcodePatchedAssemblyAttribute : Attribute
{
}
}