352 lines
11 KiB
C#
352 lines
11 KiB
C#
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<dlcData> dlcList = new List<dlcData>();
|
|
}
|
|
|
|
[Header("Inspector")]
|
|
public List<Column> columns = new List<Column>();
|
|
|
|
[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<Column> 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<columnCardPrefab>();
|
|
if (card != null && card.columnNameText != null)
|
|
{
|
|
card.columnNameText.text = col.columnName ?? "(Unnamed)";
|
|
}
|
|
else
|
|
{
|
|
var txt = headerGO.transform.Find("ColumnNameText")?.GetComponent<Text>();
|
|
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<dlcButton>();
|
|
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<RectTransform>();
|
|
if (rt != null) UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rt);
|
|
|
|
var allButtons = dlc_scContent.GetComponentsInChildren<dlcButton>(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<Column> BuildDisplayColumns()
|
|
{
|
|
List<Column> result = new List<Column>();
|
|
HashSet<dlcData> knownDlcs = new HashSet<dlcData>();
|
|
|
|
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<dlcData>()
|
|
};
|
|
|
|
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<dlcData>()
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
}
|