Decompiled source of BBNO Concert COS v1.0.9

BepInEx\plugins\BBNO-COS\BBNO-COS.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BBNO_COS;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
using UnityEngine.Timeline;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BBNO-COS")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BBNO-COS")]
[assembly: AssemblyTitle("BBNO-COS")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public class BBNOController : MonoBehaviourPunCallbacks
{
	public bool noOceanFog;

	public GameObject BBNO;

	public GameObject Vanilla;

	public GameObject IronMouse;

	public DayNightProfile[] profiles;

	public static BBNOController instance;

	public double timelineStartTime;

	[SerializeField]
	private bool startedConcert;

	private PlayableDirector director;

	private bool isPaused;

	private void Awake()
	{
		instance = this;
		director = ((Component)this).GetComponent<PlayableDirector>();
		if ((Object)(object)director == (Object)null)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)"BBNOController loaded without a PlayableDirector.");
			}
		}
		else
		{
			director.Play();
			director.Pause();
			director.stopped += OnStopped;
		}
	}

	private void OnStopped(PlayableDirector stoppedDirector)
	{
		((Component)this).gameObject.SetActive(false);
	}

	public void OnPlayerEnteredRoom(Player newPlayer)
	{
	}

	public void StartConcertNetworked()
	{
		if ((Object)(object)((MonoBehaviourPun)this).photonView != (Object)null)
		{
			((MonoBehaviourPun)this).photonView.RPC("SyncTime", (RpcTarget)0, new object[1] { timelineStartTime });
		}
		else
		{
			SyncTime(timelineStartTime);
		}
	}

	private void Update()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		if (!((Object)(object)director == (Object)null))
		{
			if (Time.timeScale == 0f && (int)director.state == 1 && !isPaused)
			{
				isPaused = true;
				director.Pause();
			}
			else if (Time.timeScale != 0f && isPaused)
			{
				isPaused = false;
				director.Play();
			}
		}
	}

	private void StartConcert()
	{
		if (!startedConcert && !((Object)(object)director == (Object)null))
		{
			startedConcert = true;
			director.Play();
		}
	}

	public void MoveBBNOBeginning(float upAmount)
	{
		//IL_0002: 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)
		((MonoBehaviour)this).StartCoroutine(MoveBBNORoutine(Vector3.up * upAmount));
	}

	public void setProfile(int index)
	{
		if (profiles != null && index >= 0 && index < profiles.Length && !((Object)(object)DayNightManager.instance == (Object)null))
		{
			DayNightManager.instance.BlendProfiles(profiles[index]);
		}
	}

	public void DisableEdgeFog()
	{
		((MonoBehaviour)this).StartCoroutine(DisableEdgeFogRoutine());
	}

	public void enableBBNO(bool enable)
	{
		if ((Object)(object)BBNO != (Object)null)
		{
			BBNO.SetActive(enable);
		}
	}

	public override void OnDisable()
	{
		((MonoBehaviourPunCallbacks)this).OnDisable();
		Shader.SetGlobalFloat("NoEdgeFog", 0f);
	}

	[PunRPC]
	public void SyncTime(double time)
	{
		if (!startedConcert)
		{
			StartConcert();
		}
		if ((Object)(object)director != (Object)null)
		{
			director.time = time;
		}
	}

	private IEnumerator MoveBBNORoutine(Vector3 newPosition)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)BBNO == (Object)null))
		{
			Vector3 currentPosition = BBNO.transform.position;
			float normalizedTime = 0f;
			while (normalizedTime < 1f)
			{
				normalizedTime += Time.deltaTime / 5f;
				BBNO.transform.position = Vector3.Lerp(currentPosition, currentPosition + newPosition, normalizedTime);
				yield return null;
			}
		}
	}

	private static IEnumerator DisableEdgeFogRoutine()
	{
		float normalizedTime = 0f;
		while (normalizedTime < 1f)
		{
			normalizedTime += Time.deltaTime;
			Shader.SetGlobalFloat("NoEdgeFog", normalizedTime);
			yield return null;
		}
	}
}
namespace BBNO_COS
{
	[BepInPlugin("buhe.peak.bbno.cos", "BBNO-COS", "1.0.9")]
	public sealed class Plugin : BaseUnityPlugin, IOnEventCallback
	{
		private sealed class StageVisualClone
		{
			private sealed class PoseBinding
			{
				private readonly Transform _source;

				private readonly Transform _target;

				public PoseBinding(Transform source, Transform target)
				{
					_source = source;
					_target = target;
				}

				public void Apply()
				{
					//IL_0029: Unknown result type (might be due to invalid IL or missing references)
					if (!((Object)(object)_source == (Object)null) && !((Object)(object)_target == (Object)null))
					{
						_target.localRotation = _source.localRotation;
					}
				}
			}

			private sealed class AnchorBinding
			{
				private readonly Transform _sourceAnchor;

				private readonly Transform _targetAnchor;

				private readonly Transform _cloneRoot;

				public AnchorBinding(Transform sourceAnchor, Transform targetAnchor, Transform cloneRoot)
				{
					_sourceAnchor = sourceAnchor;
					_targetAnchor = targetAnchor;
					_cloneRoot = cloneRoot;
				}

				public void Apply()
				{
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					//IL_003d: 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_0052: Unknown result type (might be due to invalid IL or missing references)
					if (!((Object)(object)_sourceAnchor == (Object)null) && !((Object)(object)_targetAnchor == (Object)null) && !((Object)(object)_cloneRoot == (Object)null))
					{
						Transform cloneRoot = _cloneRoot;
						cloneRoot.position += _sourceAnchor.position - _targetAnchor.position;
					}
				}
			}

			private readonly Transform _stageRoot;

			private readonly GameObject _cloneObject;

			private readonly int _boundRendererCount;

			private readonly PoseBinding[] _poseBindings;

			private readonly AnchorBinding _anchorBinding;

			public int BoundRendererCount => _boundRendererCount;

			private StageVisualClone(Transform stageRoot, GameObject cloneObject, int boundRendererCount, PoseBinding[] poseBindings, AnchorBinding anchorBinding)
			{
				_stageRoot = stageRoot;
				_cloneObject = cloneObject;
				_boundRendererCount = boundRendererCount;
				_poseBindings = poseBindings ?? Array.Empty<PoseBinding>();
				_anchorBinding = anchorBinding;
			}

