Decompiled source of IllegalRave v1.0.10

IllegalRave.IL2CPP.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.AvatarFramework;
using Il2CppScheduleOne.AvatarFramework.Animation;
using Il2CppScheduleOne.AvatarFramework.Emotions;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.Messaging;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Actions;
using Il2CppScheduleOne.NPCs.Behaviour;
using Il2CppScheduleOne.NPCs.Relation;
using Il2CppScheduleOne.Police;
using Il2CppScheduleOne.UI.Compass;
using Il2CppScheduleOne.Vision;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using IllegalRave.Gltf;
using IllegalRave.Gltf.Data;
using IllegalRave.Gltf.Processing;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using NLayer;
using NLayer.Decoder;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Illegal Rave")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: MelonInfo(typeof(RaveMod), "Illegal Rave", "1.0.10", "Virtunerd", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyFileVersion("1.0.10")]
[assembly: AssemblyInformationalVersion("1.0.10")]
[assembly: AssemblyVersion("1.0.10.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal static class GlbAssetProvider
{
	private const uint GLB_MAGIC = 1179937895u;

	internal static byte[] GetDjBooth()
	{
		return ReadResource("rave.assets.glb.djbooth.glb");
	}

	internal static byte[] GetDjDan()
	{
		return ReadResource("rave.assets.glb.djdan.glb");
	}

	internal static byte[] GetDjGirl()
	{
		return ReadResource("rave.assets.glb.djgirl.glb");
	}

	internal static byte[] GetDjCutChemist()
	{
		return ReadResource("rave.assets.glb.djcutchemist.glb");
	}

	internal static byte[] GetDjLudapimp()
	{
		return ReadResource("rave.assets.glb.djludapimp.glb");
	}

	internal static byte[] GetFlyer()
	{
		return ReadResource("rave.assets.glb.flyer.glb");
	}

	internal static byte[] GetTripods()
	{
		return ReadResource("rave.assets.glb.tripods.glb");
	}

	internal static byte[] GetLetsGo()
	{
		return ReadResource("rave.assets.glb.letsgo.glb");
	}

	private static byte[] ReadResource(string name)
	{
		Assembly assembly = typeof(GlbAssetProvider).Assembly;
		using (Stream stream = assembly.GetManifestResourceStream(name + ".br"))
		{
			if (stream != null)
			{
				byte[] array = DecompressGlb(stream);
				if (array != null)
				{
					return array;
				}
				RaveMod.Logger.Error("Embedded asset '" + name + ".br' could not be decompressed.");
				return null;
			}
		}
		using Stream stream2 = assembly.GetManifestResourceStream(name);
		if (stream2 == null)
		{
			return null;
		}
		using MemoryStream memoryStream = new MemoryStream((int)stream2.Length);
		stream2.CopyTo(memoryStream);
		return memoryStream.ToArray();
	}

	private static byte[] DecompressGlb(Stream compressed)
	{
		using BrotliStream stream = new BrotliStream(compressed, CompressionMode.Decompress);
		byte[] array = new byte[12];
		if (!ReadExactly(stream, array, 0, array.Length))
		{
			return null;
		}
		if (BitConverter.ToUInt32(array, 0) != 1179937895)
		{
			return null;
		}
		uint num = BitConverter.ToUInt32(array, 8);
		if (num < 12 || num > int.MaxValue)
		{
			return null;
		}
		byte[] array2 = new byte[num];
		Buffer.BlockCopy(array, 0, array2, 0, array.Length);
		if (!ReadExactly(stream, array2, array.Length, (int)num - array.Length))
		{
			return null;
		}
		return array2;
	}

	private static bool ReadExactly(Stream stream, byte[] buffer, int offset, int count)
	{
		while (count > 0)
		{
			int num = stream.Read(buffer, offset, count);
			if (num <= 0)
			{
				return false;
			}
			offset += num;
			count -= num;
		}
		return true;
	}
}
public static class LetsGoObjectController
{
	private static GameObject _prefab;

	private static GameObject _instance;

	private static bool _cacheFailed;

	private const string GO_NAME = "LetsGoObject";

	private static readonly Vector3 CORE_POSITION = new Vector3(-170.12f, -2.94f, 85.17f);

	private static readonly Vector3 PULTV2_LOCAL_POSITION = new Vector3(0.028f, 1.809f, 0.258f);

	private static readonly Vector3 OFFSET = new Vector3(-3.9f, -0.95f, -0.5f);

	private static readonly Quaternion ROTATION = Quaternion.Euler(0f, 90f, 0f);

	internal static Vector3 GetSpawnPosition()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		return CORE_POSITION + OFFSET;
	}

	internal static Quaternion GetSpawnRotation()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		return ROTATION;
	}

	internal static Vector3 GetPultWorldPosition()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		return GetSpawnPosition() + GetSpawnRotation() * PULTV2_LOCAL_POSITION;
	}

	internal static void SpawnObject()
	{
		//IL_0092: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		if (_cacheFailed)
		{
			return;
		}
		try
		{
			if ((Object)(object)_prefab == (Object)null)
			{
				byte[] letsGo = GlbAssetProvider.GetLetsGo();
				if (letsGo == null)
				{
					Debug.LogError(Object.op_Implicit("[LetsGo] GLB data null"));
					_cacheFailed = true;
					return;
				}
				GltfImporter gltfImporter = new GltfImporter().SetName("LetsGoObject").ImportAnimations(import: false).SetScale(1f);
				_prefab = gltfImporter.Load(letsGo);
			}
			if (!((Object)(object)_instance != (Object)null))
			{
				Vector3 spawnPosition = GetSpawnPosition();
				RaveMod.Logger.Msg($"[LetsGo] Spawn at ({spawnPosition.x:F2}, {spawnPosition.y:F2}, {spawnPosition.z:F2})");
				_instance = Object.Instantiate<GameObject>(_prefab, spawnPosition, ROTATION);
				((Object)_instance).name = "LetsGoObject";
				_instance.isStatic = true;
				SetupColliders(_instance);
				RaveMod.Logger.Msg($"[LetsGo] Final Position: {spawnPosition}");
				Instance logger = RaveMod.Logger;
				DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(19, 1);
				defaultInterpolatedStringHandler.AppendLiteral("[LetsGo] Rotation: ");
				Quaternion rOTATION = ROTATION;
				defaultInterpolatedStringHandler.AppendFormatted<Vector3>(((Quaternion)(ref rOTATION)).eulerAngles);
				logger.Msg(defaultInterpolatedStringHandler.ToStringAndClear());
			}
		}
		catch (Exception ex)
		{
			Debug.LogError(Object.op_Implicit("[LetsGo] Spawn failed: " + ex));
			_cacheFailed = true;
		}
	}

	internal static void SetupColliders(GameObject root)
	{
		int num = 0;
		foreach (Transform componentsInChild in root.GetComponentsInChildren<Transform>(true))
		{
			if (!((Object)componentsInChild).name.StartsWith("COL_", StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			MeshFilter component = ((Component)componentsInChild).GetComponent<MeshFilter>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null)
			{
				RaveMod.Logger.Warning("[LetsGo] Collider node '" + ((Object)componentsInChild).name + "' has no mesh – skipped");
				continue;
			}
			MeshCollider val = ((Component)componentsInChild).gameObject.GetComponent<MeshCollider>() ?? ((Component)componentsInChild).gameObject.AddComponent<MeshCollider>();
			val.sharedMesh = component.sharedMesh;
			val.convex = false;
			MeshRenderer component2 = ((Component)componentsInChild).GetComponent<MeshRenderer>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Renderer)component2).enabled = false;
			}
			num++;
			RaveMod.Logger.Msg("[LetsGo] Collider attached: " + ((Object)componentsInChild).name);
		}
		RaveMod.Logger.Msg($"[LetsGo] SetupColliders done – {num} collider(s) added");
	}

	internal static void Cleanup()
	{
		if ((Object)(object)_instance != (Object)null)
		{
			Object.Destroy((Object)(object)_instance);
			_instance = null;
		}
	}
}
public sealed class RaveAudioController
{
	private struct PendingClip
	{
		internal string TrackPath;

		internal float[] Samples;

		internal int SampleRate;

		internal int Channels;
	}

	private const int VISUAL_SPEAKER_COUNT = 4;

	private const float SPEAKER_RADIUS = 2.5f;

	private static GameObject[] _speakerVisuals;

	private static GameObject _audioObject;

	private static List<GameObject> _stageAudioObjects = new List<GameObject>();

	private static AudioSource _audioSource;

	private static bool _isPlaying = false;

	private static bool _isPaused = false;

	private const int STAGE_SYNC_TOLERANCE_SAMPLES = 441;

	private const int MAX_CACHE_SIZE = 50;

	private static Dictionary<string, AudioClip> _clipCache = new Dictionary<string, AudioClip>();

	private static LinkedList<string> _cacheOrder = new LinkedList<string>();

	private static Dictionary<AudioSource, float> _mutedSources = new Dictionary<AudioSource, float>();

	private static AudioSource[] _cachedVanillaSources;

	private static float _vanillaScanTimer = 0f;

	private const float VANILLA_SCAN_INTERVAL = 10f;

	private static readonly object _pendingLock = new object();

	private static readonly Queue<PendingClip> _pendingClips = new Queue<PendingClip>();

	private static volatile int _backgroundDecodeCount = 0;

	private static volatile bool _waitingForTrack = false;

	private static string _waitingTrackPath = null;

	private RaveAudioController()
	{
	}

