Decompiled source of SORPRESA v1.9.1

SORPRESA.dll

Decompiled a week ago
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();
	}
}