using System; using System.Collections; using System.Collections.Generic; using Steamworks; using UnityEngine; using UnityEngine.UI; public class UI_Player : MonoBehaviour { private const string FirstLaunchDatePrefKey = "player.first_launch_date"; private const string SongResourcesPath = "song_songIndex"; private const string VersionLabel = "\u5185\u90e8\u6d4b\u8bd5v207b35"; private const string ChannelLabel = "Steam"; private const float MaxDisplayedRksForSlider = 100f; private static readonly Color SteamOnlineColor = new Color32(20, 120, 45, 255); [Header("prefab")] public GameObject pInfoPrefab; public Transform pInfoParent; [Header("so")] [SerializeField] private Player_SO pSO; [Header("uData")] public Image userProfileImage; public Text userNameText; public Text userIDText; public Text user_registDate; [Header("uSource")] public Text userSource; [Header("uRecord")] public Text fcAmount; public Text firstFCsong; public Text totalPlayTime; [Header("uRks")] public Slider uRksSlider; public Text uRksText; [Header("buttons")] public Button quitButton; private readonly List spawnedInfoEntries = new List(); private Color defaultInfoColor = Color.black; private void Start() { BindQuitButton(); PlayerRksService.OnRksChanged += HandleRksChanged; StartCoroutine(InitializeAsync()); } private void OnEnable() { RefreshRksUi(); } private void OnDestroy() { PlayerRksService.OnRksChanged -= HandleRksChanged; if (quitButton != null) { quitButton.onClick.RemoveListener(ClosePanel); } } private void BindQuitButton() { if (quitButton == null) { return; } quitButton.onClick.RemoveListener(ClosePanel); quitButton.onClick.AddListener(ClosePanel); } private void ClosePanel() { gameObject.SetActive(false); } private IEnumerator InitializeAsync() { StoreOwnershipLedger.EnsureInstance().InitializeIfNeeded(); StoreOwnershipLedger.EnsureInstance().ForceSyncMirrorFlags(); PlayerRksService.EnsureLoaded(pSO); string registerDate = EnsureFirstLaunchDate(); UserInfoSnapshot snapshot = BuildUserInfoSnapshot(registerDate); ApplyLegacyTextMirrors(snapshot); RebuildInfoEntries(snapshot); RefreshRksUi(); yield return null; yield return StartCoroutine(LoadSteamProfileAsync(snapshot.isSteamOnline)); } private void HandleRksChanged(float _) { RefreshRksUi(); } private void RefreshRksUi() { float currentRks = PlayerRksService.RefreshAndPersist(pSO); if (uRksText != null) { uRksText.text = currentRks.ToString("F2"); } if (uRksSlider != null) { uRksSlider.minValue = 0f; uRksSlider.maxValue = MaxDisplayedRksForSlider; uRksSlider.value = Mathf.Clamp(currentRks, 0f, MaxDisplayedRksForSlider); } } private string EnsureFirstLaunchDate() { string playerPrefsDate = PlayerPrefs.GetString(FirstLaunchDatePrefKey, string.Empty); string finalDate = !string.IsNullOrWhiteSpace(playerPrefsDate) ? playerPrefsDate : DateTime.Now.ToString("yyyy-MM-dd"); if (playerPrefsDate != finalDate) { PlayerPrefs.SetString(FirstLaunchDatePrefKey, finalDate); PlayerPrefs.Save(); } if (pSO != null && pSO.firstLaunchDate != finalDate) { pSO.firstLaunchDate = finalDate; #if UNITY_EDITOR if (!Application.isPlaying) { UnityEditor.EditorUtility.SetDirty(pSO); } #endif } return finalDate; } private UserInfoSnapshot BuildUserInfoSnapshot(string registerDate) { SongStats songStats = CollectSongStats(); return new UserInfoSnapshot { registerDate = registerDate, ownedHeroCount = CountOwnedHeroes(), ownedSongCount = songStats.ownedSongCount, totalGameEnterCount = songStats.totalGameEnterCount, totalPlaySeconds = songStats.totalPlaySeconds, isSteamOnline = IsSteamOnline() }; } private void ApplyLegacyTextMirrors(UserInfoSnapshot snapshot) { if (user_registDate != null) { user_registDate.text = snapshot.registerDate; } if (userSource != null) { userSource.text = ChannelLabel; userSource.color = snapshot.isSteamOnline ? SteamOnlineColor : Color.black; } if (totalPlayTime != null) { totalPlayTime.text = FormatPlayTime(snapshot.totalPlaySeconds); } if (fcAmount != null && string.IsNullOrWhiteSpace(fcAmount.text)) { fcAmount.text = "-"; } if (firstFCsong != null && string.IsNullOrWhiteSpace(firstFCsong.text)) { firstFCsong.text = "-"; } } private void RebuildInfoEntries(UserInfoSnapshot snapshot) { ClearInfoEntries(); CacheDefaultInfoColor(); SpawnInfoEntry("\u6ce8\u518c\u65e5\u671f", snapshot.registerDate); SpawnInfoEntry("\u62e5\u6709\u89d2\u8272\u6570\u91cf", snapshot.ownedHeroCount.ToString()); SpawnInfoEntry("\u62e5\u6709\u6b4c\u66f2\u6570\u91cf", snapshot.ownedSongCount.ToString()); SpawnInfoEntry("\u603b\u6e38\u620f\u6b21\u6570", snapshot.totalGameEnterCount.ToString()); SpawnInfoEntry("\u6e38\u73a9\u603b\u65f6\u957f", FormatPlayTime(snapshot.totalPlaySeconds)); SpawnInfoEntry("\u6e20\u9053", ChannelLabel, snapshot.isSteamOnline ? SteamOnlineColor : defaultInfoColor); SpawnInfoEntry("\u7248\u672c", VersionLabel); } private void ClearInfoEntries() { for (int i = 0; i < spawnedInfoEntries.Count; i++) { if (spawnedInfoEntries[i] != null) { Destroy(spawnedInfoEntries[i]); } } spawnedInfoEntries.Clear(); } private void CacheDefaultInfoColor() { if (pInfoPrefab == null) { return; } pInfoPrefab sample = pInfoPrefab.GetComponent(); if (sample != null && sample.this_iDetail != null) { defaultInfoColor = sample.this_iDetail.color; } } private void SpawnInfoEntry(string title, string detail, Color? detailColor = null) { if (pInfoPrefab == null || pInfoParent == null) { return; } GameObject entryObject = Instantiate(pInfoPrefab, pInfoParent); spawnedInfoEntries.Add(entryObject); pInfoPrefab entry = entryObject.GetComponent(); if (entry == null) { return; } if (entry.this_iTitle != null) { entry.this_iTitle.text = title; } if (entry.this_iDetail != null) { entry.this_iDetail.text = detail; entry.this_iDetail.color = detailColor ?? defaultInfoColor; } } private static int CountOwnedHeroes() { AllyHero_SO[] loadedHeroes = RuntimeResourcesCache.LoadAllAllyHeroes(); HashSet uniqueHeroIds = new HashSet(); int ownedCount = 0; AllyHeroDeployLedger ledger = AllyHeroDeployLedger.EnsureInstance(); ledger.InitializeIfNeeded(); for (int i = 0; i < loadedHeroes.Length; i++) { AllyHero_SO hero = loadedHeroes[i]; if (hero == null || !uniqueHeroIds.Add(hero.ally_heroID)) { continue; } hero.ally_battleDeployCount = ledger.GetDeployCount(hero.ally_heroID); if (hero.isUnlocked) { ownedCount++; } } return ownedCount; } private static SongStats CollectSongStats() { SongData[] songs = RuntimeResourcesCache.LoadSongsFromPath(SongResourcesPath); HashSet uniqueSongIds = new HashSet(); SongStats stats = new SongStats(); for (int i = 0; i < songs.Length; i++) { SongData song = songs[i]; if (song == null || !uniqueSongIds.Add(song.songID)) { continue; } song.EnsurePersistentDataLoaded(); if (song.isUnlocked) { stats.ownedSongCount++; } stats.totalGameEnterCount += Mathf.Max(0, song.game_enterTimes); stats.totalPlaySeconds += Math.Max(0f, song.time_totalPlayingTime); } return stats; } private static string FormatPlayTime(double totalSeconds) { TimeSpan span = TimeSpan.FromSeconds(Math.Max(0d, totalSeconds)); if (span.TotalHours >= 1d) { return string.Format("{0:D2}\u5c0f\u65f6{1:D2}\u5206\u949f", (int)span.TotalHours, span.Minutes); } if (span.TotalMinutes >= 1d) { return string.Format("{0:D2}\u5206\u949f{1:D2}\u79d2", (int)span.TotalMinutes, span.Seconds); } return string.Format("{0:D2}\u79d2", Math.Max(0, span.Seconds)); } private IEnumerator LoadSteamProfileAsync(bool isSteamOnline) { int initRetry = 0; while (initRetry < 25 && !SteamManager.Initialized) { initRetry++; yield return new WaitForSecondsRealtime(0.2f); } if (!SteamManager.Initialized) { ApplySteamUnavailable(); yield break; } int retryCount = 0; while (retryCount < 20) { if (TryPopulateSteamProfile(isSteamOnline)) { yield break; } retryCount++; yield return new WaitForSecondsRealtime(0.25f); } ApplySteamUnavailable(); } private void ApplySteamUnavailable() { if (userNameText != null) { userNameText.text = "Steam Unavailable"; } if (userIDText != null) { userIDText.text = "-"; } if (userSource != null) { userSource.text = ChannelLabel; userSource.color = Color.black; } } private static bool IsSteamOnline() { if (!SteamManager.Initialized) { return false; } try { return SteamFriends.GetPersonaState() != EPersonaState.k_EPersonaStateOffline; } catch { return false; } } private bool TryPopulateSteamProfile(bool isSteamOnline) { try { CSteamID steamId = SteamUser.GetSteamID(); string personaName = SteamFriends.GetPersonaName(); if (!steamId.IsValid() || string.IsNullOrWhiteSpace(personaName)) { return false; } if (userNameText != null) { userNameText.text = personaName; } if (userIDText != null) { userIDText.text = steamId.m_SteamID.ToString(); } if (userSource != null) { userSource.text = ChannelLabel; userSource.color = isSteamOnline ? SteamOnlineColor : Color.black; } return TryApplySteamAvatar(steamId) || userProfileImage == null; } catch { return false; } } private bool TryApplySteamAvatar(CSteamID steamId) { if (userProfileImage == null) { return true; } int imageId = SteamFriends.GetLargeFriendAvatar(steamId); if (imageId == -1) { return false; } if (imageId == 0) { imageId = SteamFriends.GetMediumFriendAvatar(steamId); } if (imageId < 0) { return false; } uint width; uint height; if (!SteamUtils.GetImageSize(imageId, out width, out height) || width == 0 || height == 0) { return false; } byte[] imageBuffer = new byte[width * height * 4]; if (!SteamUtils.GetImageRGBA(imageId, imageBuffer, imageBuffer.Length)) { return false; } Texture2D sourceTexture = new Texture2D((int)width, (int)height, TextureFormat.RGBA32, false); sourceTexture.LoadRawTextureData(imageBuffer); sourceTexture.Apply(); Texture2D flippedTexture = new Texture2D((int)width, (int)height, TextureFormat.RGBA32, false); for (int y = 0; y < (int)height; y++) { Color[] rowPixels = sourceTexture.GetPixels(0, y, (int)width, 1); flippedTexture.SetPixels(0, (int)height - 1 - y, (int)width, 1, rowPixels); } flippedTexture.Apply(); userProfileImage.sprite = Sprite.Create( flippedTexture, new Rect(0, 0, flippedTexture.width, flippedTexture.height), new Vector2(0.5f, 0.5f)); return true; } private struct UserInfoSnapshot { public string registerDate; public int ownedHeroCount; public int ownedSongCount; public int totalGameEnterCount; public double totalPlaySeconds; public bool isSteamOnline; } private struct SongStats { public int ownedSongCount; public int totalGameEnterCount; public double totalPlaySeconds; } }