	internal static void SpawnSpeaker()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Expected O, but got Unknown
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Expected O, but got Unknown
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b5: Expected O, but got Unknown
		//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0321: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		//IL_032d: Unknown result type (might be due to invalid IL or missing references)
		//IL_032f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0334: Unknown result type (might be due to invalid IL or missing references)
		//IL_0342: Unknown result type (might be due to invalid IL or missing references)
		//IL_0344: Unknown result type (might be due to invalid IL or missing references)
		//IL_0360: Unknown result type (might be due to invalid IL or missing references)
		//IL_0362: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			DestroyStageAudioObjects();
			_audioObject = null;
			_audioSource = null;
			_speakerVisuals = null;
			_isPlaying = false;
			_isPaused = false;
			DestroyStaleObjects();
			Vector3 musicPosition = RaveLocationManager.MusicPosition;
			_audioObject = new GameObject("RaveSpeaker_Audio");
			_audioObject.transform.position = musicPosition;
			_audioSource = _audioObject.AddComponent<AudioSource>();
			_audioSource.spatialBlend = 1f;
			_audioSource.rolloffMode = (AudioRolloffMode)2;
			_audioSource.dopplerLevel = 0f;
			_audioSource.loop = false;
			_audioSource.volume = RaveConfig.MusicVolume.Value;
			_audioSource.playOnAwake = false;
			if (RaveLocationManager.IsUnderground)
			{
				_audioSource.minDistance = 3f;
				_audioSource.maxDistance = 20f;
				_audioSource.spread = 360f;
				AnimationCurve val = new AnimationCurve();
				val.AddKey(0f, 1f);
				val.AddKey(3f, 1f);
				val.AddKey(8f, 0.6f);
				val.AddKey(14f, 0.15f);
				val.AddKey(18f, 0.02f);
				val.AddKey(20f, 0f);
				_audioSource.SetCustomCurve((AudioSourceCurveType)0, val);
			}
			else
			{
				_audioSource.minDistance = 5f;
				_audioSource.maxDistance = 60f;
				_audioSource.spread = 180f;
				AnimationCurve val2 = new AnimationCurve();
				val2.AddKey(0f, 1f);
				val2.AddKey(5f, 1f);
				val2.AddKey(15f, 0.7f);
				val2.AddKey(30f, 0.3f);
				val2.AddKey(45f, 0.06f);
				val2.AddKey(55f, 0.01f);
				val2.AddKey(60f, 0f);
				_audioSource.SetCustomCurve((AudioSourceCurveType)0, val2);
			}
			_speakerVisuals = (GameObject[])(object)new GameObject[4];
			Vector3 val3 = default(Vector3);
			for (int i = 0; i < 4; i++)
			{
				float num = (float)Math.PI / 2f * (float)i;
				((Vector3)(ref val3))..ctor(musicPosition.x + Mathf.Cos(num) * 2.5f, musicPosition.y, musicPosition.z + Mathf.Sin(num) * 2.5f);
				GameObject val4 = new GameObject($"RaveSpeaker_{i}");
				val4.transform.position = val3;
				_speakerVisuals[i] = val4;
				GameObject val5 = GameObject.CreatePrimitive((PrimitiveType)3);
				val5.transform.SetParent(val4.transform);
				val5.transform.localPosition = new Vector3(0f, 0.5f, 0f);
				val5.transform.localScale = new Vector3(0.8f, 1.2f, 0.6f);
				Vector3 val6 = musicPosition - val3;
				val6.y = 0f;
				if (Vector3.Dot(val6, val6) > 0.01f)
				{
					val4.transform.rotation = Quaternion.LookRotation(val6);
				}
				Collider component = val5.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				Renderer component2 = val5.GetComponent<Renderer>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.material.color = new Color(0.1f, 0.1f, 0.1f);
				}
			}
			ScanVanillaAudioSources();
			if (RaveLocationManager.IsStageActive)
			{
				TrySpawnStageAudioSources();
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("Error creating audio system: " + ex.Message);
		}
	}

	private static void TrySpawnStageAudioSources()
	{
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Expected O, but got Unknown
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Expected O, but got Unknown
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			DestroyStageAudioObjects();
			GameObject val = GameObject.Find("SmallFestivalStage");
			List<(Vector3, string)> list = new List<(Vector3, string)>();
			if ((Object)(object)val != (Object)null)
			{
				foreach (Transform componentsInChild in val.GetComponentsInChildren<Transform>(false))
				{
					string name = ((Object)componentsInChild).name;
					if (name != null)
					{
						string text = name.ToLowerInvariant();
						bool flag = text.StartsWith("col_speaker");
						bool flag2 = text.StartsWith("h") && text.Contains("ngeboxen");
						bool flag3 = text == "kleinebox";
						bool flag4 = text.StartsWith("funktion");
						if (flag || flag2 || flag3 || flag4)
						{
							list.Add((componentsInChild.position, name));
						}
					}
				}
			}
			if (list.Count == 0)
			{
				if (RaveConfig.DebugCoordinates.Value)
				{
					RaveMod.Logger.Msg("[DEBUG] No stage speaker nodes found — stage audio skipped (main source only).");
				}
				return;
			}
			AnimationCurve val2 = new AnimationCurve();
			val2.AddKey(0f, 1f);
			val2.AddKey(3f, 1f);
			val2.AddKey(10f, 0.6f);
			val2.AddKey(20f, 0.2f);
			val2.AddKey(35f, 0.02f);
			val2.AddKey(40f, 0f);
			foreach (var item3 in list)
			{
				Vector3 item = item3.Item1;
				string item2 = item3.Item2;
				GameObject val3 = new GameObject("RaveSpeaker_Stage_" + item2);
				val3.transform.position = item;
				AudioSource val4 = val3.AddComponent<AudioSource>();
				val4.spatialBlend = 1f;
				val4.rolloffMode = (AudioRolloffMode)2;
				val4.dopplerLevel = 0f;
				val4.loop = false;
				val4.volume = RaveConfig.MusicVolume.Value * 0.25f;
				val4.playOnAwake = false;
				val4.minDistance = 3f;
				val4.maxDistance = 40f;
				val4.spread = 140f;
				val4.SetCustomCurve((AudioSourceCurveType)0, val2);
				_stageAudioObjects.Add(val3);
				RaveMod.Logger.Msg($"[Stage] AudioSource spawned: {item2} at ({item.x:F1},{item.y:F1},{item.z:F1})");
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("[Stage] AudioSource spawn failed: " + ex.Message);
		}
	}

	internal static void PreloadAudio()
	{
		try
		{
			RavePlaylistManager.Initialize();
			if (RavePlaylistManager.TrackCount == 0)
			{
				RaveMod.Logger.Warning("No tracks in playlist, pre-loading skipped.");
				return;
			}
			_clipCache.Clear();
			_cacheOrder.Clear();
			_waitingForTrack = false;
			_waitingTrackPath = null;
			lock (_pendingLock)
			{
				_pendingClips.Clear();
			}
			string text = RavePlaylistManager.AllTrackPaths[0];
			if (File.Exists(text))
			{
				LogTrackSizeWarning(text);
				BackgroundDecodeTrack(text);
				RaveMod.Logger.Msg("First track queued for background decode: " + Path.GetFileNameWithoutExtension(text));
			}
			RaveMod.Logger.Msg($"Playlist initialized: {RavePlaylistManager.TrackCount} tracks (async loading).");
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("PreloadAudio error: " + ex.Message);
		}
	}

	internal static void StartMusic()
	{
		if (!((Object)(object)_audioSource == (Object)null))
		{
			if (RavePlaylistManager.TrackCount == 0)
			{
				RaveMod.Logger.Warning("No tracks in playlist, rave runs without music.");
			}
			else
			{
				LoadAndPlayNextTrack();
			}
		}
	}

	internal static void StopMusic()
	{
		_isPaused = false;
		if (_isPlaying && (Object)(object)_audioSource != (Object)null)
		{
			try
			{
				_audioSource.Stop();
			}
			catch
			{
			}
			_isPlaying = false;
		}
		if (_stageAudioObjects.Count <= 0)
		{
			return;
		}
		try
		{
			foreach (GameObject stageAudioObject in _stageAudioObjects)
			{
				if (!((Object)(object)stageAudioObject == (Object)null))
				{
					AudioSource component = stageAudioObject.GetComponent<AudioSource>();
					if ((Object)(object)component != (Object)null)
					{
						component.Stop();
					}
				}
			}
		}
		catch
		{
		}
	}

	private static void SyncStageAudioSource(AudioClip clip)
	{
		if (_stageAudioObjects.Count == 0 || (Object)(object)clip == (Object)null || (Object)(object)_audioSource == (Object)null)
		{
			return;
		}
		try
		{
			int timeSamples = _audioSource.timeSamples;
			foreach (GameObject stageAudioObject in _stageAudioObjects)
			{
				if (!((Object)(object)stageAudioObject == (Object)null))
				{
					AudioSource component = stageAudioObject.GetComponent<AudioSource>();
					if (!((Object)(object)component == (Object)null))
					{
						component.clip = clip;
						component.volume = RaveConfig.MusicVolume.Value * 0.25f;
						component.timeSamples = timeSamples;
						component.Play();
					}
				}
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("[Stage] SyncStageAudioSource failed: " + ex.Message);
		}
	}

	private static void PausePlayback()
	{
		if ((Object)(object)_audioSource != (Object)null)
		{
			try
			{
				_audioSource.Pause();
			}
			catch
			{
			}
		}
		foreach (GameObject stageAudioObject in _stageAudioObjects)
		{
			if ((Object)(object)stageAudioObject == (Object)null)
			{
				continue;
			}
			try
			{
				AudioSource component = stageAudioObject.GetComponent<AudioSource>();
				if ((Object)(object)component != (Object)null)
				{
					component.Pause();
				}
			}
			catch
			{
			}
		}
	}

	private static void UnPausePlayback()
	{
		if ((Object)(object)_audioSource != (Object)null)
		{
			try
			{
				_audioSource.UnPause();
			}
			catch
			{
			}
		}
		foreach (GameObject stageAudioObject in _stageAudioObjects)
		{
			if ((Object)(object)stageAudioObject == (Object)null)
			{
				continue;
			}
			try
			{
				AudioSource component = stageAudioObject.GetComponent<AudioSource>();
				if ((Object)(object)component != (Object)null)
				{
					component.UnPause();
				}
			}
			catch
			{
			}
		}
		ResyncStageToMain();
	}

	private static void ResyncStageToMain()
	{
		if (_stageAudioObjects.Count == 0 || (Object)(object)_audioSource == (Object)null)
		{
			return;
		}
		try
		{
			AudioClip clip = _audioSource.clip;
			if ((Object)(object)clip == (Object)null)
			{
				return;
			}
			int timeSamples = _audioSource.timeSamples;
			if (timeSamples < 0 || timeSamples >= clip.samples)
			{
				return;
			}
			foreach (GameObject stageAudioObject in _stageAudioObjects)
			{
				if (!((Object)(object)stageAudioObject == (Object)null))
				{
					AudioSource component = stageAudioObject.GetComponent<AudioSource>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.clip == (Object)null) && component.isPlaying && timeSamples < component.clip.samples && Mathf.Abs(component.timeSamples - timeSamples) > 441)
					{
						component.timeSamples = timeSamples;
					}
				}
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("[Stage] ResyncStageToMain failed: " + ex.Message);
		}
	}

	private static void DestroyStageAudioObjects()
	{
		if (_stageAudioObjects.Count == 0)
		{
			return;
		}
		foreach (GameObject stageAudioObject in _stageAudioObjects)
		{
			if (!((Object)(object)stageAudioObject == (Object)null))
			{
				try
				{
					Object.DestroyImmediate((Object)(object)stageAudioObject);
				}
				catch
				{
				}
			}
		}
		_stageAudioObjects.Clear();
	}

	private static void CacheClip(string trackPath, AudioClip clip)
	{
		if (trackPath == null || (Object)(object)clip == (Object)null)
		{
			return;
		}
		if (_clipCache.ContainsKey(trackPath))
		{
			_cacheOrder.Remove(trackPath);
			_cacheOrder.AddLast(trackPath);
			return;
		}
		while (_clipCache.Count >= 50 && _cacheOrder.Count > 0)
		{
			string value = _cacheOrder.First.Value;
			_cacheOrder.RemoveFirst();
			if ((Object)(object)_audioSource != (Object)null && _clipCache.ContainsKey(value) && (Object)(object)_clipCache[value] == (Object)(object)_audioSource.clip)
			{
				_cacheOrder.AddLast(value);
				break;
			}
			_clipCache.Remove(value);
		}
		((Object)clip).hideFlags = (HideFlags)32;
		_clipCache[trackPath] = clip;
		_cacheOrder.AddLast(trackPath);
	}

	private static AudioClip GetCachedClip(string trackPath)
	{
		if (trackPath == null)
		{
			return null;
		}
		if (_clipCache.TryGetValue(trackPath, out var value) && (Object)(object)value != (Object)null)
		{
			_cacheOrder.Remove(trackPath);
			_cacheOrder.AddLast(trackPath);
			return value;
		}
		return null;
	}

	internal static void UpdateAudio()
	{
		if ((Object)(object)_audioSource == (Object)null)
		{
			return;
		}
		if (Time.timeScale == 0f)
		{
			if (_isPlaying && !_isPaused)
			{
				PausePlayback();
				_isPaused = true;
			}
			return;
		}
		if (_isPaused)
		{
			UnPausePlayback();
			_isPaused = false;
		}
		if (_isPlaying)
		{
			_audioSource.volume = RaveConfig.MusicVolume.Value;
			if (_stageAudioObjects.Count > 0)
			{
				float volume = RaveConfig.MusicVolume.Value * 0.25f;
				foreach (GameObject stageAudioObject in _stageAudioObjects)
				{
					if (!((Object)(object)stageAudioObject == (Object)null))
					{
						AudioSource component = stageAudioObject.GetComponent<AudioSource>();
						if ((Object)(object)component != (Object)null)
						{
							component.volume = volume;
						}
					}
				}
			}
		}
		ProcessPendingClips();
		if (_isPlaying && !_audioSource.isPlaying && !_audioSource.loop)
		{
			LoadAndPlayNextTrack();
		}
		_vanillaScanTimer += Time.deltaTime;
		if (_vanillaScanTimer >= 10f)
		{
			_vanillaScanTimer = 0f;
			ScanVanillaAudioSources();
		}
		MuteVanillaMusicInRadius();
	}

	private static void LoadAndPlayNextTrack()
	{
		if (_waitingForTrack)
		{
			return;
		}
		int trackCount = RavePlaylistManager.TrackCount;
		int num = 0;
		while (num < trackCount)
		{
			num++;
			try
			{
				string nextTrack = RavePlaylistManager.GetNextTrack();
				if (nextTrack == null)
				{
					RaveMod.Logger.Warning("No more tracks available.");
					_isPlaying = false;
					return;
				}
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(nextTrack);
				AudioClip cachedClip = GetCachedClip(nextTrack);
				if ((Object)(object)cachedClip != (Object)null && (Object)(object)_audioSource != (Object)null)
				{
					_audioSource.clip = cachedClip;
					_audioSource.volume = RaveConfig.MusicVolume.Value;
					_audioSource.Play();
					SyncStageAudioSource(cachedClip);
					_isPlaying = true;
					_isPaused = false;
					if (RaveConfig.DebugCoordinates.Value)
					{
						RaveMod.Logger.Msg("[DEBUG] Now playing: " + fileNameWithoutExtension);
					}
					TriggerLookAhead();
				}
				else
				{
					LogTrackSizeWarning(nextTrack);
					_waitingTrackPath = nextTrack;
					_waitingForTrack = true;
					BackgroundDecodeTrack(nextTrack);
					RaveMod.Logger.Msg("Track not cached — background decoding: " + fileNameWithoutExtension);
				}
				return;
			}
			catch (Exception ex)
			{
				RaveMod.Logger.Error("Audio loading error: " + ex.Message);
			}
		}
		RaveMod.Logger.Warning("No playable tracks found.");
		_isPlaying = false;
	}

	private static AudioClip LoadWavFromFile(string filePath)
	{
		try
		{
			byte[] array = File.ReadAllBytes(filePath);
			if (array.Length < 44)
			{
				return null;
			}
			int num = BitConverter.ToInt16(array, 22);
			int num2 = BitConverter.ToInt32(array, 24);
			int num3 = BitConverter.ToInt16(array, 34);
			int i;
			int num4;
			for (i = 12; i < array.Length - 8; i += 8 + num4)
			{
				string text = Encoding.ASCII.GetString(array, i, 4);
				num4 = BitConverter.ToInt32(array, i + 4);
				if (text == "data")
				{
					i += 8;
					break;
				}
			}
			if (i >= array.Length)
			{
				return null;
			}
			int num5 = array.Length - i;
			int num6 = num3 switch
			{
				16 => num5 / 2, 
				24 => num5 / 3, 
				_ => num5 / (num3 / 8), 
			};
			float[] array2 = new float[num6];
			switch (num3)
			{
			case 16:
			{
				for (int k = 0; k < num6; k++)
				{
					int num9 = i + k * 2;
					if (num9 + 1 >= array.Length)
					{
						break;
					}
					short num10 = BitConverter.ToInt16(array, num9);
					array2[k] = (float)num10 / 32768f;
				}
				break;
			}
			case 24:
			{
				for (int j = 0; j < num6; j++)
				{
					int num7 = i + j * 3;
					if (num7 + 2 >= array.Length)
					{
						break;
					}
					int num8 = array[num7] | (array[num7 + 1] << 8) | (array[num7 + 2] << 16);
					if ((num8 & 0x800000) != 0)
					{
						num8 |= -16777216;
					}
					array2[j] = (float)num8 / 8388608f;
				}
				break;
			}
			}
			AudioClip val = AudioClip.Create(Path.GetFileNameWithoutExtension(filePath), num6 / num, num, num2, false);
			val.SetData(Il2CppStructArray<float>.op_Implicit(array2), 0);
			return val;
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("WAV loading error: " + ex.Message);
			return null;
		}
	}

	private static AudioClip LoadMp3WithNLayer(string filePath)
	{
		try
		{
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
			using MpegFile mpegFile = new MpegFile(filePath);
			int sampleRate = mpegFile.SampleRate;
			int channels = mpegFile.Channels;
			if (sampleRate <= 0 || channels <= 0)
			{
				RaveMod.Logger.Warning($"Invalid MP3 metadata: {fileNameWithoutExtension} (Rate={sampleRate}, Ch={channels})");
				return null;
			}
			long num = (long)(mpegFile.Duration.TotalSeconds * (double)sampleRate * (double)channels) + 4096;
			if (num > int.MaxValue)
			{
				num = 2147483647L;
			}
			float[] array = new float[(int)num];
			int num2 = 0;
			while (true)
			{
				if (num2 >= array.Length)
				{
					float[] array2 = new float[array.Length * 2];
					Array.Copy(array, array2, num2);
					array = array2;
				}
				int num3 = mpegFile.ReadSamples(array, num2, array.Length - num2);
				if (num3 <= 0)
				{
					break;
				}
				num2 += num3;
			}
			if (num2 == 0)
			{
				RaveMod.Logger.Warning("NLayer returned no samples: " + fileNameWithoutExtension);
				return null;
			}
			float[] array3;
			if (num2 == array.Length)
			{
				array3 = array;
			}
			else
			{
				array3 = new float[num2];
				Array.Copy(array, array3, num2);
			}
			int num4 = num2 / channels;
			AudioClip val = AudioClip.Create(fileNameWithoutExtension, num4, channels, sampleRate, false);
			val.SetData(Il2CppStructArray<float>.op_Implicit(array3), 0);
			return val;
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("MP3 decoding failed: " + ex.Message);
			return null;
		}
	}

	private static void DecodeWavToSamples(string filePath, out float[] samples, out int sampleRate, out int channels)
	{
		samples = null;
		sampleRate = 0;
		channels = 0;
		byte[] array = File.ReadAllBytes(filePath);
		if (array.Length < 44)
		{
			return;
		}
		channels = BitConverter.ToInt16(array, 22);
		sampleRate = BitConverter.ToInt32(array, 24);
		int num = BitConverter.ToInt16(array, 34);
		int i;
		int num2;
		for (i = 12; i < array.Length - 8; i += 8 + num2)
		{
			string text = Encoding.ASCII.GetString(array, i, 4);
			num2 = BitConverter.ToInt32(array, i + 4);
			if (text == "data")
			{
				i += 8;
				break;
			}
		}
		if (i >= array.Length)
		{
			return;
		}
		int num3 = array.Length - i;
		int num4 = num switch
		{
			16 => num3 / 2, 
			24 => num3 / 3, 
			_ => num3 / (num / 8), 
		};
		samples = new float[num4];
		switch (num)
		{
		case 16:
		{
			for (int k = 0; k < num4; k++)
			{
				int num7 = i + k * 2;
				if (num7 + 1 >= array.Length)
				{
					break;
				}
				short num8 = BitConverter.ToInt16(array, num7);
				samples[k] = (float)num8 / 32768f;
			}
			break;
		}
		case 24:
		{
			for (int j = 0; j < num4; j++)
			{
				int num5 = i + j * 3;
				if (num5 + 2 >= array.Length)
				{
					break;
				}
				int num6 = array[num5] | (array[num5 + 1] << 8) | (array[num5 + 2] << 16);
				if ((num6 & 0x800000) != 0)
				{
					num6 |= -16777216;
				}
				samples[j] = (float)num6 / 8388608f;
			}
			break;
		}
		}
	}

	private static void DecodeMp3ToSamples(string filePath, out float[] samples, out int sampleRate, out int channels)
	{
		samples = null;
		sampleRate = 0;
		channels = 0;
		using MpegFile mpegFile = new MpegFile(filePath);
		sampleRate = mpegFile.SampleRate;
		channels = mpegFile.Channels;
		if (sampleRate <= 0 || channels <= 0)
		{
			return;
		}
		long num = (long)(mpegFile.Duration.TotalSeconds * (double)sampleRate * (double)channels) + 4096;
		if (num > int.MaxValue)
		{
			num = 2147483647L;
		}
		float[] array = new float[(int)num];
		int num2 = 0;
		while (true)
		{
			if (num2 >= array.Length)
			{
				float[] array2 = new float[array.Length * 2];
				Array.Copy(array, array2, num2);
				array = array2;
			}
			int num3 = mpegFile.ReadSamples(array, num2, array.Length - num2);
			if (num3 <= 0)
			{
				break;
			}
			num2 += num3;
		}
		if (num2 != 0)
		{
			if (num2 == array.Length)
			{
				samples = array;
				return;
			}
			samples = new float[num2];
			Array.Copy(array, samples, num2);
		}
	}

	private static void BackgroundDecodeTrack(string trackPath)
	{
		if (trackPath == null || _clipCache.ContainsKey(trackPath) || _backgroundDecodeCount >= 2)
		{
			return;
		}
		lock (_pendingLock)
		{
			foreach (PendingClip pendingClip in _pendingClips)
			{
				if (pendingClip.TrackPath == trackPath)
				{
					return;
				}
			}
		}
		Interlocked.Increment(ref _backgroundDecodeCount);
		ThreadPool.QueueUserWorkItem(delegate
		{
			try
			{
				string fullPath = Path.GetFullPath(trackPath);
				string text = Path.GetExtension(trackPath).ToLower();
				float[] samples;
				int sampleRate;
				int channels;
				if (text == ".mp3")
				{
					DecodeMp3ToSamples(fullPath, out samples, out sampleRate, out channels);
				}
				else
				{
					if (!(text == ".wav"))
					{
						return;
					}
					DecodeWavToSamples(fullPath, out samples, out sampleRate, out channels);
				}
				if (samples == null || samples.Length == 0 || sampleRate <= 0 || channels <= 0)
				{
					return;
				}
				PendingClip item = default(PendingClip);
				item.TrackPath = trackPath;
				item.Samples = samples;
				item.SampleRate = sampleRate;
				item.Channels = channels;
				lock (_pendingLock)
				{
					_pendingClips.Enqueue(item);
				}
			}
			catch (Exception ex)
			{
				RaveMod.Logger.Warning("Background decode error: " + ex.Message);
			}
			finally
			{
				Interlocked.Decrement(ref _backgroundDecodeCount);
			}
		});
	}

	private static void ProcessPendingClips()
	{
		PendingClip pendingClip;
		lock (_pendingLock)
		{
			if (_pendingClips.Count == 0)
			{
				return;
			}
			pendingClip = _pendingClips.Dequeue();
		}
		if (_clipCache.ContainsKey(pendingClip.TrackPath))
		{
			return;
		}
		try
		{
			int num = pendingClip.Samples.Length / pendingClip.Channels;
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(pendingClip.TrackPath);
			AudioClip val = AudioClip.Create(fileNameWithoutExtension, num, pendingClip.Channels, pendingClip.SampleRate, false);
			val.SetData(Il2CppStructArray<float>.op_Implicit(pendingClip.Samples), 0);
			CacheClip(pendingClip.TrackPath, val);
			if (RaveConfig.DebugCoordinates.Value)
			{
				RaveMod.Logger.Msg($"[DEBUG] Background-decoded and cached: {fileNameWithoutExtension} ({val.length:F0}s)");
			}
			if (_waitingForTrack && _waitingTrackPath == pendingClip.TrackPath && (Object)(object)_audioSource != (Object)null)
			{
				_audioSource.clip = val;
				_audioSource.volume = RaveConfig.MusicVolume.Value;
				_audioSource.Play();
				SyncStageAudioSource(val);
				_isPlaying = true;
				_isPaused = false;
				_waitingForTrack = false;
				_waitingTrackPath = null;
				RaveMod.Logger.Msg("Now playing (async loaded): " + fileNameWithoutExtension);
				TriggerLookAhead();
			}
		}
		catch (Exception ex)
		{
			if (_waitingForTrack && _waitingTrackPath == pendingClip.TrackPath)
			{
				_waitingForTrack = false;
				_waitingTrackPath = null;
			}
			RaveMod.Logger.Warning("ProcessPendingClips error: " + ex.Message);
		}
	}

	private static void TriggerLookAhead()
	{
		string text = RavePlaylistManager.PeekNextTrack();
		if (text != null)
		{
			BackgroundDecodeTrack(text);
		}
	}

	private static void LogTrackSizeWarning(string trackPath)
	{
		try
		{
			long length = new FileInfo(trackPath).Length;
			if (length > 52428800)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(trackPath);
				float value = (float)length / 1048576f;
				RaveMod.Logger.Warning($"Large audio file: {fileNameWithoutExtension} ({value:F0} MB). Very long tracks use a lot of RAM. Consider shorter files (<15 min).");
			}
		}
		catch
		{
		}
	}

	private static void ScanVanillaAudioSources()
	{
		try
		{
			AudioSource[] array = Il2CppArrayBase<AudioSource>.op_Implicit(Object.FindObjectsOfType<AudioSource>());
			if (array == null)
			{
				_cachedVanillaSources = null;
				return;
			}
			HashSet<AudioSource> hashSet = new HashSet<AudioSource>();
			foreach (GameObject stageAudioObject in _stageAudioObjects)
			{
				if (!((Object)(object)stageAudioObject == (Object)null))
				{
					AudioSource component = stageAudioObject.GetComponent<AudioSource>();
					if ((Object)(object)component != (Object)null)
					{
						hashSet.Add(component);
					}
				}
			}
			List<AudioSource> list = new List<AudioSource>();
			foreach (AudioSource val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_audioSource) && !hashSet.Contains(val))
				{
					list.Add(val);
				}
			}
			_cachedVanillaSources = list.ToArray();
			if (RaveConfig.DebugCoordinates.Value)
			{
				RaveMod.Logger.Msg($"[DEBUG] Vanilla AudioSources scanned: {_cachedVanillaSources.Length} found");
			}
		}
		catch (Exception ex)
		{
			_cachedVanillaSources = null;
			RaveMod.Logger.Warning("ScanVanillaAudioSources error: " + ex.Message);
		}
	}

	private static void MuteVanillaMusicInRadius()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (_cachedVanillaSources == null)
			{
				return;
			}
			Vector3 centerPosition = RaveLocationManager.CenterPosition;
			float value = RaveConfig.EventRadius.Value;
			float num = value * value;
			int num2 = 0;
			for (int i = 0; i < _cachedVanillaSources.Length; i++)
			{
				AudioSource val = _cachedVanillaSources[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Vector3 val2 = ((Component)val).transform.position - centerPosition;
				float num3 = Vector3.Dot(val2, val2);
				if (num3 <= num)
				{
					if (!_mutedSources.ContainsKey(val) && val.volume > 0f)
					{
						_mutedSources[val] = val.volume;
						val.volume = 0f;
						num2++;
					}
				}
				else if (_mutedSources.ContainsKey(val))
				{
					val.volume = _mutedSources[val];
					_mutedSources.Remove(val);
				}
			}
			if (num2 > 0 && RaveConfig.DebugCoordinates.Value)
			{
				RaveMod.Logger.Msg($"[DEBUG] {num2} neue Vanilla-AudioSources stummgeschaltet (gesamt: {_mutedSources.Count})");
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("MuteVanillaMusicInRadius error: " + ex.Message);
		}
	}

	private static void RestoreMutedSources()
	{
		foreach (KeyValuePair<AudioSource, float> mutedSource in _mutedSources)
		{
			try
			{
				if ((Object)(object)mutedSource.Key != (Object)null)
				{
					mutedSource.Key.volume = mutedSource.Value;
				}
			}
			catch
			{
			}
		}
		_mutedSources.Clear();
	}

	internal static void RelocateToPosition(Vector3 newCenter)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: 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_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)_audioObject != (Object)null)
			{
				_audioObject.transform.position = newCenter;
			}
			if (_speakerVisuals == null)
			{
				return;
			}
			Vector3 val = default(Vector3);
			for (int i = 0; i < _speakerVisuals.Length; i++)
			{
				if (!((Object)(object)_speakerVisuals[i] == (Object)null))
				{
					float num = (float)Math.PI / 2f * (float)i;
					((Vector3)(ref val))..ctor(newCenter.x + Mathf.Cos(num) * 2.5f, newCenter.y, newCenter.z + Mathf.Sin(num) * 2.5f);
					_speakerVisuals[i].transform.position = val;
					Vector3 val2 = newCenter - val;
					val2.y = 0f;
					if (Vector3.Dot(val2, val2) > 0.01f)
					{
						_speakerVisuals[i].transform.rotation = Quaternion.LookRotation(val2);
					}
				}
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("RelocateAudio error: " + ex.Message);
		}
	}

	internal static void Cleanup()
	{
		StopMusic();
		RestoreMutedSources();
		if ((Object)(object)_audioObject != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)_audioObject);
			_audioObject = null;
		}
		DestroyStageAudioObjects();
		if (_speakerVisuals != null)
		{
			for (int i = 0; i < _speakerVisuals.Length; i++)
			{
				if ((Object)(object)_speakerVisuals[i] != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)_speakerVisuals[i]);
				}
			}
			_speakerVisuals = null;
		}
		RavePlaylistManager.Cleanup();
		_audioSource = null;
		_isPlaying = false;
		_isPaused = false;
		_cachedVanillaSources = null;
		_vanillaScanTimer = 0f;
		lock (_pendingLock)
		{
			_pendingClips.Clear();
		}
	}

	private static void DestroyStaleObjects()
	{
		try
		{
			GameObject[] array = Il2CppArrayBase<GameObject>.op_Implicit(Object.FindObjectsOfType<GameObject>());
			if (array == null)
			{
				return;
			}
			foreach (GameObject val in array)
			{
				if ((Object)(object)val == (Object)null || ((Object)val).name == null || !((Object)val).name.StartsWith("RaveSpeaker"))
				{
					continue;
				}
				bool flag = (Object)(object)val == (Object)(object)_audioObject;
				if (!flag && _speakerVisuals != null)
				{
					for (int j = 0; j < _speakerVisuals.Length; j++)
					{
						if ((Object)(object)val == (Object)(object)_speakerVisuals[j])
						{
							flag = true;
							break;
						}
					}
				}
				if (!flag)
				{
					flag = _stageAudioObjects.Contains(val);
				}
				if (!flag)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("Audio DestroyStaleObjects error: " + ex.Message);
		}
	}
}
public sealed class RaveCompassController
{
	[HarmonyPatch(typeof(CompassManager), "LateUpdate")]
	private static class CompassExtendedRangePatch
	{
		[HarmonyPostfix]
		private static void Postfix(CompassManager __instance)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0103: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_compassElement == null || (Object)(object)_compassElement.TargetTransform == (Object)null || !_compassElement.Visible)
				{
					return;
				}
				Transform cam = __instance.cam;
				if (!((Object)(object)cam == (Object)null))
				{
					Vector3 position = cam.position;
					Vector3 position2 = _compassElement.TargetTransform.position;
					float num = Vector3.Distance(position, position2);
					if (!(num > 300f))
					{
						float num2 = default(float);
						float num3 = default(float);
						__instance.GetCompassData(position2, ref num2, ref num3);
						float fullAlphaRange = __instance.FullAlphaRange;
						float alpha = ((!(num <= fullAlphaRange)) ? Mathf.Lerp(1f, 0.15f, (num - fullAlphaRange) / (300f - fullAlphaRange)) : 1f);
						_compassElement.Group.alpha = alpha;
						_compassElement.Rect.anchoredPosition = new Vector2(num2, 0f);
						((TMP_Text)_compassElement.DistanceLabel).text = Mathf.CeilToInt(num) + "m";
						((Component)_compassElement.Rect).gameObject.SetActive(true);
					}
				}
			}
			catch (Exception)
			{
			}
		}
	}

	private static GameObject _anchorGO;

	private static GameObject _contentGO;

	private static Element _compassElement;

	private static Sprite _iconSprite;

	private const string ANCHOR_NAME = "RaveCompassAnchor";

	private const string RESOURCE_NAME = "rave.assets.Graffiti_TLH.png";

	private const float EXTENDED_RANGE = 300f;

	private const float MIN_ALPHA = 0.15f;

	private RaveCompassController()
	{
	}

	private static bool EnsureIconLoaded()
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_iconSprite != (Object)null)
		{
			return true;
		}
		try
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("rave.assets.Graffiti_TLH.png");
			if (stream == null)
			{
				RaveMod.Logger.Error("Compass icon not found: rave.assets.Graffiti_TLH.png");
				return false;
			}
			byte[] array;
			using (MemoryStream memoryStream = new MemoryStream((int)stream.Length))
			{
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array)))
			{
				RaveMod.Logger.Error("Compass icon could not be loaded as Texture2D.");
				return false;
			}
			((Texture)val).filterMode = (FilterMode)1;
			_iconSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("Error loading compass icon: " + ex.Message);
			return false;
		}
		return true;
	}

	internal static void Show()
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Expected O, but got Unknown
		//IL_008d: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!EnsureIconLoaded())
			{
				return;
			}
			CompassManager instance = Singleton<CompassManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				RaveMod.Logger.Warning("CompassManager not found, no compass icon.");
				return;
			}
			Hide();
			_anchorGO = new GameObject("RaveCompassAnchor");
			_anchorGO.transform.position = RaveLocationManager.CenterPosition;
			Object.DontDestroyOnLoad((Object)(object)_anchorGO);
			_contentGO = new GameObject("RaveCompassContent");
			RectTransform val = _contentGO.AddComponent<RectTransform>();
			val.sizeDelta = instance.ElementContentSize * 2.5f;
			Image val2 = _contentGO.AddComponent<Image>();
			val2.sprite = _iconSprite;
			val2.preserveAspect = true;
			_compassElement = instance.AddElement(_anchorGO.transform, val, true);
			if (_compassElement != null)
			{
				((Transform)val).localScale = new Vector3(2.5f, 2.5f, 1f);
			}
			else
			{
				RaveMod.Logger.Warning("CompassManager.AddElement returned null.");
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Error("Error showing compass icon: " + ex.Message);
		}
	}

	internal static void Hide()
	{
		try
		{
			if (_compassElement != null && (Object)(object)_anchorGO != (Object)null)
			{
				CompassManager instance = Singleton<CompassManager>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.RemoveElement(_anchorGO.transform, true);
				}
				_compassElement = null;
			}
			if ((Object)(object)_contentGO != (Object)null)
			{
				Object.Destroy((Object)(object)_contentGO);
				_contentGO = null;
			}
			if ((Object)(object)_anchorGO != (Object)null)
			{
				Object.Destroy((Object)(object)_anchorGO);
				_anchorGO = null;
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("Error removing compass icon: " + ex.Message);
		}
	}

	internal static void Cleanup()
	{
		Hide();
	}
}
public sealed class RaveConfig
{
	internal sealed class FixedEntry<T>
	{
		internal T Value { get; }

