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 RealBackroomsPatch v1.4.0
BepInEx/plugins/RealBackroomsPatch/RealBackroomsPatch.dll
Decompiled a year ago#define DEBUG using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using Backrooms; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using GameNetcodeStuff; using GroanTubeScrap; using HarmonyLib; using JetBrains.Annotations; using LCSoundTool; using LethalLib.Modules; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TestAccount666.RealBackroomsPatch")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Backrooms Patch, but real")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0")] [assembly: AssemblyProduct("RealBackroomsPatch")] [assembly: AssemblyTitle("TestAccount666.RealBackroomsPatch")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RealBackroomsPatch { public class BackroomsConfig { private readonly ConfigFile _configFile; public ConfigEntry<DropHeldItemsMode> DropHeldItemsMode { get; private set; } public ConfigEntry<bool> ShouldPreventDamageTeleport { get; private set; } public ConfigEntry<bool> ShouldPreventDeathTeleport { get; private set; } public ConfigEntry<bool> ShouldPreventNegativeDamageTeleport { get; private set; } [CanBeNull] public ConfigEntry<bool> ShouldSpawnDeadBody { get; private set; } = null; public ConfigEntry<bool> ShouldTeleportAnnoyingPeople { get; private set; } [CanBeNull] public ConfigEntry<bool> ShouldTeleportToAndReplaceDeadBody { get; private set; } = null; public ConfigEntry<int> TeleportAnnoyingPeopleChance { get; private set; } public ConfigEntry<int> TeleportLethalDamageChance { get; private set; } public ConfigEntry<int> TeleportNonLethalDamageChance { get; private set; } public ConfigEntry<string> PreventBackroomsTeleportEnemies { get; private set; } public ConfigEntry<int> LightFlickerSmoothing { get; private set; } public ConfigEntry<float> LightFlickerMinIntensity { get; private set; } public ConfigEntry<float> LightFlickerMaxIntensity { get; private set; } public ConfigEntry<bool> PlayMusic { get; private set; } public BackroomsConfig(string guid, ConfigFile configFile) { _configFile = configFile; HandleConfig(); RemoveOrphans(); Plugin.InfoLog("Printing Config:"); Plugin.InfoLog($"DropHeldItemsMode: {DropHeldItemsMode.Value}"); Plugin.InfoLog($"ShouldPreventDamageTeleport: {ShouldPreventDamageTeleport.Value}"); Plugin.InfoLog($"ShouldPreventDeathTeleport: {ShouldPreventDeathTeleport.Value}"); Plugin.InfoLog($"ShouldPreventNegativeDamageTeleport: {ShouldPreventNegativeDamageTeleport.Value}"); Plugin.InfoLog($"ShouldTeleportAnnoyingPeople: {ShouldTeleportAnnoyingPeople.Value}"); Plugin.InfoLog($"TeleportAnnoyingPeopleChance: {TeleportAnnoyingPeopleChance.Value}"); Plugin.InfoLog($"TeleportLethalDamageChance: {TeleportLethalDamageChance.Value}"); Plugin.InfoLog($"TeleportNonLethalDamageChance: {TeleportNonLethalDamageChance.Value}"); Plugin.InfoLog("PreventBackroomsTeleportEnemies: " + PreventBackroomsTeleportEnemies.Value); } private void HandleConfig() { //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Expected O, but got Unknown Plugin.InfoLog("Handling Config!"); ShouldPreventDamageTeleport = _configFile.Bind<bool>("Teleport", "Prevent damage teleport", true, "If true, will prevent the backrooms mod from teleporting players that received damage"); ShouldPreventDeathTeleport = _configFile.Bind<bool>("Teleport", "Prevent death teleport", false, "If true, will prevent the backrooms mod from teleporting players that received deadly damage"); ShouldPreventNegativeDamageTeleport = _configFile.Bind<bool>("Teleport", "Prevent negative damage teleport", false, "If true, will prevent the backrooms mod from teleporting players that received negative damage (Aka healing)"); DropHeldItemsMode = _configFile.Bind<DropHeldItemsMode>("Teleport", "Drop held items on teleport", RealBackroomsPatch.DropHeldItemsMode.DropNothing, "When do you want items to be dropped when teleported into the backrooms?"); PreventBackroomsTeleportEnemies = _configFile.Bind<string>("Teleport", "Prevent backrooms when attacked by enemies", "Flowerman, MaskedPlayer", "A list of enemies that will not allow you to spawn in the backrooms, if attacked by them (Needs internal name. Example: Flowerman, Jester, ...)"); TeleportLethalDamageChance = _configFile.Bind<int>("Chances", "Teleport lethal damage chance", 1, "The chance that a person will get teleported into the backrooms with lethal damage (Requires \"Prevent death teleport\" set to \"false\")"); TeleportNonLethalDamageChance = _configFile.Bind<int>("Chances", "Teleport non-lethal damage chance", 3, "The chance that a person will get teleported into the backrooms with non-lethal damage (Requires \"Prevent damage teleport\" set to \"false\")"); ShouldTeleportAnnoyingPeople = _configFile.Bind<bool>("Extra", "Teleport annoying people", false, "If true, will teleport annoying people (e.g. People using horns)"); TeleportAnnoyingPeopleChance = _configFile.Bind<int>("Extra", "Teleport annoying people chance", 10, "The chance that annoying people can get teleported"); LightFlickerSmoothing = _configFile.Bind<int>("Light Flickering", "Smoothing", 25, new ConfigDescription("How much to smooth out the randomness; lower values = sparks, higher = lantern", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>())); LightFlickerMinIntensity = _configFile.Bind<float>("Light Flickering", "Min intensity", 0.2f, new ConfigDescription("Minimum random light intensity", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); LightFlickerMaxIntensity = _configFile.Bind<float>("Light Flickering", "Max intensity", 1.5f, new ConfigDescription("Maximum random light intensity", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); PlayMusic = _configFile.Bind<bool>("Music", "Play Music", true, "If true, plays music in the backrooms"); Plugin.InfoLog("Handled Config!"); } private void RemoveOrphans() { PropertyInfo property = ((object)_configFile).GetType().GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic); if (!(property == null)) { Dictionary<ConfigDefinition, string> dictionary = (Dictionary<ConfigDefinition, string>)property.GetValue(_configFile, null); dictionary.Clear(); _configFile.Save(); } } } [HarmonyPatch(typeof(StartOfRound))] public class DebugPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void PrintConfigOnStart() { Plugin.InfoLog("Printing Config:"); Plugin.InfoLog($"DropHeldItemsMode: {Plugin.backroomsConfig.DropHeldItemsMode.Value}"); Plugin.InfoLog($"ShouldPreventDamageTeleport: {Plugin.backroomsConfig.ShouldPreventDamageTeleport.Value}"); Plugin.InfoLog($"ShouldPreventDeathTeleport: {Plugin.backroomsConfig.ShouldPreventDeathTeleport.Value}"); Plugin.InfoLog($"ShouldPreventNegativeDamageTeleport: {Plugin.backroomsConfig.ShouldPreventNegativeDamageTeleport.Value}"); Plugin.InfoLog($"ShouldTeleportAnnoyingPeople: {Plugin.backroomsConfig.ShouldTeleportAnnoyingPeople.Value}"); Plugin.InfoLog($"TeleportAnnoyingPeopleChance: {Plugin.backroomsConfig.TeleportAnnoyingPeopleChance.Value}"); Plugin.InfoLog($"TeleportLethalDamageChance: {Plugin.backroomsConfig.TeleportLethalDamageChance.Value}"); Plugin.InfoLog($"TeleportNonLethalDamageChance: {Plugin.backroomsConfig.TeleportNonLethalDamageChance.Value}"); Plugin.InfoLog("PreventBackroomsTeleportEnemies: " + Plugin.backroomsConfig.PreventBackroomsTeleportEnemies.Value); } } public static class DependencyChecker { public static bool IsGroanTubeModInstalled() { return Chainloader.PluginInfos.Values.Any((PluginInfo metadata) => metadata.Metadata.GUID.Equals("Kittenji.GroanTubeScrap")); } public static bool IsSoundToolsModInstalled() { return Chainloader.PluginInfos.Values.Any((PluginInfo metadata) => metadata.Metadata.GUID.Equals("LCSoundTool")); } } [DataContract] public enum DropHeldItemsMode { [EnumMember] DropNothing, [EnumMember] DropAll, [EnumMember] DropAllOnDeath, [EnumMember] DropAllOnAnnoying, [EnumMember] DropAllOnAnnoyingAndDeath } [HarmonyPatch] public class GroanTubePatch { public static MethodBase TargetMethod() { Type type2 = AccessTools.GetTypesFromAssembly(typeof(Loader).Assembly).FirstOrDefault((Type type1) => type1.Name.Contains("GroanTubeItem")); return (type2 == null) ? null : AccessTools.FirstMethod(type2, (Func<MethodInfo, bool>)((MethodInfo method) => method.Name.Contains("ItemActivate"))); } public static void Prefix(NoisemakerProp __instance) { if (!((GrabbableObject)__instance).isHeld) { Plugin.WarningLog("Groan Tube not held!"); } else { Patches.TeleportForAnnoyingPeople(((GrabbableObject)__instance).playerHeldBy); } } } [RequireComponent(typeof(SphereCollider))] public class FixedKillBox : MonoBehaviour { public void OnTriggerEnter(Collider other) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB component = ((Component)other).gameObject.GetComponent<PlayerControllerB>(); if (!((Object)(object)component == (Object)null)) { component.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3)); } } } [HarmonyPatch(typeof(LightFlickerEffect))] public static class LightFlickerEffectPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(LightFlickerEffect __instance) { __instance.smoothing = Plugin.backroomsConfig.LightFlickerSmoothing.Value; __instance.minIntensity = Plugin.backroomsConfig.LightFlickerMinIntensity.Value; __instance.maxIntensity = Plugin.backroomsConfig.LightFlickerMaxIntensity.Value; } } public class MusicPlayer : MonoBehaviour { private const int MUSIC_COOLDOWN = 1000; private const float VOLUME_DECREASE_STEP = 0.05f; private readonly Random _random = new Random(); [CanBeNull] private AudioSource _audioSource; private int? _instanceId; private long _nextTimePlaying = UnixTime.GetCurrentTime() + 1000; private bool _shouldStop; private void Update() { if (_shouldStop) { return; } long currentTime = UnixTime.GetCurrentTime(); if (_nextTimePlaying <= currentTime) { int? instanceId = _instanceId; if (!instanceId.HasValue || Object.FindObjectFromInstanceID(_instanceId.Value) == null) { PlayMusic(); } } } private void PlayMusic() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown long currentTime = UnixTime.GetCurrentTime(); int num = _random.Next(0, Plugin.MusicSounds.Count); AudioClip val = Plugin.MusicSounds[num]; Plugin.DebugLog($"Playing clip '{((Object)val).name}' ({num})"); GameObject val2 = new GameObject("TemporaryMusicAudio"); _instanceId = ((Object)val2).GetInstanceID(); _audioSource = val2.AddComponent<AudioSource>(); Debug.Assert((Object)(object)_audioSource != (Object)null, "_audioSource != null"); _audioSource.clip = val; _audioSource.volume = 0.5f; _audioSource.Play(); _nextTimePlaying = (long)((float)(currentTime + 1000) + val.length * 1000f); Object.Destroy((Object)(object)val2, val.length); } public void Stop() { _shouldStop = true; ((MonoBehaviour)this).StartCoroutine(FadeOutMusic()); } private void TerminateMusic() { if (_instanceId.HasValue) { Object val = Object.FindObjectFromInstanceID(_instanceId.Value); if (val == (Object)null) { return; } Object.Destroy(val); } Object.Destroy((Object)(object)this); } private IEnumerator FadeOutMusic() { if ((Object)(object)_audioSource == (Object)null) { TerminateMusic(); yield break; } if (!_audioSource.isPlaying) { TerminateMusic(); yield break; } AudioSource audioSource = _audioSource; audioSource.volume -= 0.05f * Time.deltaTime; if (_audioSource.volume <= 0f) { TerminateMusic(); yield break; } yield return (object)new WaitForEndOfFrame(); yield return FadeOutMusic(); } } [HarmonyPatch] public static class Patches { private static readonly List<ulong> _ImmortalPlayers = new List<ulong>(); private static readonly int _BiohazardDamage = Animator.StringToHash("biohazardDamage"); public static void UnpatchDamagePlayerMethod([NotNull] Harmony harmony) { if (!Unpatch<PlayerControllerB>(harmony, "DamagePlayer", "BeforeDamage")) { Plugin.ErrorLog("Failed to unpatch DamagePlayer!"); } } public static void UnpatchKillPlayerMethod([NotNull] Harmony harmony) { if (!Unpatch<KillLocalPlayer>(harmony, "KillPlayer", "BeforeKilling")) { Plugin.ErrorLog("Failed to unpatch KillPlayer!"); } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] [HarmonyPrefix] public static bool TeleportOnDamage(int damageNumber, CauseOfDeath causeOfDeath, Vector3 force, int deathAnimation, PlayerControllerB __instance) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) bool lethalDamage = __instance.health - damageNumber <= 0; bool negativeDamage = damageNumber <= 0; Plugin.InfoLog("TeleportOnDamage!"); if (!MeetRequirementsForBackrooms(__instance, lethalDamage, negativeDamage)) { return true; } RpcCollection.Instance.TeleportPlayerToBackroomsServerRpc((int)__instance.playerClientId, spawnBody: false, force, causeOfDeath, deathAnimation, lethalDamage, negativeDamage); return false; } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] [HarmonyPrefix] public static bool TeleportOnKill(Vector3 bodyVelocity, bool spawnBody, CauseOfDeath causeOfDeath, int deathAnimation, Vector3 positionOffset, PlayerControllerB __instance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("TeleportOnKill!"); if (Plugin.backroomsConfig.ShouldPreventDeathTeleport.Value) { return true; } if (!MeetRequirementsForBackrooms(__instance)) { return true; } RpcCollection.Instance.TeleportPlayerToBackroomsServerRpc((int)__instance.playerClientId, spawnBody, bodyVelocity, causeOfDeath, deathAnimation); return false; } [HarmonyPatch(typeof(PlayerControllerB), "AllowPlayerDeath")] [HarmonyPrefix] public static bool PreventPlayerDeath(ref bool __result, PlayerControllerB __instance) { if (Plugin.backroomsConfig.ShouldPreventDeathTeleport.Value) { return true; } if (!_ImmortalPlayers.Contains(__instance.playerSteamId)) { return true; } __result = false; return false; } [HarmonyPatch(typeof(NoisemakerProp), "ItemActivate")] [HarmonyPrefix] public static void TeleportForAnnoyingPeople(PlayerControllerB ___playerHeldBy) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (Plugin.backroomsConfig.ShouldTeleportAnnoyingPeople.Value && MeetRequirementsForBackrooms(___playerHeldBy, lethalDamage: false, negativeDamage: false, annoying: true)) { RpcCollection.Instance.TeleportPlayerToBackroomsServerRpc((int)___playerHeldBy.playerClientId, spawnBody: false, default(Vector3), (CauseOfDeath)0, 0, lethalDamage: false, negativeDamage: false, annoying: true); } } [HarmonyPatch(typeof(Backrooms), "TeleportOutOfBackroomsClientRpc")] [HarmonyPostfix] public static void RestoreVanillaSounds(int client) { if (client == (int)StartOfRound.Instance.localPlayerController.playerClientId) { if (Plugin.enableSoundReplace) { SoundToolsWrapper.RestoreAudioClip("WoodStep1"); SoundToolsWrapper.RestoreAudioClip("WoodStep2"); SoundToolsWrapper.RestoreAudioClip("WoodStep3"); SoundToolsWrapper.RestoreAudioClip("WoodStep4"); } MusicPlayer component = ((Component)StartOfRound.Instance.localPlayerController).GetComponent<MusicPlayer>(); if (!((Object)(object)component == (Object)null)) { component.Stop(); } } } [HarmonyPatch(typeof(Backrooms), "TeleportOutOfBackroomsClientRpc")] [HarmonyPostfix] public static void TeleportToAndReplaceDeadBody(int client, Backrooms ___Instance) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Invalid comparison between Unknown and I4 //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if (Plugin.backroomsConfig.ShouldTeleportToAndReplaceDeadBody == null) { return; } Debug.Assert(Plugin.backroomsConfig.ShouldTeleportToAndReplaceDeadBody != null, "Plugin.BackroomsConfig.ShouldTeleportToAndReplaceDeadBody != null"); if (!Plugin.backroomsConfig.ShouldTeleportToAndReplaceDeadBody.Value) { return; } PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[client]; DeadBodyInfo deadBody = val.deadBody; if (!((Object)(object)deadBody == (Object)null) && !deadBody.deactivated && (deadBody.grabBodyObject.isInShipRoom || !deadBody.grabBodyObject.isInFactory || (int)deadBody.causeOfDeath != 2)) { Vector3 itemFloorPosition = deadBody.grabBodyObject.GetItemFloorPosition(default(Vector3)); bool isInFactory = deadBody.grabBodyObject.isInFactory; bool isInShipRoom = deadBody.grabBodyObject.isInShipRoom; bool isInElevator = deadBody.grabBodyObject.isInElevator; Vector3 spawnPosition = deadBody.spawnPosition; spawnPosition.x += Vector3.down.x * 200f; spawnPosition.y += Vector3.down.y * 200f; spawnPosition.z += Vector3.down.z * 200f; deadBody.SetRagdollPositionSafely(spawnPosition, true); if (((NetworkBehaviour)Backrooms.Instance).IsHost || ((NetworkBehaviour)Backrooms.Instance).IsServer) { val.SyncBodyPositionWithClients(); ((NetworkBehaviour)deadBody.grabBodyObject).NetworkObject.Despawn(true); } val.deadBody = null; val.TeleportPlayer(itemFloorPosition, false, 0f, false, true); val.isInsideFactory = isInFactory; val.isInHangarShipRoom = isInShipRoom; val.isInElevator = isInElevator; } } private static bool CompareMethodInfoAndMethodBase(MethodInfo methodInfo, MethodBase methodBase) { if (methodInfo == null) { return false; } if (methodBase == null) { return false; } if (methodInfo.DeclaringType == null) { return false; } if (methodBase.DeclaringType == null) { return false; } bool flag = methodInfo.Name == methodBase.Name; Debug.Assert(methodInfo.DeclaringType.FullName != null, "methodInfo.DeclaringType.FullName != null"); bool flag2 = methodInfo.DeclaringType.FullName.Equals(methodBase.DeclaringType.FullName); return flag && flag2; } private static bool Unpatch<T>([NotNull] Harmony harmony, [NotNull] string originalMethodName, [NotNull] string backroomsMethodName) { MethodInfo methodInfo = typeof(T).GetMethods().FirstOrDefault((MethodInfo method) => method.Name.Equals(originalMethodName)); MethodInfo method2 = typeof(Hook).GetMethod(backroomsMethodName, BindingFlags.Static | BindingFlags.NonPublic); foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods()) { if (!CompareMethodInfoAndMethodBase(methodInfo, allPatchedMethod)) { continue; } Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod); if (patchInfo == null) { Plugin.ErrorLog("Failed to retrieve patch information for method: " + allPatchedMethod.Name); continue; } foreach (Patch prefix in patchInfo.Prefixes) { MethodInfo method3 = prefix.GetMethod((MethodBase)method2); if (!CompareMethodInfoAndMethodBase(method2, method3)) { continue; } harmony.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)1, prefix.owner); Plugin.InfoLog("Unpatched " + originalMethodName + " method!"); return true; } } Plugin.ErrorLog("Target method not found: " + originalMethodName); return false; } private static void DisablePlayerDeath(int secondsFor, PlayerControllerB player) { _ImmortalPlayers.Add(player.playerSteamId); Timer timer = null; timer = new Timer(delegate { _ImmortalPlayers.Remove(player.playerSteamId); timer?.Dispose(); }, null, secondsFor * 1000, -1); } internal static void SpawnDeadBody(PlayerControllerB player, Vector3 bodyVelocity, CauseOfDeath causeOfDeath, int deathAnimation, EnemyAI enemyAI, bool isServer = true) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected I4, but got Unknown string text = $"PositionObject-{player.playerClientId}-{SystemClock.ToUnixTimeMilliseconds(SystemClock.now)}"; GameObject val = new GameObject(text); Transform transform = val.transform; transform.position = player.oldPlayerPosition; transform.rotation = player.thisPlayerBody.rotation; if (isServer) { GameObject val2 = Object.Instantiate<GameObject>(StartOfRound.Instance.ragdollGrabbableObjectPrefab, player.playersManager.propsContainer); val2.GetComponent<NetworkObject>().Spawn(false); val2.GetComponent<RagdollGrabbableObject>().bodyID.Value = (int)player.playerClientId; } player.SpawnDeadBody((int)player.playerClientId, bodyVelocity, (int)causeOfDeath, player, deathAnimation, transform, default(Vector3)); player.deadBody.canBeGrabbedBackByPlayers = true; if (isServer) { player.SyncBodyPositionWithClients(); } if (!((Object)(object)enemyAI == (Object)null)) { FlowermanAI val3 = (FlowermanAI)(object)((enemyAI is FlowermanAI) ? enemyAI : null); if (val3 != null) { ((EnemyAI)val3).inSpecialAnimationWithPlayer = player; val3.FinishKillAnimation(true); } } } private static void EnemyFixes(EnemyAI enemyAI) { FlowermanAI val = (FlowermanAI)(object)((enemyAI is FlowermanAI) ? enemyAI : null); if (val == null) { MaskedPlayerEnemy val2 = (MaskedPlayerEnemy)(object)((enemyAI is MaskedPlayerEnemy) ? enemyAI : null); if (val2 != null) { if ((Object)(object)((EnemyAI)val2).inSpecialAnimationWithPlayer == (Object)(object)GameNetworkManager.Instance.localPlayerController) { HUDManager.Instance.HUDAnimator.SetBool(_BiohazardDamage, false); HUDManager.Instance.HideHUD(true); HUDManager.Instance.HideHUD(false); } val2.FinishKillAnimation(false); ((EnemyAI)val2).inSpecialAnimationWithPlayer = null; } } else { val.FinishKillAnimation(false); ((EnemyAI)val).inSpecialAnimation = false; } } private static bool MeetRequirementsForBackrooms(PlayerControllerB player, bool lethalDamage = true, bool negativeDamage = false, bool annoying = false) { if ((Object)(object)player == (Object)null) { return false; } Plugin.InfoLog("Player exists!"); if (player.isPlayerDead || !player.AllowPlayerDeath() || ((Component)player).GetComponent<BackroomsHelper>().HasBeenInTheBackrooms) { return false; } Plugin.InfoLog("Passed checks!"); if (negativeDamage && Plugin.backroomsConfig.ShouldPreventNegativeDamageTeleport.Value) { return false; } Plugin.InfoLog("Negative damage check passed!"); Plugin.InfoLog("Prevent Death Teleport: " + Plugin.backroomsConfig.ShouldPreventDeathTeleport.Value); Plugin.InfoLog("Prevent Damage Teleport: " + Plugin.backroomsConfig.ShouldPreventDamageTeleport.Value); if (!annoying) { if ((lethalDamage && Plugin.backroomsConfig.ShouldPreventDeathTeleport.Value) || (!lethalDamage && Plugin.backroomsConfig.ShouldPreventDamageTeleport.Value)) { goto IL_0122; } } else if (!Plugin.backroomsConfig.ShouldTeleportAnnoyingPeople.Value) { goto IL_0122; } Plugin.InfoLog("Lethal or not and Annoying check passed!"); EnemyAI inAnimationWithEnemy = player.inAnimationWithEnemy; List<string> list = Plugin.backroomsConfig.PreventBackroomsTeleportEnemies.Value.ToLower().Replace(" ", "").Split(",") .ToList(); if ((Object)(object)inAnimationWithEnemy != (Object)null && list.Contains(inAnimationWithEnemy.enemyType.enemyName.ToLower())) { return false; } Plugin.InfoLog("AI check passed!"); int value = (annoying ? Plugin.backroomsConfig.TeleportAnnoyingPeopleChance : (lethalDamage ? Plugin.backroomsConfig.TeleportLethalDamageChance : Plugin.backroomsConfig.TeleportNonLethalDamageChance)).Value; Plugin.InfoLog($"Teleport chance: {value}!"); int num = Random.Range(1, 100); Plugin.InfoLog($"Rolled chance: {num}!"); if (num > value) { return false; } Plugin.InfoLog("Chance check passed!"); return true; IL_0122: return false; } internal static void TeleportPlayerToBackrooms(PlayerControllerB player, bool spawnBody = false, Vector3 bodyVelocity = default(Vector3), CauseOfDeath causeOfDeath = 0, int deathAnimation = 0, bool lethalDamage = true, bool annoying = false) { //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("Teleport To Backrooms method!"); EnemyAI inAnimationWithEnemy = player.inAnimationWithEnemy; int enemyId = (((Object)(object)inAnimationWithEnemy == (Object)null) ? (-1) : inAnimationWithEnemy.thisEnemyIndex); EnemyFixes(inAnimationWithEnemy); Plugin.InfoLog("Enemy Fixes passed!"); if (ShouldDropItems(lethalDamage, annoying)) { player.DropAllHeldItemsAndSync(); } DisablePlayerDeath(2, player); Plugin.InfoLog("Sending player to backrooms!"); Backrooms.Instance.TeleportToBackroomsServerRpc((int)player.playerClientId); if (Plugin.enableSoundReplace && player.playerClientId == StartOfRound.Instance.localPlayerController.playerClientId) { for (int i = 1; i <= 4; i++) { int num = i - 1; AudioClip newClip = Plugin.CarpetSounds[num]; SoundToolsWrapper.ReplaceAudioClip($"WoodStep{i}", newClip); } ((Component)player).gameObject.AddComponent<MusicPlayer>(); } if (Plugin.backroomsConfig.ShouldSpawnDeadBody != null) { Debug.Assert(Plugin.backroomsConfig.ShouldSpawnDeadBody != null, "Plugin.BackroomsConfig.ShouldSpawnDeadBody != null"); if (Plugin.backroomsConfig.ShouldSpawnDeadBody.Value && spawnBody) { Plugin.InfoLog("Spawning body!"); RpcCollection.Instance.SpawnDeadBodyServerRpc((int)player.playerClientId, bodyVelocity, causeOfDeath, deathAnimation, enemyId); } } } private static bool ShouldDropItems(bool lethalDamage, bool annoying) { int num; switch (Plugin.backroomsConfig.DropHeldItemsMode.Value) { case DropHeldItemsMode.DropAllOnAnnoyingAndDeath: num = 0; goto IL_003a; case DropHeldItemsMode.DropAllOnDeath: num = 1; goto IL_003a; case DropHeldItemsMode.DropAllOnAnnoying: if (!annoying) { break; } goto case DropHeldItemsMode.DropAll; case DropHeldItemsMode.DropAll: { return true; } IL_003a: if (!lethalDamage) { if (num == 0) { goto case DropHeldItemsMode.DropAllOnAnnoying; } if (num == 1) { break; } } goto case DropHeldItemsMode.DropAll; } return false; } } [BepInPlugin("TestAccount666.RealBackroomsPatch", "RealBackroomsPatch", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const int CARPET_SOUNDS_SIZE = 4; private static Plugin _instance; public static BackroomsConfig backroomsConfig; public static readonly AudioClip[] CarpetSounds = (AudioClip[])(object)new AudioClip[4]; public static bool enableSoundReplace; public static readonly List<AudioClip> MusicSounds = new List<AudioClip>(); private void Awake() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown _instance = this; WarningLog("This Mod is my very first Mod and is still work in progress."); backroomsConfig = new BackroomsConfig("TestAccount666.RealBackroomsPatch", ((BaseUnityPlugin)this).Config); Harmony val = new Harmony("TestAccount666.RealBackroomsPatch"); InfoLog("Creating patches!"); val.PatchAll(typeof(Patches)); val.PatchAll(typeof(LightFlickerEffect)); val.PatchAll(typeof(RpcCollectionLoader)); val.PatchAll(typeof(SmilerPatch)); if (DependencyChecker.IsGroanTubeModInstalled()) { val.PatchAll(typeof(GroanTubePatch)); } enableSoundReplace = DependencyChecker.IsSoundToolsModInstalled(); InfoLog("Unpatching Backrooms Mod!"); Patches.UnpatchDamagePlayerMethod(val); Patches.UnpatchKillPlayerMethod(val); 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); } } } ((MonoBehaviour)this).StartCoroutine(LoadAudioClips()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin RealBackroomsPatch is loaded!"); } internal static void DebugLog(string msg) { ((BaseUnityPlugin)_instance).Logger.LogDebug((object)msg); } internal static void InfoLog(string msg) { ((BaseUnityPlugin)_instance).Logger.LogInfo((object)msg); } internal static void WarningLog(string msg) { ((BaseUnityPlugin)_instance).Logger.LogWarning((object)msg); } internal static void ErrorLog(string msg) { ((BaseUnityPlugin)_instance).Logger.LogError((object)msg); } private static IEnumerator LoadAudioClips() { string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); InfoLog("Loading Carpet Step Sounds..."); Debug.Assert(assemblyDirectory != null, "assemblyDirectory != null"); string audioPath = Path.Combine(assemblyDirectory, "sounds"); audioPath = (Directory.Exists(audioPath) ? audioPath : Path.Combine(assemblyDirectory)); string carpetAudioPath2 = Path.Combine(audioPath, "CarpetSound"); carpetAudioPath2 = (Directory.Exists(carpetAudioPath2) ? carpetAudioPath2 : Path.Combine(assemblyDirectory)); for (int index = 1; index <= 4; index++) { int sound = index - 1; AudioClip carpetAudioClip = LoadAudioClipFromFile(new Uri(Path.Combine(carpetAudioPath2, $"CarpetStep{index}.wav")), $"CarpetStep{index}", (AudioType)20); CarpetSounds[sound] = carpetAudioClip; InfoLog("Loaded clip '" + ((Object)carpetAudioClip).name + "'!"); } InfoLog("Loading Music..."); string musicAudioPath2 = Path.Combine(audioPath, "Music"); musicAudioPath2 = (Directory.Exists(musicAudioPath2) ? musicAudioPath2 : Path.Combine(assemblyDirectory)); foreach (string file in Directory.EnumerateFiles(musicAudioPath2)) { string fileName = Path.GetFileName(file); if (fileName.ToLower().EndsWith(".ogg") && fileName.ToLower().StartsWith("music")) { Uri filePath = new Uri(file); string text = fileName; AudioClip musicAudioClip = LoadAudioClipFromFile(filePath, text.Substring(0, text.Length - 4), (AudioType)14); MusicSounds.Add(musicAudioClip); InfoLog("Loaded clip '" + ((Object)musicAudioClip).name + "'!"); } } yield break; } private static AudioClip LoadAudioClipFromFile(Uri filePath, string name, AudioType audioType = 20) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(filePath, audioType); try { UnityWebRequestAsyncOperation val = audioClip.SendWebRequest(); while (!((AsyncOperation)val).isDone) { Thread.Sleep(100); } if ((int)audioClip.result != 1) { ErrorLog("Failed to load AudioClip: " + audioClip.error); return null; } AudioClip content = DownloadHandlerAudioClip.GetContent(audioClip); ((Object)content).name = name; return content; } finally { ((IDisposable)audioClip)?.Dispose(); } } } public static class PluginInfo { public const string PLUGIN_GUID = "TestAccount666.RealBackroomsPatch"; public const string PLUGIN_NAME = "RealBackroomsPatch"; public const string PLUGIN_VERSION = "1.1.0"; } public class RpcCollection : NetworkBehaviour { public static RpcCollection Instance { get; private set; } public override void OnNetworkSpawn() { Plugin.InfoLog("RpcCollection network spawned!"); if ((Object)(object)NetworkManager.Singleton == (Object)null) { Plugin.ErrorLog("NetworkManager.Singleton couldn't be found?!"); return; } if ((NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) && (Object)(object)Instance != (Object)null && (Object)(object)((Component)Instance).gameObject != (Object)null) { ((Component)Instance).gameObject.GetComponent<NetworkObject>().Despawn(true); } Instance = this; } [ClientRpc] public void SpawnDeadBodyClientRpc(int playerId, Vector3 bodyVelocity, CauseOfDeath causeOfDeath, int deathAnimation, int enemyId) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("Spawn dead body ClientRpc!"); PlayerControllerB player = StartOfRound.Instance.allPlayerScripts[playerId]; EnemyAI enemyAI = ((enemyId != -1) ? RoundManager.Instance.SpawnedEnemies[enemyId] : null); Patches.SpawnDeadBody(player, bodyVelocity, causeOfDeath, deathAnimation, enemyAI, ((NetworkBehaviour)this).IsServer); } [ServerRpc(RequireOwnership = false)] public void SpawnDeadBodyServerRpc(int playerId, Vector3 bodyVelocity, CauseOfDeath causeOfDeath, int deathAnimation, int enemyId) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("Spawn body ServerRpc!"); PlayerControllerB player = StartOfRound.Instance.allPlayerScripts[playerId]; EnemyAI enemyAI = ((enemyId != -1) ? RoundManager.Instance.SpawnedEnemies[enemyId] : null); if (((NetworkBehaviour)this).IsServer && !((NetworkBehaviour)this).IsHost) { Plugin.InfoLog("Server spawning DeadBody!"); Patches.SpawnDeadBody(player, bodyVelocity, causeOfDeath, deathAnimation, enemyAI, ((NetworkBehaviour)this).IsServer); } else { SpawnDeadBodyClientRpc(playerId, bodyVelocity, causeOfDeath, deathAnimation, enemyId); } } [ClientRpc] public void TeleportPlayerToBackroomsClientRpc(int playerId, bool spawnBody = false, Vector3 bodyVelocity = default(Vector3), CauseOfDeath causeOfDeath = 0, int deathAnimation = 0, bool lethalDamage = true, bool negativeDamage = false, bool annoying = false) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("Teleport Player ClientRcp!"); PlayerControllerB player = StartOfRound.Instance.allPlayerScripts[playerId]; Patches.TeleportPlayerToBackrooms(player, spawnBody, bodyVelocity, causeOfDeath, deathAnimation, lethalDamage, annoying); } [ServerRpc(RequireOwnership = false)] public void TeleportPlayerToBackroomsServerRpc(int playerId, bool spawnBody = false, Vector3 bodyVelocity = default(Vector3), CauseOfDeath causeOfDeath = 0, int deathAnimation = 0, bool lethalDamage = true, bool negativeDamage = false, bool annoying = false) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) Plugin.InfoLog("Teleport Player ServerRcp!"); PlayerControllerB player = StartOfRound.Instance.allPlayerScripts[playerId]; if (((NetworkBehaviour)this).IsServer && !((NetworkBehaviour)this).IsHost) { Plugin.InfoLog("Server but not host Teleport!"); Patches.TeleportPlayerToBackrooms(player, spawnBody, bodyVelocity, causeOfDeath, deathAnimation, lethalDamage, annoying); } TeleportPlayerToBackroomsClientRpc(playerId, spawnBody, bodyVelocity, causeOfDeath, deathAnimation, lethalDamage, negativeDamage, annoying); } } [HarmonyPatch] public class RpcCollectionLoader { private static GameObject _networkPrefab; private static int _networkPrefabInstanceId = -1; private static int _networkHandlerHostInstanceId = -1; private static NetworkObject _networkHandlerHost; [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnDestroy")] public static void AfterDestroy() { if ((Object)(object)_networkPrefab != (Object)null) { RpcCollection component = _networkPrefab.GetComponent<RpcCollection>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Object.Destroy((Object)(object)_networkPrefab); _networkPrefab = null; _networkPrefabInstanceId = -1; } if (!((Object)(object)_networkHandlerHost == (Object)null)) { Object.Destroy((Object)(object)_networkHandlerHost); _networkHandlerHost = null; _networkPrefabInstanceId = -1; } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Awake")] public static void SpawnNetworkHandler1() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) _networkPrefab = NetworkPrefabs.CreateNetworkPrefab("RpcCollectionHandler"); _networkPrefab.AddComponent<RpcCollection>(); _networkPrefabInstanceId = ((Object)_networkPrefab).GetInstanceID(); Object.DontDestroyOnLoad((Object)(object)_networkPrefab); if ((NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) && (!((Object)(object)_networkHandlerHost != (Object)null) || !(Object.FindObjectFromInstanceID(_networkHandlerHostInstanceId) != (Object)null))) { _networkHandlerHost = Object.Instantiate<GameObject>(_networkPrefab, Vector3.zero, Quaternion.identity).GetComponent<NetworkObject>(); _networkHandlerHost.Spawn(false); Object.DontDestroyOnLoad((Object)(object)_networkHandlerHost); _networkHandlerHostInstanceId = ((Object)_networkHandlerHost).GetInstanceID(); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnEnable")] public static void SpawnNetworkHandler2() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_networkPrefab == (Object)null || Object.FindObjectFromInstanceID(_networkPrefabInstanceId) == (Object)null) { _networkPrefab = NetworkPrefabs.CreateNetworkPrefab("RpcCollectionHandler"); _networkPrefab.AddComponent<RpcCollection>(); _networkPrefabInstanceId = ((Object)_networkPrefab).GetInstanceID(); Object.DontDestroyOnLoad((Object)(object)_networkPrefab); } if ((NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) && (!((Object)(object)_networkHandlerHost != (Object)null) || !(Object.FindObjectFromInstanceID(_networkHandlerHostInstanceId) != (Object)null))) { _networkHandlerHost = Object.Instantiate<GameObject>(_networkPrefab, Vector3.zero, Quaternion.identity).GetComponent<NetworkObject>(); _networkHandlerHost.Spawn(false); Object.DontDestroyOnLoad((Object)(object)_networkHandlerHost); _networkHandlerHostInstanceId = ((Object)_networkHandlerHost).GetInstanceID(); } } } [HarmonyPatch(typeof(Smiler))] public static class SmilerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void FixSmiler(Smiler __instance) { GameObject gameObject = ((Component)((Component)__instance).gameObject.transform.Find("KillBox")).gameObject; KillBox component = gameObject.GetComponent<KillBox>(); Object.Destroy((Object)(object)component); gameObject.AddComponent<FixedKillBox>(); } } internal static class SoundToolsWrapper { public static void RestoreAudioClip(string name) { SoundTool.RestoreAudioClip(name); } public static void ReplaceAudioClip(string originalName, AudioClip newClip) { SoundTool.ReplaceAudioClip(originalName, newClip); } } public static class StartOfRoundPatch { public static void SyncBackroomsConfig() { if (Plugin.backroomsConfig == null) { Plugin.ErrorLog("Config not found, please report this!"); } else if (((NetworkBehaviour)RoundManager.Instance).IsHost) { Plugin.InfoLog("Skipping sync request, since we're the host!"); } } } public static class UnixTime { public static long GetCurrentTime() { return (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; } } }