Decompiled source of REPORaceSpectator v1.0.0

SpectateRace.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using SpectateRace.Config;
using SpectateRace.Core;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
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: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Kai")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bc2ce350dc1a821cbdd397a7f00b5ea8dcdf6a3e")]
[assembly: AssemblyProduct("SpectateRace")]
[assembly: AssemblyTitle("SpectateRace")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpectateRace
{
	[BepInPlugin("Kai.SpectateRace", "SpectateRace", "1.4")]
	public class SpectateRace : BaseUnityPlugin
	{
		internal static SpectateRace Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Patch();
			RaceWorldLabelConfig.Bind(((BaseUnityPlugin)this).Config);
			SpectateRaceViewConfig.Bind(((BaseUnityPlugin)this).Config);
			RaceQuickStartConfig.Bind(((BaseUnityPlugin)this).Config);
			if (!Object.op_Implicit((Object)(object)((Component)this).gameObject.GetComponent<RaceWorldLabelManager>()))
			{
				((Component)this).gameObject.AddComponent<RaceWorldLabelManager>();
				Logger.LogInfo((object)"[WorldLabel] Manager attached to plugin object.");
			}
			if (!Object.op_Implicit((Object)(object)((Component)this).gameObject.GetComponent<SpectateRaceViewTweaks>()))
			{
				((Component)this).gameObject.AddComponent<SpectateRaceViewTweaks>();
				Logger.LogInfo((object)"[ViewTweaks] Manager attached to plugin object.");
			}
			if (!Object.op_Implicit((Object)(object)((Component)this).gameObject.GetComponent<RaceQuickStartController>()))
			{
				((Component)this).gameObject.AddComponent<RaceQuickStartController>();
				Logger.LogInfo((object)"[RaceQuickStart] Controller attached to plugin object.");
			}
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0025: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		private void Update()
		{
		}
	}
}
namespace SpectateRace.Patchs
{
	[HarmonyPatch(typeof(ArenaRaceTimerUI), "OnPlacementsUpdated")]
	internal static class Patch_ArenaRaceTimerUI_OnPlacementsUpdated
	{
		private static void Postfix(List<PlayerAvatar> _newOrder)
		{
			RacePlacementCache.Set(_newOrder);
			SpectateRaceNameUpdater.RefreshSpectateName();
		}
	}
	[HarmonyPatch(typeof(ArenaRaceTimerUI), "Update")]
	internal static class Patch_ArenaRaceTimerUI_Update_ShowPlacementsWhileSpectating
	{
		private static void Postfix(ArenaRaceTimerUI __instance)
		{
			if (!Object.op_Implicit((Object)(object)__instance) || !SemiFunc.RunIsArena() || !SemiFunc.IsSpectating() || !Object.op_Implicit((Object)(object)ArenaRace.instance))
			{
				return;
			}
			TextMeshProUGUI allPlacementsText = __instance.allPlacementsText;
			if (Object.op_Implicit((Object)(object)allPlacementsText))
			{
				((Component)__instance).gameObject.SetActive(true);
				if (Object.op_Implicit((Object)(object)__instance.secondsText))
				{
					((Behaviour)__instance.secondsText).enabled = false;
				}
				if (Object.op_Implicit((Object)(object)__instance.hundredthsText))
				{
					((Behaviour)__instance.hundredthsText).enabled = false;
				}
				if (Object.op_Implicit((Object)(object)__instance.clearedText))
				{
					((Behaviour)__instance.clearedText).enabled = false;
				}
				if (Object.op_Implicit((Object)(object)__instance.progressText))
				{
					((Behaviour)__instance.progressText).enabled = false;
				}
				if (Object.op_Implicit((Object)(object)__instance.placeText))
				{
					((Behaviour)__instance.placeText).enabled = false;
				}
				if (Object.op_Implicit((Object)(object)__instance.trackPieceText))
				{
					((Behaviour)__instance.trackPieceText).enabled = false;
				}
				((Behaviour)allPlacementsText).enabled = true;
				((Component)allPlacementsText).gameObject.SetActive(true);
			}
		}
	}
	[HarmonyPatch(typeof(MenuPlayerHead), "Update")]
	internal static class Patch_MenuPlayerHead_SpectateRace
	{
		private static bool Prefix(MenuPlayerHead __instance)
		{
			if (!SpectateRaceViewTweaks.ShouldHidePlayerMenuHead)
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)__instance) && ((Component)__instance).gameObject.activeSelf)
			{
				((Component)__instance).gameObject.SetActive(false);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MenuPlayerListed), "Update")]
	internal static class Patch_MenuPlayerListed_SpectateRace
	{
		private static bool Prefix(MenuPlayerListed __instance)
		{
			if (!SpectateRaceViewTweaks.ShouldHideSpectatePlayerList)
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)__instance) && ((Component)__instance).gameObject.activeSelf)
			{
				((Component)__instance).gameObject.SetActive(false);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "PlayerSwitch")]
	internal static class Patch_SpectateCamera_PlayerSwitch
	{
		private static void Postfix()
		{
			SpectateRaceNameUpdater.RefreshSpectateName();
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "StateNormal")]
	internal static class Patch_SpectateCamera_StateNormal_MaxDistance
	{
		[CompilerGenerated]
		private sealed class <Transpiler>d__0 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private CodeInstruction <>2__current;

			private int <>l__initialThreadId;

			private IEnumerable<CodeInstruction> instructions;

			public IEnumerable<CodeInstruction> <>3__instructions;

			private IEnumerator<CodeInstruction> <>7__wrap1;

			CodeInstruction IEnumerator<CodeInstruction>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Transpiler>d__0(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || (uint)(num - 1) <= 1u)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>7__wrap1 = instructions.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						break;
					case 2:
						<>1__state = -3;
						break;
					}
					if (<>7__wrap1.MoveNext())
					{
						CodeInstruction current = <>7__wrap1.Current;
						if (current.opcode == OpCodes.Ldc_R4 && current.operand is float num && num == 6f)
						{
							<>2__current = CodeInstruction.Call(typeof(Patch_SpectateCamera_StateNormal_MaxDistance), "GetMaxDistance", (Type[])null, (Type[])null);
							<>1__state = 1;
							return true;
						}
						<>2__current = current;
						<>1__state = 2;
						return true;
					}
					<>m__Finally1();
					<>7__wrap1 = null;
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>7__wrap1 != null)
				{
					<>7__wrap1.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator()
			{
				<Transpiler>d__0 <Transpiler>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<Transpiler>d__ = this;
				}
				else
				{
					<Transpiler>d__ = new <Transpiler>d__0(0);
				}
				<Transpiler>d__.instructions = <>3__instructions;
				return <Transpiler>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<CodeInstruction>)this).GetEnumerator();
			}
		}

		[IteratorStateMachine(typeof(<Transpiler>d__0))]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Transpiler>d__0(-2)
			{
				<>3__instructions = instructions
			};
		}

		private static float GetMaxDistance()
		{
			if (SpectateRaceViewConfig.SpectateCameraMaxDistance == null)
			{
				return 6f;
			}
			return SpectateRaceViewConfig.SpectateCameraMaxDistance.Value;
		}
	}
}
namespace SpectateRace.Core
{
	internal static class RacePlaceFormatter
	{
		public static string GetOrdinal(int place)
		{
			if (place <= 0)
			{
				return string.Empty;
			}
			int num = place % 100;
			if (num >= 11 && num <= 13)
			{
				return place + "th";
			}
			return (place % 10) switch
			{
				1 => place + "st", 
				2 => place + "nd", 
				3 => place + "rd", 
				_ => place + "th", 
			};
		}