		internal FixedEntry(T v)
		{
			Value = v;
		}
	}

	private const string RuntimeStateFileName = "runtime_state.cfg";

	private const string StateDataFileName = "rave_state.dat";

	private const string StateDataHeader = "IllegalRaveStateV1";

	private const long MaxStateDataFileBytes = 16384L;

	internal static MelonPreferences_Category Category;

	internal static MelonPreferences_Entry<bool> SmallFestivalStageEnabled;

	internal static MelonPreferences_Entry<int> RaveStartTime;

	internal const bool EndOnSleep = true;

	internal const int FirstMessageTime = 2300;

	internal static readonly FixedEntry<float> EventRadius = new FixedEntry<float>(60f);

	internal static readonly FixedEntry<bool> DebugCoordinates = new FixedEntry<bool>(v: false);

	private static readonly string CustomLocationsFile = Path.Combine("UserData", "Rave", "locations.cfg");

	private static readonly string RuntimeStateFile = Path.Combine("UserData", "Rave", "runtime_state.cfg");

	private static readonly string StateDataFile = Path.Combine("UserData", "Rave", "rave_state.dat");

	private static string _customLocationsData = "";

	private static bool _resumeRaveActive;

	private static int _resumeRaveDay = -1;

	private static string _resumePresetName = "";

