Decompiled source of TokControl REPO Tiktoklive v1.3.91

BepInEx\plugins\TokControl_REPO_Tiktoklive.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using TMPro;
using TokControlREPOBridge.Commands;
using TokControlREPOBridge.Logging;
using TokControlREPOBridge.Network;
using TokControlREPOBridge.Ui;
using TokControlREPOBridge.Util;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TokControl_REPO_Tiktoklive")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("TokControl WebSocket bridge for R.E.P.O. TikTok Live — spawn items and enemies from stream gifts")]
[assembly: AssemblyFileVersion("1.3.91.0")]
[assembly: AssemblyInformationalVersion("1.3.91")]
[assembly: AssemblyProduct("TokControl_REPO_Tiktoklive")]
[assembly: AssemblyTitle("TokControl_REPO_Tiktoklive")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.91.0")]
[module: UnverifiableCode]
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;
		}
	}
	internal static class IsExternalInit
	{
	}
}
namespace TokControlREPOBridge
{
	[BepInPlugin("com.tokcontrol.repobridge", "TokControl_REPO_Tiktoklive", "1.3.91")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private ConfigEntry<int> _portConfig;

		private ConfigEntry<bool> _logToUnityConfig;

		private ConfigEntry<string> _defaultGhostEnemyConfig;

		private WebSocketServer? _server;

		private CommandProcessor? _processor;

		private Harmony? _harmony;

		private static readonly ConcurrentQueue<Action> MainThreadQueue = new ConcurrentQueue<Action>();

		private static readonly List<(float due, Action action)> DelayedActions = new List<(float, Action)>();

		private static readonly object DelayedLock = new object();

		private const int MinActionsPerFrame = 16;

		private const int MaxBurstActionsPerFrame = 256;

		internal static Plugin Instance { get; private set; } = null;

		internal static ManualLogSource Log { get; private set; } = null;

		internal static void EnqueueMainThread(Action action)
		{
			if (action != null)
			{
				MainThreadQueue.Enqueue(action);
			}
		}

		internal static void EnqueueMainThreadDelayed(Action action, float delaySeconds)
		{
			if (action == null)
			{
				return;
			}
			if (delaySeconds <= 0.001f)
			{
				EnqueueMainThread(action);
				return;
			}
			lock (DelayedLock)
			{
				DelayedActions.Add((Time.realtimeSinceStartup + delaySeconds, action));
			}
		}

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			ModLog.Info("=== TokControl_REPO_Tiktoklive v1.3.91 ===");
			ModLog.Info("REPOLib dependency OK — initializing WebSocket bridge");
			try
			{
				_harmony = new Harmony("com.tokcontrol.repobridge");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				ModLog.Info("Enemy spawn patches applied");
			}
			catch (Exception ex)
			{
				ModLog.Warn("Harmony patch failed: " + ex.Message);
			}
			_processor = new CommandProcessor(_defaultGhostEnemyConfig.Value);
			SpawnRelay.Initialize(_processor.Actions);
			EffectRelay.Initialize(_processor.Actions);
			SpeakBroadcast.Initialize();
			try
			{
				_server = new WebSocketServer(_portConfig.Value, _processor);
				_server.Start();
				ModLog.Info($"WebSocket listening on ws://127.0.0.1:{_server.Port}/");
				if (_server.Port != _portConfig.Value)
				{
					ModLog.Warn($"Configured port {_portConfig.Value} was busy. Update TokControl URL → ws://127.0.0.1:{_server.Port}/");
				}
			}
			catch (Exception ex2)
			{
				ModLog.Error("Failed to start WebSocket bridge: " + ex2.Message);
				ModLog.Error("Disable other TikTok/stream mods using port 8080, or change Server.Port in the config file.");
				_server = null;
			}
			MainThreadDispatcher.Enqueue(delegate
			{
				ItemRegistry.EnsureLoaded();
				_ = EffectTimerHost.Instance;
				TokControlStatusHud.Ensure();
			});
			ModLog.Info("Waiting for TokControl / Pandy App commands...");
		}

		private void Update()
		{
			float deltaTime = Time.deltaTime;
			try
			{
				RunGate.Tick(deltaTime);
			}
			catch
			{
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			lock (DelayedLock)
			{
				for (int num = DelayedActions.Count - 1; num >= 0; num--)
				{
					if (!(DelayedActions[num].due > realtimeSinceStartup))
					{
						Action item = DelayedActions[num].action;
						DelayedActions.RemoveAt(num);
						if (item != null)
						{
							MainThreadQueue.Enqueue(item);
						}
					}
				}
			}
			int count = MainThreadQueue.Count;
			int num2 = ((count > 2) ? Math.Min(count, 256) : 16);
			int num3 = 0;
			Action result;
			while (num3 < num2 && MainThreadQueue.TryDequeue(out result))
			{
				num3++;
				try
				{
					result();
				}
				catch (Exception ex)
				{
					ModLog.Error("Main thread action failed: " + ex.Message);
				}
			}
		}

		private void BindConfig()
		{
			_portConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "Port", 8080, "Local WebSocket port for TokControl commands (ws://127.0.0.1:PORT/)");
			_logToUnityConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogToUnityConsole", true, "Mirror TokControl bridge logs to Unity debug console");
			_defaultGhostEnemyConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Gameplay", "DefaultGhostEnemy", "Hidden", "Enemy name used for spawn_ghost when no name is provided (e.g. Hidden, Robe, Hunter)");
		}

		internal static bool ShouldLogToUnity()
		{
			return Instance?._logToUnityConfig?.Value ?? true;
		}

		private void OnDestroy()
		{
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
			_server?.Dispose();
			ModLog.Info("TokControl_REPO_Tiktoklive shut down");
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.tokcontrol.repobridge";

		public const string PLUGIN_NAME = "TokControl_REPO_Tiktoklive";

		public const string PLUGIN_VERSION = "1.3.91";
	}
}
namespace TokControlREPOBridge.Util
{
	internal static class BurstCoalescer
	{
		private const float QuietSeconds = 0.45f;

		private static readonly Dictionary<string, Action> FlushActions = new Dictionary<string, Action>();

		internal static void Debounce(string key, Action onFlush, Action? onTouch = null)
		{
			onTouch?.Invoke();
			FlushActions[key] = onFlush;
			EffectTimerHost.Instance.Stop(key);
			EffectTimerHost.Instance.RunForSeconds(key, 0.45f, delegate
			{
			}, delegate
			{
				if (!FlushActions.TryGetValue(key, out Action value))
				{
					return;
				}
				FlushActions.Remove(key);
				try
				{
					value();
				}
				catch (Exception ex)
				{
					ModLog.Warn("Burst flush '" + key + "' failed: " + ex.Message);
				}
			});
		}
	}
	public static class GameNotifier
	{
		public static void AnnounceSpawn(string user, string target, int count, string kind)
		{
			string text = (string.IsNullOrWhiteSpace(user) ? "viewer" : user.Trim());
			string text2 = (string.IsNullOrWhiteSpace(target) ? "item" : target.Trim());
			string text3 = ((count > 1) ? $" x{count}" : "");
			PostAnnouncement(text + " activates '" + text2 + text3 + "'", 4.5f);
		}

		public static void AnnounceEvent(string user, string eventId)
		{
			string text = (string.IsNullOrWhiteSpace(user) ? "viewer" : user.Trim());
			string label = EventLangCatalog.GetLabel(eventId);
			PostAnnouncement(text + " activates '" + label + "'", 3.5f);
		}

		public static void AnnounceCustom(string user, string message, float seconds = 4.5f)
		{
			string text = (string.IsNullOrWhiteSpace(user) ? "viewer" : user.Trim());
			string text2 = (string.IsNullOrWhiteSpace(message) ? "event" : message.Trim());
			if (text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				PostAnnouncement(text2, seconds);
			}
			else
			{
				PostAnnouncement(text + " → " + text2, seconds);
			}
		}

