using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; namespace BulletHellTemplate { public class UIRankingMenu : MonoBehaviour { [Header("Prefabs and Containers")] [Tooltip("Prefab for each ranking entry.")] public RankingEntry rankingEntryPrefab; [Tooltip("Container for ranking entries.")] public Transform container; [Tooltip("Container for displaying the favorite character.")] public Transform characterFavouriteContainer; [Header("Selected Player UI Elements")] [Tooltip("Text displaying the selected player's nickname.")] public TextMeshProUGUI selectedPlayerNickname; [Tooltip("Text displaying the selected player's rank.")] public TextMeshProUGUI selectedPlayerRank; [Tooltip("Image displaying the selected player's icon.")] public Image selectedPlayerIcon; [Tooltip("Image displaying the selected player's frame.")] public Image selectedPlayerFrame; [Header("Other UI Elements")] [Tooltip("Text displaying the current player's rank.")] public TextMeshProUGUI myRank; public TextMeshProUGUI localPlayerName; public Image localPlayerIcon; public Image localPlayerFrame; [Tooltip("Button to refresh the rankings.")] public Button refreshButton; [Tooltip("Image used to display cooldown on the refresh button.")] public Image cooldownImage; [Header("UI Translates Messages")] public string yourRank = "Your Rank: {0}"; public NameTranslatedByLanguage[] yourRankTranslated; public string rankNotAvaliable = "Rank not available"; public NameTranslatedByLanguage[] rankNotAvaliableTranslated; public string rank = "Rank:"; public NameTranslatedByLanguage[] rankTranslated; [Header("Events")] [Tooltip("Event invoked when the menu is opened.")] public UnityEvent OnOpenMenu; [Tooltip("Event invoked when the menu is closed.")] public UnityEvent OnCloseMenu; public static UIRankingMenu Singleton; private bool isCooldownActive = false; private float cooldownTime = 5f; private string currentLang; private int localRanking; /// /// List of all instantiated ranking entries. /// private List rankingEntries = new List(); private void Awake() { if (Singleton == null) { Singleton = this; } else { Destroy(gameObject); } LoadRankings(); } /// /// Called when the object becomes enabled and active. /// Selects the first ranking entry once everything is loaded. /// private void OnEnable() { OnOpenMenu.Invoke(); currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage(); if (rankingEntries.Count > 0) { OnRankingEntrySelected(rankingEntries[0]); } LoadLocalPlayerInfo(); } private void OnDisable() { OnCloseMenu.Invoke(); } /// /// Resets the cooldown for the refresh button. /// public void ResetCooldown() { StartCoroutine(StartCooldown()); } /// /// Loads the rankings and displays the top players, sorted by score in descending order. /// public async void LoadRankings() { if (isCooldownActive) return; StartCoroutine(StartCooldown()); List> topPlayers = await BackendManager.Singleton.GetTopPlayersAsync(); // Sort players by score in descending order var sortedPlayers = topPlayers .Select(playerData => { string scoreStr = playerData.ContainsKey("score") ? playerData["score"].ToString() : "0"; long.TryParse(scoreStr, out long score); // Convert score to long string playerName = playerData.ContainsKey("PlayerName") ? playerData["PlayerName"].ToString() : "Unknown"; return new { PlayerData = playerData, Score = score, PlayerName = playerName }; }) .OrderByDescending(p => p.Score) // Sort by score descending .ToList(); // Clear existing entries foreach (Transform child in container) { Destroy(child.gameObject); } rankingEntries.Clear(); // Populate the ranking list for (int i = 0; i < sortedPlayers.Count; i++) { var playerInfo = sortedPlayers[i]; Dictionary playerData = playerInfo.PlayerData; RankingEntry entry = Instantiate(rankingEntryPrefab, container); // Retrieve player data with error handling for missing information string playerName = playerInfo.PlayerName; string score = playerInfo.Score.ToString(); string playerIconId = playerData.ContainsKey("PlayerIcon") ? playerData["PlayerIcon"].ToString() : "defaultIconId"; string playerFrameId = playerData.ContainsKey("PlayerFrame") ? playerData["PlayerFrame"].ToString() : "defaultFrameId"; // Convert PlayerCharacterFavourite to int int playerCharacterFavourite = playerData.ContainsKey("PlayerCharacterFavourite") ? int.Parse(playerData["PlayerCharacterFavourite"].ToString()) : 0; // Set ranking information entry.SetRankingInfo( _playerName: playerName, _score: score, rankingPosition: i + 1, playerIconId: GetPlayerIcon(playerIconId), playerFrameId: GetPlayerFrame(playerFrameId), playerCharacterFavourite: playerCharacterFavourite ); // Add entry to the list rankingEntries.Add(entry); } // Get and display the current player's rank localRanking = await BackendManager.Singleton.GetPlayerRankAsync(); if (rankingEntries.Count > 0) { OnRankingEntrySelected(rankingEntries[0]); } } public void LoadLocalPlayerInfo() { string _playerName = PlayerSave.GetPlayerName(); string _playerIcon = PlayerSave.GetPlayerIcon(); string _playerFrame = PlayerSave.GetPlayerFrame(); localPlayerName.text = _playerName; localPlayerIcon.sprite = GetPlayerIcon(_playerIcon); localPlayerFrame.sprite = GetPlayerFrame(_playerFrame); string _yourRankTranslated = GetTranslatedString(yourRankTranslated, yourRank, currentLang); string _rankNotEvaliableTranslated = GetTranslatedString(rankNotAvaliableTranslated, rankNotAvaliable, currentLang); string formattedYourRank = string.Format(_yourRankTranslated, rank); myRank.text = localRanking > 0 ? formattedYourRank : _rankNotEvaliableTranslated; } /// /// Handles the selection of a ranking entry. /// Activates the selected image and updates selected player variables. /// /// The ranking entry that was selected. public void OnRankingEntrySelected(RankingEntry selectedEntry) { // Deactivate 'selected' image on all entries foreach (var entry in rankingEntries) { entry.SetSelected(false); } // Activate 'selected' image on the selected entry selectedEntry.SetSelected(true); // Update selected player variables selectedPlayerNickname.text = selectedEntry.playerName.text; string stringRankTranslated = GetTranslatedString(rankTranslated, rank, currentLang); selectedPlayerRank.text = $"{stringRankTranslated} {selectedEntry.ranking.text}"; selectedPlayerIcon.sprite = selectedEntry.playerIcon.sprite; selectedPlayerFrame.sprite = selectedEntry.playerFrame.sprite; // Display the favorite character DisplayFavouriteCharacter(selectedEntry.characterFavourite); } /// /// Instantiates the favorite character in the specified container. /// /// The ID of the character to instantiate. public void DisplayFavouriteCharacter(int characterId) { // Clear existing favorite character display foreach (Transform child in characterFavouriteContainer) { Destroy(child.gameObject); } // Find the character prefab by ID and instantiate it CharacterData characterPrefab = null; foreach (var character in GameInstance.Singleton.characterData) { if (character.characterId == characterId) { characterPrefab = character; break; } } if (characterPrefab != null) { Instantiate(characterPrefab.characterModel, characterFavouriteContainer); } else { Debug.LogWarning("Character with ID " + characterId + " not found."); } if (DragRotateAndHit.Singleton != null) { DragRotateAndHit.Singleton.UpdateCharacter(); } } public Sprite GetPlayerIcon(string iconId) { foreach (var icon in GameInstance.Singleton.iconItems) { if (icon.iconId == iconId) { return icon.icon; ; } } return null; } public Sprite GetPlayerFrame(string frameId) { foreach (var frame in GameInstance.Singleton.frameItems) { if (frame.frameId == frameId) { return frame.icon; ; } } return null; } private string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string currentLang) { if (translations != null) { foreach (var trans in translations) { if (!string.IsNullOrEmpty(trans.LanguageId) && trans.LanguageId.Equals(currentLang) && !string.IsNullOrEmpty(trans.Translate)) { return trans.Translate; } } } return fallback; } /// /// Starts the cooldown for the refresh button and updates the UI. /// /// private IEnumerator StartCooldown() { isCooldownActive = true; refreshButton.interactable = false; float elapsedTime = 0f; while (elapsedTime < cooldownTime) { elapsedTime += Time.deltaTime; cooldownImage.fillAmount = 1 - (elapsedTime / cooldownTime); yield return null; } cooldownImage.fillAmount = 0f; refreshButton.interactable = true; isCooldownActive = false; } } }