Files
bansonic_beta_main/Assets/scripts/gamePlay_gameplay/JudgeManager.cs
T

339 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JudgeManager : MonoBehaviour
{
[Header("settlement go")]
public GameObject settlement_go;
public settlementController sc;
public NoteSpawner ns;
private bool _cachedIsDebugEnabled;
private void UpdateDebugCache()
{
_cachedIsDebugEnabled = (_enableDebugLogs || GameConfig.verboseLogs);
}
[Header("Debug Settings")]
[SerializeField] private bool _enableDebugLogs = false;
public bool EnableDebugLogs => _enableDebugLogs;
public static bool IsDebugEnabled => (Instance != null && Instance._cachedIsDebugEnabled);
[Header("Global Note Settings")]
[Tooltip("Global material applied to hold notes when they are judged.")]
public Material globalJudgedMaterial;
[Tooltip("Whether to enable material switching after a hold note is judged.")]
public bool enableJudgedMaterial = false;
public static JudgeManager Instance { get; private set; }
// total/remaining notes tracking for end-of-song detection
private int totalNotes = 0;
private int remainingNotes = 0;
// ===== ԭ =====
private Dictionary<KeyCode, Queue<Note>> judgeQueues = new Dictionary<KeyCode, Queue<Note>>();
private Dictionary<string, bool> startJudgedNotes = new Dictionary<string, bool>();
private Dictionary<string, bool> releasedNotes = new Dictionary<string, bool>();
private Dictionary<string, float> noteEndTimes = new Dictionary<string, float>();
private Dictionary<string, int> middlePassedCounts = new Dictionary<string, int>();
// ===== ȫֻ״̬ =====
private class HoldJudgeState
{
public bool startResolved; // Start ǷѾжɹ or ʧܣ
public bool endResolved; // End ǷѾж
public bool skillTriggered; // ǷѴ
}
private Dictionary<string, HoldJudgeState> holdStates = new Dictionary<string, HoldJudgeState>();
private HoldJudgeState GetHoldState(string noteID)
{
if (!holdStates.TryGetValue(noteID, out var state))
{
state = new HoldJudgeState();
holdStates[noteID] = state;
}
return state;
}
// Public event and hook invoked when the entire beatmap's last note has been judged.
// The method is intentionally empty so you can override or call it via the singleton.
public event Action AllNotesJudged;
/// <summary>
/// Public empty hook called when all notes have been judged. Implement your end-of-song logic here
/// or subscribe to the AllNotesJudged event. This method is intentionally left empty.
/// </summary>
public void OnAllNotesJudged()
{
if (settlement_go == null)
{
if (IsDebugEnabled) Debug.LogError("结算页面不存在!");
return;
}
// Activate settlement UI so components run their lifecycle, then call update next frame
try
{
settlement_go.SetActive(true);
}
catch (Exception ex)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] Failed to activate settlement_go: {ex}");
}
// Try to resolve settlementController reference if missing
if (sc == null && settlement_go != null)
{
sc = settlement_go.GetComponent<settlementController>() ?? settlement_go.GetComponentInChildren<settlementController>(true);
}
// Invoke the settlement update on next frame to ensure UI initialization finished
StartCoroutine(InvokeSettlementUpdateNextFrame());
}
private IEnumerator InvokeSettlementUpdateNextFrame()
{
yield return null; // wait one frame to allow Awake/Start/OnEnable
if (sc != null)
{
try
{
sc.startSettlement_uiUpdate();
}
catch (Exception ex)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] Exception calling startSettlement_uiUpdate: {ex}");
}
}
else
{
if (IsDebugEnabled) Debug.LogWarning("[JudgeManager] settlementController (sc) not found on settlement_go; cannot call startSettlement_uiUpdate().");
}
}
/// <summary>
/// Helper to invoke the AllNotesJudged event and call the public hook.
/// Call this when you detect the last note was judged.
/// </summary>
public void TriggerAllNotesJudged()
{
try
{
AllNotesJudged?.Invoke();
}
catch (Exception ex)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] Exception while invoking AllNotesJudged event: {ex}");
}
try
{
OnAllNotesJudged();
}
catch (Exception ex)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] Exception in OnAllNotesJudged hook: {ex}");
}
}
private void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
UpdateDebugCache();
if (settlement_go != null) settlement_go.SetActive(false);
}
/// <summary>
/// Set the total number of notes in the current beatmap. Remaining notes will be initialized
/// to this value. Call before playback/spawning starts.
/// </summary>
public void SetTotalNotes(int total)
{
totalNotes = Mathf.Max(0, total);
remainingNotes = totalNotes;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] SetTotalNotes: total={totalNotes}");
}
/// <summary>
/// Notify that one note has been finally judged (hit or miss). When remaining reaches zero,
/// TriggerAllNotesJudged will be invoked.
/// </summary>
public void NotifyNoteJudged()
{
if (remainingNotes <= 0) return;
remainingNotes = Mathf.Max(0, remainingNotes - 1);
if (IsDebugEnabled) Debug.Log($"[JudgeManager] NotifyNoteJudged: remaining={remainingNotes}");
if (remainingNotes == 0)
{
if (IsDebugEnabled) Debug.Log("[JudgeManager] All notes judged -> Triggering AllNotesJudged");
TriggerAllNotesJudged();
}
}
// ===== жխ =====
public bool TryResolveStart(string noteID)
{
var s = GetHoldState(noteID);
if (s.startResolved)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] TryResolveStart: already resolved start for {noteID}");
return false;
}
s.startResolved = true;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] TryResolveStart: start resolved for {noteID} at time={Time.time:F3}");
return true;
}
public bool TryResolveEnd(string noteID)
{
var s = GetHoldState(noteID);
if (s.endResolved)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] TryResolveEnd: already resolved end for {noteID}");
return false;
}
s.endResolved = true;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] TryResolveEnd: end resolved for {noteID} at time={Time.time:F3}");
return true;
}
public bool TryTriggerSkill(string noteID)
{
var s = GetHoldState(noteID);
if (s.skillTriggered)
{
if (IsDebugEnabled) Debug.LogWarning($"[JudgeManager] TryTriggerSkill: already triggered for {noteID}");
return false;
}
s.skillTriggered = true;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] TryTriggerSkill: skill triggered for {noteID} at time={Time.time:F3}");
return true;
}
public void ClearHoldState(string noteID)
{
if (holdStates.ContainsKey(noteID))
{
holdStates.Remove(noteID);
if (IsDebugEnabled) Debug.Log($"[JudgeManager] ClearHoldState: cleared state for {noteID}");
}
}
// ===== ԭнӿڣֲ䣩 =====
public void RegisterScheduledEndTime(string noteID, float endTime)
{
noteEndTimes[noteID] = endTime;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] RegisterScheduledEndTime: {noteID} -> {endTime:F3}");
}
public float GetScheduledEndTime(string noteID)
{
return noteEndTimes.TryGetValue(noteID, out var endTime) ? endTime : 0f;
}
public void RegisterStartJudged(string noteID, bool state)
{
startJudgedNotes[noteID] = state;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] RegisterStartJudged: {noteID} = {state} at time={Time.time:F3}");
}
public bool IsStartJudged(string noteID)
{
return startJudgedNotes.TryGetValue(noteID, out var state) && state;
}
public void RegisterNoteReleased(string noteID, bool state)
{
releasedNotes[noteID] = state;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] RegisterNoteReleased: {noteID} = {state}");
}
public bool HasNoteReleased(string noteID)
{
return releasedNotes.TryGetValue(noteID, out var state) && state;
}
public void RegisterNote(KeyCode key, Note note)
{
if (!judgeQueues.ContainsKey(key))
judgeQueues[key] = new Queue<Note>();
if (!judgeQueues[key].Contains(note))
judgeQueues[key].Enqueue(note);
if (IsDebugEnabled) Debug.Log($"[JudgeManager] RegisterNote: key={key} note={note.name} time={Time.time:F3} queueSize={judgeQueues[key].Count}");
}
public void UnregisterNote(KeyCode key, Note note)
{
if (!judgeQueues.ContainsKey(key)) return;
Queue<Note> newQueue = new Queue<Note>();
while (judgeQueues[key].Count > 0)
{
Note n = judgeQueues[key].Dequeue();
if (n != note)
newQueue.Enqueue(n);
else if (!n.IsJudged())
n.JudgeMiss();
}
judgeQueues[key] = newQueue;
if (IsDebugEnabled) Debug.Log($"[JudgeManager] UnregisterNote: key={key} removed {note.name}, newQueueSize={judgeQueues[key].Count}");
}
public void JudgeEarliestNote(KeyCode key)
{
if (judgeQueues.ContainsKey(key) && judgeQueues[key].Count > 0)
{
Note note = judgeQueues[key].Dequeue();
if (note != null && !note.IsJudged())
{
if (IsDebugEnabled) Debug.Log($"[JudgeManager] JudgeEarliestNote: judging {note.name} for key={key} at time={Time.time:F3}");
note.Judge();
}
}
}
public void RegisterMiddlePassed(string noteID)
{
if (middlePassedCounts.TryGetValue(noteID, out int count))
{
middlePassedCounts[noteID] = count + 1;
}
else
{
middlePassedCounts[noteID] = 1;
}
if (IsDebugEnabled) Debug.Log($"[JudgeManager] Middle passed for {noteID}, total={middlePassedCounts[noteID]}");
}
// Event for syncing alpha across segments of a hold note
public event Action<string, float> OnHoldAlphaSync;
public void SyncHoldAlpha(string noteID, float alpha)
{
OnHoldAlphaSync?.Invoke(noteID, alpha);
}
public int GetMiddlePassedCount(string noteID)
{
return middlePassedCounts.TryGetValue(noteID, out var count) ? count : 0;
}
public void ClearNoteRecord(string noteID)
{
startJudgedNotes.Remove(noteID);
releasedNotes.Remove(noteID);
noteEndTimes.Remove(noteID);
middlePassedCounts.Remove(noteID);
ClearHoldState(noteID);
if (IsDebugEnabled) Debug.Log($"[JudgeManager] ClearNoteRecord: cleared records for {noteID}");
}
}