		private static void PostAnnouncement(string line, float seconds = 3f)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrWhiteSpace(line))
			{
				PostTruck(line);
				PostMission(line, new Color(1f, 0.45f, 0.15f), Color.white, Math.Max(2f, seconds));
			}
		}

		private static void PostTruck(string message)
		{
			try
			{
				if ((Object)(object)TruckScreenText.instance == (Object)null)
				{
					ModLog.Debug("TruckScreenText not ready — skip monitor message");
				}
				else
				{
					TruckScreenText.instance.MessageSendCustom("", "{arrowright}" + message + "{arrowleft}", 0);
				}
			}
			catch (Exception ex)
			{
				ModLog.Debug("Truck notify failed: " + ex.Message);
			}
		}

		private static void PostMission(string text, Color colorA, Color colorB, float seconds)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)MissionUI.instance == (Object)null))
				{
					MissionUI.instance.MissionText(text, colorA, colorB, seconds);
				}
			}
			catch (Exception ex)
			{
				ModLog.Debug("Mission notify failed: " + ex.Message);
			}
		}
	}
	public static class MainThreadDispatcher
	{
		public static bool IsReady => (Object)(object)Plugin.Instance != (Object)null;

		public static void Enqueue(Action action)
		{
			Plugin.EnqueueMainThread(action);
		}

		public static void EnqueueDelayed(Action action, float delaySeconds)
		{
			Plugin.EnqueueMainThreadDelayed(action, delaySeconds);
		}
	}
	public static class SimpleJson
	{
		public static string? GetString(string json, string key)
		{
			if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key))
			{
				return null;
			}
			string value = "\"" + key + "\"";
			int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				return null;
			}
			num = json.IndexOf(':', num);
			if (num < 0)
			{
				return null;
			}
			for (num++; num < json.Length && char.IsWhiteSpace(json[num]); num++)
			{
			}
			if (num >= json.Length)
			{
				return null;
			}
			if (json[num] == '"')
			{
				num++;
				int num2 = num;
				while (num < json.Length)
				{
					if (json[num] == '\\')
					{
						num += 2;
						continue;
					}
					if (json[num] == '"')
					{
						break;
					}
					num++;
				}
				return json.Substring(num2, num - num2);
			}
			int num3 = num;
			for (; num < json.Length && json[num] != ',' && json[num] != '}'; num++)
			{
			}
			return json.Substring(num3, num - num3).Trim().Trim('"');
		}

		public static int? GetInt(string json, string key)
		{
			string text = GetString(json, key);
			if (text != null && int.TryParse(text, out var result))
			{
				return result;
			}
			return null;
		}

		public static string Escape(string value)
		{
			return (value ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n")
				.Replace("\r", "\\r");
		}

		public static string CommandResult(bool success, string message, string? detail = null)
		{
			if (!string.IsNullOrEmpty(detail))
			{
				return "{\"success\":" + (success ? "true" : "false") + ",\"message\":\"" + Escape(message) + "\",\"detail\":\"" + Escape(detail) + "\"}";
			}
			return "{\"success\":" + (success ? "true" : "false") + ",\"message\":\"" + Escape(message) + "\"}";
		}

		public static string EffectPayload(string eventId, string user, int playerViewId = 0, int count = 1, string? namedPlayer = null)
		{
			int num = Math.Max(1, Math.Min(count, 100));
			string text = Escape(namedPlayer ?? "");
			return $"{{\"count\":{num},\"eventId\":\"{Escape(eventId)}\",\"user\":\"{Escape(user)}\",\"playerViewId\":{playerViewId},\"namedPlayer\":\"{text}\"}}";
		}

		public static bool TryParseEffectPayload(string json, out string eventId, out string user)
		{
			int playerViewId;
			int count;
			string namedPlayer;
			return TryParseEffectPayload(json, out eventId, out user, out playerViewId, out count, out namedPlayer);
		}

		public static bool TryParseEffectPayload(string json, out string eventId, out string user, out int playerViewId)
		{
			int count;
			string namedPlayer;
			return TryParseEffectPayload(json, out eventId, out user, out playerViewId, out count, out namedPlayer);
		}

		public static bool TryParseEffectPayload(string json, out string eventId, out string user, out int playerViewId, out int count)
		{
			string namedPlayer;
			return TryParseEffectPayload(json, out eventId, out user, out playerViewId, out count, out namedPlayer);
		}

		public static bool TryParseEffectPayload(string json, out string eventId, out string user, out int playerViewId, out int count, out string namedPlayer)
		{
			eventId = GetString(json, "eventId") ?? GetString(json, "cmd") ?? "";
			user = GetString(json, "user") ?? "viewer";
			playerViewId = GetInt(json, "playerViewId").GetValueOrDefault();
			count = Math.Max(1, Math.Min(GetInt(json, "count") ?? 1, 100));
			namedPlayer = GetString(json, "namedPlayer") ?? GetString(json, "targetPlayer") ?? "";
			return !string.IsNullOrWhiteSpace(eventId);
		}

		public static string SpawnPayload(string cmd, string name, int count, string user, int playerViewId = 0)
		{
			return $"{{\"count\":{count},\"cmd\":\"{Escape(cmd)}\",\"name\":\"{Escape(name)}\",\"user\":\"{Escape(user)}\",\"playerViewId\":{playerViewId}}}";
		}

		public static bool TryParseSpawnPayload(string json, out string cmd, out string name, out int count, out string user)
		{
			int playerViewId;
			return TryParseSpawnPayload(json, out cmd, out name, out count, out user, out playerViewId);
		}

		public static bool TryParseSpawnPayload(string json, out string cmd, out string name, out int count, out string user, out int playerViewId)
		{
			cmd = GetString(json, "cmd") ?? "";
			name = GetString(json, "name") ?? "";
			count = GetInt(json, "count") ?? 1;
			user = GetString(json, "user") ?? "viewer";
			playerViewId = GetInt(json, "playerViewId").GetValueOrDefault();
			return !string.IsNullOrWhiteSpace(cmd);
		}
	}
}
namespace TokControlREPOBridge.Ui
{
	internal static class EnemyRoomCounter
	{
		private const float RoomRadius = 22f;

		internal static int CountInCurrentRooms()
		{
			return HudStatsProvider.GetEnemyCount();
		}

