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.9
BalanceTweaks.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BalanceTweaksPlugin.Effects; using BalanceTweaksPlugin.Patches; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("LKKBalanceTweaks")] [assembly: AssemblyDescription("A plugin that only nerfs player, only downgrade features configurable")] [assembly: AssemblyFileVersion("1.0.9.0")] [assembly: AssemblyInformationalVersion("1.0.9")] [assembly: AssemblyProduct("LKKBalanceTweaks")] [assembly: AssemblyTitle("LKKBalanceTweaks")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.9.0")] namespace BalanceTweaksPlugin { [BepInPlugin("LKKBalanceTweaks", "LKKBalanceTweaks", "1.0.9")] 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> EnableInteractDiscardBlock; 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> EnableJesterOwnershipCorrection; public static ConfigEntry<bool> EnableJesterSpeed; public static ConfigEntry<bool> EnableStressMechanism; public static ConfigEntry<bool> EnableStressHealthDrain; public const string PLUGIN_GUID = "LKKBalanceTweaks"; public const string PLUGIN_NAME = "LKKBalanceTweaks"; public const string PLUGIN_VERSION = "1.0.9"; public const string PLUGIN_VERSION_FULL = "1.0.9.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"); EnableInteractDiscardBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "EnableInteractDiscardBlock", false, "Define whether to block interacting and discarding during shovel reel-up or knife cooldown"); 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"); EnableJesterOwnershipCorrection = ((BaseUnityPlugin)this).Config.Bind<bool>("Jester", "EnableJesterOwnershipCorrection", true, "Define whether to periodically correct Jester ownership"); EnableJesterSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("Jester", "EnableJesterSpeed", true, "Define whether to compute the chase speed on non-owner clients, actually a good nerf"); EnableStressMechanism = ((BaseUnityPlugin)this).Config.Bind<bool>("Stress", "EnableStressMechanism", true, "Define whether to enable stress mechanism"); EnableStressHealthDrain = ((BaseUnityPlugin)this).Config.Bind<bool>("Stress", "EnableStressHealthDrain", true, "Define whether to enable stress health drain"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LKKBalanceTweaks is loaded!"); ((Component)this).gameObject.AddComponent<AudioManager>(); ((Component)this).gameObject.AddComponent<StressVignetteEffect>(); ((Component)this).gameObject.AddComponent<DesaturateEffect>(); ((Component)this).gameObject.AddComponent<TinnitusEffect>(); } } } 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(); } } } internal static class InteractDiscardCooldownPatch { private const float KnifeCooldownTime = 0.43f; private static readonly FieldInfo knifeCooldownField = AccessTools.Field(typeof(KnifeItem), "timeAtLastDamageDealt"); internal static bool IsInCooldown(PlayerControllerB player) { GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; if ((Object)(object)currentlyHeldObjectServer == (Object)null) { return false; } Shovel val = (Shovel)(object)((currentlyHeldObjectServer is Shovel) ? currentlyHeldObjectServer : null); if ((Object)(object)val != (Object)null && val.reelingUp) { return true; } KnifeItem val2 = (KnifeItem)(object)((currentlyHeldObjectServer is KnifeItem) ? currentlyHeldObjectServer : null); if ((Object)(object)val2 != (Object)null && Time.realtimeSinceStartup - (float)knifeCooldownField.GetValue(val2) < 0.43f) { return true; } return false; } } [HarmonyPatch(typeof(PlayerControllerB), "Interact_performed")] internal static class BlockInteractPatch { [HarmonyPrefix] private static bool Prefix(PlayerControllerB __instance) { if (!BalanceTweaksPlugin.EnableInteractDiscardBlock.Value) { return true; } return !InteractDiscardCooldownPatch.IsInCooldown(__instance); } } [HarmonyPatch(typeof(PlayerControllerB), "Discard_performed")] internal static class BlockDiscardPatch { [HarmonyPrefix] private static bool Prefix(PlayerControllerB __instance) { if (!BalanceTweaksPlugin.EnableInteractDiscardBlock.Value) { return true; } return !InteractDiscardCooldownPatch.IsInCooldown(__instance); } } [HarmonyPatch(typeof(JesterAI), "OnCollideWithPlayer")] internal static class JesterCollisionTransferPatch { 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_008d: Unknown result type (might be due to invalid IL or missing references) if (((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)GameNetworkManager.Instance.localPlayerController) && !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 })) { ((EnemyAI)__instance).ChangeOwnershipOfEnemy(val.actualClientId); } } } } [HarmonyPatch(typeof(RoundManager), "Update")] internal static class JesterOwnershipCorrectionPatch { private static float _timer; [HarmonyPostfix] private static void Postfix() { if (!BalanceTweaksPlugin.EnableJesterOwnershipCorrection.Value) { return; } _timer += Time.deltaTime; if (_timer < 0.8f) { return; } _timer = 0f; List<EnemyAI> spawnedEnemies = RoundManager.Instance.SpawnedEnemies; if (spawnedEnemies == null) { return; } for (int i = 0; i < spawnedEnemies.Count; i++) { EnemyAI val = spawnedEnemies[i]; if ((Object)(object)val == (Object)null || val.isEnemyDead) { continue; } JesterAI val2 = (JesterAI)(object)((val is JesterAI) ? val : null); if (val2 != null && ((EnemyAI)val2).currentBehaviourStateIndex == 2 && ((NetworkBehaviour)val2).IsOwner) { PlayerControllerB targetPlayer = ((EnemyAI)val2).targetPlayer; if (!((Object)(object)targetPlayer == (Object)null) && ((EnemyAI)val2).PlayerIsTargetable(targetPlayer, false, false, true) && !((Object)(object)targetPlayer == (Object)(object)GameNetworkManager.Instance.localPlayerController)) { ((EnemyAI)val2).ChangeOwnershipOfEnemy(targetPlayer.actualClientId); } } } } } [HarmonyPatch(typeof(JesterAI), "Update")] internal static class JesterSpeedPatch { private static readonly FieldRef<JesterAI, bool> inKillAnimation = AccessTools.FieldRefAccess<JesterAI, bool>("inKillAnimation"); [HarmonyPostfix] private static void Postfix(JesterAI __instance) { if (BalanceTweaksPlugin.EnableJesterSpeed.Value && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).currentBehaviourStateIndex == 2 && !((NetworkBehaviour)__instance).IsOwner) { if (inKillAnimation.Invoke(__instance) || ((EnemyAI)__instance).stunNormalizedTimer > 0f) { ((EnemyAI)__instance).agent.speed = 0f; } else { ((EnemyAI)__instance).agent.speed = Mathf.Clamp(((EnemyAI)__instance).agent.speed + Time.deltaTime * 1.45f, 0f, 18f); } } } } [HarmonyPatch(typeof(EnemyAI), "SwitchToBehaviourState")] internal static class JesterStatePatch { [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; } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] internal static class StressHealthDrainPatch { private sealed class DrainState { public float timer; } private const float DrainInterval = 0.5f; private const int DrainAmount = 5; private static readonly ConditionalWeakTable<PlayerControllerB, DrainState> drainStates = new ConditionalWeakTable<PlayerControllerB, DrainState>(); [HarmonyPostfix] private static void Postfix(PlayerControllerB __instance) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!BalanceTweaksPlugin.EnableStressMechanism.Value || !BalanceTweaksPlugin.EnableStressHealthDrain.Value || (Object)(object)__instance != (Object)(object)GameNetworkManager.Instance.localPlayerController) { return; } DrainState orCreateValue = drainStates.GetOrCreateValue(__instance); if (StressMechanismPatch.GetEffectiveSprintMeter(__instance) > 0f || __instance.isPlayerDead) { orCreateValue.timer = 0f; return; } orCreateValue.timer += Time.deltaTime; if (!(orCreateValue.timer < 0.5f)) { orCreateValue.timer = 0f; __instance.DamagePlayer(5, true, true, (CauseOfDeath)5, 0, false, default(Vector3)); } } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] internal static class StressMechanismPatch { [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] internal static class DamageTakenStressPatch { [HarmonyPostfix] private static void Postfix(PlayerControllerB __instance, int damageNumber) { if (!((Object)(object)__instance != (Object)(object)GameNetworkManager.Instance.localPlayerController)) { pendingDamageTaken += damageNumber; } } } [HarmonyPatch(typeof(LungProp), "EquipItem")] internal static class ApparatusPulledPatch { private const float PullStressGain = 0.04f; [HarmonyPrefix] private static void Prefix(LungProp __instance) { if (BalanceTweaksPlugin.EnableStressMechanism.Value && __instance.isLungDocked) { PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if (!((Object)(object)localPlayerController == (Object)null) && !localPlayerController.isPlayerDead) { stressTimer = Mathf.Min(1f, stressTimer + 0.04f); } } } } private static readonly FieldRef<PlayerControllerB, bool> isWalking = AccessTools.FieldRefAccess<PlayerControllerB, bool>("isWalking"); private const float HealthFactorStartHp = 100f; private const float HealthFactorEndHp = 20f; private const float HealthFactorMultiplier = 0.2f; private const float FearFactorMultiplier = 0.2f; private const float SoloShipRate = 0.000462f; private const float SoloOutsideRate = 0.000694f; private const float SoloFactoryRate = 0.000925f; private const float MultiShipRate = 0.000694f; private const float MultiOutsideRate = 0.001041f; private const float MultiFactoryRate = 0.001388f; private const float NearOthersRadius = 17f; private const float CompanionshipMultiplier = 0.5f; private const float PlayerFactorMultiplier = 1.15f; private static readonly HashSet<string> NoStressLevelSceneNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "CompanyBuilding" }; internal static float stressTimer; internal static float pendingDamageTaken; internal static int otherTotal; internal static int otherAlive; internal static float alivePercent; internal static float currentLocationRate; [HarmonyPostfix] private static void Postfix(PlayerControllerB __instance) { UpdateStress(__instance); __instance.sprintMeter = GetEffectiveSprintMeter(__instance); } internal static void UpdateStress(PlayerControllerB player) { if (BalanceTweaksPlugin.EnableStressMechanism.Value && !((Object)(object)player != (Object)(object)GameNetworkManager.Instance.localPlayerController)) { UpdateStressTimer(player); } } internal static float GetEffectiveSprintMeter(PlayerControllerB player) { if (!BalanceTweaksPlugin.EnableStressMechanism.Value) { return player.sprintMeter; } if ((Object)(object)player != (Object)(object)GameNetworkManager.Instance.localPlayerController) { return player.sprintMeter; } if (!player.isInsideFactory) { return player.sprintMeter; } if (player.isSprinting) { return player.sprintMeter; } if (player.isCrouching) { return player.sprintMeter; } if (player.isMovementHindered > 0) { return player.sprintMeter; } if (!isWalking.Invoke(player)) { return player.sprintMeter; } float num = 1f; if (player.drunkness > 0.02f) { num *= Mathf.Abs(StartOfRound.Instance.drunknessSpeedEffect.Evaluate(player.drunkness) - 1.25f); } float deltaTime = Time.deltaTime; float num2 = stressTimer * 0.3f; float num3 = deltaTime / (player.sprintTime + 9f) * num; float num4 = deltaTime / player.sprintTime * player.carryWeight * num * num2; float num5 = Mathf.Clamp01(stressTimer / 0.5f); float num6 = Mathf.InverseLerp(0.5f, 1f, stressTimer); float num7 = num4 * num6; return Mathf.Clamp(player.sprintMeter - num3 * num5 - num7, 0f, 1f); } private static bool IsNoStressLevel() { if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.currentLevel != (Object)null) { return NoStressLevelSceneNames.Contains(StartOfRound.Instance.currentLevel.sceneName); } return false; } private static float GetLocationRate(PlayerControllerB player) { bool flag = otherTotal == 0; if (player.isInsideFactory) { if (!flag) { return 0.001388f; } return 0.000925f; } if (player.isInHangarShipRoom) { if (!flag) { return 0.000694f; } return 0.000462f; } if (!flag) { return 0.001041f; } return 0.000694f; } internal static float GetHealthFactor(PlayerControllerB player) { return Mathf.Lerp(0f, 0.2f, Mathf.InverseLerp(100f, 20f, (float)player.health)); } private static void UpdateStressTimer(PlayerControllerB player) { if (player.isPlayerDead) { return; } otherTotal = StartOfRound.Instance.connectedPlayersAmount; otherAlive = StartOfRound.Instance.livingPlayers - ((!player.isPlayerDead) ? 1 : 0); alivePercent = ((otherTotal > 0) ? Mathf.Clamp01((float)otherAlive / (float)otherTotal) : 0f); bool flag = otherTotal == 0; float num = ((!flag) ? Mathf.Lerp(0.00075f, 0.000375f, alivePercent) : 0.0005f); if (StartOfRound.Instance.inShipPhase) { stressTimer = 0f; pendingDamageTaken = 0f; } else if (!IsNoStressLevel()) { float healthFactor = GetHealthFactor(player); float num2 = Mathf.Lerp(0f, 0.2f, Mathf.Clamp01(StartOfRound.Instance.fearLevel)); float num3 = (flag ? 1f : Mathf.Lerp(1.15f, 1f, alivePercent)); float num4 = (player.NearOtherPlayers(17f) ? 0.5f : 1f); float num5 = (currentLocationRate = GetLocationRate(player) * (1f + healthFactor + num2) * num3 * num4); stressTimer += Time.deltaTime * num5; if (pendingDamageTaken > 0f) { stressTimer += pendingDamageTaken * num * (1f + healthFactor); pendingDamageTaken = 0f; } stressTimer = Mathf.Clamp(stressTimer, 0f, 1f); } } } } namespace BalanceTweaksPlugin.Effects { internal class AudioManager : MonoBehaviour { private const string BasicFolder = "Basic"; private const string HallucinationFolder = "Hallucination"; private const string HallucinationPrefix = "Hallucination"; private const string BlackoutSoundName = "Blackout"; private const string DesaturateSoundName = "Desaturate"; private const string TinnitusSoundName = "Tinnitus"; private const float SoundVolume = 1f; private static readonly string[] AudioExtensions = new string[3] { ".wav", ".ogg", ".mp3" }; private AudioSource blackoutSource; private AudioSource desaturateSource; private AudioSource tinnitusSource; private AudioSource hallucinationSource; private float desaturateTargetVolume = 1f; private AudioClip blackoutClip; private AudioClip desaturateClip; private AudioClip tinnitusClip; private readonly List<AudioClip> hallucinationClips = new List<AudioClip>(); private void Awake() { blackoutSource = CreateSource(); desaturateSource = CreateSource(); tinnitusSource = CreateSource(); hallucinationSource = CreateSource(); ((MonoBehaviour)this).StartCoroutine(LoadClip("Blackout", "Basic", delegate(AudioClip clip) { blackoutClip = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadClip("Desaturate", "Basic", delegate(AudioClip clip) { desaturateClip = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadClip("Tinnitus", "Basic", delegate(AudioClip clip) { tinnitusClip = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadHallucinationClips()); } private void Update() { if (!((Object)(object)desaturateSource == (Object)null)) { desaturateSource.volume = Mathf.Lerp(desaturateSource.volume, desaturateTargetVolume, 5f * Time.deltaTime); } } private AudioSource CreateSource() { AudioSource obj = ((Component)this).gameObject.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.loop = false; obj.spatialBlend = 0f; obj.volume = 1f; return obj; } private void PlayOneShot(AudioSource source, AudioClip clip) { if (!((Object)(object)source == (Object)null) && !((Object)(object)clip == (Object)null)) { source.PlayOneShot(clip); } } public void PlayBlackoutSound() { PlayOneShot(blackoutSource, blackoutClip); } public void PlayDesaturateSound() { PlayOneShot(desaturateSource, desaturateClip); } public void PlayTinnitusSound() { PlayOneShot(tinnitusSource, tinnitusClip); } public void PlayHallucinationSound() { if (hallucinationClips.Count != 0) { PlayOneShot(hallucinationSource, hallucinationClips[Random.Range(0, hallucinationClips.Count)]); } } private void StopOneShot(AudioSource source) { if (!((Object)(object)source == (Object)null)) { source.Stop(); } } public void StopBlackoutSound() { StopOneShot(blackoutSource); } public void StopDesaturateSound() { StopOneShot(desaturateSource); } public void StopTinnitusSound() { StopOneShot(tinnitusSource); } public void StopHallucinationSound() { StopOneShot(hallucinationSource); } public void SetDesaturateSoundMuted(bool muted) { desaturateTargetVolume = (muted ? 0f : 1f); } private IEnumerator LoadClip(string baseName, string subDir, Action<AudioClip> onLoaded) { if (string.IsNullOrWhiteSpace(baseName)) { yield break; } string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "assets", subDir); string text2 = null; AudioType audioType = (AudioType)20; string[] audioExtensions = AudioExtensions; foreach (string text3 in audioExtensions) { string text4 = Path.Combine(text, baseName + text3); if (File.Exists(text4)) { text2 = text4; audioType = GetAudioType(text3); break; } } if (text2 == null) { BalanceTweaksPlugin.logger.LogWarning((object)("Stress sound '" + baseName + "' not found in '" + text + "' (tried " + string.Join(", ", AudioExtensions) + ")")); } else { yield return LoadClipAtPath(text2, audioType, onLoaded); } } private IEnumerator LoadHallucinationClips() { string dir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "assets", "Hallucination"); if (!Directory.Exists(dir)) { BalanceTweaksPlugin.logger.LogWarning((object)("Hallucination sound folder not found: " + dir)); yield break; } string[] files = Directory.GetFiles(dir); foreach (string path in files) { if (!Path.GetFileNameWithoutExtension(path).StartsWith("Hallucination", StringComparison.OrdinalIgnoreCase)) { continue; } string text = Path.GetExtension(path).ToLowerInvariant(); if (Array.IndexOf(AudioExtensions, text) >= 0) { yield return LoadClipAtPath(path, GetAudioType(text), delegate(AudioClip clip) { hallucinationClips.Add(clip); }); } } if (hallucinationClips.Count == 0) { BalanceTweaksPlugin.logger.LogWarning((object)("No hallucination sounds found in '" + dir + "' (expected files starting with 'Hallucination')")); } } private IEnumerator LoadClipAtPath(string path, AudioType audioType, Action<AudioClip> onLoaded) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip("file://" + path, audioType); try { request.SendWebRequest(); while (!request.isDone) { yield return null; } if ((int)request.result != 1) { BalanceTweaksPlugin.logger.LogWarning((object)("Failed to load stress sound '" + path + "': " + request.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { BalanceTweaksPlugin.logger.LogWarning((object)("Stress sound '" + path + "' loaded but produced no clip.")); } else { onLoaded?.Invoke(content); } } finally { ((IDisposable)request)?.Dispose(); } } private static AudioType GetAudioType(string extension) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) string text = extension.ToLowerInvariant(); if (!(text == ".ogg")) { if (text == ".mp3") { return (AudioType)13; } return (AudioType)20; } return (AudioType)14; } } internal class DesaturateEffect : MonoBehaviour { private const float DesaturateSaturation = -100f; private const float TriggerThreshold = 0.6f; private const float MinChancePerSecond = 0.01f; private const float MaxChancePerSecond = 0.2f; private const float CooldownAtThreshold = 90f; private const float CooldownAtMaxStress = 45f; private const float DesaturateDuration = 12f; private AudioManager audio; private Volume desaturateVolume; private ColorAdjustments colorAdjustments; private bool hallucinationActive; private float hallucinationTimer; private float cooldownRemaining; private void Awake() { CreateVolume(); audio = ((Component)this).GetComponent<AudioManager>(); } private void Update() { if (hallucinationActive) { if (InGameContext()) { UpdateActiveHallucination(); } else { EndHallucination(StressMechanismPatch.stressTimer); } } else if (cooldownRemaining > 0f) { cooldownRemaining -= Time.deltaTime; } else { if (!InGameContext()) { return; } float stressTimer = StressMechanismPatch.stressTimer; if (stressTimer > 0.6f) { float num = Mathf.InverseLerp(0.6f, 1f, stressTimer); float num2 = Mathf.Lerp(0.01f, 0.2f, num) * Time.deltaTime; if (Random.value < num2) { StartHallucination(); } } } } private void CreateVolume() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown GameObject val = new GameObject("StressDesaturateVolume"); val.transform.SetParent(((Component)this).transform, false); desaturateVolume = val.AddComponent<Volume>(); desaturateVolume.isGlobal = true; desaturateVolume.priority = 100f; desaturateVolume.weight = 0f; VolumeProfile val2 = ScriptableObject.CreateInstance<VolumeProfile>(); desaturateVolume.profile = val2; colorAdjustments = val2.Add<ColorAdjustments>(true); ((VolumeParameter<float>)(object)colorAdjustments.saturation).Override(0f); ((VolumeParameter)colorAdjustments.postExposure).overrideState = false; } private bool InGameContext() { if (!BalanceTweaksPlugin.EnableStressMechanism.Value) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { return false; } PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val != (Object)null && !val.isPlayerDead) { return val.isInsideFactory; } return false; } private void StartHallucination() { hallucinationActive = true; hallucinationTimer = 0f; audio.PlayDesaturateSound(); } private void UpdateActiveHallucination() { hallucinationTimer += Time.deltaTime; ((VolumeParameter<float>)(object)colorAdjustments.saturation).Override(-100f); desaturateVolume.weight = 1f; if (hallucinationTimer >= 12f) { EndHallucination(StressMechanismPatch.stressTimer); } } private void EndHallucination(float stress) { if (hallucinationActive) { hallucinationActive = false; ((VolumeParameter<float>)(object)colorAdjustments.saturation).Override(0f); desaturateVolume.weight = 0f; audio.StopDesaturateSound(); float num = Mathf.InverseLerp(0.6f, 1f, stress); cooldownRemaining = Mathf.Lerp(90f, 45f, num); } } } internal class StressVignetteEffect : MonoBehaviour { private const float VignetteIntensity = 0.85f; private const float VignetteSmoothness = 0.6f; private const float CenterExposure = -1f; private const float TriggerThreshold = 0.4f; private const float MinChancePerSecond = 0.01f; private const float MaxChancePerSecond = 0.225f; private const float CooldownAtThreshold = 80f; private const float CooldownAtMaxStress = 40f; private const float BlackoutDuration = 12f; private AudioManager audio; private Volume stressVolume; private Vignette vignette; private ColorAdjustments colorAdjustments; private bool blackoutActive; private float blackoutTimer; private float blackoutDuration; private float cooldownRemaining; private void Awake() { CreateVolume(); audio = ((Component)this).GetComponent<AudioManager>(); } private void Update() { if (blackoutActive) { if (InGameContext()) { UpdateBlackout(StressMechanismPatch.stressTimer); } else { EndBlackout(StressMechanismPatch.stressTimer); } } else if (cooldownRemaining > 0f) { cooldownRemaining -= Time.deltaTime; } else if (InGameContext()) { float stressTimer = StressMechanismPatch.stressTimer; if (stressTimer > 0.4f) { TryStartBlackout(stressTimer); } } } private void CreateVolume() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("StressVignetteVolume"); val.transform.SetParent(((Component)this).transform, false); stressVolume = val.AddComponent<Volume>(); stressVolume.isGlobal = true; stressVolume.priority = 100f; stressVolume.weight = 0f; VolumeProfile val2 = ScriptableObject.CreateInstance<VolumeProfile>(); stressVolume.profile = val2; vignette = val2.Add<Vignette>(true); ((VolumeParameter<VignetteMode>)(object)vignette.mode).Override((VignetteMode)0); ((VolumeParameter<Color>)(object)vignette.color).Override(Color.black); ((VolumeParameter<Vector2>)(object)vignette.center).Override(new Vector2(0.5f, 0.5f)); ((VolumeParameter<float>)(object)vignette.intensity).Override(0f); ((VolumeParameter<float>)(object)vignette.smoothness).Override(0.6f); ((VolumeParameter<float>)(object)vignette.roundness).Override(1f); ((VolumeParameter<float>)(object)vignette.opacity).Override(1f); colorAdjustments = val2.Add<ColorAdjustments>(true); ((VolumeParameter<float>)(object)colorAdjustments.postExposure).Override(0f); ((VolumeParameter)colorAdjustments.saturation).overrideState = false; } private bool InGameContext() { if (!BalanceTweaksPlugin.EnableStressMechanism.Value) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { return false; } PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val != (Object)null) { return !val.isPlayerDead; } return false; } private void TryStartBlackout(float stress) { float num = Mathf.InverseLerp(0.4f, 1f, stress); float num2 = Mathf.Lerp(0.01f, 0.225f, num) * Time.deltaTime; if (Random.value < num2) { blackoutActive = true; blackoutTimer = 0f; blackoutDuration = 12f; audio.PlayBlackoutSound(); } } private void UpdateBlackout(float stress) { blackoutTimer += Time.deltaTime; ApplyDepth(1f, stress); if (blackoutTimer >= blackoutDuration) { EndBlackout(stress); } } private void ApplyDepth(float alpha, float stress) { ((VolumeParameter<float>)(object)vignette.intensity).Override(0.85f * alpha); ((VolumeParameter<float>)(object)colorAdjustments.postExposure).Override(-1f * alpha); stressVolume.weight = 1f; } private void EndBlackout(float stress) { if (blackoutActive) { blackoutActive = false; ((VolumeParameter<float>)(object)vignette.intensity).Override(0f); ((VolumeParameter<float>)(object)colorAdjustments.postExposure).Override(0f); stressVolume.weight = 0f; audio.StopBlackoutSound(); float num = Mathf.InverseLerp(0.4f, 1f, stress); cooldownRemaining = Mathf.Lerp(80f, 40f, num); } } } internal class TinnitusEffect : MonoBehaviour { private const float DeafenDecibels = -80f; private const float HallucinationMuteDelay = 5f; private const float TinnitusThreshold = 0.2f; private const float HallucinationThreshold = 0.65f; private const float MinChancePerSecond = 0.01f; private const float MaxChancePerSecond = 0.25f; private const float CooldownAtThreshold = 70f; private const float CooldownAtMaxStress = 35f; private const float TinnitusDuration = 5f; private const float HallucinationDuration = 10f; private AudioManager audio; private bool tinnitusActive; private float soundRemaining; private float muteDelayRemaining; private float cooldownRemaining; private void Awake() { audio = ((Component)this).GetComponent<AudioManager>(); } private void Update() { if (tinnitusActive) { if (InGameContext()) { UpdateActiveTinnitus(); } else { EndTinnitus(StressMechanismPatch.stressTimer); } } else if (cooldownRemaining > 0f) { cooldownRemaining -= Time.deltaTime; } else { if (!InGameContext()) { return; } float stressTimer = StressMechanismPatch.stressTimer; if (stressTimer > 0.2f) { float num = Mathf.InverseLerp(0.2f, 1f, stressTimer); float num2 = Mathf.Lerp(0.01f, 0.25f, num) * Time.deltaTime; if (Random.value < num2) { StartTinnitus(stressTimer); } } } } private void OnDisable() { EndTinnitus(StressMechanismPatch.stressTimer); } private bool InGameContext() { if (!BalanceTweaksPlugin.EnableStressMechanism.Value) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { return false; } PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val != (Object)null) { return !val.isPlayerDead; } return false; } private void StartTinnitus(float stress) { tinnitusActive = true; if (stress >= 0.65f && IsInsideFactory() && Random.value < 0.5f) { soundRemaining = 10f; muteDelayRemaining = 5f; audio.PlayHallucinationSound(); } else { soundRemaining = 5f; muteDelayRemaining = 0f; audio.PlayTinnitusSound(); } } private void UpdateActiveTinnitus() { soundRemaining -= Time.deltaTime; if (muteDelayRemaining > 0f) { muteDelayRemaining -= Time.deltaTime; } else { ApplyMute(); } if (soundRemaining <= 0f) { EndTinnitus(StressMechanismPatch.stressTimer); } } private void ApplyMute() { if ((Object)(object)SoundManager.Instance != (Object)null) { SoundManager.Instance.SetDiageticMasterVolume(-80f); } audio.SetDesaturateSoundMuted(muted: true); if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)GameNetworkManager.Instance.localPlayerController) && (Object)(object)val.currentVoiceChatIngameSettings != (Object)null && (Object)(object)val.currentVoiceChatIngameSettings.voiceAudio != (Object)null) { val.currentVoiceChatIngameSettings.voiceAudio.mute = true; } } } private static bool IsInsideFactory() { if ((Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null) { return GameNetworkManager.Instance.localPlayerController.isInsideFactory; } return false; } private void EndTinnitus(float stress) { if (!tinnitusActive) { return; } tinnitusActive = false; soundRemaining = 0f; muteDelayRemaining = 0f; audio.StopTinnitusSound(); audio.StopHallucinationSound(); audio.SetDesaturateSoundMuted(muted: false); float num = Mathf.InverseLerp(0.2f, 1f, stress); cooldownRemaining = Mathf.Lerp(70f, 35f, num); if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)GameNetworkManager.Instance.localPlayerController) && (Object)(object)val.currentVoiceChatIngameSettings != (Object)null && (Object)(object)val.currentVoiceChatIngameSettings.voiceAudio != (Object)null) { val.currentVoiceChatIngameSettings.voiceAudio.mute = false; } } } } }