Decompiled source of BBNO Concert COS v1.0.9
BepInEx\plugins\BBNO-COS\BBNO-COS.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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