		internal static int ScanLiveCountInCurrentRooms()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsInGameplayLevel())
			{
				return 0;
			}
			List<Vector3> playerRoomAnchors = HudRoomHelper.GetPlayerRoomAnchors();
			if (playerRoomAnchors.Count == 0)
			{
				return 0;
			}
			int num = 0;
			EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
			foreach (EnemyParent val in array)
			{
				if (IsLiveEnemy(val) && IsNearAnyAnchor(((Component)val).transform.position, playerRoomAnchors, 22f))
				{
					num++;
				}
			}
			return num;
		}

		private static bool IsInGameplayLevel()
		{
			try
			{
				if (SemiFunc.MenuLevel())
				{
					return false;
				}
				return SemiFunc.RunIsLevel();
			}
			catch
			{
				return (Object)(object)RunManager.instance != (Object)null;
			}
		}

		private static bool IsNearAnyAnchor(Vector3 position, List<Vector3> anchors, float radius)
		{
			//IL_000f: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			float num = radius * radius;
			foreach (Vector3 anchor in anchors)
			{
				if (HorizontalDistanceSqr(position, anchor) <= num)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsLiveEnemy(EnemyParent parent)
		{
			if ((Object)(object)parent == (Object)null || !((Component)parent).gameObject.activeInHierarchy)
			{
				return false;
			}
			Enemy componentInChildren = ((Component)parent).GetComponentInChildren<Enemy>(true);
			if ((Object)(object)componentInChildren == (Object)null || !((Component)componentInChildren).gameObject.activeInHierarchy)
			{
				return false;
			}
			if (ReadBool(parent, "despawned"))
			{
				return false;
			}
			if (ReadBool(parent, "disabled"))
			{
				return false;
			}
			if (ReadBool(componentInChildren, "disabled"))
			{
				return false;
			}
			if (ReadBool(componentInChildren, "dead"))
			{
				return false;
			}
			if (ReadBool(componentInChildren, "isDead"))
			{
				return false;
			}
			if (ReadBool(componentInChildren, "despawned"))
			{
				return false;
			}
			return true;
		}

		private static bool ReadBool(object target, string fieldName)
		{
			try
			{
				FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field?.FieldType == typeof(bool))
				{
					return (bool)field.GetValue(target);
				}
			}
			catch
			{
			}
			return false;
		}

		private static float HorizontalDistanceSqr(Vector3 a, Vector3 b)
		{
			//IL_0018: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			a.y = 0f;
			b.y = 0f;
			Vector3 val = a - b;
			return ((Vector3)(ref val)).sqrMagnitude;
		}
	}
	internal static class HudRoomHelper
	{
		private const float RoomRadius = 22f;

		internal static bool IsInCurrentRoom(Vector3 position)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> playerRoomAnchors = GetPlayerRoomAnchors();
			if (playerRoomAnchors.Count == 0)
			{
				return false;
			}
			float num = 484f;
			foreach (Vector3 item in playerRoomAnchors)
			{
				if (HorizontalDistanceSqr(position, item) <= num)
				{
					return true;
				}
			}
			return false;
		}

		internal static List<Vector3> GetPlayerRoomAnchors()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			try
			{
				List<LevelPoint> list2 = SemiFunc.LevelPointsGetInPlayerRooms();
				if (list2 != null)
				{
					foreach (LevelPoint item in list2)
					{
						if ((Object)(object)item != (Object)null)
						{
							list.Add(((Component)item).transform.position);
						}
					}
				}
			}
			catch
			{
			}
			if (list.Count > 0)
			{
				return list;
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if ((Object)(object)val != (Object)null)
			{
				list.Add(((Component)val).transform.position);
			}
			return list;
		}

		private static float HorizontalDistanceSqr(Vector3 a, Vector3 b)
		{
			//IL_0018: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			a.y = 0f;
			b.y = 0f;
			Vector3 val = a - b;
			return ((Vector3)(ref val)).sqrMagnitude;
		}
	}
	internal static class HudStatsProvider
	{
		private const float MapInterval = 0.5f;

		private const float CartInterval = 0.55f;

		private const float CosmeticInterval = 1.35f;

		private const float EnemyInterval = 0.6f;

		private static readonly Dictionary<string, string> RarityColors = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["common"] = "#55FF55",
			["uncommon"] = "#5599FF",
			["rare"] = "#BB55FF",
			["ultrarare"] = "#FF8800",
			["ultra"] = "#FF8800",
			["ultra rare"] = "#FF8800"
		};

		private static readonly string[] RarityOrder = new string[4] { "common", "uncommon", "rare", "ultrarare" };

		private static float _cachedMap;

		private static float _cachedCart;

		private static int _cachedEnemies;

		private static string _cachedCosmetics = string.Empty;

		private static bool _hasCosmetics;

		private static float _mapNext;

		private static float _cartNext;

		private static float _cosmeticNext;

		private static float _enemyNext;

		internal static void InvalidateCache()
		{
			_mapNext = 0f;
			_cartNext = 0f;
			_cosmeticNext = 0f;
			_enemyNext = 0f;
		}

		internal static void TickCache()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= _mapNext)
			{
				_cachedMap = ScanMapValue();
				_mapNext = unscaledTime + 0.5f;
			}
			if (unscaledTime >= _cartNext)
			{
				_cachedCart = ScanCartValue();
				_cartNext = unscaledTime + 0.55f;
			}
			if (unscaledTime >= _cosmeticNext)
			{
				_hasCosmetics = ScanCosmeticIconLine(out _cachedCosmetics);
				_cosmeticNext = unscaledTime + 1.35f;
			}
			if (unscaledTime >= _enemyNext)
			{
				_cachedEnemies = EnemyRoomCounter.ScanLiveCountInCurrentRooms();
				_enemyNext = unscaledTime + 0.6f;
			}
		}

		internal static float GetMapValue()
		{
			return _cachedMap;
		}

		internal static float GetCartValue()
		{
			return _cachedCart;
		}

		internal static int GetEnemyCount()
		{
			return _cachedEnemies;
		}

		internal static bool TryBuildCosmeticIconLine(out string line)
		{
			line = _cachedCosmetics;
			return _hasCosmetics;
		}

		private static float ScanMapValue()
		{
			float num = 0f;
			HashSet<PhysGrabObject> cartPhysObjects = GetCartPhysObjects();
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
			foreach (ValuableObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled)
				{
					PhysGrabObject component = ((Component)val).GetComponent<PhysGrabObject>();
					if (!((Object)(object)component != (Object)null) || !cartPhysObjects.Contains(component))
					{
						num += Mathf.Max(0f, val.dollarValueCurrent);
					}
				}
			}
			return num;
		}

		private static float ScanCartValue()
		{
			float num = 0f;
			PhysGrabCart[] allCarts = CartHelper.GetAllCarts();
			foreach (PhysGrabCart cart in allCarts)
			{
				foreach (PhysGrabObject cartItemObject in CartHelper.GetCartItemObjects(cart))
				{
					if (!((Object)(object)cartItemObject == (Object)null))
					{
						ValuableObject component = ((Component)cartItemObject).GetComponent<ValuableObject>();
						if ((Object)(object)component != (Object)null)
						{
							num += Mathf.Max(0f, component.dollarValueCurrent);
						}
					}
				}
			}
			return num;
		}

		private static bool ScanCosmeticIconLine(out string line)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			CosmeticWorldObject[] array = Object.FindObjectsOfType<CosmeticWorldObject>();
			foreach (CosmeticWorldObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled && !IsCosmeticBoxExtracted(val) && HudRoomHelper.IsInCurrentRoom(((Component)val).transform.position))
				{
					string text = NormalizeRarity(ReadCosmeticRarity(val));
					if (!string.IsNullOrWhiteSpace(text))
					{
						hashSet.Add(text);
					}
				}
			}
			if (hashSet.Count == 0)
			{
				line = string.Empty;
				return false;
			}
			StringBuilder stringBuilder = new StringBuilder();
			string[] rarityOrder = RarityOrder;
			foreach (string text2 in rarityOrder)
			{
				if (hashSet.Contains(text2))
				{
					string value;
					string text3 = (RarityColors.TryGetValue(text2, out value) ? value : "#DDDDDD");
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(' ');
					}
					stringBuilder.Append("<color=" + text3 + ">■</color>");
				}
			}
			line = stringBuilder.ToString();
			return line.Length > 0;
		}

		private static bool IsCosmeticBoxExtracted(CosmeticWorldObject box)
		{
			try
			{
				CosmeticWorldObjectHealth component = ((Component)box).GetComponent<CosmeticWorldObjectHealth>();
				if ((Object)(object)component != (Object)null)
				{
					FieldInfo field = typeof(CosmeticWorldObjectHealth).GetField("health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						object value = field.GetValue(component);
						if (value is float num && num <= 0f)
						{
							return true;
						}
						if (value is int num2 && num2 <= 0)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			string text = ((Object)((Component)box).gameObject).name.ToLowerInvariant();
			if (!text.Contains("extract") && !text.Contains("broken"))
			{
				return text.Contains("opened");
			}
			return true;
		}

		private static string ReadCosmeticRarity(CosmeticWorldObject box)
		{
			try
			{
				FieldInfo field = typeof(CosmeticWorldObject).GetField("cosmeticRarity", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value = field.GetValue(box);
					if (value != null)
					{
						return value.ToString() ?? string.Empty;
					}
				}
			}
			catch
			{
			}
			return GuessRarityFromName(((Object)((Component)box).gameObject).name);
		}

		private static string NormalizeRarity(string rarity)
		{
			string text = rarity.Replace("_", " ").Trim().ToLowerInvariant();
			if (text.Contains("ultra"))
			{
				return "ultrarare";
			}
			if (text.Contains("uncommon"))
			{
				return "uncommon";
			}
			if (text.Contains("common"))
			{
				return "common";
			}
			if (text.Contains("rare"))
			{
				return "rare";
			}
			return text;
		}

		private static string GuessRarityFromName(string name)
		{
			string text = name.ToLowerInvariant();
			if (text.Contains("ultra"))
			{
				return "ultrarare";
			}
			if (text.Contains("uncommon"))
			{
				return "uncommon";
			}
			if (text.Contains("rare"))
			{
				return "rare";
			}
			if (text.Contains("common"))
			{
				return "common";
			}
			return string.Empty;
		}

		private static HashSet<PhysGrabObject> GetCartPhysObjects()
		{
			HashSet<PhysGrabObject> hashSet = new HashSet<PhysGrabObject>();
			PhysGrabCart[] allCarts = CartHelper.GetAllCarts();
			foreach (PhysGrabCart cart in allCarts)
			{
				foreach (PhysGrabObject cartItemObject in CartHelper.GetCartItemObjects(cart))
				{
					if ((Object)(object)cartItemObject != (Object)null)
					{
						hashSet.Add(cartItemObject);
					}
				}
			}
			return hashSet;
		}
	}
	internal sealed class MapValueAnimator
	{
		private const float ScanDuration = 0.85f;

		private const float LossFadeDuration = 1.75f;

		private float _mapValue;

		private float _lossAmount;

		private float _lossFadeTimer;

		private float _scanTimer;

		private float _scanMax;

		private bool _scanReady;

		internal bool IsScanReady => _scanReady;

		internal bool IsAnimating => _lossFadeTimer > 0f;

		internal void BeginLevel()
		{
			_mapValue = 0f;
			_lossAmount = 0f;
			_lossFadeTimer = 0f;
			_scanTimer = 0.85f;
			_scanMax = 0f;
			_scanReady = false;
		}

		internal void EndLevel()
		{
			_scanReady = false;
			_scanTimer = 0f;
			_lossAmount = 0f;
			_lossFadeTimer = 0f;
		}

		internal void Tick(float actualValue, float deltaTime)
		{
			if (!_scanReady)
			{
				_scanMax = Mathf.Max(_scanMax, actualValue);
				_scanTimer -= deltaTime;
				if (_scanTimer <= 0f)
				{
					_mapValue = _scanMax;
					_scanReady = true;
				}
				return;
			}
			if (actualValue > _mapValue + 0.5f)
			{
				_mapValue = actualValue;
				return;
			}
			if (actualValue < _mapValue - 0.5f)
			{
				_lossAmount = _mapValue - actualValue;
				_mapValue = actualValue;
				_lossFadeTimer = 1.75f;
			}
			else
			{
				_mapValue = actualValue;
			}
			if (_lossFadeTimer > 0f)
			{
				_lossFadeTimer -= deltaTime;
				if (_lossFadeTimer <= 0f)
				{
					_lossAmount = 0f;
					_lossFadeTimer = 0f;
				}
			}
		}

		internal string? BuildMapLine()
		{
			if (!_scanReady)
			{
				return null;
			}
			if (_lossAmount > 0.5f && _lossFadeTimer > 0f)
			{
				float num = Mathf.Clamp01(_lossFadeTimer / 1.75f);
				string arg = Color32ToHex(byte.MaxValue, (byte)Mathf.RoundToInt(77f * num), (byte)Mathf.RoundToInt(77f * num));
				return $"<color=#{arg}>-${_lossAmount:N0}</color> MAP: ${_mapValue:N0}";
			}
			return $"MAP: ${_mapValue:N0}";
		}

		private static string Color32ToHex(byte r, byte g, byte b)
		{
			return $"{r:X2}{g:X2}{b:X2}";
		}
	}
	internal sealed class TokControlStatusHud : MonoBehaviour
	{
		private const float RefreshInterval = 0.4f;

		private const float LossRefreshInterval = 0.08f;

		private const float RepositionInterval = 1.5f;

		private const float LineHeight = 14f;

		private const float FontSize = 13f;

		private static TokControlStatusHud? _instance;

		private readonly MapValueAnimator _mapAnimator = new MapValueAnimator();

		private GameObject? _panelRoot;

		private TextMeshProUGUI? _label;

		private RectTransform? _panelRt;

		private RectTransform? _gameHudRt;

		private RectTransform? _taxHaulRt;

		private float _refreshTimer;

		private float _repositionTimer;

		private string _lastText = string.Empty;

		private int _trackedLevel = -1;

		private bool _wasInLevel;

		internal static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("TokControlStatusHud");
				Object.DontDestroyOnLoad((Object)(object)val);
				_instance = val.AddComponent<TokControlStatusHud>();
			}
		}

		private void Update()
		{
			bool flag = IsInPlayableLevel();
			if (flag && !_wasInLevel)
			{
				_trackedLevel = GetLevelId();
				_mapAnimator.BeginLevel();
				HudStatsProvider.InvalidateCache();
			}
			else if (flag)
			{
				int levelId = GetLevelId();
				if (levelId != _trackedLevel)
				{
					_trackedLevel = levelId;
					_mapAnimator.BeginLevel();
					HudStatsProvider.InvalidateCache();
				}
			}
			else if (_wasInLevel)
			{
				_trackedLevel = -1;
				_mapAnimator.EndLevel();
				HudStatsProvider.InvalidateCache();
			}
			_wasInLevel = flag;
			if (!ShouldShowHud())
			{
				HidePanel();
				return;
			}
			if (!EnsurePanel())
			{
				HidePanel();
				return;
			}
			_repositionTimer -= Time.unscaledDeltaTime;
			if (_repositionTimer <= 0f)
			{
				_repositionTimer = 1.5f;
				RepositionBelowHudStack();
			}
			HudStatsProvider.TickCache();
			_mapAnimator.Tick(HudStatsProvider.GetMapValue(), Time.unscaledDeltaTime);
			float refreshTimer = (_mapAnimator.IsAnimating ? 0.08f : 0.4f);
			_refreshTimer -= Time.unscaledDeltaTime;
			if (!(_refreshTimer > 0f) || !((Object)(object)_panelRoot != (Object)null) || !_panelRoot.activeSelf)
			{
				_refreshTimer = refreshTimer;
				string text = BuildStatusText();
				if (!(text == _lastText) || !((Object)(object)_panelRoot != (Object)null) || !_panelRoot.activeSelf)
				{
					_lastText = text;
					((TMP_Text)_label).SetText(text, true);
					_panelRoot.SetActive(true);
				}
			}
		}

		private string BuildStatusText()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			float cartValue = HudStatsProvider.GetCartValue();
			int enemyCount = HudStatsProvider.GetEnemyCount();
			string line;
			bool flag = HudStatsProvider.TryBuildCosmeticIconLine(out line);
			int num = 2;
			if (_mapAnimator.IsScanReady)
			{
				num++;
			}
			num++;
			if (flag)
			{
				num++;
			}
			if ((Object)(object)_panelRt != (Object)null)
			{
				_panelRt.sizeDelta = new Vector2(168f, 14f * (float)num);
			}
			StringBuilder stringBuilder = new StringBuilder();
			string value = _mapAnimator.BuildMapLine();
			if (!string.IsNullOrEmpty(value))
			{
				stringBuilder.AppendLine(value);
			}
			stringBuilder.AppendLine($"C.A.R.T.: ${cartValue:N0}");
			stringBuilder.AppendLine($"MON: {enemyCount}");
			if (flag)
			{
				stringBuilder.Append(line);
			}
			return stringBuilder.ToString().TrimEnd();
		}

		private static bool IsInPlayableLevel()
		{
			try
			{
				return !SemiFunc.MenuLevel() && SemiFunc.RunIsLevel();
			}
			catch
			{
				return (Object)(object)RunManager.instance != (Object)null;
			}
		}

		private static int GetLevelId()
		{
			try
			{
				Level val = RunManager.instance?.levelCurrent;
				return ((Object)(object)val == (Object)null) ? (-1) : ((object)val).GetHashCode();
			}
			catch
			{
				return -1;
			}
		}

		private static bool ShouldShowHud()
		{
			if (!IsInPlayableLevel())
			{
				return false;
			}
			if (IsMapOpen())
			{
				return false;
			}
			return true;
		}

		private static bool IsMapOpen()
		{
			try
			{
				if (SemiFunc.InputHold((InputKey)8))
				{
					return true;
				}
			}
			catch
			{
			}
			if (Input.GetKey((KeyCode)9))
			{
				return true;
			}
			try
			{
				if ((Object)(object)MapToolController.instance == (Object)null)
				{
					return false;
				}
				FieldInfo field = typeof(MapToolController).GetField("mapToggled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field?.FieldType == typeof(bool))
				{
					return (bool)field.GetValue(MapToolController.instance);
				}
			}
			catch
			{
			}
			return false;
		}

		private bool EnsurePanel()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panelRoot != (Object)null && (Object)(object)_label != (Object)null && (Object)(object)_panelRt != (Object)null)
			{
				return true;
			}
			GameObject val = GameObject.Find("Game Hud");
			GameObject val2 = GameObject.Find("Tax Haul");
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return false;
			}
			TMP_Text component = val2.GetComponent<TMP_Text>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.font == (Object)null)
			{
				return false;
			}
			_gameHudRt = val.GetComponent<RectTransform>();
			_taxHaulRt = val2.GetComponent<RectTransform>();
			_panelRoot = new GameObject("TokControl Status HUD");
			_panelRoot.SetActive(false);
			_label = _panelRoot.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_label).font = component.font;
			((TMP_Text)_label).fontSize = 13f;
			((TMP_Text)_label).lineSpacing = 0f;
			((TMP_Text)_label).paragraphSpacing = 0f;
			((TMP_Text)_label).enableWordWrapping = false;
			((TMP_Text)_label).alignment = (TextAlignmentOptions)260;
			((TMP_Text)_label).horizontalAlignment = (HorizontalAlignmentOptions)4;
			((TMP_Text)_label).verticalAlignment = (VerticalAlignmentOptions)256;
			((Graphic)_label).color = new Color(0.79f, 0.91f, 0.9f, 1f);
			((TMP_Text)_label).richText = true;
			((TMP_Text)_label).margin = new Vector4(0f, 0f, 0f, 0f);
			_panelRoot.transform.SetParent(val.transform, false);
			_panelRt = _panelRoot.GetComponent<RectTransform>();
			_panelRt.anchorMin = new Vector2(1f, 1f);
			_panelRt.anchorMax = new Vector2(1f, 1f);
			_panelRt.pivot = new Vector2(1f, 1f);
			_panelRt.sizeDelta = new Vector2(168f, 56f);
			return true;
		}

		private void RepositionBelowHudStack()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: 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_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panelRt == (Object)null)
			{
				return;
			}
			if ((Object)(object)_gameHudRt == (Object)null || (Object)(object)_taxHaulRt == (Object)null)
			{
				GameObject val = GameObject.Find("Game Hud");
				GameObject obj = GameObject.Find("Tax Haul");
				RectTransform val2 = ((obj != null) ? obj.GetComponent<RectTransform>() : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					return;
				}
				_gameHudRt = val.GetComponent<RectTransform>();
				_taxHaulRt = val2;
			}
			float num = _taxHaulRt.anchoredPosition.y;
			TMP_Text[] componentsInChildren = ((Component)_gameHudRt).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val3 in componentsInChildren)
			{
				if ((Object)(object)val3 == (Object)null || (Object)(object)val3 == (Object)(object)_label || ((Object)((Component)val3).gameObject).name.StartsWith("TokControl") || !((Component)val3).gameObject.activeInHierarchy)
				{
					continue;
				}
				RectTransform component = ((Component)val3).GetComponent<RectTransform>();
				if (!((Object)(object)component == (Object)null) && !(component.anchorMax.x < 0.55f))
				{
					float num2 = component.anchoredPosition.y - Mathf.Max(component.sizeDelta.y, val3.fontSize * 0.85f);
					if (num2 < num)
					{
						num = num2;
					}
				}
			}
			_panelRt.anchoredPosition = new Vector2(-12f, num - 4f);
		}

		private void HidePanel()
		{
			if ((Object)(object)_panelRoot != (Object)null)
			{
				_panelRoot.SetActive(false);
			}
			_lastText = string.Empty;
		}
	}
}
namespace TokControlREPOBridge.Network
{
	public static class EffectRelay
	{
		private const string EventName = "TokControl_EffectRelay_v1";

