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<int, GameObject> _replaced = new Dictionary<int, GameObject>();
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<ProtoActor>();
HashSet<int> hashSet = new HashSet<int>();
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<int> list = new List<int>();
foreach (KeyValuePair<int, GameObject> 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<GameObject>(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<Renderer>();
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<Renderer>(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<ProtoActor> GetSortedActors()
{
return (from a in Object.FindObjectsOfType<ProtoActor>()
orderby GetNetworkId(a)
select a).ToList();
}
public static List<ProtoActor> GetSortedPlayers()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Invalid comparison between Unknown and I4
List<ProtoActor> list = new List<ProtoActor>();
ProtoActor[] array = Object.FindObjectsOfType<ProtoActor>();
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<ProtoActor> 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<MonoBehaviour>();
foreach (MonoBehaviour val in array)
{
if (((object)val).GetType().Name == "VoiceManager")
{
return _voiceManager = val;
}
}
_voiceManagerMissing = true;
return null;
}
public static T CallVoiceManager<T>(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<object> _deadArchives = new List<object>();
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<ProtoActor> 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<T> Pick<T>(IList<T> items, Func<T, int> idOf, int count, int offsetSeed)
{
List<T> list = items.OrderBy(idOf).ToList();
if (list.Count == 0 || count <= 0)
{
return new List<T>();
}
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<T>(IEnumerable<T> sortedItems, Func<T, int> 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<ProtoActor> 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<ProtoActor> 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<Renderer> hidden = new List<Renderer>();
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<Renderer>(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<Texture2D> _faces = new List<Texture2D>();
private static readonly List<AudioClip> _screams = new List<AudioClip>();
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<string> list = new List<string>();
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<Canvas>();
obj.renderMode = (RenderMode)0;
obj.sortingOrder = 9999;
_canvasGroup = _overlayRoot.AddComponent<CanvasGroup>();
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
CanvasScaler obj2 = _overlayRoot.AddComponent<CanvasScaler>();
obj2.uiScaleMode = (ScaleMode)1;
obj2.referenceResolution = new Vector2(1920f, 1080f);
GameObject val = new GameObject("BG");
val.transform.SetParent(_overlayRoot.transform, false);
((Graphic)val.AddComponent<RawImage>()).color = Color.black;
StretchRect(val.GetComponent<RectTransform>());
GameObject val2 = new GameObject("Face");
val2.transform.SetParent(_overlayRoot.transform, false);
_faceImage = val2.AddComponent<RawImage>();
((Graphic)_faceImage).color = Color.white;
_faceImage.uvRect = new Rect(0f, 0f, 1f, 1f);
StretchRect(val2.GetComponent<RectTransform>());
GameObject val3 = new GameObject("Audio");
val3.transform.SetParent(_overlayRoot.transform, false);
_audioSource = val3.AddComponent<AudioSource>();
_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<GameObject>(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<AudioSource>();
_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<Vector3> list = new List<Vector3>();
List<Vector2> list2 = new List<Vector2>();
List<Vector3> list3 = new List<Vector3>();
List<Vector3> list4 = new List<Vector3>();
List<Vector2> list5 = new List<Vector2>();
List<Vector3> list6 = new List<Vector3>();
Dictionary<long, int> cache = new Dictionary<long, int>();
List<List<int>> list7 = new List<List<int>>();
List<string> list8 = new List<string>();
List<int> 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<int>();
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<int>();
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<string, Texture2D> 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<MeshFilter>().sharedMesh = val;
MeshRenderer obj = val3.AddComponent<MeshRenderer>();
((Renderer)obj).sharedMaterials = array6;
((Renderer)obj).shadowCastingMode = (ShadowCastingMode)1;
if (withCollider)
{
MeshCollider obj2 = val3.AddComponent<MeshCollider>();
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<Vector3> list = new List<Vector3>();
List<int> list2 = new List<int>();
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<MeshCollider>();
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<Vector3> positions, List<Vector2> uvs, List<Vector3> normals, List<Vector3> outVerts, List<Vector2> outUvs, List<Vector3> outNormals, Dictionary<long, int> 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<string, Texture2D> ParseMtl(string dir, string mtlFile)
{
Dictionary<string, Texture2D> dictionary = new Dictionary<string, Texture2D>();
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<VendingMachineLevelObject>();
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<TramConsole>();
if (array != null && array.Length != 0)
{
return ((Component)array[0]).transform.position;
}
TramStartLeverLevelObject[] array2 = Object.FindObjectsOfType<TramStartLeverLevelObject>();
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<ProtoActor>();
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<ProtoActor> sortedPlayers = ChaosCoordinator.GetSortedPlayers();
if (sortedPlayers.Count < 2)
{
_active = false;
yield break;
}
List<ProtoActor> 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<AudioSourceFilterController>(true);
if ((Object)(object)componentInChildren != (Object)null)
{
AudioSource val3 = ((Component)componentInChildren).GetComponent<AudioSource>();
if ((Object)(object)val3 == (Object)null)
{
val3 = ((Component)componentInChildren).GetComponentInChildren<AudioSource>(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<AudioSource>();
}
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<ProtoActor>();
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();
}
}