	private static bool _resumeBookedDjStateCaptured;

	private static bool _resumeDjDanBooked;

	private static bool _resumeDjGirlBooked;

	private static bool _resumeDjCutChemistBooked;

	private static bool _resumeDjLudapimpBooked;

	internal static MelonPreferences_Entry<int> CustomRaveLocation;

	internal static MelonPreferences_Entry<bool> PresetTriggerChanceEnabled;

	private static MelonPreferences_Entry<string>[] _customSlots;

	internal static MelonPreferences_Entry<float> MusicVolume;

	internal static MelonPreferences_Entry<int> LampCount;

	internal static MelonPreferences_Entry<float> StrobeSpeed;

	internal static MelonPreferences_Entry<float> LightIntensity;

	internal static MelonPreferences_Entry<float> LightRange;

	internal static MelonPreferences_Entry<bool> StrobeEnabled;

	internal static MelonPreferences_Entry<float> StrobeInterval;

	internal static MelonPreferences_Entry<float> StrobeDuration;

	internal static MelonPreferences_Entry<int> LampColorR;

	internal static MelonPreferences_Entry<int> LampColorG;

	internal static MelonPreferences_Entry<int> LampColorB;

	internal static MelonPreferences_Entry<bool> LampRotation;

	internal static bool DJDanBooked;

