using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
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 LethalParrying.Patches;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
[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 = "")]
[assembly: AssemblyCompany("Ryokune")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Lethal Company Parrying")]
[assembly: AssemblyFileVersion("1.0.31.0")]
[assembly: AssemblyInformationalVersion("1.0.31")]
[assembly: AssemblyProduct("LethalParrying")]
[assembly: AssemblyTitle("Ryokune.LethalParrying")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.31.0")]
[module: UnverifiableCode]
namespace LethalParrying
{
public class ParryNetworkHandler : NetworkBehaviour
{
}
[BepInPlugin("Ryokune.LethalParrying", "LethalParrying", "1.0.31")]
[BepInProcess("Lethal Company.exe")]
public class LethalParryBase : BaseUnityPlugin
{
public static LethalParryBase instance;
public static ManualLogSource logger;
public static bool serverModCheck = true;
public static bool stun = false;
internal static ConfigEntry<int> DropProbability;
internal static ConfigEntry<bool> Notify;
internal static ConfigEntry<bool> DisplayCooldown;
private readonly Harmony harmony = new Harmony("Ryokune.LethalParrying");
private void NetCodeWeaver()
{
Type[] types = Assembly.GetExecutingAssembly().GetTypes();
Type[] array = types;
foreach (Type type in array)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo[] array2 = methods;
foreach (MethodInfo methodInfo in array2)
{
object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false);
if (customAttributes.Length != 0)
{
methodInfo.Invoke(null, null);
}
}
}
}
private void Awake()
{
DropProbability = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Drop chance", 15, "Probability for how often you might drop your weapon when failing to parry or holding F.");
Notify = ((BaseUnityPlugin)this).Config.Bind<bool>("Screen Information", "Display Parry Notifications", true, "Enables/Disables screen notifications for parry information. (Will be removed when sounds and effects are added)");
DisplayCooldown = ((BaseUnityPlugin)this).Config.Bind<bool>("Screen Information", "Display Parry Cooldown (Notification)", true, "Will show you a notification if your parry is on cooldown. (Display Parry Notifications does not affect this.)");
if (DropProbability.Value < (int)((ConfigEntryBase)DropProbability).DefaultValue)
{
DropProbability.Value = (int)((ConfigEntryBase)DropProbability).DefaultValue;
}
NetCodeWeaver();
if ((Object)(object)instance == (Object)null)
{
instance = this;
logger = ((BaseUnityPlugin)this).Logger;
}
logger.LogInfo((object)"---[Only skill issue can kill you now..]---");
logger.LogInfo((object)"---[Plugin Ryokune.LethalParrying is loaded!]---");
logger.LogInfo((object)"---[Only skill issue can kill you now..]---");
harmony.PatchAll(typeof(LethalParryBase));
harmony.PatchAll(typeof(PlayerControllerBPatch));
harmony.PatchAll(typeof(ShovelPatch));
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "Ryokune.LethalParrying";
public const string PLUGIN_NAME = "LethalParrying";
public const string PLUGIN_VERSION = "1.0.31";
}
}
namespace LethalParrying.Patches
{
[HarmonyPatch(typeof(PlayerControllerB))]
internal class PlayerControllerBPatch
{
internal static bool isPerfectParryFrame = false;
internal static bool isSlow = false;
internal static float perfectParryWindow = 0.25f;
internal static float lastParryTime;
internal static float perfectParryCooldown = 2f;
internal static GrabbableObject currentItem;
internal static Shovel shovel = null;
internal static bool ParriedDeath = false;
internal static Coroutine currentCoroutine;
[HarmonyPatch("KillPlayer")]
[HarmonyPrefix]
private static bool KillPlayerPatch(PlayerControllerB __instance, CauseOfDeath causeOfDeath)
{
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Invalid comparison between Unknown and I4
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Invalid comparison between Unknown and I4
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Invalid comparison between Unknown and I4
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Invalid comparison between Unknown and I4
if (!LethalParryBase.serverModCheck)
{
return true;
}
if (!((NetworkBehaviour)__instance).IsOwner)
{
return false;
}
if (__instance.isPlayerDead)
{
return false;
}
if (!__instance.AllowPlayerDeath())
{
return false;
}
if (ParriedDeath)
{
LethalParryBase.logger.LogWarning((object)"Server called KillPlayer method again. Might have to patch this method properly.");
ParriedDeath = false;
return false;
}
if ((int)causeOfDeath == 0 || (int)causeOfDeath == 2 || (int)causeOfDeath == 10 || (int)causeOfDeath == 5 || (int)causeOfDeath == 9 || (Object)(object)shovel == (Object)null)
{
return true;
}
if (isPerfectParryFrame)
{
if (LethalParryBase.Notify.Value)
{
HUDManager.Instance.DisplayTip("Parried Death..", "Nothing can stop you.", true, false, "LC_Tip1");
}
HUDManager.Instance.ShakeCamera((ScreenShakeType)3);
__instance.DropAllHeldItems(true, false);
lastParryTime = 0f;
ParriedDeath = true;
return false;
}
return true;
}
[HarmonyPatch("DamagePlayer")]
[HarmonyPrefix]
private static bool DamagePlayerPatch(PlayerControllerB __instance, ref int damageNumber, CauseOfDeath causeOfDeath)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Invalid comparison between Unknown and I4
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Invalid comparison between Unknown and I4
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Invalid comparison between Unknown and I4
//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
if (!LethalParryBase.serverModCheck)
{
return true;
}
if (!((NetworkBehaviour)__instance).IsOwner)
{
return false;
}
if (__instance.isPlayerDead)
{
return false;
}
if (!__instance.AllowPlayerDeath())
{
return false;
}
if ((int)causeOfDeath == 10 || (int)causeOfDeath == 5 || (int)causeOfDeath == 9 || (Object)(object)shovel == (Object)null)
{
return true;
}
if (isPerfectParryFrame)
{
isSlow = false;
if (currentCoroutine == null)
{
currentCoroutine = ((MonoBehaviour)__instance).StartCoroutine(DoHit(__instance, shovel));
}
MakeCriticallyInjured(__instance, enable: false);
HUDManager.Instance.ShakeCamera((ScreenShakeType)1);
if (LethalParryBase.Notify.Value)
{
HUDManager.Instance.DisplayTip("Parried!", "Nice job!", false, false, "LC_Tip1");
}
__instance.ResetFallGravity();
lastParryTime = 0f;
}
else if (((ButtonControl)Keyboard.current.fKey).isPressed)
{
int num = Random.Range(0, damageNumber - Random.Range(0, damageNumber));
damageNumber -= num;
if (LethalParryBase.Notify.Value)
{
HUDManager.Instance.DisplayTip("Blocked Damage", $"Damage negated: {num}", false, false, "LC_Tip1");
}
HUDManager.Instance.ShakeCamera((ScreenShakeType)1);
int num2 = Random.Range(1, 100);
if (num2 <= LethalParryBase.DropProbability.Value && (Object)(object)currentItem != (Object)null)
{
__instance.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true);
}
}
return !isPerfectParryFrame;
}
[HarmonyPatch("Update")]
[HarmonyPostfix]
private static void UpdatePatch(PlayerControllerB __instance)
{
if (!LethalParryBase.serverModCheck || ((!((NetworkBehaviour)__instance).IsOwner || !__instance.isPlayerControlled || (((NetworkBehaviour)__instance).IsServer && !__instance.isHostPlayerObject)) && !__instance.isTestingPlayer))
{
return;
}
currentItem = __instance.ItemSlots[__instance.currentItemSlot];
if (!((ButtonControl)Keyboard.current.fKey).isPressed || (Object)(object)currentItem == (Object)null || (Object)(object)shovel == (Object)null)
{
if (__instance.bleedingHeavily)
{
isSlow = false;
return;
}
if (isSlow && __instance.criticallyInjured)
{
isSlow = false;
MakeCriticallyInjured(__instance, enable: false);
}
}
if ((Object)(object)currentItem == (Object)null)
{
return;
}
shovel = (Shovel)((currentItem is Shovel) ? /*isinst with value type is only supported in some contexts*/: null);
if ((Object)(object)shovel == (Object)null || !((ButtonControl)Keyboard.current.fKey).wasPressedThisFrame)
{
return;
}
if (shovel.reelingUp || __instance.bleedingHeavily)
{
if (LethalParryBase.Notify.Value)
{
HUDManager.Instance.DisplayTip("Can't parry.", "You are attacking or injured.", false, false, "LC_Tip1");
}
}
else if (!IsParryOnCooldown())
{
((MonoBehaviour)__instance).StartCoroutine(PerfectParryWindow(__instance, shovel));
MakeCriticallyInjured(__instance, enable: true);
LethalParryBase.stun = true;
isSlow = true;
}
else if (!isPerfectParryFrame && LethalParryBase.DisplayCooldown.Value)
{
HUDManager.Instance.DisplayTip("Can't parry. On cooldown", $"Can parry again after {Math.Round(perfectParryCooldown - Time.time + lastParryTime, 1)} seconds.", false, false, "LC_Tip1");
}
}
private static IEnumerator DoHit(PlayerControllerB player, Shovel shovel)
{
AccessTools.Field(typeof(Shovel), "previousPlayerHeldBy").SetValue(shovel, player);
shovel.reelingUp = true;
if (currentCoroutine == null)
{
player.activatingItem = true;
player.twoHanded = true;
player.playerBodyAnimator.ResetTrigger("shovelHit");
player.playerBodyAnimator.SetBool("reelingUp", true);
shovel.shovelAudio.PlayOneShot(shovel.reelUp);
yield return (object)new WaitForSeconds(0.1f);
if (shovel != null)
{
shovel.SwingShovel(!((GrabbableObject)shovel).isHeld);
}
yield return (object)new WaitForSeconds(0.15f);
if (shovel != null)
{
shovel.HitShovel(!((GrabbableObject)shovel).isHeld);
}
shovel.reelingUp = false;
AccessTools.Field(typeof(Shovel), "reelingUpCoroutine").SetValue(shovel, null);
currentCoroutine = null;
}
}
private static IEnumerator PerfectParryWindow(PlayerControllerB player, Shovel shovel)
{
if (!isPerfectParryFrame)
{
isPerfectParryFrame = true;
lastParryTime = Time.fixedTime;
shovel.shovelAudio.PlayOneShot(shovel.reelUp);
shovel.ReelUpSFXServerRpc();
yield return (object)new WaitForSeconds(perfectParryWindow);
isPerfectParryFrame = false;
}
}
private static void MakeCriticallyInjured(PlayerControllerB player, bool enable)
{
player.criticallyInjured = enable;
player.playerBodyAnimator.SetBool("Limp", enable);
}
private static bool IsParryOnCooldown()
{
return Time.fixedTime - lastParryTime < perfectParryCooldown;
}
}
[HarmonyPatch(typeof(Shovel))]
internal class ShovelPatch
{
[HarmonyPatch("HitShovel")]
[HarmonyPostfix]
public static void HitShovelPostfix(ref Shovel __instance, ref List<RaycastHit> ___objectsHitByShovelList, ref PlayerControllerB ___previousPlayerHeldBy)
{
//IL_0048: 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)
if (___objectsHitByShovelList == null && (___objectsHitByShovelList.Count == 0 || !LethalParryBase.stun || !LethalParryBase.serverModCheck))
{
return;
}
LethalParryBase.logger.LogInfo((object)"Got hit list");
EnemyAI val = default(EnemyAI);
foreach (RaycastHit ___objectsHitByShovel in ___objectsHitByShovelList)
{
RaycastHit current = ___objectsHitByShovel;
if (((Component)((RaycastHit)(ref current)).transform.parent).TryGetComponent<EnemyAI>(ref val) && (Object)(object)((RaycastHit)(ref current)).transform != (Object)(object)___previousPlayerHeldBy && (Object)(object)val != (Object)null)
{
try
{
LethalParryBase.logger.LogInfo((object)$"Stunned enemy {val.enemyType}!");
val.SetEnemyStunned(true, 1f, ___previousPlayerHeldBy);
}
catch (Exception arg)
{
Debug.Log((object)$"Exception caught when hitting object with shovel from player #{___previousPlayerHeldBy.playerClientId}: {arg}");
}
}
}
LethalParryBase.stun = false;
}
}
}
namespace LethalParrying.Netcode
{
[HarmonyPatch]
public class ServerModCheck
{
public static PlayerControllerB localPlayer;
public static bool hasMod;
public static bool synced;
[HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")]
[HarmonyPostfix]
public static void InitLocalPlayer(PlayerControllerB __instance)
{
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Expected O, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Expected O, but got Unknown
localPlayer = __instance;
if (NetworkManager.Singleton.IsServer)
{
hasMod = true;
NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("LethalParryingOnRequestModCheck", new HandleNamedMessageDelegate(OnRequest));
LethalParryBase.logger.LogInfo((object)"Setting up Server CustomMessagingManager");
SyncOnLocalClient();
}
else
{
NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("LethalParryingOnReceiveModCheck", new HandleNamedMessageDelegate(OnReceive));
LethalParryBase.logger.LogInfo((object)"Setting up Client CustomMessagingManager");
SendRequestToServer();
}
}
[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
[HarmonyPrefix]
public static bool OnDestroy()
{
if ((Object)(object)NetworkManager.Singleton != (Object)null)
{
if (NetworkManager.Singleton.IsServer)
{
NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("LethalParryingOnRequestModCheck");
LethalParryBase.logger.LogInfo((object)"Destroying Server CustomMessagingManager");
}
else
{
NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("LethalParryingOnReceiveModCheck");
LethalParryBase.logger.LogInfo((object)"Destroying Client CustomMessagingManager");
}
}
hasMod = false;
LethalParryBase.serverModCheck = false;
LethalParryBase.logger.LogInfo((object)"Setting mod check to false");
return true;
}
public static void SendRequestToServer()
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
if (NetworkManager.Singleton.IsClient)
{
LethalParryBase.logger.LogInfo((object)"Sending request to server.");
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1);
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("LethalParryingOnRequestModCheck", 0uL, val, (NetworkDelivery)3);
}
else
{
LethalParryBase.logger.LogError((object)"Faile to send request if server has mod.");
}
}
public static void OnRequest(ulong clientId, FastBufferReader reader)
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
if (NetworkManager.Singleton.IsServer)
{
LethalParryBase.logger.LogInfo((object)$"Player_ID: {clientId} Requested for Mod Check.");
bool flag = true;
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(FastBufferWriter.GetWriteSize<bool>(ref flag, default(ForStructs)), (Allocator)2, -1);
((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref flag, default(ForPrimitives));
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("LethalParryingOnReceiveModCheck", clientId, val, (NetworkDelivery)3);
}
}
public static void OnReceive(ulong clientId, FastBufferReader reader)
{
//IL_0007: 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)
bool flag = default(bool);
((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives));
hasMod = flag;
LethalParryBase.logger.LogInfo((object)$"Received mod check: {hasMod} from server!");
SyncOnLocalClient();
}
public static void SyncOnLocalClient()
{
LethalParryBase.serverModCheck = hasMod;
}
}
}