//////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /// <summary> /// Build cell /// </summary> /// <param name="p_TableView">Table view instance</param> /// <param name="p_Index">Cell index</param> /// <returns></returns> public HMUI.TableCell CellForIdx(HMUI.TableView p_TableView, int p_Index) { LevelListTableCell l_Cell = GetTableCell(); TextMeshProUGUI l_Text = l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songNameText"); TextMeshProUGUI l_SubText = l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songAuthorText"); l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_favoritesBadgeImage").gameObject.SetActive(false); var l_HoverHint = l_Cell.gameObject.GetComponent <HMUI.HoverHint>(); if (l_HoverHint == null || !l_HoverHint) { l_HoverHint = l_Cell.gameObject.AddComponent <HMUI.HoverHint>(); l_HoverHint.SetField("_hoverHintController", UnityEngine.Resources.FindObjectsOfTypeAll <HMUI.HoverHintController>().First()); } if (l_Cell.gameObject.GetComponent <LocalizedHoverHint>()) { UnityEngine.GameObject.Destroy(l_Cell.gameObject.GetComponent <LocalizedHoverHint>()); } var l_SongEntry = Data[p_Index]; l_SongEntry.Init(); if ((l_SongEntry.BeatSaver_Map != null && !l_SongEntry.BeatSaver_Map.Partial) || l_SongEntry.CustomLevel != null) { var l_HaveSong = l_SongEntry.CustomLevel != null && SongCore.Loader.CustomLevels.ContainsKey(l_SongEntry.CustomLevel.customLevelPath); string l_MapName = ""; string l_MapAuthor = ""; string l_MapSongAuthor = ""; float l_Duration = 0f; float l_BPM = 0f; if (l_SongEntry.CustomLevel != null) { l_MapName = l_SongEntry.CustomLevel.songName; l_MapAuthor = l_SongEntry.CustomLevel.levelAuthorName; l_MapSongAuthor = l_SongEntry.CustomLevel.songAuthorName; l_BPM = l_SongEntry.CustomLevel.beatsPerMinute; l_Duration = l_SongEntry.CustomLevel.songDuration; } else { var l_FirstDiff = l_SongEntry.BeatSaver_Map.Metadata.Characteristics.First().Difficulties.Where(x => x.Value.HasValue).LastOrDefault(); l_MapName = l_SongEntry.BeatSaver_Map.Name; l_MapAuthor = l_SongEntry.BeatSaver_Map.Metadata.LevelAuthorName; l_MapSongAuthor = l_SongEntry.BeatSaver_Map.Metadata.SongAuthorName; l_BPM = l_SongEntry.BeatSaver_Map.Metadata.BPM; l_Duration = l_FirstDiff.Value.Value.Length; } string l_Title = l_SongEntry.TitlePrefix + (l_SongEntry.TitlePrefix.Length != 0 ? " " : "") + (l_HaveSong ? "<#7F7F7F>" : "") + l_MapName; string l_SubTitle = l_MapAuthor + " [" + l_MapSongAuthor + "]"; if (l_Title.Length > (28 + (l_HaveSong ? "<#7F7F7F>".Length : 0))) { l_Title = l_Title.Substring(0, 28 + (l_HaveSong ? "<#7F7F7F>".Length : 0)) + "..."; } if (l_SubTitle.Length > 28) { l_SubTitle = l_SubTitle.Substring(0, 28) + "..."; } l_Text.text = l_Title; l_SubText.text = l_SubTitle; var l_BPMText = l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songBpmText"); l_BPMText.gameObject.SetActive(true); l_BPMText.text = ((int)l_BPM).ToString(); var l_DurationText = l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songDurationText"); l_DurationText.gameObject.SetActive(true); l_DurationText.text = l_Duration >= 0.0 ? $"{Math.Floor((double)l_Duration / 60):N0}:{Math.Floor((double)l_Duration % 60):00}" : "--"; l_Cell.transform.Find("BpmIcon").gameObject.SetActive(true); if (l_SongEntry.Cover != null) { l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = l_SongEntry.Cover; } else if (CoverCache.TryGetValue(l_SongEntry.GetLevelHash(), out var l_Cover)) { l_SongEntry.Cover = l_Cover; l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = l_SongEntry.Cover; OnCoverFetched?.Invoke(l_Cell.idx, l_SongEntry); } else { l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = m_DefaultCover; if (l_HaveSong) { var l_CoverTask = l_SongEntry.CustomLevel.GetCoverImageAsync(CancellationToken.None); _ = l_CoverTask.ContinueWith(p_CoverTaskResult => { if (l_Cell.idx >= Data.Count || l_SongEntry != Data[l_Cell.idx]) { return; } Unity.MainThreadInvoker.Enqueue(() => { /// Update infos l_SongEntry.Cover = p_CoverTaskResult.Result; /// Cache cover if (!CoverCache.ContainsKey(l_SongEntry.GetLevelHash())) { CoverCache.Add(l_SongEntry.GetLevelHash(), l_SongEntry.Cover); } if (l_Cell.idx < Data.Count && l_SongEntry == Data[l_Cell.idx]) { l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = l_SongEntry.Cover; l_Cell.RefreshVisuals(); OnCoverFetched?.Invoke(l_Cell.idx, l_SongEntry); } }); }); } else if (l_SongEntry.BeatSaver_Map != null) { /// Fetch cover var l_CoverTask = l_SongEntry.BeatSaver_Map.FetchCoverImage(); _ = l_CoverTask.ContinueWith(p_CoverTaskResult => { if (l_Cell.idx >= Data.Count || l_SongEntry != Data[l_Cell.idx]) { return; } Unity.MainThreadInvoker.Enqueue(() => { var l_Texture = Unity.Texture2D.CreateFromRaw(p_CoverTaskResult.Result); if (l_Texture != null) { l_SongEntry.Cover = UnityEngine.Sprite.Create(l_Texture, new UnityEngine.Rect(0, 0, l_Texture.width, l_Texture.height), new UnityEngine.Vector2(0.5f, 0.5f), 100); /// Cache cover if (!CoverCache.ContainsKey(l_SongEntry.GetLevelHash())) { CoverCache.Add(l_SongEntry.GetLevelHash(), l_SongEntry.Cover); } if (l_Cell.idx < Data.Count && l_SongEntry == Data[l_Cell.idx]) { l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = l_SongEntry.Cover; l_Cell.RefreshVisuals(); OnCoverFetched?.Invoke(l_Cell.idx, l_SongEntry); } } }); }); } } } else if (l_SongEntry.BeatSaver_Map != null && l_SongEntry.BeatSaver_Map.Partial) { l_Text.text = "Loading from BeatSaver..."; l_SubText.text = ""; l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songBpmText").gameObject.SetActive(false); l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songDurationText").gameObject.SetActive(false); l_Cell.transform.Find("BpmIcon").gameObject.SetActive(false); l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = m_DefaultCover; } else { l_Text.text = "<#FF0000>Invalid song"; l_SubText.text = l_SongEntry.CustomLevel != null?l_SongEntry.CustomLevel.levelID.Replace("custom_level_", "") : ""; l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songBpmText").gameObject.SetActive(false); l_Cell.GetField <TextMeshProUGUI, LevelListTableCell>("_songDurationText").gameObject.SetActive(false); l_Cell.transform.Find("BpmIcon").gameObject.SetActive(false); l_Cell.GetField <UnityEngine.UI.Image, LevelListTableCell>("_coverImage").sprite = m_DefaultCover; } if (!string.IsNullOrEmpty(l_SongEntry.HoverHint)) { var l_HoverHintText = l_SongEntry.HoverHint; if (l_SongEntry.HoverHintTimeArg.HasValue && l_HoverHintText.Contains("$$time$$")) { var l_Replace = ""; var l_Elapsed = Misc.Time.UnixTimeNow() - Misc.Time.ToUnixTime(l_SongEntry.HoverHintTimeArg.Value); if (l_Elapsed < (60 * 60)) { l_Replace = Math.Max(1, l_Elapsed / 60).ToString() + " minute(s) ago"; } else if (l_Elapsed < (60 * 60 * 24)) { l_Replace = Math.Max(1, l_Elapsed / (60 * 60)).ToString() + " hour(s) ago"; } else { l_Replace = Math.Max(1, l_Elapsed / (60 * 60 * 24)).ToString() + " day(s) ago"; } l_HoverHintText = l_HoverHintText.Replace("$$time$$", l_Replace); } l_HoverHint.enabled = true; l_HoverHint.text = l_HoverHintText; } else { l_HoverHint.enabled = false; } return(l_Cell); }