		public static string GetPlaceColor(int place)
		{
			return place switch
			{
				1 => "#FFD700", 
				2 => "#9AD7FF", 
				3 => "#D0822A", 
				_ => "#B8B8B8", 
			};
		}

		public static string GetColoredOrdinal(int place)
		{
			string ordinal = GetOrdinal(place);
			if (string.IsNullOrEmpty(ordinal))
			{
				return string.Empty;
			}
			string placeColor = GetPlaceColor(place);
			return "<b><color=" + placeColor + ">" + ordinal + "</color></b>";
		}

		public static string BuildColoredName(int place, string playerName)
		{
			string coloredOrdinal = GetColoredOrdinal(place);
			string text = EscapeRichText(playerName);
			if (string.IsNullOrEmpty(coloredOrdinal))
			{
				return "<color=#FFFFFF>" + text + "</color>";
			}
			return coloredOrdinal + " <color=#FFFFFF>" + text + "</color>";
		}

		private static string EscapeRichText(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return string.Empty;
			}
			return text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");
		}
	}
	internal static class RacePlacementCache
	{
		private static readonly List<PlayerAvatar> Placements = new List<PlayerAvatar>();

		public static void Set(List<PlayerAvatar> players)
		{
			Placements.Clear();
			if (players == null)
			{
				return;
			}
			foreach (PlayerAvatar player in players)
			{
				if ((Object)(object)player != (Object)null && !player.isDisabled)
				{
					Placements.Add(player);
				}
			}
		}

		public static int GetPlace(PlayerAvatar player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return -1;
			}
			for (int i = 0; i < Placements.Count; i++)
			{
				if ((Object)(object)Placements[i] == (Object)(object)player)
				{
					return i + 1;
				}
			}
			return -1;
		}

		public static List<PlayerAvatar> GetPlacementsSnapshot()
		{
			return new List<PlayerAvatar>(Placements);
		}

		public static void Clear()
		{
			Placements.Clear();
		}
	}
	internal sealed class RaceQuickStartController : MonoBehaviour
	{
		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(RaceQuickStartConfig.RaceQuickStartKey.Value) && SemiFunc.NoTextInputsActive())
			{
				StartArenaRace();
			}
		}

		private static void StartArenaRace()
		{
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				SpectateRace.Logger.LogWarning((object)"[RaceQuickStart] RunManager not found.");
				return;
			}
			if (instance.restarting)
			{
				SpectateRace.Logger.LogInfo((object)"[RaceQuickStart] Scene is already restarting.");
				return;
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				SpectateRace.Logger.LogInfo((object)"[RaceQuickStart] Only host or singleplayer can change level.");
				return;
			}
			if (instance.levelArena == null || instance.levelArena.Count == 0)
			{
				SpectateRace.Logger.LogWarning((object)"[RaceQuickStart] Arena level list is empty.");
				return;
			}
			EnsureRunInitializedFromMainMenu(instance);
			Level val = PickArenaRaceLevel(instance);
			if ((Object)(object)val == (Object)null)
			{
				SpectateRace.Logger.LogWarning((object)"[RaceQuickStart] Arena Race level not found.");
				return;
			}
			ChangeToLevelDirect(instance, val);
			SpectateRace.Logger.LogInfo((object)("[RaceQuickStart] Changing level: " + ((Object)val).name));
		}

		private static Level PickArenaRaceLevel(RunManager run)
		{
			if (run.levelArena == null || run.levelArena.Count == 0)
			{
				return null;
			}
			foreach (Level item in run.levelArena)
			{
				if (!((Object)(object)item == (Object)null) && string.Equals(((Object)item).name, "Arena Race", StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
			}
			foreach (Level item2 in run.levelArena)
			{
				if (!((Object)(object)item2 == (Object)null) && ((Object)item2).name.IndexOf("Race", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return item2;
				}
			}
			return null;
		}

		private static void ChangeToLevelDirect(RunManager run, Level level)
		{
			run.gameOver = false;
			run.levelCurrent = level;
			if (GameManager.Multiplayer())
			{
				run.runManagerPUN.photonView.RPC("UpdateLevelRPC", (RpcTarget)4, new object[3]
				{
					((Object)level).name,
					run.levelsCompleted,
					false
				});
			}
			run.UpdateSteamRichPresence();
			if (SemiFunc.IsLevelShop(level))
			{
				run.saveLevel = 1;
			}
			else if ((Object)(object)level == (Object)(object)run.levelLobby)
			{
				run.saveLevel = 2;
			}
			else
			{
				run.saveLevel = 0;
			}
			SemiFunc.StatSetSaveLevel(run.saveLevel);
			run.RestartScene();
			SemiFunc.OnSceneSwitch(false, false);
		}

		private static void EnsureRunInitializedFromMainMenu(RunManager run)
		{
			if (!((Object)(object)run == (Object)null) && SemiFunc.IsMainMenu())
			{
				if ((Object)(object)StatsManager.instance != (Object)null)
				{
					StatsManager.instance.ResetAllStats();
				}
				run.levelsCompleted = 0;
				run.loadLevel = 0;
				run.runStarted = true;
				run.gameOver = false;
			}
		}
	}
	internal sealed class RaceWorldLabel : MonoBehaviour
	{
		private PlayerAvatar _player;

		private int _place;

		private Canvas _canvas;

		private RectTransform _root;

		private RectTransform _glowRect;

		private Image _glowImage;

		private Image _backgroundImage;

		private TextMeshProUGUI _text;

		private float _currentScale;

		public static RaceWorldLabel Create(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			GameObject val = new GameObject("SpectateRace.WorldLabel");
			val.transform.SetParent(parent, false);
			RaceWorldLabel raceWorldLabel = val.AddComponent<RaceWorldLabel>();
			raceWorldLabel.Build();
			return raceWorldLabel;
		}

		public void Bind(PlayerAvatar player, int place)
		{
			_player = player;
			_place = place;
			string playerName = player.playerName;
			string sampleText = RacePlaceFormatter.GetOrdinal(place) + " " + playerName;
			RaceWorldLabelFontResolver.Apply(_text, sampleText);
			((TMP_Text)_text).text = RacePlaceFormatter.BuildColoredName(place, playerName);
			ApplyVisualStyle(place);
		}

		public void Tick(Camera cam)
		{
			//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_0021: 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)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_004c: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_player))
			{
				((Component)this).gameObject.SetActive(false);
				return;
			}
			Vector3 targetPosition = GetTargetPosition();
			Vector3 val = targetPosition - ((Component)cam).transform.position;
			if (RaceWorldLabelConfig.HideBehindCamera.Value)
			{
				float num = Vector3.Dot(((Component)cam).transform.forward, ((Vector3)(ref val)).normalized);
				if (num <= 0f)
				{
					((Component)this).gameObject.SetActive(false);
					return;
				}
			}
			if (!((Component)this).gameObject.activeSelf)
			{
				((Component)this).gameObject.SetActive(true);
			}
			((Component)this).transform.position = targetPosition;
			((Component)this).transform.rotation = Quaternion.LookRotation(((Component)this).transform.position - ((Component)cam).transform.position, ((Component)cam).transform.up);
			float distance = Vector3.Distance(((Component)cam).transform.position, targetPosition);
			float num2 = CalculateScale(distance);
			if (_currentScale <= 0f)
			{
				_currentScale = num2;
			}
			else
			{
				_currentScale = Mathf.Lerp(_currentScale, num2, Time.deltaTime * 8f);
			}
			((Component)this).transform.localScale = Vector3.one * _currentScale;
		}

		private Vector3 GetTargetPosition()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			float value = RaceWorldLabelConfig.HeightOffset.Value;
			return ((Component)_player).transform.position + Vector3.up * value;
		}

		private float CalculateScale(float distance)
		{
			float value = RaceWorldLabelConfig.MinScaleDistance.Value;
			float value2 = RaceWorldLabelConfig.MaxScaleDistance.Value;
			float num = Mathf.InverseLerp(value, value2, distance);
			float value3 = RaceWorldLabelConfig.MinScale.Value;
			float value4 = RaceWorldLabelConfig.MaxScale.Value;
			return Mathf.Lerp(value3, value4, num);
		}

		private void Build()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			_canvas = ((Component)this).gameObject.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)2;
			_canvas.sortingOrder = 5000;
			_root = ((Component)this).gameObject.GetComponent<RectTransform>();
			_root.sizeDelta = new Vector2(360f, 64f);
			CreateGlow();
			CreateBackground();
			CreateText();
		}

		private void CreateGlow()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0039: 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)
			//IL_006d: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Glow");
			val.transform.SetParent(((Component)this).transform, false);
			_glowRect = val.AddComponent<RectTransform>();
			_glowRect.anchorMin = new Vector2(0.5f, 0.5f);
			_glowRect.anchorMax = new Vector2(0.5f, 0.5f);
			_glowRect.pivot = new Vector2(0.5f, 0.5f);
			_glowRect.anchoredPosition = Vector2.zero;
			_glowRect.sizeDelta = new Vector2(380f, 80f);
			_glowImage = val.AddComponent<Image>();
			((Graphic)_glowImage).raycastTarget = false;
		}

		private void CreateBackground()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Background");
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = new Vector2(0.5f, 0.5f);
			val2.anchorMax = new Vector2(0.5f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(340f, 56f);
			_backgroundImage = val.AddComponent<Image>();
			((Graphic)_backgroundImage).raycastTarget = false;
			((Graphic)_backgroundImage).color = new Color(0f, 0f, 0f, 0.65f);
		}

		private void CreateText()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Text");
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = new Vector2(0.5f, 0.5f);
			val2.anchorMax = new Vector2(0.5f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(320f, 48f);
			_text = val.AddComponent<TextMeshProUGUI>();
			((Graphic)_text).raycastTarget = false;
			((TMP_Text)_text).richText = true;
			((TMP_Text)_text).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_text).fontSize = 30f;
			((TMP_Text)_text).enableWordWrapping = false;
			((TMP_Text)_text).overflowMode = (TextOverflowModes)0;
			((TMP_Text)_text).outlineWidth = 0.18f;
			((TMP_Text)_text).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220);
		}

		private void ApplyVisualStyle(int place)
		{
			//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_0048: 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_0054: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Color val = ParseColor(RacePlaceFormatter.GetPlaceColor(place));
			((Graphic)_glowImage).color = new Color(val.r, val.g, val.b, place switch
			{
				1 => 0.28f, 
				2 => 0.2f, 
				3 => 0.24f, 
				_ => 0.08f, 
			});
			RectTransform glowRect = _glowRect;
			glowRect.sizeDelta = (Vector2)(place switch
			{
				1 => new Vector2(400f, 86f), 
				2 => new Vector2(380f, 78f), 
				3 => new Vector2(380f, 78f), 
				_ => new Vector2(360f, 68f), 
			});
		}

		private static Color ParseColor(string htmlColor)
		{
			//IL_000c: 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)
			Color result = default(Color);
			if (ColorUtility.TryParseHtmlString(htmlColor, ref result))
			{
				return result;
			}
			return Color.white;
		}
	}
	internal static class RaceWorldLabelFontResolver
	{
		private static TMP_FontAsset _cachedFont;

		private static TMP_FontAsset _cachedJapaneseFallback;

		private static bool _loggedFontInfo;

		public static void Apply(TextMeshProUGUI text, string sampleText = null)
		{
			if (Object.op_Implicit((Object)(object)text))
			{
				TMP_FontAsset orCreateFont = GetOrCreateFont();
				if (Object.op_Implicit((Object)(object)orCreateFont))
				{
					((TMP_Text)text).font = orCreateFont;
				}
			}
		}

		private static TMP_FontAsset GetOrCreateFont()
		{
			if (Object.op_Implicit((Object)(object)_cachedFont))
			{
				return _cachedFont;
			}
			TMP_FontAsset defaultTmpFont = GetDefaultTmpFont();
			if (!Object.op_Implicit((Object)(object)defaultTmpFont))
			{
				Debug.LogWarning((object)"[SpectateRace] TMP default font was not found.");
				return null;
			}
			_cachedFont = Object.Instantiate<TMP_FontAsset>(defaultTmpFont);
			((Object)_cachedFont).name = "SpectateRace_LabelFont";
			if (_cachedFont.fallbackFontAssetTable == null)
			{
				_cachedFont.fallbackFontAssetTable = new List<TMP_FontAsset>();
			}
			AddFallbacksFromBaseFont(defaultTmpFont, _cachedFont);
			AddFallbacksFromTmpSettings(_cachedFont);
			TMP_FontAsset japaneseFallback = GetJapaneseFallback();
			if (Object.op_Implicit((Object)(object)japaneseFallback) && !_cachedFont.fallbackFontAssetTable.Contains(japaneseFallback))
			{
				_cachedFont.fallbackFontAssetTable.Add(japaneseFallback);
				Debug.Log((object)("[SpectateRace] Japanese TMP fallback added: " + ((Object)japaneseFallback).name));
			}
			else if (!Object.op_Implicit((Object)(object)japaneseFallback))
			{
				Debug.LogWarning((object)"[SpectateRace] Japanese TMP fallback was not found. Japanese names may appear as tofu.");
				LogAvailableFontsOnce();
			}
			return _cachedFont;
		}

		private static TMP_FontAsset GetDefaultTmpFont()
		{
			if (Object.op_Implicit((Object)(object)TMP_Settings.defaultFontAsset))
			{
				return TMP_Settings.defaultFontAsset;
			}
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			TMP_FontAsset[] array2 = array;
			foreach (TMP_FontAsset val in array2)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Object)val).name.Contains("LiberationSans"))
				{
					return val;
				}
			}
			if (array.Length == 0)
			{
				return null;
			}
			return array[0];
		}

		private static void AddFallbacksFromBaseFont(TMP_FontAsset source, TMP_FontAsset target)
		{
			if (!Object.op_Implicit((Object)(object)source) || source.fallbackFontAssetTable == null)
			{
				return;
			}
			foreach (TMP_FontAsset item in source.fallbackFontAssetTable)
			{
				if (Object.op_Implicit((Object)(object)item) && !target.fallbackFontAssetTable.Contains(item))
				{
					target.fallbackFontAssetTable.Add(item);
				}
			}
		}

		private static void AddFallbacksFromTmpSettings(TMP_FontAsset target)
		{
			if (TMP_Settings.fallbackFontAssets == null)
			{
				return;
			}
			foreach (TMP_FontAsset fallbackFontAsset in TMP_Settings.fallbackFontAssets)
			{
				if (Object.op_Implicit((Object)(object)fallbackFontAsset) && !target.fallbackFontAssetTable.Contains(fallbackFontAsset))
				{
					target.fallbackFontAssetTable.Add(fallbackFontAsset);
				}
			}
		}

		private static TMP_FontAsset GetJapaneseFallback()
		{
			if (Object.op_Implicit((Object)(object)_cachedJapaneseFallback))
			{
				return _cachedJapaneseFallback;
			}
			_cachedJapaneseFallback = FindJapaneseFontAsset();
			if (Object.op_Implicit((Object)(object)_cachedJapaneseFallback))
			{
				return _cachedJapaneseFallback;
			}
			Debug.LogWarning((object)"[SpectateRace] Japanese TMP fallback was not found. Japanese names may appear as tofu.");
			LogAvailableFontsOnce();
			return null;
		}

		private static TMP_FontAsset FindJapaneseFontAsset()
		{
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			TMP_FontAsset[] array2 = array;
			foreach (TMP_FontAsset val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					string text = ((Object)val).name.ToLowerInvariant();
					if ((text.Contains("japanese") || text.Contains("jp") || text.Contains("noto") || text.Contains("sourcehan") || text.Contains("mplus") || text.Contains("yugothic") || text.Contains("meiryo") || text.Contains("gothic")) && CanRenderJapanese(val))
					{
						return val;
					}
				}
			}
			TMP_FontAsset[] array3 = array;
			foreach (TMP_FontAsset val2 in array3)
			{
				if (Object.op_Implicit((Object)(object)val2) && CanRenderJapanese(val2))
				{
					return val2;
				}
			}
			return null;
		}

		private static TMP_FontAsset CreateJapaneseOsFontAsset()
		{
			string[] array = new string[6] { "Yu Gothic UI", "Yu Gothic", "Meiryo", "MS Gothic", "Noto Sans CJK JP", "Noto Sans JP" };
			Font val = Font.CreateDynamicFontFromOSFont(array, 90);
			if (!Object.op_Implicit((Object)(object)val))
			{
				Debug.LogWarning((object)"[SpectateRace] Failed to create Japanese OS font.");
				return null;
			}
			TMP_FontAsset val2 = TMP_FontAsset.CreateFontAsset(val, 90, 9, (GlyphRenderMode)4165, 1024, 1024, (AtlasPopulationMode)1, true);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				Debug.LogWarning((object)"[SpectateRace] Failed to create TMP font asset from Japanese OS font.");
				return null;
			}
			((Object)val2).name = "SpectateRace_Japanese_OSFont_Fallback";
			val2.atlasPopulationMode = (AtlasPopulationMode)1;
			val2.isMultiAtlasTexturesEnabled = true;
			Debug.Log((object)("[SpectateRace] Created Japanese OS font fallback: " + ((Object)val).name));
			return val2;
		}

		private static bool CanRenderJapanese(TMP_FontAsset font)
		{
			return CanRender(font, "日本語ハヒフヘホ");
		}

		private static bool CanRender(TMP_FontAsset font, string text)
		{
			if (!Object.op_Implicit((Object)(object)font) || string.IsNullOrEmpty(text))
			{
				return false;
			}
			foreach (char c in text)
			{
				if (!char.IsWhiteSpace(c) && !font.HasCharacter(c, true, true))
				{
					return false;
				}
			}
			return true;
		}

		private static void LogAvailableFontsOnce()
		{
			if (_loggedFontInfo)
			{
				return;
			}
			_loggedFontInfo = true;
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			Debug.Log((object)("[SpectateRace] Available TMP fonts: " + array.Length));
			TMP_FontAsset[] array2 = array;
			foreach (TMP_FontAsset val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Debug.Log((object)("[SpectateRace] TMP font: " + ((Object)val).name));
				}
			}
		}
	}
	internal sealed class RaceWorldLabelManager : MonoBehaviour
	{
		private readonly Dictionary<PlayerAvatar, RaceWorldLabel> _labels = new Dictionary<PlayerAvatar, RaceWorldLabel>();

		private RaceWorldLabelMode _mode;

		private float _refreshTimer;

		private void Awake()
		{
			_mode = RaceWorldLabelConfig.DefaultMode.Value;
		}

		private void Update()
		{
			HandleToggle();
			if (!ShouldRun())
			{
				ClearLabels();
				return;
			}
			_refreshTimer -= Time.deltaTime;
			if (_refreshTimer <= 0f)
			{
				_refreshTimer = 0.1f;
				RefreshLabels();
			}
			UpdateLabels();
		}

		private void HandleToggle()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(RaceWorldLabelConfig.ToggleKey.Value))
			{
				_mode = _mode switch
				{
					RaceWorldLabelMode.Off => RaceWorldLabelMode.Top3, 
					RaceWorldLabelMode.Top3 => RaceWorldLabelMode.All, 
					_ => RaceWorldLabelMode.Off, 
				};
				SpectateRace.Logger.LogInfo((object)$"[WorldLabels] Mode: {_mode}");
			}
		}

		private bool ShouldRun()
		{
			if (_mode == RaceWorldLabelMode.Off)
			{
				return false;
			}
			if (!SemiFunc.RunIsArena())
			{
				return false;
			}
			if (!SemiFunc.IsSpectating())
			{
				return false;
			}
			if (!Object.op_Implicit((Object)(object)ArenaRace.instance))
			{
				return false;
			}
			return true;
		}

		private void RefreshLabels()
		{
			List<PlayerAvatar> placementsSnapshot = RacePlacementCache.GetPlacementsSnapshot();
			HashSet<PlayerAvatar> hashSet = new HashSet<PlayerAvatar>();
			for (int i = 0; i < placementsSnapshot.Count; i++)
			{
				PlayerAvatar val = placementsSnapshot[i];
				if (!Object.op_Implicit((Object)(object)val) || val.isDisabled)
				{
					continue;
				}
				int num = i + 1;
				if (_mode != RaceWorldLabelMode.Top3 || num <= 3)
				{
					hashSet.Add(val);
					if (!_labels.TryGetValue(val, out RaceWorldLabel value) || !Object.op_Implicit((Object)(object)value))
					{
						value = RaceWorldLabel.Create(((Component)this).transform);
						_labels[val] = value;
					}
					value.Bind(val, num);
				}
			}
			RemoveInactiveLabels(hashSet);
		}

		private void UpdateLabels()
		{
			Camera main = Camera.main;
			if (!Object.op_Implicit((Object)(object)main))
			{
				return;
			}
			foreach (RaceWorldLabel value in _labels.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					value.Tick(main);
				}
			}
		}

		private void RemoveInactiveLabels(HashSet<PlayerAvatar> activePlayers)
		{
			List<PlayerAvatar> list = new List<PlayerAvatar>();
			foreach (KeyValuePair<PlayerAvatar, RaceWorldLabel> label in _labels)
			{
				PlayerAvatar key = label.Key;
				RaceWorldLabel value = label.Value;
				if (!Object.op_Implicit((Object)(object)key) || !activePlayers.Contains(key))
				{
					if (Object.op_Implicit((Object)(object)value))
					{
						Object.Destroy((Object)(object)((Component)value).gameObject);
					}
					list.Add(key);
				}
			}
			foreach (PlayerAvatar item in list)
			{
				_labels.Remove(item);
			}
		}

		private void ClearLabels()
		{
			if (_labels.Count <= 0)
			{
				return;
			}
			foreach (RaceWorldLabel value in _labels.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
			_labels.Clear();
		}
	}
	internal enum RaceWorldLabelMode
	{
		Off,
		Top3,
		All
	}
	internal static class SpectateRaceNameUpdater
	{
		private static string _lastDisplayName;

		public static void RefreshSpectateName()
		{
			if (!SemiFunc.RunIsArena() || !SemiFunc.IsSpectating() || !Object.op_Implicit((Object)(object)ArenaRace.instance) || !Object.op_Implicit((Object)(object)SpectateCamera.instance))
			{
				return;
			}
			PlayerAvatar player = SpectateCamera.instance.player;
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			int place = RacePlacementCache.GetPlace(player);
			if (place > 0)
			{
				string coloredOrdinal = RacePlaceFormatter.GetColoredOrdinal(place);
				string text = coloredOrdinal + " " + player.playerName;
				if (!(_lastDisplayName == text))
				{
					_lastDisplayName = text;
					SemiFunc.HUDSpectateSetName(text);
				}
			}
		}

		public static void ResetCache()
		{
			_lastDisplayName = null;
		}
	}
	internal sealed class SpectateRaceViewTweaks : MonoBehaviour
	{
		private const float PhotoModeFarClipPlane = 500f;

		private const float PhotoModeShadowDistance = 300f;

		private const float PhotoModeLodBias = 3f;

		private const int PhotoModeMaximumLodLevel = 0;

		private bool _active;

		private bool _savedFog;

		private bool _fogSaved;

		private Color _savedAmbientLight;

		private bool _ambientLightSaved;

		private float _savedFarClipPlane;

		private bool _farClipSaved;

		private float _savedShadowDistance;

		private bool _shadowDistanceSaved;

		private float _savedLodBias;

		private bool _lodBiasSaved;

		private int _savedMaximumLodLevel;

		private bool _maximumLodLevelSaved;

		private MenuSpectateList _menuSpectateList;

		private readonly List<GameObject> _hiddenPlayerMenuHeads = new List<GameObject>();

		private readonly List<GameObject> _hiddenSpectateHeadUis = new List<GameObject>();

		private readonly List<GameObject> _hiddenSpectateListObjects = new List<GameObject>();

		private readonly List<GameObject> _hiddenMenuPlayerListedObjects = new List<GameObject>();

		private float _uiRefreshTimer;

		internal static SpectateRaceViewTweaks Instance { get; private set; }

		internal static bool ShouldHidePlayerMenuHead
		{
			get
			{
				if ((Object)(object)Instance != (Object)null && Instance._active && !AllPlayersDead())
				{
					return SpectateRaceViewConfig.HidePlayerMenuHead.Value;
				}
				return false;
			}
		}

		internal static bool ShouldHideSpectatePlayerList
		{
			get
			{
				if ((Object)(object)Instance != (Object)null && Instance._active && !AllPlayersDead())
				{
					return SpectateRaceViewConfig.HideSpectatePlayerList.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			RestoreAll();
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void OnDisable()
		{
			RestoreAll();
		}

		private void Update()
		{
			HandleInput();
			bool flag = ShouldRun();
			if (flag && !_active)
			{
				Enable();
			}
			else if (!flag && _active)
			{
				Disable();
				return;
			}
			if (_active)
			{
				HideUiTick();
			}
		}

		private void LateUpdate()
		{
			if (_active && ShouldRun())
			{
				ApplyViewTweaks();
			}
		}

		private void HandleInput()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!IsArenaRaceSpectateContext() || (!Input.GetKey((KeyCode)308) && !Input.GetKey((KeyCode)307)))
			{
				return;
			}
			float y = Input.mouseScrollDelta.y;
			if (Mathf.Abs(y) <= 0.001f)
			{
				return;
			}
			float num = Mathf.Clamp(SpectateRaceViewConfig.NightVisionStrength.Value, 0f, 100f);
			float num2 = Mathf.Max(1f, SpectateRaceViewConfig.NightVisionWheelStep.Value);
			float num3 = Mathf.Clamp(num + y * num2, 0f, 100f);
			if (!Mathf.Approximately(num, num3))
			{
				SpectateRaceViewConfig.NightVisionStrength.Value = num3;
				if (_active)
				{
					ApplyNightVisionOnly();
				}
				SpectateRace.Logger.LogInfo((object)$"[ViewTweaks] NightVision: {num3:0}");
			}
		}

		private static bool IsArenaRaceSpectateContext()
		{
			if (!SpectateRaceViewConfig.EnableViewTweaks.Value)
			{
				return false;
			}
			if (!SemiFunc.RunIsArena())
			{
				return false;
			}
			if (!SemiFunc.IsSpectating())
			{
				return false;
			}
			if (!Object.op_Implicit((Object)(object)ArenaRace.instance))
			{
				return false;
			}
			if (AllPlayersDead())
			{
				return false;
			}
			return true;
		}

		private static bool ShouldRun()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return false;
			}
			return IsArenaRaceSpectateContext();
		}

		internal static bool AllPlayersDead()
		{
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null)
			{
				return false;
			}
			if (GameDirector.instance.PlayerList.Count == 0)
			{
				return false;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if ((Object)(object)player != (Object)null && !player.isDisabled)
				{
					return false;
				}
			}
			return true;
		}

		private void Enable()
		{
			SaveCurrentValues();
			CacheUiReferences();
			_active = true;
			ApplyViewTweaks();
			HideUi(includeInactive: true);
		}

		private void Disable()
		{
			RestoreAll();
			_active = false;
		}

		private void SaveCurrentValues()
		{
			//IL_0074: 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)
			_savedFog = RenderSettings.fog;
			_fogSaved = true;
			Camera mainCamera = GetMainCamera();
			if ((Object)(object)mainCamera != (Object)null)
			{
				_savedFarClipPlane = mainCamera.farClipPlane;
				_farClipSaved = true;
			}
			else
			{
				_farClipSaved = false;
			}
			_savedShadowDistance = QualitySettings.shadowDistance;
			_shadowDistanceSaved = true;
			_savedLodBias = QualitySettings.lodBias;
			_lodBiasSaved = true;
			_savedMaximumLodLevel = QualitySettings.maximumLODLevel;
			_maximumLodLevelSaved = true;
			_savedAmbientLight = RenderSettings.ambientLight;
			_ambientLightSaved = true;
		}

		private void ApplyViewTweaks()
		{
			ApplyPhotoModeValues();
			ApplyNightVision();
		}

		private void ApplyNightVisionOnly()
		{
			ApplyNightVision();
		}

		private void ApplyPhotoModeValues()
		{
			if (SpectateRaceViewConfig.DisableFog.Value)
			{
				RenderSettings.fog = false;
			}
			Camera mainCamera = GetMainCamera();
			if ((Object)(object)mainCamera != (Object)null)
			{
				mainCamera.farClipPlane = 500f;
			}
			QualitySettings.shadowDistance = 300f;
			QualitySettings.lodBias = Mathf.Max(QualitySettings.lodBias, 3f);
			QualitySettings.maximumLODLevel = 0;
		}

		private void ApplyNightVision()
		{
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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)
			float num = Mathf.Clamp(SpectateRaceViewConfig.NightVisionStrength.Value, 0f, 100f);
			if (num <= 0.001f)
			{
				if (_ambientLightSaved)
				{
					RenderSettings.ambientLight = _savedAmbientLight;
				}
			}
			else
			{
				float num2 = Mathf.Clamp01(num / 100f);
				Color ambientLight = RenderSettings.ambientLight;
				Color ambientLight2 = Color.Lerp(ambientLight, Color.white * 2f, num2);
				RenderSettings.ambientLight = ambientLight2;
			}
		}

		private void HideUiTick()
		{
			_uiRefreshTimer -= Time.deltaTime;
			if (!(_uiRefreshTimer > 0f))
			{
				_uiRefreshTimer = 0.25f;
				HideUi(includeInactive: false);
			}
		}

		private void HideUi(bool includeInactive)
		{
			CacheUiReferences();
			if (SpectateRaceViewConfig.HidePlayerMenuHead.Value)
			{
				HidePlayerMenuHeads(includeInactive);
			}
			if (SpectateRaceViewConfig.HideSpectateHeadUI.Value)
			{
				HideSpectateHeadUis(includeInactive);
			}
			if (SpectateRaceViewConfig.HideSpectatePlayerList.Value)
			{
				HideSpectatePlayerList();
			}
		}

		private void CacheUiReferences()
		{
			if (!Object.op_Implicit((Object)(object)_menuSpectateList))
			{
				_menuSpectateList = Object.FindObjectOfType<MenuSpectateList>(true);
			}
		}

		private void HidePlayerMenuHeads(bool includeInactive)
		{
			MenuPlayerHead[] array = Object.FindObjectsOfType<MenuPlayerHead>(includeInactive);
			MenuPlayerHead[] array2 = array;
			foreach (MenuPlayerHead val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					HideGameObject(((Component)val).gameObject, _hiddenPlayerMenuHeads);
				}
			}
		}

		private void HideSpectateHeadUis(bool includeInactive)
		{
			SpectateHeadUI[] array = Object.FindObjectsOfType<SpectateHeadUI>(includeInactive);
			SpectateHeadUI[] array2 = array;
			foreach (SpectateHeadUI val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					HideGameObject(((Component)val).gameObject, _hiddenSpectateHeadUis);
				}
			}
		}

		private void HideSpectatePlayerList()
		{
			if (Object.op_Implicit((Object)(object)_menuSpectateList))
			{
				((SemiUI)_menuSpectateList).Hide();
				HideMenuPlayerListedChildren();
				HideGameObject(((Component)_menuSpectateList).gameObject, _hiddenSpectateListObjects);
				GameObject val = FindSpectateListContainer(((Component)_menuSpectateList).transform);
				if (Object.op_Implicit((Object)(object)val))
				{
					HideGameObject(val, _hiddenSpectateListObjects);
				}
			}
		}

		private static GameObject FindSpectateListContainer(Transform start)
		{
			if (!Object.op_Implicit((Object)(object)start))
			{
				return null;
			}
			Transform val = start;
			Transform val2 = start;
			while (Object.op_Implicit((Object)(object)val.parent))
			{
				Transform parent = val.parent;
				if (Object.op_Implicit((Object)(object)((Component)parent).GetComponent<Canvas>()))
				{
					break;
				}
				string text = ((Object)((Component)parent).gameObject).name.ToLowerInvariant();
				if (!text.Contains("spectate") && !text.Contains("player") && !text.Contains("list"))
				{
					break;
				}
				val2 = parent;
				val = parent;
			}
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return null;
			}
			return ((Component)val2).gameObject;
		}

		private void HideMenuPlayerListedChildren()
		{
			if (!Object.op_Implicit((Object)(object)_menuSpectateList))
			{
				return;
			}
			for (int i = 0; i < _menuSpectateList.listObjects.Count; i++)
			{
				GameObject val = _menuSpectateList.listObjects[i];
				if (Object.op_Implicit((Object)(object)val))
				{
					HideGameObject(val, _hiddenMenuPlayerListedObjects);
				}
			}
		}

		private static void HideGameObject(GameObject go, List<GameObject> restoreList)
		{
			if (Object.op_Implicit((Object)(object)go) && go.activeSelf)
			{
				go.SetActive(false);
				if (!restoreList.Contains(go))
				{
					restoreList.Add(go);
				}
			}
		}

		private void RestoreAll()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (_fogSaved)
			{
				RenderSettings.fog = _savedFog;
			}
			Camera mainCamera = GetMainCamera();
			if ((Object)(object)mainCamera != (Object)null && _farClipSaved)
			{
				mainCamera.farClipPlane = _savedFarClipPlane;
			}
			if (_shadowDistanceSaved)
			{
				QualitySettings.shadowDistance = _savedShadowDistance;
			}
			if (_lodBiasSaved)
			{
				QualitySettings.lodBias = _savedLodBias;
			}
			if (_maximumLodLevelSaved)
			{
				QualitySettings.maximumLODLevel = _savedMaximumLodLevel;
			}
			if (_ambientLightSaved)
			{
				RenderSettings.ambientLight = _savedAmbientLight;
			}
			RestorePlayerMenuHeads();
			RestoreSpectateHeadUis();
			RestoreSpectatePlayerList();
			ClearSavedFlags();
		}

		private void RestorePlayerMenuHeads()
		{
			RestoreGameObjects(_hiddenPlayerMenuHeads);
		}

		private void RestoreSpectateHeadUis()
		{
			RestoreGameObjects(_hiddenSpectateHeadUis);
		}

		private void RestoreSpectatePlayerList()
		{
			RestoreGameObjects(_hiddenSpectateListObjects);
			RestoreGameObjects(_hiddenMenuPlayerListedObjects);
			if (Object.op_Implicit((Object)(object)_menuSpectateList))
			{
				((Behaviour)_menuSpectateList).enabled = true;
			}
		}

		private static void RestoreGameObjects(List<GameObject> objects)
		{
			for (int i = 0; i < objects.Count; i++)
			{
				GameObject val = objects[i];
				if (Object.op_Implicit((Object)(object)val) && !val.activeSelf)
				{
					val.SetActive(true);
				}
			}
			objects.Clear();
		}

		private void ClearSavedFlags()
		{
			_fogSaved = false;
			_farClipSaved = false;
			_shadowDistanceSaved = false;
			_lodBiasSaved = false;
			_maximumLodLevelSaved = false;
			_ambientLightSaved = false;
			_uiRefreshTimer = 0f;
		}

		private static Camera GetMainCamera()
		{
			if ((Object)(object)GameDirector.instance != (Object)null && (Object)(object)GameDirector.instance.MainCamera != (Object)null)
			{
				return GameDirector.instance.MainCamera;
			}
			return Camera.main;
		}
	}
}
namespace SpectateRace.Config
{
	internal static class RaceQuickStartConfig
	{
		internal static ConfigEntry<KeyCode>? RaceQuickStartKey;