	internal static bool DJGirlBooked;

	internal static bool DJCutChemistBooked;

	internal static bool DJLudapimpBooked;

	internal static MelonPreferences_Entry<bool> BundleDebugLogging;

	internal static MelonPreferences_Entry<int> NPCCount;

	internal static MelonPreferences_Entry<float> DanceSpeed;

	internal static MelonPreferences_Entry<float> DanceRadius;

	internal static int SecondMessageTime
	{
		get
		{
			int value = RaveStartTime.Value;
			int num = value / 100;
			int num2 = value % 100;
			num2 -= 5;
			if (num2 < 0)
			{
				num2 += 60;
				num--;
			}
			if (num < 0)
			{
				num += 24;
			}
			return num * 100 + num2;
		}
	}

	internal static string CustomLocationsValue
	{
		get
		{
			return _customLocationsData;
		}
		set
		{
			_customLocationsData = value ?? "";
			SaveCustomLocations();
		}
	}

	private RaveConfig()
	{
	}

	internal static void Initialize()
	{
		Category = MelonPreferences.CreateCategory("Illegal Rave: Settings", "Rave Settings Update");
		SmallFestivalStageEnabled = Category.CreateEntry<bool>("SmallFestivalStageEnabled", true, "Enable Small Rave Stage (Parkinglot only - trigger 100%)", (string)null, false, false, (ValueValidator)null, (string)null);
		RaveStartTime = Category.CreateEntry<int>("RaveStartTime", 400, "Rave Start Time (HHMM)", "When the rave starts (100 = 01:00 to 400 = 04:00).", false, false, (ValueValidator)null, (string)null);
		LoadStateData();
		CustomRaveLocation = Category.CreateEntry<int>("CustomRaveLocation", 0, "Custom Rave", "Custom location slot (1-3). 0=use a random preset location.", false, false, (ValueValidator)null, (string)null);
		PresetTriggerChanceEnabled = Category.CreateEntry<bool>("PresetTriggerChanceEnabled", false, "Rave Trigger Chance", "Preset nights use the 30% trigger chance. When disabled, preset nights always happen. Custom locations (1-3) always trigger 100%.", false, false, (ValueValidator)null, (string)null);
		_customSlots = new MelonPreferences_Entry<string>[3];
		string[] slotCoordinates = GetSlotCoordinates();
		for (int i = 0; i < _customSlots.Length; i++)
		{
			string text = ((slotCoordinates[i] != null) ? "saved" : "free");
			_customSlots[i] = Category.CreateEntry<string>($"Custom{i + 1}", text, $"Custom{i + 1}", slotCoordinates[i] ?? "", false, false, (ValueValidator)null, (string)null);
			_customSlots[i].Value = text;
		}
		MusicVolume = Category.CreateEntry<float>("MusicVolume", 0.7f, "Music Volume", "Rave music volume (0.0 - 1.5)", false, false, (ValueValidator)null, (string)null);
		LampCount = Category.CreateEntry<int>("LampCount", 6, "Lamp Count", "Number of standing lamps placed around the rave", false, false, (ValueValidator)null, (string)null);
		StrobeSpeed = Category.CreateEntry<float>("StrobeSpeed", 4f, "Color Rotation", "Color change speed of the lamps (Hz)", false, false, (ValueValidator)null, (string)null);
		LightIntensity = Category.CreateEntry<float>("LightIntensity", 5f, "Light Intensity", "Brightness of the rave lamps", false, false, (ValueValidator)null, (string)null);
		LightRange = Category.CreateEntry<float>("LightRange", 10f, "Light Range", "Range of the lamps", false, false, (ValueValidator)null, (string)null);
		StrobeEnabled = Category.CreateEntry<bool>("StrobeEnabled", true, "Strobo Flash", "White flashes in addition to color cycling", false, false, (ValueValidator)null, (string)null);
		StrobeInterval = Category.CreateEntry<float>("StrobeInterval", 0.5f, "Strobo Steps", "Time between flashes in seconds (0.1-0.5)", false, false, (ValueValidator)null, (string)null);
		StrobeDuration = Category.CreateEntry<float>("StrobeDuration", 0.01f, "Strobo Time", "Duration of a flash in seconds (0.01-0.1)", false, false, (ValueValidator)null, (string)null);
		LampColorR = Category.CreateEntry<int>("LampColorR", 255, "Lamp Color Red", "Red channel of lamp light color (0-255). Set all to 0 for rainbow mode.", false, false, (ValueValidator)null, (string)null);
		LampColorG = Category.CreateEntry<int>("LampColorG", 0, "Lamp Color Green", "Green channel of lamp light color (0-255). Set all to 0 for rainbow mode.", false, false, (ValueValidator)null, (string)null);
		LampColorB = Category.CreateEntry<int>("LampColorB", 255, "Lamp Color Blue", "Blue channel of lamp light color (0-255). Set all to 0 for rainbow mode.", false, false, (ValueValidator)null, (string)null);
		LampRotation = Category.CreateEntry<bool>("LampRotation", true, "Lamp Color Rotation", "Whether lamp colors rotate through the spectrum (true) or stay fixed (false)", false, false, (ValueValidator)null, (string)null);
		BundleDebugLogging = Category.CreateEntry<bool>("BundleDebugLogging", false, "Debug: Bundle Diagnostics", "Enable massive diagnostics logging for DJ bundle load/cleanup/retry flow. Keep OFF for normal gameplay.", false, false, (ValueValidator)null, (string)null);
		NPCCount = Category.CreateEntry<int>("NPCCount", 8, "NPC Count (Custom)", "Number of dancing NPCs at custom locations (presets have fixed values)", false, false, (ValueValidator)null, (string)null);
		DanceSpeed = Category.CreateEntry<float>("DanceSpeed", 6f, "Dance Speed (Custom)", "NPC dance speed multiplier at custom locations (1.0=slow, 3.0=normal, 6.0=fast, 10.0=very fast)", false, false, (ValueValidator)null, (string)null);
		DanceRadius = Category.CreateEntry<float>("DanceRadius", 5f, "Dance Radius (Custom)", "Radius in meters where NPCs dance at custom locations (1.0-30.0)", false, false, (ValueValidator)null, (string)null);
		ApplySafetyClamps();
	}

	private static void ApplySafetyClamps()
	{
		ClampSingle(MusicVolume, 0f, 2f);
		ClampInt(LampCount, 1, 20);
		ClampSingle(StrobeSpeed, 0.5f, 20f);
		ClampSingle(LightIntensity, 0.1f, 50f);
		ClampSingle(LightRange, 1f, 100f);
		ClampSingle(StrobeInterval, 0.05f, 5f);
		ClampSingle(StrobeDuration, 0.005f, 1f);
		ClampInt(NPCCount, 0, 30);
		ClampInt(LampColorR, 0, 255);
		ClampInt(LampColorG, 0, 255);
		ClampInt(LampColorB, 0, 255);
		ClampSingle(DanceSpeed, 0.5f, 20f);
		ClampSingle(DanceRadius, 1f, 30f);
		ClampInt(CustomRaveLocation, 0, 3);
		ClampInt(RaveStartTime, 100, 500);
	}

	private static void ClampSingle(MelonPreferences_Entry<float> entry, float min, float max)
	{
		if (entry.Value < min)
		{
			entry.Value = min;
		}
		if (entry.Value > max)
		{
			entry.Value = max;
		}
	}

	private static void ClampInt(MelonPreferences_Entry<int> entry, int min, int max)
	{
		if (entry.Value < min)
		{
			entry.Value = min;
		}
		if (entry.Value > max)
		{
			entry.Value = max;
		}
	}

	internal static void SetBookedDj(bool djDanBooked, bool djGirlBooked, bool djCutChemistBooked = false, bool djLudapimpBooked = false)
	{
		int num = (djDanBooked ? 1 : 0) + (djGirlBooked ? 1 : 0) + (djCutChemistBooked ? 1 : 0) + (djLudapimpBooked ? 1 : 0);
		if (num > 1)
		{
			RaveMod.Logger.Warning($"Invalid DJ booking state requested: {num} DJs booked. Keeping last one only.");
			if (djLudapimpBooked)
			{
				djDanBooked = false;
				djGirlBooked = false;
				djCutChemistBooked = false;
			}
			else if (djCutChemistBooked)
			{
				djDanBooked = false;
				djGirlBooked = false;
				djLudapimpBooked = false;
			}
			else if (djGirlBooked)
			{
				djDanBooked = false;
				djCutChemistBooked = false;
				djLudapimpBooked = false;
			}
		}
		DJDanBooked = djDanBooked;
		DJGirlBooked = djGirlBooked;
		DJCutChemistBooked = djCutChemistBooked;
		DJLudapimpBooked = djLudapimpBooked;
		SaveStateData();
	}

	internal static void ClearBookedDjs()
	{
		DJDanBooked = false;
		DJGirlBooked = false;
		DJCutChemistBooked = false;
		DJLudapimpBooked = false;
		SaveStateData();
	}

	internal static void SetActiveRaveResumeState(bool isActive, int elapsedDays, string presetName, bool djDanBooked, bool djGirlBooked, bool djCutChemistBooked = false, bool djLudapimpBooked = false)
	{
		_resumeRaveActive = isActive;
		_resumeRaveDay = (isActive ? elapsedDays : (-1));
		_resumePresetName = (isActive ? (presetName ?? string.Empty) : string.Empty);
		_resumeBookedDjStateCaptured = isActive;
		_resumeDjDanBooked = isActive && djDanBooked;
		_resumeDjGirlBooked = isActive && djGirlBooked;
		_resumeDjCutChemistBooked = isActive && djCutChemistBooked;
		_resumeDjLudapimpBooked = isActive && djLudapimpBooked;
		int num = (_resumeDjDanBooked ? 1 : 0) + (_resumeDjGirlBooked ? 1 : 0) + (_resumeDjCutChemistBooked ? 1 : 0) + (_resumeDjLudapimpBooked ? 1 : 0);
		if (num > 1)
		{
			RaveMod.Logger.Warning("Invalid active rave resume DJ state requested: multiple DJs booked. Keeping last one only.");
			if (_resumeDjLudapimpBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjGirlBooked = false;
				_resumeDjCutChemistBooked = false;
			}
			else if (_resumeDjCutChemistBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjGirlBooked = false;
				_resumeDjLudapimpBooked = false;
			}
			else if (_resumeDjGirlBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjCutChemistBooked = false;
				_resumeDjLudapimpBooked = false;
			}
		}
		SaveStateData();
	}

