using 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("BalanceTweaksForMe")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("BalanceTweaks")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("BalanceTweaksForMe")] [assembly: AssemblyTitle("BalanceTweaksForMe")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.1.0")] namespace BalanceTweaksPlugin { [BepInPlugin("BalanceTweaksForMe", "BalanceTweaksForMe", "1.0.1")] public class BalanceTweaksPlugin : BaseUnityPlugin { public static ConfigEntry CreateNetworkPrefab; public static ConfigEntry ShotgunConductive; public static ConfigEntry ShotgunEnemyDamage; public static ConfigEntry BeltBagShotgunAmmo; public static ConfigEntry AmmoWeight; public static ConfigEntry ShotgunWeight; public static ConfigEntry ShovelWeight; public static ConfigEntry KnifeWeight; public static ConfigEntry EnableShovelNarrowHitbox; public static ConfigEntry EnableShovelTriggerHit; public static ConfigEntry EnableShovelLinecastBlock; public static ConfigEntry EnableKnifeTriggerHit; public static ConfigEntry EnableKnifeLinecastBlock; public const string PLUGIN_GUID = "BalanceTweaksForMe"; public const string PLUGIN_NAME = "BalanceTweaksForMe"; public const string PLUGIN_VERSION = "1.0.1"; public const string PLUGIN_VERSION_FULL = "1.0.1.0"; private Harmony harmony = new Harmony("BalanceTweaksForMe"); public static ManualLogSource logger; public static BalanceTweaksPlugin Instance; private void Awake() { Instance = this; logger = ((BaseUnityPlugin)this).Logger; CreateNetworkPrefab = ((BaseUnityPlugin)this).Config.Bind("General", "CreateNetworkPrefab", false, ""); ShotgunConductive = ((BaseUnityPlugin)this).Config.Bind("General", "ShotgunConductive", true, ""); ShotgunEnemyDamage = ((BaseUnityPlugin)this).Config.Bind("General", "ShotgunEnemyDamage", true, ""); BeltBagShotgunAmmo = ((BaseUnityPlugin)this).Config.Bind("General", "BeltBagShotgunAmmo", true, ""); AmmoWeight = ((BaseUnityPlugin)this).Config.Bind("Balance", "AmmoWeight", false, ""); ShotgunWeight = ((BaseUnityPlugin)this).Config.Bind("Balance", "ShotgunWeight", true, ""); ShovelWeight = ((BaseUnityPlugin)this).Config.Bind("Balance", "ShovelWeight", true, ""); KnifeWeight = ((BaseUnityPlugin)this).Config.Bind("Balance", "KnifeWeight", true, ""); EnableShovelNarrowHitbox = ((BaseUnityPlugin)this).Config.Bind("HitDetection", "EnableShovelNarrowHitbox", true, ""); EnableShovelTriggerHit = ((BaseUnityPlugin)this).Config.Bind("HitDetection", "EnableShovelTriggerHit", true, ""); EnableShovelLinecastBlock = ((BaseUnityPlugin)this).Config.Bind("HitDetection", "EnableShovelLinecastBlock", true, ""); EnableKnifeTriggerHit = ((BaseUnityPlugin)this).Config.Bind("HitDetection", "EnableKnifeTriggerHit", true, ""); EnableKnifeLinecastBlock = ((BaseUnityPlugin)this).Config.Bind("HitDetection", "EnableKnifeLinecastBlock", true, ""); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"BalanceTweaksForMe is loaded!"); } } } namespace BalanceTweaksPlugin.Patches { [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class AmmoWeightPatch { [HarmonyPostfix] private static void ModifyAmmoWeight() { if (!BalanceTweaksPlugin.AmmoWeight.Value) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if (items.itemName == "Ammo") { items.weight = 1f; break; } } } } [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() is GunAmmo) { __instance.CancelAddObjectToBagClientRpc(playerWhoAdded); return false; } return true; } } [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 Transpiler(IEnumerable instructions) { List list = new List(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 Transpiler(IEnumerable instructions) { if (!BalanceTweaksPlugin.EnableKnifeTriggerHit.Value) { return instructions; } List list = new List(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("BalanceTweaksForMe Prefab"); ((Object)val).hideFlags = (HideFlags)(((Object)val).hideFlags | 0x3D); Object.DontDestroyOnLoad((Object)(object)val); NetworkObject obj = val.AddComponent(); try { FieldInfo field = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { uint hashCode = (uint)"BalanceTweaksForMe".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 Transpiler(IEnumerable 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 Transpiler(IEnumerable instructions) { List list = new List(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 Transpiler(IEnumerable instructions) { if (!BalanceTweaksPlugin.EnableShovelTriggerHit.Value) { return instructions; } List list = new List(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; } } }