		internal static void Bind(ConfigFile config)
		{
			RaceQuickStartKey = config.Bind<KeyCode>("Race Quick Start", "RaceQuickStartKey", (KeyCode)290, "Key to quickly transition to Arena Race. Host or singleplayer only.");
		}
	}
	internal static class RaceWorldLabelConfig
	{
		internal static ConfigEntry<KeyCode>? ToggleKey;

		internal static ConfigEntry<RaceWorldLabelMode>? DefaultMode;

		internal static ConfigEntry<float>? HeightOffset;

		internal static ConfigEntry<float>? MinScale;

		internal static ConfigEntry<float>? MaxScale;

		internal static ConfigEntry<float>? MinScaleDistance;

		internal static ConfigEntry<float>? MaxScaleDistance;

		internal static ConfigEntry<bool>? HideBehindCamera;

		public static void Bind(ConfigFile config)
		{
			ToggleKey = config.Bind<KeyCode>("World Labels", "ToggleKey", (KeyCode)99, "Toggle race world labels mode.");
			DefaultMode = config.Bind<RaceWorldLabelMode>("World Labels", "DefaultMode", RaceWorldLabelMode.Top3, "Default world label mode.");
			HeightOffset = config.Bind<float>("World Labels", "HeightOffset", 2.2f, "Vertical offset above the player.");
			MinScale = config.Bind<float>("World Labels", "MinScale", 0.008f, "Minimum world label scale.");
			MaxScale = config.Bind<float>("World Labels", "MaxScale", 0.026f, "Maximum world label scale.");
			MinScaleDistance = config.Bind<float>("World Labels", "MinScaleDistance", 8f, "Distance where labels use minimum scale.");
			MaxScaleDistance = config.Bind<float>("World Labels", "MaxScaleDistance", 55f, "Distance where labels reach maximum scale.");
			HideBehindCamera = config.Bind<bool>("World Labels", "HideBehindCamera", true, "Hide labels when they are behind the camera.");
		}
	}
	internal static class SpectateRaceViewConfig
	{
		internal static ConfigEntry<bool> EnableViewTweaks;

