391 lines
14 KiB
C#
391 lines
14 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using UnityEngine.SceneManagement;
|
||
using System.Collections;
|
||
using DG.Tweening;
|
||
|
||
public class SongButton : MonoBehaviour
|
||
{
|
||
[Header("thisSong_so")]
|
||
public ScriptableObject thisSong_so;
|
||
[Header("展示的信息")]
|
||
public Text songNameText;
|
||
public Text multiNameText;
|
||
public Text joinTime_and_dlcName_Text;
|
||
public Text songOverallDescriptionText;
|
||
public Text highestScoreText;
|
||
public Text highestScoreDifficultyText;
|
||
public Text difficultyIDText;
|
||
public Text songIDText;
|
||
public Image buttonImage;
|
||
public Image scoreLevelImage;
|
||
[Header("头图")]
|
||
public Image profileImage;
|
||
public int song_songSerialID;
|
||
[Header("select mark")]
|
||
public Image selected_boarder;
|
||
public Image fade_image;
|
||
|
||
[Header("计数百分比")]
|
||
public float easePercent = 1f;
|
||
public float ssPercent = 0.95f;
|
||
public float sPercent = 0.9f;
|
||
public float aPercent = 0.8f;
|
||
public float bPercent = 0.7f;
|
||
public float cPercent = 0.6f;
|
||
public float dPercent = 0.5f;
|
||
public float ePercent = 0.4f;
|
||
public float fPercent = 0.3f;
|
||
[Header("阶段图")]
|
||
public Sprite easeSprite;
|
||
public Sprite ssSprite;
|
||
public Sprite sSprite;
|
||
public Sprite aSprite;
|
||
public Sprite bSprite;
|
||
public Sprite cSprite;
|
||
public Sprite dSprite;
|
||
public Sprite eSprite;
|
||
public Sprite fSprite;
|
||
public Sprite ufSprite;
|
||
|
||
[Header("Fade Image Settings")]
|
||
public float minAlpha = 0f;
|
||
public float maxAlpha = 1f;
|
||
public float blinkDuration = 0.5f;
|
||
|
||
private static SongButton currentSelected;
|
||
private Coroutine fadeCoroutine;
|
||
private Coroutine blinkCoroutine;
|
||
|
||
public void Start()
|
||
{
|
||
if (selected_boarder != null)
|
||
{
|
||
Color c = selected_boarder.color;
|
||
c.a = 0f;
|
||
selected_boarder.color = c;
|
||
}
|
||
if (fade_image != null)
|
||
{
|
||
Color c = fade_image.color;
|
||
c.a = 0f;
|
||
fade_image.color = c;
|
||
}
|
||
}
|
||
|
||
public void SetButtonData(string songName, int highestScore, int difficultyID, Sprite songSprite, int songID)
|
||
{
|
||
Debug.Log("SetButtonData 被调用, songID 参数: " + songID);
|
||
|
||
var sd = SongDataLibrary.Instance != null ? SongDataLibrary.Instance.GetSongDataByID(songID) : null;
|
||
if (sd != null)
|
||
{
|
||
if (songNameText != null) songNameText.text = sd.songName;
|
||
|
||
if (multiNameText != null)
|
||
multiNameText.text = "音乐:" + sd.artistName + " | 曲绘:" + sd.painter + " | 制谱:" + sd.level_creator;
|
||
|
||
if (joinTime_and_dlcName_Text != null)
|
||
{
|
||
string join = string.IsNullOrEmpty(sd.thisLevel_addDateString) ? "未知" : sd.thisLevel_addDateString;
|
||
string dlc = string.IsNullOrEmpty(sd.belongsTo_whichDLC) ? "未归档DLC" : sd.belongsTo_whichDLC;
|
||
joinTime_and_dlcName_Text.text = "歌曲收录时间:" + join + " | " + "所属DLC:" + dlc;
|
||
}
|
||
|
||
if (songOverallDescriptionText != null)
|
||
{
|
||
songOverallDescriptionText.text = string.IsNullOrEmpty(sd.thisLevel_overallDescription) ? "暂无歌曲简介。" : sd.thisLevel_overallDescription;
|
||
}
|
||
|
||
// Robust logic: compute best total across chartFiles if present
|
||
int bestTotal = -1;
|
||
int bestDiff = -1;
|
||
|
||
if (sd.chartFiles != null && sd.chartFiles.Count > 0)
|
||
{
|
||
foreach (var entry in sd.chartFiles)
|
||
{
|
||
if (entry == null) continue;
|
||
int total = 0;
|
||
// prefer stored total if > 0
|
||
if (entry.totalPersonalRecordForThisDifficulty > 0)
|
||
{
|
||
total = entry.totalPersonalRecordForThisDifficulty;
|
||
}
|
||
else
|
||
{
|
||
// else compute from entry fields
|
||
int p = entry.chartPersonalRecordForThisDifficulty;
|
||
int i = entry.idolPersonalRecordForThisDifficulty;
|
||
total = p + i;
|
||
|
||
// as fallback also check SongData maps
|
||
if (total == 0 && sd.personalRecordMap != null)
|
||
{
|
||
int pm = sd.personalRecordMap.ContainsKey(entry.difficulty) ? sd.personalRecordMap[entry.difficulty] : 0;
|
||
int im = sd.idolRecordMap != null && sd.idolRecordMap.ContainsKey(entry.difficulty) ? sd.idolRecordMap[entry.difficulty] : 0;
|
||
total = pm + im;
|
||
}
|
||
}
|
||
|
||
if (bestDiff == -1 || total > bestTotal)
|
||
{
|
||
bestTotal = total;
|
||
bestDiff = entry.difficulty;
|
||
}
|
||
}
|
||
}
|
||
|
||
// If still not found, fallback to maps
|
||
if (bestDiff == -1)
|
||
{
|
||
if (sd.personalRecordMap != null)
|
||
{
|
||
foreach (var kvp in sd.personalRecordMap)
|
||
{
|
||
int diff = kvp.Key;
|
||
int p = kvp.Value;
|
||
int i = sd.GetIdolRecord(diff);
|
||
int combined = p + i;
|
||
if (bestDiff == -1 || combined > bestTotal)
|
||
{
|
||
bestTotal = combined;
|
||
bestDiff = diff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// final fallback
|
||
if (bestDiff == -1)
|
||
{
|
||
bestTotal = sd.personalRecord;
|
||
bestDiff = sd.thisLevel_selectedDifficultyID;
|
||
}
|
||
|
||
if (bestTotal < 0) bestTotal = 0;
|
||
|
||
if (highestScoreText != null) highestScoreText.text = bestTotal.ToString();
|
||
|
||
// Map difficulty index to label and set highestScoreDifficultyText as (Label)
|
||
if (highestScoreDifficultyText != null)
|
||
{
|
||
string label = "";
|
||
switch (bestDiff)
|
||
{
|
||
case 0: label = "(Easy)"; break;
|
||
case 1: label = "(Hard)"; break;
|
||
case 2: label = "(Incredible)"; break;
|
||
case 3: label = "(Impossible)"; break;
|
||
default: label = ""; break;
|
||
}
|
||
highestScoreDifficultyText.text = label;
|
||
}
|
||
|
||
// Assign scoreLevelImage based on thresholds relative to 2,000,000
|
||
if (scoreLevelImage != null)
|
||
{
|
||
Sprite chosenSprite = null;
|
||
float baseScore = 2000000f;
|
||
// Evaluate from top to bottom thresholds
|
||
if (bestTotal >= baseScore * easePercent && easeSprite != null)
|
||
chosenSprite = easeSprite;
|
||
else if (bestTotal >= baseScore * ssPercent && ssSprite != null)
|
||
chosenSprite = ssSprite;
|
||
else if (bestTotal >= baseScore * sPercent && sSprite != null)
|
||
chosenSprite = sSprite;
|
||
else if (bestTotal >= baseScore * aPercent && aSprite != null)
|
||
chosenSprite = aSprite;
|
||
else if (bestTotal >= baseScore * bPercent && bSprite != null)
|
||
chosenSprite = bSprite;
|
||
else if (bestTotal >= baseScore * cPercent && cSprite != null)
|
||
chosenSprite = cSprite;
|
||
else if (bestTotal >= baseScore * dPercent && dSprite != null)
|
||
chosenSprite = dSprite;
|
||
else if (bestTotal >= baseScore * ePercent && eSprite != null)
|
||
chosenSprite = eSprite;
|
||
else if (bestTotal >= baseScore * fPercent && fSprite != null)
|
||
chosenSprite = fSprite;
|
||
else if (ufSprite != null)
|
||
chosenSprite = ufSprite;
|
||
|
||
if (chosenSprite != null)
|
||
scoreLevelImage.sprite = chosenSprite;
|
||
}
|
||
|
||
// Determine displayed difficulty: show the highest difficultyLEVEL present in chartFiles (if any)
|
||
string diffDisplay = "0";
|
||
if (sd.chartFiles != null && sd.chartFiles.Count > 0)
|
||
{
|
||
float maxLevel = float.MinValue;
|
||
foreach (var entry in sd.chartFiles)
|
||
{
|
||
if (entry == null) continue;
|
||
if (entry.difficultyLEVEL > maxLevel)
|
||
{
|
||
maxLevel = entry.difficultyLEVEL;
|
||
}
|
||
}
|
||
if (maxLevel != float.MinValue)
|
||
{
|
||
diffDisplay = maxLevel.ToString();
|
||
}
|
||
}
|
||
|
||
if (difficultyIDText != null) difficultyIDText.text = diffDisplay;
|
||
|
||
if (buttonImage != null) buttonImage.sprite = sd.backgroudPIC;
|
||
|
||
if (profileImage != null)
|
||
{
|
||
profileImage.sprite = sd.card_profileImage;
|
||
if (sd.card_profileImage != null)
|
||
{
|
||
profileImage.color = new Color(profileImage.color.r, profileImage.color.g, profileImage.color.b, 1f);
|
||
profileImage.enabled = true;
|
||
}
|
||
else
|
||
{
|
||
profileImage.enabled = false;
|
||
}
|
||
}
|
||
|
||
song_songSerialID = songID;
|
||
if (songIDText != null) songIDText.text = "数据库索引号:" + song_songSerialID.ToString();
|
||
}
|
||
else
|
||
{
|
||
if (songNameText != null) songNameText.text = songName;
|
||
if (highestScoreText != null) highestScoreText.text = "PR: " + highestScore.ToString();
|
||
if (difficultyIDText != null) difficultyIDText.text = difficultyID.ToString();
|
||
if (buttonImage != null) buttonImage.sprite = songSprite;
|
||
song_songSerialID = songID;
|
||
if (songIDText != null) songIDText.text = "Song ID: " + song_songSerialID.ToString();
|
||
}
|
||
}
|
||
|
||
public void OnSongButtonClick()
|
||
{
|
||
Debug.Log("点击按钮的 song_songSerialID: " + song_songSerialID);
|
||
SongData selectedSong = SongDataLibrary.Instance.GetSongDataByID(song_songSerialID);
|
||
if (selectedSong != null)
|
||
{
|
||
if (currentSelected != null && currentSelected != this)
|
||
{
|
||
currentSelected.FadeOutSelectedBorder();
|
||
}
|
||
currentSelected = this;
|
||
FadeInSelectedBorder();
|
||
|
||
SongDataHolder.SelectedSongData = selectedSong;
|
||
var selectedInfo = FindObjectOfType<selected_songInfo>();
|
||
if (selectedInfo != null)
|
||
{
|
||
selectedInfo.UpdateSelectedSongDisplay();
|
||
}
|
||
|
||
// Save per-DLC selected song index to PlayerPrefs
|
||
string dlcKey = SongSelectUI.CurrentDlcKey;
|
||
if (string.IsNullOrEmpty(dlcKey)) dlcKey = "default_0";
|
||
string prefsKey = "song_selected_" + dlcKey;
|
||
int index = GetIndexAmongSongButtons();
|
||
PlayerPrefs.SetInt(prefsKey, index);
|
||
PlayerPrefs.Save();
|
||
Debug.Log($"SongButton: saved selected song index {index} for key {prefsKey}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("未找到对应的 SongData, songID: " + song_songSerialID);
|
||
}
|
||
}
|
||
|
||
private int GetIndexAmongSongButtons()
|
||
{
|
||
// Use parent traversal to find only SongButton siblings, to avoid mismatch when other UI children exist.
|
||
var parent = transform.parent;
|
||
if (parent == null) return 0;
|
||
int idx = 0;
|
||
for (int i = 0; i < parent.childCount; i++)
|
||
{
|
||
var child = parent.GetChild(i);
|
||
var sb = child != null ? child.GetComponent<SongButton>() : null;
|
||
if (sb == null) continue;
|
||
if (sb == this) return idx;
|
||
idx++;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
private void FadeInSelectedBorder()
|
||
{
|
||
if (selected_boarder != null)
|
||
{
|
||
if (fadeCoroutine != null) StopCoroutine(fadeCoroutine);
|
||
fadeCoroutine = StartCoroutine(FadeImage(selected_boarder, selected_boarder.color.a, 1f, 0.3f));
|
||
}
|
||
if (fade_image != null)
|
||
{
|
||
if (blinkCoroutine != null) StopCoroutine(blinkCoroutine);
|
||
blinkCoroutine = StartCoroutine(BlinkFadeImage());
|
||
}
|
||
}
|
||
|
||
private void FadeOutSelectedBorder()
|
||
{
|
||
if (selected_boarder != null)
|
||
{
|
||
if (fadeCoroutine != null) StopCoroutine(fadeCoroutine);
|
||
fadeCoroutine = StartCoroutine(FadeImage(selected_boarder, selected_boarder.color.a, 0f, 0.3f));
|
||
}
|
||
if (fade_image != null)
|
||
{
|
||
if (blinkCoroutine != null) StopCoroutine(blinkCoroutine);
|
||
fadeCoroutine = StartCoroutine(FadeImage(fade_image, fade_image.color.a, 0f, 0.3f));
|
||
}
|
||
}
|
||
|
||
private IEnumerator FadeImage(Image img, float startAlpha, float endAlpha, float duration)
|
||
{
|
||
if (img == null) yield break;
|
||
float time = 0f;
|
||
Color color = img.color;
|
||
while (time < duration)
|
||
{
|
||
time += Time.deltaTime;
|
||
float alpha = Mathf.Lerp(startAlpha, endAlpha, time / duration);
|
||
color.a = alpha;
|
||
img.color = color;
|
||
yield return null;
|
||
}
|
||
color.a = endAlpha;
|
||
img.color = color;
|
||
}
|
||
|
||
private IEnumerator BlinkFadeImage()
|
||
{
|
||
if (fade_image == null) yield break;
|
||
Color color = fade_image.color;
|
||
while (true)
|
||
{
|
||
float time = 0f;
|
||
while (time < blinkDuration / 2f)
|
||
{
|
||
time += Time.deltaTime;
|
||
float alpha = Mathf.Lerp(minAlpha, maxAlpha, time / (blinkDuration / 2f));
|
||
color.a = alpha;
|
||
fade_image.color = color;
|
||
yield return null;
|
||
}
|
||
time = 0f;
|
||
while (time < blinkDuration / 2f)
|
||
{
|
||
time += Time.deltaTime;
|
||
float alpha = Mathf.Lerp(maxAlpha, minAlpha, time / (blinkDuration / 2f));
|
||
color.a = alpha;
|
||
fade_image.color = color;
|
||
yield return null;
|
||
}
|
||
}
|
||
}
|
||
}
|