using System; using TMPro; using UnityEngine; using UnityEngine.UI; using static BulletHellTemplate.PlayerSave; namespace BulletHellTemplate { public class UIMainMenu : MonoBehaviour { [Header("UI Account")] public TextMeshProUGUI playerName; public Image playerIcon; public Image playerFrame; public TextMeshProUGUI accountLevel; public TextMeshProUGUI accountcurrentExp; public Image accountLevelProgressBar; public Transform characterContainer; public static UIMainMenu Singleton; private int selectedCharacter; private CharacterData characterData; [Header("Battle Pass")] public TextMeshProUGUI battlePassLevel; public TextMeshProUGUI battlePassCurrentExp; public Image battlePassProgressBar; public GameObject hasRewardToClaim; [Header("Character")] public TextMeshProUGUI characterLevel; public TextMeshProUGUI characterCurrentExp; public Image characterProgressLevel; public TextMeshProUGUI masteryLevelName; public Image masteryIcon; [Header("Daily Rewards")] public TextMeshProUGUI nextDailyReward; public GameObject canClaimDailyRewardObj; public GameObject canClaimNewPlayerRewardObj; private string currentLang; private DailyRewardsData localDailyData; private NewPlayerRewardsData localNewPlayerData; private bool canClaimDaily; // cached flags private bool canClaimNewPlayer; private int currentDayIndex; private bool allRewardsCollected; private DateTime nextResetTime; /// /// Awake is called when the script Singleton is being loaded. /// It initializes the Singleton. /// private void Awake() { Singleton = this; currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage(); LanguageManager.LanguageManager.onLanguageChanged += UpdateCharacter; } /// /// Refreshes reward flags every time. /// private void OnEnable() { localDailyData = PlayerSave.GetDailyRewardsLocal(); localNewPlayerData = PlayerSave.GetNewPlayerRewardsLocal(); if (localDailyData.firstClaimDate != DateTime.MinValue) currentDayIndex = (int)(DateTime.Now.Date - localDailyData.firstClaimDate.Date).TotalDays; else currentDayIndex = 0; nextResetTime = PlayerSave.GetNextDailyReset(); if (nextResetTime == DateTime.MinValue || nextResetTime <= DateTime.Now) { nextResetTime = DateTime.Now.Date.AddDays(1); PlayerSave.SetNextDailyReset(nextResetTime); } EvaluateRewardFlags(); UpdateDailyRewardUI(); } public void OnDestroy() { Singleton = null; LanguageManager.LanguageManager.onLanguageChanged -= UpdateCharacter; } /// /// Start is called before the first frame update. /// private void Start() { BackendManager.SetBackendInitialized(); LoadPlayerInfo(); canClaimDailyRewardObj.SetActive(false); canClaimNewPlayerRewardObj.SetActive(false); // Load local daily data once localDailyData = PlayerSave.GetDailyRewardsLocal(); // Calculate current day index based on firstClaimDate DateTime firstDate = localDailyData.firstClaimDate.Date; currentDayIndex = (int)(DateTime.Now.Date - firstDate).TotalDays; // Get the next daily reset time nextResetTime = PlayerSave.GetNextDailyReset(); } private void Update() { if (Time.frameCount % 30 == 0) EvaluateRewardFlags(); UpdateDailyRewardUI(); } /// /// Loads player information from PlayerSave and updates the UI elements. /// Shows "MAX" if the account level is at maximum. /// public void LoadPlayerInfo() { // Get player name string name = PlayerSave.GetPlayerName(); playerName.text = !string.IsNullOrEmpty(name) ? name : "Unknown Player"; // Account level and EXP int _accountLevel = PlayerSave.GetAccountLevel(); int _currentAccountExp = PlayerSave.GetAccountCurrentExp(); int _expToNextLevel = GameInstance.Singleton.GetAccountExpForLevel(_accountLevel); accountLevel.text = _accountLevel.ToString(); // Check if the account is at max level int maxAccountLevel = GameInstance.Singleton.accountLevels.accountMaxLevel; if (_accountLevel >= maxAccountLevel) { accountcurrentExp.text = "MAX"; accountLevelProgressBar.fillAmount = 1f; } else { accountcurrentExp.text = $"{_currentAccountExp}/{_expToNextLevel}"; accountLevelProgressBar.fillAmount = _expToNextLevel > 0 ? (float)_currentAccountExp / _expToNextLevel : 0f; } // Assign player icon string iconId = PlayerSave.GetPlayerIcon(); foreach (IconItem item in GameInstance.Singleton.iconItems) { if (item.iconId == iconId) { playerIcon.sprite = item.icon; break; } } // Assign player frame string frameId = PlayerSave.GetPlayerFrame(); foreach (FrameItem item in GameInstance.Singleton.frameItems) { if (item.frameId == frameId) { playerFrame.sprite = item.icon; break; } } // Battle Pass info int currentPassLevel = BattlePassManager.Singleton.currentLevel; int currentPassXP = BattlePassManager.Singleton.currentXP; int xpForNextPassLevel = BattlePassManager.Singleton.xpForNextLevel; battlePassLevel.text = currentPassLevel.ToString(); battlePassCurrentExp.text = currentPassXP + "/" + xpForNextPassLevel; battlePassProgressBar.fillAmount = (float)currentPassXP / xpForNextPassLevel; // Finally, load the selected character info LoadCharacterInfo(); } /// /// Updates only the battle pass elements. /// public void UpdateBattlePassInfo() { int currentPassLevel = BattlePassManager.Singleton.currentLevel; int currentPassXP = BattlePassManager.Singleton.currentXP; int xpForNextPassLevel = BattlePassManager.Singleton.xpForNextLevel; battlePassLevel.text = currentPassLevel.ToString(); battlePassCurrentExp.text = currentPassXP + "/" + xpForNextPassLevel; battlePassProgressBar.fillAmount = (float)currentPassXP / xpForNextPassLevel; } /// /// Loads character information and updates the character model. /// public void LoadCharacterInfo() { selectedCharacter = PlayerSave.GetSelectedCharacter(); UpdateCharacter(); } /// /// Updates the displayed character model based on the selected character and applied skin. /// Shows "MAX" if the character is at max level. /// public void UpdateCharacter() { currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage(); // Clear previous character model if (characterContainer != null) { for (int i = characterContainer.childCount - 1; i >= 0; i--) { Transform child = characterContainer.GetChild(i); if (child != null) Destroy(child.gameObject); } } // Find the character data for the selected character characterData = null; foreach (CharacterData character in GameInstance.Singleton.characterData) { if (character.characterId == selectedCharacter) { characterData = character; break; } } if (characterContainer == null || characterData == null) { return; } // Retrieve the skin index from PlayerSave int skinIndex = PlayerSave.GetCharacterSkin(selectedCharacter); // Check if a valid skin is applied if (characterData.characterSkins != null && characterData.characterSkins.Length > 0 && skinIndex >= 0 && skinIndex < characterData.characterSkins.Length) { CharacterSkin skin = characterData.characterSkins[skinIndex]; if (skin.skinCharacterModel != null) { Instantiate(skin.skinCharacterModel, characterContainer); } else if (characterData.characterModel != null) { Instantiate(characterData.characterModel, characterContainer); } } else if (characterData.characterModel != null) { Instantiate(characterData.characterModel, characterContainer); } // Optional: update rotation or other logic if (DragRotateAndHit.Singleton != null) { DragRotateAndHit.Singleton.UpdateCharacter(); } // Get character level and current exp int _chracterLevel = PlayerSave.GetCharacterLevel(selectedCharacter); int _characterCurrentExp = PlayerSave.GetCharacterCurrentExp(selectedCharacter); // Calculate required exp safely int requiredExp = 0; if (characterData.expPerLevel != null && _chracterLevel < characterData.expPerLevel.Length) { requiredExp = characterData.expPerLevel[_chracterLevel]; } // Check if character is at max level if (_chracterLevel >= characterData.maxLevel) { // Display MAX characterLevel.text = _chracterLevel.ToString(); characterCurrentExp.text = "MAX"; characterProgressLevel.fillAmount = 1f; } else { characterLevel.text = _chracterLevel.ToString(); characterCurrentExp.text = $"{_characterCurrentExp}/{requiredExp}"; characterProgressLevel.fillAmount = requiredExp > 0 ? (float)_characterCurrentExp / requiredExp : 0f; } // Mastery level name and icon int currentMasteryLevel = PlayerSave.GetCharacterMasteryLevel(selectedCharacter); CharacterMasteryLevel masteryInfo = GameInstance.Singleton.GetMasteryLevel(currentMasteryLevel); string masteryTranslatedName = GetTranslatedString( masteryInfo.masteryNameTranslated, masteryInfo.masteryName, currentLang); masteryLevelName.text = masteryTranslatedName; if (masteryIcon != null) masteryIcon.sprite = masteryInfo.masteryIcon; } /// /// Changes the selected character and updates the UI accordingly. /// /// The ID of the new selected character. public void ChangeSelectedCharacter(int characterId) { LoadCharacterInfo(); } private void EvaluateRewardFlags() { /* DAILY */ bool claimedToday = localDailyData.lastClaimDate != DateTime.MinValue && localDailyData.lastClaimDate.Date == DateTime.Now.Date; bool notClaimedCurrentDay = !localDailyData.claimedRewards.Contains(currentDayIndex); bool resetReached = (nextResetTime - DateTime.Now).TotalSeconds <= 0; canClaimDaily = (!claimedToday && notClaimedCurrentDay) || resetReached; /* NEW‑PLAYER */ int expectedIdx = localNewPlayerData.claimedRewards.Count; int daysSinceJoin = localNewPlayerData.accountCreationDate != DateTime.MinValue ? (int)(DateTime.Now.Date - localNewPlayerData.accountCreationDate.Date).TotalDays : int.MaxValue; bool claimedNPtoday = localNewPlayerData.lastClaimDate != DateTime.MinValue && localNewPlayerData.lastClaimDate.Date == DateTime.Now.Date; canClaimNewPlayer = !claimedNPtoday && expectedIdx <= daysSinceJoin; } public void UpdateDailyRewardUI() { // Toggle buttons canClaimDailyRewardObj.SetActive(canClaimDaily); canClaimNewPlayerRewardObj.SetActive(canClaimNewPlayer); if (canClaimDaily) { nextDailyReward.text = "Daily reward ready!"; return; } TimeSpan remaining = nextResetTime - DateTime.Now; if (remaining.TotalSeconds < 0) remaining = TimeSpan.Zero; nextDailyReward.text = string.Format( "{0:D2}:{1:D2}:{2:D2}", remaining.Hours, remaining.Minutes, remaining.Seconds ); } 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; } public async void TestAddAccountExp() { var result = await BackendManager.Service.TestAddAccountExpAsync(200); if (result.Success) { LoadPlayerInfo(); return; } Debug.LogError("Failed to add account exp"); } } }