	internal static void ClearActiveRaveResumeState()
	{
		if (_resumeRaveActive || _resumeRaveDay >= 0 || !string.IsNullOrEmpty(_resumePresetName) || _resumeBookedDjStateCaptured || _resumeDjDanBooked || _resumeDjGirlBooked || _resumeDjCutChemistBooked || _resumeDjLudapimpBooked)
		{
			_resumeRaveActive = false;
			_resumeRaveDay = -1;
			_resumePresetName = string.Empty;
			_resumeBookedDjStateCaptured = false;
			_resumeDjDanBooked = false;
			_resumeDjGirlBooked = false;
			_resumeDjCutChemistBooked = false;
			_resumeDjLudapimpBooked = false;
			SaveStateData();
		}
	}

	internal static bool TryConsumeActiveRaveResumeState(int currentDay, int currentTime, out string presetName, out bool hasBookedDjState, out bool djDanBooked, out bool djGirlBooked, out bool djCutChemistBooked, out bool djLudapimpBooked)
	{
		presetName = null;
		hasBookedDjState = false;
		djDanBooked = false;
		djGirlBooked = false;
		djCutChemistBooked = false;
		djLudapimpBooked = false;
		if (!_resumeRaveActive)
		{
			return false;
		}
		if (currentDay != _resumeRaveDay || currentTime < RaveStartTime.Value || currentTime >= 500)
		{
			ClearActiveRaveResumeState();
			return false;
		}
		presetName = (string.IsNullOrWhiteSpace(_resumePresetName) ? null : _resumePresetName);
		hasBookedDjState = _resumeBookedDjStateCaptured;
		djDanBooked = _resumeDjDanBooked;
		djGirlBooked = _resumeDjGirlBooked;
		djCutChemistBooked = _resumeDjCutChemistBooked;
		djLudapimpBooked = _resumeDjLudapimpBooked;
		_resumeRaveActive = false;
		_resumeRaveDay = -1;
		_resumePresetName = string.Empty;
		_resumeBookedDjStateCaptured = false;
		_resumeDjDanBooked = false;
		_resumeDjGirlBooked = false;
		_resumeDjCutChemistBooked = false;
		_resumeDjLudapimpBooked = false;
		SaveStateData();
		return true;
	}

	private static void LoadStateData()
	{
		try
		{
			DJDanBooked = false;
			DJGirlBooked = false;
			DJCutChemistBooked = false;
			DJLudapimpBooked = false;
			_customLocationsData = "";
			_resumeRaveActive = false;
			_resumeRaveDay = -1;
			_resumePresetName = string.Empty;
			_resumeBookedDjStateCaptured = false;
			_resumeDjDanBooked = false;
			_resumeDjGirlBooked = false;
			_resumeDjCutChemistBooked = false;
			_resumeDjLudapimpBooked = false;
			if (!TryLoadUnifiedStateData())
			{
				if (TryMigrateLegacyStateData())
				{
					SaveStateData();
				}
				else
				{
					LogRuntimeStateDebug("No state data found. Starting with defaults at '" + StateDataFile + "'.");
				}
			}
		}
		catch (IOException ex)
		{
			RaveMod.Logger.Warning("Failed to load runtime rave state: " + ex.Message);
		}
		catch (UnauthorizedAccessException ex2)
		{
			RaveMod.Logger.Warning("Failed to load runtime rave state: " + ex2.Message);
		}
	}

	private static bool TryLoadUnifiedStateData()
	{
		if (!File.Exists(StateDataFile))
		{
			return false;
		}
		if (!TryReadSmallTextFile(StateDataFile, 16384L, out var content))
		{
			return false;
		}
		Dictionary<string, string> dictionary = ParseStateEntries(content, StateDataFile);
		if (dictionary.Count == 0)
		{
			return false;
		}
		if (!dictionary.TryGetValue("Format", out var value) || !string.Equals(value, "IllegalRaveStateV1", StringComparison.Ordinal))
		{
			RaveMod.Logger.Warning("Unsupported rave state format in '" + StateDataFile + "'.");
			return false;
		}
		ApplyParsedStateEntries(dictionary, StateDataFile);
		LogRuntimeStateDebug($"Loaded unified rave state from '{StateDataFile}': DJDanBooked={DJDanBooked}, DJGirlBooked={DJGirlBooked}, DJCutChemistBooked={DJCutChemistBooked}, DJLudapimpBooked={DJLudapimpBooked}");
		return true;
	}

	private static bool TryMigrateLegacyStateData()
	{
		bool flag = false;
		if (TryReadSmallTextFile(RuntimeStateFile, 16384L, out var content))
		{
			ApplyParsedStateEntries(ParseStateEntries(content, RuntimeStateFile), RuntimeStateFile);
			flag = true;
		}
		if (TryReadSmallTextFile(CustomLocationsFile, 16384L, out var content2))
		{
			_customLocationsData = content2.Trim();
			flag = true;
		}
		if (flag)
		{
			LogRuntimeStateDebug($"Migrated legacy rave state from '{RuntimeStateFile}' and '{CustomLocationsFile}' into '{StateDataFile}'.");
		}
		return flag;
	}

	private static void SaveStateData()
	{
		try
		{
			string directoryName = Path.GetDirectoryName(StateDataFile);
			if (!string.IsNullOrWhiteSpace(directoryName) && !Directory.Exists(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			string text = BuildStateDataContent();
			byte[] bytes = Encoding.UTF8.GetBytes(text);
			if ((long)bytes.Length > 16384L)
			{
				RaveMod.Logger.Warning($"Refusing to save rave state because '{StateDataFile}' would exceed {16384} bytes.");
				return;
			}
			File.WriteAllText(StateDataFile, text, Encoding.UTF8);
			TryDeleteLegacyStateFile(RuntimeStateFile);
			TryDeleteLegacyStateFile(CustomLocationsFile);
			LogRuntimeStateDebug($"Saved unified rave state to '{StateDataFile}': DJDanBooked={DJDanBooked}, DJGirlBooked={DJGirlBooked}, DJCutChemistBooked={DJCutChemistBooked}, DJLudapimpBooked={DJLudapimpBooked}");
		}
		catch (IOException ex)
		{
			RaveMod.Logger.Warning("Failed to save runtime rave state: " + ex.Message);
		}
		catch (UnauthorizedAccessException ex2)
		{
			RaveMod.Logger.Warning("Failed to save runtime rave state: " + ex2.Message);
		}
	}

	private static void LogRuntimeStateDebug(string message)
	{
		try
		{
			if (BundleDebugLogging != null && BundleDebugLogging.Value)
			{
				RaveMod.Logger.Msg("[RaveRuntimeState] " + message);
			}
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("Runtime state debug logging failed: " + ex.Message);
		}
	}

	private static bool TryReadSmallTextFile(string path, long maxBytes, out string content)
	{
		content = null;
		if (!File.Exists(path))
		{
			return false;
		}
		FileInfo fileInfo = new FileInfo(path);
		if (fileInfo.Length > maxBytes)
		{
			RaveMod.Logger.Warning($"Ignoring state file '{path}' because it exceeds {maxBytes} bytes.");
			return false;
		}
		content = File.ReadAllText(path, Encoding.UTF8);
		return true;
	}

	private static Dictionary<string, string> ParseStateEntries(string content, string sourcePath)
	{
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
		if (string.IsNullOrWhiteSpace(content))
		{
			return dictionary;
		}
		string[] array = content.Replace("\r", string.Empty).Split('\n');
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (string.IsNullOrEmpty(text) || text.StartsWith("#", StringComparison.Ordinal))
			{
				continue;
			}
			int num = text.IndexOf('=');
			if (num <= 0)
			{
				RaveMod.Logger.Warning($"Ignoring invalid rave state line in '{sourcePath}': '{text}'");
			}
			else
			{
				string text2 = text.Substring(0, num).Trim();
				string value = text.Substring(num + 1).Trim();
				if (!string.IsNullOrWhiteSpace(text2))
				{
					dictionary[text2] = value;
				}
			}
		}
		return dictionary;
	}

	private static void ApplyParsedStateEntries(Dictionary<string, string> entries, string sourcePath)
	{
		if (entries.TryGetValue("DJDanBooked", out var value) && bool.TryParse(value, out var result))
		{
			DJDanBooked = result;
		}
		if (entries.TryGetValue("DJGirlBooked", out var value2) && bool.TryParse(value2, out var result2))
		{
			DJGirlBooked = result2;
		}
		if (entries.TryGetValue("DJCutChemistBooked", out var value3) && bool.TryParse(value3, out var result3))
		{
			DJCutChemistBooked = result3;
		}
		if (entries.TryGetValue("DJLudapimpBooked", out var value4) && bool.TryParse(value4, out var result4))
		{
			DJLudapimpBooked = result4;
		}
		if (entries.TryGetValue("CustomLocations", out var value5))
		{
			_customLocationsData = NormalizeCustomLocationsData(value5);
		}
		if (entries.TryGetValue("ResumeRaveActive", out var value6) && bool.TryParse(value6, out var result5))
		{
			_resumeRaveActive = result5;
		}
		if (entries.TryGetValue("ResumeRaveDay", out var value7) && int.TryParse(value7, out var result6))
		{
			_resumeRaveDay = result6;
		}
		if (entries.TryGetValue("ResumePresetName", out var value8))
		{
			_resumePresetName = value8 ?? string.Empty;
		}
		if (entries.TryGetValue("ResumeBookedDjStateCaptured", out var value9) && bool.TryParse(value9, out var result7))
		{
			_resumeBookedDjStateCaptured = result7;
		}
		if (entries.TryGetValue("ResumeDJDanBooked", out var value10) && bool.TryParse(value10, out var result8))
		{
			_resumeDjDanBooked = result8;
		}
		if (entries.TryGetValue("ResumeDJGirlBooked", out var value11) && bool.TryParse(value11, out var result9))
		{
			_resumeDjGirlBooked = result9;
		}
		if (entries.TryGetValue("ResumeDJCutChemistBooked", out var value12) && bool.TryParse(value12, out var result10))
		{
			_resumeDjCutChemistBooked = result10;
		}
		if (entries.TryGetValue("ResumeDJLudapimpBooked", out var value13) && bool.TryParse(value13, out var result11))
		{
			_resumeDjLudapimpBooked = result11;
		}
		int num = (DJDanBooked ? 1 : 0) + (DJGirlBooked ? 1 : 0) + (DJCutChemistBooked ? 1 : 0) + (DJLudapimpBooked ? 1 : 0);
		if (num > 1)
		{
			RaveMod.Logger.Warning($"State file '{sourcePath}' contained {num} DJs as booked. Keeping last one only.");
			if (DJLudapimpBooked)
			{
				DJDanBooked = false;
				DJGirlBooked = false;
				DJCutChemistBooked = false;
			}
			else if (DJCutChemistBooked)
			{
				DJDanBooked = false;
				DJGirlBooked = false;
				DJLudapimpBooked = false;
			}
			else if (DJGirlBooked)
			{
				DJDanBooked = false;
				DJCutChemistBooked = false;
				DJLudapimpBooked = false;
			}
		}
		int num2 = (_resumeDjDanBooked ? 1 : 0) + (_resumeDjGirlBooked ? 1 : 0) + (_resumeDjCutChemistBooked ? 1 : 0) + (_resumeDjLudapimpBooked ? 1 : 0);
		if (num2 > 1)
		{
			RaveMod.Logger.Warning($"State file '{sourcePath}' contained {num2} resume DJs as booked. Keeping last one only.");
			if (_resumeDjLudapimpBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjGirlBooked = false;
				_resumeDjCutChemistBooked = false;
			}
			else if (_resumeDjCutChemistBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjGirlBooked = false;
				_resumeDjLudapimpBooked = false;
			}
			else if (_resumeDjGirlBooked)
			{
				_resumeDjDanBooked = false;
				_resumeDjCutChemistBooked = false;
				_resumeDjLudapimpBooked = false;
			}
		}
	}

	private static string BuildStateDataContent()
	{
		string text = _customLocationsData ?? string.Empty;
		text = text.Replace("\r", string.Empty).Replace("\n", string.Empty);
		text = NormalizeCustomLocationsData(text);
		return string.Join(Environment.NewLine, "Format=IllegalRaveStateV1", $"{"DJDanBooked"}={DJDanBooked}", $"{"DJGirlBooked"}={DJGirlBooked}", $"{"DJCutChemistBooked"}={DJCutChemistBooked}", $"{"DJLudapimpBooked"}={DJLudapimpBooked}", "CustomLocations=" + text, $"ResumeRaveActive={_resumeRaveActive}", $"ResumeRaveDay={_resumeRaveDay}", "ResumePresetName=" + (_resumePresetName ?? string.Empty), $"ResumeBookedDjStateCaptured={_resumeBookedDjStateCaptured}", $"ResumeDJDanBooked={_resumeDjDanBooked}", $"ResumeDJGirlBooked={_resumeDjGirlBooked}", $"ResumeDJCutChemistBooked={_resumeDjCutChemistBooked}", $"ResumeDJLudapimpBooked={_resumeDjLudapimpBooked}");
	}

	private static void TryDeleteLegacyStateFile(string path)
	{
		if (!File.Exists(path))
		{
			return;
		}
		try
		{
			File.Delete(path);
		}
		catch (IOException ex)
		{
			LogRuntimeStateDebug("Legacy state cleanup failed for '" + path + "': " + ex.Message);
		}
		catch (UnauthorizedAccessException ex2)
		{
			LogRuntimeStateDebug("Legacy state cleanup failed for '" + path + "': " + ex2.Message);
		}
	}

	private static void SaveCustomLocations()
	{
		try
		{
			_customLocationsData = NormalizeCustomLocationsData(_customLocationsData);
			SaveStateData();
			UpdateCustomSlots();
			MelonPreferences.Save();
		}
		catch (IOException ex)
		{
			MelonLogger.Warning("Could not save custom locations: " + ex.Message);
		}
		catch (UnauthorizedAccessException ex2)
		{
			MelonLogger.Warning("Could not save custom locations: " + ex2.Message);
		}
	}

	private static void UpdateCustomSlots()
	{
		if (_customSlots == null)
		{
			return;
		}
		string[] slotCoordinates = GetSlotCoordinates();
		for (int i = 0; i < _customSlots.Length; i++)
		{
			if (_customSlots[i] != null)
			{
				_customSlots[i].Value = ((slotCoordinates[i] != null) ? "saved" : "free");
				((MelonPreferences_Entry)_customSlots[i]).Description = slotCoordinates[i] ?? "";
			}
		}
	}

	private static string[] GetSlotCoordinates()
	{
		string[] array = new string[3];
		if (string.IsNullOrWhiteSpace(_customLocationsData))
		{
			return array;
		}
		string[] array2 = _customLocationsData.Split(';');
		for (int i = 0; i < array2.Length && i < array.Length; i++)
		{
			string text = array2[i].Trim();
			if (!string.IsNullOrEmpty(text))
			{
				string[] array3 = text.Split(',');
				if (array3.Length == 3)
				{
					array[i] = text;
				}
			}
		}
		return array;
	}

	private static string NormalizeCustomLocationsData(string raw)
	{
		if (string.IsNullOrWhiteSpace(raw))
		{
			return string.Empty;
		}
		string[] array = raw.Split(';');
		List<string> list = new List<string>(3);
		for (int i = 0; i < array.Length; i++)
		{
			if (list.Count >= 3)
			{
				break;
			}
			string text = array[i].Trim();
			if (!string.IsNullOrEmpty(text))
			{
				string[] array2 = text.Split(',');
				if (array2.Length == 3)
				{
					list.Add(text);
				}
			}
		}
		return string.Join(";", list);
	}
}
public sealed class RaveDJBoothController
{
	private enum ScaleMode
	{
		ByY,
		ByMaxAxis,
		ByWidth
	}

