using UnityEngine;
using UnityEngine.Events;
using TMPro;
/// <summary>
/// GAME 1303 Tier 4 — Times the run, shows it, and saves the best. Zap Attack's time
/// attack; optional for Cutthroat Course.
///
/// SETUP
/// 1. Empty GameObject "LevelTimer" with this script (or put it on your HUD).
/// 2. Level Id: must match the LevelData asset for this scene, or the best time saves
/// under the wrong key. Leave blank to use the scene name.
/// 3. Drag in TMP texts for the running timer and the best time (both optional).
/// 4. Mode:
/// CountUp – classic speedrun timer.
/// CountDown – set Start Time; OnTimedOut fires at zero (wire to Health.Kill or
/// GameManager.GameOver). Nicholas explicitly wants NO time limit,
/// so his stays CountUp or off entirely.
/// 5. Stopping: wire LevelExit.OnCompleted -> LevelTimer.StopAndRecord. That saves the
/// best time and fires OnNewRecord if it beat the old one.
/// 6. Auto Start runs from scene load; otherwise call StartTimer() (a starting-line trigger).
///
/// The timer pauses automatically while GameManager is paused, because it uses
/// Time.deltaTime and pausing sets timeScale to 0.
/// </summary>
public class LevelTimer : MonoBehaviour
{
public enum Mode { CountUp, CountDown }
public Mode mode = Mode.CountUp;
[Tooltip("Match the LevelData asset's Level Id. Blank = use the scene name.")]
public string levelId = "";
public bool autoStart = true;
[Tooltip("CountDown mode: seconds on the clock.")]
public float startTime = 60f;
[Header("Display (optional)")]
public TMP_Text timerText;
public TMP_Text bestTimeText;
public string bestTimePrefix = "Best: ";
[Tooltip("Turn the timer red under this many seconds (CountDown).")]
public float warningThreshold = 10f;
public Color warningColor = Color.red;
[Header("Events")]
public UnityEvent OnTimedOut;
public UnityEvent<float> OnNewRecord; // the new best time
public UnityEvent<float> OnStopped; // final time either way
public float Time_ { get; private set; }
public bool IsRunning { get; private set; }
private Color normalColor = Color.white;
private bool timedOut;
void Awake()
{
if (string.IsNullOrEmpty(levelId))
levelId = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
if (timerText != null) normalColor = timerText.color;
}
void Start()
{
Time_ = mode == Mode.CountDown ? startTime : 0f;
ShowBest();
Display();
if (autoStart) StartTimer();
}
void Update()
{
if (!IsRunning) return;
Time_ += (mode == Mode.CountDown ? -1f : 1f) * Time.deltaTime;
if (mode == Mode.CountDown && Time_ <= 0f)
{
Time_ = 0f;
IsRunning = false;
timedOut = true;
OnTimedOut?.Invoke();
}
Display();
}
public void StartTimer()
{
timedOut = false;
IsRunning = true;
}
public void PauseTimer() => IsRunning = false;
/// <summary>Wire LevelExit.OnCompleted here.</summary>
public void StopAndRecord()
{
if (timedOut) return;
IsRunning = false;
float finalTime = mode == Mode.CountDown ? startTime - Time_ : Time_;
bool record = LevelProgress.IsNewRecord(levelId, finalTime);
LevelProgress.MarkCleared(levelId);
LevelProgress.RecordTime(levelId, finalTime);
ShowBest();
if (record) OnNewRecord?.Invoke(finalTime);
OnStopped?.Invoke(finalTime);
}
void Display()
{
if (timerText == null) return;
timerText.text = Format(Time_);
timerText.color = (mode == Mode.CountDown && Time_ <= warningThreshold) ? warningColor : normalColor;
}
void ShowBest()
{
if (bestTimeText == null) return;
float best = LevelProgress.GetBestTime(levelId);
bestTimeText.text = best < 0f ? bestTimePrefix + "--:--.--" : bestTimePrefix + Format(best);
}
/// <summary>mm:ss.hh — the standard speedrun readout.</summary>
public static string Format(float t)
{
if (t < 0f) t = 0f;
int minutes = (int)(t / 60f);
int seconds = (int)(t % 60f);
int hundredths = (int)((t * 100f) % 100f);
return $"{minutes:00}:{seconds:00}.{hundredths:00}";
}
}