		internal static ConfigEntry<bool> HidePlayerMenuHead;

		internal static ConfigEntry<bool> HideSpectatePlayerList;

		internal static ConfigEntry<bool> HideSpectateHeadUI;

		internal static ConfigEntry<bool> DisableFog;

		internal static ConfigEntry<float> NightVisionStrength;

		internal static ConfigEntry<float> NightVisionWheelStep;

		internal static ConfigEntry<float> SpectateCameraMaxDistance;

		public static void Bind(ConfigFile config)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			EnableViewTweaks = config.Bind<bool>("Spectator View", "EnableViewTweaks", true, "Enable spectator view tweaks while spectating Arena Race.");
			HidePlayerMenuHead = config.Bind<bool>("Spectator View", "HidePlayerMenuHead", false, "Hide player head UI while spectating Arena Race.");
			HideSpectatePlayerList = config.Bind<bool>("Spectator View", "HideSpectatePlayerList", false, "Hide spectate player list while spectating Arena Race.");
			HideSpectateHeadUI = config.Bind<bool>("Spectator View", "HideSpectateHeadUI", true, "Hide spectate head UI while spectating Arena Race.");
			DisableFog = config.Bind<bool>("Spectator View", "DisableFog", true, "Disable scene fog while spectating Arena Race.");
			SpectateCameraMaxDistance = config.Bind<float>("Spectator View", "SpectateCameraMaxDistance", 12f, new ConfigDescription("Maximum normal spectate camera distance. Vanilla is 6.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 30f), Array.Empty<object>()));
			NightVisionStrength = config.Bind<float>("Spectator View", "NightVisionStrength", 25f, new ConfigDescription("Ambient-light based night vision strength while spectating Arena Race. 0 = off, 100 = max.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			NightVisionWheelStep = config.Bind<float>("Spectator View", "NightVisionWheelStep", 5f, new ConfigDescription("Night vision adjustment step per mouse wheel notch while holding Alt.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 25f), Array.Empty<object>()));
		}
	}
}