	private const string GO_BOOTH_NAME = "RaveDJBooth";

	private const string GO_DJDAN_NAME = "RaveDJDan";

	private const string GO_DJGIRL_NAME = "RaveDJGirl";

	private const string GO_DJCUTCHEMIST_NAME = "RaveDJCutChemist";

	private const string GO_DJLUDAPIMP_NAME = "RaveDJLudapimp";

	private const string GO_STAGE_NAME = "SmallFestivalStage";

	private static GameObject _boothPrefab;

	private static GameObject _djDanPrefab;

	private static GameObject _djGirlPrefab;

	private static GameObject _djCutChemistPrefab;

	private static GameObject _djLudapimpPrefab;

	private static GameObject _stagePrefab;

	private static GameObject _stageInstance;

	private static bool _stageCacheFailed;

	private static GameObject _boothInstance;

	private static GameObject _djDanInstance;

	private static GameObject _djGirlInstance;

	private static GameObject _djCutChemistInstance;

	private static GameObject _djLudapimpInstance;

	private static bool _boothCacheFailed;

	private static bool _djDanCacheFailed;

	private static bool _djGirlCacheFailed;

	private static bool _djCutChemistCacheFailed;

	private static bool _djLudapimpCacheFailed;

	private static readonly HashSet<string> _debugOnceKeys = new HashSet<string>();

	private static Vector3 _lastNormalizedScaleFactor = Vector3.one;

	private const float BOOTH_TARGET_WIDTH = 1.5f;

	private const float BOOTH_Y_OFFSET = -0.5f;

	private const float DJ_Z_EXTRA = 1.2f;

	private const float DJ_Z_EXTRA_PIMP = 1.4f;

	private const float GROUND_RAYCAST_HEIGHT = 1.2f;

	private const float GROUND_RAYCAST_DISTANCE = 6f;

	private const float MIN_LOOK_DIRECTION = 0.0001f;

	private const float SKINNED_BOUNDS_EXPAND = 0.05f;

	private const float DJDAN_TARGET_HEIGHT = 1.8f;

	private const float DJDAN_ALIGN_Y_OFFSET = 0f;

	private const float DJDAN_YAW_CORRECTION = 0f;

	private static readonly ScaleMode DJDAN_SCALE_MODE = ScaleMode.ByMaxAxis;

	private const float DJGIRL_TARGET_HEIGHT = 1.9f;

	private const float DJGIRL_ALIGN_Y_OFFSET = -1f;

	private const float DJGIRL_YAW_CORRECTION = 0f;

	private static readonly ScaleMode DJGIRL_SCALE_MODE = ScaleMode.ByMaxAxis;

	private const float DJCUTCHEMIST_TARGET_HEIGHT = 1.8f;

	private const float DJCUTCHEMIST_ALIGN_Y_OFFSET = 0f;

	private const float DJCUTCHEMIST_YAW_CORRECTION = 180f;

	private static readonly ScaleMode DJCUTCHEMIST_SCALE_MODE = ScaleMode.ByMaxAxis;

	private const float DJLUDAPIMP_TARGET_HEIGHT = 1.6f;

	private const float DJLUDAPIMP_ALIGN_Y_OFFSET = 0f;

	private const float DJLUDAPIMP_YAW_CORRECTION = 0f;

	private static readonly ScaleMode DJLUDAPIMP_SCALE_MODE = ScaleMode.ByMaxAxis;

	private const float DJ_STAGE_OFFSET_X = -0.85f;

	private const float DJ_STAGE_OFFSET_Y = -0.6f;

	private const float DJ_STAGE_OFFSET_Z = 0f;

	private RaveDJBoothController()
	{
	}

	internal static void PreCacheGlbPrefabs()
	{
		try
		{
			EnsureGlbCached("booth", ref _boothPrefab, GlbAssetProvider.GetDjBooth, ref _boothCacheFailed);
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("PreCache booth error: " + ex.Message);
		}
		try
		{
			EnsureGlbCached("djdan", ref _djDanPrefab, GlbAssetProvider.GetDjDan, ref _djDanCacheFailed);
		}
		catch (Exception ex2)
		{
			RaveMod.Logger.Warning("PreCache djdan error: " + ex2.Message);
		}
		try
		{
			EnsureGlbCached("djgirl", ref _djGirlPrefab, GlbAssetProvider.GetDjGirl, ref _djGirlCacheFailed);
		}
		catch (Exception ex3)
		{
			RaveMod.Logger.Warning("PreCache djgirl error: " + ex3.Message);
		}
		try
		{
			EnsureGlbCached("djcutchemist", ref _djCutChemistPrefab, GlbAssetProvider.GetDjCutChemist, ref _djCutChemistCacheFailed);
		}
		catch (Exception ex4)
		{
			RaveMod.Logger.Warning("PreCache djcutchemist error: " + ex4.Message);
		}
		try
		{
			EnsureGlbCached("djludapimp", ref _djLudapimpPrefab, GlbAssetProvider.GetDjLudapimp, ref _djLudapimpCacheFailed);
		}
		catch (Exception ex5)
		{
			RaveMod.Logger.Warning("PreCache djludapimp error: " + ex5.Message);
		}
		if (RaveConfig.SmallFestivalStageEnabled.Value)
		{
			try
			{
				EnsureGlbCached("LetsGo", ref _stagePrefab, GlbAssetProvider.GetLetsGo, ref _stageCacheFailed);
			}
			catch (Exception ex6)
			{
				RaveMod.Logger.Warning("PreCache LetsGo error: " + ex6.Message);
			}
		}
		LogDebug($"PreCacheGlbPrefabs done: booth={(Object)(object)_boothPrefab != (Object)null}, djdan={(Object)(object)_djDanPrefab != (Object)null}, djgirl={(Object)(object)_djGirlPrefab != (Object)null}, djcutchemist={(Object)(object)_djCutChemistPrefab != (Object)null}, djludapimp={(Object)(object)_djLudapimpPrefab != (Object)null}, stage={(Object)(object)_stagePrefab != (Object)null}");
	}

