using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.Json; using FusionKillerMode; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSLZ.VRMK; using Il2CppTMPro; using LabFusion.Data; using LabFusion.Downloading; using LabFusion.Entities; using LabFusion.Extensions; using LabFusion.Marrow; using LabFusion.Marrow.Extenders; using LabFusion.Marrow.Integration; using LabFusion.Marrow.Pool; using LabFusion.Menu.Data; using LabFusion.Network; using LabFusion.Player; using LabFusion.Preferences.Client; using LabFusion.RPC; using LabFusion.SDK.Gamemodes; using LabFusion.SDK.Metadata; using LabFusion.SDK.Modules; using LabFusion.SDK.MonoBehaviours; using LabFusion.SDK.Triggers; using LabFusion.Scene; using LabFusion.Senders; using LabFusion.UI.Popups; using LabFusion.Utilities; using LabFusion.Voice; using MelonLoader; using MelonLoader.Preferences; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BONELAB Fusion Killer Mode")] [assembly: AssemblyFileVersion("0.3.11")] [assembly: MelonInfo(typeof(KillerModeMod), "BONELAB Fusion Killer Mode", "0.3.11", "Fusion Killer Mode Team", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonOptionalDependencies(new string[] { "LabFusion" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.11.0")] [module: UnverifiableCode] namespace FusionKillerMode; internal static class EmbeddedWavLoader { private const string ResourcePrefix = "FusionKillerMode.Assets.Audio."; public static AudioClip Load(string fileName) { try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FusionKillerMode.Assets.Audio." + fileName); if (stream == null) { return null; } using BinaryReader binaryReader = new BinaryReader(stream); if (ReadFourCc(binaryReader) != "RIFF") { return null; } binaryReader.ReadUInt32(); if (ReadFourCc(binaryReader) != "WAVE") { return null; } ushort num = 0; ushort num2 = 0; int num3 = 0; ushort num4 = 0; byte[] array = null; while (stream.Position + 8 <= stream.Length) { string text = ReadFourCc(binaryReader); uint num5 = binaryReader.ReadUInt32(); long val = stream.Position + num5 + (num5 & 1); if (text == "fmt ") { num = binaryReader.ReadUInt16(); num2 = binaryReader.ReadUInt16(); num3 = binaryReader.ReadInt32(); binaryReader.ReadUInt32(); binaryReader.ReadUInt16(); num4 = binaryReader.ReadUInt16(); } else if (text == "data") { array = binaryReader.ReadBytes(checked((int)num5)); } stream.Position = Math.Min(val, stream.Length); } if (num != 1 || num4 != 16 || num2 == 0 || num3 <= 0 || array == null) { return null; } int num6 = array.Length / 2; float[] array2 = new float[num6]; for (int i = 0; i < num6; i++) { array2[i] = (float)BitConverter.ToInt16(array, i * 2) / 32768f; } int num7 = num6 / num2; AudioClip obj = AudioClip.Create("KillerMode_" + Path.GetFileNameWithoutExtension(fileName), num7, (int)num2, num3, false); obj.SetData(Il2CppStructArray.op_Implicit(array2), 0); MelonLogger.Msg($"[Killer Mode audio] Loaded {fileName} ({num2}ch, {num3} Hz, {num7} frames)."); return obj; } catch (Exception ex) { MelonLogger.Warning("[Killer Mode] Could not load embedded sound " + fileName + ": " + ex.Message); return null; } } private static string ReadFourCc(BinaryReader reader) { return new string(reader.ReadChars(4)); } } public static class KillerContentReferences { public const string DefaultCombatKnife = "c1534c5a-1fb8-477c-afbe-2a95436f6d62"; public const string BowieKnife = "BurntLettuce.BowieKnife.Spawnable.BowieKnife"; public const string GiantCleaver = "HorrorLab.GiantCleaver2.Spawnable.GiantCleaver"; public const string DeathloopMachete = "Frictic.DeathloopMachete.Spawnable.DeathloopMachete"; public const string Flashlight = "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks"; public const string FlashlightBrighter = "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworksBrighter"; public const string PolyBlankAvatar = "c3534c5a-94b2-40a4-912a-24a8506f6c79"; public const string StrongAvatar = "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong"; public const string HeavyAvatar = "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Heavy"; public const string FastAvatar = "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Fast"; public static string GetWeaponBarcode(KillerWeaponChoice choice) { return choice switch { KillerWeaponChoice.DefaultCombatKnife => "c1534c5a-1fb8-477c-afbe-2a95436f6d62", KillerWeaponChoice.GiantCleaver => "HorrorLab.GiantCleaver2.Spawnable.GiantCleaver", KillerWeaponChoice.DeathloopMachete => "Frictic.DeathloopMachete.Spawnable.DeathloopMachete", KillerWeaponChoice.BowieKnife => "BurntLettuce.BowieKnife.Spawnable.BowieKnife", _ => string.Empty, }; } public static string GetKillerAvatarBarcode(KillerSkinChoice choice) { return choice switch { KillerSkinChoice.Strong => "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", KillerSkinChoice.Heavy => "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Heavy", KillerSkinChoice.Fast => "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Fast", KillerSkinChoice.PolyBlank => "c3534c5a-94b2-40a4-912a-24a8506f6c79", _ => string.Empty, }; } public static bool IsInstalled(string barcode) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(barcode) || !AssetWarehouse.ready) { return false; } return CrateFilterer.HasCrate(new Barcode(barcode)); } public static bool IsAvatarInstalled(string barcode) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(barcode) || !AssetWarehouse.ready) { return false; } return CrateFilterer.HasCrate(new Barcode(barcode)); } public static bool IsAnyCrateInstalled(string barcode) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(barcode) || !AssetWarehouse.ready) { return false; } return CrateFilterer.HasCrate(new Barcode(barcode)); } } public static class KillerEnvironmentEffects { private static bool _captured; private static float _ambientIntensity; private static float _reflectionIntensity; private static Color _ambientLight; private static bool _fog; private static Color _fogColor; private static FogMode _fogMode; private static float _fogDensity; private static float _fogStartDistance; private static float _fogEndDistance; private static GameObject _overlayRoot; private static Image _overlayImage; public static void Apply(bool darkMap, int darknessStrength, bool darkFog, int fogStrength) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) Capture(); RestoreCapturedValues(); if (darkMap) { float num = Mathf.Lerp(1f, 0.08f, Mathf.Clamp01((float)darknessStrength / 100f)); RenderSettings.ambientIntensity = _ambientIntensity * num; RenderSettings.reflectionIntensity = _reflectionIntensity * Mathf.Lerp(1f, 0.18f, Mathf.Clamp01((float)darknessStrength / 100f)); RenderSettings.ambientLight = _ambientLight * num; } if (darkFog) { float num2 = Mathf.Clamp01((float)fogStrength / 100f); RenderSettings.fog = true; RenderSettings.fogMode = (FogMode)1; RenderSettings.fogColor = Color.Lerp(new Color(0.025f, 0.03f, 0.04f, 1f), Color.black, num2 * 0.75f); RenderSettings.fogStartDistance = Mathf.Lerp(15f, 2f, num2); RenderSettings.fogEndDistance = Mathf.Lerp(70f, 12f, num2); RenderSettings.fogDensity = Mathf.Max(_fogDensity, Mathf.Lerp(0.008f, 0.055f, num2)); } ApplyReliableOverlay(darkMap, darknessStrength, darkFog, fogStrength); } public static void Restore() { if (_captured) { RestoreCapturedValues(); if ((Object)(object)_overlayRoot != (Object)null) { _overlayRoot.SetActive(false); } _captured = false; } } private static void Capture() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!_captured) { _captured = true; _ambientIntensity = RenderSettings.ambientIntensity; _reflectionIntensity = RenderSettings.reflectionIntensity; _ambientLight = RenderSettings.ambientLight; _fog = RenderSettings.fog; _fogColor = RenderSettings.fogColor; _fogMode = RenderSettings.fogMode; _fogDensity = RenderSettings.fogDensity; _fogStartDistance = RenderSettings.fogStartDistance; _fogEndDistance = RenderSettings.fogEndDistance; } } private static void RestoreCapturedValues() { //IL_0014: 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_0032: Unknown result type (might be due to invalid IL or missing references) RenderSettings.ambientIntensity = _ambientIntensity; RenderSettings.reflectionIntensity = _reflectionIntensity; RenderSettings.ambientLight = _ambientLight; RenderSettings.fog = _fog; RenderSettings.fogColor = _fogColor; RenderSettings.fogMode = _fogMode; RenderSettings.fogDensity = _fogDensity; RenderSettings.fogStartDistance = _fogStartDistance; RenderSettings.fogEndDistance = _fogEndDistance; } private static void ApplyReliableOverlay(bool darkMap, int darknessStrength, bool darkFog, int fogStrength) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) float num = (darkMap ? (Mathf.Clamp01((float)darknessStrength / 100f) * 0.48f) : 0f); float num2 = (darkFog ? Mathf.Lerp(0.04f, 0.22f, Mathf.Clamp01((float)fogStrength / 100f)) : 0f); float num3 = 1f - (1f - num) * (1f - num2); if (!(num3 <= 0.001f) && RigData.HasPlayer) { RigRefs refs = RigData.Refs; if (!((Object)(object)((refs != null) ? refs.Headset : null) == (Object)null)) { EnsureOverlay(); _overlayRoot.SetActive(true); ((Graphic)_overlayImage).color = new Color(0.005f, 0.008f, 0.012f, Mathf.Clamp01(num3)); return; } } if ((Object)(object)_overlayRoot != (Object)null) { _overlayRoot.SetActive(false); } } private static void EnsureOverlay() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_overlayRoot != (Object)null) { _overlayRoot.transform.SetParent(RigData.Refs.Headset, false); return; } _overlayRoot = new GameObject("Killer Mode Synchronized Darkness"); ((Object)_overlayRoot).hideFlags = (HideFlags)52; _overlayRoot.transform.SetParent(RigData.Refs.Headset, false); _overlayRoot.transform.localPosition = new Vector3(0f, 0f, 0.5f); _overlayRoot.transform.localRotation = Quaternion.identity; _overlayRoot.transform.localScale = Vector3.one * 0.45f; Canvas obj = _overlayRoot.AddComponent(); obj.renderMode = (RenderMode)2; obj.sortingOrder = -32768; ((Component)obj).GetComponent().sizeDelta = new Vector2(8f, 8f); GameObject val = new GameObject("Dark Atmosphere Panel"); val.transform.SetParent(_overlayRoot.transform, false); _overlayImage = val.AddComponent(); ((Graphic)_overlayImage).raycastTarget = false; RectTransform rectTransform = ((Graphic)_overlayImage).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; } } public static class KillerFlashEffect { private static GameObject _root; private static Image _image; private static float _remaining; private static float _duration; private static float _intensity; public static void Trigger(float duration, float intensity) { if (RigData.HasPlayer) { EnsureVisual(); _duration = Mathf.Max(0.25f, duration); _remaining = _duration; _intensity = Mathf.Clamp01(intensity); _root.SetActive(true); SetAlpha(_intensity); } } public static void Tick() { if (!((Object)(object)_root == (Object)null) && !(_remaining <= 0f)) { _remaining = Mathf.Max(0f, _remaining - Time.deltaTime); float num = ((_duration <= 0f) ? 0f : (_remaining / _duration)); SetAlpha(_intensity * num * num); if (_remaining <= 0f) { _root.SetActive(false); } } } public static void Clear() { _remaining = 0f; if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _image = null; } private static void EnsureVisual() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { _root.transform.SetParent(RigData.Refs.Headset, false); return; } _root = new GameObject("Killer Mode Flashbang Whiteout"); ((Object)_root).hideFlags = (HideFlags)52; _root.transform.SetParent(RigData.Refs.Headset, false); _root.transform.localPosition = new Vector3(0f, 0f, 0.45f); _root.transform.localRotation = Quaternion.identity; _root.transform.localScale = Vector3.one * 0.45f; Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)2; obj.sortingOrder = 32767; ((Component)obj).GetComponent().sizeDelta = new Vector2(8f, 8f); GameObject val = new GameObject("White Panel"); val.transform.SetParent(_root.transform, false); _image = val.AddComponent(); RectTransform rectTransform = ((Graphic)_image).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Graphic)_image).color = Color.white; } private static void SetAlpha(float alpha) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_image != (Object)null) { ((Graphic)_image).color = new Color(1f, 1f, 1f, Mathf.Clamp01(alpha)); } } } [RegisterTypeInIl2Cpp] public sealed class KillerFlashbangRuntime : MonoBehaviour { private GamemodeItem _item; private NetworkEntity _entity; private float _releasedTimer; private bool _detonated; public KillerFlashbangRuntime(IntPtr ptr) : base(ptr) { } [HideFromIl2Cpp] public void Initialize(NetworkEntity entity, GamemodeItem item) { _entity = entity; _item = item; } private void Update() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (_detonated || (Object)(object)_item == (Object)null || _entity == null || !_entity.IsOwner || !_item.HasBeenInteracted) { return; } if (_item.IsHeld) { _releasedTimer = 0f; return; } _releasedTimer += Time.deltaTime; if (!(_releasedTimer < 1.2f)) { _detonated = true; KillerModeGamemode.RequestFlashbangDetonation(((Component)this).transform.position); } } } public static class KillerModeAudio { private const int SampleRate = 44100; private static AudioClip _roundStart; private static AudioClip _roundEnd; private static AudioClip _lastSurvivor; private static AudioClip _countdownTick; private static AudioClip _heartbeatSlow; private static AudioClip _heartbeatFast; private static AudioSource _heartbeatSource; private static AudioSource _oneShotSource; private static bool _heartbeatIsFast; public static void PlayRoundStart() { Play(_roundStart ?? (_roundStart = Load("round_start.wav", CreateRoundStart)), 0.72f); } public static void PlayCountdownTick(int remaining) { remaining = Mathf.Clamp(remaining, 1, 10); Play(_countdownTick ?? (_countdownTick = Load("countdown_tick.wav", () => CreateCountdownTick(remaining))), (remaining <= 3) ? 0.82f : 0.58f); } public static void PlayLastSurvivor() { Play(_lastSurvivor ?? (_lastSurvivor = Load("last_survivor.wav", CreateLastSurvivor)), 0.64f); } public static void PlayRoundEnd(bool survivorsWin) { Play(_roundEnd ?? (_roundEnd = Load("round_end.wav", () => CreateRoundEnd(survivorsWin))), 0.76f); } public static void SetHeartbeat(bool enabled, bool fast) { if (!enabled) { StopHeartbeat(); return; } AudioClip val = (fast ? (_heartbeatFast ?? (_heartbeatFast = Load("heartbeat_fast.wav", () => CreateHeartbeat(fast: true)))) : (_heartbeatSlow ?? (_heartbeatSlow = Load("heartbeat_slow.wav", () => CreateHeartbeat(fast: false))))); if (!((Object)(object)val == (Object)null)) { EnsureHeartbeatSource(); if ((Object)(object)_heartbeatSource.clip != (Object)(object)val || _heartbeatIsFast != fast) { _heartbeatSource.Stop(); _heartbeatSource.clip = val; _heartbeatIsFast = fast; } _heartbeatSource.volume = (fast ? 0.34f : 0.27f); if (!_heartbeatSource.isPlaying) { _heartbeatSource.Play(); } } } public static void StopHeartbeat() { if (!((Object)(object)_heartbeatSource == (Object)null)) { _heartbeatSource.Stop(); _heartbeatSource.clip = null; } } private static void Play(AudioClip clip, float volume) { if (!((Object)(object)clip == (Object)null)) { EnsureOneShotSource(); _oneShotSource.PlayOneShot(clip, Mathf.Clamp01(volume)); MelonLogger.Msg("[Killer Mode audio] Playing " + ((Object)clip).name + "."); } } private static AudioClip Load(string fileName, Func fallback) { return EmbeddedWavLoader.Load(fileName) ?? fallback(); } private static void EnsureHeartbeatSource() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)_heartbeatSource != (Object)null)) { GameObject val = new GameObject("KillerMode_LastSurvivorHeartbeat"); Object.DontDestroyOnLoad((Object)val); _heartbeatSource = val.AddComponent(); _heartbeatSource.playOnAwake = false; _heartbeatSource.loop = true; _heartbeatSource.spatialBlend = 0f; _heartbeatSource.bypassReverbZones = true; _heartbeatSource.outputAudioMixerGroup = LocalAudioPlayer.InHead; } } private static void EnsureOneShotSource() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)_oneShotSource != (Object)null)) { GameObject val = new GameObject("KillerMode_OneShotAudio"); Object.DontDestroyOnLoad((Object)val); _oneShotSource = val.AddComponent(); _oneShotSource.playOnAwake = false; _oneShotSource.loop = false; _oneShotSource.spatialBlend = 0f; _oneShotSource.bypassReverbZones = true; _oneShotSource.outputAudioMixerGroup = LocalAudioPlayer.InHead; } } private static AudioClip CreateRoundStart() { return Create("KillerMode_RoundStart", 1.25f, delegate(float time, Random random) { float num = Mathf.Sin((float)Math.PI * 2f * (58f + 34f * time) * time) * Mathf.Exp(-3.4f * time); float num2 = Mathf.Sin((float)Math.PI * 2f * (310f + 520f * time) * time) * Mathf.Exp(-4.5f * time); float num3 = ((float)random.NextDouble() * 2f - 1f) * Mathf.Exp(-8f * time); return 0.62f * num + 0.18f * num2 + 0.1f * num3; }); } private static AudioClip CreateCountdownTick(int remaining) { float frequency = 520f + (float)(10 - remaining) * 38f; return Create($"KillerMode_Countdown_{remaining}", 0.16f, (float time, Random _) => Mathf.Exp(-24f * time) * (0.72f * Mathf.Sin((float)Math.PI * 2f * frequency * time) + 0.18f * Mathf.Sin((float)Math.PI * 2f * frequency * 2.01f * time))); } private static AudioClip CreateLastSurvivor() { return Create("KillerMode_LastSurvivor", 2.35f, delegate(float time, Random random) { float num = Mathf.Clamp01(time * 3f); float num2 = Mathf.Clamp01((2.35f - time) * 1.2f); float num3 = num * num2; float num4 = Mathf.Sin((float)Math.PI * 108f * time) + 0.7f * Mathf.Sin((float)Math.PI * 115f * time); float num5 = Mathf.Sin((float)Math.PI * 2f * (1.7f + time * 0.5f) * time); float num6 = ((float)random.NextDouble() * 2f - 1f) * 0.08f; return num3 * (0.3f * num4 * (0.75f + 0.25f * num5) + num6); }); } private static AudioClip CreateRoundEnd(bool survivorsWin) { return Create(survivorsWin ? "KillerMode_SurvivorsWin" : "KillerMode_KillerWins", 1.8f, delegate(float time, Random random) { float num = (survivorsWin ? 1f : (-1f)); float num2 = 150f + num * 42f * time; float num3 = Mathf.Clamp01(time * 8f) * Mathf.Clamp01((1.8f - time) * 1.5f); float num4 = Mathf.Sin((float)Math.PI * 2f * num2 * time) + 0.42f * Mathf.Sin((float)Math.PI * 2f * num2 * 1.5f * time); float num5 = Mathf.Sin((float)Math.PI * 108f * time) * Mathf.Exp(-5f * time); float num6 = ((float)random.NextDouble() * 2f - 1f) * Mathf.Exp(-7f * time); return num3 * 0.28f * num4 + 0.48f * num5 + 0.06f * num6; }); } private static AudioClip CreateHeartbeat(bool fast) { float seconds = (fast ? 0.82f : 1.45f); return Create(fast ? "KillerMode_HeartbeatFast" : "KillerMode_HeartbeatSlow", seconds, delegate(float time, Random _) { float num = Mathf.Exp(-42f * time) * Mathf.Sin((float)Math.PI * 116f * time); float num2 = Mathf.Max(0f, time - (fast ? 0.22f : 0.34f)); float num3 = Mathf.Exp(-48f * num2) * Mathf.Sin((float)Math.PI * 104f * num2); return 0.72f * num + 0.54f * num3; }); } private static AudioClip Create(string name, float seconds, Func sample) { int num = Mathf.CeilToInt(seconds * 44100f); float[] array = new float[num]; Random arg = new Random(name.GetHashCode()); for (int i = 0; i < num; i++) { array[i] = Mathf.Clamp(sample((float)i / 44100f, arg), -0.95f, 0.95f); } AudioClip obj = AudioClip.Create(name, num, 1, 44100, false); obj.SetData(Il2CppStructArray.op_Implicit(array), 0); return obj; } } public static class KillerModeBranding { private const string LogoResource = "FusionKillerMode.Assets.KillerModeLogo.png"; private static Texture2D _logo; public static Texture Logo { get { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown if ((Object)(object)_logo != (Object)null) { return (Texture)(object)_logo; } try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FusionKillerMode.Assets.KillerModeLogo.png"); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } _logo = new Texture2D(2, 2, (TextureFormat)4, false) { name = "Killer Mode Logo", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; ImageConversion.LoadImage(_logo, Il2CppStructArray.op_Implicit(array)); } catch (Exception ex) { MelonLogger.Warning("[Killer Mode] Could not load the embedded logo: " + ex.Message); } return (Texture)(object)_logo; } } } public static class KillerModeHud { private static GameObject _root; private static TextMeshProUGUI _text; private static Image _background; private static string _lastText; private static Color _lastColor; public static void Update(KillerRoundState state, int remainingSeconds, KillerPlayerState localRole, bool enabled) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (!enabled || state == KillerRoundState.Lobby || state == KillerRoundState.Ending) { SetVisible(visible: false); } else if (EnsureCreated()) { object obj = state switch { KillerRoundState.Preparing => "ROUND STARTING", KillerRoundState.SurvivorHeadStart => (localRole != KillerPlayerState.Killer) ? "HEAD START" : "SURVIVORS HIDING", KillerRoundState.ExitOpen => "EXIT OPEN • ROUND TIME", _ => "ROUND TIME", }; string text = ((state == KillerRoundState.Preparing) ? Math.Max(0, remainingSeconds).ToString() : FormatTime(remainingSeconds)); string text2 = (string?)obj + " • " + text; Color val = (Color)(state switch { KillerRoundState.Preparing => new Color(1f, 0.78f, 0.18f, 1f), KillerRoundState.ExitOpen => new Color(0.3f, 1f, 0.45f, 1f), _ => (localRole != KillerPlayerState.Killer) ? new Color(0.4f, 1f, 0.55f, 1f) : new Color(1f, 0.3f, 0.3f, 1f), }); if (_lastText != text2) { _lastText = text2; ((TMP_Text)_text).text = text2; } if (_lastColor != val) { _lastColor = val; ((Graphic)_text).color = val; } SetVisible(visible: true); } } public static void Destroy() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _text = null; _background = null; _lastText = null; _lastColor = default(Color); } private static bool EnsureCreated() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null && (Object)(object)_text != (Object)null) { return true; } if (RigData.HasPlayer) { RigRefs refs = RigData.Refs; if (!((Object)(object)((refs != null) ? refs.Headset : null) == (Object)null)) { _root = new GameObject("KillerMode_RoundTimerHUD"); Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)2; obj.overrideSorting = true; obj.sortingOrder = 32760; Transform transform = _root.transform; transform.SetParent(RigData.Refs.Headset, false); transform.localPosition = new Vector3(0f, -0.19f, 0.58f); transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one * 0.00065f; RectTransform component = _root.GetComponent(); if ((Object)(object)component != (Object)null) { component.sizeDelta = new Vector2(500f, 58f); } GameObject val = new GameObject("Background"); val.transform.SetParent(transform, false); _background = val.AddComponent(); ((Graphic)_background).color = new Color(0.015f, 0.015f, 0.02f, 0.72f); ((Graphic)_background).raycastTarget = false; Stretch(val.GetComponent()); GameObject val2 = new GameObject("Time"); val2.transform.SetParent(transform, false); _text = val2.AddComponent(); ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ((TMP_Text)_text).fontSize = 27f; ((TMP_Text)_text).fontStyle = (FontStyles)1; ((TMP_Text)_text).enableWordWrapping = false; ((Graphic)_text).raycastTarget = false; ((TMP_Text)_text).outlineWidth = 0.16f; ((TMP_Text)_text).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)230); Stretch(val2.GetComponent()); return true; } } return false; } private static void Stretch(RectTransform rect) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null)) { rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = new Vector2(12f, 4f); rect.offsetMax = new Vector2(-12f, -4f); } } private static void SetVisible(bool visible) { if ((Object)(object)_root != (Object)null && _root.activeSelf != visible) { _root.SetActive(visible); } } private static string FormatTime(int totalSeconds) { totalSeconds = Math.Max(0, totalSeconds); return $"{totalSeconds / 60:00}:{totalSeconds % 60:00}"; } } public static class KillerModePreferences { private static MelonPreferences_Category _category; private static MelonPreferences_Entry _settingsJson; public static KillerModeSettings Current { get; private set; } = new KillerModeSettings(); public static void Initialize() { _category = MelonPreferences.CreateCategory("BONELAB Fusion Killer Mode"); _settingsJson = _category.CreateEntry("HostSettings", string.Empty, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); if (!string.IsNullOrWhiteSpace(_settingsJson.Value)) { try { Current = JsonSerializer.Deserialize(_settingsJson.Value) ?? new KillerModeSettings(); } catch (Exception ex) { MelonLogger.Warning("[Killer Mode] Could not load saved host settings: " + ex.Message); Current = new KillerModeSettings(); } } } public static void Save(KillerModeSettings settings) { Current.CopyFrom(settings); _settingsJson.Value = JsonSerializer.Serialize(Current); _category.SaveToFile(false); } } public sealed class KillerModeSettings { public int MinimumPlayers { get; set; } = 2; public int StartCountdownSeconds { get; set; } = 10; public int RoundSeconds { get; set; } = 600; public int HeadStartSeconds { get; set; } = 30; public int ExitOpenTimeRemaining { get; set; } = 60; public float KillerVitality { get; set; } = 3f; public float SurvivorVitality { get; set; } = 1f; public float KillerSpeedMultiplier { get; set; } = 1.1f; public float SurvivorSpeedMultiplier { get; set; } = 1f; public bool KillerCanDie { get; set; } public bool SurvivorsCanDamageKiller { get; set; } public bool PreventSameKillerTwice { get; set; } = true; public bool LastSurvivorRevealEnabled { get; set; } = true; public int LastSurvivorRevealInterval { get; set; } = 10; public int LastSurvivorRevealDuration { get; set; } = 2; public bool ClearStartingInventory { get; set; } = true; public bool GiveSurvivorsWeapon { get; set; } public bool SpawnSurvivorFlashlights { get; set; } = true; public bool EnableModeSounds { get; set; } = true; public bool ShowRoundTimer { get; set; } = true; public bool DarkMapEnabled { get; set; } public int DarknessStrength { get; set; } = 70; public bool DarkFogEnabled { get; set; } public int FogStrength { get; set; } = 35; public bool FlashbangBrightEffect { get; set; } = true; public float FlashbangRadius { get; set; } = 18f; public float FlashbangDuration { get; set; } = 3f; public bool GiveSurvivorsFlashbangs { get; set; } public KillerMapModeOverride MapModeOverride { get; set; } public KillerGameplayPreset GameplayPreset { get; set; } public KillerSelectionMode SelectionMode { get; set; } public KillerWeaponChoice WeaponChoice { get; set; } = KillerWeaponChoice.GiantCleaver; public KillerSkinChoice SkinChoice { get; set; } public string KillerAvatarBarcode { get; set; } = string.Empty; public string CustomKillerWeaponBarcode { get; set; } = string.Empty; public string SurvivorWeaponBarcode { get; set; } = string.Empty; public string FlashlightBarcode { get; set; } = "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks"; public string FlashbangBarcode { get; set; } = string.Empty; public void CopyFrom(KillerModeSettings other) { if (other != null) { MinimumPlayers = other.MinimumPlayers; StartCountdownSeconds = other.StartCountdownSeconds; RoundSeconds = other.RoundSeconds; HeadStartSeconds = other.HeadStartSeconds; ExitOpenTimeRemaining = other.ExitOpenTimeRemaining; KillerVitality = other.KillerVitality; SurvivorVitality = other.SurvivorVitality; KillerSpeedMultiplier = other.KillerSpeedMultiplier; SurvivorSpeedMultiplier = other.SurvivorSpeedMultiplier; KillerCanDie = other.KillerCanDie; SurvivorsCanDamageKiller = other.SurvivorsCanDamageKiller; PreventSameKillerTwice = other.PreventSameKillerTwice; LastSurvivorRevealEnabled = other.LastSurvivorRevealEnabled; LastSurvivorRevealInterval = other.LastSurvivorRevealInterval; LastSurvivorRevealDuration = other.LastSurvivorRevealDuration; ClearStartingInventory = other.ClearStartingInventory; GiveSurvivorsWeapon = other.GiveSurvivorsWeapon; SpawnSurvivorFlashlights = other.SpawnSurvivorFlashlights; EnableModeSounds = other.EnableModeSounds; ShowRoundTimer = other.ShowRoundTimer; DarkMapEnabled = other.DarkMapEnabled; DarknessStrength = other.DarknessStrength; DarkFogEnabled = other.DarkFogEnabled; FogStrength = other.FogStrength; FlashbangBrightEffect = other.FlashbangBrightEffect; FlashbangRadius = other.FlashbangRadius; FlashbangDuration = other.FlashbangDuration; GiveSurvivorsFlashbangs = other.GiveSurvivorsFlashbangs; MapModeOverride = other.MapModeOverride; GameplayPreset = other.GameplayPreset; SelectionMode = other.SelectionMode; WeaponChoice = other.WeaponChoice; SkinChoice = other.SkinChoice; KillerAvatarBarcode = other.KillerAvatarBarcode ?? string.Empty; CustomKillerWeaponBarcode = other.CustomKillerWeaponBarcode ?? string.Empty; SurvivorWeaponBarcode = other.SurvivorWeaponBarcode ?? string.Empty; FlashlightBarcode = (string.IsNullOrWhiteSpace(other.FlashlightBarcode) ? "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks" : other.FlashlightBarcode); FlashbangBarcode = other.FlashbangBarcode ?? string.Empty; } } public void ApplyPreset(KillerGameplayPreset preset) { GameplayPreset = preset; switch (preset) { case KillerGameplayPreset.Classic: RoundSeconds = 600; HeadStartSeconds = 30; KillerVitality = 3f; SurvivorVitality = 1f; KillerSpeedMultiplier = 1.1f; SurvivorSpeedMultiplier = 1f; KillerCanDie = false; SurvivorsCanDamageKiller = false; LastSurvivorRevealEnabled = true; DarkMapEnabled = false; DarkFogEnabled = false; break; case KillerGameplayPreset.Casual: RoundSeconds = 720; HeadStartSeconds = 45; KillerVitality = 2f; SurvivorVitality = 1.25f; KillerSpeedMultiplier = 1.02f; SurvivorSpeedMultiplier = 1.05f; KillerCanDie = true; SurvivorsCanDamageKiller = true; LastSurvivorRevealEnabled = false; DarkMapEnabled = false; DarkFogEnabled = false; break; case KillerGameplayPreset.Hardcore: RoundSeconds = 480; HeadStartSeconds = 20; KillerVitality = 5f; SurvivorVitality = 0.8f; KillerSpeedMultiplier = 1.2f; SurvivorSpeedMultiplier = 0.95f; KillerCanDie = false; SurvivorsCanDamageKiller = false; LastSurvivorRevealEnabled = true; DarkMapEnabled = true; DarknessStrength = 78; DarkFogEnabled = true; FogStrength = 32; break; } } } public enum KillerRoundState { Lobby, Preparing, SurvivorHeadStart, Hunt, ExitOpen, Ending } public enum KillerPlayerState { None, Killer, SurvivorAlive, SurvivorEliminated, SurvivorEscaped, Spectator, Disconnected } public enum KillerMapMode { Survival, Escape } public enum KillerMapModeOverride { Auto, Survival, Escape } public enum KillerSelectionMode { Random, Host, Rotation } public enum SoloTestRole { Killer, Survivor } public enum KillerWeaponChoice { MapDefined, DefaultCombatKnife, GiantCleaver, DeathloopMachete, BowieKnife, CustomBarcode, Random } public enum KillerGameplayPreset { Custom, Classic, Casual, Hardcore } public enum KillerSkinChoice { PlayerAvatar, SelectedDownloadedAvatar, Strong, Heavy, Fast, PolyBlank } public enum KillerSpawnRole { Lobby, Killer, Survivor, Spectator } public enum KillerItemAudience { Everyone, KillerOnly, SurvivorsOnly } public enum KillerDoorRule { LockedUntilHunt, LockedUntilExitOpen, AlwaysUnlocked } public enum KillerDoorAnimation { None, SlideUp, Swing } public enum KillerWinReason { None, KillerEliminatedSurvivors, SurvivorsOutlastedTimer, SurvivorEscaped, KillerDisconnected, KillerDied, NotEnoughPlayers, SoloTestComplete } public static class KillerRoundRules { public static bool IsSpectator(KillerPlayerState state) { if (state != KillerPlayerState.Spectator && state != KillerPlayerState.SurvivorEliminated) { return state == KillerPlayerState.SurvivorEscaped; } return true; } public static bool CanHearVoice(KillerPlayerState listener, KillerPlayerState speaker) { bool num = listener == KillerPlayerState.Killer || listener == KillerPlayerState.SurvivorAlive; bool flag = speaker == KillerPlayerState.Killer || speaker == KillerPlayerState.SurvivorAlive; bool flag2 = IsSpectator(listener); bool flag3 = IsSpectator(speaker); if (!(num && flag)) { return flag2 && flag3; } return true; } public static bool CanAttack(KillerPlayerState attacker, KillerPlayerState target, KillerRoundState round, bool survivorsCanDamageKiller) { if (round != KillerRoundState.Hunt && round != KillerRoundState.ExitOpen) { return false; } switch (attacker) { case KillerPlayerState.Killer: return target == KillerPlayerState.SurvivorAlive; case KillerPlayerState.SurvivorAlive: if (survivorsCanDamageKiller) { return target == KillerPlayerState.Killer; } return false; default: return false; } } public static bool ShouldOpenExit(KillerMapMode mode, KillerRoundState state, int remainingSeconds, int openTimeRemaining) { if (mode != KillerMapMode.Escape || state == KillerRoundState.ExitOpen) { return false; } return remainingSeconds <= Math.Max(0, openTimeRemaining); } public static KillerWinReason TimerExpired(KillerMapMode mode, bool anyEscaped) { if (mode != KillerMapMode.Survival) { if (!anyEscaped) { return KillerWinReason.KillerEliminatedSurvivors; } return KillerWinReason.SurvivorEscaped; } return KillerWinReason.SurvivorsOutlastedTimer; } public static bool ShouldEndForNoLivingSurvivors(bool soloTest, int livingSurvivors) { if (!soloTest) { return livingSurvivors <= 0; } return false; } public static bool CanStartRoundNow(bool isHost, bool roundStarted, bool requirementsMet) { return isHost && !roundStarted && requirementsMet; } public static bool ShouldDeferAutomaticPause(bool roundStarted) { return roundStarted; } public static bool ShouldWaitForSelectedAvatarBarcode(bool isKiller, KillerSkinChoice choice, string barcode) { if (isKiller && choice == KillerSkinChoice.SelectedDownloadedAvatar) { return string.IsNullOrWhiteSpace(barcode); } return false; } } public class KillerModeGamemode : Gamemode { private sealed class SpawnedRoundItem { public ushort EntityId; public GameObject GameObject; public bool DestroyOnRoundEnd; public KillerItemSpawnPoint Source; } private sealed class ItemSpawnerState { public KillerItemSpawnPoint Point; public GamemodeItem CurrentItem; public float Cooldown; public int Uses; } private const string InGameMapSetupMetadataKey = "InGameMapSetupProfile"; public readonly KillerModeSettings Settings = new KillerModeSettings(); private readonly TeamManager _teams = new TeamManager(); private readonly Team _killerTeam = new Team("Killer"); private readonly Team _survivorTeam = new Team("Survivors"); private readonly Team _spectatorTeam = new Team("Spectators"); private readonly MetadataPlayerDictionary _playerStates = new MetadataPlayerDictionary(); private readonly Dictionary _hostPlayerStates = new Dictionary(); private MetadataInt _roundState; private MetadataInt _remainingSeconds; private MetadataInt _winReason; private MetadataInt _effectiveMapMode; private MetadataInt _effectiveKillerVitality; private MetadataInt _effectiveSurvivorVitality; private MetadataInt _effectiveKillerSpeed; private MetadataInt _effectiveSurvivorSpeed; private MetadataBool _effectiveKillerCanDie; private MetadataBool _effectiveSurvivorDamage; private MetadataBool _effectiveClearStartingInventory; private MetadataBool _effectiveModeSounds; private MetadataBool _effectiveShowRoundTimer; private MetadataInt _effectiveWeapon; private MetadataInt _effectiveSkin; private MetadataVariable _effectiveSkinBarcode; private MetadataBool _effectiveRevealEnabled; private MetadataInt _effectiveRevealInterval; private MetadataInt _effectiveRevealDuration; private MetadataVariable _effectiveWeaponBarcode; private MetadataVariable _effectiveFlashlightBarcode; private MetadataVariable _effectiveFlashbangBarcode; private MetadataBool _effectiveDarkMap; private MetadataInt _effectiveDarknessStrength; private MetadataBool _effectiveDarkFog; private MetadataInt _effectiveFogStrength; private MetadataBool _effectiveFlashbangEffect; private MetadataInt _effectiveFlashbangRadius; private MetadataInt _effectiveFlashbangDuration; private TriggerEvent _escapeRequest; private TriggerEvent _lastSurvivorEvent; private TriggerEvent _timeExpiredEvent; private TriggerEvent _flashbangRequestEvent; private TriggerEvent _flashbangDetonationEvent; private float _hostTimer; private float _endingTimer; private float _weaponRecoveryCooldown; private float _soloLastSurvivorTriggerDelay; private float _flashbangRequestCooldown; private int _lastPublishedSecond = -1; private ulong _killerPlatformId; private byte _previousKillerSmallId = byte.MaxValue; private bool _ending; private bool _spawnedItems; private bool _killerWeaponSpawned; private bool _localInventoryCleared; private bool _localRoundStartSoundPlayed; private bool _localRoundEndSoundPlayed; private bool _localRoleMessageShown; private bool _localLastSurvivorHeartbeatActive; private bool _localAvatarLockApplied; private bool _localKillerSkinFallbackNotified; private string _localRoundAvatarBarcode; private bool _killerAvatarDownloadPending; private string _pendingKillerAvatarBarcode; private string _failedKillerAvatarBarcode; private int _lastLocalStartCountdownSecond = -1; private int _lastLocalCountdownSecond = -1; private bool _showInGameSetupMarkers = true; private bool _inGameSetupEditing; private bool _automaticRoundsPaused; private bool _pauseAutomaticRoundsAfterCurrentRound; private bool _localEscapeRequested; private float _localEscapeRetry; private float _environmentRefreshTimer; private float _revealCycle; private float _revealVisible; private byte? _revealedPlayerId; private Texture2D _revealTexture; private float? _baseAvatarSpeed; private float? _baseAvatarAgility; private string _roundWeaponBarcode; private string _roundFlashlightBarcode = "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks"; private string _roundFlashbangBarcode = string.Empty; private bool _roundModeSoundsEnabled; private bool _roundClearStartingInventory; private readonly List _spawnedRoundItems = new List(); private readonly Dictionary _itemSpawnerStates = new Dictionary(); public override string Title => "Killer Mode"; public override string Author => "Fusion Killer Mode Team"; public override string Barcode => "FusionKillerMode.Gamemode.KillerMode"; public override string Description => "One killer hunts the survivors. Outlast the timer, or escape if the map has an exit."; public override Texture Logo => KillerModeBranding.Logo; public override bool DisableDevTools => true; public override bool DisableSpawnGun => true; public override bool DisableManualUnragdoll => true; public override bool AutoStopOnSceneLoad => true; protected virtual bool IsSoloTest => false; protected virtual bool FlashbangFeatureEnabled => IsSoloTest; protected virtual bool SavePreferencesOnStart => true; protected virtual KillerPlayerState SoloPlayerState => KillerPlayerState.Killer; private KillerRoundState RoundState => (KillerRoundState)((MetadataVariableT)(object)_roundState).GetValue(); private KillerMapMode EffectiveMapMode => (KillerMapMode)((MetadataVariableT)(object)_effectiveMapMode).GetValue(); protected virtual string ResolveKillerWeaponBarcode() { if (Settings.WeaponChoice == KillerWeaponChoice.MapDefined) { return (from point in KillerMapRegistry.ItemSpawns.OfType() where (Object)(object)point != (Object)null && ((Behaviour)point).isActiveAndEnabled select point.SpawnableBarcode).FirstOrDefault((string barcode) => !string.IsNullOrWhiteSpace(barcode)) ?? string.Empty; } if (Settings.WeaponChoice == KillerWeaponChoice.CustomBarcode) { return Settings.CustomKillerWeaponBarcode?.Trim() ?? string.Empty; } if (Settings.WeaponChoice == KillerWeaponChoice.Random) { string[] array = new string[5] { "c1534c5a-1fb8-477c-afbe-2a95436f6d62", "HorrorLab.GiantCleaver2.Spawnable.GiantCleaver", "Frictic.DeathloopMachete.Spawnable.DeathloopMachete", "BurntLettuce.BowieKnife.Spawnable.BowieKnife", Settings.CustomKillerWeaponBarcode?.Trim() }.Where(KillerContentReferences.IsInstalled).ToArray(); if (array.Length != 0) { return array[Random.Range(0, array.Length)]; } return "c1534c5a-1fb8-477c-afbe-2a95436f6d62"; } return KillerContentReferences.GetWeaponBarcode(Settings.WeaponChoice); } public KillerModeGamemode() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown Settings.CopyFrom(KillerModePreferences.Current); } public override GroupElementData CreateSettingsGroup() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0134: 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_0149: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_002b: 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_0040: Expected O, but got Unknown //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Expected O, but got Unknown //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Expected O, but got Unknown //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Expected O, but got Unknown //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_0496: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Expected O, but got Unknown //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04be: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Expected O, but got Unknown //IL_04e1: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_04f1: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Expected O, but got Unknown //IL_052f: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Expected O, but got Unknown //IL_0705: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_0715: Unknown result type (might be due to invalid IL or missing references) //IL_072c: Expected O, but got Unknown //IL_077b: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_078b: Unknown result type (might be due to invalid IL or missing references) //IL_07a2: Expected O, but got Unknown //IL_09cc: Unknown result type (might be due to invalid IL or missing references) //IL_09d1: Unknown result type (might be due to invalid IL or missing references) //IL_09dc: Unknown result type (might be due to invalid IL or missing references) //IL_09f3: Expected O, but got Unknown //IL_09f5: Unknown result type (might be due to invalid IL or missing references) //IL_09fa: Unknown result type (might be due to invalid IL or missing references) //IL_0a05: Unknown result type (might be due to invalid IL or missing references) //IL_0a1c: Expected O, but got Unknown //IL_0a1e: Unknown result type (might be due to invalid IL or missing references) //IL_0a23: Unknown result type (might be due to invalid IL or missing references) //IL_0a2e: Unknown result type (might be due to invalid IL or missing references) //IL_0a45: Expected O, but got Unknown //IL_0a8d: Unknown result type (might be due to invalid IL or missing references) //IL_0a92: Unknown result type (might be due to invalid IL or missing references) //IL_0a9d: Unknown result type (might be due to invalid IL or missing references) //IL_0ab4: Expected O, but got Unknown //IL_0ab6: Unknown result type (might be due to invalid IL or missing references) //IL_0abb: Unknown result type (might be due to invalid IL or missing references) //IL_0ac6: Unknown result type (might be due to invalid IL or missing references) //IL_0add: Expected O, but got Unknown //IL_0adf: Unknown result type (might be due to invalid IL or missing references) //IL_0ae4: Unknown result type (might be due to invalid IL or missing references) //IL_0aef: Unknown result type (might be due to invalid IL or missing references) //IL_0b06: Expected O, but got Unknown //IL_07fc: Unknown result type (might be due to invalid IL or missing references) //IL_0801: Unknown result type (might be due to invalid IL or missing references) //IL_080c: Unknown result type (might be due to invalid IL or missing references) //IL_0823: Expected O, but got Unknown //IL_08b5: Unknown result type (might be due to invalid IL or missing references) //IL_08ba: Unknown result type (might be due to invalid IL or missing references) //IL_08c5: Unknown result type (might be due to invalid IL or missing references) //IL_08dc: Expected O, but got Unknown GroupElementData val = ((Gamemode)this).CreateSettingsGroup(); if (!IsSoloTest && NetworkInfo.HasServer && !NetworkInfo.IsHost) { GroupElementData val2 = new GroupElementData("Host settings"); val2.AddElement(new LabelElementData { Title = "Only the Fusion host can configure Killer Mode or control rounds." }); val.AddElement(val2); return val; } GroupElementData val3 = new GroupElementData("Host controls"); GroupElementData val4 = new GroupElementData("Round"); GroupElementData val5 = new GroupElementData("Roles and equipment"); GroupElementData val6 = new GroupElementData("Presets and atmosphere"); GroupElementData val7 = new GroupElementData("In-game map setup"); if (!IsSoloTest) { val.AddElement(val3); } val.AddElement(val4); val.AddElement(val5); val.AddElement(val6); val.AddElement(val7); if (!IsSoloTest) { val3.AddElement(new FunctionElementData { Title = "START ROUND NOW", OnPressed = StartRoundNow }); val3.AddElement(new FunctionElementData { Title = "PAUSE AUTO-START AFTER THIS ROUND", OnPressed = PauseAutomaticRounds }); val3.AddElement(new FunctionElementData { Title = "RESUME AUTOMATIC ROUNDS", OnPressed = ResumeAutomaticRounds }); val3.AddElement(new LabelElementData { Title = "Start never interrupts an active round. Pause lets the current round finish first." }); } val6.AddElement(new EnumElementData { Title = "Gameplay Preset", EnumType = typeof(KillerGameplayPreset), Value = Settings.GameplayPreset, OnValueChanged = delegate(Enum value) { ApplyGameplayPreset((KillerGameplayPreset)(object)value); } }); val6.AddElement(new FunctionElementData { Title = "APPLY MAP RECOMMENDATIONS", OnPressed = ApplyMapRecommendations }); val6.AddElement(BoolSetting("Make Map Darker", Settings.DarkMapEnabled, delegate(bool v) { Settings.DarkMapEnabled = v; SyncEnvironmentSettingsIfStarted(); })); val6.AddElement(IntSetting("Darkness Strength", Settings.DarknessStrength, 0, 100, delegate(int v) { Settings.DarknessStrength = v; SyncEnvironmentSettingsIfStarted(); }, 5)); val6.AddElement(BoolSetting("Dark Fog", Settings.DarkFogEnabled, delegate(bool v) { Settings.DarkFogEnabled = v; SyncEnvironmentSettingsIfStarted(); })); val6.AddElement(IntSetting("Fog Strength", Settings.FogStrength, 0, 100, delegate(int v) { Settings.FogStrength = v; SyncEnvironmentSettingsIfStarted(); }, 5)); val4.AddElement(IntSetting("Minimum Players", Settings.MinimumPlayers, IsSoloTest ? 1 : 2, 32, delegate(int v) { Settings.MinimumPlayers = v; })); val4.AddElement(IntSetting("Start Countdown", Settings.StartCountdownSeconds, 3, 30, delegate(int v) { Settings.StartCountdownSeconds = v; })); val4.AddElement(IntSetting("Round Seconds", Settings.RoundSeconds, 60, 3600, delegate(int v) { Settings.RoundSeconds = v; }, 30)); val4.AddElement(IntSetting("Killer Head Start", Settings.HeadStartSeconds, 0, 180, delegate(int v) { Settings.HeadStartSeconds = v; }, 5)); val4.AddElement(IntSetting("Exit Opens With Time Left", Settings.ExitOpenTimeRemaining, 0, 600, delegate(int v) { Settings.ExitOpenTimeRemaining = v; }, 15)); val4.AddElement(new FunctionElementData { Title = "STOP KILLER MODE (STAY STOPPED)", OnPressed = StopKillerModeManually }); val4.AddElement(new EnumElementData { Title = "Map Mode", EnumType = typeof(KillerMapModeOverride), Value = Settings.MapModeOverride, OnValueChanged = delegate(Enum v) { Settings.MapModeOverride = (KillerMapModeOverride)(object)v; } }); val5.AddElement(new EnumElementData { Title = "Killer Selection", EnumType = typeof(KillerSelectionMode), Value = Settings.SelectionMode, OnValueChanged = delegate(Enum v) { Settings.SelectionMode = (KillerSelectionMode)(object)v; } }); val5.AddElement(new EnumElementData { Title = "Killer Weapon", EnumType = typeof(KillerWeaponChoice), Value = Settings.WeaponChoice, OnValueChanged = delegate(Enum v) { Settings.WeaponChoice = (KillerWeaponChoice)(object)v; } }); val5.AddElement(StringSetting("Custom Killer Weapon Barcode", Settings.CustomKillerWeaponBarcode, delegate(string v) { Settings.CustomKillerWeaponBarcode = v; })); val5.AddElement(new FunctionElementData { Title = "USE RIGHT-HAND ITEM AS KILLER WEAPON", OnPressed = SelectRightHandAsKillerWeapon }); val5.AddElement(new FunctionElementData { Title = "RECOVER LOST KILLER WEAPON", OnPressed = RecoverLostKillerWeapon }); val5.AddElement(new EnumElementData { Title = "Killer Skin", EnumType = typeof(KillerSkinChoice), Value = Settings.SkinChoice, OnValueChanged = delegate(Enum v) { Settings.SkinChoice = (KillerSkinChoice)(object)v; } }); val5.AddElement(new FunctionElementData { Title = "USE MY CURRENT AVATAR AS KILLER SKIN", OnPressed = SelectCurrentAvatarForKiller }); val5.AddElement(FloatSetting("Killer Vitality", Settings.KillerVitality, 0.2f, 20f, delegate(float v) { Settings.KillerVitality = v; })); val5.AddElement(FloatSetting("Survivor Vitality", Settings.SurvivorVitality, 0.2f, 20f, delegate(float v) { Settings.SurvivorVitality = v; })); val5.AddElement(FloatSetting("Killer Speed", Settings.KillerSpeedMultiplier, 0.5f, 2f, delegate(float v) { Settings.KillerSpeedMultiplier = v; })); val5.AddElement(FloatSetting("Survivor Speed", Settings.SurvivorSpeedMultiplier, 0.5f, 2f, delegate(float v) { Settings.SurvivorSpeedMultiplier = v; })); val5.AddElement(BoolSetting("Survivors Can Damage Killer", Settings.SurvivorsCanDamageKiller, delegate(bool v) { Settings.SurvivorsCanDamageKiller = v; })); val5.AddElement(BoolSetting("Killer Can Die", Settings.KillerCanDie, delegate(bool v) { Settings.KillerCanDie = v; })); val5.AddElement(BoolSetting("Prevent Same Killer Twice", Settings.PreventSameKillerTwice, delegate(bool v) { Settings.PreventSameKillerTwice = v; })); val5.AddElement(BoolSetting("Clear Starting Inventory", Settings.ClearStartingInventory, delegate(bool v) { Settings.ClearStartingInventory = v; })); val5.AddElement(BoolSetting("Give Survivors Selected Weapon", Settings.GiveSurvivorsWeapon, delegate(bool v) { Settings.GiveSurvivorsWeapon = v; })); val5.AddElement(StringSetting("Survivor Weapon Barcode", Settings.SurvivorWeaponBarcode, delegate(string v) { Settings.SurvivorWeaponBarcode = v; })); val5.AddElement(new FunctionElementData { Title = "USE RIGHT-HAND ITEM AS SURVIVOR WEAPON", OnPressed = SelectRightHandAsSurvivorWeapon }); val5.AddElement(BoolSetting("Give Survivors Flashlights", Settings.SpawnSurvivorFlashlights, delegate(bool v) { Settings.SpawnSurvivorFlashlights = v; })); val5.AddElement(StringSetting("Flashlight Barcode", Settings.FlashlightBarcode, delegate(string v) { Settings.FlashlightBarcode = v; })); val5.AddElement(new FunctionElementData { Title = "USE RIGHT-HAND ITEM AS SURVIVOR FLASHLIGHT", OnPressed = SelectRightHandAsSurvivorFlashlight }); if (FlashbangFeatureEnabled) { val5.AddElement(BoolSetting("Give Survivors Flashbangs", Settings.GiveSurvivorsFlashbangs, delegate(bool v) { Settings.GiveSurvivorsFlashbangs = v; })); val5.AddElement(StringSetting("Flashbang Barcode", Settings.FlashbangBarcode, delegate(string v) { Settings.FlashbangBarcode = v; })); val5.AddElement(new FunctionElementData { Title = "USE RIGHT-HAND ITEM AS SURVIVOR FLASHBANG", OnPressed = SelectRightHandAsSurvivorFlashbang }); val5.AddElement(BoolSetting("Flashbang Bright Effect", Settings.FlashbangBrightEffect, delegate(bool v) { Settings.FlashbangBrightEffect = v; })); val5.AddElement(FloatSetting("Flashbang Radius", Settings.FlashbangRadius, 5f, 40f, delegate(float v) { Settings.FlashbangRadius = v; })); val5.AddElement(FloatSetting("Flashbang Whiteout Seconds", Settings.FlashbangDuration, 0.5f, 8f, delegate(float v) { Settings.FlashbangDuration = v; })); if (IsSoloTest) { val5.AddElement(new FunctionElementData { Title = "TEST FLASHBANG WHITEOUT (LOCAL)", OnPressed = delegate { KillerFlashEffect.Trigger(Settings.FlashbangDuration, 1f); } }); } } val5.AddElement(BoolSetting("Mode Sounds", Settings.EnableModeSounds, delegate(bool v) { Settings.EnableModeSounds = v; })); val5.AddElement(BoolSetting("Visible Round Timer", Settings.ShowRoundTimer, delegate(bool v) { Settings.ShowRoundTimer = v; })); val5.AddElement(BoolSetting("Last Survivor Reveal", Settings.LastSurvivorRevealEnabled, delegate(bool v) { Settings.LastSurvivorRevealEnabled = v; })); val5.AddElement(IntSetting("Reveal Interval", Settings.LastSurvivorRevealInterval, 3, 60, delegate(int v) { Settings.LastSurvivorRevealInterval = v; })); val5.AddElement(IntSetting("Reveal Duration", Settings.LastSurvivorRevealDuration, 1, 10, delegate(int v) { Settings.LastSurvivorRevealDuration = v; })); val7.AddElement(BoolSetting("Show Colored Circles", _showInGameSetupMarkers, delegate(bool value) { _showInGameSetupMarkers = value; KillerInGameMapSetup.SetVisuals(value && !((Gamemode)this).IsStarted); })); val7.AddElement(new FunctionElementData { Title = "VALIDATE CURRENT MAP", OnPressed = ValidateCurrentMapForHost }); val7.AddElement(new FunctionElementData { Title = "BEGIN SETUP / PAUSE AUTO-START", OnPressed = BeginInGameMapSetupEditing }); val7.AddElement(new FunctionElementData { Title = "FINISH SETUP / ALLOW ROUND START", OnPressed = FinishInGameMapSetupEditing }); AddMapSetupButton(val7, "Set Lobby Spawn Here", InGameSetupPoint.Lobby); AddMapSetupButton(val7, "Set Killer Spawn Here", InGameSetupPoint.Killer); AddMapSetupButton(val7, "Add Survivor Spawn Here", InGameSetupPoint.Survivor); AddMapSetupButton(val7, "Set Spectator Spawn Here", InGameSetupPoint.Spectator); AddMapSetupButton(val7, "Set Escape Door Ahead", InGameSetupPoint.Escape); val7.AddElement(new FunctionElementData { Title = "Undo Most Recent Survivor Spawn", OnPressed = RemoveLastSurvivorSetupPoint }); val7.AddElement(new FunctionElementData { Title = "Remove Escape Zone", OnPressed = RemoveEscapeSetupPoint }); val7.AddElement(new FunctionElementData { Title = "Clear This Map Setup", OnPressed = ClearCurrentMapSetup }); return val; } private void ApplyGameplayPreset(KillerGameplayPreset preset) { Settings.ApplyPreset(preset); if (preset != KillerGameplayPreset.Custom) { Notify("Preset Applied", $"{preset} settings are ready. Reopen this page to see every updated value.", (NotificationType)3); } } private void ApplyMapRecommendations() { if (!NetworkInfo.IsHost) { Notify("Map Recommendations", "Only the Fusion host can apply map recommendations.", (NotificationType)1); return; } KillerMapConfig activeConfig = KillerMapRegistry.ActiveConfig; if ((Object)(object)activeConfig == (Object)null) { Notify("Map Recommendations", "This map does not provide Killer Mode recommendations.", (NotificationType)1); return; } Settings.GameplayPreset = KillerGameplayPreset.Custom; Settings.MinimumPlayers = Math.Clamp(activeConfig.RecommendedPlayersMin, IsSoloTest ? 1 : 2, 32); Settings.RoundSeconds = Math.Clamp(activeConfig.RecommendedRoundSeconds, 60, 3600); Settings.DarkMapEnabled = activeConfig.RecommendDarkMap; Settings.DarkFogEnabled = activeConfig.RecommendDarkFog; Notify("Map Recommendations Applied", "Player count, round time, darkness, and fog recommendations are ready.", (NotificationType)3); } private void SelectRightHandAsKillerWeapon() { SelectRightHandItem("Killer weapon", delegate(string barcode) { Settings.CustomKillerWeaponBarcode = barcode; Settings.WeaponChoice = KillerWeaponChoice.CustomBarcode; }); } private void SelectRightHandAsSurvivorWeapon() { SelectRightHandItem("Survivor weapon", delegate(string barcode) { Settings.SurvivorWeaponBarcode = barcode; Settings.GiveSurvivorsWeapon = true; }); } private void SelectRightHandAsSurvivorFlashlight() { SelectRightHandItem("Survivor flashlight", delegate(string barcode) { Settings.FlashlightBarcode = barcode; Settings.SpawnSurvivorFlashlights = true; }); } private void SelectRightHandAsSurvivorFlashbang() { SelectRightHandItem("Survivor flashbang", delegate(string barcode) { Settings.FlashbangBarcode = barcode; Settings.GiveSurvivorsFlashbangs = true; }); } private void SelectRightHandItem(string destination, Action apply) { if (!NetworkInfo.IsHost) { Notify("Right-Hand Item", "Only the Fusion host can choose shared round equipment.", (NotificationType)1); return; } if (RigData.HasPlayer) { RigRefs refs = RigData.Refs; if (!((Object)(object)((refs != null) ? refs.RightHand : null) == (Object)null)) { GameObject currentAttachedGO = RigData.Refs.RightHand.m_CurrentAttachedGO; if ((Object)(object)currentAttachedGO == (Object)null) { Notify("Right-Hand Item", "Hold the item in your right hand, then press this button with your left hand.", (NotificationType)1, 6f); return; } Poolee obj = currentAttachedGO.GetComponentInParent() ?? Poolee.Cache.Get(currentAttachedGO); object obj2; if (obj == null) { obj2 = null; } else { SpawnableCrate spawnableCrate = obj.SpawnableCrate; if (spawnableCrate == null) { obj2 = null; } else { Barcode barcode = ((Scannable)spawnableCrate).Barcode; obj2 = ((barcode != null) ? barcode.ID : null); } } string text = (string)obj2; if (string.IsNullOrWhiteSpace(text) || !KillerContentReferences.IsInstalled(text)) { Notify("Right-Hand Item", "That held object does not expose a usable Spawnable barcode.", (NotificationType)1, 6f); return; } apply(text); KillerModePreferences.Save(Settings); Notify("Right-Hand Item Saved", destination + ": " + text + "\nIt will be used next round. Reopen settings to see the updated selection.", (NotificationType)3, 8f); return; } } Notify("Right-Hand Item", "Your player rig is not ready yet.", (NotificationType)1); } private void SelectCurrentAvatarForKiller() { if (!NetworkInfo.IsHost) { Notify("Killer Skin", "Only the Fusion host can choose the shared Killer skin.", (NotificationType)1); return; } string avatarBarcode = LocalAvatar.AvatarBarcode; if (string.IsNullOrWhiteSpace(avatarBarcode) && RigData.HasPlayer) { avatarBarcode = RigData.GetAvatarBarcode(); } if (string.IsNullOrWhiteSpace(avatarBarcode) || !KillerContentReferences.IsAvatarInstalled(avatarBarcode)) { Notify("Killer Skin", "Wear the downloaded avatar first, then press this button again.", (NotificationType)1); return; } Settings.KillerAvatarBarcode = avatarBarcode; Settings.SkinChoice = KillerSkinChoice.SelectedDownloadedAvatar; Notify("Killer Skin Saved", "Your currently worn avatar will be used for the Killer next round.", (NotificationType)3); } private void AddMapSetupButton(GroupElementData group, string title, InGameSetupPoint point) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown group.AddElement(new FunctionElementData { Title = title, OnPressed = delegate { PlaceInGameSetupPoint(point); } }); } private void ValidateCurrentMapForHost() { KillerMapModeOverride killerMapModeOverride = KillerMapRegistry.ActiveConfig?.ModeOverride ?? Settings.MapModeOverride; IReadOnlyList readOnlyList = KillerMapValidator.Validate((killerMapModeOverride == KillerMapModeOverride.Escape || (killerMapModeOverride == KillerMapModeOverride.Auto && KillerMapRegistry.EscapeZones.Any((KillerEscapeZone zone) => (Object)(object)zone != (Object)null && ((Behaviour)zone).isActiveAndEnabled))) ? KillerMapMode.Escape : KillerMapMode.Survival); if (readOnlyList.Count == 0 && ((Gamemode)this).CheckReadyConditions()) { Notify("Map Validation Passed", "This map and the selected required content are ready.", (NotificationType)3); return; } string message = readOnlyList.FirstOrDefault() ?? "Check minimum players and required host barcode settings."; Notify("Map Cannot Start", message, (NotificationType)1, 7f); } private static IntElementData IntSetting(string title, int value, int min, int max, Action changed, int increment = 1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown return new IntElementData { Title = title, Value = value, MinValue = min, MaxValue = max, Increment = increment, OnValueChanged = changed }; } private static FloatElementData FloatSetting(string title, float value, float min, float max, Action changed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown return new FloatElementData { Title = title, Value = value, MinValue = min, MaxValue = max, Increment = 0.2f, OnValueChanged = changed }; } private static BoolElementData BoolSetting(string title, bool value, Action changed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown return new BoolElementData { Title = title, Value = value, OnValueChanged = changed }; } private static StringElementData StringSetting(string title, string value, Action changed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) //IL_0024: Expected O, but got Unknown return new StringElementData { Title = title, Value = (value ?? string.Empty), OnValueChanged = changed }; } public override bool CheckReadyConditions() { if (_inGameSetupEditing || _automaticRoundsPaused) { return false; } int num = (IsSoloTest ? 1 : Math.Clamp(Settings.MinimumPlayers, 2, 32)); if (PlayerIDManager.PlayerCount < num) { return false; } if ((KillerMapRegistry.ActiveConfig?.ModeOverride ?? Settings.MapModeOverride) == KillerMapModeOverride.Escape && !KillerMapRegistry.EscapeZones.Any((KillerEscapeZone z) => (Object)(object)z != (Object)null && ((Behaviour)z).isActiveAndEnabled)) { return false; } KillerMapConfig activeConfig = KillerMapRegistry.ActiveConfig; if ((Object)(object)activeConfig != (Object)null) { if (activeConfig.SdkCompatibilityVersion > 3) { return false; } if ((activeConfig.RequiredSpawnableBarcodes ?? Array.Empty()).Any((string text) => !string.IsNullOrWhiteSpace(text) && !KillerContentReferences.IsAnyCrateInstalled(text))) { return false; } } foreach (KillerItemSpawnPoint item in KillerMapRegistry.ItemSpawns.Where((KillerItemSpawnPoint point) => (Object)(object)point != (Object)null && point.RequiredContent)) { if (!KillerContentReferences.IsInstalled((item is KillerFlashlightSpawner { UseHostBarcode: not false }) ? Settings.FlashlightBarcode : ((item is KillerFlashbangSpawner { UseHostBarcode: not false }) ? Settings.FlashbangBarcode : item.SpawnableBarcode))) { return false; } } string barcode = ResolveKillerWeaponBarcode(); KillerWeaponChoice weaponChoice = Settings.WeaponChoice; if ((weaponChoice == KillerWeaponChoice.CustomBarcode || weaponChoice == KillerWeaponChoice.MapDefined) && !KillerContentReferences.IsInstalled(barcode)) { return false; } if (IsSoloTest) { return true; } bool flag = GamemodeMarker.FilterMarkers((BoneTagReference)null).Count > 0; bool num2 = KillerMapRegistry.GetSpawns(KillerSpawnRole.Killer).Any() || flag; bool flag2 = KillerMapRegistry.GetSpawns(KillerSpawnRole.Survivor).Any() || flag; if (!num2 || !flag2) { return false; } return true; } public override void OnGamemodeSelected() { _inGameSetupEditing = false; _automaticRoundsPaused = false; _pauseAutomaticRoundsAfterCurrentRound = false; LoadAndPublishInGameMapSetup(); TeleportLocalToRole(KillerSpawnRole.Lobby); } public override void OnGamemodeDeselected() { KillerInGameMapSetup.ClearRuntimeObjects(); GamemodeHelper.ResetSpawnPoints(); } public override void OnGamemodeRegistered() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Expected O, but got Unknown //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Expected O, but got Unknown //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Expected O, but got Unknown //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Expected O, but got Unknown //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Expected O, but got Unknown //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Expected O, but got Unknown //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Expected O, but got Unknown //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Expected O, but got Unknown //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Expected O, but got Unknown //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Expected O, but got Unknown //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Expected O, but got Unknown //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Expected O, but got Unknown //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Expected O, but got Unknown _roundState = new MetadataInt("KillerRoundState", ((Gamemode)this).Metadata); _remainingSeconds = new MetadataInt("RemainingSeconds", ((Gamemode)this).Metadata); _winReason = new MetadataInt("KillerWinReason", ((Gamemode)this).Metadata); _effectiveMapMode = new MetadataInt("EffectiveMapMode", ((Gamemode)this).Metadata); _effectiveKillerVitality = new MetadataInt("KillerVitalityX100", ((Gamemode)this).Metadata); _effectiveSurvivorVitality = new MetadataInt("SurvivorVitalityX100", ((Gamemode)this).Metadata); _effectiveKillerSpeed = new MetadataInt("KillerSpeedX100", ((Gamemode)this).Metadata); _effectiveSurvivorSpeed = new MetadataInt("SurvivorSpeedX100", ((Gamemode)this).Metadata); _effectiveKillerCanDie = new MetadataBool("KillerCanDie", ((Gamemode)this).Metadata); _effectiveSurvivorDamage = new MetadataBool("SurvivorsCanDamageKiller", ((Gamemode)this).Metadata); _effectiveClearStartingInventory = new MetadataBool("ClearStartingInventory", ((Gamemode)this).Metadata); _effectiveModeSounds = new MetadataBool("ModeSounds", ((Gamemode)this).Metadata); _effectiveShowRoundTimer = new MetadataBool("ShowRoundTimer", ((Gamemode)this).Metadata); _effectiveWeapon = new MetadataInt("KillerWeapon", ((Gamemode)this).Metadata); _effectiveSkin = new MetadataInt("KillerSkin", ((Gamemode)this).Metadata); _effectiveSkinBarcode = new MetadataVariable("KillerSkinBarcode", ((Gamemode)this).Metadata); _effectiveRevealEnabled = new MetadataBool("LastSurvivorRevealEnabled", ((Gamemode)this).Metadata); _effectiveRevealInterval = new MetadataInt("LastSurvivorRevealInterval", ((Gamemode)this).Metadata); _effectiveRevealDuration = new MetadataInt("LastSurvivorRevealDuration", ((Gamemode)this).Metadata); _effectiveWeaponBarcode = new MetadataVariable("KillerWeaponBarcode", ((Gamemode)this).Metadata); _effectiveFlashlightBarcode = new MetadataVariable("FlashlightBarcode", ((Gamemode)this).Metadata); _effectiveFlashbangBarcode = new MetadataVariable("FlashbangBarcode", ((Gamemode)this).Metadata); _effectiveDarkMap = new MetadataBool("DarkMap", ((Gamemode)this).Metadata); _effectiveDarknessStrength = new MetadataInt("DarknessStrength", ((Gamemode)this).Metadata); _effectiveDarkFog = new MetadataBool("DarkFog", ((Gamemode)this).Metadata); _effectiveFogStrength = new MetadataInt("FogStrength", ((Gamemode)this).Metadata); _effectiveFlashbangEffect = new MetadataBool("FlashbangBrightEffect", ((Gamemode)this).Metadata); _effectiveFlashbangRadius = new MetadataInt("FlashbangRadiusX100", ((Gamemode)this).Metadata); _effectiveFlashbangDuration = new MetadataInt("FlashbangDurationX100", ((Gamemode)this).Metadata); ((MetadataDictionary)(object)_playerStates).Register(((Gamemode)this).Metadata, "KillerPlayerState"); _playerStates.OnPlayerVariableChanged += OnPlayerStateChanged; _teams.Register((Gamemode)(object)this); _teams.AddTeam(_killerTeam); _teams.AddTeam(_survivorTeam); _teams.AddTeam(_spectatorTeam); _teams.OnAssignedToTeam += OnAssignedToTeam; _escapeRequest = new TriggerEvent("EscapeRequest", ((Gamemode)this).Relay, false); _escapeRequest.OnTriggeredWithValue += OnEscapeRequest; _lastSurvivorEvent = new TriggerEvent("LastSurvivor", ((Gamemode)this).Relay, true); _lastSurvivorEvent.OnTriggered += delegate { switch ((PlayerIDManager.LocalID != null) ? GetPlayerState(PlayerIDManager.LocalID) : KillerPlayerState.None) { case KillerPlayerState.SurvivorAlive: { _localLastSurvivorHeartbeatActive = true; string message2 = (IsSoloTest ? "Solo test: the Last Survivor cue and private heartbeat are active." : (((MetadataVariableT)(object)_effectiveRevealEnabled).GetValue() ? "You are the last Survivor. Your location will be periodically revealed to the Killer." : "You are the last Survivor. Stay alive.")); Notify("YOU'RE THE LAST SURVIVOR", message2, (NotificationType)1); break; } case KillerPlayerState.Killer: { _localLastSurvivorHeartbeatActive = false; string message = (((MetadataVariableT)(object)_effectiveRevealEnabled).GetValue() ? "Only one Survivor remains. Their location will be revealed." : "Only one Survivor remains."); Notify("LAST SURVIVOR", message, (NotificationType)1); break; } default: _localLastSurvivorHeartbeatActive = false; Notify("Last Survivor", "Only one living Survivor remains.", (NotificationType)1); break; } if (ModeSoundsEnabled()) { KillerModeAudio.PlayLastSurvivor(); } KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeLastSurvivor(); }); }; _timeExpiredEvent = new TriggerEvent("TimeExpired", ((Gamemode)this).Relay, true); _timeExpiredEvent.OnTriggered += delegate { KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeTimeExpired(); }); }; _flashbangRequestEvent = new TriggerEvent("FlashbangRequest", ((Gamemode)this).Relay, false); _flashbangRequestEvent.OnTriggeredWithValue += OnFlashbangRequest; _flashbangDetonationEvent = new TriggerEvent("FlashbangDetonation", ((Gamemode)this).Relay, true); _flashbangDetonationEvent.OnTriggeredWithValue += OnFlashbangDetonation; MultiplayerHooking.OnPlayerAction += new PlayerAction(OnPlayerAction); FusionOverrides.OnValidateNametag += new UserOverride(OnValidateNametag); LocalHealth.OnRespawn += OnLocalRespawn; LocalAvatar.OnAvatarChanged += new PlayerAvatarDelegate(OnLocalAvatarChanged); } public override void OnGamemodeUnregistered() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown RestoreLocalAvatarLock(); MultiplayerHooking.OnPlayerAction -= new PlayerAction(OnPlayerAction); FusionOverrides.OnValidateNametag -= new UserOverride(OnValidateNametag); LocalHealth.OnRespawn -= OnLocalRespawn; LocalAvatar.OnAvatarChanged -= new PlayerAvatarDelegate(OnLocalAvatarChanged); _playerStates.OnPlayerVariableChanged -= OnPlayerStateChanged; ((MetadataDictionary)(object)_playerStates).Unregister(); _teams.OnAssignedToTeam -= OnAssignedToTeam; _teams.Unregister(); _escapeRequest.UnregisterEvent(); _lastSurvivorEvent.UnregisterEvent(); _timeExpiredEvent.UnregisterEvent(); _flashbangRequestEvent.UnregisterEvent(); _flashbangDetonationEvent.UnregisterEvent(); KillerEnvironmentEffects.Restore(); KillerFlashEffect.Clear(); } public override void OnGamemodeStarted() { _localRoundAvatarBarcode = LocalAvatar.AvatarBarcode; if (string.IsNullOrWhiteSpace(_localRoundAvatarBarcode) && RigData.HasPlayer) { _localRoundAvatarBarcode = RigData.GetAvatarBarcode(); } _ending = false; _weaponRecoveryCooldown = 0f; _flashbangRequestCooldown = 0f; _soloLastSurvivorTriggerDelay = 0f; _spawnedItems = false; _killerWeaponSpawned = false; _localInventoryCleared = false; _localRoundStartSoundPlayed = false; _localRoundEndSoundPlayed = false; _localRoleMessageShown = false; _localLastSurvivorHeartbeatActive = false; _localKillerSkinFallbackNotified = false; _failedKillerAvatarBarcode = null; KillerModeAudio.StopHeartbeat(); _lastLocalStartCountdownSecond = -1; _lastLocalCountdownSecond = -1; KillerInGameMapSetup.SetVisuals(visible: false); _localEscapeRequested = false; _localEscapeRetry = 0f; _environmentRefreshTimer = 0f; _revealCycle = 0f; _revealVisible = 0f; _revealedPlayerId = null; _spawnedRoundItems.Clear(); _itemSpawnerStates.Clear(); _hostPlayerStates.Clear(); LocalControls.DisableSlowMo = true; KillerEnvironmentEffects.Restore(); KillerFlashEffect.Clear(); if (NetworkInfo.IsHost) { if (SavePreferencesOnStart) { KillerModePreferences.Save(Settings); } BeginHostRound(); } ApplyLocalEnvironment(); } private void BeginHostRound() { KillerMapConfig activeConfig = KillerMapRegistry.ActiveConfig; KillerMapMode value = (((Object)(object)activeConfig != (Object)null) ? activeConfig.ModeOverride : Settings.MapModeOverride) switch { KillerMapModeOverride.Survival => KillerMapMode.Survival, KillerMapModeOverride.Escape => KillerMapMode.Escape, _ => KillerMapRegistry.EscapeZones.Any((KillerEscapeZone z) => (Object)(object)z != (Object)null && ((Behaviour)z).isActiveAndEnabled) ? KillerMapMode.Escape : KillerMapMode.Survival, }; int val = (((Object)(object)activeConfig != (Object)null) ? activeConfig.ExitOpenTimeRemaining : Settings.ExitOpenTimeRemaining); ((MetadataVariableT)(object)_effectiveMapMode).SetValue((int)value); ((MetadataVariableT)(object)_effectiveKillerVitality).SetValue(Mathf.RoundToInt(Settings.KillerVitality * 100f)); ((MetadataVariableT)(object)_effectiveSurvivorVitality).SetValue(Mathf.RoundToInt(Settings.SurvivorVitality * 100f)); ((MetadataVariableT)(object)_effectiveKillerSpeed).SetValue(Mathf.RoundToInt(Mathf.Clamp(Settings.KillerSpeedMultiplier, 0.5f, 2f) * 100f)); ((MetadataVariableT)(object)_effectiveSurvivorSpeed).SetValue(Mathf.RoundToInt(Mathf.Clamp(Settings.SurvivorSpeedMultiplier, 0.5f, 2f) * 100f)); ((MetadataVariableT)(object)_effectiveKillerCanDie).SetValue(Settings.KillerCanDie); ((MetadataVariableT)(object)_effectiveSurvivorDamage).SetValue(Settings.SurvivorsCanDamageKiller); _roundClearStartingInventory = Settings.ClearStartingInventory; ((MetadataVariableT)(object)_effectiveClearStartingInventory).SetValue(_roundClearStartingInventory); _roundModeSoundsEnabled = Settings.EnableModeSounds; _roundFlashlightBarcode = (string.IsNullOrWhiteSpace(Settings.FlashlightBarcode) ? "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks" : Settings.FlashlightBarcode.Trim()); _roundFlashbangBarcode = ((!FlashbangFeatureEnabled) ? string.Empty : (Settings.FlashbangBarcode?.Trim() ?? string.Empty)); ((MetadataVariableT)(object)_effectiveModeSounds).SetValue(_roundModeSoundsEnabled); ((MetadataVariableT)(object)_effectiveShowRoundTimer).SetValue(Settings.ShowRoundTimer); ((MetadataVariableT)(object)_effectiveWeapon).SetValue((int)Settings.WeaponChoice); ((MetadataVariableT)(object)_effectiveSkin).SetValue((int)Settings.SkinChoice); _effectiveSkinBarcode.SetValue(Settings.KillerAvatarBarcode ?? string.Empty); ((MetadataVariableT)(object)_effectiveRevealEnabled).SetValue(Settings.LastSurvivorRevealEnabled); ((MetadataVariableT)(object)_effectiveRevealInterval).SetValue(Math.Clamp(Settings.LastSurvivorRevealInterval, 3, 60)); ((MetadataVariableT)(object)_effectiveRevealDuration).SetValue(Math.Clamp(Settings.LastSurvivorRevealDuration, 1, 10)); _roundWeaponBarcode = ResolveKillerWeaponBarcode(); if (!KillerContentReferences.IsInstalled(_roundWeaponBarcode)) { KillerWeaponChoice weaponChoice = Settings.WeaponChoice; if (weaponChoice != KillerWeaponChoice.CustomBarcode && weaponChoice != KillerWeaponChoice.MapDefined) { MelonLogger.Warning("[Killer Mode] Selected weapon is missing; using BONELAB's Combat Knife instead: " + _roundWeaponBarcode); _roundWeaponBarcode = "c1534c5a-1fb8-477c-afbe-2a95436f6d62"; } } _effectiveWeaponBarcode.SetValue(_roundWeaponBarcode ?? string.Empty); _effectiveFlashlightBarcode.SetValue(_roundFlashlightBarcode); _effectiveFlashbangBarcode.SetValue(_roundFlashbangBarcode); ((MetadataVariableT)(object)_effectiveDarkMap).SetValue(Settings.DarkMapEnabled); ((MetadataVariableT)(object)_effectiveDarknessStrength).SetValue(Math.Clamp(Settings.DarknessStrength, 0, 100)); ((MetadataVariableT)(object)_effectiveDarkFog).SetValue(Settings.DarkFogEnabled); ((MetadataVariableT)(object)_effectiveFogStrength).SetValue(Math.Clamp(Settings.FogStrength, 0, 100)); ((MetadataVariableT)(object)_effectiveFlashbangEffect).SetValue(FlashbangFeatureEnabled && Settings.FlashbangBrightEffect); ((MetadataVariableT)(object)_effectiveFlashbangRadius).SetValue(Mathf.RoundToInt(Mathf.Clamp(Settings.FlashbangRadius, 5f, 40f) * 100f)); ((MetadataVariableT)(object)_effectiveFlashbangDuration).SetValue(Mathf.RoundToInt(Mathf.Clamp(Settings.FlashbangDuration, 0.5f, 8f) * 100f)); ((MetadataVariableT)(object)_winReason).SetValue(0); ApplyLocalEnvironment(); SelectAndAssignRoles(); _hostTimer = Math.Clamp(Settings.StartCountdownSeconds, 3, 30); _lastPublishedSecond = -1; SetRoundState(KillerRoundState.Preparing); ((Gamemode)this).Metadata.TrySetMetadata("RoundDuration", Math.Max(60, Settings.RoundSeconds).ToString()); ((Gamemode)this).Metadata.TrySetMetadata("ExitOpenTimeRemaining", Math.Max(0, val).ToString()); PublishRemaining(); } private void SelectAndAssignRoles() { PlayerID[] array = PlayerIDManager.PlayerIDs.OrderBy((PlayerID p) => p.SmallID).ToArray(); if (array.Length == 0) { return; } _hostPlayerStates.Clear(); PlayerID[] array2; if (IsSoloTest) { PlayerID val = ((IEnumerable)array).FirstOrDefault((Func)((PlayerID p) => p.SmallID == 0)) ?? array[0]; _killerPlatformId = ((SoloPlayerState == KillerPlayerState.Killer) ? val.PlatformID : 0); array2 = array; foreach (PlayerID val2 in array2) { KillerPlayerState killerPlayerState = ((val2 == val) ? SoloPlayerState : KillerPlayerState.Spectator); SetPlayerState(val2, killerPlayerState); TeamManager teams = _teams; PlayerID val3 = val2; teams.TryAssignTeam(val3, (Team)(killerPlayerState switch { KillerPlayerState.Killer => _killerTeam, KillerPlayerState.SurvivorAlive => _survivorTeam, _ => _spectatorTeam, })); } return; } PlayerID val4 = (PlayerID)(Settings.SelectionMode switch { KillerSelectionMode.Host => ((IEnumerable)array).FirstOrDefault((Func)((PlayerID p) => p.SmallID == 0)) ?? array[0], KillerSelectionMode.Rotation => ((IEnumerable)array).FirstOrDefault((Func)((PlayerID p) => p.SmallID > _previousKillerSmallId)) ?? array[0], _ => array[Random.Range(0, array.Length)], }); if (Settings.PreventSameKillerTwice && array.Length > 1 && val4.SmallID == _previousKillerSmallId) { val4 = array.First((PlayerID p) => p.SmallID != _previousKillerSmallId); } _previousKillerSmallId = val4.SmallID; _killerPlatformId = val4.PlatformID; array2 = array; foreach (PlayerID val5 in array2) { bool flag = val5 == val4; SetPlayerState(val5, flag ? KillerPlayerState.Killer : KillerPlayerState.SurvivorAlive); _teams.TryAssignTeam(val5, flag ? _killerTeam : _survivorTeam); } } public override void OnLevelReady() { LoadAndPublishInGameMapSetup(); if (NetworkInfo.IsHost) { foreach (string item in KillerMapValidator.Validate(EffectiveMapMode)) { MelonLogger.Error("[Killer Mode map] " + item); } foreach (string item2 in KillerMapValidator.Warnings(EffectiveMapMode)) { MelonLogger.Warning("[Killer Mode map] " + item2); } if (!_spawnedItems) { SpawnRoundItems(); _spawnedItems = true; } } ApplyLocalState(teleport: true); ApplyLocalEnvironment(); } protected override void OnUpdate() { if (((Gamemode)this).IsStarted) { if (NetworkInfo.IsHost) { HostUpdate(); } UpdateLocalRoundTimer(); LocalRoundLoadoutUpdate(); LocalInteractionUpdate(); LocalLastSurvivorRevealUpdate(); LocalLastSurvivorHeartbeatUpdate(); KillerFlashEffect.Tick(); _environmentRefreshTimer -= TimeReferences.DeltaTime; if (_environmentRefreshTimer <= 0f) { _environmentRefreshTimer = 1f; ApplyLocalEnvironment(); } } } private void HostUpdate() { if (_ending) { _endingTimer -= TimeReferences.DeltaTime; if (_endingTimer <= 0f) { GamemodeManager.StopGamemode(); } return; } _weaponRecoveryCooldown = Math.Max(0f, _weaponRecoveryCooldown - TimeReferences.DeltaTime); _flashbangRequestCooldown = Math.Max(0f, _flashbangRequestCooldown - TimeReferences.DeltaTime); if (_soloLastSurvivorTriggerDelay > 0f) { _soloLastSurvivorTriggerDelay -= TimeReferences.DeltaTime; if (_soloLastSurvivorTriggerDelay <= 0f) { _lastSurvivorEvent.TryInvoke(); } } UpdateItemSpawners(); switch (RoundState) { case KillerRoundState.Preparing: _hostTimer -= TimeReferences.DeltaTime; PublishRemaining(); if (_hostTimer <= 0f) { StartActiveRound(); } break; case KillerRoundState.SurvivorHeadStart: _hostTimer -= TimeReferences.DeltaTime; PublishRemaining(); if (_hostTimer <= 0f) { _hostTimer = Math.Max(60, Settings.RoundSeconds); SetRoundState(KillerRoundState.Hunt); SpawnKillerWeapon(); PublishRemaining(); } break; case KillerRoundState.Hunt: case KillerRoundState.ExitOpen: _hostTimer -= TimeReferences.DeltaTime; PublishRemaining(); UpdateExitState(); if (_hostTimer <= 0f) { FinishOnTimer(); } break; } } private void StartActiveRound() { _hostTimer = Math.Max(0, Settings.HeadStartSeconds); _lastPublishedSecond = -1; KillerRoundState killerRoundState = ((Settings.HeadStartSeconds > 0) ? KillerRoundState.SurvivorHeadStart : KillerRoundState.Hunt); SetRoundState(killerRoundState); if (killerRoundState == KillerRoundState.Hunt) { _hostTimer = Math.Max(60, Settings.RoundSeconds); SpawnKillerWeapon(); } if (IsSoloTest && SoloPlayerState == KillerPlayerState.SurvivorAlive) { _soloLastSurvivorTriggerDelay = 4.25f; } PublishRemaining(); } private void UpdateExitState() { if (EffectiveMapMode == KillerMapMode.Escape && RoundState != KillerRoundState.ExitOpen) { int openTimeRemaining = ParseMetadataInt("ExitOpenTimeRemaining", Settings.ExitOpenTimeRemaining); if (KillerRoundRules.ShouldOpenExit(EffectiveMapMode, RoundState, Mathf.CeilToInt(_hostTimer), openTimeRemaining)) { SetRoundState(KillerRoundState.ExitOpen); } } } private void FinishOnTimer() { _timeExpiredEvent.TryInvoke(); if (IsSoloTest) { EndRound(KillerWinReason.SoloTestComplete); return; } bool anyEscaped = PlayerIDManager.PlayerIDs.Any((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorEscaped); if (EffectiveMapMode == KillerMapMode.Escape) { PlayerID[] array = PlayerIDManager.PlayerIDs.Where((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorAlive).ToArray(); foreach (PlayerID val in array) { SetPlayerState(val, KillerPlayerState.SurvivorEliminated); _teams.TryAssignTeam(val, _spectatorTeam); } } EndRound(KillerRoundRules.TimerExpired(EffectiveMapMode, anyEscaped)); } private int ParseMetadataInt(string key, int fallback) { if (!int.TryParse(((Gamemode)this).Metadata.GetMetadata(key), out var result)) { return fallback; } return result; } private void PublishRemaining() { int num = Math.Max(0, Mathf.CeilToInt(_hostTimer)); if (num != _lastPublishedSecond) { _lastPublishedSecond = num; ((MetadataVariableT)(object)_remainingSeconds).SetValue(num); HandleRemainingSecondsChanged(); } } private void SetRoundState(KillerRoundState state) { ((MetadataVariableT)(object)_roundState).SetValue((int)state); } private void SetPlayerState(PlayerID player, KillerPlayerState state) { if (player != null) { if (NetworkInfo.IsHost) { _hostPlayerStates[player.PlatformID] = state; } ((MetadataVariableT)(object)_playerStates.GetVariable(player)).SetValue((int)state); } } private KillerPlayerState GetPlayerState(PlayerID player) { if (player == null) { return KillerPlayerState.None; } if (NetworkInfo.IsHost && _hostPlayerStates.TryGetValue(player.PlatformID, out var value)) { return value; } return (KillerPlayerState)((MetadataVariableT)(object)_playerStates.GetVariable(player)).GetValue(); } private void OnPlayerAction(PlayerID player, PlayerActionType type, PlayerID otherPlayer = null) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 if (((Gamemode)this).IsStarted && NetworkInfo.IsHost && ((int)type == 2 || (int)type == 7)) { KillerPlayerState playerState = GetPlayerState(player); if (playerState == KillerPlayerState.Killer && ((MetadataVariableT)(object)_effectiveKillerCanDie).GetValue()) { SetPlayerState(player, KillerPlayerState.Spectator); _teams.TryAssignTeam(player, _spectatorTeam); EndRound(KillerWinReason.KillerDied); } else if (playerState == KillerPlayerState.SurvivorAlive) { SetPlayerState(player, KillerPlayerState.SurvivorEliminated); _teams.TryAssignTeam(player, _spectatorTeam); CheckSurvivorsRemaining(); } } } protected override void OnPlayerJoined(PlayerID playerID) { if (NetworkInfo.IsHost) { SetPlayerState(playerID, KillerPlayerState.Spectator); _teams.TryAssignTeam(playerID, _spectatorTeam); } } protected override void OnPlayerLeft(PlayerID playerID) { if (NetworkInfo.IsHost && !_ending) { _hostPlayerStates.Remove(playerID.PlatformID); if (playerID.PlatformID == _killerPlatformId) { EndRound(KillerWinReason.KillerDisconnected); } else { CheckSurvivorsRemaining(); } } } private void CheckSurvivorsRemaining() { int num = PlayerIDManager.PlayerIDs.Count((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorAlive); if (num == 1) { _lastSurvivorEvent.TryInvoke(); } if (KillerRoundRules.ShouldEndForNoLivingSurvivors(IsSoloTest, num)) { bool flag = PlayerIDManager.PlayerIDs.Any((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorEscaped); EndRound((!flag) ? KillerWinReason.KillerEliminatedSurvivors : KillerWinReason.SurvivorEscaped); } } private void EndRound(KillerWinReason reason) { if (!_ending) { _ending = true; _endingTimer = 6f; ((MetadataVariableT)(object)_winReason).SetValue((int)reason); SetRoundState(KillerRoundState.Ending); ((MetadataVariableT)(object)_remainingSeconds).SetValue(0); KillerModeAudio.StopHeartbeat(); if (NetworkInfo.IsHost) { CleanupRoundItems(); } } } protected override void OnMetadataChanged(string key, string value) { switch (key) { case "KillerRoundState": { KillerDoor[] array = KillerMapRegistry.Doors.ToArray(); foreach (KillerDoor killerDoor in array) { if ((Object)(object)killerDoor != (Object)null) { killerDoor.Apply(RoundState); } } ApplyLocalState(teleport: false); switch (RoundState) { case KillerRoundState.Preparing: KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeRoundStarting(); }); break; case KillerRoundState.SurvivorHeadStart: PlayRoundStartSoundOnce(); ShowLocalRoleMessageOnce(); KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeSurvivorsReleased(); }); break; case KillerRoundState.Hunt: PlayRoundStartSoundOnce(); ShowLocalRoleMessageOnce(); KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeKillerReleased(); }); break; case KillerRoundState.ExitOpen: Notify("Exit Open", "The escape route is open!", (NotificationType)3); KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeExitOpen(); }); break; case KillerRoundState.Ending: KillerModeAudio.StopHeartbeat(); KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeRoundOver(); }); break; } break; } case "KillerWinReason": { KillerWinReason reason = (KillerWinReason)((MetadataVariableT)(object)_winReason).GetValue(); if (reason == KillerWinReason.None) { break; } PlayRoundEndSoundOnce(reason); Notify("Round Over", WinMessage(reason), (NotificationType)((!IsSurvivorWin(reason)) ? 1 : 3)); KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { if (IsSurvivorWin(reason)) { e.InvokeSurvivorsWin(); } else { e.InvokeKillerWins(); } }); break; } case "RemainingSeconds": HandleRemainingSecondsChanged(); break; case "KillerSkin": case "KillerSkinBarcode": ApplyLocalState(teleport: false); break; case "DarkMap": case "DarknessStrength": case "DarkFog": case "FogStrength": ApplyLocalEnvironment(); break; case "InGameMapSetupProfile": if (!NetworkInfo.IsHost) { KillerInGameMapSetup.ApplySerialized(value, _showInGameSetupMarkers && !((Gamemode)this).IsStarted); } break; } } private void PlayRoundStartSoundOnce() { if (!_localRoundStartSoundPlayed && ModeSoundsEnabled()) { _localRoundStartSoundPlayed = true; KillerModeAudio.PlayRoundStart(); } } private void PlayRoundEndSoundOnce(KillerWinReason reason) { if (!_localRoundEndSoundPlayed && ModeSoundsEnabled()) { _localRoundEndSoundPlayed = true; KillerModeAudio.PlayRoundEnd(IsSurvivorWin(reason)); } } private void LocalLastSurvivorHeartbeatUpdate() { KillerRoundState roundState = RoundState; bool flag = roundState == KillerRoundState.SurvivorHeadStart || roundState == KillerRoundState.Hunt || roundState == KillerRoundState.ExitOpen; bool flag2 = PlayerIDManager.LocalID != null && GetPlayerState(PlayerIDManager.LocalID) == KillerPlayerState.SurvivorAlive; int num = PlayerIDManager.PlayerIDs.Count((PlayerID player) => GetPlayerState(player) == KillerPlayerState.SurvivorAlive); bool enabled = _localLastSurvivorHeartbeatActive && ModeSoundsEnabled() && flag && flag2 && num == 1; roundState = RoundState; int num2; if (roundState == KillerRoundState.Hunt || roundState == KillerRoundState.ExitOpen) { int value = ((MetadataVariableT)(object)_remainingSeconds).GetValue(); num2 = ((value > 0 && value <= 10) ? 1 : 0); } else { num2 = 0; } bool fast = (byte)num2 != 0; KillerModeAudio.SetHeartbeat(enabled, fast); } private void ApplyLocalEnvironment() { if (((Gamemode)this).IsStarted) { KillerEnvironmentEffects.Apply(((MetadataVariableT)(object)_effectiveDarkMap).GetValue(), ((MetadataVariableT)(object)_effectiveDarknessStrength).GetValue(), ((MetadataVariableT)(object)_effectiveDarkFog).GetValue(), ((MetadataVariableT)(object)_effectiveFogStrength).GetValue()); } } private void SyncEnvironmentSettingsIfStarted() { if (NetworkInfo.IsHost && ((Gamemode)this).IsStarted) { ((MetadataVariableT)(object)_effectiveDarkMap).SetValue(Settings.DarkMapEnabled); ((MetadataVariableT)(object)_effectiveDarknessStrength).SetValue(Math.Clamp(Settings.DarknessStrength, 0, 100)); ((MetadataVariableT)(object)_effectiveDarkFog).SetValue(Settings.DarkFogEnabled); ((MetadataVariableT)(object)_effectiveFogStrength).SetValue(Math.Clamp(Settings.FogStrength, 0, 100)); ApplyLocalEnvironment(); } } public static void RequestFlashbangDetonation(Vector3 position) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (GamemodeManager.IsGamemodeStarted && GamemodeManager.ActiveGamemode is KillerModeGamemode killerModeGamemode) { killerModeGamemode.SendFlashbangRequest(position); } } private void SendFlashbangRequest(Vector3 position) { if (PlayerIDManager.LocalID != null && GetPlayerState(PlayerIDManager.LocalID) == KillerPlayerState.SurvivorAlive) { string text = string.Join("|", PlayerIDManager.LocalPlatformID.ToString(CultureInfo.InvariantCulture), position.x.ToString("R", CultureInfo.InvariantCulture), position.y.ToString("R", CultureInfo.InvariantCulture), position.z.ToString("R", CultureInfo.InvariantCulture)); _flashbangRequestEvent.TryInvoke(text); } } private void OnFlashbangRequest(string payload) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (!NetworkInfo.IsHost || !((MetadataVariableT)(object)_effectiveFlashbangEffect).GetValue() || _flashbangRequestCooldown > 0f) { return; } KillerRoundState roundState = RoundState; if ((roundState == KillerRoundState.Hunt || roundState == KillerRoundState.ExitOpen) && TryParseFlashbangPayload(payload, out var platformId, out var position)) { PlayerID playerID = PlayerIDManager.GetPlayerID(platformId); if (GetPlayerState(playerID) == KillerPlayerState.SurvivorAlive && TryGetPlayerFeet(playerID, out var feet) && !(Vector3.Distance(feet, position) > 25f)) { _flashbangRequestCooldown = 0.25f; string text = string.Join("|", position.x.ToString("R", CultureInfo.InvariantCulture), position.y.ToString("R", CultureInfo.InvariantCulture), position.z.ToString("R", CultureInfo.InvariantCulture)); _flashbangDetonationEvent.TryInvoke(text); } } } private void OnFlashbangDetonation(string payload) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (((MetadataVariableT)(object)_effectiveFlashbangEffect).GetValue() && PlayerIDManager.LocalID != null && GetPlayerState(PlayerIDManager.LocalID) == KillerPlayerState.Killer && TryParseVector(payload, out var position) && RigData.HasPlayer) { float num = Mathf.Max(1f, (float)((MetadataVariableT)(object)_effectiveFlashbangRadius).GetValue() / 100f); float num2 = Vector3.Distance(RigData.Refs.Headset.position, position); if (!(num2 > num)) { float intensity = Mathf.Lerp(0.25f, 1f, 1f - num2 / num); KillerFlashEffect.Trigger(Mathf.Max(0.25f, (float)((MetadataVariableT)(object)_effectiveFlashbangDuration).GetValue() / 100f), intensity); } } } private static bool TryParseFlashbangPayload(string payload, out ulong platformId, out Vector3 position) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) platformId = 0uL; position = Vector3.zero; string[] array = payload?.Split('|'); if (array != null && array.Length == 4 && ulong.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out platformId)) { return TryParseVector(array[1] + "|" + array[2] + "|" + array[3], out position); } return false; } private static bool TryParseVector(string payload, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; string[] array = payload?.Split('|'); if (array == null || array.Length != 3 || !float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } position = new Vector3(result, result2, result3); return true; } private void HandleRemainingSecondsChanged() { int value = ((MetadataVariableT)(object)_remainingSeconds).GetValue(); if (RoundState == KillerRoundState.Preparing && value > 0 && value <= 30) { if (value != _lastLocalStartCountdownSecond) { _lastLocalStartCountdownSecond = value; NotifyCountdown(value); if (ModeSoundsEnabled()) { KillerModeAudio.PlayCountdownTick(Math.Min(10, value)); } } return; } KillerRoundState roundState = RoundState; if ((roundState == KillerRoundState.Hunt || roundState == KillerRoundState.ExitOpen) && value > 0 && value <= 10 && value != _lastLocalCountdownSecond) { _lastLocalCountdownSecond = value; if (ModeSoundsEnabled()) { KillerModeAudio.PlayCountdownTick(value); } if (value == 10) { Notify("TIME RUNNING OUT", "10 seconds remain.", (NotificationType)1); } } } private void ShowLocalRoleMessageOnce() { if (!_localRoleMessageShown && PlayerIDManager.LocalID != null) { _localRoleMessageShown = true; Notifier.Cancel("KillerModeStartCountdown"); switch (GetPlayerState(PlayerIDManager.LocalID)) { case KillerPlayerState.Killer: Notify("YOU ARE THE KILLER", "KILL ALL SURVIVORS", (NotificationType)1, 5f); break; case KillerPlayerState.SurvivorAlive: Notify("YOU ARE A SURVIVOR", "HIDE OR ESCAPE", (NotificationType)3, 5f); break; } } } private void UpdateLocalRoundTimer() { KillerPlayerState localRole = ((PlayerIDManager.LocalID != null) ? GetPlayerState(PlayerIDManager.LocalID) : KillerPlayerState.None); KillerModeHud.Update(RoundState, ((MetadataVariableT)(object)_remainingSeconds).GetValue(), localRole, ((MetadataVariableT)(object)_effectiveShowRoundTimer).GetValue()); } private void OnPlayerStateChanged(PlayerID player, MetadataInt variable) { KillerPlayerState value = (KillerPlayerState)((MetadataVariableT)(object)variable).GetValue(); switch (value) { case KillerPlayerState.Killer: KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeKillerSelected(); }); break; case KillerPlayerState.SurvivorEliminated: KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeSurvivorEliminated(); }); break; case KillerPlayerState.SurvivorEscaped: KillerMapRegistry.InvokeEvents(delegate(KillerMapEvents e) { e.InvokeSurvivorEscaped(); }); break; } if (player.IsMe) { ApplyLocalState(teleport: true); } NetworkPlayer val = default(NetworkPlayer); if (NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(player), ref val)) { bool flag = value == KillerPlayerState.Spectator || value == KillerPlayerState.SurvivorEliminated || value == KillerPlayerState.SurvivorEscaped; KillerPlayerState playerState = GetPlayerState(PlayerIDManager.LocalID); bool flag2 = playerState == KillerPlayerState.Spectator || playerState == KillerPlayerState.SurvivorEliminated || playerState == KillerPlayerState.SurvivorEscaped; val.ForceHide = flag && !flag2; } } private void OnAssignedToTeam(PlayerID player, Team team) { if (player.IsMe) { ApplyLocalState(teleport: true); } } private void ApplyLocalState(bool teleport) { if (((Gamemode)this).IsStarted && PlayerIDManager.LocalID != null) { KillerPlayerState playerState = GetPlayerState(PlayerIDManager.LocalID); bool flag = playerState == KillerPlayerState.Spectator || playerState == KillerPlayerState.SurvivorEliminated || playerState == KillerPlayerState.SurvivorEscaped; bool flag2 = playerState == KillerPlayerState.Killer; ApplyLocalKillerSkin(flag2); bool flag3 = RoundState == KillerRoundState.Preparing; LocalControls.DisableInteraction = flag || flag3; LocalControls.DisableInventory = flag; LocalControls.DisableAmmoPouch = flag; LocalControls.LockedMovement = flag3 || (flag2 && RoundState == KillerRoundState.SurvivorHeadStart); LocalHealth.MortalityOverride = !flag && (!flag2 || ((MetadataVariableT)(object)_effectiveKillerCanDie).GetValue()); LocalHealth.RegenerationOverride = (flag ? ((bool?)null) : new bool?(false)); LocalHealth.VitalityOverride = (flag ? ((float?)null) : new float?((float)(flag2 ? ((MetadataVariableT)(object)_effectiveKillerVitality).GetValue() : ((MetadataVariableT)(object)_effectiveSurvivorVitality).GetValue()) / 100f)); ApplyLocalMovement(flag ? 1f : ((float)(flag2 ? ((MetadataVariableT)(object)_effectiveKillerSpeed).GetValue() : ((MetadataVariableT)(object)_effectiveSurvivorSpeed).GetValue()) / 100f)); if (teleport) { TeleportLocal(playerState); } } } private void TeleportLocal(KillerPlayerState state) { TeleportLocalToRole(state switch { KillerPlayerState.Killer => KillerSpawnRole.Killer, KillerPlayerState.SurvivorAlive => KillerSpawnRole.Survivor, _ => KillerSpawnRole.Spectator, }); } private static void TeleportLocalToRole(KillerSpawnRole role) { //IL_002a: 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) if (PlayerIDManager.LocalID == null || !RigData.HasPlayer) { return; } KillerSpawnMarker spawn = KillerMapRegistry.GetSpawn(role, PlayerIDManager.LocalSmallID); if ((Object)(object)spawn != (Object)null) { LocalPlayer.TeleportToPosition(((Component)spawn).transform.position, ((Component)spawn).transform.forward); return; } IReadOnlyList readOnlyList = GamemodeMarker.FilterMarkers((BoneTagReference)null); if (readOnlyList.Count > 0) { GamemodeHelper.SetSpawnPoint(readOnlyList[PlayerIDManager.LocalSmallID % readOnlyList.Count]); GamemodeHelper.TeleportToSpawnPoint(); } } private void OnLocalRespawn() { if (((Gamemode)this).IsStarted) { ApplyLocalState(teleport: true); } } private void OnLocalAvatarChanged(Avatar avatar, string barcode) { if (((Gamemode)this).IsStarted && !((Object)(object)avatar == (Object)null)) { _baseAvatarSpeed = avatar._speed; _baseAvatarAgility = avatar._agility; ApplyLocalState(teleport: false); } } private void ApplyLocalKillerSkin(bool isKiller) { KillerSkinChoice value = (KillerSkinChoice)((MetadataVariableT)(object)_effectiveSkin).GetValue(); string text = ((value == KillerSkinChoice.SelectedDownloadedAvatar) ? _effectiveSkinBarcode.GetValueOrEmpty() : KillerContentReferences.GetKillerAvatarBarcode(value)); if (KillerRoundRules.ShouldWaitForSelectedAvatarBarcode(isKiller, value, text)) { return; } string text2 = ((isKiller && !string.IsNullOrWhiteSpace(text)) ? text : _localRoundAvatarBarcode); if (string.IsNullOrWhiteSpace(text2)) { return; } if (!KillerContentReferences.IsAvatarInstalled(text2)) { string barcode = (KillerContentReferences.IsAvatarInstalled("c3534c5a-94b2-40a4-912a-24a8506f6c79") ? "c3534c5a-94b2-40a4-912a-24a8506f6c79" : _localRoundAvatarBarcode); LockLocalAvatar(barcode); if (isKiller && !_localKillerSkinFallbackNotified) { _localKillerSkinFallbackNotified = true; Notify("Killer Skin Fallback", "The selected avatar is unavailable. Using PolyBlank safely while Fusion tries the download.", (NotificationType)1, 7f); MelonLogger.Warning("[Killer Mode] Selected Killer avatar is unavailable; using safe fallback: " + text2); } if (isKiller && value == KillerSkinChoice.SelectedDownloadedAvatar && _failedKillerAvatarBarcode != text2) { TryDownloadSelectedKillerAvatar(text2); } else { MelonLogger.Warning("[Killer Mode] Round avatar was not found: " + text2); } } else { LockLocalAvatar(text2); } } private void LockLocalAvatar(string barcode) { if (!string.IsNullOrWhiteSpace(barcode) && KillerContentReferences.IsAvatarInstalled(barcode)) { if (LocalAvatar.AvatarOverride != barcode) { LocalAvatar.AvatarOverride = barcode; } _localAvatarLockApplied = true; } } private void TryDownloadSelectedKillerAvatar(string barcode) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (_killerAvatarDownloadPending && _pendingKillerAvatarBarcode == barcode) { return; } if (!ClientSettings.Downloading.DownloadAvatars.Value) { _failedKillerAvatarBarcode = barcode; Notify("Killer Skin Missing", "Enable Fusion's Download Avatars setting, then start the next round again.", (NotificationType)1); return; } if (PlayerIDManager.LocalID == null || NetworkInfo.IsHost) { _failedKillerAvatarBarcode = barcode; Notify("Killer Skin Missing", "The host no longer has the selected avatar installed.", (NotificationType)1); return; } _killerAvatarDownloadPending = true; _pendingKillerAvatarBarcode = barcode; Notify("Killer Skin", "Downloading the host's selected Killer avatar...", (NotificationType)0); long value = DataConversions.ConvertMegabytesToBytes((long)ClientSettings.Downloading.MaxFileSize.Value); NetworkModRequester.RequestAndInstallMod(new ModInstallInfo { Target = 0, Barcode = barcode, MaxBytes = value, FinishDownloadCallback = (DownloadCallback)delegate(DownloadCallbackInfo info) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (!(_pendingKillerAvatarBarcode != barcode)) { _killerAvatarDownloadPending = false; _pendingKillerAvatarBarcode = null; if ((int)info.Result != 2) { _failedKillerAvatarBarcode = barcode; Notify("Killer Skin Download Failed", "PolyBlank will be used this round. Install the selected avatar manually or enable avatar downloads before the next round.", (NotificationType)1, 8f); } else if (!KillerContentReferences.IsAvatarInstalled(barcode)) { _failedKillerAvatarBarcode = barcode; Notify("Killer Skin Download Failed", "The download finished but the avatar crate is unavailable. PolyBlank will be used this round.", (NotificationType)1, 8f); } else { _failedKillerAvatarBarcode = null; if (((Gamemode)this).IsStarted && PlayerIDManager.LocalID != null && GetPlayerState(PlayerIDManager.LocalID) == KillerPlayerState.Killer) { ApplyLocalKillerSkin(isKiller: true); } } } } }); } private void RestoreLocalAvatarLock() { if (_localAvatarLockApplied) { _localAvatarLockApplied = false; LocalAvatar.AvatarOverride = null; _localRoundAvatarBarcode = null; _localKillerSkinFallbackNotified = false; _failedKillerAvatarBarcode = null; } } private void ApplyLocalMovement(float multiplier) { if (!RigData.HasPlayer) { return; } Avatar avatar = RigData.Refs.RigManager.avatar; if (!((Object)(object)avatar == (Object)null)) { float valueOrDefault = _baseAvatarSpeed.GetValueOrDefault(); if (!_baseAvatarSpeed.HasValue) { valueOrDefault = avatar._speed; _baseAvatarSpeed = valueOrDefault; } valueOrDefault = _baseAvatarAgility.GetValueOrDefault(); if (!_baseAvatarAgility.HasValue) { valueOrDefault = avatar._agility; _baseAvatarAgility = valueOrDefault; } avatar._speed = _baseAvatarSpeed.Value * Mathf.Clamp(multiplier, 0.5f, 2f); avatar._agility = _baseAvatarAgility.Value * Mathf.Clamp(multiplier, 0.5f, 2f); } } private void RestoreLocalMovement() { if (RigData.HasPlayer) { Avatar avatar = RigData.Refs.RigManager.avatar; if ((Object)(object)avatar != (Object)null) { if (_baseAvatarSpeed.HasValue) { avatar._speed = _baseAvatarSpeed.Value; } if (_baseAvatarAgility.HasValue) { avatar._agility = _baseAvatarAgility.Value; } } } _baseAvatarSpeed = null; _baseAvatarAgility = null; } private void LocalInteractionUpdate() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (PlayerIDManager.LocalID == null || GetPlayerState(PlayerIDManager.LocalID) != KillerPlayerState.SurvivorAlive || !RigData.HasPlayer) { return; } Vector3 feet = RigData.Refs.RigManager.physicsRig.feet.transform.position; if (_localEscapeRequested) { _localEscapeRetry -= TimeReferences.DeltaTime; if (_localEscapeRetry <= 0f) { _localEscapeRequested = false; } } if (RoundState == KillerRoundState.ExitOpen && !_localEscapeRequested && KillerMapRegistry.EscapeZones.Any((KillerEscapeZone z) => (Object)(object)z != (Object)null && z.Contains(feet))) { _localEscapeRequested = _escapeRequest.TryInvoke(PlayerIDManager.LocalPlatformID.ToString()); _localEscapeRetry = 1f; } } private void LocalRoundLoadoutUpdate() { if (_localInventoryCleared || PlayerIDManager.LocalID == null) { return; } KillerPlayerState playerState = GetPlayerState(PlayerIDManager.LocalID); if ((playerState == KillerPlayerState.Killer || playerState == KillerPlayerState.SurvivorAlive) && TryGetClearStartingInventorySetting(out var enabled)) { if (!enabled) { _localInventoryCleared = true; } else if (RigData.HasPlayer && RigData.Refs != null && RigData.Refs.IsValid) { int value = ClearLocalStartingInventory(); MelonLogger.Msg($"[Killer Mode] Starting inventory cleared: {value} held/holstered item(s), plus all carried ammo."); _localInventoryCleared = true; } } } private bool TryGetClearStartingInventorySetting(out bool enabled) { if (NetworkInfo.IsHost) { enabled = _roundClearStartingInventory; return true; } string text = default(string); if (!((Gamemode)this).Metadata.TryGetMetadata("ClearStartingInventory", ref text)) { enabled = false; return false; } enabled = ((MetadataVariableT)(object)_effectiveClearStartingInventory).GetValue(); return true; } private static int ClearLocalStartingInventory() { //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); Hand[] array = (Hand[])(object)new Hand[2] { RigData.Refs.LeftHand, RigData.Refs.RightHand }; NetworkEntity val3 = default(NetworkEntity); foreach (Hand val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.m_CurrentAttachedGO == (Object)null) { continue; } Poolee val2 = val.m_CurrentAttachedGO.GetComponentInParent() ?? Poolee.Cache.Get(val.m_CurrentAttachedGO); if ((Object)(object)val2 != (Object)null) { if (PooleeExtender.Cache.TryGet(val2, ref val3)) { hashSet.Add(val3.ID); } else { hashSet2.Add(val2); } } HandExtensions.TryDetach(val); } InventorySlotReceiver[] array2 = RigData.Refs.RigSlots ?? Array.Empty(); NetworkEntity val5 = default(NetworkEntity); foreach (InventorySlotReceiver val4 in array2) { if ((Object)(object)val4 == (Object)null || (Object)(object)val4._slottedWeapon == (Object)null) { continue; } if (WeaponSlotExtender.Cache.TryGet(val4._slottedWeapon, ref val5)) { hashSet.Add(val5.ID); } else { Poolee componentInParent = ((Component)val4._slottedWeapon).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { hashSet2.Add(componentInParent); } } val4.DropWeapon(); } LocalInventory.ClearAmmo(); foreach (ushort item in hashSet) { NetworkAssetSpawner.Despawn(new DespawnRequestInfo { EntityID = item, DespawnEffect = false }); } foreach (Poolee item2 in hashSet2) { if ((Object)(object)item2 != (Object)null) { item2.Despawn(); } } return hashSet.Count + hashSet2.Count; } private void LocalLastSurvivorRevealUpdate() { if (PlayerIDManager.LocalID == null || GetPlayerState(PlayerIDManager.LocalID) != KillerPlayerState.Killer || !((MetadataVariableT)(object)_effectiveRevealEnabled).GetValue()) { SetRevealedPlayer(null); return; } PlayerID[] array = PlayerIDManager.PlayerIDs.Where((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorAlive).ToArray(); if (array.Length != 1) { SetRevealedPlayer(null); _revealCycle = 0f; _revealVisible = 0f; return; } _revealCycle += TimeReferences.DeltaTime; if (_revealCycle >= (float)Math.Max(3, ((MetadataVariableT)(object)_effectiveRevealInterval).GetValue())) { _revealCycle = 0f; _revealVisible = Math.Max(1, ((MetadataVariableT)(object)_effectiveRevealDuration).GetValue()); SetRevealedPlayer(array[0].SmallID); } _revealVisible = Math.Max(0f, _revealVisible - TimeReferences.DeltaTime); if (_revealVisible <= 0f) { SetRevealedPlayer(null); } } private void SetRevealedPlayer(byte? smallId) { if (_revealedPlayerId == smallId) { return; } if (_revealedPlayerId.HasValue) { PlayerID playerID = PlayerIDManager.GetPlayerID(_revealedPlayerId.Value); NetworkPlayer val = default(NetworkPlayer); if (playerID != null && NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(playerID), ref val)) { val.Icon.Visible = false; } } _revealedPlayerId = smallId; if (smallId.HasValue) { PlayerID playerID2 = PlayerIDManager.GetPlayerID(smallId.Value); NetworkPlayer val2 = default(NetworkPlayer); if (playerID2 != null && NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(playerID2), ref val2)) { EnsureRevealTexture(); val2.Icon.Texture = (Texture)(object)_revealTexture; val2.Icon.Visible = true; } } FusionOverrides.ForceUpdateOverrides(); } private void EnsureRevealTexture() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_revealTexture != (Object)null)) { _revealTexture = new Texture2D(16, 16); _revealTexture.SetPixels(Il2CppStructArray.op_Implicit(Enumerable.Repeat(new Color(1f, 0.08f, 0.03f, 1f), 256).ToArray())); _revealTexture.Apply(); } } private void OnEscapeRequest(string payload) { if (NetworkInfo.IsHost && RoundState == KillerRoundState.ExitOpen && ulong.TryParse(payload, out var result)) { PlayerID playerID = PlayerIDManager.GetPlayerID(result); if (GetPlayerState(playerID) == KillerPlayerState.SurvivorAlive && TryGetPlayerFeet(playerID, out var feet) && KillerMapRegistry.EscapeZones.Any((KillerEscapeZone z) => (Object)(object)z != (Object)null && z.Contains(feet))) { SetPlayerState(playerID, KillerPlayerState.SurvivorEscaped); _teams.TryAssignTeam(playerID, _spectatorTeam); CheckSurvivorsRemaining(); } } } private static bool TryGetPlayerFeet(PlayerID player, out Vector3 feet) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) feet = Vector3.zero; if (player == null) { return false; } if (player.IsMe && RigData.HasPlayer) { feet = RigData.Refs.RigManager.physicsRig.feet.transform.position; return true; } NetworkPlayer val = default(NetworkPlayer); if (NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(player), ref val) && val.HasRig) { feet = val.RigRefs.RigManager.physicsRig.feet.transform.position; return true; } return false; } public override bool CanAttack(PlayerID target) { if (!((Gamemode)this).IsStarted || PlayerIDManager.LocalID == null) { return true; } KillerPlayerState playerState = GetPlayerState(PlayerIDManager.LocalID); KillerPlayerState playerState2 = GetPlayerState(target); return KillerRoundRules.CanAttack(playerState, playerState2, RoundState, ((MetadataVariableT)(object)_effectiveSurvivorDamage).GetValue()); } internal bool CanLocalPlayerHearVoice(PlayerID speaker) { if (!((Gamemode)this).IsStarted || PlayerIDManager.LocalID == null || speaker == null) { return true; } return KillerRoundRules.CanHearVoice(GetPlayerState(PlayerIDManager.LocalID), GetPlayerState(speaker)); } private bool OnValidateNametag(PlayerID id) { if (!((Gamemode)this).IsStarted) { return true; } if (PlayerIDManager.LocalID != null && GetPlayerState(PlayerIDManager.LocalID) == KillerPlayerState.Killer && _revealedPlayerId.HasValue) { return id.SmallID == _revealedPlayerId.Value; } return false; } public override void OnGamemodeStopped() { _localLastSurvivorHeartbeatActive = false; KillerModeAudio.StopHeartbeat(); KillerFlashEffect.Clear(); KillerEnvironmentEffects.Restore(); _killerAvatarDownloadPending = false; _pendingKillerAvatarBarcode = null; RestoreLocalAvatarLock(); if (NetworkInfo.IsHost) { CleanupRoundItems(); PlayerID[] array = PlayerIDManager.PlayerIDs.ToArray(); foreach (PlayerID player in array) { SetPlayerState(player, KillerPlayerState.None); } _teams.UnassignAllPlayers(); if (_pauseAutomaticRoundsAfterCurrentRound) { _pauseAutomaticRoundsAfterCurrentRound = false; _automaticRoundsPaused = true; if (GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } Notify("Automatic Rounds Paused", "The finished round will not restart automatically.", (NotificationType)3); } } LocalControls.LockedMovement = false; LocalControls.DisableInteraction = false; LocalControls.DisableSlowMo = false; LocalControls.DisableInventory = false; LocalControls.DisableAmmoPouch = false; LocalHealth.MortalityOverride = null; LocalHealth.RegenerationOverride = null; LocalHealth.VitalityOverride = null; RestoreLocalMovement(); GamemodeHelper.ResetSpawnPoints(); SetRevealedPlayer(null); KillerModeHud.Destroy(); KillerInGameMapSetup.SetVisuals(_showInGameSetupMarkers); if ((Object)(object)_revealTexture != (Object)null) { Object.Destroy((Object)(object)_revealTexture); _revealTexture = null; } NetworkPlayer val = default(NetworkPlayer); foreach (PlayerID playerID in PlayerIDManager.PlayerIDs) { if (NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(playerID), ref val)) { val.ForceHide = false; } } TeleportLocalToRole(KillerSpawnRole.Lobby); KillerDoor[] array2 = KillerMapRegistry.Doors.ToArray(); foreach (KillerDoor killerDoor in array2) { if ((Object)(object)killerDoor != (Object)null) { killerDoor.Apply(KillerRoundState.Lobby); } } } private void SpawnRoundItems() { //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) foreach (KillerItemSpawnPoint item in KillerMapRegistry.ItemSpawns.Where((KillerItemSpawnPoint p) => (Object)(object)p != (Object)null)) { if (FlashbangFeatureEnabled || !(item is KillerFlashbangSpawner)) { ItemSpawnerState itemSpawnerState = new ItemSpawnerState { Point = item }; _itemSpawnerStates[item] = itemSpawnerState; if (item.SpawnAtRoundStart) { SpawnFromItemPoint(itemSpawnerState); } } } if (Settings.GiveSurvivorsWeapon) { string text = Settings.SurvivorWeaponBarcode?.Trim() ?? string.Empty; if (!KillerContentReferences.IsInstalled(text)) { MelonLogger.Warning("[Killer Mode] Survivor weapon barcode is not installed: " + text); } else { foreach (PlayerID item2 in PlayerIDManager.PlayerIDs.Where((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorAlive)) { if (TryGetRoleSpawnPose(item2, KillerSpawnRole.Survivor, out var position, out var rotation)) { SpawnBarcode(text, position + Vector3.up + rotation * (Vector3.right * 0.55f), rotation, destroyOnRoundEnd: true); continue; } MelonLogger.Warning($"[Killer Mode] Could not find a safe weapon spawn position for survivor {item2.SmallID}."); } } } if (Settings.SpawnSurvivorFlashlights) { string text2 = EffectiveFlashlightBarcode(); if (!KillerContentReferences.IsInstalled(text2)) { MelonLogger.Warning("[Killer Mode] Survivor flashlight barcode is not installed: " + text2); } else { SpawnSurvivorFlashlights(text2); } } if (!FlashbangFeatureEnabled || !Settings.GiveSurvivorsFlashbangs) { return; } string text3 = EffectiveFlashbangBarcode(); if (!KillerContentReferences.IsInstalled(text3)) { MelonLogger.Warning("[Killer Mode] Survivor flashbang barcode is not installed: " + text3); return; } foreach (PlayerID item3 in PlayerIDManager.PlayerIDs.Where((PlayerID p) => GetPlayerState(p) == KillerPlayerState.SurvivorAlive)) { if (TryGetRoleSpawnPose(item3, KillerSpawnRole.Survivor, out var position2, out var rotation2)) { SpawnBarcode(text3, position2 + Vector3.up * 1.15f, rotation2, destroyOnRoundEnd: true, delegate(SpawnCallbackInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) AttachFlashbangRuntime(info); }); } } } private void SpawnSurvivorFlashlights(string barcode) { //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) PlayerID[] array = (from player in PlayerIDManager.PlayerIDs where GetPlayerState(player) == KillerPlayerState.SurvivorAlive orderby player.SmallID select player).ToArray(); if (array.Length == 0) { MelonLogger.Warning("[Killer Mode] No living Survivors were found when flashlights were spawned."); return; } var enumerable = from player in array select new { Player = player, Marker = KillerMapRegistry.GetSpawn(KillerSpawnRole.Survivor, player.SmallID) } into assignment where (Object)(object)assignment.Marker != (Object)null group assignment by assignment.Marker; HashSet placed = new HashSet(); Vector3 val = default(Vector3); foreach (var item in enumerable) { var array2 = item.ToArray(); Transform transform = ((Component)item.Key).transform; for (int num = 0; num < array2.Length; num++) { float num2 = ((array2.Length == 1) ? 0f : ((float)num * (float)Math.PI * 2f / (float)array2.Length)); float num3 = ((array2.Length == 1) ? 0f : 0.38f); ((Vector3)(ref val))..ctor(Mathf.Cos(num2) * num3, 0f, Mathf.Sin(num2) * num3); Vector3 position = transform.position + Vector3.up * 0.25f + transform.rotation * val; byte survivorSmallId = array2[num].Player.SmallID; SpawnBarcode(barcode, position, transform.rotation, destroyOnRoundEnd: true, delegate { MelonLogger.Msg($"[Killer Mode] Flashlight spawned in Survivor circle for player {survivorSmallId}."); }); placed.Add(array2[num].Player.PlatformID); } } foreach (PlayerID survivor in array.Where((PlayerID player) => !placed.Contains(player.PlatformID))) { if (TryGetRoleSpawnPose(survivor, KillerSpawnRole.Survivor, out var position2, out var rotation)) { SpawnBarcode(barcode, position2 + Vector3.up * 0.25f, rotation, destroyOnRoundEnd: true, delegate { MelonLogger.Msg($"[Killer Mode] Flashlight spawned at the fallback Survivor spawn for player {survivor.SmallID}."); }); } else { MelonLogger.Warning($"[Killer Mode] Could not find a Survivor circle for player {survivor.SmallID}."); } } MelonLogger.Msg($"[Killer Mode] Requested {array.Length} Survivor flashlight(s) using barcode {barcode}."); } private static bool TryGetRoleSpawnPose(PlayerID player, KillerSpawnRole role, out Vector3 position, out Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; rotation = Quaternion.identity; if (player == null) { return false; } KillerSpawnMarker spawn = KillerMapRegistry.GetSpawn(role, player.SmallID); if ((Object)(object)spawn != (Object)null) { position = ((Component)spawn).transform.position; rotation = ((Component)spawn).transform.rotation; return true; } IReadOnlyList readOnlyList = GamemodeMarker.FilterMarkers((BoneTagReference)null); if (readOnlyList.Count > 0) { Transform transform = ((Component)readOnlyList[player.SmallID % readOnlyList.Count]).transform; position = transform.position; rotation = transform.rotation; return true; } if (TryGetPlayerFeet(player, out position)) { rotation = ((player.IsMe && RigData.HasPlayer) ? ((Component)RigData.Refs.RigManager).transform.rotation : Quaternion.identity); return true; } return false; } private void UpdateItemSpawners() { ItemSpawnerState[] array = _itemSpawnerStates.Values.ToArray(); foreach (ItemSpawnerState itemSpawnerState in array) { KillerItemSpawnPoint point = itemSpawnerState.Point; if ((Object)(object)point == (Object)null) { continue; } if ((Object)(object)itemSpawnerState.CurrentItem != (Object)null && itemSpawnerState.CurrentItem.HasBeenInteracted) { itemSpawnerState.CurrentItem = null; itemSpawnerState.Cooldown = Math.Max(0f, point.RespawnCooldown); } if (!((Object)(object)itemSpawnerState.CurrentItem != (Object)null) && point.RespawnEnabled && itemSpawnerState.Uses < Math.Max(1, point.MaxUsesPerRound)) { itemSpawnerState.Cooldown -= TimeReferences.DeltaTime; int num = _spawnedRoundItems.Count((SpawnedRoundItem spawnedRoundItem) => (Object)(object)spawnedRoundItem.GameObject != (Object)null && (Object)(object)spawnedRoundItem.Source == (Object)(object)point); if (itemSpawnerState.Cooldown <= 0f && num < Math.Max(1, point.MaxActive)) { SpawnFromItemPoint(itemSpawnerState); } } } } private void SpawnFromItemPoint(ItemSpawnerState state) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) KillerItemSpawnPoint point = state.Point; if ((Object)(object)point == (Object)null || state.Uses >= Math.Max(1, point.MaxUsesPerRound)) { return; } state.Uses++; string barcode = ResolveItemSpawnBarcode(point); SpawnBarcode(barcode, ((Component)point).transform.position, ((Component)point).transform.rotation, point.DestroyOnRoundEnd, delegate(SpawnCallbackInfo info) { //IL_0000: 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) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)info.Spawned == (Object)null)) { GamemodeItem val = info.Spawned.AddComponent(); Poolee val2 = Poolee.Cache.Get(info.Spawned); if ((Object)(object)val2 != (Object)null) { val.Initialize(info.Entity, val2); } if (point is KillerFlashbangSpawner) { AttachFlashbangRuntime(info, val); } state.CurrentItem = val; } }, point); } private string ResolveItemSpawnBarcode(KillerItemSpawnPoint point) { if (point is KillerFlashlightSpawner { UseHostBarcode: not false }) { return EffectiveFlashlightBarcode(); } if (!(point is KillerFlashbangSpawner { UseHostBarcode: not false })) { return point.SpawnableBarcode?.Trim() ?? string.Empty; } return EffectiveFlashbangBarcode(); } private bool ModeSoundsEnabled() { if (!NetworkInfo.IsHost) { return ((MetadataVariableT)(object)_effectiveModeSounds).GetValue(); } return _roundModeSoundsEnabled; } private string EffectiveFlashlightBarcode() { string text = (NetworkInfo.IsHost ? _roundFlashlightBarcode : _effectiveFlashlightBarcode.GetValue()); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } return "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks"; } private string EffectiveFlashbangBarcode() { return (NetworkInfo.IsHost ? _roundFlashbangBarcode : _effectiveFlashbangBarcode.GetValue())?.Trim() ?? string.Empty; } private static void AttachFlashbangRuntime(SpawnCallbackInfo info, GamemodeItem item = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0042: 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) if ((Object)(object)info.Spawned == (Object)null || info.Entity == null) { return; } if (item == null) { item = info.Spawned.GetComponent(); } if ((Object)(object)item == (Object)null) { item = info.Spawned.AddComponent(); Poolee val = Poolee.Cache.Get(info.Spawned); if ((Object)(object)val != (Object)null) { item.Initialize(info.Entity, val); } } info.Spawned.AddComponent().Initialize(info.Entity, item); } private void SpawnKillerWeapon() { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if (!NetworkInfo.IsHost || _killerWeaponSpawned) { return; } _killerWeaponSpawned = true; string text = (string.IsNullOrWhiteSpace(_roundWeaponBarcode) ? _effectiveWeaponBarcode.GetValue() : _roundWeaponBarcode); if (string.IsNullOrWhiteSpace(text)) { return; } PlayerID playerID = PlayerIDManager.GetPlayerID(_killerPlatformId); if (playerID == null) { return; } object obj; if (Settings.WeaponChoice != KillerWeaponChoice.MapDefined) { obj = null; } else { KillerWeaponSpawn? killerWeaponSpawn = KillerMapRegistry.ItemSpawns.OfType().FirstOrDefault((KillerWeaponSpawn point) => (Object)(object)point != (Object)null && ((Behaviour)point).isActiveAndEnabled); obj = ((killerWeaponSpawn != null) ? ((Component)killerWeaponSpawn).transform : null); } Transform val = (Transform)obj; if (val == null) { KillerSpawnMarker spawn = KillerMapRegistry.GetSpawn(KillerSpawnRole.Killer, playerID.SmallID); val = ((spawn != null) ? ((Component)spawn).transform : null); } if ((Object)(object)val == (Object)null) { IReadOnlyList readOnlyList = GamemodeMarker.FilterMarkers((BoneTagReference)null); if (readOnlyList.Count > 0) { val = ((Component)readOnlyList[playerID.SmallID % readOnlyList.Count]).transform; } } Vector3 feet; if ((Object)(object)val != (Object)null) { SpawnBarcode(text, val.position + Vector3.up, val.rotation, destroyOnRoundEnd: true); } else if (TryGetPlayerFeet(playerID, out feet)) { SpawnBarcode(text, feet + Vector3.up, Quaternion.identity, destroyOnRoundEnd: true); } } private void RecoverLostKillerWeapon() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) if (!NetworkInfo.IsHost) { Notify("Killer Weapon", "Only the Fusion host can recover the Killer's weapon.", (NotificationType)1); return; } if (((Gamemode)this).IsStarted) { KillerRoundState roundState = RoundState; if (roundState == KillerRoundState.Hunt || roundState == KillerRoundState.ExitOpen) { if (_weaponRecoveryCooldown > 0f) { Notify("Killer Weapon", $"Wait {Mathf.CeilToInt(_weaponRecoveryCooldown)} seconds before recovering it again.", (NotificationType)1); return; } string text = (string.IsNullOrWhiteSpace(_roundWeaponBarcode) ? _effectiveWeaponBarcode.GetValue() : _roundWeaponBarcode); PlayerID playerID = PlayerIDManager.GetPlayerID(_killerPlatformId); if (string.IsNullOrWhiteSpace(text) || playerID == null || !TryGetPlayerFeet(playerID, out var feet)) { Notify("Killer Weapon", "Could not find the Killer or the selected weapon.", (NotificationType)1); return; } SpawnBarcode(text, feet + Vector3.up * 0.8f, Quaternion.identity, destroyOnRoundEnd: true); _weaponRecoveryCooldown = 10f; Notify("Killer Weapon Recovered", "A replacement weapon spawned beside the Killer.", (NotificationType)3); return; } } Notify("Killer Weapon", "The weapon can be recovered after the Hunt begins.", (NotificationType)1); } private void SpawnBarcode(string barcode, Vector3 position, Quaternion rotation, bool destroyOnRoundEnd, Action callback = null, KillerItemSpawnPoint source = null) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (!KillerContentReferences.IsInstalled(barcode)) { MelonLogger.Warning("[Killer Mode] Optional spawnable is not installed: " + barcode); return; } Spawnable val = LocalAssetSpawner.CreateSpawnable(barcode); LocalAssetSpawner.Register(val); NetworkAssetSpawner.Spawn(new SpawnRequestInfo { Spawnable = val, Position = position, Rotation = rotation, SpawnEffect = true, SpawnSource = (EntitySource)1, SpawnCallback = delegate(SpawnCallbackInfo info) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) _spawnedRoundItems.Add(new SpawnedRoundItem { EntityId = info.Entity.ID, GameObject = info.Spawned, DestroyOnRoundEnd = destroyOnRoundEnd, Source = source }); callback?.Invoke(info); } }); } private void CleanupRoundItems() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) SpawnedRoundItem[] array = _spawnedRoundItems.Where((SpawnedRoundItem item) => item.DestroyOnRoundEnd).ToArray(); SpawnedRoundItem[] array2 = array; foreach (SpawnedRoundItem spawnedRoundItem in array2) { NetworkAssetSpawner.Despawn(new DespawnRequestInfo { EntityID = spawnedRoundItem.EntityId, DespawnEffect = false }); } _spawnedRoundItems.Clear(); _itemSpawnerStates.Clear(); if (array.Length != 0) { MelonLogger.Msg($"[Killer Mode] Removed {array.Length} round item(s); fresh equipment will spawn next round."); } } private static bool IsSurvivorWin(KillerWinReason reason) { if (reason != KillerWinReason.SurvivorsOutlastedTimer && reason != KillerWinReason.SurvivorEscaped && reason != KillerWinReason.KillerDisconnected) { return reason == KillerWinReason.KillerDied; } return true; } private static string WinMessage(KillerWinReason reason) { return reason switch { KillerWinReason.SurvivorsOutlastedTimer => "The survivors outlasted the killer.", KillerWinReason.SurvivorEscaped => "A survivor escaped.", KillerWinReason.KillerDisconnected => "The killer disconnected. Survivors win.", KillerWinReason.KillerDied => "The killer was defeated. Survivors win.", KillerWinReason.NotEnoughPlayers => "The round ended because there are not enough players.", KillerWinReason.SoloTestComplete => "Solo test complete.", _ => "The killer eliminated every survivor.", }; } protected static void Notify(string title, string message, NotificationType type, float length = 4f, string tag = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown Notifier.Send(new Notification { Title = NotificationText.op_Implicit(title), Message = NotificationText.op_Implicit(message), Type = type, ShowPopup = true, SaveToMenu = false, PopupLength = length, Tag = tag }); } private static void NotifyCountdown(int remaining) { Notifier.Cancel("KillerModeStartCountdown"); Notify($"ROUND STARTING IN {remaining}", (remaining == 1) ? "GET READY" : remaining.ToString(), (NotificationType)1, 0.92f, "KillerModeStartCountdown"); } private void LoadAndPublishInGameMapSetup() { if (NetworkInfo.IsHost) { string text = KillerInGameMapSetup.LoadCurrentMap(_showInGameSetupMarkers && !((Gamemode)this).IsStarted); ((Gamemode)this).Metadata.TrySetMetadata("InGameMapSetupProfile", text); } } private void PlaceInGameSetupPoint(InGameSetupPoint point) { if (CanEditInGameMapSetup()) { if (KillerInGameMapSetup.PlaceAtPlayer(point, out var message)) { ((Gamemode)this).Metadata.TrySetMetadata("InGameMapSetupProfile", KillerInGameMapSetup.SerializeCurrentMap()); Notify("Map Setup Saved", message, (NotificationType)3); } else { Notify("Map Setup", message, (NotificationType)1); } } } private void RemoveLastSurvivorSetupPoint() { if (CanEditInGameMapSetup()) { PublishMapSetupResult(KillerInGameMapSetup.RemoveLastSurvivor(out var message), message); } } private void RemoveEscapeSetupPoint() { if (CanEditInGameMapSetup()) { PublishMapSetupResult(KillerInGameMapSetup.RemoveEscape(out var message), message); } } private void ClearCurrentMapSetup() { if (CanEditInGameMapSetup()) { PublishMapSetupResult(KillerInGameMapSetup.ClearCurrentMap(out var message), message); } } private void PublishMapSetupResult(bool success, string message) { if (success) { ((Gamemode)this).Metadata.TrySetMetadata("InGameMapSetupProfile", KillerInGameMapSetup.SerializeCurrentMap()); } Notify(success ? "Map Setup Saved" : "Map Setup", message, (NotificationType)((!success) ? 1 : 3)); } private bool CanEditInGameMapSetup() { if (!NetworkInfo.IsHost) { Notify("Map Setup", "Only the Fusion host can edit map setup points.", (NotificationType)1); return false; } if (((Gamemode)this).IsStarted) { Notify("Map Setup", "Stop the round before editing spawn locations.", (NotificationType)1); return false; } if (!_inGameSetupEditing) { BeginInGameMapSetupEditing(); } return true; } private void BeginInGameMapSetupEditing() { if (!NetworkInfo.IsHost) { Notify("Map Setup", "Only the Fusion host can edit map setup points.", (NotificationType)1); return; } if (((Gamemode)this).IsStarted) { Notify("Map Setup", "Stop the current round before entering setup mode.", (NotificationType)1); return; } _inGameSetupEditing = true; if (GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } Notify("Map Setup Mode", "Automatic round start is paused. Walk to each location and place its marker.", (NotificationType)3); } private void FinishInGameMapSetupEditing() { if (!NetworkInfo.IsHost) { Notify("Map Setup", "Only the Fusion host can finish map setup.", (NotificationType)1); return; } _inGameSetupEditing = false; GamemodeManager.ValidateReadyConditions(); Notify("Map Setup Finished", "The saved layout is active. Fusion may now start the round countdown.", (NotificationType)3); } private void StopKillerModeManually() { if (!NetworkInfo.IsHost) { Notify("Killer Mode", "Only the Fusion host can stop the gamemode.", (NotificationType)1); return; } GamemodeRoundManager.ActiveRotation = null; _inGameSetupEditing = false; Notify("Killer Mode Stopped", "Automatic rounds were disabled. Select Killer Mode again when you want to restart.", (NotificationType)3); GamemodeManager.DeselectGamemode(); } private void StartRoundNow() { if (!NetworkInfo.IsHost) { Notify("Host Controls", "Only the Fusion host can start a round.", (NotificationType)1); return; } if (GamemodeManager.IsGamemodeStarted) { Notify("Round Already Running", "Finish the current round before starting another one.", (NotificationType)1); return; } bool automaticRoundsPaused = _automaticRoundsPaused; bool pauseAutomaticRoundsAfterCurrentRound = _pauseAutomaticRoundsAfterCurrentRound; _automaticRoundsPaused = false; _pauseAutomaticRoundsAfterCurrentRound = false; if (!((Gamemode)this).CheckReadyConditions()) { _automaticRoundsPaused = automaticRoundsPaused; _pauseAutomaticRoundsAfterCurrentRound = pauseAutomaticRoundsAfterCurrentRound; GamemodeManager.ValidateReadyConditions(); Notify("Round Cannot Start", "Check minimum players, map spawns, required content, and map setup mode.", (NotificationType)1, 7f); } else { KillerModePreferences.Save(Settings); GamemodeManager.StartGamemode(); Notify("Starting Round", "The host started Killer Mode now.", (NotificationType)3); } } private void PauseAutomaticRounds() { if (!NetworkInfo.IsHost) { Notify("Host Controls", "Only the Fusion host can pause automatic rounds.", (NotificationType)1); return; } if (GamemodeManager.IsGamemodeStarted) { _pauseAutomaticRoundsAfterCurrentRound = true; Notify("Pause Scheduled", "The current round will finish normally, then automatic starting will pause.", (NotificationType)3); return; } _pauseAutomaticRoundsAfterCurrentRound = false; _automaticRoundsPaused = true; if (GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } Notify("Automatic Rounds Paused", "No round will start until the host resumes or presses Start Round Now.", (NotificationType)3); } private void ResumeAutomaticRounds() { if (!NetworkInfo.IsHost) { Notify("Host Controls", "Only the Fusion host can resume automatic rounds.", (NotificationType)1); return; } _pauseAutomaticRoundsAfterCurrentRound = false; _automaticRoundsPaused = false; GamemodeManager.ValidateReadyConditions(); Notify("Automatic Rounds Enabled", "Fusion's normal between-round countdown will start when requirements are met.", (NotificationType)3); } } public sealed class SoloTestGamemode : KillerModeGamemode { public override string Title => "Killer Mode - Solo Test"; public override string Barcode => "FusionKillerMode.Gamemode.SoloTest"; public override string Description => "One-player test mode. Choose Killer or Survivor; Killer receives the built-in Combat Knife."; public override bool ManualReady => true; protected override bool IsSoloTest => true; protected override bool SavePreferencesOnStart => false; protected override KillerPlayerState SoloPlayerState { get { if (TestRole != SoloTestRole.Killer) { return KillerPlayerState.SurvivorAlive; } return KillerPlayerState.Killer; } } public SoloTestRole TestRole { get; private set; } protected override string ResolveKillerWeaponBarcode() { string text = Settings.CustomKillerWeaponBarcode?.Trim() ?? string.Empty; if (Settings.WeaponChoice != KillerWeaponChoice.CustomBarcode || !KillerContentReferences.IsInstalled(text)) { return "c1534c5a-1fb8-477c-afbe-2a95436f6d62"; } return text; } public SoloTestGamemode() { Settings.MinimumPlayers = 1; Settings.RoundSeconds = 600; Settings.HeadStartSeconds = 0; Settings.MapModeOverride = KillerMapModeOverride.Survival; Settings.SpawnSurvivorFlashlights = false; } public override bool CheckReadyConditions() { return false; } public override GroupElementData CreateSettingsGroup() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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) //IL_00aa: Expected O, but got Unknown GroupElementData obj = base.CreateSettingsGroup(); GroupElementData val = new GroupElementData("Solo test"); val.AddElement(new EnumElementData { Title = "Play As", EnumType = typeof(SoloTestRole), Value = TestRole, OnValueChanged = delegate(Enum value) { TestRole = (SoloTestRole)(object)value; } }); val.AddElement(new FunctionElementData { Title = "START SOLO ROUND", OnPressed = StartSoloRound }); val.AddElement(new FunctionElementData { Title = "STOP SOLO ROUND", OnPressed = StopSoloRound }); obj.AddElement(val); return obj; } public override void OnGamemodeSelected() { base.OnGamemodeSelected(); if (NetworkInfo.IsHost && GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } } public override void OnGamemodeStopped() { base.OnGamemodeStopped(); if (NetworkInfo.IsHost && GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } } private void StartSoloRound() { if (!NetworkInfo.IsHost) { KillerModeGamemode.Notify("Solo Test", "Only the host can start the solo round.", (NotificationType)1); } else if (((Gamemode)this).IsStarted) { KillerModeGamemode.Notify("Solo Test", "The solo round is already running.", (NotificationType)1); } else if (FusionSceneManager.IsLoading() || string.IsNullOrWhiteSpace(FusionSceneManager.Barcode) || !RigData.HasPlayer) { KillerModeGamemode.Notify("Solo Test", "Wait until the current level and your player rig finish loading.", (NotificationType)1); } else if (PlayerIDManager.PlayerCount < 1) { KillerModeGamemode.Notify("Solo Test", "Start or join a Fusion lobby first, then press Start Solo Round.", (NotificationType)1); } else { GamemodeManager.StartGamemode(); } } private void StopSoloRound() { if (!NetworkInfo.IsHost) { KillerModeGamemode.Notify("Solo Test", "Only the host can stop the solo round.", (NotificationType)1); } else if (((Gamemode)this).IsStarted) { GamemodeManager.StopGamemode(); } else if (GamemodeManager.IsGamemodeReady) { GamemodeManager.UnreadyGamemode(); } else { KillerModeGamemode.Notify("Solo Test", "No solo round is running.", (NotificationType)0); } } } public sealed class KillerModeMod : MelonMod { public const string Name = "BONELAB Fusion Killer Mode"; public const string Version = "0.3.11"; public const string Author = "Fusion Killer Mode Team"; public override void OnInitializeMelon() { KillerModePreferences.Initialize(); KillerInGameMapSetup.Initialize(); ModuleManager.LoadModules(typeof(KillerModeMod).Assembly); ((MelonBase)this).LoggerInstance.Msg("Loaded Killer Mode module and map runtime."); } } public sealed class KillerModeModule : Module { public override string Name => "BONELAB Fusion Killer Mode"; public override string Author => "Fusion Killer Mode Team"; public override Version Version => new Version(0, 3, 10); public override ConsoleColor Color => ConsoleColor.DarkRed; protected override void OnModuleRegistered() { GamemodeRegistration.RegisterGamemode(); ((Module)this).LoggerInstance.Log("Registered the Killer Mode gamemode.", ConsoleColor.White); } } public enum InGameSetupPoint { Lobby, Killer, Survivor, Spectator, Escape } public sealed class SavedMapPose { public float X { get; set; } public float Y { get; set; } public float Z { get; set; } public float Yaw { get; set; } } public sealed class KillerInGameMapProfile { public SavedMapPose Lobby { get; set; } public SavedMapPose Killer { get; set; } public List Survivors { get; set; } = new List(); public SavedMapPose Spectator { get; set; } public SavedMapPose Escape { get; set; } } public static class KillerInGameMapSetup { private static MelonPreferences_Category _category; private static MelonPreferences_Entry _profilesJson; private static Dictionary _profiles = new Dictionary(); private static readonly List RuntimeObjects = new List(); private static readonly List VisualObjects = new List(); private static bool _showVisuals = true; public static string CurrentMapKey { get { if (!string.IsNullOrWhiteSpace(FusionSceneManager.Barcode)) { return FusionSceneManager.Barcode; } return string.Empty; } } public static void Initialize() { _category = MelonPreferences.CreateCategory("BONELAB Fusion Killer Mode Map Setups"); _profilesJson = _category.CreateEntry("Profiles", string.Empty, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); if (string.IsNullOrWhiteSpace(_profilesJson.Value)) { return; } try { _profiles = JsonSerializer.Deserialize>(_profilesJson.Value) ?? new Dictionary(); } catch (Exception ex) { MelonLogger.Warning("[Killer Mode] Could not load in-game map setups: " + ex.Message); _profiles = new Dictionary(); } } public static string LoadCurrentMap(bool showVisuals) { string currentMapKey = CurrentMapKey; KillerInGameMapProfile value; KillerInGameMapProfile obj = ((!string.IsNullOrWhiteSpace(currentMapKey) && _profiles.TryGetValue(currentMapKey, out value)) ? value : new KillerInGameMapProfile()); ApplyProfile(obj, showVisuals); return JsonSerializer.Serialize(obj); } public static void ApplySerialized(string json, bool showVisuals) { try { ApplyProfile(string.IsNullOrWhiteSpace(json) ? new KillerInGameMapProfile() : (JsonSerializer.Deserialize(json) ?? new KillerInGameMapProfile()), showVisuals); } catch (Exception ex) { MelonLogger.Warning("[Killer Mode] Could not apply synchronized map setup: " + ex.Message); } } public static bool PlaceAtPlayer(InGameSetupPoint point, out string message) { string currentMapKey = CurrentMapKey; if (string.IsNullOrWhiteSpace(currentMapKey)) { message = "The current map is still loading."; return false; } if (!TryCapturePose(out var pose)) { message = "The player rig is not ready. Close the menu, wait a moment, and try again."; return false; } if (!_profiles.TryGetValue(currentMapKey, out var value)) { value = new KillerInGameMapProfile(); _profiles[currentMapKey] = value; } switch (point) { case InGameSetupPoint.Lobby: value.Lobby = pose; message = "Lobby spawn placed at your feet."; break; case InGameSetupPoint.Killer: value.Killer = pose; message = "Killer spawn placed at your feet."; break; case InGameSetupPoint.Survivor: value.Survivors.Add(pose); message = $"Survivor spawn {value.Survivors.Count} added at your feet."; break; case InGameSetupPoint.Spectator: value.Spectator = pose; message = "Spectator spawn placed at your feet."; break; case InGameSetupPoint.Escape: value.Escape = pose; message = "Escape door created ahead of you, with the escape zone safely behind it."; break; default: message = "Unknown setup point."; return false; } Save(); ApplyProfile(value, _showVisuals); return true; } public static bool RemoveLastSurvivor(out string message) { if (!TryGetCurrentProfile(out var profile) || profile.Survivors.Count == 0) { message = "There are no saved Survivor spawns to remove."; return false; } profile.Survivors.RemoveAt(profile.Survivors.Count - 1); Save(); ApplyProfile(profile, _showVisuals); message = $"Removed the most recently added Survivor spawn. {profile.Survivors.Count} remain."; return true; } public static bool RemoveEscape(out string message) { if (!TryGetCurrentProfile(out var profile) || profile.Escape == null) { message = "This map has no saved Escape Zone."; return false; } profile.Escape = null; Save(); ApplyProfile(profile, _showVisuals); message = "Removed the saved Escape Zone. The map will use Survival rules."; return true; } public static bool ClearCurrentMap(out string message) { string currentMapKey = CurrentMapKey; if (string.IsNullOrWhiteSpace(currentMapKey) || !_profiles.Remove(currentMapKey)) { message = "This map has no saved in-game setup."; return false; } Save(); ApplyProfile(new KillerInGameMapProfile(), _showVisuals); message = "Cleared every saved setup point for this map."; return true; } public static string SerializeCurrentMap() { if (!TryGetCurrentProfile(out var profile)) { return JsonSerializer.Serialize(new KillerInGameMapProfile()); } return JsonSerializer.Serialize(profile); } public static void SetVisuals(bool visible) { _showVisuals = visible; foreach (GameObject visualObject in VisualObjects) { if ((Object)(object)visualObject != (Object)null) { visualObject.SetActive(visible); } } } public static void ClearRuntimeObjects() { foreach (GameObject runtimeObject in RuntimeObjects) { if (!((Object)(object)runtimeObject == (Object)null)) { runtimeObject.SetActive(false); Object.Destroy((Object)(object)runtimeObject); } } RuntimeObjects.Clear(); VisualObjects.Clear(); } private static bool TryGetCurrentProfile(out KillerInGameMapProfile profile) { string currentMapKey = CurrentMapKey; return _profiles.TryGetValue(currentMapKey, out profile); } private static bool TryCapturePose(out SavedMapPose pose) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) pose = null; if (RigData.HasPlayer) { RigRefs refs = RigData.Refs; object obj; if (refs == null) { obj = null; } else { RigManager rigManager = refs.RigManager; if (rigManager == null) { obj = null; } else { PhysicsRig physicsRig = rigManager.physicsRig; obj = ((physicsRig != null) ? physicsRig.feet : null); } } if (!((Object)obj == (Object)null)) { Vector3 position = RigData.Refs.RigManager.physicsRig.feet.transform.position; Vector3 val = (((Object)(object)RigData.Refs.Head != (Object)null) ? RigData.Refs.Head.forward : ((Component)RigData.Refs.RigManager).transform.forward); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); float y = ((Quaternion)(ref val2)).eulerAngles.y; pose = new SavedMapPose { X = position.x, Y = position.y, Z = position.z, Yaw = y }; return true; } } return false; } private static void Save() { _profilesJson.Value = JsonSerializer.Serialize(_profiles); _category.SaveToFile(false); } private static void ApplyProfile(KillerInGameMapProfile profile, bool showVisuals) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) ClearRuntimeObjects(); _showVisuals = showVisuals; CreateSpawn(profile.Lobby, KillerSpawnRole.Lobby, Color.white, "Saved Lobby Spawn"); CreateSpawn(profile.Killer, KillerSpawnRole.Killer, new Color(1f, 0.05f, 0.02f), "Saved Killer Spawn"); foreach (SavedMapPose item in profile.Survivors ?? new List()) { CreateSpawn(item, KillerSpawnRole.Survivor, Color.cyan, "Saved Survivor Spawn"); } CreateSpawn(profile.Spectator, KillerSpawnRole.Spectator, Color.gray, "Saved Spectator Spawn"); CreateEscape(profile.Escape); } private static void CreateSpawn(SavedMapPose pose, KillerSpawnRole role, Color color, string name) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (pose != null) { GameObject obj = CreateRuntimeObject(name, pose); obj.AddComponent().Role = role; CreateCircle(obj.transform, color, 0.8f); } } private static void CreateEscape(SavedMapPose pose) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (pose != null) { GameObject val = CreateRuntimeObject("Saved Escape Zone", pose); BoxCollider obj = val.AddComponent(); ((Collider)obj).isTrigger = true; obj.center = new Vector3(0f, 1.5f, 3.5f); obj.size = new Vector3(4f, 3f, 4f); val.AddComponent().ValidationRadius = 2f; CreateEscapeDoor(val.transform); GameObject val2 = new GameObject("Escape Zone Preview Anchor") { hideFlags = (HideFlags)52 }; val2.transform.SetParent(val.transform, false); val2.transform.localPosition = Vector3.forward * 3.5f; CreateCircle(val2.transform, Color.green, 2f); } } private static void CreateEscapeDoor(Transform parent) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val).name = "Saved Brown Exit Door"; ((Object)val).hideFlags = (HideFlags)52; val.transform.SetParent(parent, false); val.transform.localPosition = new Vector3(0f, 1.4f, 1.5f); val.transform.localScale = new Vector3(3f, 2.8f, 0.35f); BoxCollider obj = val.GetComponent() ?? val.AddComponent(); ((Collider)obj).enabled = true; ((Collider)obj).isTrigger = false; SetBrownMaterial(val.GetComponent()); CreateDoorFramePiece(parent, "Left Door Frame", new Vector3(-1.75f, 1.55f, 1.5f), new Vector3(0.35f, 3.1f, 0.55f)); CreateDoorFramePiece(parent, "Right Door Frame", new Vector3(1.75f, 1.55f, 1.5f), new Vector3(0.35f, 3.1f, 0.55f)); CreateDoorFramePiece(parent, "Top Door Frame", new Vector3(0f, 3f, 1.5f), new Vector3(3.85f, 0.35f, 0.55f)); KillerDoor killerDoor = ((Component)parent).gameObject.AddComponent(); killerDoor.Rule = KillerDoorRule.LockedUntilExitOpen; killerDoor.LockBlocker = val; killerDoor.AutomaticVisuals = true; killerDoor.Animation = KillerDoorAnimation.SlideUp; killerDoor.AnimateTransitions = true; killerDoor.AnimationSeconds = 1.25f; killerDoor.OpenOffset = new Vector3(0f, 3.4f, 0f); killerDoor.Apply(KillerRoundState.Lobby); } private static void CreateDoorFramePiece(Transform parent, string name, Vector3 localPosition, Vector3 localScale) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = name; ((Object)obj).hideFlags = (HideFlags)52; obj.transform.SetParent(parent, false); obj.transform.localPosition = localPosition; obj.transform.localScale = localScale; SetBrownMaterial(obj.GetComponent()); } private static void SetBrownMaterial(Renderer renderer) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown if (!((Object)(object)renderer == (Object)null)) { Shader val = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); if ((Object)(object)val != (Object)null) { renderer.material = new Material(val) { color = new Color(0.32f, 0.12f, 0.035f, 1f) }; } else { renderer.material.color = new Color(0.32f, 0.12f, 0.035f, 1f); } } } private static GameObject CreateRuntimeObject(string name, SavedMapPose pose) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name) { hideFlags = (HideFlags)52 }; val.transform.SetPositionAndRotation(new Vector3(pose.X, pose.Y, pose.Z), Quaternion.Euler(0f, pose.Yaw, 0f)); RuntimeObjects.Add(val); return val; } private static void CreateCircle(Transform parent, Color color, float radius) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0026: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Setup Circle") { hideFlags = (HideFlags)52 }; val.transform.SetParent(parent, false); val.transform.localPosition = Vector3.up * 0.035f; LineRenderer val2 = val.AddComponent(); val2.useWorldSpace = false; val2.loop = true; val2.positionCount = 48; val2.startWidth = 0.055f; val2.endWidth = 0.055f; val2.startColor = color; val2.endColor = color; Shader val3 = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard"); if ((Object)(object)val3 != (Object)null) { ((Renderer)val2).material = new Material(val3) { color = color }; } for (int i = 0; i < val2.positionCount; i++) { float num = (float)i * (float)Math.PI * 2f / (float)val2.positionCount; val2.SetPosition(i, new Vector3(Mathf.Cos(num) * radius, 0f, Mathf.Sin(num) * radius)); } val.SetActive(_showVisuals); VisualObjects.Add(val); } } [RegisterTypeInIl2Cpp] public sealed class KillerMapConfig : MonoBehaviour { public const int CurrentSdkVersion = 3; public int SdkCompatibilityVersion = 3; public string DisplayName = "Killer Mode Map"; public string MapAuthor = "Unknown"; public KillerMapModeOverride ModeOverride; public int ExitOpenTimeRemaining = 60; public int RecommendedPlayersMin = 2; public int RecommendedPlayersMax = 8; public int RecommendedRoundSeconds = 600; public bool RecommendDarkMap; public bool RecommendDarkFog; public string[] RequiredSpawnableBarcodes = Array.Empty(); public string[] OptionalSpawnableBarcodes = Array.Empty(); public KillerMapConfig(IntPtr ptr) : base(ptr) { } private void Awake() { KillerMapRegistry.Configs.Add(this); } private void OnDestroy() { KillerMapRegistry.Configs.Remove(this); } } [RegisterTypeInIl2Cpp] public sealed class KillerSpawnMarker : MonoBehaviour { public KillerSpawnRole Role = KillerSpawnRole.Survivor; public KillerSpawnMarker(IntPtr ptr) : base(ptr) { } private void Awake() { KillerMapRegistry.Spawns.Add(this); } private void OnDestroy() { KillerMapRegistry.Spawns.Remove(this); } } [RegisterTypeInIl2Cpp] public sealed class KillerEscapeZone : MonoBehaviour { public float ValidationRadius = 4f; public KillerEscapeZone(IntPtr ptr) : base(ptr) { } private void Awake() { KillerMapRegistry.EscapeZones.Add(this); } private void OnDestroy() { KillerMapRegistry.EscapeZones.Remove(this); } [HideFromIl2Cpp] public bool Contains(Vector3 position) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Collider component = ((Component)this).GetComponent(); if (!((Object)(object)component != (Object)null)) { return Vector3.Distance(((Component)this).transform.position, position) <= ValidationRadius; } Bounds bounds = component.bounds; return ((Bounds)(ref bounds)).Contains(position); } } [RegisterTypeInIl2Cpp] public class KillerItemSpawnPoint : MonoBehaviour { public string SpawnableBarcode = ""; public KillerItemAudience Audience; public bool SpawnAtRoundStart = true; public bool RespawnEnabled; public float RespawnCooldown = 45f; public int MaxActive = 1; public int MaxUsesPerRound = 1; public bool DestroyOnRoundEnd = true; public bool RequiredContent; public KillerItemSpawnPoint(IntPtr ptr) : base(ptr) { } protected void Awake() { KillerMapRegistry.ItemSpawns.Add(this); } protected void OnDestroy() { KillerMapRegistry.ItemSpawns.Remove(this); } } [RegisterTypeInIl2Cpp] public sealed class KillerFlashlightSpawner : KillerItemSpawnPoint { public bool UseHostBarcode = true; public KillerFlashlightSpawner(IntPtr ptr) : base(ptr) { SpawnableBarcode = "Blueberrycat.BONEWORKSFlashlight.Spawnable.FlashlightBoneworks"; Audience = KillerItemAudience.SurvivorsOnly; } } [RegisterTypeInIl2Cpp] public sealed class KillerFlashbangSpawner : KillerItemSpawnPoint { public bool UseHostBarcode = true; public KillerFlashbangSpawner(IntPtr ptr) : base(ptr) { Audience = KillerItemAudience.SurvivorsOnly; RespawnEnabled = true; RespawnCooldown = 45f; MaxUsesPerRound = 6; } } [RegisterTypeInIl2Cpp] public sealed class KillerWeaponSpawn : KillerItemSpawnPoint { public KillerWeaponSpawn(IntPtr ptr) : base(ptr) { Audience = KillerItemAudience.KillerOnly; SpawnAtRoundStart = false; RespawnEnabled = false; RequiredContent = true; } } [RegisterTypeInIl2Cpp] public class KillerDoor : MonoBehaviour { public KillerDoorRule Rule; public GameObject LockBlocker; public GameObject OpenVisual; public bool AutomaticVisuals = true; public KillerDoorAnimation Animation = KillerDoorAnimation.SlideUp; public bool AnimateTransitions = true; public float AnimationSeconds = 1.25f; public Vector3 OpenOffset = new Vector3(0f, 3f, 0f); public Vector3 OpenEuler = new Vector3(0f, 95f, 0f); private GameObject _runtimeOpenVisual; private bool _poseCaptured; private Vector3 _closedLocalPosition; private Quaternion _closedLocalRotation; private float _animationValue; private float _animationTarget; public KillerDoor(IntPtr ptr) : base(ptr) { }//IL_0030: 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) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) protected void Awake() { KillerMapRegistry.Doors.Add(this); EnsureVisuals(); } protected void OnDestroy() { KillerMapRegistry.Doors.Remove(this); if ((Object)(object)_runtimeOpenVisual != (Object)null) { Object.Destroy((Object)(object)_runtimeOpenVisual); } } public void Apply(KillerRoundState state) { bool flag = Rule switch { KillerDoorRule.LockedUntilHunt => state < KillerRoundState.Hunt, KillerDoorRule.LockedUntilExitOpen => state < KillerRoundState.ExitOpen, _ => false, }; EnsureVisuals(); CaptureClosedPose(); _animationTarget = (flag ? 0f : 1f); if (!AnimateTransitions || Animation == KillerDoorAnimation.None) { _animationValue = _animationTarget; ApplyAnimatedPose(); } else if ((Object)(object)LockBlocker != (Object)null && flag) { LockBlocker.SetActive(true); } if ((Object)(object)OpenVisual != (Object)null) { OpenVisual.SetActive(!flag && Rule == KillerDoorRule.LockedUntilExitOpen); } } private void Update() { if (_poseCaptured && !((Object)(object)LockBlocker == (Object)null) && !Mathf.Approximately(_animationValue, _animationTarget)) { if (_animationTarget > _animationValue) { LockBlocker.SetActive(true); } _animationValue = Mathf.MoveTowards(_animationValue, _animationTarget, Time.deltaTime / Mathf.Max(0.05f, AnimationSeconds)); ApplyAnimatedPose(); } } private void CaptureClosedPose() { //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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!_poseCaptured && !((Object)(object)LockBlocker == (Object)null)) { _poseCaptured = true; _closedLocalPosition = LockBlocker.transform.localPosition; _closedLocalRotation = LockBlocker.transform.localRotation; _animationValue = (LockBlocker.activeSelf ? 0f : 1f); } } private void ApplyAnimatedPose() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)LockBlocker == (Object)null)) { LockBlocker.transform.localPosition = _closedLocalPosition + ((Animation == KillerDoorAnimation.SlideUp) ? (OpenOffset * _animationValue) : Vector3.zero); LockBlocker.transform.localRotation = _closedLocalRotation * Quaternion.Euler((Animation == KillerDoorAnimation.Swing) ? (OpenEuler * _animationValue) : Vector3.zero); LockBlocker.SetActive(_animationValue < 0.999f); } } private void EnsureVisuals() { //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Expected O, but got Unknown if (!AutomaticVisuals || Rule != KillerDoorRule.LockedUntilExitOpen) { return; } if ((Object)(object)LockBlocker != (Object)null) { foreach (Renderer componentsInChild in LockBlocker.GetComponentsInChildren(true)) { componentsInChild.enabled = true; if ((Object)(object)componentsInChild.material != (Object)null) { componentsInChild.material.color = new Color(0.32f, 0.12f, 0.035f, 1f); } } } if (!((Object)(object)OpenVisual != (Object)null)) { GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val).name = "Killer Exit White Light"; ((Object)val).hideFlags = (HideFlags)52; Collider component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } if ((Object)(object)LockBlocker != (Object)null) { val.transform.SetPositionAndRotation(LockBlocker.transform.position + LockBlocker.transform.forward * 0.08f, LockBlocker.transform.rotation); Vector3 lossyScale = LockBlocker.transform.lossyScale; val.transform.localScale = new Vector3(lossyScale.x * 0.9f, lossyScale.y * 0.9f, 0.05f); val.transform.SetParent(((Component)this).transform, true); } else { val.transform.SetParent(((Component)this).transform, false); val.transform.localPosition = Vector3.up * 1.25f; val.transform.localScale = new Vector3(2.7f, 2.25f, 0.05f); } Renderer component2 = val.GetComponent(); Shader val2 = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); if ((Object)(object)component2 != (Object)null && (Object)(object)val2 != (Object)null) { component2.material = new Material(val2) { color = Color.white }; } GameObject val3 = new GameObject("Exit Glow"); val3.transform.SetParent(val.transform, false); val3.transform.localPosition = new Vector3(0f, 0f, -0.25f); Light obj = val3.AddComponent(); obj.type = (LightType)2; obj.color = Color.white; obj.intensity = 3.2f; obj.range = 6f; val.SetActive(false); OpenVisual = val; _runtimeOpenVisual = val; } } } [RegisterTypeInIl2Cpp] public sealed class KillerExitDoor : KillerDoor { public KillerExitDoor(IntPtr ptr) : base(ptr) { Rule = KillerDoorRule.LockedUntilExitOpen; } } [RegisterTypeInIl2Cpp] public sealed class KillerSpectatorCamera : MonoBehaviour { public string DisplayName = "Camera"; public KillerSpectatorCamera(IntPtr ptr) : base(ptr) { } private void Awake() { KillerMapRegistry.SpectatorCameras.Add(this); } private void OnDestroy() { KillerMapRegistry.SpectatorCameras.Remove(this); } } [RegisterTypeInIl2Cpp] public sealed class KillerMapEvents : MonoBehaviour { public UnityEvent RoundStarting; public UnityEvent KillerSelected; public UnityEvent SurvivorsReleased; public UnityEvent KillerReleased; public UnityEvent LastSurvivor; public UnityEvent ExitOpen; public UnityEvent SurvivorEscaped; public UnityEvent SurvivorEliminated; public UnityEvent KillerWins; public UnityEvent SurvivorsWin; public UnityEvent TimeExpired; public UnityEvent RoundOver; public KillerMapEvents(IntPtr ptr) : base(ptr) { } private void Awake() { KillerMapRegistry.Events.Add(this); } private void OnDestroy() { KillerMapRegistry.Events.Remove(this); } public void InvokeRoundStarting() { UnityEvent roundStarting = RoundStarting; if (roundStarting != null) { roundStarting.Invoke(); } } public void InvokeKillerSelected() { UnityEvent killerSelected = KillerSelected; if (killerSelected != null) { killerSelected.Invoke(); } } public void InvokeSurvivorsReleased() { UnityEvent survivorsReleased = SurvivorsReleased; if (survivorsReleased != null) { survivorsReleased.Invoke(); } } public void InvokeKillerReleased() { UnityEvent killerReleased = KillerReleased; if (killerReleased != null) { killerReleased.Invoke(); } } public void InvokeLastSurvivor() { UnityEvent lastSurvivor = LastSurvivor; if (lastSurvivor != null) { lastSurvivor.Invoke(); } } public void InvokeExitOpen() { UnityEvent exitOpen = ExitOpen; if (exitOpen != null) { exitOpen.Invoke(); } } public void InvokeSurvivorEscaped() { UnityEvent survivorEscaped = SurvivorEscaped; if (survivorEscaped != null) { survivorEscaped.Invoke(); } } public void InvokeSurvivorEliminated() { UnityEvent survivorEliminated = SurvivorEliminated; if (survivorEliminated != null) { survivorEliminated.Invoke(); } } public void InvokeKillerWins() { UnityEvent killerWins = KillerWins; if (killerWins != null) { killerWins.Invoke(); } } public void InvokeSurvivorsWin() { UnityEvent survivorsWin = SurvivorsWin; if (survivorsWin != null) { survivorsWin.Invoke(); } } public void InvokeTimeExpired() { UnityEvent timeExpired = TimeExpired; if (timeExpired != null) { timeExpired.Invoke(); } } public void InvokeRoundOver() { UnityEvent roundOver = RoundOver; if (roundOver != null) { roundOver.Invoke(); } } } [RegisterTypeInIl2Cpp] public sealed class KillerModeStart : MonoBehaviour { public bool HostOnly = true; public KillerModeStart(IntPtr ptr) : base(ptr) { } public void RequestStart() { if ((!HostOnly || NetworkInfo.IsHost) && !GamemodeManager.IsGamemodeStarted && GamemodeManager.ActiveGamemode is KillerModeGamemode killerModeGamemode) { if (!((Gamemode)killerModeGamemode).CheckReadyConditions()) { MelonLogger.Warning("[Killer Mode] Map start rejected: minimum player requirement is not met."); } else { GamemodeManager.StartGamemode(); } } } } public static class KillerMapRegistry { public static readonly HashSet Configs = new HashSet(); public static readonly HashSet Spawns = new HashSet(); public static readonly HashSet EscapeZones = new HashSet(); public static readonly HashSet ItemSpawns = new HashSet(); public static readonly HashSet Doors = new HashSet(); public static readonly HashSet Events = new HashSet(); public static readonly HashSet SpectatorCameras = new HashSet(); public static KillerMapConfig ActiveConfig => Configs.FirstOrDefault((KillerMapConfig c) => (Object)(object)c != (Object)null && ((Behaviour)c).isActiveAndEnabled); public static IEnumerable GetSpawns(KillerSpawnRole role) { return Spawns.Where((KillerSpawnMarker s) => (Object)(object)s != (Object)null && ((Behaviour)s).isActiveAndEnabled && s.Role == role); } public static KillerSpawnMarker GetSpawn(KillerSpawnRole role, byte playerId) { KillerSpawnMarker[] array = (from s in GetSpawns(role) orderby ((Object)s).name select s).ToArray(); if (array.Length != 0) { return array[playerId % array.Length]; } return null; } public static void InvokeEvents(Action invoke) { KillerMapEvents[] array = Events.ToArray(); foreach (KillerMapEvents killerMapEvents in array) { if ((Object)(object)killerMapEvents != (Object)null && ((Behaviour)killerMapEvents).isActiveAndEnabled) { invoke(killerMapEvents); } } } } public static class KillerMapValidator { public static IReadOnlyList Validate(KillerMapMode mode) { List list = new List(); bool flag = GamemodeMarker.FilterMarkers((BoneTagReference)null).Count > 0; if (!KillerMapRegistry.GetSpawns(KillerSpawnRole.Killer).Any() && !flag) { list.Add("Missing Killer spawn marker and no generic Fusion fallback exists."); } if (!KillerMapRegistry.GetSpawns(KillerSpawnRole.Survivor).Any() && !flag) { list.Add("Missing Survivor spawn marker and no generic Fusion fallback exists."); } if (!KillerMapRegistry.GetSpawns(KillerSpawnRole.Spectator).Any() && !flag) { list.Add("Missing Spectator spawn marker and no generic Fusion fallback exists."); } if (mode == KillerMapMode.Escape && !KillerMapRegistry.EscapeZones.Any()) { list.Add("Escape mode requires at least one Escape Zone."); } KillerMapConfig activeConfig = KillerMapRegistry.ActiveConfig; if ((Object)(object)activeConfig != (Object)null) { if (activeConfig.SdkCompatibilityVersion > 3) { list.Add($"Map requires Killer Mode Map SDK {activeConfig.SdkCompatibilityVersion}, but runtime supports {3}."); } string[] array = activeConfig.RequiredSpawnableBarcodes ?? Array.Empty(); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !KillerContentReferences.IsAnyCrateInstalled(text)) { list.Add("Missing required map content: " + text); } } } foreach (KillerItemSpawnPoint item in KillerMapRegistry.ItemSpawns.Where((KillerItemSpawnPoint point) => (Object)(object)point != (Object)null && point.RequiredContent)) { if (!string.IsNullOrWhiteSpace(item.SpawnableBarcode) && !KillerContentReferences.IsInstalled(item.SpawnableBarcode)) { list.Add("Missing required item content for " + ((Object)item).name + ": " + item.SpawnableBarcode); } } return list; } public static IReadOnlyList Warnings(KillerMapMode mode) { List list = new List(); bool flag = GamemodeMarker.FilterMarkers((BoneTagReference)null).Count > 0; if (!KillerMapRegistry.GetSpawns(KillerSpawnRole.Lobby).Any() && !flag) { list.Add("No Lobby spawn or generic Fusion fallback was found."); } if (!KillerMapRegistry.SpectatorCameras.Any()) { list.Add("No optional Spectator Camera markers were found."); } if (mode == KillerMapMode.Escape && !KillerMapRegistry.Doors.Any((KillerDoor door) => (Object)(object)door != (Object)null && door.Rule == KillerDoorRule.LockedUntilExitOpen)) { list.Add("Escape mode has no Killer Exit Door visual."); } KillerMapConfig activeConfig = KillerMapRegistry.ActiveConfig; if ((Object)(object)activeConfig != (Object)null) { string[] array = activeConfig.OptionalSpawnableBarcodes ?? Array.Empty(); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !KillerContentReferences.IsAnyCrateInstalled(text)) { list.Add("Optional map content is missing: " + text); } } } foreach (KillerItemSpawnPoint item in KillerMapRegistry.ItemSpawns.Where((KillerItemSpawnPoint point) => (Object)(object)point != (Object)null && !point.RequiredContent)) { if (!(item is KillerFlashlightSpawner { UseHostBarcode: not false }) && !(item is KillerFlashbangSpawner { UseHostBarcode: not false }) && !string.IsNullOrWhiteSpace(item.SpawnableBarcode) && !KillerContentReferences.IsInstalled(item.SpawnableBarcode)) { list.Add("Optional item is unavailable and will be skipped: " + item.SpawnableBarcode); } } return list; } } [HarmonyPatch(typeof(VoiceHelper), "OnVoiceDataReceived")] internal static class KillerModeVoicePatch { [HarmonyPrefix] private static bool Prefix(PlayerID player) { if (!GamemodeManager.IsGamemodeStarted || !(GamemodeManager.ActiveGamemode is KillerModeGamemode killerModeGamemode)) { return true; } return killerModeGamemode.CanLocalPlayerHearVoice(player); } }