using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; public class loadDlcListPrefab : MonoBehaviour { private static readonly bool VerboseLogs = false; private const string RuntimeInstalledColumnName = "Installed DLC"; private const int DefaultFallbackDlcId = 66002; private const string DlcSelectedIndexPrefsKey = "dlc_selected_index"; [System.Serializable] public class Column { public string columnName; public List dlcList = new List(); } [Header("Inspector")] public List columns = new List(); [Header("Runtime DLC")] [SerializeField] bool includeRuntimeInstalledDlcs = true; [SerializeField] string runtimeInstalledColumnName = RuntimeInstalledColumnName; [Header("Inspector")] public GameObject dlc_scContent; public GameObject columnPrefab; public GameObject dlcButtonPrefab; void Start() { RefreshUI(); } public void RefreshUI() { if (dlc_scContent == null) return; dlcButton.ResetSelectionState(); for (int i = dlc_scContent.transform.childCount - 1; i >= 0; i--) { var c = dlc_scContent.transform.GetChild(i).gameObject; #if UNITY_EDITOR if (!Application.isPlaying) DestroyImmediate(c); else Destroy(c); #else Destroy(c); #endif } List displayColumns = BuildDisplayColumns(); if (displayColumns == null) return; for (int colIndex = 0; colIndex < displayColumns.Count; colIndex++) { var col = displayColumns[colIndex]; if (columnPrefab != null) { var headerGO = Instantiate(columnPrefab, dlc_scContent.transform); headerGO.name = "Column_" + colIndex; var card = headerGO.GetComponent(); if (card != null && card.columnNameText != null) { card.columnNameText.text = col.columnName ?? "(Unnamed)"; } else { var txt = headerGO.transform.Find("ColumnNameText")?.GetComponent(); if (txt != null) txt.text = col.columnName ?? "(Unnamed)"; } } if (col.dlcList == null) continue; for (int entryIndex = 0; entryIndex < col.dlcList.Count; entryIndex++) { var dlc = col.dlcList[entryIndex]; if (dlcButtonPrefab == null) { Debug.LogWarning("dlcButtonPrefab is not assigned"); break; } var itemGO = Instantiate(dlcButtonPrefab, dlc_scContent.transform); itemGO.name = $"DLC_Col{colIndex}_Item{entryIndex}"; var btn = itemGO.GetComponent(); if (btn != null) { if (dlc != null) { btn.SetDlcData(dlc); } else { if (btn.dlcName != null) btn.dlcName.text = "(None)"; if (btn.dlcID != null) btn.dlcID.text = ""; if (btn.dlc_profileImgae != null) btn.dlc_profileImgae.enabled = false; if (btn.dlcDescription != null) btn.dlcDescription.text = ""; if (btn.dlcSongsAmount != null) btn.dlcSongsAmount.text = "0"; } } } } var rt = dlc_scContent.GetComponent(); if (rt != null) UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rt); var allButtons = dlc_scContent.GetComponentsInChildren(true); StartCoroutine(ResolveAndRestore(allButtons)); } private IEnumerator ResolveAndRestore(dlcButton[] allButtons) { if (allButtons != null) { for (int i = 0; i < allButtons.Length; i++) { var b = allButtons[i]; if (b != null) { yield return b.ResolveDlcDataAsync(); } } } // Let layout and any fades settle for a frame yield return null; if (allButtons == null || allButtons.Length == 0) { if (VerboseLogs) Debug.LogWarning("loadDlcListPrefab: no dlc buttons found after RefreshUI"); yield break; } dlcButton targetButton = ResolvePreferredRestoreButton(allButtons, out bool forceFirstSong); if (targetButton == null) { targetButton = allButtons[0]; forceFirstSong = true; } SongSelectUI.ForceFirstSongOnNextRestore = forceFirstSong; // Restore is read-only: applying the selection here (including the // fallback DLC) must not overwrite the user's genuine last choice. // Select() runs synchronously inside OnButtonClicked_ShowSongs, so the // suppression window only needs to span this call. dlcButton.SuppressSelectionPersist = true; try { targetButton.OnButtonClicked_ShowSongs(); } finally { dlcButton.SuppressSelectionPersist = false; } if (VerboseLogs) { Debug.Log( $"loadDlcListPrefab: restored dlc button '{targetButton.gameObject.name}', " + $"dlcId={GetDlcId(targetButton)}, forceFirstSong={forceFirstSong}"); } } private dlcButton ResolvePreferredRestoreButton(dlcButton[] allButtons, out bool forceFirstSong) { forceFirstSong = true; if (allButtons == null || allButtons.Length == 0) { return null; } // Prefer restoring by the saved DLC id, which is stable across // session-to-session ordering changes (e.g. runtime-installed DLCs). int savedDlcId = PlayerPrefs.GetInt(dlcButton.SelectedDlcIdKey, 0); if (savedDlcId > 0) { dlcButton savedById = FindButtonByDlcId(allButtons, savedDlcId); if (savedById != null && HasSavedSongSelection(savedById)) { forceFirstSong = false; return savedById; } } // Fall back to the saved positional index for legacy saves. int savedIndex = PlayerPrefs.GetInt(DlcSelectedIndexPrefsKey, 0); if (savedIndex >= 0 && savedIndex < allButtons.Length) { dlcButton savedButton = allButtons[savedIndex]; if (HasSavedSongSelection(savedButton)) { forceFirstSong = false; return savedButton; } } dlcButton fallbackById = FindButtonByDlcId(allButtons, DefaultFallbackDlcId); if (fallbackById != null) { return fallbackById; } return allButtons[0]; } private dlcButton FindButtonByDlcId(dlcButton[] allButtons, int dlcId) { if (allButtons == null) { return null; } for (int i = 0; i < allButtons.Length; i++) { dlcButton button = allButtons[i]; if (button == null) { continue; } if (GetDlcId(button) == dlcId) { return button; } } return null; } private bool HasSavedSongSelection(dlcButton button) { if (button == null) { return false; } string dlcKey = BuildDlcKey(button); if (string.IsNullOrEmpty(dlcKey)) { return false; } // Only the songID key proves a genuine user click: SongButton.OnSongButtonClick // always writes it alongside the index. The index key alone is unreliable because // RestoreSelectedSongForCurrentDlc auto-writes it to 0 on mere display, so relying // on it would treat any browsed-but-not-selected DLC as "user selected". string songIdKey = SongButton.BuildSongSelectedIdPrefsKey(dlcKey); return PlayerPrefs.HasKey(songIdKey) && PlayerPrefs.GetInt(songIdKey, 0) > 0; } private string BuildDlcKey(dlcButton button) { int dlcId = GetDlcId(button); if (dlcId > 0) { return "dlc_" + dlcId; } return null; } private int GetDlcId(dlcButton button) { // Delegate to the button's own resolver to keep id resolution in one place. return button != null ? button.ResolveOwnDlcId() : 0; } private List BuildDisplayColumns() { List result = new List(); HashSet knownDlcs = new HashSet(); if (columns != null) { for (int i = 0; i < columns.Count; i++) { Column source = columns[i]; if (source == null) { continue; } Column copy = new Column { columnName = source.columnName, dlcList = new List() }; if (source.dlcList != null) { for (int j = 0; j < source.dlcList.Count; j++) { dlcData item = source.dlcList[j]; if (item == null) { continue; } copy.dlcList.Add(item); knownDlcs.Add(item); } } result.Add(copy); } } if (!includeRuntimeInstalledDlcs) { return result; } dlcData[] runtimeDlcs = DlcRuntimeRegistry.GetAllDlcs(); if (runtimeDlcs == null || runtimeDlcs.Length == 0) { return result; } Column runtimeColumn = new Column { columnName = string.IsNullOrWhiteSpace(runtimeInstalledColumnName) ? RuntimeInstalledColumnName : runtimeInstalledColumnName, dlcList = new List() }; for (int i = 0; i < runtimeDlcs.Length; i++) { dlcData dlc = runtimeDlcs[i]; if (dlc == null || knownDlcs.Contains(dlc)) { continue; } runtimeColumn.dlcList.Add(dlc); } if (runtimeColumn.dlcList.Count > 0) { result.Add(runtimeColumn); } return result; } }