diff --git a/MiraAPI/Patches/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index 8efe152..f6baacb 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -53,6 +53,7 @@ public static class RoleGuidePatches private static bool _modifiersTabDirty; private static IEnumerator? _panelCreation; private static bool _panelsReady; + private static LoadingRing? _loadingSpinner; private static SortingFilter _sortFilter = SortingFilter.EnabledOnly; private static SortingMethod _sortMethod = SortingMethod.Alphabetical; private static SortingGroups _sortGrouping = SortingGroups.Ungrouped; @@ -220,6 +221,12 @@ public static class RoleGuidePatches .First(x => x.name.Contains("BG_Gradient")).transform.localPosition = new Vector3(0, -0.62f, -5); _advancedInfoTabScroller = wikiTab.GetComponent(); + var spinnerParent = __instance.settingsTabs[RolesTabIndex].transform.parent; + var spinnerPos = spinnerParent.InverseTransformPoint(__instance.MatchInfoRoleMaskArea.transform.position); + spinnerPos.z = -5f; + _loadingSpinner = LoadingRing.Create(spinnerParent, spinnerPos, 0.6f); + _loadingSpinner.GetComponent().color = MiraAssets.AcceptedTeal; + DisplayNormalRoleSettings(__instance, true); var searchIconButton = Object.Instantiate( @@ -456,6 +463,12 @@ public static class RoleGuidePatches (!isDisabled && _sortFilter is SortingFilter.DisabledOnly)); } + private static void UpdateSpinner() + { + _loadingSpinner?.gameObject.SetActive( + !_panelsReady && MatchInfoGuide.Instance.activeTabIndex is RolesTabIndex or ModifiersTabIndex); + } + private static float GetGridScrollBounds(int count) { return Mathf.Clamp(Mathf.Ceil(count / 2f) * 1.3f - 1.5f, 0f, 999f); @@ -638,6 +651,7 @@ public static class RoleGuidePatches GameManager.Instance.LogicOptions.GetTaskBarMode().ToString()); _panelsReady = false; + instance.rolesEnabledMessage.SetActive(false); if (_panelCreation != null) { Coroutines.Stop(_panelCreation); @@ -647,6 +661,7 @@ public static class RoleGuidePatches } _advancedInfoTabScroller?.gameObject.SetActive(false); + UpdateSpinner(); if (!_panelsReady) { @@ -728,6 +743,7 @@ public static class RoleGuidePatches _panelsReady = true; _panelCreation = null; RefreshActiveTab(false); + UpdateSpinner(); instance.CreatePlayerEntries(); } @@ -911,12 +927,14 @@ public static class RoleGuidePatches } _advancedInfoTabScroller?.gameObject.SetActive(false); + UpdateSpinner(); } private static bool TryPrepareAdvancedWiki(MatchInfoGuide instance) { instance.settingsTabs[RolesTabIndex].SetActive(false); _modifiersScroller?.gameObject.SetActive(false); + UpdateSpinner(); if (_currentAdvancedTabObject) { _currentAdvancedTabObject.SetActive(false); diff --git a/MiraAPI/Utilities/LoadingRing.cs b/MiraAPI/Utilities/LoadingRing.cs new file mode 100644 index 0000000..34ce27f --- /dev/null +++ b/MiraAPI/Utilities/LoadingRing.cs @@ -0,0 +1,81 @@ +using System; +using Reactor.Utilities.Attributes; +using UnityEngine; + +namespace MiraAPI.Utilities; + +/// +/// A spinning ring used to indicate that content is loading. +/// +[RegisterInIl2Cpp] +public class LoadingRing(IntPtr cppPtr) : MonoBehaviour(cppPtr) +{ + private static Sprite? _ringSprite; + + /// + /// Gets or sets the rotation speed in degrees per second. + /// + public float Speed { get; set; } = 270f; + + /// + /// Creates a on the UI layer. + /// + /// The transform to parent the spinner to. + /// The local position of the spinner. + /// The uniform scale of the spinner. + /// The created . + public static LoadingRing Create(Transform parent, Vector3 localPosition, float scale) + { + _ringSprite ??= CreateRingSprite(); + var obj = new GameObject("LoadingRing") + { + transform = + { + parent = parent, + localPosition = localPosition, + localScale = Vector3.one * scale, + }, + layer = LayerMask.NameToLayer("UI"), + }; + var renderer = obj.AddComponent(); + renderer.sprite = _ringSprite; + return obj.AddComponent(); + } + + /// + /// Rotates the ring. + /// + public void Update() + { + transform.Rotate(0f, 0f, -Speed * Time.deltaTime); + } + + private static Sprite CreateRingSprite() + { + const int size = 128; + const float innerRadius = 46f; + const float outerRadius = 60f; + var texture = new Texture2D(size, size, TextureFormat.RGBA32, false) + { + filterMode = FilterMode.Bilinear, + wrapMode = TextureWrapMode.Clamp, + }; + var pixels = new Color[size * size]; + for (var y = 0; y < size; y++) + { + for (var x = 0; x < size; x++) + { + var dx = x + 0.5f - (size / 2f); + var dy = y + 0.5f - (size / 2f); + var radius = Mathf.Sqrt((dx * dx) + (dy * dy)); + var ring = Mathf.Clamp01(radius - innerRadius) * Mathf.Clamp01(outerRadius - radius); + var angle = (Mathf.Atan2(dy, dx) + Mathf.PI) / (2f * Mathf.PI); + pixels[(y * size) + x] = new Color(1f, 1f, 1f, ring * Mathf.Lerp(1f, 0.1f, angle)); + } + } + + texture.SetPixels(pixels); + texture.Apply(); + return Sprite.Create(texture, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f), 100f); + } +}