		private static NetworkedEvent? _relayEvent;

		public static void Initialize(GameActions actions)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			_relayEvent = new NetworkedEvent("TokControl_EffectRelay_v1", (Action<EventData>)OnRelayReceived);
			ModLog.Info("Effect relay initialized (client → host)");
		}

		public static CommandResult ExecuteEffect(string eventId, string user, int count = 1)
		{
			return StreamEventRunner.Execute(eventId, user, count);
		}

		public static CommandResult RelayKnownEvent(string eventId, string user, int count = 1, int playerViewId = 0, string? namedPlayer = null)
		{
			return RelayToHost(eventId, user, playerViewId, count, namedPlayer);
		}

		private static CommandResult RelayToHost(string eventId, string user, int playerViewId, int count, string? namedPlayer = null)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (_relayEvent == null)
			{
				return CommandResult.Fail("relay_not_ready");
			}
			count = Math.Max(1, Math.Min(count, 100));
			try
			{
				string text = SimpleJson.EffectPayload(eventId, user, playerViewId, count, namedPlayer);
				_relayEvent.RaiseEvent((object)text, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
				ModLog.Info($"Effect relayed to host: {eventId} x{count} for @{user} (view={playerViewId} name={namedPlayer})");
				return CommandResult.Ok("relayed_to_host", $"Effect {eventId} x{count} sent to lobby host");
			}
			catch (Exception ex)
			{
				ModLog.Error("Effect relay failed: " + ex.Message);
				return CommandResult.Fail("relay_failed:" + ex.Message);
			}
		}

