Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LKKBalanceTweaks v1.0.3
BalanceTweaks.dll
Decompiled 5 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("LKKBalanceTweaks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("BalanceTweaks")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: AssemblyProduct("LKKBalanceTweaks")] [assembly: AssemblyTitle("LKKBalanceTweaks")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.3.0")] namespace BalanceTweaksPlugin { [BepInPlugin("LKKBalanceTweaks", "LKKBalanceTweaks", "1.0.3")] public class BalanceTweaksPlugin : BaseUnityPlugin { public static ConfigEntry<bool> CreateNetworkPrefab; public static ConfigEntry<bool> ShotgunConductive; public static ConfigEntry<bool> ShotgunEnemyDamage; public static ConfigEntry<bool> BeltBagShotgunAmmo; public static ConfigEntry<bool> ShotgunWeight; public static ConfigEntry<bool> ShovelWeight; public static ConfigEntry<bool> KnifeWeight; public static ConfigEntry<bool> EnableShovelNarrowHitbox; public static ConfigEntry<bool> EnableShovelTriggerHit; public static ConfigEntry<bool> EnableShovelLinecastBlock; public static ConfigEntry<bool> EnableKnifeTriggerHit; public static ConfigEntry<bool> EnableKnifeLinecastBlock; public static ConfigEntry<bool> EnableJesterOwnershipFallback; public const string PLUGIN_GUID = "LKKBalanceTweaks"; public const string PLUGIN_NAME = "LKKBalanceTweaks"; public const string PLUGIN_VERSION = "1.0.3"; public const string PLUGIN_VERSION_FULL = "1.0.3.0"; private readonly Harmony harmony = new Harmony("LKKBalanceTweaks"); public static ManualLogSource logger; public static BalanceTweaksPlugin Instance; private void Awake() { Instance = this; logger = ((BaseUnityPlugin)this).Logger; CreateNetworkPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CreateNetworkPrefab", false, "Define whether to create a network prefab"); ShotgunConductive = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShotgunConductive", true, "Define whether the shotgun is conductive"); ShotgunEnemyDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShotgunEnemyDamage", true, "Define whether to nerf shotgun damage"); BeltBagShotgunAmmo = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BeltBagShotgunAmmo", true, "Define whether the belt bag can't store shotgun ammo"); ShotgunWeight = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "ShotgunWeight", true, "Define whether to modify shotgun weight"); ShovelWeight = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "ShovelWeight", true, "Define whether to modify shovel weight"); KnifeWeight = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "KnifeWeight", true, "Define whether to modify knife weight"); EnableShovelNarrowHitbox = ((BaseUnityPlugin)this).Config.Bind<bool>("HitDetection", "EnableShovelNarrowHitbox", true, "Define whether to narrow the shovel hit SphereCast radius"); EnableShovelTriggerHit = ((BaseUnityPlugin)this).Config.Bind<bool>("HitDetection", "EnableShovelTriggerHit", true, "Define whether to allow shovel to hit through trigger colliders"); EnableShovelLinecastBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("HitDetection", "EnableShovelLinecastBlock", true, "Define whether to change shovel linecast queryTriggerInteraction"); EnableKnifeTriggerHit = ((BaseUnityPlugin)this).Config.Bind<bool>("HitDetection", "EnableKnifeTriggerHit", true, "Define whether to allow knife to hit through trigger colliders"); EnableKnifeLinecastBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("HitDetection", "EnableKnifeLinecastBlock", true, "Define whether to change knife linecast queryTriggerInteraction"); EnableJesterOwnershipFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Jester", "EnableJesterOwnershipFallback", false, "Define whether to enable Jester fallback kill path"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LKKBalanceTweaks is loaded!"); } } } namespace BalanceTweaksPlugin.Patches { [HarmonyPatch(typeof(BeltBagItem), "TryAddObjectToBagServerRpc")] internal static class BeltBagShotgunAmmoPatch { [HarmonyPrefix] private static bool Prefix(BeltBagItem __instance, NetworkObjectReference netObjectRef, int playerWhoAdded) { if (!((NetworkBehaviour)__instance).IsHost) { return true; } if (!BalanceTweaksPlugin.BeltBagShotgunAmmo.Value) { return true; } NetworkObject val = default(NetworkObject); if (((NetworkObjectReference)(ref netObjectRef)).TryGet(ref val, (NetworkManager)null) && ((Component)val).GetComponent<GrabbableObject>() is GunAmmo) { __instance.CancelAddObjectToBagClientRpc(playerWhoAdded); return false; } return true; } } [HarmonyPatch(typeof(FlashlightItem), "DiscardItem")] internal static class FlashlightDiscardItemPatch { [HarmonyPrefix] private static void Prefix(FlashlightItem __instance) { if (((GrabbableObject)__instance).isBeingUsed && ((GrabbableObject)__instance).insertedBattery != null && !((GrabbableObject)__instance).insertedBattery.empty && (int)(((GrabbableObject)__instance).insertedBattery.charge * 100f) == 0) { ((GrabbableObject)__instance).UseUpBatteries(); } } } [HarmonyPatch(typeof(JesterAI), "OnCollideWithPlayer")] internal static class JesterOwnershipFallbackPatch { private static readonly FieldRef<JesterAI, bool> inKillAnimation = AccessTools.FieldRefAccess<JesterAI, bool>("inKillAnimation"); private static readonly MethodInfo isSeparatedByMineshaftElevator = AccessTools.Method(typeof(EnemyAI), "IsSeparatedByMineshaftElevator", (Type[])null, (Type[])null); [HarmonyPostfix] private static void Postfix(JesterAI __instance, Collider other) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (BalanceTweaksPlugin.CreateNetworkPrefab.Value && BalanceTweaksPlugin.EnableJesterOwnershipFallback.Value && ((EnemyAI)__instance).currentBehaviourStateIndex == 2 && ((NetworkBehaviour)__instance).IsOwner && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).ventAnimationFinished) { PlayerControllerB val = ((other != null) ? ((Component)other).GetComponent<PlayerControllerB>() : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)((EnemyAI)__instance).targetPlayer) && !inKillAnimation.Invoke(__instance) && !(((EnemyAI)__instance).stunNormalizedTimer >= 0f) && ((EnemyAI)__instance).PlayerIsTargetable(val, false, false, true) && !(bool)isSeparatedByMineshaftElevator.Invoke(__instance, new object[1] { ((Component)val).transform.position })) { __instance.KillPlayerServerRpc((int)val.playerClientId); } } } } [HarmonyPatch(typeof(EnemyAI), "SwitchToBehaviourState")] internal static class JesterStateTransitionPatch { [HarmonyPostfix] private static void Postfix(EnemyAI __instance, int stateIndex) { if (stateIndex == 2 && __instance is JesterAI && ((NetworkBehaviour)__instance).IsServer && ((NetworkBehaviour)__instance).IsOwner && !((Object)(object)__instance.targetPlayer == (Object)null) && !((Object)(object)__instance.targetPlayer == (Object)(object)GameNetworkManager.Instance.localPlayerController)) { __instance.ChangeOwnershipOfEnemy(__instance.targetPlayer.actualClientId); } } } [HarmonyPatch(typeof(KnifeItem), "HitKnife")] internal static class KnifeAttackPatch { private const int QueryTriggerIgnore = 1; private const int QueryTriggerUseGlobal = 0; private const sbyte QueryTriggerIgnore_SByte = 1; [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (BalanceTweaksPlugin.EnableKnifeLinecastBlock.Value && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Call && list[i + 1].operand is MethodInfo { Name: "Linecast", IsStatic: not false }) { if (val.opcode == OpCodes.Ldc_I4_1) { val.opcode = OpCodes.Ldc_I4_0; val.operand = null; } else if (val.opcode == OpCodes.Ldc_I4_S && val.operand is sbyte b && b == 1) { val.operand = (sbyte)0; } else if (val.opcode == OpCodes.Ldc_I4 && val.operand is int num && num == 1) { val.operand = 0; } } } return list; } } [HarmonyPatch(typeof(KnifeItem), "HitKnife")] internal static class KnifeTriggerPatch { private static readonly MethodInfo IsTriggerGetter = AccessTools.PropertyGetter(typeof(Collider), "isTrigger"); [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { if (!BalanceTweaksPlugin.EnableKnifeTriggerHit.Value) { return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (!(list[i].opcode == OpCodes.Callvirt) || !(list[i].operand is MethodInfo methodInfo) || !(methodInfo == IsTriggerGetter)) { continue; } list[i].opcode = OpCodes.Pop; list[i].operand = null; for (int j = i + 1; j < list.Count; j++) { if (list[j].opcode == OpCodes.Brtrue || list[j].opcode == OpCodes.Brtrue_S) { list[j].opcode = OpCodes.Nop; list[j].operand = null; break; } } break; } return list; } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class KnifeWeightPatch { [HarmonyPostfix] private static void ModifyKnifeWeight() { if (!BalanceTweaksPlugin.KnifeWeight.Value) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (items.itemName == "Kitchen knife") { items.weight = 1.05f; break; } } } } [HarmonyPatch(typeof(NetworkManager), "SetSingleton")] internal static class NetworkPrefabPatch { private static void Postfix() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!BalanceTweaksPlugin.CreateNetworkPrefab.Value || (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.PrefabHandler == null) { return; } GameObject val = new GameObject("LKKBalanceTweaks Prefab"); ((Object)val).hideFlags = (HideFlags)(((Object)val).hideFlags | 0x3D); Object.DontDestroyOnLoad((Object)(object)val); NetworkObject obj = val.AddComponent<NetworkObject>(); try { FieldInfo field = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { uint hashCode = (uint)"LKKBalanceTweaks".GetHashCode(); field.SetValue(obj, hashCode); } } catch { } NetworkManager.Singleton.PrefabHandler.AddNetworkPrefab(val); } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class ShotgunConductivePatch { [HarmonyPostfix] private static void MakeShotgunConductive() { if (!BalanceTweaksPlugin.ShotgunConductive.Value || !((NetworkBehaviour)StartOfRound.Instance).IsHost) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (items.itemName == "Shotgun") { items.isConductiveMetal = true; break; } } } } [HarmonyPatch(typeof(ShotgunItem), "ShootGun")] internal static class ShotgunEnemyDamagePatch { private const float OriginalRangeThreshold = 3.7f; private const int OriginalDamageClose = 5; private const int OriginalDamageMedium = 3; private const int OriginalDamageFar = 2; private const int DamageClose = 4; private const int DamageMedium = 3; private const int DamageFar = 2; [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { int remainingMatches = 0; foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instruction.operand, 3.7f)) { remainingMatches = 3; } if (remainingMatches > 0) { if (instruction.opcode == OpCodes.Ldc_I4_5) { instruction.opcode = OpCodes.Ldc_I4; instruction.operand = 4; remainingMatches--; } else if (instruction.opcode == OpCodes.Ldc_I4_3) { instruction.opcode = OpCodes.Ldc_I4; instruction.operand = 3; remainingMatches--; } else if (instruction.opcode == OpCodes.Ldc_I4_2) { instruction.opcode = OpCodes.Ldc_I4; instruction.operand = 2; remainingMatches--; } else if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num && (num == 5 || num == 3 || num == 2)) { instruction.operand = num switch { 3 => 3, 5 => 4, _ => 2, }; remainingMatches--; } } yield return instruction; } } } [HarmonyPatch(typeof(EnemyAI), "HitEnemyServerRpc")] internal static class ShotgunEnemyDamageSyncPatch { private static readonly int[] OriginalDamage = new int[3] { 5, 3, 2 }; private static readonly int[] ModdedDamage = new int[3] { 4, 3, 2 }; [HarmonyPrefix] private static void SyncShotgunDamage(ref int force, int playerWhoHit) { if (!((NetworkBehaviour)StartOfRound.Instance).IsHost || playerWhoHit < 0 || playerWhoHit >= StartOfRound.Instance.allPlayerScripts.Length) { return; } PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerWhoHit]; if ((Object)(object)val == (Object)null || !(val.currentlyHeldObjectServer is ShotgunItem)) { return; } int[] array = (BalanceTweaksPlugin.ShotgunEnemyDamage.Value ? OriginalDamage : ModdedDamage); int[] array2 = (BalanceTweaksPlugin.ShotgunEnemyDamage.Value ? ModdedDamage : OriginalDamage); for (int i = 0; i < array.Length; i++) { if (force == array[i]) { force = array2[i]; break; } } } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class ShotgunWeightPatch { [HarmonyPostfix] private static void ModifyShotgunWeight() { if (!BalanceTweaksPlugin.ShotgunWeight.Value) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (items.itemName == "Shotgun") { items.weight = 1.2f; break; } } } } [HarmonyPatch(typeof(Shovel), "HitShovel")] internal static class ShovelAttackPatch { private const float OriginalSphereCastRadius = 0.8f; private const float SphereCastRadius = 0.75f; private const float OriginalSphereCastDistance = 1.5f; private const float SphereCastDistance = 1.5f; private const float FloatComparisonEpsilon = 0.0001f; private const int QueryTriggerIgnore = 1; private const int QueryTriggerUseGlobal = 0; private const sbyte QueryTriggerIgnore_SByte = 1; [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (BalanceTweaksPlugin.EnableShovelNarrowHitbox.Value && val.opcode == OpCodes.Ldc_R4 && val.operand is float num) { if (Math.Abs(num - 0.8f) < 0.0001f) { val.operand = 0.75f; } else if (Math.Abs(num - 1.5f) < 0.0001f) { val.operand = 1.5f; } } if (BalanceTweaksPlugin.EnableShovelLinecastBlock.Value && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Call && list[i + 1].operand is MethodInfo { Name: "Linecast", IsStatic: not false }) { if (val.opcode == OpCodes.Ldc_I4_1) { val.opcode = OpCodes.Ldc_I4_0; val.operand = null; } else if (val.opcode == OpCodes.Ldc_I4_S && val.operand is sbyte b && b == 1) { val.operand = (sbyte)0; } else if (val.opcode == OpCodes.Ldc_I4 && val.operand is int num2 && num2 == 1) { val.operand = 0; } } } return list; } } [HarmonyPatch(typeof(Shovel), "HitShovel")] internal static class ShovelTriggerPatch { private static readonly MethodInfo IsTriggerGetter = AccessTools.PropertyGetter(typeof(Collider), "isTrigger"); [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { if (!BalanceTweaksPlugin.EnableShovelTriggerHit.Value) { return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (!(list[i].opcode == OpCodes.Callvirt) || !(list[i].operand is MethodInfo methodInfo) || !(methodInfo == IsTriggerGetter)) { continue; } list[i].opcode = OpCodes.Pop; list[i].operand = null; for (int j = i + 1; j < list.Count; j++) { if (list[j].opcode == OpCodes.Brtrue || list[j].opcode == OpCodes.Brtrue_S) { list[j].opcode = OpCodes.Nop; list[j].operand = null; break; } } break; } return list; } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class ShovelWeightPatch { [HarmonyPostfix] private static void ModifyShovelWeight() { if (!BalanceTweaksPlugin.ShovelWeight.Value) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (items.itemName == "Shovel") { items.weight = 1.18f; break; } } } } [HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")] internal static class BlockScrollSwitchPatch { private const float KnifeCooldownTime = 0.43f; private static readonly FieldInfo knifeCooldownField = AccessTools.Field(typeof(KnifeItem), "timeAtLastDamageDealt"); [HarmonyPrefix] private static bool Prefix(PlayerControllerB __instance) { GrabbableObject currentlyHeldObjectServer = __instance.currentlyHeldObjectServer; Shovel val = (Shovel)(object)((currentlyHeldObjectServer is Shovel) ? currentlyHeldObjectServer : null); if ((Object)(object)val != (Object)null && val.reelingUp) { return false; } GrabbableObject currentlyHeldObjectServer2 = __instance.currentlyHeldObjectServer; KnifeItem val2 = (KnifeItem)(object)((currentlyHeldObjectServer2 is KnifeItem) ? currentlyHeldObjectServer2 : null); if ((Object)(object)val2 != (Object)null && Time.realtimeSinceStartup - (float)knifeCooldownField.GetValue(val2) < 0.43f) { return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB), "UseUtilitySlot_performed")] internal static class BlockUtilitySlotSwitchPatch { private const float KnifeCooldownTime = 0.43f; private static readonly FieldInfo knifeCooldownField = AccessTools.Field(typeof(KnifeItem), "timeAtLastDamageDealt"); [HarmonyPrefix] private static bool Prefix(PlayerControllerB __instance) { GrabbableObject currentlyHeldObjectServer = __instance.currentlyHeldObjectServer; Shovel val = (Shovel)(object)((currentlyHeldObjectServer is Shovel) ? currentlyHeldObjectServer : null); if ((Object)(object)val != (Object)null && val.reelingUp) { return false; } GrabbableObject currentlyHeldObjectServer2 = __instance.currentlyHeldObjectServer; KnifeItem val2 = (KnifeItem)(object)((currentlyHeldObjectServer2 is KnifeItem) ? currentlyHeldObjectServer2 : null); if ((Object)(object)val2 != (Object)null && Time.realtimeSinceStartup - (float)knifeCooldownField.GetValue(val2) < 0.43f) { return false; } return true; } } }