UI HUGE UPDATE
This commit is contained in:
@@ -969,6 +969,70 @@ public class SongData : ScriptableObject
|
||||
return null;
|
||||
}
|
||||
|
||||
public SongDlcContentSO GetResolvedDlcContent()
|
||||
{
|
||||
return SongDlcContentResolver.GetContent(this);
|
||||
}
|
||||
|
||||
public Sprite GetResolvedIllustration()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.illustration != null ? content.illustration : illustration;
|
||||
}
|
||||
|
||||
public Sprite GetResolvedBackgroundPic()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.backgroudPIC != null ? content.backgroudPIC : backgroudPIC;
|
||||
}
|
||||
|
||||
public Sprite GetResolvedFullscreenSongPicture()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.fullscreen_songPicture != null ? content.fullscreen_songPicture : fullscreen_songPicture;
|
||||
}
|
||||
|
||||
public Sprite GetResolvedCardProfileImage()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.card_profileImage != null ? content.card_profileImage : card_profileImage;
|
||||
}
|
||||
|
||||
public Sprite GetResolvedRightBottomImage()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.right_pic_bottom_image != null ? content.right_pic_bottom_image : right_pic_bottom_image;
|
||||
}
|
||||
|
||||
public Sprite GetResolvedRightTopImage()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.right_pic_top_image != null ? content.right_pic_top_image : right_pic_top_image;
|
||||
}
|
||||
|
||||
public AudioClip GetResolvedAudioFile()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.audioFile != null ? content.audioFile : audioFile;
|
||||
}
|
||||
|
||||
public AudioClip GetResolvedPreviewAudio()
|
||||
{
|
||||
SongDlcContentSO content = GetResolvedDlcContent();
|
||||
return content != null && content.audio_previewAudio != null ? content.audio_previewAudio : audio_previewAudio;
|
||||
}
|
||||
|
||||
public TextAsset GetResolvedChartFile(int difficulty)
|
||||
{
|
||||
TextAsset overrideChart = SongDlcContentResolver.GetChartFile(this, difficulty);
|
||||
return overrideChart != null ? overrideChart : GetChartFile(difficulty);
|
||||
}
|
||||
|
||||
public AudioClip GetResolvedSettlementMusic()
|
||||
{
|
||||
return SongDlcContentResolver.GetSettlementMusic(this);
|
||||
}
|
||||
|
||||
public void SyncChartFileMap()
|
||||
{
|
||||
if (chartFileMap == null) chartFileMap = new Dictionary<int, TextAsset>();
|
||||
|
||||
@@ -111,6 +111,9 @@ public class SongButton : MonoBehaviour
|
||||
sd = SongDataLibrary.Instance.GetSongDataByID(songID) ?? sd;
|
||||
if (sd != null)
|
||||
{
|
||||
Sprite resolvedBackground = sd.GetResolvedBackgroundPic();
|
||||
Sprite resolvedProfile = sd.GetResolvedCardProfileImage();
|
||||
|
||||
if (songNameText != null) songNameText.text = sd.songName;
|
||||
|
||||
if (multiNameText != null)
|
||||
@@ -173,12 +176,12 @@ public class SongButton : MonoBehaviour
|
||||
// Set rank images for the first three difficulties
|
||||
SetDifficultyRankImages(sd);
|
||||
|
||||
if (buttonImage != null) buttonImage.sprite = sd.backgroudPIC;
|
||||
if (buttonImage != null) buttonImage.sprite = resolvedBackground;
|
||||
|
||||
if (profileImage != null)
|
||||
{
|
||||
profileImage.sprite = sd.card_profileImage;
|
||||
if (sd.card_profileImage != null)
|
||||
profileImage.sprite = resolvedProfile;
|
||||
if (resolvedProfile != null)
|
||||
{
|
||||
profileImage.color = new Color(profileImage.color.r, profileImage.color.g, profileImage.color.b, 1f);
|
||||
profileImage.enabled = true;
|
||||
|
||||
@@ -387,15 +387,17 @@ public class SongSelectUI : MonoBehaviour
|
||||
{
|
||||
songButton.thisSong_so = sd != null ? sd : song;
|
||||
int diffForDisplay = bestDiff >= 0 ? bestDiff : song.difficultyID;
|
||||
songButton.SetButtonData(song.songName, bestTotal, diffForDisplay, song.illustration, song.songID);
|
||||
Sprite resolvedIllustration = sd != null ? sd.GetResolvedIllustration() : song.GetResolvedIllustration();
|
||||
songButton.SetButtonData(song.songName, bestTotal, diffForDisplay, resolvedIllustration, song.songID);
|
||||
}
|
||||
|
||||
Image songImage = songButtonObj.GetComponentInChildren<Image>();
|
||||
TMP_Text[] texts = songButtonObj.GetComponentsInChildren<TMP_Text>();
|
||||
|
||||
if (songImage != null && song.illustration != null)
|
||||
Sprite displayIllustration = sd != null ? sd.GetResolvedIllustration() : song.GetResolvedIllustration();
|
||||
if (songImage != null && displayIllustration != null)
|
||||
{
|
||||
songImage.sprite = song.illustration;
|
||||
songImage.sprite = displayIllustration;
|
||||
songImage.enabled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ public class SongDataLibrary : MonoBehaviour
|
||||
{
|
||||
public static SongDataLibrary Instance;
|
||||
private Dictionary<int, SongData> songDataDictionary = new Dictionary<int, SongData>();
|
||||
private Coroutine loadCoroutine;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void EnsureRuntimeInstance()
|
||||
@@ -38,7 +39,7 @@ public class SongDataLibrary : MonoBehaviour
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
StartCoroutine(LoadSongDataCoroutine());
|
||||
BeginLoad();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -50,10 +51,12 @@ public class SongDataLibrary : MonoBehaviour
|
||||
{
|
||||
if (IsLoading || IsLoaded)
|
||||
{
|
||||
loadCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
IsLoading = true;
|
||||
IsLoaded = false;
|
||||
songDataDictionary.Clear();
|
||||
|
||||
// Allow one frame to render before heavy load begins
|
||||
@@ -73,8 +76,10 @@ public class SongDataLibrary : MonoBehaviour
|
||||
if (songList != null && songList.songs != null && songList.songs.Count > 0)
|
||||
{
|
||||
yield return LoadFromList(songList.songs, "songList");
|
||||
yield return LoadFromRuntimeRegistry("runtimeSongs");
|
||||
IsLoaded = true;
|
||||
IsLoading = false;
|
||||
loadCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
@@ -98,6 +103,7 @@ public class SongDataLibrary : MonoBehaviour
|
||||
|
||||
IsLoaded = true;
|
||||
IsLoading = false;
|
||||
loadCoroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator LoadFromPath(string path, string label)
|
||||
@@ -174,6 +180,44 @@ public class SongDataLibrary : MonoBehaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator LoadFromRuntimeRegistry(string label)
|
||||
{
|
||||
SongData[] runtimeSongs = DlcRuntimeRegistry.GetAllSongs();
|
||||
if (runtimeSongs == null || runtimeSongs.Length == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
int yielded = 0;
|
||||
for (int i = 0; i < runtimeSongs.Length; i++)
|
||||
{
|
||||
SongData data = runtimeSongs[i];
|
||||
if (data == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!songDataDictionary.ContainsKey(data.songID))
|
||||
{
|
||||
songDataDictionary.Add(data.songID, data);
|
||||
if (logLoadProgress)
|
||||
{
|
||||
Debug.Log("Loaded SongData [" + label + "] songID: " + data.songID + ", name: " + data.songName);
|
||||
}
|
||||
}
|
||||
else if (logLoadProgress)
|
||||
{
|
||||
Debug.LogWarning("Duplicate songID: " + data.songID);
|
||||
}
|
||||
|
||||
yielded++;
|
||||
if (yieldEvery > 0 && (yielded % yieldEvery) == 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public SongData GetSongDataByID(int songID)
|
||||
{
|
||||
if (songDataDictionary.ContainsKey(songID))
|
||||
@@ -185,4 +229,38 @@ public SongData GetSongDataByID(int songID)
|
||||
Debug.LogError("未找到 songID: " + songID);
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<SongData> GetAllSongs()
|
||||
{
|
||||
return new List<SongData>(songDataDictionary.Values);
|
||||
}
|
||||
|
||||
public void RefreshLibrary()
|
||||
{
|
||||
if (!isActiveAndEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadCoroutine != null)
|
||||
{
|
||||
StopCoroutine(loadCoroutine);
|
||||
loadCoroutine = null;
|
||||
}
|
||||
|
||||
IsLoaded = false;
|
||||
IsLoading = false;
|
||||
songDataDictionary.Clear();
|
||||
BeginLoad();
|
||||
}
|
||||
|
||||
private void BeginLoad()
|
||||
{
|
||||
if (loadCoroutine != null)
|
||||
{
|
||||
StopCoroutine(loadCoroutine);
|
||||
}
|
||||
|
||||
loadCoroutine = StartCoroutine(LoadSongDataCoroutine());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
if (song_bgImage != null) song_bgImage.sprite = song.backgroudPIC;
|
||||
if (song_bgImage != null) song_bgImage.sprite = song.GetResolvedBackgroundPic();
|
||||
if (songNameText != null) songNameText.text = song.songName;
|
||||
if (artistNameText != null) artistNameText.text = song.artistName;
|
||||
if (painterNameText != null) painterNameText.text = song.painter;
|
||||
@@ -213,7 +213,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
if (dlcNameText != null) dlcNameText.text = song.belongsTo_whichDLC;
|
||||
if (songSerialNumberText != null) songSerialNumberText.text = song.songID.ToString();
|
||||
if (bpmText != null) bpmText.text = LocalizationService.GetFormat("song.details.bpm", song.bpm);
|
||||
if (profound_image != null) profound_image.sprite = song.fullscreen_songPicture;
|
||||
if (profound_image != null) profound_image.sprite = song.GetResolvedFullscreenSongPicture();
|
||||
if (timeSpentText != null) timeSpentText.text = LocalizationService.GetFormat("song.details.play_time", song.time_totalPlayingTime.ToString("F0"));
|
||||
if (enterTimeText != null) enterTimeText.text = LocalizationService.GetFormat("song.details.play_count", song.game_enterTimes);
|
||||
if (detail_informations_text != null)
|
||||
@@ -252,7 +252,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
currentSong = SongDataHolder.SelectedSongData;
|
||||
this_song_SO = currentSong;
|
||||
|
||||
song_bgImage.sprite = currentSong.backgroudPIC;
|
||||
song_bgImage.sprite = currentSong.GetResolvedBackgroundPic();
|
||||
// Documentation text normalized.
|
||||
songNameText.text = currentSong.songName;
|
||||
artistNameText.text = currentSong.artistName;
|
||||
@@ -262,7 +262,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
songSerialNumberText.text = currentSong.songID.ToString();
|
||||
|
||||
bpmText.text = LocalizationService.GetFormat("song.details.bpm", currentSong.bpm);
|
||||
profound_image.sprite = currentSong.fullscreen_songPicture;
|
||||
profound_image.sprite = currentSong.GetResolvedFullscreenSongPicture();
|
||||
|
||||
if (timeSpentText != null) timeSpentText.text = LocalizationService.GetFormat("song.details.play_time", currentSong.time_totalPlayingTime.ToString("F0"));
|
||||
if (enterTimeText != null) enterTimeText.text = LocalizationService.GetFormat("song.details.play_count", currentSong.game_enterTimes);
|
||||
@@ -270,7 +270,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
id_of_thisSong = currentSong.songID;
|
||||
detail_informations_text.text = LocalizationService.GetFormat("song.details.information",
|
||||
currentSong.artistName, currentSong.songName, currentSong.painter, currentSong.level_creator, currentSong.belongsTo_whichDLC);
|
||||
if (currentSong.illustration != null)
|
||||
if (currentSong.GetResolvedIllustration() != null)
|
||||
{
|
||||
//songImage.sprite = currentSong.illustration;
|
||||
//songImage.enabled = true;
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
public static class SongDlcContentResolver
|
||||
{
|
||||
private const string RuntimeResourcesFolder = "so/songDlcContents";
|
||||
private const string EditorSearchFolder = "Assets/Resources/so/songDlcContents";
|
||||
|
||||
private static SongDlcContentSO[] cachedAllContents;
|
||||
private static readonly Dictionary<int, List<SongDlcContentSO>> CachedBySongId = new Dictionary<int, List<SongDlcContentSO>>();
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetStaticState()
|
||||
{
|
||||
cachedAllContents = null;
|
||||
CachedBySongId.Clear();
|
||||
}
|
||||
|
||||
public static void InvalidateCache()
|
||||
{
|
||||
cachedAllContents = null;
|
||||
CachedBySongId.Clear();
|
||||
}
|
||||
|
||||
public static SongDlcContentSO GetContent(SongData song)
|
||||
{
|
||||
if (song == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
EnsureCacheBuilt();
|
||||
if (!CachedBySongId.TryGetValue(song.songID, out List<SongDlcContentSO> list) || list == null || list.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string preferredDlcKey = song.belongsTo_whichDLC ?? string.Empty;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
SongDlcContentSO item = list[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsMatchingDlc(item, preferredDlcKey))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return list[0];
|
||||
}
|
||||
|
||||
public static TextAsset GetChartFile(SongData song, int difficulty)
|
||||
{
|
||||
SongDlcContentSO content = GetContent(song);
|
||||
if (content == null || content.chartOverrides == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < content.chartOverrides.Count; i++)
|
||||
{
|
||||
SongDlcChartOverrideEntry entry = content.chartOverrides[i];
|
||||
if (entry == null || entry.difficulty != difficulty || entry.chartFile == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return entry.chartFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AudioClip GetSettlementMusic(SongData song)
|
||||
{
|
||||
SongDlcContentSO content = GetContent(song);
|
||||
if (content != null && content.settlementMusic != null)
|
||||
{
|
||||
return content.settlementMusic;
|
||||
}
|
||||
|
||||
dlcData owningDlc = GetOwningDlc(song);
|
||||
return owningDlc != null ? owningDlc.settlementMusic : null;
|
||||
}
|
||||
|
||||
public static dlcData GetOwningDlc(SongData song)
|
||||
{
|
||||
if (song == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SongDlcContentSO content = GetContent(song);
|
||||
if (content != null && content.ownerDlc != null)
|
||||
{
|
||||
return content.ownerDlc;
|
||||
}
|
||||
|
||||
dlcData[] allDlcs = RuntimeResourcesCache.LoadAllDlcs();
|
||||
string preferredDlcKey = song.belongsTo_whichDLC ?? string.Empty;
|
||||
dlcData fallback = null;
|
||||
|
||||
for (int i = 0; i < allDlcs.Length; i++)
|
||||
{
|
||||
dlcData dlc = allDlcs[i];
|
||||
if (dlc == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dlc.songList != null && dlc.songList.Contains(song))
|
||||
{
|
||||
if (IsMatchingDlc(dlc, preferredDlcKey))
|
||||
{
|
||||
return dlc;
|
||||
}
|
||||
|
||||
fallback = fallback ?? dlc;
|
||||
}
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
public static SongDlcContentSO[] LoadAllSongDlcContents()
|
||||
{
|
||||
EnsureCacheBuilt();
|
||||
return cachedAllContents ?? Array.Empty<SongDlcContentSO>();
|
||||
}
|
||||
|
||||
private static bool IsMatchingDlc(SongDlcContentSO content, string preferredDlcKey)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(preferredDlcKey))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(content.sourceDlcId) &&
|
||||
string.Equals(content.sourceDlcId.Trim(), preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (content.ownerDlc != null)
|
||||
{
|
||||
if (string.Equals(content.ownerDlc.dlcName ?? string.Empty, preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.Equals(content.ownerDlc.dlcID.ToString(), preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsMatchingDlc(dlcData dlc, string preferredDlcKey)
|
||||
{
|
||||
if (dlc == null || string.IsNullOrWhiteSpace(preferredDlcKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string safeKey = preferredDlcKey.Trim();
|
||||
return string.Equals(dlc.dlcName ?? string.Empty, safeKey, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(dlc.dlcID.ToString(), safeKey, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static void EnsureCacheBuilt()
|
||||
{
|
||||
if (cachedAllContents != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cachedAllContents = LoadAllContentsInternal();
|
||||
CachedBySongId.Clear();
|
||||
|
||||
for (int i = 0; i < cachedAllContents.Length; i++)
|
||||
{
|
||||
SongDlcContentSO content = cachedAllContents[i];
|
||||
if (content == null || content.songId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CachedBySongId.TryGetValue(content.songId, out List<SongDlcContentSO> list) || list == null)
|
||||
{
|
||||
list = new List<SongDlcContentSO>();
|
||||
CachedBySongId[content.songId] = list;
|
||||
}
|
||||
|
||||
list.Add(content);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, List<SongDlcContentSO>> pair in CachedBySongId)
|
||||
{
|
||||
pair.Value.Sort(CompareContent);
|
||||
}
|
||||
}
|
||||
|
||||
private static int CompareContent(SongDlcContentSO left, SongDlcContentSO right)
|
||||
{
|
||||
if (ReferenceEquals(left, right))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (left == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (right == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ownerCompare = string.CompareOrdinal(left.sourceDlcId ?? string.Empty, right.sourceDlcId ?? string.Empty);
|
||||
if (ownerCompare != 0)
|
||||
{
|
||||
return ownerCompare;
|
||||
}
|
||||
|
||||
return string.CompareOrdinal(left.GetResolvedContentId(), right.GetResolvedContentId());
|
||||
}
|
||||
|
||||
private static SongDlcContentSO[] LoadAllContentsInternal()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
string[] searchFolders = { EditorSearchFolder };
|
||||
string[] guids = AssetDatabase.FindAssets("t:SongDlcContentSO", searchFolders);
|
||||
if (guids != null && guids.Length > 0)
|
||||
{
|
||||
List<SongDlcContentSO> result = new List<SongDlcContentSO>(guids.Length);
|
||||
for (int i = 0; i < guids.Length; i++)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
SongDlcContentSO content = AssetDatabase.LoadAssetAtPath<SongDlcContentSO>(assetPath);
|
||||
if (content != null)
|
||||
{
|
||||
result.Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
string[] fallbackGuids = AssetDatabase.FindAssets("t:SongDlcContentSO");
|
||||
List<SongDlcContentSO> fallback = new List<SongDlcContentSO>(fallbackGuids.Length);
|
||||
for (int i = 0; i < fallbackGuids.Length; i++)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(fallbackGuids[i]);
|
||||
SongDlcContentSO content = AssetDatabase.LoadAssetAtPath<SongDlcContentSO>(assetPath);
|
||||
if (content != null)
|
||||
{
|
||||
fallback.Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return fallback.ToArray();
|
||||
}
|
||||
#endif
|
||||
|
||||
SongDlcContentSO[] loaded = RuntimeResourcesCache.LoadAllSongDlcContents();
|
||||
if (loaded != null && loaded.Length > 0)
|
||||
{
|
||||
return loaded;
|
||||
}
|
||||
|
||||
return Resources.LoadAll<SongDlcContentSO>(string.Empty) ?? Array.Empty<SongDlcContentSO>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 113603159b1b54d46aa96b227e93e111
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public class SongDlcChartOverrideEntry
|
||||
{
|
||||
public int difficulty;
|
||||
public TextAsset chartFile;
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "SongDlcContent_", menuName = "EaseOut/Song DLC Content")]
|
||||
public class SongDlcContentSO : ScriptableObject
|
||||
{
|
||||
[Header("Identity")]
|
||||
public string contentId;
|
||||
public string sourceDlcId;
|
||||
public int songId;
|
||||
public string songName;
|
||||
|
||||
[Header("Song Pack")]
|
||||
public dlcData ownerDlc;
|
||||
|
||||
[Header("Illustrations")]
|
||||
public Sprite illustration;
|
||||
public Sprite backgroudPIC;
|
||||
public Sprite fullscreen_songPicture;
|
||||
public Sprite card_profileImage;
|
||||
public Sprite right_pic_bottom_image;
|
||||
public Sprite right_pic_top_image;
|
||||
|
||||
[Header("Audio")]
|
||||
public AudioClip audioFile;
|
||||
public AudioClip audio_previewAudio;
|
||||
public AudioClip settlementMusic;
|
||||
|
||||
[Header("Charts")]
|
||||
public List<SongDlcChartOverrideEntry> chartOverrides = new List<SongDlcChartOverrideEntry>();
|
||||
|
||||
public string GetResolvedContentId()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(contentId))
|
||||
{
|
||||
return contentId.Trim();
|
||||
}
|
||||
|
||||
string safeName = string.IsNullOrWhiteSpace(name) ? "song_dlc_content" : name.Trim();
|
||||
return "song_" + songId + "_" + safeName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60bf2d72664d2cf4b90b5e3ccfa56a0b
|
||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
||||
[CreateAssetMenu(fileName = "NewDLCData", menuName = "EaseOut/dlcData")]
|
||||
public class dlcData : ScriptableObject
|
||||
{
|
||||
[Header("Runtime")]
|
||||
public string runtimeDlcId;
|
||||
|
||||
[Header("Inspector")]
|
||||
public Sprite dlc_image;
|
||||
|
||||
@@ -19,6 +22,19 @@ public class dlcData : ScriptableObject
|
||||
[Header("Inspector")]
|
||||
public bool dlcIsUnlocked;
|
||||
|
||||
[Header("Entitlement")]
|
||||
[Tooltip("When enabled, this DLC requires ownership validation before its content can be used.")]
|
||||
public bool requiresOwnership;
|
||||
|
||||
[Tooltip("Steam DLC App ID. Keep 0 when unused.")]
|
||||
public int steamAppId;
|
||||
|
||||
[Tooltip("Optional remote catalog/content id for future remote delivery.")]
|
||||
public string remoteCatalogId;
|
||||
|
||||
[Tooltip("True when this DLC's content ships inside the current player build.")]
|
||||
public bool builtInContent = true;
|
||||
|
||||
[Header("Inspector")]
|
||||
[TextArea(3,10)]
|
||||
public string dlcDescription;
|
||||
@@ -29,7 +45,35 @@ public class dlcData : ScriptableObject
|
||||
[Header("Inspector")]
|
||||
public List<SongData> songList = new List<SongData>();
|
||||
|
||||
[Header("DLC Song Content")]
|
||||
public List<SongDlcContentSO> songContentOverrides = new List<SongDlcContentSO>();
|
||||
|
||||
[Header("Inspector")]
|
||||
[Tooltip("Documentation text normalized.")]
|
||||
public AudioClip settlementMusic;
|
||||
|
||||
public string GetResolvedDlcKey()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(runtimeDlcId))
|
||||
{
|
||||
return runtimeDlcId.Trim();
|
||||
}
|
||||
|
||||
if (dlcID > 0)
|
||||
{
|
||||
return dlcID.ToString();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dlcName))
|
||||
{
|
||||
return dlcName.Trim();
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(name) ? "dlc" : name.Trim();
|
||||
}
|
||||
|
||||
public bool ShouldEnforceEntitlement()
|
||||
{
|
||||
return requiresOwnership;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
public class loadDlcListPrefab : MonoBehaviour
|
||||
{
|
||||
private static readonly bool VerboseLogs = false;
|
||||
private const string RuntimeInstalledColumnName = "Installed DLC";
|
||||
[System.Serializable]
|
||||
public class Column
|
||||
{
|
||||
@@ -16,6 +17,10 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
[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;
|
||||
@@ -41,11 +46,12 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
#endif
|
||||
}
|
||||
|
||||
if (columns == null) return;
|
||||
List<Column> displayColumns = BuildDisplayColumns();
|
||||
if (displayColumns == null) return;
|
||||
|
||||
for (int colIndex = 0; colIndex < columns.Count; colIndex++)
|
||||
for (int colIndex = 0; colIndex < displayColumns.Count; colIndex++)
|
||||
{
|
||||
var col = columns[colIndex];
|
||||
var col = displayColumns[colIndex];
|
||||
|
||||
if (columnPrefab != null)
|
||||
{
|
||||
@@ -154,4 +160,80 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -646,18 +646,22 @@ public class selected_songInfo : MonoBehaviour
|
||||
if (SongDataHolder.SelectedSongData != null)
|
||||
{
|
||||
var song = SongDataHolder.SelectedSongData;
|
||||
if (t_songCoverImage != null && song.right_pic_top_image != null)
|
||||
Sprite topCover = song.GetResolvedRightTopImage();
|
||||
Sprite bottomCover = song.GetResolvedRightBottomImage();
|
||||
AudioClip previewAudio = song.GetResolvedPreviewAudio();
|
||||
|
||||
if (t_songCoverImage != null && topCover != null)
|
||||
{
|
||||
t_songCoverImage.sprite = song.right_pic_top_image;
|
||||
t_songCoverImage.sprite = topCover;
|
||||
t_songCoverImage.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
t_songCoverImage.enabled = false;
|
||||
}
|
||||
if (btm_songCoverImage != null && song.right_pic_bottom_image != null)
|
||||
if (btm_songCoverImage != null && bottomCover != null)
|
||||
{
|
||||
btm_songCoverImage.sprite = song.right_pic_bottom_image;
|
||||
btm_songCoverImage.sprite = bottomCover;
|
||||
btm_songCoverImage.enabled = true;
|
||||
}
|
||||
else
|
||||
@@ -673,7 +677,7 @@ public class selected_songInfo : MonoBehaviour
|
||||
if (preListenAS != null)
|
||||
{
|
||||
preListenAS.Stop();
|
||||
preListenAS.clip = song.audio_previewAudio;
|
||||
preListenAS.clip = previewAudio;
|
||||
preListenAS.time = 0;
|
||||
UpdatePlayPauseButtonSprite(false);
|
||||
UpdateMusicUI(0, preListenAS.clip != null ? preListenAS.clip.length : 0);
|
||||
@@ -1256,7 +1260,7 @@ public class selected_songInfo : MonoBehaviour
|
||||
}
|
||||
|
||||
// Validate that the selected difficulty has a chart file before proceeding
|
||||
var chart = sd.GetChartFile(sd.thisLevel_selectedDifficultyID);
|
||||
var chart = sd.GetResolvedChartFile(sd.thisLevel_selectedDifficultyID);
|
||||
if (chart == null)
|
||||
{
|
||||
Debug.LogError($"QuickEnter aborted: Song '{sd.songName}' has no chart file for difficulty {sd.thisLevel_selectedDifficultyID}.");
|
||||
@@ -1491,7 +1495,7 @@ public class selected_songInfo : MonoBehaviour
|
||||
LogVerbose($"selected song: {selected.songName} (id {selected.songID}), difficulty {selected.thisLevel_selectedDifficultyID}");
|
||||
|
||||
// get chart TextAsset from SongData for current selected difficulty
|
||||
TextAsset chartAsset = selected.GetChartFile(selected.thisLevel_selectedDifficultyID);
|
||||
TextAsset chartAsset = selected.GetResolvedChartFile(selected.thisLevel_selectedDifficultyID);
|
||||
if (chartAsset == null)
|
||||
{
|
||||
LogVerbose($"Chart file for difficulty {selected.thisLevel_selectedDifficultyID} not found on SongData {selected.songName}");
|
||||
@@ -1532,9 +1536,10 @@ public class selected_songInfo : MonoBehaviour
|
||||
// assign audio clip to GameManager.musicSource if available
|
||||
if (gameManager != null)
|
||||
{
|
||||
if (selected.audioFile != null && gameManager.musicSource != null)
|
||||
AudioClip resolvedAudio = selected.GetResolvedAudioFile();
|
||||
if (resolvedAudio != null && gameManager.musicSource != null)
|
||||
{
|
||||
gameManager.musicSource.clip = selected.audioFile;
|
||||
gameManager.musicSource.clip = resolvedAudio;
|
||||
gameManager.musicSource.loop = false;
|
||||
// make sure AudioSource won't auto-play due to inspector settings
|
||||
gameManager.musicSource.playOnAwake = false;
|
||||
|
||||
Reference in New Issue
Block a user