using System.Collections; using UnityEngine; using TMPro; using UnityEngine.SceneManagement; using UnityEngine.UI; public class MainMenuManager : MonoBehaviour { public const string MainMenuSceneName = "MainMenu"; const string CryptoAddressPrefsKey = "MainMenu.CryptoAddress"; /// 500 CC fee to process a withdrawal. const int WithdrawalFeeCc = 500; /// $20 USD = 20.0 RC, stored in DB as 80 rc coins (4 coins per 1.0 RC). const int MinimumWithdrawalRcCoins = 80; [Header("Stats")] [SerializeField] private GameObject statsPanel; [SerializeField] private TMP_Text usernameText; [SerializeField] private TMP_Text ccText; [SerializeField] private TMP_Text rcText; [Header("Screens")] public GameObject mainMenuScreen; public GameObject gameModesScreen; public GameObject dummyBuyScreen; public GameObject withdrawalScreen; public GameObject settingsScreen; [Header("Play")] public Button btnPlay; public Button btnTutorial; public TMP_Text rcPlayText; public TMP_Text entryFeeTxt; public TMP_Text rcPrizeTxt; [SerializeField] string tutorialGameScene = GameTutorialManager.DefaultGameSceneName; [Header("Dummy Buy")] public Button btnBuyUsd5; public Button btnBuyUsd20; public Button btnBuyUsd50; [Header("Withdrawal")] public Button btnWithdrawal; public Button btnPaste; public TMP_InputField inputCryptoAddress; public TMP_Text txtWithdrawStatus; private bool isCoinBuyInProgress; [Header("Settings")] public ProfileInfoFetcher profileInfoFetcher; public TMP_Text txtUsername; public TMP_Text txtEmail; public TMP_Text txtAge,txtMatches,txtWinRatio; public TMP_Text txtId; public Button btnLogout; [Header("Audio")] public ToggleButton toggleSfx; public ToggleButton toggleMusic; /// Default play cost (coin units) if /settings is not loaded yet. const int FallbackEntryFeeCoins = 21; int _matchEntryFeeCoins = FallbackEntryFeeCoins; public static MainMenuManager instance; void Awake() { if(LoginManager.instance==null){ SceneManager.LoadScene(0); } instance = this; } void Start() { LoginManager.OnUserDataUpdated += OnUserUpdated; usernameText.text = ""; ccText.text = ""; rcText.text = ""; LoginManager.instance.GetUserData((success, error, user) => { if (success) { OnUserUpdated(user); RefreshSettingsProfile(); } else { if (btnPlay != null) btnPlay.interactable = false; if (rcPlayText != null) rcPlayText.text = string.IsNullOrEmpty(error) ? "Could not load profile. Try again." : error; } }); RefreshSettingsProfile(); btnBuyUsd5.onClick.AddListener(() => OnBuy(RcPack.Usd5)); btnBuyUsd20.onClick.AddListener(() => OnBuy(RcPack.Usd20)); btnBuyUsd50.onClick.AddListener(() => OnBuy(RcPack.Usd50)); if (inputCryptoAddress != null) { inputCryptoAddress.text = PlayerPrefs.GetString(CryptoAddressPrefsKey, string.Empty); inputCryptoAddress.onValueChanged.AddListener(OnCryptoAddressChanged); } UpdateWithdrawalStatus(); if (btnPaste != null) btnPaste.onClick.AddListener(OnPasteFromClipboard); btnPlay.onClick.AddListener(OnPlay); if (btnTutorial != null && btnTutorial != btnPlay) btnTutorial.onClick.AddListener(OnPlayTutorial); toggleSfx.onToggleValueChanged.AddListener(OnToggleSfx); toggleMusic.onToggleValueChanged.AddListener(OnToggleMusic); toggleSfx.SetIsOn(AudioManager.instance.IsSFXOn()); toggleMusic.SetIsOn(AudioManager.instance.IsMusicOn()); if (btnLogout != null) btnLogout.onClick.AddListener(OnLogout); if (!LevelLoadManager.BlocksMainMenuSettingsBootstrap) StartCoroutine(CoBootstrapSettingsWithoutLoader()); } void OnDestroy(){ LoginManager.OnUserDataUpdated -= OnUserUpdated; toggleSfx.onToggleValueChanged.RemoveListener(OnToggleSfx); toggleMusic.onToggleValueChanged.RemoveListener(OnToggleMusic); if (btnTutorial != null && btnTutorial != btnPlay) btnTutorial.onClick.RemoveListener(OnPlayTutorial); if (btnLogout != null) btnLogout.onClick.RemoveListener(OnLogout); } void OnLogout() { if (MessageBoxDialog.IsAvailable) { MessageBoxDialog.Show( "Log out?", "You will need to sign in again to play.", confirmed => { if (confirmed) ConfirmLogout(); }); return; } ConfirmLogout(); } void ConfirmLogout() { LoginManager.Logout(); SceneManager.LoadScene(0); } void OnToggleSfx(bool isOn) { AudioManager.instance.SetSfxVolume(isOn ? 1f : 0f); } void OnToggleMusic(bool isOn) { AudioManager.instance.SetMusicVolume(isOn ? 1f : 0f); } void OnPlay() { if (LoginManager.CurrentUser == null) { Debug.LogError("Play blocked: user profile not loaded."); return; } if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins) { string fee = CoinHelper.FormatString(_matchEntryFeeCoins) + " RC"; if (MessageBoxDialog.IsAvailable) { MessageBoxDialog.Show( "Not enough RC", "You need " + fee + " to play. Buy more RC and try again."); } else { Debug.LogError("Not enough RC to play"); } return; } if (CupidLobby.instance == null) { Debug.LogError("Play blocked: CupidLobby is missing from this scene."); return; } CupidLobby.instance.Matchmake(); } public void OnPlayTutorial() { if (LoginManager.CurrentUser == null) { Debug.LogError("Tutorial blocked: user profile not loaded."); return; } if (LevelLoadManager.instance == null) { Debug.LogError("Tutorial blocked: LevelLoadManager is missing."); return; } StartCoroutine(CoLaunchTutorial()); } IEnumerator CoLaunchTutorial() { MatchmadePlayer myPlayer = GameTutorialManager.CreateMyPlayerFromUser(LoginManager.CurrentUser); MatchmadePlayer cpuPlayer = GameTutorialManager.CreateCpuOpponent(); yield return GameTutorialManager.CoFetchPlayerL10(myPlayer); GameTutorialManager.PrepareLaunch(myPlayer, cpuPlayer); LevelLoadManager.LoadLevel(tutorialGameScene); } void OnCryptoAddressChanged(string value) { PlayerPrefs.SetString(CryptoAddressPrefsKey, value ?? string.Empty); PlayerPrefs.Save(); UpdateWithdrawalStatus(); } void UpdateWithdrawalStatus(UserData user = null) { if (txtWithdrawStatus == null) return; UserData u = user ?? LoginManager.CurrentUser; if (u == null) return; if (inputCryptoAddress != null && string.IsNullOrWhiteSpace(inputCryptoAddress.text)) { txtWithdrawStatus.text = "Enter a valid wallet address to withdraw safely."; return; } if (u.rc < MinimumWithdrawalRcCoins) { txtWithdrawStatus.text = "Your RC balance is below the minimum withdrawal amount ($20 USD / 20.0 RC)."; return; } if (u.cc < WithdrawalFeeCc) { txtWithdrawStatus.text = "Withdrawal costs 500 CC. Your CC balance is below 500 CC."; return; } txtWithdrawStatus.text = string.Empty; } void OnPasteFromClipboard() { if (inputCryptoAddress == null) return; string clip = GUIUtility.systemCopyBuffer; if (!string.IsNullOrEmpty(clip)) inputCryptoAddress.text = clip; } void OnBuy(string packId){ if (isCoinBuyInProgress) return; isCoinBuyInProgress = true; SetCoinBuyButtonsInteractable(false); LoginManager.instance.PurchaseRcPack(packId, (success, error) => { if(success){ OnUserUpdated(LoginManager.CurrentUser); AudioManager.instance.PlayCoinsSfx(); } isCoinBuyInProgress = false; SetCoinBuyButtonsInteractable(true); }); } void SetCoinBuyButtonsInteractable(bool interactable) { if (btnBuyUsd5 != null) btnBuyUsd5.interactable = interactable; if (btnBuyUsd20 != null) btnBuyUsd20.interactable = interactable; if (btnBuyUsd50 != null) btnBuyUsd50.interactable = interactable; } // Update is called once per frame void Update() { } void OnUserUpdated(UserData user){ usernameText.text = user.username; ccText.text = user.cc.ToString("N0") + " CC"; rcText.text = CoinHelper.FormatString(user.rc) + " RC"; if (rcPlayText != null) rcPlayText.text = ""; if (btnPlay != null) btnPlay.interactable = true; UpdateWithdrawalStatus(user); } /// Called by so the loading overlay stays until matchmaker settings are refreshed. public IEnumerator FinishLoadingScreenAndApplySettings() { yield return CoReadyMatchmakerSettingsThenApply(); } IEnumerator CoBootstrapSettingsWithoutLoader() { yield return null; yield return CoReadyMatchmakerSettingsThenApply(); } IEnumerator CoReadyMatchmakerSettingsThenApply() { const float endpointWaitSec = 30f; float deadline = Time.realtimeSinceStartup + endpointWaitSec; while (!Cupid.IsEndpointReady && Time.realtimeSinceStartup < deadline) yield return null; yield return Cupid.CoRefreshSettings(); ApplyMatchmakerFeeUi(); if (LoginManager.CurrentUser != null) OnUserUpdated(LoginManager.CurrentUser); } void ApplyMatchmakerFeeUi() { if (Cupid.Settings == null) { _matchEntryFeeCoins = FallbackEntryFeeCoins; if (entryFeeTxt != null) entryFeeTxt.text = "—"; if (rcPrizeTxt != null) rcPrizeTxt.text = "—"; return; } CupidSettings s = Cupid.Settings; _matchEntryFeeCoins = s.entry_fee > 0 ? s.entry_fee : FallbackEntryFeeCoins; if (entryFeeTxt != null) { entryFeeTxt.text = s.entry_fee > 0 ? CoinHelper.FormatString(s.entry_fee) + " RC" : "—"; } if (rcPrizeTxt != null) { rcPrizeTxt.text = s.rc_prize > 0 ? CoinHelper.FormatString(s.rc_prize) + " RC" : "—"; } } void SetStatsPanelVisible(bool visible) { if (statsPanel == null) return; statsPanel.SetActive(visible); Transform topPanel = statsPanel.transform.parent; if (topPanel != null) topPanel.gameObject.SetActive(visible); } public void ShowMainMenuScreen(){ SetStatsPanelVisible(true); mainMenuScreen.SetActive(true); gameModesScreen.SetActive(false); dummyBuyScreen.SetActive(false); withdrawalScreen.SetActive(false); settingsScreen.SetActive(false); } public void ShowGameModesScreen(){ SetStatsPanelVisible(true); mainMenuScreen.SetActive(false); gameModesScreen.SetActive(true); dummyBuyScreen.SetActive(false); withdrawalScreen.SetActive(false); settingsScreen.SetActive(false); } public void ShowDummyBuyScreen(){ SetStatsPanelVisible(false); mainMenuScreen.SetActive(false); gameModesScreen.SetActive(false); dummyBuyScreen.SetActive(true); withdrawalScreen.SetActive(false); settingsScreen.SetActive(false); } public void ShowWithdrawalScreen(){ SetStatsPanelVisible(false); mainMenuScreen.SetActive(false); gameModesScreen.SetActive(false); dummyBuyScreen.SetActive(false); withdrawalScreen.SetActive(true); settingsScreen.SetActive(false); UpdateWithdrawalStatus(); } public void ShowSettingsScreen(){ SetStatsPanelVisible(false); mainMenuScreen.SetActive(false); gameModesScreen.SetActive(false); dummyBuyScreen.SetActive(false); withdrawalScreen.SetActive(false); settingsScreen.SetActive(true); RefreshSettingsProfile(); } void RefreshSettingsProfile() { if (profileInfoFetcher == null) return; UserData user = LoginManager.CurrentUser; if (user == null) return; profileInfoFetcher.GetPlayerProfile(user.id, (success, error, profile) => { if (!success || profile == null) { Debug.LogWarning("Settings profile fetch failed: " + (error ?? "unknown")); return; } ApplySettingsProfile(profile); }); } void ApplySettingsProfile(PlayerProfileApiResponse profile) { if (txtUsername != null) txtUsername.text = profile.username ?? ""; if (txtEmail != null) txtEmail.text = profile.email ?? ""; if (txtId != null) txtId.text = profile.player_id.ToString(); if (txtAge != null) txtAge.text = profile.days_since_registration.ToString(); if (txtMatches != null) txtMatches.text = profile.matches_played.ToString(); if (txtWinRatio != null) txtWinRatio.text = (profile.win_ratio * 100f).ToString("0.#") + "%"; } }