using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; using UnityEngine.Serialization; using UnityEngine.SceneManagement; using System.Collections; using UnityEngine.Audio; using UnityEditor; public class settlementController : MonoBehaviour { [Header("�������֮��")] [SerializeField] private BeatmapManager bmm; [SerializeField] private ScoreManager sm; public GameManager gm; private SongData thisSong_so; private int maxScore_sum = 2000000; [SerializeField] private Image thisSong_backPic; [Header("��ת֮��ť")] public Button exit_toSelectSongs; public Button replay_thisGame; public Button display_rankList; public Button share_toSocialMedia; [Header("�ı��ͽ�����")] public Text songName_Text; [Tooltip("��ǰ�ؿ��Ľ��Ȱٷֱ�")] public Text thisLevel_currentPercentage_Text; public Text finalScore_Text; public Text pmScoreSum_Text; public Text idolScoreSum_Text; public Text accuracy_Text; public Image thisLevel_progressBar_Image; [Header("׼ȷ���㷨")] public float perfect_weight = 1f; public float great_weight = 0.6666667f; public float good_weight = 0.333333f; public float miss_weight = 0; [Header("����������Ϣ")] public Text reward_playerEXP_Text; public Text reward_money_Text; public Text reward_idolEXP_bottle_Text; [Header("�Ŷӽ���")] public loadSettlementTeamPrefab settlementTeamLoader; public Image mvp_hero_hd_image; [Header("������Ǯ")] [SerializeField] private long moneyToGive_thisLevel; // �õ�����ͳ�� [Header("�������ͳ��")] public Text perfectHitCount_Text; public Text perfectHitPercent_Text; public Image perfect_barFill_Image; public Text greatHitCount_Text; public Text greatHitPercent_Text; public Image great_barFill_Image; public Text goodHitCount_Text; public Text goodHitPercent_Text; public Image good_barFill_Image; public Text missHitCount_Text; public Text missHitPercent_Text; public Image miss_barFill_Image; public Text gameNote_rate; [Header("Timing Statistics")] public Text earlyHitCount_Text; public Text lateHitCount_Text; public Text avgOffset_Text; [Header("��������ϵͳ")] [Tooltip("�������ֲ��������½���")] public AudioSource settlementAudioSource; [Tooltip("��Ϸ���ֲ�������ԭ��Ϸ���֣�")] public AudioSource oriGameMusicSource; [Tooltip("��Ƶ�����������ڵ�ͨ�˲���������")] public AudioMixer gameMusicMixer; [Tooltip("��Ƶ�������е�ͨ�˲�����������")] public string lowpassParamName = "inGameMusic_lowpass"; [Tooltip("��ͨ�˲�����������ʱ�䣨�룩")] public float lowpassFadeDuration = 2f; [Tooltip("������� CanvasGroup�����ڵ���Ч����")] public CanvasGroup settlementCanvasGroup; [Header("����������")] [Tooltip("������")] public GameObject cdm; private Coroutine musicTransitionCoroutine; private Coroutine canvasFadeCoroutine; private void Awake() { // Setup button listeners if (replay_thisGame != null) { replay_thisGame.onClick.AddListener(OnReplayButtonClicked); } if (exit_toSelectSongs != null) { exit_toSelectSongs.onClick.AddListener(OnExitButtonClicked); } // ���� CDM �����ڿ�ʼʱ���ֽ���״̬�� if (cdm != null) { cdm.SetActive(false); if (GameConfig.verboseLogs) Debug.Log("[SettlementController] CDM object disabled at startup"); } else { Debug.LogWarning("[SettlementController] CDM object is not assigned"); } } private void Start() { // 确保 LowPass 在开始时处于原始位置(通常是 22000Hz,即不滤波) if (gameMusicMixer != null && !string.IsNullOrEmpty(lowpassParamName)) { try { gameMusicMixer.SetFloat(lowpassParamName, 22000f); } catch { } } } private void OnDestroy() { if (replay_thisGame != null) { replay_thisGame.onClick.RemoveListener(OnReplayButtonClicked); } if (exit_toSelectSongs != null) { exit_toSelectSongs.onClick.RemoveListener(OnExitButtonClicked); } // ֹͣ���ֹ���Э�� if (musicTransitionCoroutine != null) { StopCoroutine(musicTransitionCoroutine); musicTransitionCoroutine = null; } // ֹͣ CanvasGroup ����Э�� if (canvasFadeCoroutine != null) { StopCoroutine(canvasFadeCoroutine); canvasFadeCoroutine = null; } } // Start is called once before the first execution of Update after the MonoBehaviour is created public void getThisSong_info() { if (bmm != null) { thisSong_so = bmm.assignedSongData; } } public void startSettlement_uiUpdate() { // --- �������ڽ������̿�ʼʱ���� CanvasGroup Ϊ͸�� --- InitializeSettlementCanvas(); // --- ���������ý��������� --- if (cdm != null) { cdm.SetActive(true); Debug.Log("[SettlementController] CDM object enabled at settlement start"); } getThisSong_info(); // --- Statistics: Record total play time at settlement --- if (gm != null) gm.RecordTotalPlayTime(); else { var activeGM = FindObjectOfType(); if (activeGM != null) activeGM.RecordTotalPlayTime(); } // ׼���������֣���UI����֮ǰ�� PrepareSettlementMusic(); if(sm != null) { songName_Text.text = bmm.parsedTitle; thisSong_backPic.sprite = bmm.assignedSongData.fullscreen_songPicture; finalScore_Text.text = (sm.allSum_pmScore + sm.allSum_idolScore).ToString(); pmScoreSum_Text.text = sm.allSum_pmScore.ToString(); idolScoreSum_Text.text = sm.allSum_idolScore.ToString(); thisLevel_currentPercentage_Text.text = ((float)(sm.allSum_pmScore + sm.allSum_idolScore) / maxScore_sum * 100).ToString("F2") + "%"; thisLevel_progressBar_Image.fillAmount = (float)(sm.allSum_pmScore + sm.allSum_idolScore) / maxScore_sum; int noteCountSum = sm.countPerfect + sm.countGreat + sm.countGood + sm.countMiss; perfectHitCount_Text.text = sm.countPerfect.ToString(); greatHitCount_Text.text = sm.countGreat.ToString(); goodHitCount_Text.text = sm.countGood.ToString(); missHitCount_Text.text = sm.countMiss.ToString(); perfect_barFill_Image.fillAmount = (float)sm.countPerfect / noteCountSum; great_barFill_Image.fillAmount = (float)sm.countGreat / noteCountSum; good_barFill_Image.fillAmount = (float)sm.countGood / noteCountSum; miss_barFill_Image.fillAmount = (float)sm.countMiss / noteCountSum; gameNote_rate.text = "已完成: " + noteCountSum.ToString() + "/" + bmm.parsedNoteAmount.ToString(); perfectHitPercent_Text.text = ((float)sm.countPerfect / noteCountSum * 100).ToString("F1") + "%"; greatHitPercent_Text.text = ((float)sm.countGreat / noteCountSum * 100).ToString("F1") + "%"; goodHitPercent_Text.text = ((float)sm.countGood / noteCountSum * 100).ToString("F1") + "%"; missHitPercent_Text.text = ((float)sm.countMiss / noteCountSum * 100).ToString("F1") + "%"; accuracy_Text.text = ((float)(sm.countPerfect * perfect_weight + sm.countGreat * great_weight + sm.countGood * good_weight + sm.countMiss * miss_weight) / noteCountSum * 100).ToString("F3") + "%"; // Timing Statistics if (earlyHitCount_Text != null) earlyHitCount_Text.text = sm.countEarly.ToString(); if (lateHitCount_Text != null) lateHitCount_Text.text = sm.countLate.ToString(); if (avgOffset_Text != null) { float avg = sm.offsetCount > 0 ? sm.totalOffsetMs / sm.offsetCount : 0f; avgOffset_Text.text = avg.ToString("F2") + " ms"; } } else Debug.LogError("score manager is null"); // --- Achievement System: Load and display achievements --- if (InGamePerformanceManager.Instance != null) { // Calculate total cure, damage and mana from teamUIController float totalCure = 0; float totalDamage = 0; float totalMana = 0; if (teamUIController.Instance != null) { if (teamUIController.Instance.totalHealProvided != null) { foreach (float heal in teamUIController.Instance.totalHealProvided) { totalCure += heal; } } if (teamUIController.Instance.totalDamageDealt != null) { foreach (float dmg in teamUIController.Instance.totalDamageDealt) { totalDamage += dmg; } } if (teamUIController.Instance.totalManaRestored != null) { foreach (float mana in teamUIController.Instance.totalManaRestored) { totalMana += mana; } } Debug.Log($"[SettlementController] Stats Aggregated - Cure: {totalCure}, Damage: {totalDamage}, Mana: {totalMana}"); } // Update achievement categories InGamePerformanceManager.Instance.UpdateTotalCure(totalCure); InGamePerformanceManager.Instance.UpdateTotalDamage(totalDamage); InGamePerformanceManager.Instance.UpdateTotalManaRestored(totalMana); // Load and display achievement prefabs InGamePerformanceManager.Instance.LoadArchievePrefab(); } // Persist run results into the SongData SO for this difficulty // (update personal / idol / total records and per-entry progress if improved) getThisSong_info(); // ensure thisSong_so set if (thisSong_so != null && bmm != null) { int diff = bmm.assignedDifficulty; if (diff >= 0) { int pm = sm != null ? sm.allSum_pmScore : 0; int idol = sm != null ? sm.allSum_idolScore : 0; int total = pm + idol; // 更新最高分 (仅当本次总分更高时) thisSong_so.UpdateDifficultyRecord(diff, pm, idol); // 更新上次游玩分数 thisSong_so.UpdateChartScore(diff, total); // 更新进度 (仅当进步时) if (thisSong_so.chartFiles != null) { var entry = thisSong_so.chartFiles.Find(e => e != null && e.difficulty == diff); if (entry != null) { // 进度计算 (0..1) float newProgress = Mathf.Clamp01((float)total / (float)maxScore_sum); if (newProgress > entry.levelProgressForThisDifficulty) { entry.levelProgressForThisDifficulty = newProgress; } if (thisSong_so.thisLevel_selectedDifficultyID == diff) { thisSong_so.current_levelProgress = Mathf.Max(thisSong_so.current_levelProgress, entry.levelProgressForThisDifficulty); } } } } } // Populate settlement team cards if a loader is assigned if (settlementTeamLoader == null) { // try to auto-locate loader in scene if not assigned in inspector settlementTeamLoader = FindObjectOfType(); } if (settlementTeamLoader != null) { // ensure loader has key references if (settlementTeamLoader.sm == null) settlementTeamLoader.sm = sm ?? ScoreManager.Instance; if (settlementTeamLoader.tuic == null) settlementTeamLoader.tuic = teamUIController.Instance; if (settlementTeamLoader.gm == null) settlementTeamLoader.gm = gm ?? FindObjectOfType(); try { settlementTeamLoader.PopulateSettlementCards(); } catch (System.Exception ex) { Debug.LogWarning("Failed to PopulateSettlementCards: " + ex); } } // ��ʼ���ֹ��ɣ��ڽ���UI��ʾ�� // ��ѡ�� MVP ������ͼƬ SetMvpHeroImageFromTopScorer(); StartMusicTransition(); // --- �޸ģ������߼�ĩβ��ʼ CanvasGroup ���� --- StartCanvasFadeIn(); } /// /// ��ʼ��������� CanvasGroup Ϊ͸��״̬ /// private void InitializeSettlementCanvas() { if (settlementCanvasGroup != null) { settlementCanvasGroup.alpha = 0f; settlementCanvasGroup.interactable = false; settlementCanvasGroup.blocksRaycasts = false; Debug.Log("[SettlementController] Settlement canvas initialized to transparent state"); } else { Debug.LogWarning("[SettlementController] settlementCanvasGroup is not assigned, cannot initialize"); } } /// /// ������dlcData�з����Ұ�����ǰ������DLC����׼���������� /// private void PrepareSettlementMusic() { // --- �޸ģ��Ƴ� CanvasGroup ���ã������ڽ����߼�ĩβ�ŵ��� --- if (thisSong_so == null) { Debug.LogWarning("[SettlementController] thisSong_so is null, cannot prepare settlement music"); return; } if (settlementAudioSource == null) { Debug.LogWarning("[SettlementController] settlementAudioSource is not assigned, cannot prepare settlement music"); return; } // �������� dlcData ScriptableObjects dlcData[] allDlcs = Resources.LoadAll(""); dlcData foundDlc = null; // ��������DLC�����Ұ�����ǰ������DLC foreach (var dlc in allDlcs) { if (dlc == null || dlc.songList == null) continue; if (dlc.songList.Contains(thisSong_so)) { foundDlc = dlc; break; } } if (foundDlc == null) { Debug.LogWarning($"[SettlementController] No DLC found containing song '{thisSong_so.songName}'"); return; } if (foundDlc.settlementMusic == null) { Debug.LogWarning($"[SettlementController] DLC '{foundDlc.dlcName}' does not have settlement music assigned"); return; } // ���ý������ֵ�AudioSource settlementAudioSource.clip = foundDlc.settlementMusic; settlementAudioSource.loop = true; settlementAudioSource.playOnAwake = false; // ��������ͣ���ȴ����� settlementAudioSource.volume = 1f; settlementAudioSource.mute = true; settlementAudioSource.Stop(); Debug.Log($"[SettlementController] Prepared settlement music from DLC '{foundDlc.dlcName}': {foundDlc.settlementMusic.name}"); // --- �޸ģ��Ƴ� CanvasGroup ������ã������ڽ����߼�ĩβ�ŵ��� --- } /// /// ��ʼ���ֹ��ɣ�������Ϸ���֣�ͨ����ͨ�˲�������Ȼ�󲥷Ž������� /// private void StartMusicTransition() { if (musicTransitionCoroutine != null) { StopCoroutine(musicTransitionCoroutine); } musicTransitionCoroutine = StartCoroutine(MusicTransitionRoutine()); } /// /// ���ֹ���Э�̣� /// 1. ����Ϸ���ֵĵ�ͨ�˲�����22000Hz�𽥽���0Hz��ͬʱ��������0 /// 2. ������ֹͣԭ���ֲ���ʼ���Ž������� /// private IEnumerator MusicTransitionRoutine() { float elapsed = 0f; float startLowpass = 22000f; float endLowpass = 0f; // --- ��������¼��ʼ���� --- float startVolume = (oriGameMusicSource != null) ? oriGameMusicSource.volume : 1f; // ���ԭ��Ϸ����Դ�ͻ����������ڣ�����е�ͨ�˲����������������� if (oriGameMusicSource != null && gameMusicMixer != null && !string.IsNullOrEmpty(lowpassParamName)) { Debug.Log($"[SettlementController] Starting lowpass and volume fade over {lowpassFadeDuration}s"); while (elapsed < lowpassFadeDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / Mathf.Max(0.0001f, lowpassFadeDuration)); // 1. ���õ�ͨ�˲��� float currentLowpass = Mathf.Lerp(startLowpass, endLowpass, t); try { gameMusicMixer.SetFloat(lowpassParamName, currentLowpass); } catch (System.Exception ex) { Debug.LogWarning($"Mixer error: {ex.Message}"); } // 2. --- �޸ģ�ƽ���������� --- oriGameMusicSource.volume = Mathf.Lerp(startVolume, 0f, t); yield return null; } // ȷ������״̬ try { gameMusicMixer.SetFloat(lowpassParamName, endLowpass); } catch { } // --- �޸ģ��������㲢��ͣ/ֹͣ --- oriGameMusicSource.volume = 0f; oriGameMusicSource.Pause(); // ����ʹ�� .Stop() Debug.Log("[SettlementController] Lowpass and volume fade complete, game music paused."); } else { Debug.LogWarning("[SettlementController] oriGameMusicSource or gameMusicMixer not assigned, skipping fade"); } // ������ɺ󣬿�ʼ���Ž������� if (settlementAudioSource != null && settlementAudioSource.clip != null) { try { settlementAudioSource.mute = false; settlementAudioSource.Play(); Debug.Log($"[SettlementController] Settlement music started: {settlementAudioSource.clip.name}"); // --- 一旦结算音乐开始播放,立刻将 LowPass 设置回原位确保音频正常 --- if (gameMusicMixer != null && !string.IsNullOrEmpty(lowpassParamName)) { gameMusicMixer.SetFloat(lowpassParamName, 22000f); Debug.Log("[SettlementController] Reset lowpass to 22000Hz for normal audio quality."); } } catch (System.Exception ex) { Debug.LogWarning($"[SettlementController] Failed to start settlement music: {ex.Message}"); } } musicTransitionCoroutine = null; } /// /// ��ʼ CanvasGroup ����Ч�� /// private void StartCanvasFadeIn() { if (canvasFadeCoroutine != null) { StopCoroutine(canvasFadeCoroutine); } canvasFadeCoroutine = StartCoroutine(CanvasFadeInRoutine()); } /// /// CanvasGroup ����Э�̣�0.25���ڴ� alpha=0 ���뵽 alpha=1 /// private IEnumerator CanvasFadeInRoutine() { if (settlementCanvasGroup == null) { Debug.LogWarning("[SettlementController] settlementCanvasGroup is not assigned, cannot fade in"); yield break; } float fadeDuration = 0.25f; float elapsed = 0f; float startAlpha = settlementCanvasGroup.alpha; float targetAlpha = 1f; Debug.Log($"[SettlementController] Starting CanvasGroup fade-in from {startAlpha} to {targetAlpha} over {fadeDuration}s"); while (elapsed < fadeDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / fadeDuration); settlementCanvasGroup.alpha = Mathf.Lerp(startAlpha, targetAlpha, t); yield return null; } // ȷ������״̬ settlementCanvasGroup.alpha = targetAlpha; settlementCanvasGroup.interactable = true; settlementCanvasGroup.blocksRaycasts = true; Debug.Log("[SettlementController] CanvasGroup fade-in complete"); canvasFadeCoroutine = null; } /// /// ����������ǰ��Ϸ - ���¼������桢���ֺͳ�ʼ��������Ϸ״̬ /// private void OnReplayButtonClicked() { Debug.Log("[SettlementController] Replay button clicked - restarting current game"); // ��֤��Ҫ������ if (bmm == null) { Debug.LogError("[SettlementController] BeatmapManager is null, cannot replay"); return; } if (bmm.assignedSongData == null) { Debug.LogError("[SettlementController] AssignedSongData is null, cannot replay"); return; } // ���浱ǰ�ؿ���Ϣ���������¼��أ� SongData songToReplay = bmm.assignedSongData; int difficultyToReplay = bmm.assignedDifficulty; if (difficultyToReplay < 0) { Debug.LogError("[SettlementController] AssignedDifficulty is invalid, cannot replay"); return; } Debug.Log($"[SettlementController] Reloading song: {songToReplay.songName}, difficulty: {difficultyToReplay}"); // ֹͣ�������� if (settlementAudioSource != null && settlementAudioSource.isPlaying) { settlementAudioSource.Stop(); } // ���� BeatmapManager Ϊ��һ������׼������ BeatmapManager.SetPendingSong(songToReplay, difficultyToReplay); // ���¼��ص�ǰ������GamePlay_gamePlay�� SceneManager.LoadScene(SceneManager.GetActiveScene().name); } /// /// ����ѡ����� /// private void OnExitButtonClicked() { Debug.Log("[SettlementController] Exit button clicked - returning to song selection"); // ֹͣ�������� if (settlementAudioSource != null && settlementAudioSource.isPlaying) { settlementAudioSource.Stop(); } // ����κδ����������� BeatmapManager.pendingSongData = null; BeatmapManager.pendingDifficulty = -1; // Ensure time scale restored try { Time.timeScale = 1f; } catch {} // If GameManager and its blackMaskImage available, start coroutine on gm to fade to black then load if (gm != null && gm.blackMaskImage != null) { gm.StartCoroutine(FadeToBlackAndLoadOnGM("selectYourSongFirst", 0.25f)); } else { SceneManager.LoadScene("selectYourSongFirst"); } } // Coroutine that will be started on the GameManager instance so that the gm's MonoBehaviour runs it private IEnumerator FadeToBlackAndLoadOnGM(string sceneName, float duration) { if (gm == null || gm.blackMaskImage == null) { SceneManager.LoadScene(sceneName); yield break; } var blackMask = gm.blackMaskImage; // ensure image active if (!blackMask.gameObject.activeSelf) blackMask.gameObject.SetActive(true); float startA = blackMask.color.a; float elapsed = 0f; // enable raycast target while fading to block input try { blackMask.raycastTarget = true; } catch {} while (elapsed < duration) { elapsed += Time.unscaledDeltaTime; float frac = Mathf.Clamp01(elapsed / Mathf.Max(0.0001f, duration)); Color c = blackMask.color; c.a = Mathf.Lerp(startA, 1f, frac); blackMask.color = c; yield return null; } // ensure fully opaque Color fc = blackMask.color; fc.a = 1f; blackMask.color = fc; // load target scene SceneManager.LoadScene(sceneName); } /// /// ���ݸ���ɫ�������ѡ����߷ֵĽ�ɫ�������� AllyHero_SO �е� ally_hero_HD_image ��ֵ�� mvp_hero_hd_image /// ����ʹ�� ScoreManager �� per-track pm sums��������������˵����� AllyCombatant.currentScore /// private void SetMvpHeroImageFromTopScorer() { if (mvp_hero_hd_image == null) { Debug.LogWarning("[SettlementController] mvp_hero_hd_image is not assigned"); return; } int topIndex = -1; int[] scores = new int[5]; if (sm != null) { scores[0] = sm.red_pmScore_sum; scores[1] = sm.green_pmScore_sum; scores[2] = sm.yellow_pmScore_sum; scores[3] = sm.purple_pmScore_sum; scores[4] = sm.blue_pmScore_sum; } else { // fallback: read AllyCombatant.currentScore from scene for (int i = 0; i < 5; i++) { var go = GameObject.Find($"ally_0{ i+1 }"); if (go != null) { var ac = go.GetComponent(); scores[i] = ac != null ? ac.currentScore : 0; } else scores[i] = 0; } } int max = -1; for (int i = 0; i < scores.Length; i++) { if (scores[i] > max) { max = scores[i]; topIndex = i; } } if (topIndex < 0 || max <= 0) { Debug.Log("[SettlementController] No top scorer found or scores are all zero"); // hide image mvp_hero_hd_image.sprite = null; mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 0f); return; } // Try to resolve AllyHero_SO for the winning slot AllyHero_SO heroSO = null; // try SkillBuilder cache if (SkillBuilder.Instance != null) { try { heroSO = SkillBuilder.Instance.GetAllyHeroSOBySlot(topIndex); } catch { heroSO = null; } } // fallback: use teamUIController slot ids to find hero id -> load SO if (heroSO == null && teamUIController.Instance != null && teamUIController.Instance.allySlotIds != null) { int allyId = -1; if (topIndex >= 0 && topIndex < teamUIController.Instance.allySlotIds.Count) allyId = teamUIController.Instance.allySlotIds[topIndex]; if (allyId > 0) { var arr = Resources.LoadAll(""); foreach (var a in arr) { if (a != null && a.ally_heroID == allyId) { heroSO = a; break; } } } } // final fallback: try direct Resources lookup by scanning all and picking first non-null for slot if (heroSO == null) { var arr = Resources.LoadAll(""); if (arr != null && arr.Length > 0) { // attempt to match by name or just pick first heroSO = arr[0] as AllyHero_SO; } } if (heroSO != null && heroSO.ally_hero_HD_image != null) { mvp_hero_hd_image.sprite = heroSO.ally_hero_HD_image; mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 1f); Debug.Log($"[SettlementController] MVP assigned from slot {topIndex+1} with score {max}: {heroSO.ally_heroName}"); } else { mvp_hero_hd_image.sprite = null; mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 0f); Debug.LogWarning("[SettlementController] MVP SO or HD image not found"); } } }