		private static void OnRelayReceived(EventData eventData)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				ModLog.Debug("Effect relay received on non-host — ignored");
				return;
			}
			try
			{
				string text = eventData.CustomData as string;
				if (string.IsNullOrWhiteSpace(text))
				{
					ModLog.Warn("Effect relay payload empty");
					return;
				}
				if (!SimpleJson.TryParseEffectPayload(text, out string eventId, out string user, out int playerViewId, out int count, out string namedPlayer))
				{
					ModLog.Warn("Effect relay payload invalid");
					return;
				}
				ModLog.Info($"Host executing effect relay: {eventId} x{count} for @{user} view={playerViewId} name={namedPlayer}");
				MainThreadDispatcher.Enqueue(delegate
				{
					string commandLine;
					if (!string.IsNullOrWhiteSpace(namedPlayer))
					{
						StreamEventRunner.Execute(eventId, user ?? "viewer", count, null, namedPlayer);
					}
					else if (!EventCommandCatalog.TryGetCommandLine(eventId, out commandLine))
					{
						ModLog.Warn("Host relay unknown event: " + eventId);
					}
					else
					{
						PlayerAvatar targetPlayer = null;
						if (playerViewId > 0)
						{
							try
							{
								targetPlayer = SemiFunc.PlayerAvatarGetFromPhotonID(playerViewId);
							}
							catch
							{
							}
						}
						StreamEventRunner.ExecuteLocal(eventId, commandLine, user ?? "viewer", count, targetPlayer);
					}
				});
			}
			catch (Exception ex)
			{
				ModLog.Error("Effect relay handler error: " + ex.Message);
			}
		}
	}
	public static class SpawnRelay
	{
		private const string EventName = "TokControl_SpawnRelay_v1";

		private static NetworkedEvent? _relayEvent;

		private static GameActions? _actions;

		public static void Initialize(GameActions actions)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			_actions = actions;
			_relayEvent = new NetworkedEvent("TokControl_SpawnRelay_v1", (Action<EventData>)OnRelayReceived);
			ModLog.Info("Spawn relay initialized (client → host)");
		}

		public static CommandResult ExecuteSpawn(string cmd, string name, int count, string user)
		{
			if (_actions == null)
			{
				return CommandResult.Fail("relay_not_ready");
			}
			if (!MainThreadDispatcher.IsReady)
			{
				return CommandResult.Fail("game_not_ready");
			}
			if (!RunGate.IsReadyForGameEvents())
			{
				return CommandResult.Fail("game_not_ready");
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			int playerViewId = 0;
			try
			{
				playerViewId = (((Object)(object)val?.photonView != (Object)null) ? val.photonView.ViewID : 0);
			}
			catch
			{
			}
			if (SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.IsMultiplayer())
			{
				return ExecuteLocally(cmd, name, count, user, val);
			}
			return RelayToHost(cmd, name, count, user, playerViewId);
		}

		private static CommandResult RelayToHost(string cmd, string name, int count, string user, int playerViewId)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (_relayEvent == null)
			{
				return CommandResult.Fail("relay_not_ready");
			}
			try
			{
				string text = SimpleJson.SpawnPayload(cmd, name, count, user, playerViewId);
				_relayEvent.RaiseEvent((object)text, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
				ModLog.Info($"Relayed to host: {cmd} {name} x{count} for @{user} (view={playerViewId})");
				return CommandResult.Ok("relayed_to_host", "Spawn sent to lobby host — host must have this mod installed");
			}
			catch (Exception ex)
			{
				ModLog.Error("Relay failed: " + ex.Message);
				return CommandResult.Fail("relay_failed:" + ex.Message);
			}
		}

		private static void OnRelayReceived(EventData eventData)
		{
			if (_actions == null)
			{
				return;
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				ModLog.Debug("Relay received on non-host — ignored");
				return;
			}
			try
			{
				string text = eventData.CustomData as string;
				if (string.IsNullOrWhiteSpace(text))
				{
					ModLog.Warn("Relay payload empty");
					return;
				}
				if (!SimpleJson.TryParseSpawnPayload(text, out string cmd, out string name, out int count, out string user, out int playerViewId))
				{
					ModLog.Warn("Relay payload invalid");
					return;
				}
				ModLog.Info($"Host executing relay: {cmd} {name} x{count} for @{user} view={playerViewId}");
				MainThreadDispatcher.Enqueue(delegate
				{
					PlayerAvatar targetPlayer = null;
					if (playerViewId > 0)
					{
						try
						{
							targetPlayer = SemiFunc.PlayerAvatarGetFromPhotonID(playerViewId);
						}
						catch
						{
						}
					}
					ExecuteLocally(cmd, name, count, user ?? "viewer", targetPlayer);
				});
			}
			catch (Exception ex)
			{
				ModLog.Error("Relay handler error: " + ex.Message);
			}
		}

		private static CommandResult ExecuteLocally(string cmd, string name, int count, string user, PlayerAvatar? targetPlayer)
		{
			if (_actions == null)
			{
				return CommandResult.Fail("actions_not_ready");
			}
			count = Math.Max(1, Math.Min(count, 100));
			cmd = cmd.Trim().ToLowerInvariant();
			EventContext.SetTarget(targetPlayer ?? SemiFunc.PlayerAvatarLocal());
			try
			{
				switch (cmd)
				{
				case "spawn_item":
				case "spawnitem":
				case "item":
					return _actions.SpawnItemLocal(name, count, user);
				case "spawnghost":
				case "spawn_ghost":
				case "ghost":
					return _actions.SpawnEnemyLocal(name, count, user);
				case "spawnenemy":
				case "spawn_enemy":
				case "enemy":
					return _actions.SpawnEnemyLocal(name, count, user);
				case "spawn_valuable":
				case "spawnvaluable":
				case "valuable":
					return _actions.SpawnValuableLocal(name, count, user);
				case "spawnbatch":
				case "spawn_batch":
				case "batch":
					return _actions.SpawnBatchLocal(name, user);
				default:
					return CommandResult.Fail("unknown_spawn_cmd:" + cmd);
				}
			}
			finally
			{
				EventContext.Clear();
			}
		}
	}
	internal static class SpeakBroadcast
	{
		private const string EventName = "TokControl_SpeakBroadcast_v1";

		private static NetworkedEvent? _event;

		private static bool _handling;

		public static void Initialize()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			_event = new NetworkedEvent("TokControl_SpeakBroadcast_v1", (Action<EventData>)OnReceived);
			ModLog.Info("Speak broadcast initialized");
		}

		public static void Broadcast(string message)
		{
			if (!string.IsNullOrWhiteSpace(message))
			{
				SpeakHelper.ForceSpeakNow(message);
				RaiseOthers(message);
			}
		}

		public static void RaiseOthers(string message)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(message) || _event == null)
			{
				return;
			}
			try
			{
				if (SemiFunc.IsMultiplayer())
				{
					_event.RaiseEvent((object)message, NetworkingEvents.RaiseOthers, SendOptions.SendReliable);
				}
			}
			catch (Exception ex)
			{
				ModLog.Debug("Speak broadcast failed: " + ex.Message);
			}
		}

		private static void OnReceived(EventData eventData)
		{
			if (_handling)
			{
				return;
			}
			string raw = eventData.CustomData as string;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			MainThreadDispatcher.Enqueue(delegate
			{
				_handling = true;
				try
				{
					SpeakHelper.ForceSpeakNow(raw);
				}
				finally
				{
					_handling = false;
				}
			});
		}
	}
	public sealed class WebSocketServer : IDisposable
	{
		private readonly int _preferredPort;

		private readonly CommandProcessor _processor;

		private readonly CancellationTokenSource _cts = new CancellationTokenSource();

		private HttpListener? _listener;

		private Task? _acceptTask;

		public int Port { get; private set; }

		public WebSocketServer(int port, CommandProcessor processor)
		{
			_preferredPort = ((port > 0) ? port : 8080);
			Port = _preferredPort;
			_processor = processor;
		}

		public void Start()
		{
			int[] array = BuildPortCandidates(_preferredPort);
			Exception ex = null;
			int[] array2 = array;
			foreach (int num in array2)
			{
				HttpListener httpListener = null;
				try
				{
					httpListener = new HttpListener();
					httpListener.Prefixes.Add($"http://127.0.0.1:{num}/");
					httpListener.Start();
					_listener = httpListener;
					Port = num;
					_acceptTask = Task.Run(() => AcceptLoopAsync(_cts.Token));
					if (num != _preferredPort)
					{
						ModLog.Warn($"Port {_preferredPort} was busy — TokControl bridge moved to ws://127.0.0.1:{num}/");
						ModLog.Warn($"Set TokControl Connection URL to: ws://127.0.0.1:{num}/");
					}
					else
					{
						ModLog.Info($"HTTP/WebSocket listener started on port {num} (127.0.0.1 only)");
					}
					return;
				}
				catch (Exception ex2)
				{
					ex = ex2;
					try
					{
						httpListener?.Close();
					}
					catch
					{
					}
					ModLog.Warn($"Could not bind 127.0.0.1:{num} — {ex2.Message}");
				}
			}
			throw new InvalidOperationException("TokControl bridge failed to bind any port (tried " + string.Join(", ", array) + "). Last error: " + ex?.Message);
		}

		private static int[] BuildPortCandidates(int preferred)
		{
			List<int> list = new List<int> { preferred };
			int[] array = new int[5] { 8080, 8082, 8090, 18080, 28080 };
			foreach (int item in array)
			{
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			return list.ToArray();
		}

		private async Task AcceptLoopAsync(CancellationToken ct)
		{
			while (!ct.IsCancellationRequested && _listener != null && _listener.IsListening)
			{
				HttpListenerContext context = null;
				try
				{
					context = await _listener.GetContextAsync().ConfigureAwait(continueOnCapturedContext: false);
				}
				catch (HttpListenerException) when (ct.IsCancellationRequested)
				{
					break;
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (Exception ex3)
				{
					ModLog.Warn("Accept error: " + ex3.Message);
					continue;
				}
				Task.Run(() => HandleContextAsync(context, ct), ct);
			}
		}

		private async Task HandleContextAsync(HttpListenerContext context, CancellationToken ct)
		{
			_ = 3;
			try
			{
				if (context.Request.IsWebSocketRequest)
				{
					await HandleWebSocketAsync((await context.AcceptWebSocketAsync(null).ConfigureAwait(continueOnCapturedContext: false)).WebSocket, ct).ConfigureAwait(continueOnCapturedContext: false);
					return;
				}
				string text = context.Request.Url?.AbsolutePath ?? "/";
				string s;
				if (text.Equals("/health", StringComparison.OrdinalIgnoreCase))
				{
					s = "{\"ok\":true,\"mod\":\"TokControl_REPO_Tiktoklive\",\"version\":\"1.3.91\",\"port\":" + Port + "}";
				}
				else if (context.Request.HttpMethod == "POST")
				{
					using StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding);
					string raw = await reader.ReadToEndAsync().ConfigureAwait(continueOnCapturedContext: false);
					CommandResult commandResult = _processor.Process(raw);
					s = commandResult.ToJson();
				}
				else
				{
					s = "{\"ok\":true,\"mod\":\"TokControl_REPO_Tiktoklive\",\"hint\":\"Connect via WebSocket ws://127.0.0.1:" + Port + "/\"}";
				}
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				context.Response.StatusCode = 200;
				context.Response.ContentType = "application/json";
				context.Response.ContentLength64 = bytes.Length;
				await context.Response.OutputStream.WriteAsync(bytes, 0, bytes.Length, ct).ConfigureAwait(continueOnCapturedContext: false);
				context.Response.Close();
			}
			catch (Exception ex)
			{
				ModLog.Warn("Request handler error: " + ex.Message);
				try
				{
					context.Response.StatusCode = 500;
					context.Response.Close();
				}
				catch
				{
				}
			}
		}

		private async Task HandleWebSocketAsync(WebSocket socket, CancellationToken ct)
		{
			byte[] buffer = new byte[8192];
			ModLog.Info("WebSocket client connected");
			try
			{
				while (socket.State == WebSocketState.Open && !ct.IsCancellationRequested)
				{
					WebSocketReceiveResult webSocketReceiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), ct).ConfigureAwait(continueOnCapturedContext: false);
					if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
					{
						break;
					}
					if (webSocketReceiveResult.MessageType != WebSocketMessageType.Text)
					{
						continue;
					}
					string message = Encoding.UTF8.GetString(buffer, 0, webSocketReceiveResult.Count);
					Task.Run(delegate
					{
						try
						{
							CommandResult commandResult = _processor.Process(message);
							byte[] bytes = Encoding.UTF8.GetBytes(commandResult.ToJson());
							socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, endOfMessage: true, ct);
						}
						catch (Exception ex3)
						{
							ModLog.Warn("WS message error: " + ex3.Message);
						}
					}, ct);
				}
			}
			catch (WebSocketException ex)
			{
				ModLog.Debug("WebSocket closed: " + ex.Message);
			}
			catch (Exception ex2)
			{
				ModLog.Warn("WebSocket error: " + ex2.Message);
			}
			finally
			{
				ModLog.Info("WebSocket client disconnected");
				try
				{
					if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived)
					{
						await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "bye", CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false);
					}
				}
				catch
				{
				}
				socket.Dispose();
			}
		}

		public void Dispose()
		{
			_cts.Cancel();
			try
			{
				_listener?.Stop();
			}
			catch
			{
			}
			try
			{
				_listener?.Close();
			}
			catch
			{
			}
			_listener = null;
			_cts.Dispose();
		}
	}
}
namespace TokControlREPOBridge.Logging
{
	public static class ModLog
	{
		public static void Info(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)Format(message));
			}
			if (Plugin.ShouldLogToUnity())
			{
				Debug.Log((object)Format(message));
			}
		}

		public static void Warn(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)Format(message));
			}
			if (Plugin.ShouldLogToUnity())
			{
				Debug.LogWarning((object)Format(message));
			}
		}

		public static void Error(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogError((object)Format(message));
			}
			if (Plugin.ShouldLogToUnity())
			{
				Debug.LogError((object)Format(message));
			}
		}

		public static void Debug(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogDebug((object)Format(message));
			}
		}

		private static string Format(string message)
		{
			return "[TokControl] " + message;
		}
	}
}
namespace TokControlREPOBridge.Commands
{
	internal static class ArenaHelper
	{
		public static bool IsPlayerInCrownArenaBeforeStart(PlayerAvatar player)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Arena instance = Arena.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!IsBeforeCrownContestStart(instance))
			{
				return false;
			}
			Vector3 arenaCenter = GetArenaCenter(instance);
			float num = Vector2.Distance(new Vector2(((Component)player).transform.position.x, ((Component)player).transform.position.z), new Vector2(arenaCenter.x, arenaCenter.z));
			if (num < 18f)
			{
				return Mathf.Abs(((Component)player).transform.position.y - arenaCenter.y) < 10f;
			}
			return false;
		}

		public static bool IsContestMap()
		{
			if ((Object)(object)Arena.instance != (Object)null)
			{
				return true;
			}
			if ((Object)(object)ArenaRace.instance != (Object)null)
			{
				return true;
			}
			try
			{
				List<SpawnPoint> list = FindSpawnPoints();
				if (list.Count < 2)
				{
					return false;
				}
				List<LevelPoint> list2 = null;
				try
				{
					list2 = SemiFunc.LevelPointsGetAll();
				}
				catch
				{
				}
				return list2 == null || list2.Count <= 8;
			}
			catch
			{
				return false;
			}
		}

		public static Vector3? GetContestTeleportPosition(Vector3 avoidNear, Vector3 playerForward)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ArenaRace.instance != (Object)null)
			{
				Vector3? farthestSpawnPosition = GetFarthestSpawnPosition(avoidNear, 64f);
				if (farthestSpawnPosition.HasValue)
				{
					return farthestSpawnPosition;
				}
				Vector3? raceStartPosition = GetRaceStartPosition(avoidNear);
				if (raceStartPosition.HasValue && FarEnough(raceStartPosition.Value, avoidNear, 64f))
				{
					return raceStartPosition;
				}
				Vector3? farthestRaceTrackPosition = GetFarthestRaceTrackPosition(avoidNear, 64f);
				if (farthestRaceTrackPosition.HasValue)
				{
					return farthestRaceTrackPosition;
				}
				Vector3? backwardOnTrack = GetBackwardOnTrack(avoidNear, playerForward, 64f);
				if (backwardOnTrack.HasValue && FarEnough(backwardOnTrack.Value, avoidNear, 64f))
				{
					return backwardOnTrack;
				}
				return OffsetFarBehind(avoidNear, playerForward);
			}
			if ((Object)(object)Arena.instance != (Object)null && (Object)(object)ArenaRace.instance == (Object)null)
			{
				Vector3? result = GetFarthestSpawnPosition(avoidNear, 64f) ?? GetRandomSpawnPosition(avoidNear);
				if (result.HasValue)
				{
					return result;
				}
			}
			return (GetFarthestRaceTrackPosition(avoidNear, 64f) ?? GetRandomRaceTrackPosition(avoidNear)) ?? GetFarthestSpawnPosition(avoidNear, 64f) ?? GetRandomSpawnPosition(avoidNear);
		}

		public static Vector3? GetContestTeleportPosition(Vector3 avoidNear)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return GetContestTeleportPosition(avoidNear, Vector3.back);
		}

		public static Vector3? GetRandomSpawnPosition(Vector3 avoidNear)
		{
			//IL_0007: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//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)
			List<Vector3> list = (from p in FindSpawnPoints()
				select ((Component)p).transform.position).ToList();
			if (list.Count == 0)
			{
				return CartHelper.GetRandomPlayerSpawnPoint(avoidNear);
			}
			List<Vector3> list2 = list.Where(delegate(Vector3 pos)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = pos - avoidNear;
				return ((Vector3)(ref val)).sqrMagnitude > 2.25f;
			}).ToList();
			List<Vector3> list3 = ((list2.Count > 0) ? list2 : list);
			Vector3 position = list3[Random.Range(0, list3.Count)];
			return KeepOnSurface(position, avoidNear);
		}

		private static Vector3? GetFarthestSpawnPosition(Vector3 avoidNear, float minSepSqr)
		{
			//IL_0007: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = (from p in FindSpawnPoints()
				select ((Component)p).transform.position).ToList();
			if (list.Count == 0)
			{
				Vector3? randomPlayerSpawnPoint = CartHelper.GetRandomPlayerSpawnPoint(avoidNear);
				if (randomPlayerSpawnPoint.HasValue && FarEnough(randomPlayerSpawnPoint.Value, avoidNear, minSepSqr))
				{
					return KeepOnSurface(randomPlayerSpawnPoint.Value, avoidNear);
				}
				return null;
			}
			List<Vector3> list2 = list.OrderByDescending((Vector3 pos) => HorizontalSqr(pos, avoidNear)).ToList();
			foreach (Vector3 item in list2)
			{
				if (FarEnough(item, avoidNear, minSepSqr))
				{
					ModLog.Info("Contest teleport: farthest spawn / race start");
					return KeepOnSurface(item, avoidNear);
				}
			}
			if (list2.Count > 0 && FarEnough(list2[0], avoidNear, 4f))
			{
				return KeepOnSurface(list2[0], avoidNear);
			}
			return null;
		}

		public static Vector3 GetCrownArenaDropPosition()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			Vector3? randomSpawnPosition = GetRandomSpawnPosition(Vector3.zero);
			if (randomSpawnPosition.HasValue)
			{
				return randomSpawnPosition.Value;
			}
			Arena instance = Arena.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return Vector3.zero;
			}
			Vector3 val = (((Object)(object)instance.crownTransform != (Object)null) ? instance.crownTransform.position : (((Object)(object)instance.crownPlatform != (Object)null) ? instance.crownPlatform.transform.position : ((Component)instance).transform.position));
			return KeepOnSurface(val + Vector3.up * 1.2f);
		}

		public static Vector3 KeepOnSurface(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return KeepOnSurface(position, null);
		}

		public static Vector3 KeepOnSurface(Vector3 position, Vector3? ignoreNear)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = position + Vector3.up * 8f;
			RaycastHit[] array = Physics.RaycastAll(val, Vector3.down, 24f, -1, (QueryTriggerInteraction)1);
			if (array != null && array.Length != 0)
			{
				Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				RaycastHit[] array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					RaycastHit val2 = array2[num];
					if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !(((RaycastHit)(ref val2)).normal.y < 0.35f) && (!ignoreNear.HasValue || !(HorizontalSqr(((RaycastHit)(ref val2)).point, ignoreNear.Value) < 16f)) && !LooksLikeVehicle(((RaycastHit)(ref val2)).collider))
					{
						return ((RaycastHit)(ref val2)).point + Vector3.up * 0.35f;
					}
				}
			}
			return position + Vector3.up * 0.4f;
		}

		private static bool LooksLikeVehicle(Collider col)
		{
			Transform val = ((Component)col).transform;
			for (int i = 0; i < 6; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				string text = ((Object)val).name.ToLowerInvariant();
				if (text.Contains("vehicle") || text.Contains("scooter") || text.Contains("cart") || text.Contains("car") || text.Contains("buggy") || text.Contains("truck"))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static bool FarEnough(Vector3 a, Vector3 b, float minSepSqr)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return HorizontalSqr(a, b) >= minSepSqr;
		}

		private static float HorizontalSqr(Vector3 a, Vector3 b)
		{
			//IL_0000: 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_000e: 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)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return num * num + num2 * num2;
		}

		private static Vector3? GetRaceStartPosition(Vector3 avoidNear)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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)
			List<SpawnPoint> list = FindSpawnPoints();
			if (list.Count > 0)
			{
				return KeepOnSurface(((Component)list[0]).transform.position, avoidNear);
			}
			try
			{
				ArenaRace instance = ArenaRace.instance;
				if ((Object)(object)instance != (Object)null)
				{
					return KeepOnSurface(((Component)instance).transform.position + Vector3.up * 1.2f, avoidNear);
				}
			}
			catch
			{
			}
			return null;
		}

		private static Vector3? GetRaceStartPosition()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GetRaceStartPosition(Vector3.zero);
		}

		private static Vector3 OffsetFarBehind(Vector3 from, Vector3 forward)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = Vector3.back;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 position = from - forward * Random.Range(28f, 42f) + Vector3.up * 1.2f;
			ModLog.Info("Contest teleport: far reverse offset");
			return KeepOnSurface(position, from);
		}

		private static Vector3? GetBackwardOnTrack(Vector3 from, Vector3 forward, float minSepSqr)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = Vector3.back;
			}
			((Vector3)(ref forward)).Normalize();
			try
			{
				ArenaRaceTrackPiece[] array = Object.FindObjectsOfType<ArenaRaceTrackPiece>();
				if (array != null && array.Length != 0)
				{
					ArenaRaceTrackPiece val = null;
					float num = 1f;
					float num2 = 0f;
					ArenaRaceTrackPiece[] array2 = array;
					foreach (ArenaRaceTrackPiece val2 in array2)
					{
						if ((Object)(object)val2 == (Object)null || !((Behaviour)val2).isActiveAndEnabled)
						{
							continue;
						}
						Vector3 val3 = ((Component)val2).transform.position - from;
						val3.y = 0f;
						float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
						if (!(sqrMagnitude < minSepSqr))
						{
							float num3 = Vector3.Dot(((Vector3)(ref val3)).normalized, forward);
							if (!(num3 >= num))
							{
								num = num3;
								num2 = sqrMagnitude;
								val = val2;
							}
						}
					}
					if ((Object)(object)val != (Object)null && num < -0.05f && num2 >= minSepSqr)
					{
						Collider componentInChildren = ((Component)val).GetComponentInChildren<Collider>();
						Vector3 val4;
						if (!((Object)(object)componentInChildren != (Object)null))
						{
							val4 = ((Component)val).transform.position + Vector3.up * 1.5f;
						}
						else
						{
							Bounds bounds = componentInChildren.bounds;
							val4 = ((Bounds)(ref bounds)).center + Vector3.up * 1.15f;
						}
						Vector3 position = val4;
						ModLog.Info("Contest teleport: reverse track piece '" + ((Object)val).name + "'");
						return KeepOnSurface(position, from);
					}
				}
			}
			catch (Exception ex)
			{
				ModLog.Debug("GetBackwardOnTrack failed: " + ex.Message);
			}
			return OffsetFarBehind(from, forward);
		}

		private static Vector3? GetFarthestRaceTrackPosition(Vector3 avoidNear, float minSepSqr)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: 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_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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)
			//IL_0105: 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)
			try
			{
				ArenaRaceTrackPiece[] array = Object.FindObjectsOfType<ArenaRaceTrackPiece>();
				if (array == null || array.Length == 0)
				{
					return null;
				}
				ArenaRaceTrackPiece val = null;
				float num = -1f;
				ArenaRaceTrackPiece[] array2 = array;
				foreach (ArenaRaceTrackPiece val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null) && ((Behaviour)val2).isActiveAndEnabled)
					{
						float num2 = HorizontalSqr(((Component)val2).transform.position, avoidNear);
						if (!(num2 <= num))
						{
							num = num2;
							val = val2;
						}
					}
				}
				if ((Object)(object)val == (Object)null || num < minSepSqr)
				{
					return null;
				}
				Collider componentInChildren = ((Component)val).GetComponentInChildren<Collider>();
				Vector3 val3;
				if (!((Object)(object)componentInChildren != (Object)null))
				{
					val3 = ((Component)val).transform.position + Vector3.up * 1.5f;
				}
				else
				{
					Bounds bounds = componentInChildren.bounds;
					val3 = ((Bounds)(ref bounds)).center + Vector3.up * 1.15f;
				}
				Vector3 position = val3;
				ModLog.Info("Contest teleport: farthest track piece '" + ((Object)val).name + "'");
				return KeepOnSurface(position, avoidNear);
			}
			catch (Exception ex)
			{
				ModLog.Debug("GetFarthestRaceTrackPosition failed: " + ex.Message);
				return null;
			}
		}

		private static Vector3? GetRandomRaceTrackPosition(Vector3 avoidNear)
		{
			//IL_0007: 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_00e3: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ArenaRaceTrackPiece[] array = Object.FindObjectsOfType<ArenaRaceTrackPiece>();
				if (array == null || array.Length == 0)
				{
					return null;
				}
				List<ArenaRaceTrackPiece> list = array.Where((ArenaRaceTrackPiece p) => (Object)(object)p != (Object)null && ((Behaviour)p).isActiveAndEnabled).ToList();
				if (list.Count == 0)
				{
					return null;
				}
				List<ArenaRaceTrackPiece> list2 = list.Where((ArenaRaceTrackPiece p) => HorizontalSqr(((Component)p).transform.position, avoidNear) > 64f).ToList();
				List<ArenaRaceTrackPiece> list3 = ((list2.Count > 0) ? list2 : list);
				ArenaRaceTrackPiece val = list3[Random.Range(0, list3.Count)];
				Collider componentInChildren = ((Component)val).GetComponentInChildren<Collider>();
				Vector3 val2;
				if (!((Object)(object)componentInChildren != (Object)null))
				{
					val2 = ((Component)val).transform.position + Vector3.up * 1.5f;
				}
				else
				{
					Bounds bounds = componentInChildren.bounds;
					val2 = ((Bounds)(ref bounds)).center + Vector3.up * 1.15f;
				}
				Vector3 position = val2;
				ModLog.Info("Contest teleport: driving track piece '" + ((Object)val).name + "'");
				return KeepOnSurface(position, avoidNear);
			}
			catch (Exception ex)
			{
				ModLog.Debug("GetRandomRaceTrackPosition failed: " + ex.Message);
				return null;
			}
		}

		private static List<SpawnPoint> FindSpawnPoints()
		{
			SpawnPoint[] array = null;
			try
			{
				array = Object.FindObjectsOfType<SpawnPoint>(true);
			}
			catch
			{
				array = Object.FindObjectsOfType<SpawnPoint>();
			}
			if (array != null)
			{
				return array.Where((SpawnPoint p) => (Object)(object)p != (Object)null).ToList();
			}
			return new List<SpawnPoint>();
		}

		private static Vector3 GetArenaCenter(Arena arena)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)arena.crownTransform != (Object)null)
			{
				return arena.crownTransform.position;
			}
			if ((Object)(object)arena.floorDoorTransform != (Object)null)
			{
				return arena.floorDoorTransform.position;
			}
			return ((Component)arena).transform.position;
		}

		private static bool IsBeforeCrownContestStart(Arena arena)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0039: Invalid comparison between Unknown and I4
			try
			{
				if (typeof(Arena).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(arena) is States val)
				{
					if ((int)val == 0 || (int)val == 5)
					{
						return true;
					}
					return false;
				}
			}
			catch
			{
			}
			return true;
		}
	}
	internal static class CartHelper
	{
		public static PhysGrabCart[] GetAllCarts()
		{
			return (from c in Object.FindObjectsOfType<PhysGrabCart>()
				where (Object)(object)c != (Object)null && ((Behaviour)c).isActiveAndEnabled
				select c).ToArray();
		}

		public static List<PhysGrabObject> GetCartItemObjects(PhysGrabCart cart)
		{
			if (cart?.itemsInCart == null || cart.itemsInCart.Count == 0)
			{
				return new List<PhysGrabObject>();
			}
			return cart.itemsInCart.Where((PhysGrabObject o) => (Object)(object)o != (Object)null).ToList();
		}

		public static bool TeleportCart(PhysGrabCart cart, Vector3 targetPosition)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)cart == (Object)null)
			{
				return false;
			}
			Rigidbody rb = cart.rb;
			if ((Object)(object)rb == (Object)null)
			{
				return false;
			}
			float cartHeight = GetCartHeight(cart);
			Vector3 val = targetPosition + Vector3.up * cartHeight;
			Vector3 position = rb.position;
			rb.position = val;
			rb.velocity = Vector3.zero;
			rb.angularVelocity = Vector3.zero;
			TeleportItemsInCart(cart, val, position);
			return true;
		}

		public static void TeleportItemsInCart(PhysGrabCart cart, Vector3 newPos, Vector3 oldPos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = newPos - oldPos;
			foreach (PhysGrabObject cartItemObject in GetCartItemObjects(cart))
			{
				try
				{
					cartItemObject.Teleport(((Component)cartItemObject).transform.position + val, ((Component)cartItemObject).transform.rotation);
				}
				catch (Exception ex)
				{
					ModLog.Debug("Cart item teleport failed: " + ex.Message);
				}
			}
		}

		public static void ShakeItemsInAllCarts(float minForce, float maxForce, float minDelay, float maxDelay)
		{
			PhysGrabCart[] allCarts = GetAllCarts();
			if (allCarts.Length != 0)
			{
				EffectTimerHost.Instance.RunRoutine(ShakeAllCartsRoutine(allCarts, minForce, maxForce, minDelay, maxDelay));
			}
		}

		public static bool TeleportAllCarts(bool toStart)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			PhysGrabCart[] allCarts = GetAllCarts();
			if (allCarts.Length == 0)
			{
				return false;
			}
			LevelPoint lastPoint = null;
			bool result = false;
			PhysGrabCart[] array = allCarts;
			foreach (PhysGrabCart cart in array)
			{
				Vector3? val = (toStart ? GetNextStartRoomPoint(ref lastPoint) : GetNextRandomMapPoint(ref lastPoint, excludePlayerRooms: true));
				if (val.HasValue && TeleportCart(cart, val.Value))
				{
					result = true;
				}
			}
			return result;
		}

		private static IEnumerator ShakeAllCartsRoutine(PhysGrabCart[] carts, float minForce, float maxForce, float minDelay, float maxDelay)
		{
			float num = Mathf.Max(0f, Mathf.Min(minDelay, maxDelay));
			float num2 = Mathf.Max(num, Mathf.Max(minDelay, maxDelay));
			if (num2 > 0.001f)
			{
				yield return (object)new WaitForSeconds(Random.Range(num, num2));
			}
			else
			{
				yield return null;
			}
			foreach (PhysGrabCart val in carts)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				foreach (PhysGrabObject cartItemObject in GetCartItemObjects(val))
				{
					Rigidbody rb = cartItemObject.rb;
					if (!((Object)(object)rb == (Object)null))
					{
						rb.isKinematic = false;
						rb.WakeUp();
						float num3 = Random.Range(minForce, maxForce);
						rb.velocity = Vector3.zero;
						rb.angularVelocity = Vector3.zero;
						rb.AddForce(Vector3.up * num3, (ForceMode)1);
						CartItemScatterBehavior cartItemScatterBehavior = ((Component)cartItemObject).gameObject.GetComponent<CartItemScatterBehavior>() ?? ((Component)cartItemObject).gameObject.AddComponent<CartItemScatterBehavior>();
						cartItemScatterBehavior.Begin();
					}
				}
			}
		}

		private static float GetCartHeight(PhysGrabCart cart)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Collider componentInParent = ((Component)cart).GetComponentInParent<Collider>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return 1f;
			}
			Bounds bounds = componentInParent.bounds;
			return ((Bounds)(ref bounds)).size.y;
		}

		private static Vector3? GetNextStartRoomPoint(ref LevelPoint? lastPoint)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				List<LevelPoint> list = SemiFunc.LevelPointsGetInStartRoom();
				if (list == null || list.Count == 0)
				{
					return GetPlayerSpawnPoint();
				}
				lastPoint = PickNextPoint(list, lastPoint);
				LevelPoint? obj = lastPoint;
				return (obj != null) ? new Vector3?(((Component)obj).transform.position) : ((Vector3?)null);
			}
			catch (Exception ex)
			{
				ModLog.Debug("GetNextStartRoomPoint failed: " + ex.Message);
				return GetPlayerSpawnPoint();
			}
		}

		private static Vector3? GetNextRandomMapPoint(ref LevelPoint? lastPoint, bool excludePlayerRooms)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				List<LevelPoint> list = SemiFunc.LevelPointsGetAll();
				if (list == null || list.Count == 0)
				{
					return null;
				}
				if (excludePlayerRooms)
				{
					List<LevelPoint> playerRooms = SemiFunc.LevelPointsGetInPlayerRooms() ?? new List<LevelPoint>();
					list = list.Where((LevelPoint p) => (Object)(object)p != (Object)null && !playerRooms.Contains(p)).ToList();
				}
				if (list.Count == 0)
				{
					return null;
				}
				lastPoint = PickNextPoint(list, lastPoint);
				LevelPoint? obj = lastPoint;
				return (obj != null) ? new Vector3?(((Component)obj).transform.position) : ((Vector3?)null);
			}
			catch (Exception ex)
			{
				ModLog.Debug("GetNextRandomMapPoint failed: " + ex.Message);
				return null;
			}
		}

		private static LevelPoint PickNextPoint(IReadOnlyList<LevelPoint> points, LevelPoint? lastPoint)
		{
			if (points.Count == 1)
			{
				return points[0];
			}
			int num = 0;
			LevelPoint val;
			do
			{
				val = points[Random.Range(0, points.Count)];
				num++;
			}
			while ((Object)(object)val == (Object)(object)lastPoint && num < 8);
			return val;
		}

		private static Vector3? GetPlayerSpawnPoint()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			SpawnPoint[] array = Object.FindObjectsOfType<SpawnPoint>();
			if (array == null || array.Length == 0)
			{
				return null;
			}
			return ((Component)array[Random.Range(0, array.Length)]).transform.position;
		}

		public static Vector3? GetRandomPlayerSpawnPoint(Vector3 avoidNear)
		{
			//IL_0007: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = (from p in Object.FindObjectsOfType<SpawnPoint>()
				where (Object)(object)p != (Object)null
				select ((Component)p).transform.position).ToList();
			if (list.Count == 0)
			{
				return SpawnHelper.TryGetStartRoomLevelPoint(0);
			}
			List<Vector3> list2 = list.OrderByDescending(delegate(Vector3 pos)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = pos - avoidNear;
				return ((Vector3)(ref val)).sqrMagnitude;
			}).ToList();
			return list2[Random.Range(0, list2.Count)];
		}
	}
	internal sealed class CartItemScatterBehavior : MonoBehaviour
	{
		private Rigidbody? _rb;

		private bool _airborne;

		private float _airTime;

		private bool _ceilingHitThisArc;

		private float _cooldown;

		public void Begin()
		{
			_rb = ((Component)this).GetComponentInChildren<Rigidbody>();
		}

		private void FixedUpdate()
		{
			if ((Object)(object)_rb == (Object)null || ValuableDamageHelper.IsDestroyed(((Component)this).gameObject))
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			_cooldown -= Time.fixedDeltaTime;
			CheckCeilingHit();
			if (!IsGrounded())
			{
				_airborne = true;
				_airTime += Time.fixedDeltaTime;
				return;
			}
			if (_airborne && _airTime > 0.12f)
			{
				ApplyImpact(heavy: false);
				if (ValuableDamageHelper.IsDestroyed(((Component)this).gameObject))
				{
					Object.Destroy((Object)(object)this);
					return;
				}
			}
			_airborne = false;
			_airTime = 0f;
			_ceilingHitThisArc = false;
		}

		private void CheckCeilingHit()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_rb == (Object)null) && !_ceilingHitThisArc && !(_rb.velocity.y < 0.8f))
			{
				Vector3 val = ((Component)this).transform.position + Vector3.up * 0.15f;
				RaycastHit val2 = default(RaycastHit);
				if (Physics.Raycast(val, Vector3.up, ref val2, 1.4f, -5, (QueryTriggerInteraction)1) && !(((RaycastHit)(ref val2)).normal.y > -0.2f) && !(_cooldown > 0f))
				{
					ApplyImpact(heavy: true);
					_ceilingHitThisArc = true;
				}
			}
		}

		private void ApplyImpact(bool heavy)
		{
			if (!(_cooldown > 0f))
			{
				_cooldown = 0.18f;
				ValuableDamageHelper.ApplyImpactDamage(((Component)this).gameObject, 0.22f, heavy);
			}
		}

		private bool IsGrounded()
		{
			//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_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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)this).transform.position + Vector3.up * 0.1f;
			return Physics.Raycast(val, Vector3.down, 0.35f, -5, (QueryTriggerInteraction)1);
		}
	}
	internal sealed class ChompBookHuntBehavior : MonoBehaviour
	{
		private ChompBookTrap? _trap;

		private Transform? _player;

		private float _duration = 45f;

		private float _elapsed;

		private float _attackTimer = 0.15f;

		public void Configure(ChompBookTrap trap, float durationSeconds = 45f)
		{
			_trap = trap;
			_duration = Mathf.Max(12f, durationSeconds);
			PlayerAvatar? localPlayer = PlayerEffectHelper.GetLocalPlayer();
			_player = ((localPlayer != null) ? ((Component)localPlayer).transform : null);
			ForceTrapOn();
			RefreshTarget();
			TryAttack();
		}

		private void FixedUpdate()
		{
			if ((Object)(object)_trap == (Object)null || ValuableDamageHelper.IsDestroyed(((Component)this).gameObject))
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			_elapsed += Time.fixedDeltaTime;
			if (_elapsed >= _duration)
			{
				try
				{
					_trap.TrapStop();
				}
				catch
				{
				}
				Object.Destroy((Object)(object)this);
				return;
			}
			ForceTrapOn();
			RefreshTarget();
			_attackTimer -= Time.fixedDeltaTime;
			if (_attackTimer <= 0f)
			{
				_attackTimer = 1.05f;
				TryAttack();
			}
		}

		private void ForceTrapOn()
		{
			if (!((Object)(object)_trap == (Object)null))
			{
				((Trap)_trap).isLocal = true;
				((Trap)_trap).trapStart = true;
				if (_trap.biteAmount < 8)
				{
					_trap.biteAmount = 12;
				}
				try
				{
					_trap.TrapActivate();
				}
				catch
				{
					TryInvoke(_trap, "TrapActivate");
				}
				Animator component = ((Component)_trap).GetComponent<Animator>();
				if ((Object)(object)component != (Object)null)
				{
					((Behaviour)component).enabled = true;
				}
			}
		}

		private void RefreshTarget()
		{
			if ((Object)(object)_trap == (Object)null)
			{
				return;
			}
			if ((Object)(object)_player == (Object)null)
			{
				PlayerAvatar? localPlayer = PlayerEffectHelper.GetLocalPlayer();
				_player = ((localPlayer != null) ? ((Component)localPlayer).transform : null);
			}
			if ((Object)(object)_player == (Object)null)
			{
				return;
			}
			try
			{
				typeof(ChompBookTrap).GetField("targetTransform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(_trap, _player);
			}
			catch
			{
			}
		}

		private void TryAttack()
		{
			if ((Object)(object)_trap == (Object)null)
			{
				return;
			}
			try
			{
				_trap.Attack();
			}
			catch (Exception ex)
			{
				ModLog.Debug("Chomp Book Attack: " + ex.Message);
			}
		}

		private static void TryInvoke(object target, string methodName)
		{
			tr