			public static StageVisualClone Create(Transform stageRoot, GameObject cloneObject)
			{
				//IL_0225: Unknown result type (might be due to invalid IL or missing references)
				//IL_022a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0234: Unknown result type (might be due to invalid IL or missing references)
				//IL_0239: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)stageRoot == (Object)null || (Object)(object)cloneObject == (Object)null)
				{
					return null;
				}
				SkinnedMeshRenderer[] stageRenderers = (from renderer in ((Component)stageRoot).GetComponentsInChildren<SkinnedMeshRenderer>(true)
					where (Object)(object)renderer != (Object)null && !IsChildOf(((Component)renderer).transform, cloneObject.transform)
					select renderer).ToArray();
				Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
				Transform[] componentsInChildren = ((Component)stageRoot).GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !IsChildOf(val, cloneObject.transform))
					{
						string text = NormalizeTransformName(((Object)val).name);
						if (text.Length != 0 && !dictionary.ContainsKey(text))
						{
							dictionary.Add(text, val);
						}
					}
				}
				int num2 = 0;
				Dictionary<Transform, PoseBinding> dictionary2 = new Dictionary<Transform, PoseBinding>();
				AnchorBinding anchorBinding = null;
				int num3 = 0;
				SkinnedMeshRenderer[] componentsInChildren2 = cloneObject.GetComponentsInChildren<SkinnedMeshRenderer>(true);
				foreach (SkinnedMeshRenderer val2 in componentsInChildren2)
				{
					if ((Object)(object)val2 == (Object)null || val2.bones == null || val2.bones.Length == 0)
					{
						continue;
					}
					SkinnedMeshRenderer val3 = FindBestStageRenderer(val2, stageRenderers);
					IReadOnlyDictionary<string, Transform> readOnlyDictionary = BuildBoneMap(((Object)(object)val3 == (Object)null) ? null : val3.bones);
					int num4 = 0;
					for (int num5 = 0; num5 < val2.bones.Length; num5++)
					{
						Transform val4 = val2.bones[num5];
						string text2 = (((Object)(object)val4 == (Object)null) ? string.Empty : NormalizeTransformName(((Object)val4).name));
						if (text2.Length > 0 && ((readOnlyDictionary != null && readOnlyDictionary.TryGetValue(text2, out var value)) || dictionary.TryGetValue(text2, out value)))
						{
							if ((Object)(object)val4 != (Object)null && (Object)(object)value != (Object)null && !dictionary2.ContainsKey(val4))
							{
								dictionary2.Add(val4, new PoseBinding(value, val4));
							}
							int num6 = AnchorPriority(text2);
							if ((Object)(object)val4 != (Object)null && (Object)(object)value != (Object)null && num6 > num3)
							{
								anchorBinding = new AnchorBinding(value, val4, cloneObject.transform);
								num3 = num6;
							}
							num4++;
						}
					}
					if (num4 != 0)
					{
						((Renderer)val2).localBounds = new Bounds(Vector3.zero, Vector3.one * 10000f);
						val2.updateWhenOffscreen = true;
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogInfo((object)string.Format("Stage visual pose bind: '{0}' -> '{1}', matched bones {2}/{3}.", ((Object)val2).name, ((Object)(object)val3 == (Object)null) ? "global stage bones" : ((Object)val3).name, num4, val2.bones.Length));
						}
						num2++;
					}
				}
				return new StageVisualClone(stageRoot, cloneObject, num2, dictionary2.Values.ToArray(), anchorBinding);
			}

			private static SkinnedMeshRenderer FindBestStageRenderer(SkinnedMeshRenderer sourceRenderer, SkinnedMeshRenderer[] stageRenderers)
			{
				if ((Object)(object)sourceRenderer == (Object)null || stageRenderers == null || stageRenderers.Length == 0)
				{
					return null;
				}
				SkinnedMeshRenderer result = null;
				int num = 0;
				string text = NormalizeTransformName(((Object)sourceRenderer).name);
				string text2 = RendererCategory((Renderer)(object)sourceRenderer);
				int num2 = ((((Renderer)sourceRenderer).sharedMaterials != null) ? ((Renderer)sourceRenderer).sharedMaterials.Length : 0);
				foreach (SkinnedMeshRenderer val in stageRenderers)
				{
					if (!((Object)(object)val == (Object)null) && val.bones != null && val.bones.Length != 0)
					{
						int num3 = CountMatchingBones(sourceRenderer.bones, val.bones) * 4;
						if (text.Length > 0 && string.Equals(text, NormalizeTransformName(((Object)val).name), StringComparison.OrdinalIgnoreCase))
						{
							num3 += 80;
						}
						if (num2 > 0 && ((Renderer)val).sharedMaterials != null && ((Renderer)val).sharedMaterials.Length == num2)
						{
							num3 += 12;
						}
						string text3 = RendererCategory((Renderer)(object)val);
						if (text2.Length > 0 && text2 == text3)
						{
							num3 += 40;
						}
						if (num3 > num)
						{
							num = num3;
							result = val;
						}
					}
				}
				if (num < 12)
				{
					return null;
				}
				return result;
			}

			private static int CountMatchingBones(Transform[] sourceBones, Transform[] candidateBones)
			{
				if (sourceBones == null || candidateBones == null)
				{
					return 0;
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				Transform[] array = candidateBones;
				foreach (Transform val in array)
				{
					string text = (((Object)(object)val == (Object)null) ? string.Empty : NormalizeTransformName(((Object)val).name));
					if (text.Length > 0)
					{
						hashSet.Add(text);
					}
				}
				int num = 0;
				array = sourceBones;
				foreach (Transform val2 in array)
				{
					string text2 = (((Object)(object)val2 == (Object)null) ? string.Empty : NormalizeTransformName(((Object)val2).name));
					if (text2.Length > 0 && hashSet.Contains(text2))
					{
						num++;
					}
				}
				return num;
			}

			private static IReadOnlyDictionary<string, Transform> BuildBoneMap(Transform[] bones)
			{
				if (bones == null || bones.Length == 0)
				{
					return null;
				}
				Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
				foreach (Transform val in bones)
				{
					string text = (((Object)(object)val == (Object)null) ? string.Empty : NormalizeTransformName(((Object)val).name));
					if (text.Length > 0 && !dictionary.ContainsKey(text))
					{
						dictionary.Add(text, val);
					}
				}
				return dictionary;
			}

			private static string RendererCategory(Renderer renderer)
			{
				if ((Object)(object)renderer == (Object)null)
				{
					return string.Empty;
				}
				string text = (((Object)renderer).name ?? string.Empty) + " " + string.Join(" ", (from material in renderer.sharedMaterials ?? Array.Empty<Material>()
					where (Object)(object)material != (Object)null
					select CleanMaterialName(((Object)material).name)).ToArray());
				if (text.IndexOf("skirt", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "skirt";
				}
				if (text.IndexOf("short", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("pant", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "pants";
				}
				if (text.IndexOf("shoe", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("boot", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("sock", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "feet";
				}
				if (text.IndexOf("sash", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("badge", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "sash";
				}
				if (text.IndexOf("eye", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "eyes";
				}
				if (text.IndexOf("mouth", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "mouth";
				}
				if (text.IndexOf("hair", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("hat", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "head";
				}
				if (text.IndexOf("skin", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("scout", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("fit", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("outfit", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "body";
				}
				return string.Empty;
			}

			public void UpdatePose()
			{
				//IL_0029: 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_003e: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_stageRoot == (Object)null) && !((Object)(object)_cloneObject == (Object)null))
				{
					Transform transform = _cloneObject.transform;
					transform.localPosition = Vector3.zero;
					transform.localRotation = Quaternion.identity;
					transform.localScale = Vector3.one;
					PoseBinding[] poseBindings = _poseBindings;
					for (int i = 0; i < poseBindings.Length; i++)
					{
						poseBindings[i].Apply();
					}
					_anchorBinding?.Apply();
				}
			}

			public void Destroy()
			{
				if ((Object)(object)_cloneObject != (Object)null)
				{
					Object.Destroy((Object)(object)_cloneObject);
				}
			}

			private static bool IsChildOf(Transform transform, Transform possibleParent)
			{
				while ((Object)(object)transform != (Object)null)
				{
					if ((Object)(object)transform == (Object)(object)possibleParent)
					{
						return true;
					}
					transform = transform.parent;
				}
				return false;
			}

			private static void ReparentStaticVisualRenderers(IReadOnlyDictionary<string, Transform> stageTransforms, GameObject cloneObject)
			{
				//IL_007e: 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_0086: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0093: 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_00a7: 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)
				if (stageTransforms == null || (Object)(object)cloneObject == (Object)null)
				{
					return;
				}
				MeshRenderer[] componentsInChildren = cloneObject.GetComponentsInChildren<MeshRenderer>(true);
				foreach (MeshRenderer val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponent<SkinnedMeshRenderer>() != (Object)null))
					{
						Transform transform = ((Component)val).transform;
						Transform parent = transform.parent;
						string text = (((Object)(object)parent == (Object)null) ? string.Empty : NormalizeTransformName(((Object)parent).name));
						if (text.Length != 0 && stageTransforms.TryGetValue(text, out var value))
						{
							Vector3 localPosition = transform.localPosition;
							Quaternion localRotation = transform.localRotation;
							Vector3 localScale = transform.localScale;
							transform.SetParent(value, false);
							transform.localPosition = localPosition;
							transform.localRotation = localRotation;
							transform.localScale = localScale;
						}
					}
				}
			}

			private static string NormalizeTransformName(string name)
			{
				if (string.IsNullOrWhiteSpace(name))
				{
					return string.Empty;
				}
				string text = name.Replace("(Clone)", string.Empty).Trim();
				int num = text.LastIndexOf('.');
				if (num > 0 && num < text.Length - 1)
				{
					bool flag = true;
					for (int i = num + 1; i < text.Length; i++)
					{
						if (!char.IsDigit(text[i]))
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						text = text.Substring(0, num);
					}
				}
				return text;
			}

			private static int AnchorPriority(string normalizedName)
			{
				if (string.IsNullOrWhiteSpace(normalizedName))
				{
					return 0;
				}
				if (string.Equals(normalizedName, "root", StringComparison.OrdinalIgnoreCase))
				{
					return 4;
				}
				if (string.Equals(normalizedName, "hip", StringComparison.OrdinalIgnoreCase) || string.Equals(normalizedName, "hips", StringComparison.OrdinalIgnoreCase) || string.Equals(normalizedName, "pelvis", StringComparison.OrdinalIgnoreCase))
				{
					return 3;
				}
				if (normalizedName.IndexOf("spine", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return 2;
				}
				return 0;
			}
		}

		private sealed class PlayerOption
		{
			public string Key { get; }

			public string Label { get; }

			public PlayerOption(string key, string label)
			{
				Key = key;
				Label = label;
			}
		}

		private sealed class StageRendererSnapshot
		{
			private readonly Mesh _skinnedMesh;

			private readonly Mesh _filterMesh;

			private readonly bool _rendererEnabled;

			private readonly bool _gameObjectActive;

			public Renderer Renderer { get; }

			public Material[] SharedMaterials { get; }

			public StageRendererSnapshot(Renderer renderer)
			{
				Renderer = renderer;
				SharedMaterials = (((Object)(object)renderer == (Object)null || renderer.sharedMaterials == null) ? Array.Empty<Material>() : renderer.sharedMaterials.ToArray());
				_rendererEnabled = (Object)(object)renderer != (Object)null && renderer.enabled;
				_gameObjectActive = (Object)(object)renderer != (Object)null && ((Component)renderer).gameObject.activeSelf;
				SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
				if ((Object)(object)val != (Object)null)
				{
					_skinnedMesh = val.sharedMesh;
				}
				MeshFilter val2 = (((Object)(object)renderer == (Object)null) ? null : ((Component)renderer).GetComponent<MeshFilter>());
				if ((Object)(object)val2 != (Object)null)
				{
					_filterMesh = val2.sharedMesh;
				}
			}

			public void Restore()
			{
				if (!((Object)(object)Renderer == (Object)null))
				{
					((Component)Renderer).gameObject.SetActive(_gameObjectActive);
					Renderer.enabled = _rendererEnabled;
					Renderer.sharedMaterials = SharedMaterials;
					Renderer.SetPropertyBlock((MaterialPropertyBlock)null);
					Renderer renderer = Renderer;
					SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
					if ((Object)(object)val != (Object)null)
					{
						val.sharedMesh = _skinnedMesh;
					}
					MeshFilter component = ((Component)Renderer).GetComponent<MeshFilter>();
					if ((Object)(object)component != (Object)null)
					{
						component.sharedMesh = _filterMesh;
					}
				}
			}
		}

		public const string PluginGuid = "buhe.peak.bbno.cos";

		public const string PluginName = "BBNO-COS";

		public const string PluginVersion = "1.0.9";

		private const string BundleFileName = "peak_bbno_concert";

		private const string PreferredPrefabName = "BBNOConcertRoot";

		private const double OfficialConcertStartTime = 68.45;

		private const string OfficialSelectionKey = "official";

		private const string AuthorUrl = "https://jisuk.top";

		private const byte NetworkPlayEventCode = 57;

		private const byte NetworkStopEventCode = 58;

		private const float NativeDanceStepSeconds = 1.55f;

		private const int StageAppearanceRetryMaxAttempts = 60;

		private const float StageAppearanceRetryIntervalSeconds = 0.5f;

		private const double BackFlipLandingHoldSeconds = 0.08;

		private const float VolumeApplyEpsilon = 0.001f;

		private static readonly PropertyInfo GuiStyleFontProperty = typeof(GUIStyle).GetProperty("font");

		private static readonly MethodInfo ResourcesGetBuiltinResourceMethod = typeof(Resources).GetMethod("GetBuiltinResource", new Type[2]
		{
			typeof(Type),
			typeof(string)
		});

		internal static ManualLogSource Log;

		private AssetBundle _bundle;

		private GameObject _concertRoot;

		private PlayableDirector _director;

		private AudioSource[] _concertAudioSources = Array.Empty<AudioSource>();

		private float _lastAppliedConcertVolume = -1f;

		private bool _showPanel;

		private bool _panelEnglish;

		private bool _concertSkyActive;

		private Dictionary<string, Material> _borrowableMaterials;

		private Dictionary<string, Shader> _borrowableShaders;

		private bool _savedFogState;

		private float _savedAmbienceMaxFog;

		private bool _savedAmbienceUseFog;

		private bool _savedRenderSettingsFog;

		private FogMode _savedRenderSettingsFogMode;

		private float _savedRenderSettingsFogDensity;

		private Color _savedRenderSettingsFogColor;

		private float _savedUseFogGlobal;

		private float _savedMaxFogGlobal;

		private float _savedFogEnabledGlobal;

		private float _savedFogSphereSizeGlobal;

		private float _savedCloseDistanceModGlobal;

		private float _savedWeatherBlendGlobal;

		private float _savedWindFogDensityGlobal;

		private float _savedWindFogTextureDensityGlobal;

		private float _savedBandFogAmountGlobal;

		private float _savedExtraFogGlobal;

		private float _savedNoEdgeFogGlobal;

		private Vector4 _savedFogCenterGlobal;

		private float _savedFakeMountainEnabledGlobal;

		private ConfigEntry<float> _concertVolumeConfig;

		private ConfigEntry<string> _stageASelection;

		private ConfigEntry<string> _stageBSelection;

		private ConfigEntry<string> _stageCSelection;

		private ConfigEntry<bool> _enableStagePlayerReplacement;

		private ConfigEntry<bool> _startNativePlayerDanceWithConcert;

		private ConfigEntry<bool> _defaultEnglishPanel;

		private bool _bingBongRainBoostApplied;

		private bool _customizationCursorUnlocked;

		private CursorLockMode _previousCursorLockState;

		private bool _previousCursorVisible;

		private int _openStageDropdown = -1;

		private Vector2 _customizationScroll;

		private bool _nativePlayerDanceActive;

		private float _nativePlayerDanceNextStepTime;

		private int _nativePlayerDanceStepIndex;

		private bool _stageAppearanceRetryActive;

		private float _nextStageAppearanceRetryTime;

		private int _stageAppearanceRetryAttempts;

		private readonly Dictionary<string, StageRendererSnapshot[]> _stageOriginalMaterials = new Dictionary<string, StageRendererSnapshot[]>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, StageVisualClone> _stageVisualClones = new Dictionary<string, StageVisualClone>(StringComparer.OrdinalIgnoreCase);

		private static readonly string[] NativePlayerDanceSequence = new string[6] { "A_Scout_Emote_Dance1", "A_Scout_Emote_Dance2", "A_Scout_Emote_CrossedArms", "A_Scout_Emote_Nono", "A_Scout_Emote_Salute", "A_Scout_Emote_Flex" };

		private static readonly string[] PreferredStageHatOrder = new string[39]
		{
			"Cap", "Beret", "Fedora", "PropellerHat", "StrawHat", "AviatorHat", "SailorHat", "MedicHelmet", "Midsummer", "MushroomHat",
			"CrabHat", "CourierHat", "ScoutmasterHat", "Bandana", "NinjaHeadband", "ChefHat", "WizardHat", "WolfEars", "Crown", "Goat",
			"DesertHat", "SunVisorHat", "StrawSunHat", "CowboyHat", "BBHat", "Racing Helmet", "SpaceHelmet White", "PumpkinHead", "MummyHat", "Antlers",
			"GnomeHat", "PlagueDoctor", "ClimbingHelmet", "NumberOne", "FlowerHat", "Sprout", "Ironmouse", "Ice Hood Blue", "Ice Hood Pink"
		};

		private GUIStyle _panelBoxStyle;

		private GUIStyle _panelLabelStyle;

		private GUIStyle _panelButtonStyle;

		private bool _panelFallbackFontResolved;

		private object _panelFallbackFont;

		private bool _panelFallbackFontLogged;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			_stageASelection = ((BaseUnityPlugin)this).Config.Bind<string>("Stage Model Mapping", "StageA", "official", "Dance model A selection. Use 'official' to keep the original concert model.");
			_stageBSelection = ((BaseUnityPlugin)this).Config.Bind<string>("Stage Model Mapping", "StageB", "official", "Dance model B selection. Use 'official' to keep the original concert model.");
			_stageCSelection = ((BaseUnityPlugin)this).Config.Bind<string>("Stage Model Mapping", "StageC", "official", "Dance model C selection. Use 'official' to keep the original concert model.");
			_enableStagePlayerReplacement = ((BaseUnityPlugin)this).Config.Bind<bool>("Stage Model Mapping", "EnablePlayerReplacement", true, "Enable the experimental stage actor customization mapping.");
			_startNativePlayerDanceWithConcert = ((BaseUnityPlugin)this).Config.Bind<bool>("Experimental Native Player Dance", "StartWithConcert", true, "Start the native player dance driver when F8 starts the official concert.");
			_defaultEnglishPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Panel", "DefaultEnglish", false, "Open the BBNO-COS panel in English by default. The in-game language button can still switch it.");
			_panelEnglish = _defaultEnglishPanel.Value;
			_concertVolumeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "ConcertVolume", 1f, "Concert playback volume (0.0 to 1.0).");
			GetConfiguredConcertVolume();
			PhotonNetwork.AddCallbackTarget((object)this);
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"BBNO-COS loaded. F8 plays the official concert prefab when the bundle exists.");
		}

		private void OnSceneUnloaded(Scene scene)
		{
			try
			{
				CleanupRuntimeState();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Error cleaning up runtime state on scene unload: " + ex.Message));
				}
			}
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)289))
			{
				BroadcastAndPlayConcert();
			}
			if (Input.GetKeyDown((KeyCode)290))
			{
				BroadcastAndStopConcert();
			}
			if (Input.GetKeyDown((KeyCode)291))
			{
				_showPanel = !_showPanel;
			}
			if (IsCustomizationCursorKeyDown())
			{
				if (_customizationCursorUnlocked)
				{
					RestoreCustomizationCursor();
					_openStageDropdown = -1;
				}
				else
				{
					UnlockCustomizationCursor();
				}
			}
			if (_customizationCursorUnlocked)
			{
				UnlockCustomizationCursor();
			}
		}

		private void LateUpdate()
		{
			if (_customizationCursorUnlocked)
			{
				UnlockCustomizationCursor();
			}
			if (_concertSkyActive)
			{
				ApplyConcertSky();
				ApplyConcertFogOverride();
				ApplyBingBongRainBoost();
				UpdateStageVisualClones();
				RetryPendingStageAppearanceApply();
				if (_concertVolumeConfig != null)
				{
					UpdateConcertVolume(GetConfiguredConcertVolume());
				}
			}
		}

		private void OnDisable()
		{
			CleanupRuntimeState();
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			SceneManager.sceneUnloaded -= OnSceneUnloaded;
			CleanupRuntimeState();
		}

		private void BroadcastAndPlayConcert()
		{
			RaiseConcertNetworkEvent(57, SelectionKeyToActorNumber(_stageASelection.Value), SelectionKeyToActorNumber(_stageBSelection.Value), SelectionKeyToActorNumber(_stageCSelection.Value));
			PlayConcert();
			ScheduleStageAppearanceRetryIfNeeded("local play");
		}

		private void BroadcastAndStopConcert()
		{
			RaiseConcertNetworkEvent(58, 0, 0, 0);
			StopConcert();
		}

		private void RaiseConcertNetworkEvent(byte eventCode, int stageAActor, int stageBActor, int stageCActor)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (PhotonNetwork.InRoom)
				{
					object[] array = new object[5]
					{
						stageAActor,
						stageBActor,
						stageCActor,
						(PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0,
						PhotonNetwork.Time
					};
					RaiseEventOptions val = new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					};
					bool flag = PhotonNetwork.RaiseEvent(eventCode, (object)array, val, SendOptions.SendReliable);
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)$"Broadcast BBNO-COS network event {eventCode}: raised={flag}, A={stageAActor}, B={stageBActor}, C={stageCActor}.");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Failed to broadcast BBNO-COS network event: " + ex.Message));
				}
			}
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent == null)
			{
				return;
			}
			if (photonEvent.Code == 57)
			{
				ApplyNetworkConcertPlay(photonEvent.CustomData as object[]);
			}
			else if (photonEvent.Code == 58)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"Received BBNO-COS network stop event.");
				}
				StopConcert();
			}
		}

		private void ApplyNetworkConcertPlay(object[] payload)
		{
			try
			{
				if (payload == null || payload.Length < 3)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"Received invalid BBNO-COS play event payload.");
					}
					return;
				}
				int num = Convert.ToInt32(payload[0]);
				int num2 = Convert.ToInt32(payload[1]);
				int num3 = Convert.ToInt32(payload[2]);
				_stageASelection.Value = ActorNumberToSelectionKey(num);
				_stageBSelection.Value = ActorNumberToSelectionKey(num2);
				_stageCSelection.Value = ActorNumberToSelectionKey(num3);
				_openStageDropdown = -1;
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"Received BBNO-COS network play event: A={num}, B={num2}, C={num3}.");
				}
				PlayConcert();
				ScheduleStageAppearanceRetryIfNeeded("network play");
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogWarning((object)("Failed to apply BBNO-COS network play event: " + ex.Message));
				}
			}
		}

		private void PlayConcert()
		{
			DestroyConcertInstance();
			if (EnsureConcertLoaded())
			{
				_concertRoot.SetActive(true);
				if ((Object)(object)_director == (Object)null)
				{
					_concertRoot.SetActive(false);
					Log.LogWarning((object)"Concert prefab has no PlayableDirector, so there is nothing to play yet.");
					return;
				}
				PrepareConcertDirectorForPlayback();
				ApplySelectedStageAppearances();
				_director.time = 68.45;
				_director.Evaluate();
				_director.Play();
				CaptureConcertFogState();
				_concertSkyActive = true;
				ApplyConcertSky();
				ApplyConcertFogOverride();
				Log.LogInfo((object)$"Started BBNO official Timeline at {68.45:0.00}s.");
			}
		}

		private static int SelectionKeyToActorNumber(string selectionKey)
		{
			if (string.IsNullOrWhiteSpace(selectionKey) || string.Equals(selectionKey, "official", StringComparison.OrdinalIgnoreCase))
			{
				return 0;
			}
			return GetCharacterActorNumber(FindCharacterByKey(selectionKey));
		}

		private static string ActorNumberToSelectionKey(int actorNumber)
		{
			if (actorNumber > 0)
			{
				return "actor:" + actorNumber;
			}
			return "official";
		}

		private void PrepareConcertDirectorForPlayback()
		{
			if (!((Object)(object)_director == (Object)null))
			{
				_director.Stop();
				PatchBackFlipTimelineContinuity(_director);
				ResetConcertAnimatorState();
				_director.time = 0.0;
				_director.Evaluate();
				_director.Play();
				_director.Pause();
				_director.time = 68.45;
				_director.Evaluate();
			}
		}

		private static void PatchBackFlipTimelineContinuity(PlayableDirector director)
		{
			PlayableAsset playableAsset = director.playableAsset;
			TimelineAsset val = (TimelineAsset)(object)((playableAsset is TimelineAsset) ? playableAsset : null);
			if (val == null)
			{
				return;
			}
			TimelineClip val2 = null;
			TimelineClip val3 = null;
			foreach (TrackAsset outputTrack in val.GetOutputTracks())
			{
				if ((Object)(object)outputTrack == (Object)null)
				{
					continue;
				}
				foreach (TimelineClip clip in outputTrack.GetClips())
				{
					if (clip != null)
					{
						if (string.Equals(clip.displayName, "A_Scout_Emote_BackFlip", StringComparison.Ordinal) && IsNear(clip.start, 189.1, 0.02))
						{
							val2 = clip;
						}
						if (string.Equals(clip.displayName, "A_Scout_Emote_BackFlip", StringComparison.Ordinal))
						{
							RetimingBackFlipClipToFullLength(clip);
							clip.blendInDuration = 0.0;
							clip.blendOutDuration = 0.0;
						}
						else if (string.Equals(clip.displayName, "A_Scout_Emote_Dance1", StringComparison.Ordinal) && IsNear(clip.start, 189.1945518909643, 0.02))
						{
							val3 = clip;
						}
					}
				}
			}
			if (val2 != null && val3 != null)
			{
				double start = val2.start + val2.duration + 0.08;
				double start2 = val3.start;
				val3.start = start;
				val3.blendInDuration = 0.0;
				val2.blendOutDuration = 0.0;
				Log.LogInfo((object)$"Patched BBNO BackFlip continuity: moved Dance1 from {start2:0.000}s to {val3.start:0.000}s ({0.08:0.00}s landing hold, 0 blend).");
			}
		}

		private static void RetimingBackFlipClipToFullLength(TimelineClip clip)
		{
			if (clip == null || clip.duration <= 0.0)
			{
				return;
			}
			Object asset = clip.asset;
			AnimationPlayableAsset val = (AnimationPlayableAsset)(object)((asset is AnimationPlayableAsset) ? asset : null);
			if (val != null && !((Object)(object)val.clip == (Object)null))
			{
				double num = val.clip.length;
				if (!(num <= 0.0) && !(clip.duration * clip.timeScale >= num * 0.98))
				{
					double timeScale = clip.timeScale;
					clip.clipIn = 0.0;
					clip.timeScale = num / clip.duration;
					Log.LogInfo((object)$"Retimed BBNO BackFlip at {clip.start:0.000}s from timeScale {timeScale:0.000000} to {clip.timeScale:0.000000} so the full landing plays.");
				}
			}
		}

		private static bool IsNear(double value, double target, double tolerance)
		{
			return Math.Abs(value - target) <= tolerance;
		}

		private void ResetConcertAnimatorState()
		{
			if ((Object)(object)_concertRoot == (Object)null)
			{
				return;
			}
			Animator[] componentsInChildren = _concertRoot.GetComponentsInChildren<Animator>(true);
			foreach (Animator val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					val.cullingMode = (AnimatorCullingMode)0;
					val.updateMode = (AnimatorUpdateMode)0;
					val.Rebind();
					val.Update(0f);
				}
			}
			SkinnedMeshRenderer[] componentsInChildren2 = _concertRoot.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val2 in componentsInChildren2)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					val2.updateWhenOffscreen = true;
				}
			}
		}

		private void StopConcert()
		{
			ClearStageAppearanceRetry();
			if ((Object)(object)_director != (Object)null)
			{
				_director.Stop();
			}
			if ((Object)(object)_concertRoot != (Object)null)
			{
				_concertRoot.SetActive(false);
			}
			_concertSkyActive = false;
			ResetConcertSky();
			RestoreConcertFogState();
			StopNativePlayerDance();
			DestroyConcertInstance();
		}

		private void CleanupRuntimeState()
		{
			bool num = _concertSkyActive || _savedFogState;
			RestoreCustomizationCursor();
			_openStageDropdown = -1;
			ClearStageAppearanceRetry();
			if ((Object)(object)_director != (Object)null)
			{
				_director.Stop();
			}
			if ((Object)(object)_concertRoot != (Object)null)
			{
				_concertRoot.SetActive(false);
			}
			_concertSkyActive = false;
			if (num)
			{
				ResetConcertSky();
				RestoreConcertFogState();
			}
			StopNativePlayerDance();
			RestoreAllStageOriginalMaterials();
			DestroyConcertInstance();
		}

		private void DestroyConcertInstance()
		{
			DestroyAllStageVisualClones();
			_stageOriginalMaterials.Clear();
			if ((Object)(object)_director != (Object)null)
			{
				_director.Stop();
				_director = null;
			}
			if ((Object)(object)_concertRoot != (Object)null)
			{
				_concertRoot.SetActive(false);
				Object.Destroy((Object)(object)_concertRoot);
				_concertRoot = null;
			}
			_concertAudioSources = Array.Empty<AudioSource>();
			_lastAppliedConcertVolume = -1f;
		}

		private void UnlockCustomizationCursor()
		{
			//IL_0009: 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)
			if (!_customizationCursorUnlocked)
			{
				_previousCursorLockState = Cursor.lockState;
				_previousCursorVisible = Cursor.visible;
				_customizationCursorUnlocked = true;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
		}

		private void RestoreCustomizationCursor()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (_customizationCursorUnlocked)
			{
				Cursor.lockState = _previousCursorLockState;
				Cursor.visible = _previousCursorVisible;
				_customizationCursorUnlocked = false;
			}
		}

		private static bool IsCustomizationCursorKeyDown()
		{
			if (Input.GetKeyDown((KeyCode)96))
			{
				return true;
			}
			try
			{
				return Input.GetKeyDown("`") || Input.GetKeyDown("~");
			}
			catch
			{
				return false;
			}
		}

		private void InitPanelStyles()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_003c: 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_004d: Expected O, but got Unknown
			//IL_007c: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			_panelBoxStyle = new GUIStyle(GUI.skin.box);
			_panelLabelStyle = new GUIStyle(GUI.skin.label)
			{
				clipping = (TextClipping)1
			};
			_panelButtonStyle = new GUIStyle(GUI.skin.button)
			{
				clipping = (TextClipping)1
			};
			ApplyPanelFont(_panelBoxStyle);
			ApplyPanelFont(_panelLabelStyle);
			ApplyPanelFont(_panelButtonStyle);
			_panelBoxStyle.normal.textColor = Color.white;
			_panelBoxStyle.focused.textColor = Color.white;
			_panelBoxStyle.active.textColor = Color.white;
			_panelBoxStyle.hover.textColor = Color.white;
			_panelBoxStyle.onNormal.textColor = Color.white;
			_panelBoxStyle.onFocused.textColor = Color.white;
			_panelBoxStyle.onActive.textColor = Color.white;
			_panelBoxStyle.onHover.textColor = Color.white;
			_panelLabelStyle.normal.textColor = Color.white;
			_panelLabelStyle.focused.textColor = Color.white;
			_panelLabelStyle.active.textColor = Color.white;
			_panelLabelStyle.hover.textColor = Color.white;
			_panelLabelStyle.onNormal.textColor = Color.white;
			_panelLabelStyle.onFocused.textColor = Color.white;
			_panelLabelStyle.onActive.textColor = Color.white;
			_panelLabelStyle.onHover.textColor = Color.white;
			_panelButtonStyle.normal.textColor = Color.white;
			_panelButtonStyle.focused.textColor = Color.white;
			_panelButtonStyle.active.textColor = Color.white;
			_panelButtonStyle.hover.textColor = Color.white;
			_panelButtonStyle.onNormal.textColor = Color.white;
			_panelButtonStyle.onFocused.textColor = Color.white;
			_panelButtonStyle.onActive.textColor = Color.white;
			_panelButtonStyle.onHover.textColor = Color.white;
		}

		private void ApplyPanelFont(GUIStyle style)
		{
			if (style == null)
			{
				return;
			}
			try
			{
				if (!(GuiStyleFontProperty == null))
				{
					GuiStyleFontProperty.SetValue(style, ResolvePanelFallbackFont(), null);
				}
			}
			catch (Exception ex)
			{
				if (!_panelFallbackFontLogged)
				{
					_panelFallbackFontLogged = true;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Failed to apply BBNO-COS panel font fallback: " + ex.Message));
					}
				}
			}
		}

		private object ResolvePanelFallbackFont()
		{
			if (_panelFallbackFontResolved)
			{
				return _panelFallbackFont;
			}
			_panelFallbackFontResolved = true;
			try
			{
				Type type = GuiStyleFontProperty?.PropertyType ?? Type.GetType("UnityEngine.Font, UnityEngine.TextRenderingModule");
				if (type == null)
				{
					return null;
				}
				if (ResourcesGetBuiltinResourceMethod != null)
				{
					string[] array = new string[2] { "Arial.ttf", "LegacyRuntime.ttf" };
					foreach (string text in array)
					{
						object obj = ResourcesGetBuiltinResourceMethod.Invoke(null, new object[2] { type, text });
						if (obj != null)
						{
							_panelFallbackFont = obj;
							ManualLogSource log = Log;
							if (log != null)
							{
								log.LogInfo((object)("BBNO-COS panel using IMGUI font fallback: " + text));
							}
							return _panelFallbackFont;
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (!_panelFallbackFontLogged)
				{
					_panelFallbackFontLogged = true;
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Failed to resolve BBNO-COS panel font fallback: " + ex.Message));
					}
				}
			}
			return null;
		}

		private void OnGUI()
		{
			if (!_showPanel)
			{
				return;
			}
			InitPanelStyles();
			try
			{
				DrawMainPanel();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to draw BBNO-COS panel: " + ex.Message));
				}
			}
		}

		private void DrawMainPanel()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerOption> list = BuildPlayerOptions();
			GUILayout.BeginArea(new Rect(20f, 20f, 470f, 345f), _panelBoxStyle);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(PanelText("PEAK BBNO$ 演唱会 COS", "PEAK BBNO$ Concert COS"), _panelLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(225f),
				GUILayout.Height(22f)
			});
			if (GUILayout.Button(PanelText("作者:KK还要玩,不鹤", "Author: KK还要玩, 不鹤"), _panelButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(160f),
				GUILayout.Height(22f)
			}))
			{
				Application.OpenURL("https://jisuk.top");
			}
			if (GUILayout.Button(_panelEnglish ? "中文" : "English", _panelButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(70f),
				GUILayout.Height(22f)
			}))
			{
				_panelEnglish = !_panelEnglish;
			}
			GUILayout.EndHorizontal();
			GUILayout.Label(PanelText("F8 播放演唱会    F9 停止    F10 隐藏/显示面板", "F8 Play concert    F9 Stop    F10 Show/hide panel"), _panelLabelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(_customizationCursorUnlocked ? PanelText($"鼠标已解锁(` 键切换)    玩家数:{Mathf.Max(0, list.Count - 1)}", $"Cursor unlocked (` to toggle)    Players: {Mathf.Max(0, list.Count - 1)}") : PanelText($"按 ` 解锁鼠标选择角色    玩家数:{Mathf.Max(0, list.Count - 1)}", $"Press ` to unlock cursor    Players: {Mathf.Max(0, list.Count - 1)}"), _panelLabelStyle, Array.Empty<GUILayoutOption>());
			_customizationScroll = GUILayout.BeginScrollView(_customizationScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(140f) });
			DrawStageSelectionRow(0, PanelText("舞蹈 A", "Dance A"), _stageASelection, list);
			DrawStageSelectionRow(1, PanelText("舞蹈 B", "Dance B"), _stageBSelection, list);
			DrawStageSelectionRow(2, PanelText("舞蹈 C", "Dance C"), _stageCSelection, list);
			GUILayout.EndScrollView();
			GUILayout.BeginHorizontal(_panelBoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) });
			GUILayout.Label(PanelText("演唱会音量", "Concert Volume"), _panelLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
			float configuredConcertVolume = GetConfiguredConcertVolume();
			float num = Mathf.Clamp01(GUILayout.HorizontalSlider(configuredConcertVolume, 0f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }));
			GUILayout.Space(10f);
			GUILayout.Label($"{(int)(num * 100f)}%", _panelLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) });
			if (Mathf.Abs(num - configuredConcertVolume) > 0.01f)
			{
				_concertVolumeConfig.Value = num;
				((BaseUnityPlugin)this).Config.Save();
				UpdateConcertVolume(num);
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.Label(PanelText("本模组免费", "This mod is free"), _panelLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) });
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button(PanelText("全部恢复官方原版", "Restore official models"), _panelButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(300f),
				GUILayout.Height(26f)
			}))
			{
				_stageASelection.Value = "official";
				_stageBSelection.Value = "official";
				_stageCSelection.Value = "official";
				((BaseUnityPlugin)this).Config.Save();
				_openStageDropdown = -1;
			}
			GUILayout.Label(PanelText($"当前可选玩家:{Mathf.Max(0, list.Count - 1)}", $"Available players: {Mathf.Max(0, list.Count - 1)}"), _panelLabelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.EndArea();
		}

		private string PanelText(string chinese, string english)
		{
			if (!_panelEnglish)
			{
				return chinese;
			}
			return english;
		}

		private void DrawStageSelectionRow(int stageIndex, string label, ConfigEntry<string> selection, List<PlayerOption> options)
		{
			GUILayout.BeginVertical(_panelBoxStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(label, _panelLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
			if (GUILayout.Button(GetOptionLabel(selection.Value, options), _panelButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }))
			{
				_openStageDropdown = ((_openStageDropdown == stageIndex) ? (-1) : stageIndex);
			}
			GUILayout.EndHorizontal();
			if (_openStageDropdown == stageIndex)
			{
				foreach (PlayerOption option in options)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Space(110f);
					if (GUILayout.Button(option.Label, _panelButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(300f),
						GUILayout.Height(24f)
					}))
					{
						selection.Value = option.Key;
						((BaseUnityPlugin)this).Config.Save();
						_openStageDropdown = -1;
					}
					GUILayout.EndHorizontal();
				}
			}
			GUILayout.EndVertical();
		}

		private string GetOptionLabel(string key, List<PlayerOption> options)
		{
			PlayerOption playerOption = options.FirstOrDefault((PlayerOption playerOption2) => string.Equals(playerOption2.Key, key, StringComparison.OrdinalIgnoreCase));
			if (playerOption != null)
			{
				return playerOption.Label;
			}
			Character val = FindCharacterByKey(key);
			if (!((Object)(object)val == (Object)null))
			{
				return CharacterDisplayName(val);
			}
			return PanelText("玩家不存在(" + key + ")", "Player missing (" + key + ")");
		}

		private List<PlayerOption> BuildPlayerOptions()
		{
			List<PlayerOption> list = new List<PlayerOption>
			{
				new PlayerOption("official", PanelText("官方原版", "Official original"))
			};
			foreach (Character item in SnapshotCharacters().Where(IsSelectablePlayerCharacter).OrderByDescending(IsLocalCharacter).ThenBy((Character character) => GetCharacterActorNumber(character)))
			{
				string key = CharacterKey(item);
				if (!string.IsNullOrWhiteSpace(key) && !list.Any((PlayerOption option) => option.Key == key))
				{
					string text = CharacterDisplayName(item);
					list.Add(new PlayerOption(key, IsLocalCharacter(item) ? (text + PanelText("(自己)", " (You)")) : text));
				}
			}
			return list;
		}

		private static IEnumerable<Character> SnapshotCharacters()
		{
			try
			{
				IEnumerable<Character> result;
				if (Character.AllCharacters != null)
				{
					IEnumerable<Character> enumerable = Character.AllCharacters.ToArray();
					result = enumerable;
				}
				else
				{
					result = Enumerable.Empty<Character>();
				}
				return result;
			}
			catch
			{
				return Enumerable.Empty<Character>();
			}
		}

		private static bool IsSelectablePlayerCharacter(Character character)
		{
			try
			{
				return (Object)(object)character != (Object)null && (Object)(object)((Component)character).gameObject != (Object)null && !character.isBot;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsLocalCharacter(Character character)
		{
			try
			{
				return (Object)(object)character != (Object)null && character.IsLocal;
			}
			catch
			{
				return false;
			}
		}

		private static string CharacterKey(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return string.Empty;
			}
			if (IsLocalCharacter(character))
			{
				return "local";
			}
			try
			{
				if ((Object)(object)((MonoBehaviourPun)character).photonView != (Object)null && ((MonoBehaviourPun)character).photonView.Owner != null)
				{
					string userId = ((MonoBehaviourPun)character).photonView.Owner.UserId;
					if (!string.IsNullOrWhiteSpace(userId))
					{
						return "uid:" + userId;
					}
					return "actor:" + ((MonoBehaviourPun)character).photonView.Owner.ActorNumber;
				}
			}
			catch
			{
			}
			return "name:" + character.characterName;
		}

		private static Character FindCharacterByKey(string key)
		{
			if (string.IsNullOrWhiteSpace(key) || string.Equals(key, "official", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			foreach (Character item in SnapshotCharacters())
			{
				if (IsSelectablePlayerCharacter(item) && CharacterMatchesKey(item, key))
				{
					return item;
				}
			}
			return null;
		}

		private static bool CharacterMatchesKey(Character character, string key)
		{
			if ((Object)(object)character == (Object)null || string.IsNullOrWhiteSpace(key))
			{
				return false;
			}
			if (string.Equals(CharacterKey(character), key, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (string.Equals(key, "local", StringComparison.OrdinalIgnoreCase))
			{
				return IsLocalCharacter(character);
			}
			if (TryParseActorSelectionKey(key, out var actorNumber))
			{
				return GetCharacterActorNumber(character) == actorNumber;
			}
			return false;
		}

		private static bool TryParseActorSelectionKey(string key, out int actorNumber)
		{
			actorNumber = 0;
			if (string.IsNullOrWhiteSpace(key) || !key.StartsWith("actor:", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return int.TryParse(key.Substring("actor:".Length), out actorNumber);
		}

		private static int GetCharacterActorNumber(Character character)
		{
			try
			{
				return ((Object)(object)character != (Object)null && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null && ((MonoBehaviourPun)character).photonView.Owner != null) ? ((MonoBehaviourPun)character).photonView.Owner.ActorNumber : int.MaxValue;
			}
			catch
			{
				return int.MaxValue;
			}
		}

		private static string CharacterDisplayName(Character character)
		{
			try
			{
				string characterName = character.characterName;
				if (!string.IsNullOrWhiteSpace(characterName))
				{
					return characterName;
				}
			}
			catch
			{
			}
			return $"Player {GetCharacterActorNumber(character)}";
		}

		private void ToggleNativePlayerDance()
		{
			if (_nativePlayerDanceActive)
			{
				StopNativePlayerDance();
			}
			else
			{
				StartNativePlayerDance();
			}
		}

		private void StartNativePlayerDance()
		{
			_nativePlayerDanceActive = true;
			_nativePlayerDanceStepIndex = 0;
			_nativePlayerDanceNextStepTime = 0f;
			UpdateNativePlayerDance();
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Native player dance driver started.");
			}
		}

		private void StopNativePlayerDance()
		{
			if (!_nativePlayerDanceActive)
			{
				return;
			}
			_nativePlayerDanceActive = false;
			foreach (Character item in SnapshotCharacters().Where(IsSelectablePlayerCharacter))
			{
				TrySetNativePlayerEmote(item, enabled: false);
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Native player dance driver stopped.");
			}
		}

		private void UpdateNativePlayerDance()
		{
			if (!_nativePlayerDanceActive || Time.time < _nativePlayerDanceNextStepTime)
			{
				return;
			}
			_nativePlayerDanceNextStepTime = Time.time + 1.55f;
			int num = 0;
			foreach (Character item in SnapshotCharacters().Where(IsSelectablePlayerCharacter))
			{
				int num2 = Mathf.Abs(GetCharacterActorNumber(item));
				string animationName = NativePlayerDanceSequence[(_nativePlayerDanceStepIndex + num2) % NativePlayerDanceSequence.Length];
				if (TryPlayNativePlayerDance(item, animationName))
				{
					num++;
				}
			}
			_nativePlayerDanceStepIndex++;
			if (num == 0)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Native player dance driver found no playable player animator states this step.");
				}
			}
		}

		private static bool TryPlayNativePlayerDance(Character character, string animationName)
		{
			try
			{
				Animator val = (((Object)(object)character == (Object)null || character.refs == null) ? null : character.refs.animator);
				if ((Object)(object)val == (Object)null || string.IsNullOrWhiteSpace(animationName))
				{
					return false;
				}
				if (!HasAnimatorState(val, animationName))
				{
					return false;
				}
				val.cullingMode = (AnimatorCullingMode)0;
				val.updateMode = (AnimatorUpdateMode)0;
				val.SetBool("Emote", true);
				val.Play(animationName, 0, 0f);
				val.Update(0f);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Native player dance failed for '" + animationName + "': " + ex.Message));
				}
				return false;
			}
		}

		private static bool HasAnimatorState(Animator animator, string stateName)
		{
			if ((Object)(object)animator == (Object)null || string.IsNullOrWhiteSpace(stateName))
			{
				return false;
			}
			if (!animator.HasState(0, Animator.StringToHash(stateName)))
			{
				return animator.HasState(0, Animator.StringToHash("Base Layer." + stateName));
			}
			return true;
		}

		private static void TrySetNativePlayerEmote(Character character, bool enabled)
		{
			try
			{
				Animator val = (((Object)(object)character == (Object)null || character.refs == null) ? null : character.refs.animator);
				if ((Object)(object)val != (Object)null)
				{
					val.SetBool("Emote", enabled);
				}
			}
			catch
			{
			}
		}

		private void ApplySelectedStageAppearances()
		{
			ApplyStageAppearance("Dance Model A", "BBNO Parent", _stageASelection.Value);
			ApplyStageAppearance("Dance Model B", "IronMouseParent", _stageBSelection.Value);
			ApplyStageAppearance("Dance Model C", "Vanilla Parent", _stageCSelection.Value);
		}

		private void ScheduleStageAppearanceRetryIfNeeded(string reason)
		{
			if (!HasUnresolvedStageSelection())
			{
				_stageAppearanceRetryActive = false;
				return;
			}
			_stageAppearanceRetryActive = true;
			_stageAppearanceRetryAttempts = 0;
			_nextStageAppearanceRetryTime = Time.time + 0.5f;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)("Scheduled BBNO-COS stage appearance retry after " + reason + ": " + DescribeStageSelections() + "."));
			}
		}

		private void RetryPendingStageAppearanceApply()
		{
			if (!_stageAppearanceRetryActive || Time.time < _nextStageAppearanceRetryTime)
			{
				return;
			}
			if ((Object)(object)_concertRoot == (Object)null || !_concertRoot.activeInHierarchy)
			{
				ClearStageAppearanceRetry();
				return;
			}
			_stageAppearanceRetryAttempts++;
			if (!HasUnresolvedStageSelection())
			{
				ApplySelectedStageAppearances();
				ClearStageAppearanceRetry();
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"BBNO-COS stage appearance retry resolved after {_stageAppearanceRetryAttempts} attempt(s).");
				}
			}
			else if (_stageAppearanceRetryAttempts >= 60)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("BBNO-COS stage appearance retry expired with unresolved selection(s): " + DescribeStageSelections() + "."));
				}
				ClearStageAppearanceRetry();
			}
			else
			{
				_nextStageAppearanceRetryTime = Time.time + 0.5f;
			}
		}

		private void ClearStageAppearanceRetry()
		{
			_stageAppearanceRetryActive = false;
			_stageAppearanceRetryAttempts = 0;
			_nextStageAppearanceRetryTime = 0f;
		}

		private bool HasUnresolvedStageSelection()
		{
			if (!IsUnresolvedStageSelection(_stageASelection.Value) && !IsUnresolvedStageSelection(_stageBSelection.Value))
			{
				return IsUnresolvedStageSelection(_stageCSelection.Value);
			}
			return true;
		}

		private static bool IsUnresolvedStageSelection(string selectionKey)
		{
			if (!string.IsNullOrWhiteSpace(selectionKey) && !string.Equals(selectionKey, "official", StringComparison.OrdinalIgnoreCase))
			{
				return (Object)(object)FindCharacterByKey(selectionKey) == (Object)null;
			}
			return false;
		}

		private string DescribeStageSelections()
		{
			return "A=" + _stageASelection.Value + ", B=" + _stageBSelection.Value + ", C=" + _stageCSelection.Value;
		}

		private void ApplyStageAppearance(string label, string stageRootName, string selectionKey)
		{
			Transform val = FindChildByName(((Object)(object)_concertRoot == (Object)null) ? null : _concertRoot.transform, stageRootName);
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)(label + ": stage root '" + stageRootName + "' was not found; keeping official original."));
				}
				return;
			}
			CaptureStageOriginalMaterials(stageRootName, val);
			if (string.IsNullOrWhiteSpace(selectionKey) || string.Equals(selectionKey, "official", StringComparison.OrdinalIgnoreCase))
			{
				RestoreStageOriginalMaterials(stageRootName);
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)(label + ": keeping official original."));
				}
				return;
			}
			Character val2 = FindCharacterByKey(selectionKey);
			if ((Object)(object)val2 == (Object)null)
			{
				RestoreStageOriginalMaterials(stageRootName);
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogWarning((object)(label + ": selected player '" + selectionKey + "' was not found; keeping official original."));
				}
				return;
			}
			RestoreStageOriginalMaterials(stageRootName);
			if (!IsStagePlayerReplacementEnabled())
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogInfo((object)(label + ": player appearance replacement is disabled for now; keeping official original."));
				}
			}
			else if (CopyPlayerCustomizationToStage(val2, val))
			{
				ManualLogSource log5 = Log;
				if (log5 != null)
				{
					log5.LogInfo((object)(label + ": copied player customization from " + CharacterDisplayName(val2) + "."));
				}
			}
			else
			{
				ManualLogSource log6 = Log;
				if (log6 != null)
				{
					log6.LogWarning((object)(label + ": player customization mapping failed; keeping official original."));
				}
			}
		}

		private bool IsStagePlayerReplacementEnabled()
		{
			if (_enableStagePlayerReplacement != null)
			{
				return _enableStagePlayerReplacement.Value;
			}
			return false;
		}

		private void CaptureStageOriginalMaterials(string stageRootName, Transform stageRoot)
		{
			if (!_stageOriginalMaterials.ContainsKey(stageRootName) && !((Object)(object)stageRoot == (Object)null))
			{
				_stageOriginalMaterials[stageRootName] = (from renderer in ((Component)stageRoot).GetComponentsInChildren<Renderer>(true)
					where (Object)(object)renderer != (Object)null
					select new StageRendererSnapshot(renderer)).ToArray();
			}
		}

		private void RestoreStageOriginalMaterials(string stageRootName)
		{
			DestroyStageVisualClone(stageRootName);
			if (!_stageOriginalMaterials.TryGetValue(stageRootName, out var value))
			{
				return;
			}
			StageRendererSnapshot[] array = value;
			foreach (StageRendererSnapshot stageRendererSnapshot in array)
			{
				if ((Object)(object)stageRendererSnapshot.Renderer != (Object)null)
				{
					stageRendererSnapshot.Restore();
				}
			}
			RemoveTemporaryStageCustomizationRefs(stageRootName);
		}

		private void RemoveTemporaryStageCustomizationRefs(string stageRootName)
		{
			Transform val = FindChildByName(((Object)(object)_concertRoot == (Object)null) ? null : _concertRoot.transform, stageRootName);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = 0;
			CustomizationRefs[] componentsInChildren = ((Component)val).GetComponentsInChildren<CustomizationRefs>(true);
			foreach (CustomizationRefs val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					Object.Destroy((Object)(object)val2);
					num++;
				}
			}
			if (num > 0)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Removed {num} temporary stage customization refs under '{stageRootName}'.");
				}
			}
		}

		private void RestoreAllStageOriginalMaterials()
		{
			string[] array = _stageOriginalMaterials.Keys.ToArray();
			foreach (string stageRootName in array)
			{
				RestoreStageOriginalMaterials(stageRootName);
			}
		}

		private void UpdateStageVisualClones()
		{
			StageVisualClone[] array = _stageVisualClones.Values.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i]?.UpdatePose();
			}
		}

		private void DestroyStageVisualClone(string stageRootName)
		{
			if (!string.IsNullOrWhiteSpace(stageRootName) && _stageVisualClones.TryGetValue(stageRootName, out var value))
			{
				value.Destroy();
				_stageVisualClones.Remove(stageRootName);
			}
		}

		private void DestroyAllStageVisualClones()
		{
			string[] array = _stageVisualClones.Keys.ToArray();
			foreach (string stageRootName in array)
			{
				DestroyStageVisualClone(stageRootName);
			}
		}

		private static Transform FindChildByName(Transform root, string childName)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (string.Equals(((Object)val).name, childName, StringComparison.OrdinalIgnoreCase))
				{
					return val;
				}
			}
			return null;
		}

		private bool ApplyPlayerVisualCloneToStage(Character source, string stageRootName, Transform stageRoot)
		{
			//IL_006b: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || (Object)(object)stageRoot == (Object)null || string.IsNullOrWhiteSpace(stageRootName))
			{
				return false;
			}
			Transform sourceVisualRoot = GetSourceVisualRoot(source);
			if ((Object)(object)sourceVisualRoot == (Object)null)
			{
				return false;
			}
			DestroyStageVisualClone(stageRootName);
			HideOfficialStageVisuals(stageRoot);
			GameObject val = Object.Instantiate<GameObject>(((Component)sourceVisualRoot).gameObject, stageRoot);
			((Object)val).name = "BBNO-COS Visual Clone - " + CharacterDisplayName(source);
			val.SetActive(true);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = Vector3.one;
			StripRuntimeComponentsFromVisualClone(val);
			StageVisualClone stageVisualClone = StageVisualClone.Create(stageRoot, val);
			if (stageVisualClone == null || stageVisualClone.BoundRendererCount == 0)
			{
				Object.Destroy((Object)(object)val);
				RestoreStageOriginalMaterials(stageRootName);
				return false;
			}
			stageVisualClone.UpdatePose();
			_stageVisualClones[stageRootName] = stageVisualClone;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Created stage visual clone for '{stageRootName}'. Bound skinned renderers: {stageVisualClone.BoundRendererCount}.");
			}
			return true;
		}

		private static Transform GetSourceVisualRoot(Character source)
		{
			try
			{
				if ((Object)(object)source != (Object)null && source.refs != null && (Object)(object)source.refs.animator != (Object)null)
				{
					return ((Component)source.refs.animator).transform;
				}
			}
			catch
			{
			}
			CustomizationRefs customizationRefs = GetCustomizationRefs(source);
			if (!((Object)(object)customizationRefs == (Object)null))
			{
				return ((Component)customizationRefs).transform;
			}
			return null;
		}

		private static void HideOfficialStageVisuals(Transform stageRoot)
		{
			if ((Object)(object)stageRoot == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)stageRoot).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (ShouldHideOfficialStageRenderer(val))
				{
					val.enabled = false;
				}
			}
		}

		private static bool ShouldHideOfficialStageRenderer(Renderer renderer)
		{
			if ((Object)(object)renderer == (Object)null || renderer is ParticleSystemRenderer || renderer is LineRenderer || renderer is TrailRenderer || (!(renderer is MeshRenderer) && !(renderer is SkinnedMeshRenderer)))
			{
				return false;
			}
			string name = ((Object)renderer).name ?? string.Empty;
			string transformPath = GetTransformPath(((Component)renderer).transform);
			if (LooksLikeNonCharacterStageEffect(name) || LooksLikeNonCharacterStageEffect(transformPath))
			{
				return false;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			foreach (Material val in sharedMaterials)
			{
				string name2 = (((Object)(object)val == (Object)null) ? string.Empty : CleanMaterialName(((Object)val).name));
				string name3 = (((Object)(object)val == (Object)null || (Object)(object)val.shader == (Object)null) ? string.Empty : ((Object)val.shader).name);
				if (LooksLikeNonCharacterStageEffect(name2) || IsObscuringEffectName(name3))
				{
					return false;
				}
			}
			return true;
		}

		private static bool LooksLikeNonCharacterStageEffect(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			if (name.IndexOf("particle", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("poof", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("steam", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("smoke", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("fog", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("spark", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("light", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("beam", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("money", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("dollar", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("cash", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("bubble", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf("sfx", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static void StripRuntimeComponentsFromVisualClone(GameObject cloneObject)
		{
			//IL_00d9: 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)
			if ((Object)(object)cloneObject == (Object)null)
			{
				return;
			}
			Animator[] componentsInChildren = cloneObject.GetComponentsInChildren<Animator>(true);
			foreach (Animator obj in componentsInChildren)
			{
				((Behaviour)obj).enabled = false;
				obj.cullingMode = (AnimatorCullingMode)0;
			}
			MonoBehaviour[] componentsInChildren2 = cloneObject.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren2)
			{
				if ((Object)(object)val != (Object)null)
				{
					((Behaviour)val).enabled = false;
				}
			}
			Collider[] componentsInChildren3 = cloneObject.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				componentsInChildren3[i].enabled = false;
			}
			Rigidbody[] componentsInChildren4 = cloneObject.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody obj2 in componentsInChildren4)
			{
				obj2.isKinematic = true;
				obj2.detectCollisions = false;
			}
			AudioSource[] componentsInChildren5 = cloneObject.GetComponentsInChildren<AudioSource>(true);
			for (int i = 0; i < componentsInChildren5.Length; i++)
			{
				((Behaviour)componentsInChildren5[i]).enabled = false;
			}
			ParticleSystem[] componentsInChildren6 = cloneObject.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem obj3 in componentsInChildren6)
			{
				EmissionModule emission = obj3.emission;
				((EmissionModule)(ref emission)).enabled = false;
				obj3.Stop(true, (ParticleSystemStopBehavior)0);
				obj3.Clear(true);
			}
			SkinnedMeshRenderer[] componentsInChildren7 = cloneObject.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer obj4 in componentsInChildren7)
			{
				((Renderer)obj4).enabled = true;
				obj4.updateWhenOffscreen = true;
			}
			Renderer[] componentsInChildren8 = cloneObject.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren8)
			{
				if (val2 is MeshRenderer)
				{
					val2.enabled = true;
				}
			}
		}

		private static bool CopyPlayerCustomizationToStage(Character source, Transform stageRoot)
		{
			CustomizationRefs customizationRefs = GetCustomizationRefs(source);
			CustomizationRefs orCreateStageCustomizationRefs = GetOrCreateStageCustomizationRefs(stageRoot, customizationRefs);
			if ((Object)(object)customizationRefs == (Object)null || (Object)(object)orCreateStageCustomizationRefs == (Object)null)
			{
				return false;
			}
			bool flag = ApplyOfficialCustomizationToStage(source, orCreateStageCustomizationRefs);
			CopyPlayerClothingMaterials(customizationRefs, orCreateStageCustomizationRefs);
			CopyPlayerSashMaterials(customizationRefs, orCreateStageCustomizationRefs);
			CopyPlayerAccessoryToStageFace(customizationRefs, orCreateStageCustomizationRefs);
			NormalizeStageCustomizationRefs(orCreateStageCustomizationRefs);
			DisableUnmappedStageCosmeticRenderers(orCreateStageCustomizationRefs);
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Stage customization refs applied. Official API: {flag}. No player renderer mesh/bone fallback was used.");
			}
			return flag;
		}

		private static void CopyPlayerClothingMaterials(CustomizationRefs sourceRefs, CustomizationRefs targetRefs)
		{
			if (!((Object)(object)sourceRefs == (Object)null) && !((Object)(object)targetRefs == (Object)null))
			{
				CopyRendererMaterialsOnly(sourceRefs.shorts, targetRefs.shorts);
				CopyRendererMaterialsOnly(sourceRefs.skirt, targetRefs.skirt);
				CopyRendererMaterialsOnly(sourceRefs.shortsShadow, targetRefs.shortsShadow);
				CopyRendererMaterialsOnly(sourceRefs.skirtShadow, targetRefs.skirtShadow);
			}
		}

		private static void CopyPlayerSashMaterials(CustomizationRefs sourceRefs, CustomizationRefs targetRefs)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			if (!((Object)(object)sourceRefs == (Object)null) && !((Object)(object)targetRefs == (Object)null) && !((Object)(object)sourceRefs.sashRenderer == (Object)null) && !((Object)(object)targetRefs.sashRenderer == (Object)null) && sourceRefs.sashRenderer.sharedMaterials != null && sourceRefs.sashRenderer.sharedMaterials.Length != 0)
			{
				targetRefs.sashRenderer.sharedMaterials = sourceRefs.sashRenderer.sharedMaterials.ToArray();
				MaterialPropertyBlock val = new MaterialPropertyBlock();
				sourceRefs.sashRenderer.GetPropertyBlock(val);
				targetRefs.sashRenderer.SetPropertyBlock(val);
			}
		}

		private static void CopyPlayerAccessoryToStageFace(CustomizationRefs sourceRefs, CustomizationRefs targetRefs)
		{
			if ((Object)(object)sourceRefs == (Object)null || (Object)(object)targetRefs == (Object)null || (Object)(object)sourceRefs.accessoryRenderer == (Object)null || (Object)(object)targetRefs.accessoryRenderer == (Object)null)
			{
				return;
			}
			Renderer[] array = FindStageAccessoryRenderers(((Component)targetRefs).transform);
			if (array.Length == 0)
			{
				array = (Renderer[])(object)new Renderer[1] { targetRefs.accessoryRenderer };
			}
			bool activeSelf = ((Component)targetRefs.accessoryRenderer).gameObject.activeSelf;
			bool enabled = targetRefs.accessoryRenderer.enabled;
			Renderer[] array2 = array;
			foreach (Renderer val in array2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					CopyRendererMaterialsOnly(sourceRefs.accessoryRenderer, val);
					((Component)val).gameObject.SetActive(activeSelf || enabled);
					val.enabled = enabled;
				}
			}
			if (array.Length > 1)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Synced accessory renderer to {array.Length} stage face cards under '{GetTransformPath(((Component)targetRefs).transform)}'.");
				}
			}
		}

		private static void CopyRendererMaterialsOnly(Renderer source, Renderer target)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (!((Object)(object)source == (Object)null) && !((Object)(object)target == (Object)null) && source.sharedMaterials != null && source.sharedMaterials.Length != 0)
			{
				try
				{
					target.materials = source.materials.ToArray();
				}
				catch
				{
					target.sharedMaterials = source.sharedMaterials.ToArray();
				}
				MaterialPropertyBlock val = new MaterialPropertyBlock();
				source.GetPropertyBlock(val);
				target.SetPropertyBlock(val);
			}
		}

		private static void DisableUnmappedStageCosmeticRenderers(CustomizationRefs refs)
		{
			if ((Object)(object)refs == (Object)null)
			{
				return;
			}
			HashSet<Renderer> hashSet = new HashSet<Renderer>();
			AddRenderer(hashSet, (Renderer)(object)refs.mainRenderer);
			AddRenderer(hashSet, (Renderer)(object)refs.mainRendererShadow);
			AddRenderer(hashSet, refs.mouthRenderer);
			AddRenderer(hashSet, refs.accessoryRenderer);
			AddRenderer(hashSet, refs.shorts);
			AddRenderer(hashSet, refs.skirt);
			AddRenderer(hashSet, refs.shortsShadow);
			AddRenderer(hashSet, refs.skirtShadow);
			AddRenderer(hashSet, refs.sashRenderer);
			AddRenderers(hashSet, refs.PlayerRenderers);
			AddRenderers(hashSet, refs.EyeRenderers);
			AddRenderers(hashSet, refs.playerHats);
			AddRenderers(hashSet, FindStageAccessoryRenderers(((Component)refs).transform));
			if ((Object)(object)refs.thirdEye != (Object)null)
			{
				AddRenderer(hashSet, refs.thirdEye.GetComponent<Renderer>());
			}
			int num = 0;
			Renderer[] componentsInChildren = ((Component)refs).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !hashSet.Contains(val) && !IsHatShadowRenderer(val) && IsStageCosmeticRenderer(val))
				{
					val.enabled = false;
					((Component)val).gameObject.SetActive(false);
					num++;
				}
			}
			if (num > 0)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Disabled {num} unmapped stage cosmetic renderers under '{GetTransformPath(((Component)refs).transform)}'.");
				}
			}
		}

		private static void AddRenderers(HashSet<Renderer> renderers, Renderer[] values)
		{
			if (values != null)
			{
				foreach (Renderer renderer in values)
				{
					AddRenderer(renderers, renderer);
				}
			}
		}

		private static void AddRenderer(HashSet<Renderer> renderers, Renderer renderer)
		{
			if (renderers != null && (Object)(object)renderer != (Object)null)
			{
				renderers.Add(renderer);
			}
		}

		private static bool IsStageCosmeticRenderer(Renderer renderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return false;
			}
			string normalized = NormalizeObjectName(((Object)renderer).name);
			if (normalized.StartsWith("HeadMesh", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("MouthCard", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("AccesoryCard", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("AccessoryCard", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("EyeCardL", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("EyeCardR", StringComparison.OrdinalIgnoreCase) || normalized.StartsWith("ThirdEye", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return PreferredStageHatOrder.Any((string name) => NormalizeObjectName(name).Equals(normalized, StringComparison.OrdinalIgnoreCase));
		}

		private static Renderer[] FindStageAccessoryRenderers(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return Array.Empty<Renderer>();
			}
			return (from renderer in ((Component)root).GetComponentsInChildren<Renderer>(true)
				where (Object)(object)renderer != (Object)null && IsAccessoryRendererName(((Object)renderer).name)
				select renderer).ToArray();
		}

		private static bool IsAccessoryRendererName(string name)
		{
			string text = NormalizeObjectName(name);
			if (!text.StartsWith("AccesoryCard", StringComparison.OrdinalIgnoreCase))
			{
				return text.StartsWith("AccessoryCard", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool ApplyOfficialCustomizationToStage(Character source, CustomizationRefs targetRefs)
		{
			try
			{
				if ((Object)(object)source == (Object)null || source.refs == null || (Object)(object)source.refs.customization == (Object)null || (Object)(object)targetRefs == (Object)null)
				{
					return false;
				}
				source.refs.customization.SetCustomizationForRef(targetRefs);
				TryRemoveTemporaryCustomizationSubscription(source, source.refs.customization);
				SetTargetCustomizationRenderersAlwaysUpdate(targetRefs);
				NormalizeStageCustomizationRefs(targetRefs);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Official player customization copy failed, using fallback renderer copy: " + ex.Message));
				}
				return false;
			}
		}

		private static void TryRemoveTemporaryCustomizationSubscription(Character source, CharacterCustomization customization)
		{
			try
			{
				if ((Object)(object)source == (Object)null || (Object)(object)((MonoBehaviourPun)source).photonView == (Object)null || ((MonoBehaviourPun)source).photonView.Owner == null || (Object)(object)customization == (Object)null)
				{
					return;
				}
				PersistentPlayerDataService service = GameHandler.GetService<PersistentPlayerDataService>();
				if (service == null)
				{
					return;
				}
				MethodInfo method = typeof(CharacterCustomization).GetMethod("OnPlayerDataChange", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(method == null))
				{
					Action<PersistentPlayerData> action = (Action<PersistentPlayerData>)Delegate.CreateDelegate(typeof(Action<PersistentPlayerData>), customization, method, throwOnBindFailure: false);
					if (action != null)
					{
						service.UnsubscribeToPlayerDataChange(((MonoBehaviourPun)source).photonView.Owner, action);
					}
				}
			}
			catch
			{
			}
		}

		private static CustomizationRefs GetOrCreateStageCustomizationRefs(Transform stageRoot, CustomizationRefs sourceRefs)
		{
			if ((Object)(object)stageRoot == (Object)null)
			{
				return null;
			}
			SkinnedMeshRenderer val = FindStageSkinnedRenderer(stageRoot, "MainMesh");
			Transform val2 = FindStageActorRoot(stageRoot, (Renderer)(object)val) ?? stageRoot;
			CustomizationRefs val3 = ((Component)val2).GetComponentInChildren<CustomizationRefs>(true);
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = ((Component)val2).gameObject.AddComponent<CustomizationRefs>();
			}
			Transform val4 = FindStageJoint(val2, "Head") ?? val2;
			Transform root = FindChildByName(val2, "Face") ?? val4;
			Transform val5 = FindChildByName(val4, "Hat") ?? FindChildByName(val2, "Hat") ?? val4;
			val3.mainRenderer = val ?? val3.mainRenderer ?? FindStageSkinnedRenderer(val2, "MainMesh");
			val3.mainRendererShadow = val3.mainRendererShadow ?? FindStageSkinnedRenderer(val2, "Shadows", "MainMesh Shadow", "HeadMesh Shadow");
			if ((Object)(object)val3.mainRendererShadow == (Object)null)
			{
				val3.mainRendererShadow = CreateDummySkinnedRenderer("BBNO-COS Main Shadow", val2, val3.mainRenderer);
			}
			val3.shorts = val3.shorts ?? FindStageRenderer(val2, "Shorts");
			val3.skirt = val3.skirt ?? FindStageRenderer(val2, "Skirt");
			val3.shortsShadow = val3.shortsShadow ?? FindStageRenderer(val2, "Shorts Shadow", "ShortsShadow");
			val3.skirtShadow = val3.skirtShadow ?? FindStageRenderer(val2, "Skirt Shadow", "SkirtShadow");
			if ((Object)(object)val3.shortsShadow == (Object)null)
			{
				ref Renderer shortsShadow = ref val3.shortsShadow;
				Renderer shorts = val3.shorts;
				shortsShadow = (Renderer)(object)CreateDummySkinnedRenderer("BBNO-COS Shorts Shadow", val2, (SkinnedMeshRenderer)(object)((shorts is SkinnedMeshRenderer) ? shorts : null));
			}
			if ((Object)(object)val3.skirtShadow == (Object)null)
			{
				ref Renderer skirtShadow = ref val3.skirtShadow;
				Renderer skirt = val3.skirt;
				skirtShadow = (Renderer)(object)CreateDummySkinnedRenderer("BBNO-COS Skirt Shadow", val2, (SkinnedMeshRenderer)(object)((skirt is SkinnedMeshRenderer) ? skirt : null));
			}
			val3.sashRenderer = val3.sashRenderer ?? FindStageRenderer(val2, "Sash");
			val3.chickenRenderer = val3.chickenRenderer ?? FindStageRenderer(val2, "Chicken");
			val3.skeletonRenderer = val3.skeletonRenderer ?? FindStageRenderer(val2, "Skeleton");
			Renderer val6 = FindStageRenderer(val2, "HeadMesh");
			Renderer val7 = FindStageRenderer(root, "Eye Card L");
			Renderer val8 = FindStageRenderer(root, "Eye Card R");
			Renderer val9 = FindStageRenderer(root, "Third Eye");
			val3.EyeRenderers = EnsureRendererArray(val3.EyeRenderers, BuildRendererArray(val7, val8, val9));
			val3.PlayerRenderers = EnsureRendererArray(val3.PlayerRenderers, BuildRendererArray(val6, (Renderer)val3.mainRenderer, val7, val8, val9));
			val3.mouthRenderer = val3.mouthRenderer ?? FindStageRenderer(root, "Mouth Card", "Mouth");
			val3.accessoryRenderer = val3.accessoryRenderer ?? FindStageRenderer(root, "Accesory Card", "Accessory Card", "Accessory");
			Renderer[] array = FindStageHatRenderers(val5, sourceRefs);
			val3.playerHats = ((array.Length != 0) ? array : EnsureRendererArray(val3.playerHats, Array.Empty<Renderer>()));
			val3.hatTransform = val3.hatTransform ?? val5;
			if ((Object)(object)val3.hatShadowMeshFilter == (Object)null)
			{
				val3.hatShadowMeshFilter = FindStageHatShadowMeshFilter(val5);
			}
			if ((Object)(object)val3.hatShadowMeshFilter == (Object)null)
			{
				val3.hatShadowMeshFilter = CreateDummyMeshFilter("BBNO-COS Hat Shadow", val5);
			}
			if (val3.sashAscentMaterials == null || val3.sashAscentMaterials.Length == 0)
			{
				val3.sashAscentMaterials = (((Object)(object)sourceRefs != (Object)null && sourceRefs.sashAscentMaterials != null && sourceRefs.sashAscentMaterials.Length != 0) ? sourceRefs.sashAscentMaterials : ExtractMaterials(val3.sashRenderer));
			}
			val3.thirdEye = val3.thirdEye ?? (((Object)(object)val9 == (Object)null) ? null : ((Component)val9).gameObject);
			val3.AllRenderers = (from renderer in BuildRendererArray(val6, (Renderer)val3.mainRenderer, val3.shorts, val3.skirt, val3.sashRenderer, val3.accessoryRenderer, val7, val8, val3.mouthRenderer, val9)
				where (Object)(object)renderer != (Object)null
				select renderer).ToArray();
			if (!HasRequiredStageCustomizationRefs(val3))
			{
				LogStageCustomizationRefs(val2, val3, "incomplete");
				return null;
			}
			NormalizeStageCustomizationRefs(val3);
			LogStageCustomizationRefs(val2, val3, "ready");
			if (!((Object)(object)val3.mainRenderer == (Object)null))
			{
				return val3;
			}
			return null;
		}

		private static Transform FindStageActorRoot(Transform stageRoot, Renderer mainRenderer)
		{
			if ((Object)(object)stageRoot == (Object)null || (Object)(object)mainRenderer == (Object)null)
			{
				return stageRoot;
			}
			Transform val = ((Component)mainRenderer).transform;
			Transform val2 = val;
			while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null && (Object)(object)val.parent != (Object)(object)stageRoot)
			{
				val = val.parent;
				val2 = val;
			}
			return val2 ?? stageRoot;
		}

		private static Renderer[] BuildRendererArray(params Renderer[] renderers)
		{
			if (renderers != null)
			{
				return renderers.Where((Renderer renderer) => (Object)(object)renderer != (Object)null).Distinct().ToArray();
			}
			return Array.Empty<Renderer>();
		}

		private static Renderer[] FindStageHatRenderers(Transform hatRoot, CustomizationRefs sourceRefs)
		{
			if ((Object)(object)hatRoot == (Object)null)
			{
				return Array.Empty<Renderer>();
			}
			Renderer[] array = (from renderer in ((Component)hatRoot).GetComponentsInChildren<Renderer>(true)
				where (Object)(object)renderer != (Object)null
				where !IsHatShadowRenderer(renderer)
				select renderer).ToArray();
			List<Renderer> list = new List<Renderer>();
			Renderer[] playerHats;
			if ((Object)(object)sourceRefs != (Object)null && sourceRefs.playerHats != null)
			{
				playerHats = sourceRefs.playerHats;
				foreach (Renderer val in playerHats)
				{
					AddMatchingStageHat(list, array, ((Object)(object)val == (Object)null) ? null : ((Object)val).name);
				}
			}
			string[] preferredStageHatOrder = PreferredStageHatOrder;
			foreach (string sourceHatName in preferredStageHatOrder)
			{
				AddMatchingStageHat(list, array, sourceHatName);
			}
			playerHats = array;
			foreach (Renderer item in playerHats)
			{
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)string.Format("Stage hat order for '{0}': {1}[{2}]", GetTransformPath(hatRoot), list.Count, string.Join(",", list.Take(16).Select(RendererName).ToArray())));
			}
			return list.ToArray();
		}

		private static void AddMatchingStageHat(List<Renderer> ordered, Renderer[] candidates, string sourceHatName)
		{
			if (ordered != null && candidates != null && !string.IsNullOrWhiteSpace(sourceHatName))
			{
				string wanted = NormalizeObjectName(sourceHatName);
				Renderer val = ((IEnumerable<Renderer>)candidates).FirstOrDefault((Func<Renderer, bool>)((Renderer renderer) => NormalizeObjectName(((Object)renderer).name).Equals(wanted, StringComparison.OrdinalIgnoreCase)));
				if ((Object)(object)val != (Object)null && !ordered.Contains(val))
				{
					ordered.Add(val);
				}
			}
		}

		private static MeshFilter FindStageHatShadowMeshFilter(Transform hatRoot)
		{
			if ((Object)(object)hatRoot == (Object)null)
			{
				return null;
			}
			MeshFilter[] componentsInChildren = ((Component)hatRoot).GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && IsHatShadowName(((Object)val).name))
				{
					return val;
				}
			}
			return null;
		}

		private static bool IsHatShadowRenderer(Renderer renderer)
		{
			if ((Object)(object)renderer != (Object)null)
			{
				return IsHatShadowName(((Object)renderer).name);
			}
			return false;
		}

		private static bool IsHatShadowName(string name)
		{
			string text = NormalizeObjectName(name);
			if (!text.Equals("Shadow", StringComparison.OrdinalIgnoreCase) && !text.Equals("Shadow1", StringComparison.OrdinalIgnoreCase))
			{
				return text.IndexOf("HatShadow", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static bool HasRequiredStageCustomizationRefs(CustomizationRefs refs)
		{
			if ((Object)(object)refs != (Object)null && (Object)(object)refs.mainRenderer != (Object)null && (Object)(object)refs.mainRendererShadow != (Object)null && refs.PlayerRenderers != null && refs.PlayerRenderers.Length != 0 && (Object)(object)refs.PlayerRenderers[0] != (Object)null && refs.EyeRenderers != null && refs.EyeRenderers.Length >= 2 && (Object)(object)refs.mouthRenderer != (Object)null && (Object)(object)refs.accessoryRenderer != (Object)null && (Object)(object)refs.shorts != (Object)null && (Object)(object)refs.skirt != (Object)null && (Object)(object)refs.shortsShadow != (Object)null && (Object)(object)refs.skirtShadow != (Object)null && refs.playerHats != null && refs.playerHats.Length >= 2 && (Object)(object)refs.playerHats[0] != (Object)null && (Object)(object)refs.playerHats[1] != (Object)null && (Object)(object)refs.hatShadowMeshFilter != (Object)null && (Object)(object)refs.sashRenderer != (Object)null && refs.sashAscentMaterials != null && refs.sashAscentMaterials.Length != 0 && (Object)(object)refs.thirdEye != (Object)null && refs.AllRenderers != null)
			{
				return refs.AllRenderers.Length != 0;
			}
			return false;
		}

		private static void LogStageCustomizationRefs(Transform root, CustomizationRefs refs, string state)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)("Stage customization refs " + state + " for '" + GetTransformPath(root) + "': main=" + RendererName((Renderer)(object)(((Object)(object)refs == (Object)null) ? null : refs.mainRenderer)) + ", head/player=" + RendererArraySummary(((Object)(object)refs == (Object)null) ? null : refs.PlayerRenderers) + ", eyes=" + RendererArraySummary(((Object)(object)refs == (Object)null) ? null : refs.EyeRenderers) + ", mouth=" + RendererName(((Object)(object)refs == (Object)null) ? null : refs.mouthRenderer) + ", accessory=" + RendererName(((Object)(object)refs == (Object)null) ? null : refs.accessoryRenderer) + ", shorts=" + RendererName(((Object)(object)refs == (Object)null) ? null : refs.shorts) + ", skirt=" + RendererName(((Object)(object)refs == (Object)null) ? null : refs.skirt) + ", sash=" + RendererName(((Object)(object)refs == (Object)null) ? null : refs.sashRenderer) + ", " + $"hats={((!((Object)(object)refs == (Object)null) && refs.playerHats != null) ? refs.playerHats.Length : 0)}, " + "hatShadow=" + (((Object)(object)refs == (Object)null || (Object)(object)refs.hatShadowMeshFilter == (Object)null) ? "null" : ((Object)refs.hatShadowMeshFilter).name) + ", thirdEye=" + (((Object)(object)refs == (Object)null || (Object)(object)refs.thirdEye == (Object)null) ? "null" : ((Object)refs.thirdEye).name) + "."));
			}
		}

		private static string RendererArraySummary(Renderer[] renderers)
		{
			if (renderers == null)
			{
				return "null";
			}
			return string.Format("{0}[{1}]", renderers.Length, string.Join(",", renderers.Take(5).Select(RendererName).ToArray()));
		}

		private static string RendererName(Renderer renderer)
		{
			if (!((Object)(object)renderer == (Object)null))
			{
				return ((Object)renderer).name;
			}
			return "null";
		}

		private static SkinnedMeshRenderer FindStageSkinnedRenderer(Transform root, params string[] names)
		{
			Renderer obj = FindStageRenderer(root, names);
			return (SkinnedMeshRenderer)(object)((obj is SkinnedMeshRenderer) ? obj : null);
		}

		private static Renderer FindStageRenderer(Transform root, params string[] names)
		{
			if ((Object)(object)root == (Object)null || names == null || names.Length == 0)
			{
				return null;
			}
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string text = NormalizeObjectName(((Object)val).name);
				string[] array2 = names;
				for (int j = 0; j < array2.Length; j++)
				{
					string value = NormalizeObjectName(array2[j]);
					if (text.Equals(value, StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
				}
			}
			array = componentsInChildren;
			foreach (Renderer val2 in array)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string text2 = NormalizeObjectName(((Object)val2).name);
				string[] array2 = names;
				for (int j = 0; j < array2.Length; j++)
				{
					string value2 = NormalizeObjectName(array2[j]);
					if (text2.Equals(value2, StringComparison.OrdinalIgnoreCase) || text2.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return val2;
					}
				}
			}
			return null;
		}

		private static Transform FindStageJoint(Transform root, string name)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			string value = NormalizeObjectName(name);
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && NormalizeObjectName(((Object)val).name).Equals(value, StringComparison.OrdinalIgnoreCase))
				{
					return val;
				}
			}
			return null;
		}

		private static Renderer[] EnsureRendererArray(Renderer[] current, Renderer[] fallback)
		{
			if (current != null && current.Length != 0 && current.Any((Renderer renderer) => (Object)(object)renderer != (Object)null))
			{
				return current;
			}
			if (fallback != null)
			{
				return fallback.Where((Renderer renderer) => (Object)(object)renderer != (Object)null).ToArray();
			}
			return Array.Empty<Renderer>();
		}

		private static Renderer[] CreateDummyRenderers(string baseName, Transform parent, int count)
		{
			int num = Mathf.Max(1, count);
			Renderer[] array = (Renderer[])(object)new Renderer[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = CreateDummyRenderer($"{baseName} {i + 1}", parent);
			}
			return array;
		}

		private static Renderer CreateDummyRenderer(string name, Transform parent)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			MeshFilter val2 = val.AddComponent<MeshFilter>();
			MeshRenderer obj = val.AddComponent<MeshRenderer>();
			((Renderer)obj).enabled = false;
			val2.sharedMesh = null;
			return (Renderer)(object)obj;
		}

		private static MeshFilter CreateDummyMeshFilter(string name, Transform parent)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			MeshFilter result = val.AddComponent<MeshFilter>();
			((Rende