using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using HarmonyLib; using JumpScaresMod; using MelonLoader; using Mimic.Actors; using Mimic.Audio; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(JumpScares), "SORPRESA", "1.9.1", "xaxiflopi", null)] [assembly: MelonGame("ReLUGames", "MIMESIS")] [assembly: AssemblyVersion("0.0.0.0")] namespace JumpScaresMod; public static class AkaliReplacer { private const float SCAN_INTERVAL = 1f; private const float DEFAULT_HEIGHT = 2.4f; private static readonly Dictionary _replaced = new Dictionary(); private static float _nextScan; public static void OnSceneLoad() { _replaced.Clear(); _nextScan = 0f; } public static void OnUpdate() { if (Time.time < _nextScan) { return; } _nextScan = Time.time + 1f; try { Scan(); } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] AkaliReplacer: " + ex.Message); } } private static void Scan() { ProtoActor[] array = Object.FindObjectsOfType(); HashSet hashSet = new HashSet(); ProtoActor[] array2 = array; foreach (ProtoActor val in array2) { if ((Object)(object)val == (Object)null || !IsMonkey(val)) { continue; } int instanceID = ((Object)val).GetInstanceID(); hashSet.Add(instanceID); if (_replaced.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { HideOriginalRenderers(val, value); continue; } GameObject akaliTemplate = ModelManager.AkaliTemplate; if ((Object)(object)akaliTemplate == (Object)null) { return; } _replaced[instanceID] = Replace(val, akaliTemplate); } if (_replaced.Count <= 0) { return; } List list = new List(); foreach (KeyValuePair item in _replaced) { if (!hashSet.Contains(item.Key)) { list.Add(item.Key); } } foreach (int item2 in list) { _replaced.Remove(item2); } } private static bool IsMonkey(ProtoActor actor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 try { if ((int)actor.ActorType != 2) { return false; } if (FeatureLogic.IsMonkeyMasterId(actor.monsterMasterID)) { return true; } string obj = actor.puppetName ?? string.Empty; string text = ((Object)((Component)actor).gameObject).name ?? string.Empty; return obj.IndexOf("Gorilla", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Gorilla", StringComparison.OrdinalIgnoreCase) >= 0; } catch { return false; } } private static GameObject Replace(ProtoActor actor, GameObject template) { //IL_0031: 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_0051: 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_006c: Unknown result type (might be due to invalid IL or missing references) float num = MeasureHeight(actor); GameObject val = Object.Instantiate(template); ((Object)val).name = "SORPRESA_AkaliSkin"; val.transform.SetParent(((Component)actor).transform, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one * FeatureLogic.AkaliLocalScale(num, ((Component)actor).transform.lossyScale.y); val.SetActive(true); HideOriginalRenderers(actor, val); MelonLogger.Msg("[SORPRESA] ¡Akali reemplaza al mono! " + $"(masterID={actor.monsterMasterID}, altura={num:F2} m)"); return val; } private static float MeasureHeight(ProtoActor actor) { //IL_000a: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_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) try { bool flag = false; Bounds bounds = default(Bounds); ((Bounds)(ref bounds))..ctor(((Component)actor).transform.position, Vector3.zero); Renderer[] componentsInChildren = ((Component)actor).GetComponentsInChildren(); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && IsBodyRenderer(val)) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } if (flag && ((Bounds)(ref bounds)).size.y > 0.5f && ((Bounds)(ref bounds)).size.y < 10f) { return ((Bounds)(ref bounds)).size.y; } } catch { } return 2.4f; } private static bool IsBodyRenderer(Renderer r) { if (!(r is SkinnedMeshRenderer)) { return r is MeshRenderer; } return true; } private static void HideOriginalRenderers(ProtoActor actor, GameObject akali) { Renderer[] componentsInChildren = ((Component)actor).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && IsBodyRenderer(val) && (!((Object)(object)akali != (Object)null) || !((Component)val).transform.IsChildOf(akali.transform))) { val.enabled = false; } } } } public static class ChaosCoordinator { private static bool _inDungeon = false; private static float _entrySeed = 0f; private static float _entryTime = -1f; private static object _voiceManager; private static bool _voiceManagerMissing; public static bool IsInDungeon => _inDungeon; public static float EntryTime => _entryTime; public static float EntrySeed => _entrySeed; public static int Generation { get; private set; } public static void OnEnterDungeon() { Generation++; _inDungeon = true; _entryTime = Time.time; _entrySeed = Mathf.Round(Time.unscaledTime / 3f) * 3f; _voiceManager = null; _voiceManagerMissing = false; MelonLogger.Msg($"[SORPRESA] ChaosCoordinator: dungeon seed = {_entrySeed} (gen {Generation})"); } public static void OnExitDungeon() { Generation++; _inDungeon = false; _entryTime = -1f; _voiceManager = null; _voiceManagerMissing = false; } public static bool IsGenerationCurrent(int generation) { if (_inDungeon) { return generation == Generation; } return false; } private static Component GetNetworkObjectComponent(ProtoActor actor) { Transform val = ((Component)actor).transform; while ((Object)(object)val != (Object)null) { Component component = ((Component)val).GetComponent("NetworkObject"); if ((Object)(object)component != (Object)null) { return component; } val = val.parent; } return null; } public static List GetSortedActors() { return (from a in Object.FindObjectsOfType() orderby GetNetworkId(a) select a).ToList(); } public static List GetSortedPlayers() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 List list = new List(); ProtoActor[] array = Object.FindObjectsOfType(); foreach (ProtoActor val in array) { try { if ((Object)(object)val != (Object)null && (int)val.ActorType == 1) { list.Add(val); } } catch { } } return list.OrderBy((ProtoActor a) => GetNetworkId(a)).ToList(); } private static int GetNetworkId(ProtoActor actor) { try { PropertyInfo property = ((object)actor).GetType().GetProperty("ActorID", BindingFlags.Instance | BindingFlags.Public); if (property != null) { return (int)property.GetValue(actor); } } catch { } try { PropertyInfo property2 = ((object)actor).GetType().GetProperty("NetworkObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (property2 != null) { object value = property2.GetValue(actor); if (value != null) { PropertyInfo property3 = value.GetType().GetProperty("ObjectId", BindingFlags.Instance | BindingFlags.Public); if (property3 != null) { return (int)property3.GetValue(value); } } } } catch { } try { Component networkObjectComponent = GetNetworkObjectComponent(actor); if ((Object)(object)networkObjectComponent != (Object)null) { PropertyInfo property4 = ((object)networkObjectComponent).GetType().GetProperty("ObjectId", BindingFlags.Instance | BindingFlags.Public); if (property4 != null) { return (int)property4.GetValue(networkObjectComponent); } } } catch { } return ((Object)actor).GetInstanceID(); } public static List PickActors(int count, int offsetSeed) { return DeterministicSelection.Pick(GetSortedPlayers(), GetNetworkId, count, offsetSeed); } public static bool IsLocalPlayer(ProtoActor actor) { try { MethodInfo method = ((object)actor).GetType().GetMethod("AmIAvatar", BindingFlags.Instance | BindingFlags.Public); if (method != null) { return (bool)method.Invoke(actor, null); } } catch { } try { PropertyInfo property = ((object)actor).GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (property != null) { return (bool)property.GetValue(actor); } } catch { } try { PropertyInfo property2 = ((object)actor).GetType().GetProperty("NetworkObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (property2 != null) { object value = property2.GetValue(actor); if (value != null) { PropertyInfo property3 = value.GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public); if (property3 != null) { return (bool)property3.GetValue(value); } } } } catch { } try { Component networkObjectComponent = GetNetworkObjectComponent(actor); if ((Object)(object)networkObjectComponent != (Object)null) { PropertyInfo property4 = ((object)networkObjectComponent).GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public); if (property4 != null) { return (bool)property4.GetValue(networkObjectComponent); } } } catch { } return false; } public static object GetVoiceManager() { if (_voiceManager != null) { return _voiceManager; } if (_voiceManagerMissing) { return null; } MonoBehaviour[] array = Object.FindObjectsOfType(); foreach (MonoBehaviour val in array) { if (((object)val).GetType().Name == "VoiceManager") { return _voiceManager = val; } } _voiceManagerMissing = true; return null; } public static T CallVoiceManager(string methodName, params object[] args) { try { object voiceManager = GetVoiceManager(); if (voiceManager == null) { return default(T); } MethodInfo method = voiceManager.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return default(T); } return (T)method.Invoke(voiceManager, args); } catch { return default(T); } } public static void CallVoiceManager(string methodName, params object[] args) { try { object voiceManager = GetVoiceManager(); voiceManager?.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(voiceManager, args); } catch { } } } [HarmonyPatch(typeof(VRoomManager), "EnterDungeon")] public static class ChaosEnterDungeonPatch { [HarmonyPostfix] public static void Postfix() { ChaosCoordinator.OnEnterDungeon(); int generation = ChaosCoordinator.Generation; MelonCoroutines.Start(VentriloquismFeature.RunOnEntry(generation)); MelonCoroutines.Start(GhostFeature.RunPeriodic(generation)); MelonCoroutines.Start(DeadVoiceFeature.RunPeriodic(generation)); MelonCoroutines.Start(DungeonStatueFeature.SpawnOnEntry(generation)); } } [HarmonyPatch(typeof(VRoomManager), "EnterWaitingRoom")] public static class ChaosExitDungeonPatch { [HarmonyPostfix] public static void Postfix() { ChaosCoordinator.OnExitDungeon(); GhostFeature.Reset(); DungeonStatueFeature.Clear(); } } public static class ChickenInverter { private const float WINDOW_SECONDS = 10f; private const int FALSE_ALARM_PERCENT = 20; private const float QUIET_GRACE_SECONDS = 45f; private static bool _isInDungeon = false; private static float _dungeonEntryTime = -1f; private static bool _lastLoggedActive = false; public static bool IsFalseAlarmActive { get { if (!_isInDungeon || _dungeonEntryTime < 0f) { return false; } float num = Time.time - _dungeonEntryTime; if (num < 45f) { return false; } return DeterministicSelection.IsFalseAlarmWindow((int)(num / 10f), 20); } } public static void OnEnterDungeon() { _isInDungeon = true; _dungeonEntryTime = Time.time; _lastLoggedActive = false; ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] Pollo mentiroso: nunca delata mimicos; falsas alarmas sincronizadas."); } public static void OnExitDungeon() { _isInDungeon = false; _dungeonEntryTime = -1f; } public static void OnUpdate() { if (_isInDungeon) { bool isFalseAlarmActive = IsFalseAlarmActive; if (isFalseAlarmActive != _lastLoggedActive) { _lastLoggedActive = isFalseAlarmActive; ((MelonBase)JumpScares.Instance).LoggerInstance.Msg(isFalseAlarmActive ? "[SORPRESA] Pollo: FALSA ALARMA (no hay nada de verdad)." : "[SORPRESA] Pollo: en silencio."); } } } } [HarmonyPatch(typeof(VRoomManager), "EnterDungeon")] public static class ChickenEnterDungeonPatch { [HarmonyPostfix] public static void Postfix() { ChickenInverter.OnEnterDungeon(); } } [HarmonyPatch(typeof(VRoomManager), "EnterWaitingRoom")] public static class ChickenExitDungeonPatch { [HarmonyPostfix] public static void Postfix() { ChickenInverter.OnExitDungeon(); } } [HarmonyPatch(typeof(Detector), "IsDetected")] public static class DetectorIsDetectedPatch { [HarmonyPostfix] public static void Postfix(ref bool __result) { if (ChaosCoordinator.IsInDungeon) { __result = ChickenInverter.IsFalseAlarmActive; } } } public static class DeadVoiceFeature { private static readonly List _deadArchives = new List(); public static void OnPlayerDied(object speechArchive) { if (speechArchive != null && !_deadArchives.Contains(speechArchive)) { _deadArchives.Add(speechArchive); } } public static void ClearDeadArchives() { _deadArchives.Clear(); } public static IEnumerator RunPeriodic(int generation) { _deadArchives.Clear(); if (ChaosCoordinator.GetVoiceManager() == null) { MelonLogger.Warning("[SORPRESA] VozMuerto: VoiceManager no encontrado. Feature omitida."); yield break; } yield return (object)new WaitForSeconds(45f); int eventIndex = 0; while (ChaosCoordinator.IsGenerationCurrent(generation)) { yield return (object)new WaitForSeconds(FeatureLogic.DeadVoiceDelaySeconds(eventIndex)); if (!ChaosCoordinator.IsGenerationCurrent(generation)) { break; } TryPlayDeadVoice(eventIndex); eventIndex++; } } private static void TryPlayDeadVoice(int eventIndex) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) object voiceManager = ChaosCoordinator.GetVoiceManager(); if (voiceManager == null) { return; } List sortedPlayers = ChaosCoordinator.GetSortedPlayers(); if (sortedPlayers.Count == 0) { return; } Vector3 position = ((Component)sortedPlayers[0]).transform.position; float num = FeatureLogic.DeadVoiceAngleRadians(eventIndex); float num2 = FeatureLogic.DeadVoiceDistanceMeters(eventIndex); Vector3 val = position + new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2); object deadArchive = GetDeadArchive(voiceManager, eventIndex); if (deadArchive == null) { return; } try { MethodInfo method = voiceManager.GetType().GetMethod("PlayVoiceOneShotAtPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(voiceManager, new object[2] { val, deadArchive }); MelonLogger.Msg($"[SORPRESA] Voz del muerto: reproducida @ {val}"); return; } MethodInfo method2 = voiceManager.GetType().GetMethod("PlayVoiceOnActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { method2.Invoke(voiceManager, new object[1] { deadArchive }); MelonLogger.Msg("[SORPRESA] Voz del muerto: reproducida via PlayVoiceOnActor"); } } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] DeadVoice: " + ex.Message); } } private static object GetDeadArchive(object vm, int eventIndex) { int num = FeatureLogic.DeadVoiceArchiveIndex(eventIndex, _deadArchives.Count); if (num >= 0) { return _deadArchives[num]; } try { return vm.GetType().GetMethod("GetRandomOtherSpeechEventArchive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(vm, null); } catch { return null; } } } [HarmonyPatch(typeof(VCreature), "OnDead")] public static class PlayerDeathVoicePatch { [HarmonyPostfix] public static void Postfix(VCreature __instance) { if (!(__instance is VPlayer) || !ChaosCoordinator.IsInDungeon) { return; } try { object voiceManager = ChaosCoordinator.GetVoiceManager(); if (voiceManager == null) { return; } MethodInfo method = voiceManager.GetType().GetMethod("GetAllSpeechEventArchives", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return; } object obj = method.Invoke(voiceManager, null); if (obj == null) { return; } foreach (object item in (IEnumerable)obj) { DeadVoiceFeature.OnPlayerDied(item); } MelonLogger.Msg("[SORPRESA] DeadVoice: voz del muerto registrada"); } catch { } } } [HarmonyPatch(typeof(VRoomManager), "EnterWaitingRoom")] public static class DeadVoiceClearPatch { [HarmonyPostfix] public static void Postfix() { DeadVoiceFeature.ClearDeadArchives(); } } public static class DeterministicSelection { public static List Pick(IList items, Func idOf, int count, int offsetSeed) { List list = items.OrderBy(idOf).ToList(); if (list.Count == 0 || count <= 0) { return new List(); } if (count > list.Count) { count = list.Count; } Random rng = new Random(ComputeSeed(list, idOf) + offsetSeed); return list.OrderBy((T _) => rng.Next()).Take(count).ToList(); } public static int ComputeSeed(IEnumerable sortedItems, Func idOf) { int num = 17; foreach (T sortedItem in sortedItems) { num = num * 31 + idOf(sortedItem); } return num; } public static bool IsFalseAlarmWindow(int windowIndex, int activePercent) { if (activePercent <= 0) { return false; } if (activePercent >= 100) { return true; } return FeatureLogic.HashU32(windowIndex) % 100 < (uint)activePercent; } } public static class DungeonStatueFeature { private const float SPAWN_DELAY = 10f; private const float DISTANCE_METERS = 2.5f; private const float STATUE_HEIGHT = 1.75f; private static GameObject _statue; public static IEnumerator SpawnOnEntry(int generation) { Vector3 pos = Vector3.zero; Quaternion rot = Quaternion.identity; bool anchored = false; float waited = 0f; RaycastHit val3 = default(RaycastHit); while (waited < 10f) { yield return (object)new WaitForSeconds(0.5f); waited += 0.5f; if (!ChaosCoordinator.IsGenerationCurrent(generation)) { yield break; } if (anchored) { continue; } List sortedPlayers = ChaosCoordinator.GetSortedPlayers(); if (FeatureLogic.ShouldCaptureStatueAnchor(sortedPlayers.Count, waited)) { ProtoActor val = sortedPlayers[0]; Vector3 val2 = ((Component)val).transform.forward; val2.y = 0f; val2 = ((((Vector3)(ref val2)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val2)).normalized : Vector3.forward); pos = ((Component)val).transform.position + val2 * 2.5f; if (Physics.Raycast(pos + Vector3.up * 1.5f, Vector3.down, ref val3, 8f, -5, (QueryTriggerInteraction)1)) { pos = ((RaycastHit)(ref val3)).point; } rot = Quaternion.LookRotation(-val2); anchored = true; MelonLogger.Msg($"[SORPRESA] Estatua: ancla capturada a los {waited:F1} s @ {pos}"); } } if (!ChaosCoordinator.IsGenerationCurrent(generation)) { yield break; } if (!anchored) { MelonLogger.Warning("[SORPRESA] Estatua: ningún jugador encontrado"); yield break; } Clear(); GameObject blondeTemplate = ModelManager.BlondeTemplate; if ((Object)(object)blondeTemplate == (Object)null) { MelonLogger.Warning("[SORPRESA] Estatua: modelo 'blonde' no cargado (¿falta Models\\blonde\\blonde.obj en la carpeta del mod?)"); yield break; } _statue = ModelManager.Spawn(blondeTemplate, pos, rot, 1.75f); if ((Object)(object)_statue != (Object)null) { MelonLogger.Msg($"[SORPRESA] Estatua colocada en la sala de entrada @ {pos}"); } } public static void Clear() { if ((Object)(object)_statue != (Object)null) { Object.Destroy((Object)(object)_statue); _statue = null; } } } public static class FeatureLogic { public static bool ShouldCaptureStatueAnchor(int playerCount, float waitedSeconds) { if (playerCount <= 0) { return false; } if (playerCount >= 2) { return true; } return waitedSeconds >= 5f; } public static bool CooldownAllows(float now, float lastTime, float cooldownSeconds) { return now - lastTime >= cooldownSeconds; } public static bool TowerInTopZone(float relY, float horizontalDist) { if (relY > 4.6800003f) { return horizontalDist < 2.5f; } return false; } public static bool TowerShouldRearm(float relY) { return relY < 3.25f; } public static bool IsMonkeyMasterId(int masterId) { int[] mONKEY_MASTER_IDS = FeatureTuning.MONKEY_MASTER_IDS; for (int i = 0; i < mONKEY_MASTER_IDS.Length; i++) { if (mONKEY_MASTER_IDS[i] == masterId) { return true; } } return false; } public static float AkaliLocalScale(float heightMeters, float parentScaleY) { return heightMeters / Math.Max(parentScaleY, 0.0001f); } public static float DeadVoiceDelaySeconds(int eventIndex) { float num = Hash01(eventIndex * 31 + 101); return 60f + num * 30f; } public static float DeadVoiceAngleRadians(int eventIndex) { return Hash01(eventIndex * 31 + 202) * 2f * (float)Math.PI; } public static float DeadVoiceDistanceMeters(int eventIndex) { return 3f + Hash01(eventIndex * 31 + 303) * 15f; } public static int DeadVoiceArchiveIndex(int eventIndex, int archiveCount) { if (archiveCount <= 0) { return -1; } return (int)(HashU32(eventIndex * 31 + 404) % (uint)archiveCount); } public static uint HashU32(int value) { int num = value * -1640531535; int num2 = (num ^ (num >>> 13)) * -2048144777; return (uint)(num2 ^ (num2 >>> 16)); } public static float Hash01(int value) { return (float)(HashU32(value) % 10000) / 10000f; } } public sealed class OncePerDungeonLatch { private bool _fired; public bool TryFire() { if (_fired) { return false; } _fired = true; return true; } public void Reset() { _fired = false; } } public static class FeatureTuning { public const float GHOST_FIRST_DELAY_SECONDS = 90f; public const float GHOST_PERIOD_SECONDS = 120f; public const float GHOST_DURATION_SECONDS = 40f; public const float GHOST_RESCAN_SECONDS = 0.25f; public const int GHOST_SEED = 2002; public const float VENTRILO_DELAY_SECONDS = 30f; public const float VENTRILO_DURATION_SECONDS = 30f; public const int VENTRILO_SEED = 1001; public const float CHICKEN_WINDOW_SECONDS = 10f; public const int CHICKEN_FALSE_ALARM_PERCENT = 20; public const float CHICKEN_QUIET_GRACE_SECONDS = 45f; public const float STATUE_SPAWN_DELAY_SECONDS = 10f; public const int STATUE_ANCHOR_MIN_PLAYERS = 2; public const float STATUE_ANCHOR_FALLBACK_SECONDS = 5f; public const float STATUE_DISTANCE_METERS = 2.5f; public const float STATUE_HEIGHT_METERS = 1.75f; public const float DEADVOICE_FIRST_DELAY_SECONDS = 45f; public const float DEADVOICE_MIN_INTERVAL_SECONDS = 60f; public const float DEADVOICE_MAX_INTERVAL_SECONDS = 90f; public const float DEADVOICE_MIN_DISTANCE_METERS = 3f; public const float DEADVOICE_SPREAD_METERS = 15f; public const float JUMPSCARE_COOLDOWN_SECONDS = 120f; public const float TOWER_HEIGHT_METERS = 6.5f; public const float TOWER_TOP_Y_FRACTION = 0.72f; public const float TOWER_REARM_Y_FRACTION = 0.5f; public const float TOWER_TOP_RADIUS_METERS = 2.5f; public static readonly int[] MONKEY_MASTER_IDS = new int[2] { 20000005, 99999997 }; public const float AKALI_DEFAULT_HEIGHT_METERS = 2.4f; public const float VOODOO_DANCE_SECONDS = 5f; public const float VOODOO_COOLDOWN_SECONDS = 2f; } public static class FirstLootFeature { private static readonly OncePerDungeonLatch _latch = new OncePerDungeonLatch(); public static void ResetForNewDungeon() { _latch.Reset(); } public static void OnLootGrabbed() { if (ChaosCoordinator.IsInDungeon && _latch.TryFire()) { MelonLogger.Msg("[SORPRESA] ¡Primer objeto de la dungeon cogido! Sonando para todos."); ModSounds.PlayTorre(); } } } [HarmonyPatch(typeof(ProtoActor), "OnPacket", new Type[] { typeof(AttachActorSig) })] public static class LootAttachedPatch { [HarmonyPostfix] public static void Postfix(ProtoActor __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 try { if ((int)__instance.ActorType == 5) { FirstLootFeature.OnLootGrabbed(); } } catch { } } } [HarmonyPatch(typeof(ProtoActor), "GrapLootingObject")] public static class LootGrabbedLocalPatch { [HarmonyPostfix] public static void Postfix() { FirstLootFeature.OnLootGrabbed(); } } [HarmonyPatch(typeof(VRoomManager), "EnterDungeon")] public static class FirstLootResetPatch { [HarmonyPostfix] public static void Postfix() { FirstLootFeature.ResetForNewDungeon(); } } public static class GhostFeature { private const float FIRST_DELAY_SECONDS = 90f; private const float PERIOD_SECONDS = 120f; private const float DURATION_SECONDS = 40f; private const float RESCAN_SECONDS = 0.25f; private const int OFFSET_SEED = 2002; private static bool _ghostActive; private static int _ghostCycle; public static void Reset() { _ghostActive = false; _ghostCycle = 0; } public static IEnumerator RunPeriodic(int generation) { _ghostActive = false; _ghostCycle = 0; yield return (object)new WaitForSeconds(90f); while (ChaosCoordinator.IsGenerationCurrent(generation)) { int cycleSeed = 2002 + _ghostCycle; _ghostCycle++; if (!_ghostActive) { MelonCoroutines.Start(MakeGhost(cycleSeed, generation)); } yield return (object)new WaitForSeconds(120f); } } private static IEnumerator MakeGhost(int cycleSeed, int generation) { if (_ghostActive || !ChaosCoordinator.IsGenerationCurrent(generation) || ChaosCoordinator.GetSortedPlayers().Count < 2) { yield break; } List list = ChaosCoordinator.PickActors(1, cycleSeed); if (list.Count == 0) { yield break; } ProtoActor ghost = list[0]; if (ChaosCoordinator.IsLocalPlayer(ghost)) { MelonLogger.Msg("[SORPRESA] ¡Tú eres el fantasma! (los otros no te ven)"); _ghostActive = true; yield return (object)new WaitForSeconds(40f); _ghostActive = false; yield break; } List hidden = new List(); MelonLogger.Msg($"[SORPRESA] Fantasma activo: {((Object)((Component)ghost).gameObject).name} invisible por {40f} s"); _ghostActive = true; for (float elapsed = 0f; elapsed < 40f; elapsed += 0.25f) { if (!ChaosCoordinator.IsGenerationCurrent(generation)) { break; } if (!((Object)(object)ghost != (Object)null)) { break; } Renderer[] componentsInChildren = ((Component)ghost).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null && val.enabled) { val.enabled = false; hidden.Add(val); } } yield return (object)new WaitForSeconds(0.25f); } foreach (Renderer item in hidden) { if ((Object)(object)item != (Object)null) { item.enabled = true; } } _ghostActive = false; MelonLogger.Msg("[SORPRESA] Fantasma: jugador vuelve a ser visible."); } } public static class JumpScareManager { private static float _lastJumpscareTime = -999f; private const float Cooldown = 120f; private static bool _initialized; private static bool _assetsLoaded; private static bool _running; private static GameObject _overlayRoot; private static RawImage _faceImage; private static AudioSource _audioSource; private static CanvasGroup _canvasGroup; private static readonly List _faces = new List(); private static readonly List _screams = new List(); private static string _modDir; private static readonly Random Rng = new Random(); public static void OnSceneLoad(string sceneName) { if ((Object)(object)_overlayRoot != (Object)null) { Object.Destroy((Object)(object)_overlayRoot); } _initialized = false; _overlayRoot = null; _faceImage = null; _audioSource = null; _canvasGroup = null; _running = false; PlayerGrabbedPatch.Reset(); } public static void OnUpdate() { if (!_initialized) { TryInitialize(); } } public static bool ShouldTrigger(float chance) { if (!_initialized || _running) { return false; } if ((Object)(object)_overlayRoot == (Object)null || _overlayRoot.activeSelf) { return false; } if (!FeatureLogic.CooldownAllows(Time.time, _lastJumpscareTime, 120f)) { return false; } if (_faces.Count == 0 || _screams.Count == 0) { return false; } return Rng.NextDouble() < (double)chance; } public static void TriggerJumpscare() { _lastJumpscareTime = Time.time; MelonCoroutines.Start(RunJumpscare()); } private static IEnumerator RunJumpscare() { _running = true; yield return (object)new WaitForSecondsRealtime((float)(Rng.NextDouble() * 2.0)); if ((Object)(object)_overlayRoot == (Object)null) { _running = false; yield break; } _faceImage.texture = (Texture)(object)_faces[Rng.Next(_faces.Count)]; _audioSource.clip = _screams[Rng.Next(_screams.Count)]; _overlayRoot.SetActive(true); if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f; } _audioSource.volume = 1f; _audioSource.Play(); yield return (object)new WaitForSecondsRealtime(1.5f + (float)(Rng.NextDouble() * 1.0)); float fadeTime = 0.3f; float elapsed = 0f; while (elapsed < fadeTime) { elapsed += Time.unscaledDeltaTime; if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f - elapsed / fadeTime; } yield return null; } _overlayRoot.SetActive(false); _running = false; } private static void TryInitialize() { if (!((Object)(object)Camera.main == (Object)null)) { _modDir = FindModDirectory(); CreateOverlay(); if (!_assetsLoaded) { _assetsLoaded = true; MelonCoroutines.Start(LoadAssets()); } _initialized = true; ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] Inicializado. Directorio: " + _modDir); } } private static string FindModDirectory() { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? AppDomain.CurrentDomain.BaseDirectory; } private static IEnumerator LoadAssets() { yield return MelonCoroutines.Start(LoadImages()); yield return MelonCoroutines.Start(LoadSounds()); ((MelonBase)JumpScares.Instance).LoggerInstance.Msg($"[SORPRESA] Assets cargados: {_faces.Count} imágenes, {_screams.Count} sonidos."); } private static IEnumerator LoadImages() { List list = new List(); list.AddRange(Directory.GetFiles(_modDir, "*.jfif")); foreach (string path in list) { UnityWebRequest req = UnityWebRequestTexture.GetTexture("file:///" + path.Replace('\\', '/')); try { yield return req.SendWebRequest(); if ((int)req.result == 1) { Texture2D content = DownloadHandlerTexture.GetContent(req); ((Object)content).name = Path.GetFileNameWithoutExtension(path); _faces.Add(content); ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] Imagen cargada: " + ((Object)content).name); } else { ((MelonBase)JumpScares.Instance).LoggerInstance.Warning("[SORPRESA] Error imagen " + path + ": " + req.error); } } finally { ((IDisposable)req)?.Dispose(); } } } private static IEnumerator LoadSounds() { string[] files = Directory.GetFiles(_modDir, "*.mp3"); string[] array = files; foreach (string path in array) { UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip("file:///" + path.Replace('\\', '/'), (AudioType)13); try { ((DownloadHandlerAudioClip)req.downloadHandler).streamAudio = false; yield return req.SendWebRequest(); if ((int)req.result == 1) { AudioClip content = DownloadHandlerAudioClip.GetContent(req); ((Object)content).name = Path.GetFileNameWithoutExtension(path); _screams.Add(content); ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] Sonido cargado: " + ((Object)content).name); } else { ((MelonBase)JumpScares.Instance).LoggerInstance.Warning("[SORPRESA] Error sonido " + path + ": " + req.error); } } finally { ((IDisposable)req)?.Dispose(); } } } private static void CreateOverlay() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_0129: Unknown result type (might be due to invalid IL or missing references) _overlayRoot = new GameObject("SORPRESA_Overlay"); Object.DontDestroyOnLoad((Object)(object)_overlayRoot); Canvas obj = _overlayRoot.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 9999; _canvasGroup = _overlayRoot.AddComponent(); _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; CanvasScaler obj2 = _overlayRoot.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); GameObject val = new GameObject("BG"); val.transform.SetParent(_overlayRoot.transform, false); ((Graphic)val.AddComponent()).color = Color.black; StretchRect(val.GetComponent()); GameObject val2 = new GameObject("Face"); val2.transform.SetParent(_overlayRoot.transform, false); _faceImage = val2.AddComponent(); ((Graphic)_faceImage).color = Color.white; _faceImage.uvRect = new Rect(0f, 0f, 1f, 1f); StretchRect(val2.GetComponent()); GameObject val3 = new GameObject("Audio"); val3.transform.SetParent(_overlayRoot.transform, false); _audioSource = val3.AddComponent(); _audioSource.spatialBlend = 0f; _audioSource.volume = 1f; _audioSource.priority = 0; _overlayRoot.SetActive(false); } private static void StretchRect(RectTransform rt) { //IL_0001: 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_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) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; } } [HarmonyPatch(typeof(ProtoActor), "CheckForMeGrabbByMonster")] public static class PlayerGrabbedPatch { private static bool _wasGrabbed; [HarmonyPostfix] public static void Postfix(bool __result) { if (__result && !_wasGrabbed && JumpScareManager.ShouldTrigger(1f)) { ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] ¡Agarrado por monstruo - SUSTO!"); JumpScareManager.TriggerJumpscare(); } _wasGrabbed = __result; } public static void Reset() { _wasGrabbed = false; } } [HarmonyPatch(typeof(StatController), "ApplyDamage")] public static class PlayerHitByMonsterPatch { private static PropertyInfo _isOwnerProp; [HarmonyPostfix] public static void Postfix(StatController __instance) { try { VCreature self = __instance.Self; VPlayer val = (VPlayer)(object)((self is VPlayer) ? self : null); if (val != null) { if (_isOwnerProp == null) { _isOwnerProp = ((object)val).GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); } if (!(_isOwnerProp == null) && (bool)_isOwnerProp.GetValue(val) && JumpScareManager.ShouldTrigger(0.75f)) { ((MelonBase)JumpScares.Instance).LoggerInstance.Msg("[SORPRESA] Golpeado por enemigo - ¡SUSTO!"); JumpScareManager.TriggerJumpscare(); } } } catch { } } } public class JumpScares : MelonMod { internal static JumpScares Instance { get; private set; } public override void OnInitializeMelon() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) Instance = this; new Harmony("com.xaxiflopi.sorpresa").PatchAll(); ((MelonBase)this).LoggerInstance.Msg("SORPRESA v1.9.1 activado. ¡Que empiece la diversión!"); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { JumpScareManager.OnSceneLoad(sceneName); AkaliReplacer.OnSceneLoad(); TowerFeature.OnSceneLoad(); ModelManager.Preload(); ModSounds.EnsureLoaded(); } public override void OnUpdate() { JumpScareManager.OnUpdate(); ChickenInverter.OnUpdate(); AkaliReplacer.OnUpdate(); TowerFeature.OnUpdate(); } } [HarmonyPatch(typeof(AudioSourceFilterController), "SetMimicVoiceEcho")] public static class MimicVoiceEchoPatch { [HarmonyPrefix] public static bool Prefix() { return false; } } public static class ModelManager { private static GameObject _akali; private static GameObject _blonde; private static GameObject _tower; private static bool _triedAkali; private static bool _triedBlonde; private static bool _triedTower; public static GameObject AkaliTemplate { get { if ((Object)(object)_akali == (Object)null && !_triedAkali) { _triedAkali = true; _akali = ObjLoader.Load(Path.Combine(ModDir(), "Models", "akali", "Akali.obj"), "SORPRESA_Akali"); } return _akali; } } public static GameObject BlondeTemplate { get { if ((Object)(object)_blonde == (Object)null && !_triedBlonde) { _triedBlonde = true; _blonde = ObjLoader.Load(Path.Combine(ModDir(), "Models", "blonde", "blonde.obj"), "SORPRESA_Blonde"); } return _blonde; } } public static GameObject TowerTemplate { get { if ((Object)(object)_tower == (Object)null && !_triedTower) { _triedTower = true; _tower = ObjLoader.Load(Path.Combine(ModDir(), "Models", "tower", "tower.obj"), "SORPRESA_Tower"); if ((Object)(object)_tower != (Object)null) { ObjLoader.AddColliderFromObj(_tower, Path.Combine(ModDir(), "Models", "tower", "tower_col.obj")); } } return _tower; } } public static void Preload() { GameObject akaliTemplate = AkaliTemplate; GameObject blondeTemplate = BlondeTemplate; GameObject towerTemplate = TowerTemplate; if ((Object)(object)akaliTemplate == (Object)null || (Object)(object)blondeTemplate == (Object)null || (Object)(object)towerTemplate == (Object)null) { MelonLogger.Warning("[SORPRESA] ModelManager: akali=" + (((Object)(object)akaliTemplate != (Object)null) ? "OK" : "FALTA") + ", blonde=" + (((Object)(object)blondeTemplate != (Object)null) ? "OK" : "FALTA") + ", tower=" + (((Object)(object)towerTemplate != (Object)null) ? "OK" : "FALTA")); } } public static GameObject Spawn(GameObject template, Vector3 position, Quaternion rotation, float heightMeters) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)template == (Object)null) { return null; } GameObject obj = Object.Instantiate(template, position, rotation); obj.transform.localScale = Vector3.one * heightMeters; obj.SetActive(true); return obj; } private static string ModDir() { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? AppDomain.CurrentDomain.BaseDirectory; } } public static class ModSounds { private static AudioClip _torre; private static bool _loading; private static AudioSource _source; public static bool TorreReady => (Object)(object)_torre != (Object)null; public static void EnsureLoaded() { if ((Object)(object)_torre == (Object)null && !_loading) { _loading = true; MelonCoroutines.Start(LoadTorre()); } } private static IEnumerator LoadTorre() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "Sounds", "sonido-torre.mp3"); if (!File.Exists(text)) { MelonLogger.Warning("[SORPRESA] ModSounds: no existe " + text); yield break; } UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip("file:///" + text.Replace('\\', '/'), (AudioType)13); try { ((DownloadHandlerAudioClip)req.downloadHandler).streamAudio = false; yield return req.SendWebRequest(); if ((int)req.result == 1) { _torre = DownloadHandlerAudioClip.GetContent(req); ((Object)_torre).name = "sonido-torre"; MelonLogger.Msg("[SORPRESA] ModSounds: sonido-torre cargado"); } else { MelonLogger.Warning("[SORPRESA] ModSounds: error cargando sonido-torre: " + req.error); } } finally { ((IDisposable)req)?.Dispose(); } } public static void PlayTorre() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if (!((Object)(object)_torre == (Object)null)) { if ((Object)(object)_source == (Object)null) { GameObject val = new GameObject("SORPRESA_Sounds"); Object.DontDestroyOnLoad((Object)val); _source = val.AddComponent(); _source.spatialBlend = 0f; _source.volume = 1f; _source.priority = 0; } _source.PlayOneShot(_torre); } } } public static class ObjLoader { private static MethodInfo _loadImage; private static Shader _shader; public static GameObject Load(string objPath, string templateName, bool withCollider = false) { //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: 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_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Expected O, but got Unknown //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Expected O, but got Unknown //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_046e: Unknown result type (might be due to invalid IL or missing references) //IL_0475: Unknown result type (might be due to invalid IL or missing references) //IL_0488: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) try { if (!File.Exists(objPath)) { MelonLogger.Warning("[SORPRESA] ObjLoader: no existe " + objPath); return null; } List list = new List(); List list2 = new List(); List list3 = new List(); List list4 = new List(); List list5 = new List(); List list6 = new List(); Dictionary cache = new Dictionary(); List> list7 = new List>(); List list8 = new List(); List list9 = null; bool flag = false; string mtlFile = null; foreach (string item2 in File.ReadLines(objPath)) { string text = item2.Trim(); if (text.Length == 0 || text[0] == '#') { continue; } if (text.StartsWith("v ")) { float[] array = SplitFloats(text, 2); list.Add(new Vector3(0f - array[0], array[1], array[2])); } else if (text.StartsWith("vt ")) { float[] array2 = SplitFloats(text, 3); list2.Add(new Vector2(array2[0], array2[1])); } else if (text.StartsWith("vn ")) { float[] array3 = SplitFloats(text, 3); list3.Add(new Vector3(0f - array3[0], array3[1], array3[2])); flag = true; } else if (text.StartsWith("usemtl ")) { string item = text.Substring(7).Trim(); int num = list8.IndexOf(item); if (num >= 0) { list9 = list7[num]; continue; } list9 = new List(); list7.Add(list9); list8.Add(item); } else if (text.StartsWith("mtllib ")) { mtlFile = text.Substring(7).Trim(); } else { if (!text.StartsWith("f ")) { continue; } if (list9 == null) { list9 = new List(); list7.Add(list9); list8.Add("__default__"); } string[] array4 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); int num2 = array4.Length - 1; if (num2 >= 3) { int[] array5 = new int[num2]; for (int i = 0; i < num2; i++) { array5[i] = ResolveVertex(array4[i + 1], list, list2, list3, list4, list5, list6, cache); } for (int j = 1; j < num2 - 1; j++) { list9.Add(array5[0]); list9.Add(array5[j + 1]); list9.Add(array5[j]); } } } } if (list4.Count == 0) { MelonLogger.Warning("[SORPRESA] ObjLoader: " + objPath + " sin geometría"); return null; } Mesh val = new Mesh { name = templateName }; if (list4.Count > 65000) { val.indexFormat = (IndexFormat)1; } val.SetVertices(list4); if (list5.Count == list4.Count) { val.SetUVs(0, list5); } if (flag && list6.Count == list4.Count) { val.SetNormals(list6); } val.subMeshCount = list7.Count; for (int k = 0; k < list7.Count; k++) { val.SetTriangles(list7[k], k); } if (!flag || list6.Count != list4.Count) { val.RecalculateNormals(); } val.RecalculateBounds(); Dictionary dictionary = ParseMtl(Path.GetDirectoryName(objPath), mtlFile); Material[] array6 = (Material[])(object)new Material[list7.Count]; for (int l = 0; l < list7.Count; l++) { Texture2D value = null; dictionary.TryGetValue(list8[l], out value); array6[l] = CreateMaterial(list8[l], value); } GameObject val2 = new GameObject(templateName); Object.DontDestroyOnLoad((Object)(object)val2); GameObject val3 = new GameObject("Mesh"); val3.transform.SetParent(val2.transform, false); val3.AddComponent().sharedMesh = val; MeshRenderer obj = val3.AddComponent(); ((Renderer)obj).sharedMaterials = array6; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)1; if (withCollider) { MeshCollider obj2 = val3.AddComponent(); obj2.sharedMesh = val; obj2.convex = false; } Bounds bounds = val.bounds; float num3 = Mathf.Max(((Bounds)(ref bounds)).size.y, 0.0001f); float num4 = 1f / num3; val3.transform.localScale = Vector3.one * num4; val3.transform.localPosition = new Vector3((0f - ((Bounds)(ref bounds)).center.x) * num4, (0f - ((Bounds)(ref bounds)).min.y) * num4, (0f - ((Bounds)(ref bounds)).center.z) * num4); val2.SetActive(false); MelonLogger.Msg("[SORPRESA] ObjLoader: '" + templateName + "' cargado " + $"({list4.Count} vértices, {list7.Count} materiales, altura original {num3:F2})"); return val2; } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] ObjLoader: error cargando " + objPath + ": " + ex.Message); return null; } } public static bool AddColliderFromObj(GameObject templateRoot, string objPath) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: 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_01c0: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)templateRoot == (Object)null || !File.Exists(objPath)) { return false; } Transform val = templateRoot.transform.Find("Mesh"); if ((Object)(object)val == (Object)null) { return false; } List list = new List(); List list2 = new List(); foreach (string item in File.ReadLines(objPath)) { string text = item.Trim(); if (text.StartsWith("v ")) { float[] array = SplitFloats(text, 3); list.Add(new Vector3(0f - array[0], array[1], array[2])); } else { if (!text.StartsWith("f ")) { continue; } string[] array2 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); int num = array2.Length - 1; if (num < 3) { continue; } int[] array3 = new int[num]; for (int i = 0; i < num; i++) { string text2 = array2[i + 1]; int num2 = text2.IndexOf('/'); if (num2 >= 0) { text2 = text2.Substring(0, num2); } int num3 = int.Parse(text2, CultureInfo.InvariantCulture); array3[i] = ((num3 > 0) ? (num3 - 1) : (list.Count + num3)); } for (int j = 1; j < num - 1; j++) { list2.Add(array3[0]); list2.Add(array3[j + 1]); list2.Add(array3[j]); } } } if (list.Count == 0 || list2.Count == 0) { return false; } Mesh val2 = new Mesh { name = ((Object)templateRoot).name + "_col" }; if (list.Count > 65000) { val2.indexFormat = (IndexFormat)1; } val2.SetVertices(list); val2.SetTriangles(list2, 0); GameObject val3 = new GameObject("Collision"); val3.transform.SetParent(templateRoot.transform, false); val3.transform.localScale = val.localScale; val3.transform.localPosition = val.localPosition; MeshCollider obj = val3.AddComponent(); obj.sharedMesh = val2; obj.convex = false; MelonLogger.Msg("[SORPRESA] ObjLoader: collider '" + ((Object)val2).name + "' " + $"({list2.Count / 3} triángulos pisables)"); return true; } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] ObjLoader: error collider " + objPath + ": " + ex.Message); return false; } } private static int ResolveVertex(string token, List positions, List uvs, List normals, List outVerts, List outUvs, List outNormals, Dictionary cache) { //IL_012d: 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_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0171: 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) int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 1; bool flag = false; for (int i = 0; i <= token.Length; i++) { char c = ((i < token.Length) ? token[i] : '/'); switch (c) { case '/': { int num7 = (flag ? (num5 * num6) : 0); switch (num4) { case 0: num = num7; break; case 1: num2 = num7; break; default: num3 = num7; break; } num4++; num5 = 0; num6 = 1; flag = false; if (num4 <= 2) { continue; } break; } case '-': num6 = -1; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num5 = num5 * 10 + (c - 48); flag = true; continue; default: continue; } break; } num = ((num > 0) ? (num - 1) : ((num < 0) ? (positions.Count + num) : 0)); num2 = ((num2 > 0) ? (num2 - 1) : ((num2 < 0) ? (uvs.Count + num2) : (-1))); num3 = ((num3 > 0) ? (num3 - 1) : ((num3 < 0) ? (normals.Count + num3) : (-1))); long key = ((long)num << 42) | ((long)(num2 + 1) << 21) | (num3 + 1); if (cache.TryGetValue(key, out var value)) { return value; } value = outVerts.Count; outVerts.Add((num >= 0 && num < positions.Count) ? positions[num] : Vector3.zero); outUvs.Add((num2 >= 0 && num2 < uvs.Count) ? uvs[num2] : Vector2.zero); outNormals.Add((num3 >= 0 && num3 < normals.Count) ? normals[num3] : Vector3.up); cache[key] = value; return value; } private static float[] SplitFloats(string line, int expected) { string[] array = line.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); float[] array2 = new float[Mathf.Max(3, expected)]; for (int i = 1; i < array.Length && i - 1 < array2.Length; i++) { float.TryParse(array[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i - 1]); } return array2; } private static Dictionary ParseMtl(string dir, string mtlFile) { Dictionary dictionary = new Dictionary(); try { if (mtlFile == null) { return dictionary; } string path = Path.Combine(dir, mtlFile); if (!File.Exists(path)) { return dictionary; } string text = null; foreach (string item in File.ReadLines(path)) { string text2 = item.Trim(); if (text2.StartsWith("newmtl ")) { text = text2.Substring(7).Trim(); } else if (text2.StartsWith("map_Kd ") && text != null && !dictionary.ContainsKey(text)) { string path2 = text2.Substring(7).Trim(); Texture2D val = LoadTexture(Path.Combine(dir, path2)); if ((Object)(object)val != (Object)null) { dictionary[text] = val; } } } } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] ObjLoader: error MTL: " + ex.Message); } return dictionary; } private static Texture2D LoadTexture(string path) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown try { if (!File.Exists(path)) { return null; } if (_loadImage == null) { _loadImage = typeof(ImageConversion).GetMethod("LoadImage", new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }); } if (_loadImage == null) { return null; } byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true); if (!(bool)_loadImage.Invoke(null, new object[3] { val, array, false })) { return null; } ((Object)val).name = Path.GetFileNameWithoutExtension(path); return val; } catch { return null; } } private static Shader FindShader() { if ((Object)(object)_shader != (Object)null) { return _shader; } string[] array = new string[6] { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit", "Universal Render Pipeline/Unlit", "Standard", "Unlit/Texture", "Sprites/Default" }; foreach (string text in array) { Shader val = Shader.Find(text); if ((Object)(object)val != (Object)null) { MelonLogger.Msg("[SORPRESA] ObjLoader: usando shader '" + text + "'"); return _shader = val; } } return null; } private static Material CreateMaterial(string name, Texture2D tex) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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) Shader val = FindShader(); Material val2 = (((Object)(object)val != (Object)null) ? new Material(val) : new Material(Shader.Find("Hidden/InternalErrorShader"))); ((Object)val2).name = name; if ((Object)(object)tex != (Object)null) { if (val2.HasProperty("_BaseMap")) { val2.SetTexture("_BaseMap", (Texture)(object)tex); } if (val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", (Texture)(object)tex); } } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", Color.white); } if (val2.HasProperty("_Color")) { val2.SetColor("_Color", Color.white); } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", 0.15f); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } if ((Object)(object)tex != (Object)null && val2.HasProperty("_EmissionColor")) { val2.EnableKeyword("_EMISSION"); if (val2.HasProperty("_EmissionMap")) { val2.SetTexture("_EmissionMap", (Texture)(object)tex); } val2.SetColor("_EmissionColor", new Color(0.45f, 0.45f, 0.45f, 1f)); val2.globalIlluminationFlags = (MaterialGlobalIlluminationFlags)0; } return val2; } } public static class TowerFeature { private const float TOWER_HEIGHT = 6.5f; private const float SPAWN_RETRY = 2f; private const float TOP_CHECK = 0.3f; private const float CAMERA_EYE_OFFSET = 1.6f; private static GameObject _tower; private static Vector3 _towerBase; private static bool _atTop; private static float _nextSpawnTry; private static float _nextTopCheck; private static float _nextDebugLog; private static ProtoActor _localActor; public static void OnSceneLoad() { _tower = null; _localActor = null; _atTop = false; _nextSpawnTry = 0f; } public static void OnUpdate() { if (ChaosCoordinator.IsInDungeon) { return; } if ((Object)(object)_tower == (Object)null) { if (!(Time.time < _nextSpawnTry)) { _nextSpawnTry = Time.time + 2f; try { TrySpawn(); } catch (Exception ex) { MelonLogger.Warning("[SORPRESA] Torre: " + ex.Message); } } } else if (!(Time.time < _nextTopCheck)) { _nextTopCheck = Time.time + 0.3f; try { CheckTop(); } catch { } } } private static void TrySpawn() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_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) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0072: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_008a: 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) //IL_0099: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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_012a: 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) GameObject towerTemplate = ModelManager.TowerTemplate; if ((Object)(object)towerTemplate == (Object)null) { return; } VendingMachineLevelObject[] array = Object.FindObjectsOfType(); if (array == null || array.Length == 0) { return; } Vector3 val = Vector3.zero; VendingMachineLevelObject[] array2 = array; foreach (VendingMachineLevelObject val2 in array2) { val += ((Component)val2).transform.position; } val /= (float)array.Length; Vector3? val3 = FindTramPosition(); if (val3.HasValue) { Vector3 val4 = (val + val3.Value) * 0.5f; RaycastHit val5 = default(RaycastHit); if (Physics.Raycast(val4 + Vector3.up * 4f, Vector3.down, ref val5, 20f, -5, (QueryTriggerInteraction)1)) { val4 = ((RaycastHit)(ref val5)).point; } Vector3 val6 = val - val4; val6.y = 0f; Quaternion rotation = ((((Vector3)(ref val6)).sqrMagnitude > 0.001f) ? Quaternion.LookRotation(((Vector3)(ref val6)).normalized) : Quaternion.identity); _tower = ModelManager.Spawn(towerTemplate, val4, rotation, 6.5f); if (!((Object)(object)_tower == (Object)null)) { _towerBase = val4; _atTop = false; MelonLogger.Msg($"[SORPRESA] Torre plantada en el lobby @ {val4} " + $"(máquinas @ {val}, tram @ {val3.Value})"); } } } private static Vector3? FindTramPosition() { //IL_0015: 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) TramConsole[] array = Object.FindObjectsOfType(); if (array != null && array.Length != 0) { return ((Component)array[0]).transform.position; } TramStartLeverLevelObject[] array2 = Object.FindObjectsOfType(); if (array2 != null && array2.Length != 0) { return ((Component)array2[0]).transform.position; } return null; } private static void CheckTop() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tower == (Object)null) { return; } Vector3 val; if ((Object)(object)_localActor != (Object)null) { val = ((Component)_localActor).transform.position; } else { ProtoActor[] array = Object.FindObjectsOfType(); foreach (ProtoActor val2 in array) { if (ChaosCoordinator.IsLocalPlayer(val2)) { _localActor = val2; break; } } if ((Object)(object)_localActor != (Object)null) { val = ((Component)_localActor).transform.position; } else { Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } val = ((Component)main).transform.position + Vector3.down * 1.6f; } } float num = val.y - _towerBase.y; Vector3 val3 = val - _towerBase; val3.y = 0f; float magnitude = ((Vector3)(ref val3)).magnitude; bool flag = FeatureLogic.TowerInTopZone(num, magnitude); if (num > 2.925f && Time.time >= _nextDebugLog) { _nextDebugLog = Time.time + 3f; MelonLogger.Msg($"[SORPRESA] Torre: altura={num:F2} m " + $"(umbral {4.6800003f:F2}), " + $"distancia={magnitude:F2} m (límite {2.5f:F1}), " + $"arriba={flag}"); } if (flag && !_atTop) { _atTop = true; MelonLogger.Msg("[SORPRESA] ¡Has llegado a lo alto de la torre!"); ModSounds.PlayTorre(); } else if (_atTop && FeatureLogic.TowerShouldRearm(num)) { _atTop = false; } } } public static class VentriloquismFeature { private const float DELAY_SECONDS = 30f; private const float DURATION_SECONDS = 30f; private const int OFFSET_SEED = 1001; private static bool _active; public static IEnumerator RunOnEntry(int generation) { _active = false; yield return (object)new WaitForSeconds(30f); if (!ChaosCoordinator.IsGenerationCurrent(generation) || _active) { yield break; } _active = true; List sortedPlayers = ChaosCoordinator.GetSortedPlayers(); if (sortedPlayers.Count < 2) { _active = false; yield break; } List list = ChaosCoordinator.PickActors(Mathf.Min(2, sortedPlayers.Count - 1), 1001); MelonLogger.Msg($"[SORPRESA] Ventriloquismo: {list.Count} jugador(es) afectado(s)"); foreach (ProtoActor item in list) { if (ChaosCoordinator.IsLocalPlayer(item)) { continue; } ProtoActor val = null; foreach (ProtoActor item2 in sortedPlayers) { if ((Object)(object)item2 != (Object)(object)item) { val = item2; break; } } if (!((Object)(object)val == (Object)null)) { MelonCoroutines.Start(SwapVoicePosition(item, val)); } } } private static IEnumerator SwapVoicePosition(ProtoActor source, ProtoActor impostor) { AudioSource voiceAudioSource = GetVoiceAudioSource(source); if ((Object)(object)voiceAudioSource == (Object)null) { MelonLogger.Warning("[SORPRESA] Ventriloquismo: no se encontró AudioSource de voz"); yield break; } Transform originalParent = ((Component)voiceAudioSource).transform.parent; Vector3 originalLocalPos = ((Component)voiceAudioSource).transform.localPosition; MelonLogger.Msg("[SORPRESA] Ventriloquismo: ¡Voz movida!"); float elapsed = 0f; while (elapsed < 30f && ChaosCoordinator.IsInDungeon) { if ((Object)(object)impostor != (Object)null) { ((Component)voiceAudioSource).transform.position = ((Component)impostor).transform.position; } elapsed += Time.deltaTime; yield return null; } if ((Object)(object)voiceAudioSource != (Object)null) { ((Component)voiceAudioSource).transform.SetParent(originalParent, false); ((Component)voiceAudioSource).transform.localPosition = originalLocalPos; } _active = false; MelonLogger.Msg("[SORPRESA] Ventriloquismo: voz restaurada."); } private static AudioSource GetVoiceAudioSource(ProtoActor actor) { try { object voiceManager = ChaosCoordinator.GetVoiceManager(); if (voiceManager != null) { MethodInfo method = voiceManager.GetType().GetMethod("GetPlayerVoiceAudioSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object? obj = method.Invoke(voiceManager, new object[1] { actor }); AudioSource val = (AudioSource)((obj is AudioSource) ? obj : null); if (val != null) { return val; } } method = voiceManager.GetType().GetMethod("GetVoicePlaybackAudioSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object? obj2 = method.Invoke(voiceManager, new object[1] { actor }); AudioSource val2 = (AudioSource)((obj2 is AudioSource) ? obj2 : null); if (val2 != null) { return val2; } } } AudioSourceFilterController componentInChildren = ((Component)actor).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { AudioSource val3 = ((Component)componentInChildren).GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)componentInChildren).GetComponentInChildren(true); } if ((Object)(object)val3 != (Object)null) { MelonLogger.Msg("[SORPRESA] Ventriloquismo: AudioSource de voz encontrado via AudioSourceFilterController ('" + ((Object)((Component)val3).gameObject).name + "')"); return val3; } } return ((Component)actor).GetComponentInChildren(); } catch { return null; } } } public static class VoodooDollFeature { private static bool _cooling; public static void OnVoodooActivated() { if (!_cooling) { _cooling = true; MelonCoroutines.Start(DanceRoutine()); } } private static IEnumerator DanceRoutine() { yield return null; ProtoActor dancer = null; ProtoActor[] array = Object.FindObjectsOfType(); foreach (ProtoActor val in array) { if (ChaosCoordinator.IsLocalPlayer(val)) { dancer = val; break; } } if ((Object)(object)dancer == (Object)null) { MelonLogger.Warning("[SORPRESA] Vudú: no se encontró actor local"); yield return (object)new WaitForSeconds(2f); _cooling = false; yield break; } MelonLogger.Msg("[SORPRESA] ¡Muñeco de Vudú! ¡A bailar " + $"{5f:F0} segundos!"); if (!TryPlayEmote(dancer)) { Transform tf = ((Component)dancer).transform; for (float t = 0f; t < 5f; t += Time.deltaTime) { tf.Rotate(Vector3.up, 360f * Time.deltaTime, (Space)0); yield return null; } } else { yield return (object)new WaitForSeconds(5f); TryCancelEmote(dancer); } MelonLogger.Msg("[SORPRESA] Vudú: baile terminado."); yield return (object)new WaitForSeconds(2f); _cooling = false; } private static bool TryPlayEmote(ProtoActor actor) { try { Type type = ((object)actor).GetType(); string[] array = new string[5] { "PlayEmote", "UseEmote", "RequestEmote", "DoEmote", "SetEmote" }; foreach (string text in array) { MethodInfo method = type.GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { continue; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { method.Invoke(actor, null); } else { if (parameters.Length != 1 || !(parameters[0].ParameterType == typeof(int))) { continue; } method.Invoke(actor, new object[1] { 3 }); } MelonLogger.Msg("[SORPRESA] Vudú: emote vía " + text + "()"); return true; } MethodInfo method2 = type.GetMethod("GetRandomEmoteMasterID", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { object obj = method2.Invoke(actor, null); MethodInfo method3 = type.GetMethod("UseSkill", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method3 != null && obj != null) { method3.Invoke(actor, new object[1] { obj }); MelonLogger.Msg("[SORPRESA] Vudú: emote vía UseSkill"); return true; } } return false; } catch { return false; } } private static void TryCancelEmote(ProtoActor actor) { try { string[] array = new string[3] { "CancelEmote", "StopEmote", "CancelEmotion" }; foreach (string name in array) { MethodInfo method = ((object)actor).GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method == null)) { method.Invoke(actor, null); break; } } } catch { } } } [HarmonyPatch(typeof(VoodooDoll), "OnBlackOut")] public static class VoodooDollBlackOutPatch { [HarmonyPostfix] public static void Postfix() { VoodooDollFeature.OnVoodooActivated(); } }