	internal static void SpawnBooth()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_0299: Unknown result type (might be due to invalid IL or missing references)
		//IL_029f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Unknown result type (might be due to invalid IL or missing references)
		//IL_030c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_036f: Unknown result type (might be due to invalid IL or missing references)
		//IL_037b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0492: Unknown result type (might be due to invalid IL or missing references)
		//IL_049d: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_05be: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_06df: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			DestroyInstances();
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("SpawnBooth pre-cleanup error: " + ex.Message);
		}
		if (RaveLocationManager.ShouldUseStage())
		{
			SpawnStageInsteadOfBooth();
			return;
		}
		Vector3 centerPosition = RaveLocationManager.CenterPosition;
		float bOOTH_Z_OFFSET = RaveLocationManager.BOOTH_Z_OFFSET;
		LogDebug($"SpawnBooth start: djDanBooked={RaveConfig.DJDanBooked}, djGirlBooked={RaveConfig.DJGirlBooked}, djCutChemistBooked={RaveConfig.DJCutChemistBooked}, djLudapimpBooked={RaveConfig.DJLudapimpBooked}");
		LogDebug($"SpawnBooth center: x={centerPosition.x:F3}, y={centerPosition.y:F3}, z={centerPosition.z:F3}, boothZOffset={bOOTH_Z_OFFSET:F3}");
		try
		{
			EnsureGlbCached("booth", ref _boothPrefab, GlbAssetProvider.GetDjBooth, ref _boothCacheFailed);
			if ((Object)(object)_boothPrefab != (Object)null)
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(centerPosition.x, centerPosition.y + -0.5f, centerPosition.z + bOOTH_Z_OFFSET);
				val.y = ResolveGroundY(val, val.y, "RaveDJBooth");
				_boothInstance = SpawnInstance(_boothPrefab, "RaveDJBooth", val, 1.5f, ScaleMode.ByWidth, val.y + 0.18f);
			}
			if ((Object)(object)_boothInstance == (Object)null)
			{
				RaveMod.Logger.Warning("DJ Booth spawn failed.");
			}
		}
		catch (Exception value)
		{
			RaveMod.Logger.Error($"SpawnBooth (booth) error: {value}");
		}
		string goName = (RaveConfig.DJLudapimpBooked ? "RaveDJLudapimp" : (RaveConfig.DJCutChemistBooked ? "RaveDJCutChemist" : (RaveConfig.DJGirlBooked ? "RaveDJGirl" : "RaveDJDan")));
		Vector3 val2 = default(Vector3);
		((Vector3)(ref val2))..ctor(centerPosition.x, centerPosition.y + -0.5f, centerPosition.z + bOOTH_Z_OFFSET + 1.2f);
		val2.y = ResolveGroundY(val2, val2.y, goName);
		if (RaveConfig.DJLudapimpBooked)
		{
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(centerPosition.x, centerPosition.y + -0.5f, centerPosition.z + bOOTH_Z_OFFSET + 1.4f);
			val3.y = ResolveGroundY(val3, val3.y, "RaveDJLudapimp");
			try
			{
				EnsureGlbCached("djludapimp", ref _djLudapimpPrefab, GlbAssetProvider.GetDjLudapimp, ref _djLudapimpCacheFailed);
				if ((Object)(object)_djLudapimpPrefab != (Object)null)
				{
					_djLudapimpInstance = SpawnInstance(_djLudapimpPrefab, "RaveDJLudapimp", val3, 1.6f, DJLUDAPIMP_SCALE_MODE, val3.y + 0f, skipBottomAlign: true);
					AddDJCapsuleCollider(_djLudapimpInstance, "DJ Ludapimp");
					FaceBooth(_djLudapimpInstance, _boothInstance, "RaveDJLudapimp");
					LogDjRelativeToBooth(_djLudapimpInstance, _boothInstance, "RaveDJLudapimp");
				}
				if ((Object)(object)_djLudapimpInstance == (Object)null)
				{
					RaveMod.Logger.Warning("DJ Ludapimp spawn failed.");
				}
			}
			catch (Exception value2)
			{
				RaveMod.Logger.Error($"SpawnBooth (DJ Ludapimp) error: {value2}");
			}
		}
		else if (RaveConfig.DJCutChemistBooked)
		{
			try
			{
				EnsureGlbCached("djcutchemist", ref _djCutChemistPrefab, GlbAssetProvider.GetDjCutChemist, ref _djCutChemistCacheFailed);
				if ((Object)(object)_djCutChemistPrefab != (Object)null)
				{
					_djCutChemistInstance = SpawnInstance(_djCutChemistPrefab, "RaveDJCutChemist", val2, 1.8f, DJCUTCHEMIST_SCALE_MODE, val2.y + 0f, skipBottomAlign: true);
					AddDJCapsuleCollider(_djCutChemistInstance, "DJ Cut Chemist");
					FaceBooth(_djCutChemistInstance, _boothInstance, "RaveDJCutChemist", 180f);
					LogDjRelativeToBooth(_djCutChemistInstance, _boothInstance, "RaveDJCutChemist");
				}
				if ((Object)(object)_djCutChemistInstance == (Object)null)
				{
					RaveMod.Logger.Warning("DJ Cut Chemist spawn failed.");
				}
			}
			catch (Exception value3)
			{
				RaveMod.Logger.Error($"SpawnBooth (DJ Cut Chemist) error: {value3}");
			}
		}
		else if (RaveConfig.DJGirlBooked)
		{
			try
			{
				EnsureGlbCached("djgirl", ref _djGirlPrefab, GlbAssetProvider.GetDjGirl, ref _djGirlCacheFailed);
				if ((Object)(object)_djGirlPrefab != (Object)null)
				{
					_djGirlInstance = SpawnInstance(_djGirlPrefab, "RaveDJGirl", val2, 1.9f, DJGIRL_SCALE_MODE, val2.y + -1f, skipBottomAlign: true);
					AddDJCapsuleCollider(_djGirlInstance, "DJ Girl");
					FaceBooth(_djGirlInstance, _boothInstance, "RaveDJGirl");
					LogDjRelativeToBooth(_djGirlInstance, _boothInstance, "RaveDJGirl");
				}
				if ((Object)(object)_djGirlInstance == (Object)null)
				{
					RaveMod.Logger.Warning("DJ Girl spawn failed.");
				}
			}
			catch (Exception value4)
			{
				RaveMod.Logger.Error($"SpawnBooth (DJ Girl) error: {value4}");
			}
		}
		else if (RaveConfig.DJDanBooked)
		{
			try
			{
				EnsureGlbCached("djdan", ref _djDanPrefab, GlbAssetProvider.GetDjDan, ref _djDanCacheFailed);
				if ((Object)(object)_djDanPrefab != (Object)null)
				{
					_djDanInstance = SpawnInstance(_djDanPrefab, "RaveDJDan", val2, 1.8f, DJDAN_SCALE_MODE, val2.y + 0f, skipBottomAlign: true);
					AddDJCapsuleCollider(_djDanInstance, "DJ Dan");
					FaceBooth(_djDanInstance, _boothInstance, "RaveDJDan");
					LogDjRelativeToBooth(_djDanInstance, _boothInstance, "RaveDJDan");
				}
				if ((Object)(object)_djDanInstance == (Object)null)
				{
					RaveMod.Logger.Warning("DJ Dan spawn failed.");
				}
			}
			catch (Exception value5)
			{
				RaveMod.Logger.Error($"SpawnBooth (DJ Dan) error: {value5}");
			}
		}
		bool flag = (Object)(object)_boothInstance != (Object)null;
		bool flag2 = RaveConfig.DJDanBooked || RaveConfig.DJGirlBooked || RaveConfig.DJCutChemistBooked || RaveConfig.DJLudapimpBooked;
		bool flag3 = !flag2 || (Object)(object)_djDanInstance != (Object)null || (Object)(object)_djGirlInstance != (Object)null || (Object)(object)_djCutChemistInstance != (Object)null || (Object)(object)_djLudapimpInstance != (Object)null;
		string text = ((!flag2) ? "not booked" : ((!RaveConfig.DJLudapimpBooked) ? ((!RaveConfig.DJCutChemistBooked) ? ((!RaveConfig.DJGirlBooked) ? (((Object)(object)_djDanInstance != (Object)null) ? "Dan OK" : "Dan FAIL") : (((Object)(object)_djGirlInstance != (Object)null) ? "Girl OK" : "Girl FAIL")) : (((Object)(object)_djCutChemistInstance != (Object)null) ? "CutChemist OK" : "CutChemist FAIL")) : (((Object)(object)_djLudapimpInstance != (Object)null) ? "Ludapimp OK" : "Ludapimp FAIL")));
		if (!flag || !flag3)
		{
			RaveMod.Logger.Warning("DJ Booth spawn incomplete: booth=" + (((Object)(object)_boothInstance != (Object)null) ? "OK" : "FAIL") + ", dj=" + text);
		}
		else if (RaveConfig.DebugCoordinates.Value)
		{
			RaveMod.Logger.Msg("DJ Booth: booth=OK, dj=" + text);
		}
	}

	private static void AddDJCapsuleCollider(GameObject djInstance, string label)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)djInstance == (Object)null)
		{
			return;
		}
		try
		{
			float num = djInstance.transform.localScale.x;
			if (num < 0.0001f)
			{
				num = 1f;
			}
			CapsuleCollider val = djInstance.AddComponent<CapsuleCollider>();
			val.center = new Vector3(0f, 0.9f / num, 0f);
			val.radius = 0.3f / num;
			val.height = 1.8f / num;
			LogDebug($"{label} CapsuleCollider: rootScale={num:F4}, localRadius={val.radius:F4}, localHeight={val.height:F4}");
		}
		catch (Exception ex)
		{
			LogDebug(label + " CapsuleCollider error (non-fatal): " + ex.Message);
		}
	}

	private static void EnsureGlbCached(string label, ref GameObject prefab, Func<byte[]> glbProvider, ref bool cacheFailed)
	{
		if ((Object)(object)prefab != (Object)null)
		{
			return;
		}
		if (cacheFailed)
		{
			LogDebug("[" + label + "] cache previously failed, skipping.");
			return;
		}
		byte[] array;
		try
		{
			array = glbProvider();
		}
		catch (Exception value)
		{
			RaveMod.Logger.Error($"[{label}] GLB read failed: {value}");
			cacheFailed = true;
			return;
		}
		if (array == null || array.Length < 12)
		{
			RaveMod.Logger.Error("[" + label + "] GLB data null or too short.");
			cacheFailed = true;
			return;
		}
		LogDebug($"[{label}] GLB bytes: {array.Length}");
		try
		{
			DebugLog.DebugEnabled = IsBundleDebugEnabled();
			GltfImporter gltfImporter = new GltfImporter().SetName(label).ImportAnimations(import: true).ImportSkins(import: true)
				.GenerateNormals(generate: true)
				.GenerateTangents(generate: true)
				.ReadableMeshes(readable: true);
			prefab = gltfImporter.Load(array);
		}
		catch (Exception value2)
		{
			RaveMod.Logger.Error($"[{label}] GltfImporter.Load failed: {value2}");
			cacheFailed = true;
			return;
		}
		if ((Object)(object)prefab == (Object)null)
		{
			RaveMod.Logger.Error("[" + label + "] GltfImporter returned null.");
			cacheFailed = true;
			return;
		}
		prefab.SetActive(false);
		LogDebug($"[{label}] GLB prefab cached: '{((Object)prefab).name}'");
		DiagnosePrefabStructure(prefab, label);
	}

	private static void DiagnosePrefabStructure(GameObject prefab, string label)
	{
		//IL_0008: 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_0014: 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_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: 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_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_0291: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: 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_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Vector3 localScale = prefab.transform.localScale;
			int value = 0;
			int value2 = 0;
			int value3 = 0;
			Vector3 val = Vector3.zero;
			MeshFilter[] array = Il2CppArrayBase<MeshFilter>.op_Implicit(prefab.GetComponentsInChildren<MeshFilter>(true));
			Bounds bounds;
			if (array != null)
			{
				value = array.Length;
				for (int i = 0; i < array.Length; i++)
				{
					if ((Object)(object)array[i] != (Object)null && (Object)(object)array[i].sharedMesh != (Object)null)
					{
						Vector3 val2 = val;
						bounds = array[i].sharedMesh.bounds;
						val = Vector3.Max(val2, ((Bounds)(ref bounds)).size);
					}
				}
			}
			SkinnedMeshRenderer[] array2 = Il2CppArrayBase<SkinnedMeshRenderer>.op_Implicit(prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true));
			if (array2 != null)
			{
				value2 = array2.Length;
				for (int j = 0; j < array2.Length; j++)
				{
					if ((Object)(object)array2[j] != (Object)null && (Object)(object)array2[j].sharedMesh != (Object)null)
					{
						Vector3 val3 = val;
						bounds = array2[j].sharedMesh.bounds;
						val = Vector3.Max(val3, ((Bounds)(ref bounds)).size);
					}
				}
			}
			Animation[] array3 = Il2CppArrayBase<Animation>.op_Implicit(prefab.GetComponentsInChildren<Animation>(true));
			if (array3 != null)
			{
				value3 = array3.Length;
			}
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor(val.x * Mathf.Abs(localScale.x), val.y * Mathf.Abs(localScale.y), val.z * Mathf.Abs(localScale.z));
			LogDebug($"[{label}] rootScale=({localScale.x:F1},{localScale.y:F1},{localScale.z:F1}), meshLocal=({val.x:F4},{val.y:F4},{val.z:F4}), worldSize=({val4.x:F3},{val4.y:F3},{val4.z:F3}), meshFilters={value}, skins={value2}, anims={value3}");
		}
		catch (Exception ex)
		{
			RaveMod.Logger.Warning("[DJDiag] " + label + ": diagnosis error: " + ex.Message);
		}
	}

	private static GameObject SpawnInstance(GameObject prefab, string goName, Vector3 position, float targetSize, ScaleMode scaleMode, float alignBottomToY, bool skipBottomAlign = false)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: 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_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_050c: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0404: Unknown result type (might be due to invalid IL or missing references)
		//IL_042e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0458: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_025d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_030f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0330: Unknown result type (might be due to invalid IL or missing references)
		//IL_0354: Unknown result type (might be due to invalid IL or missing references)
		//IL_0378: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result