using UnityEngine; using System.Collections.Generic; using System.Linq; /// /// Tracks active fights between runners and monsters /// Provides fight data for UI display and camera zoom operations /// public class FightTracker : MonoBehaviour { [SerializeField] private float fightDisplayDuration = 3f; // How long to display fights after they end // Internal fight tracking structure private class ActiveFight { public AIAgent runner; public Monster monster; public MazeRoom room; public float startTime; public float endTime = float.MaxValue; // While fighting, this is MaxValue public bool isActive => endTime == float.MaxValue; } private List activeFights = new(); private Dictionary<(AIAgent, Monster), ActiveFight> fightLookup = new(); private static FightTracker instance; public static FightTracker Instance { get { if (instance == null) { var go = new GameObject("FightTracker"); instance = go.AddComponent(); } return instance; } } /// /// Called when a runner starts fighting a monster /// public void StartFight(AIAgent runner, Monster monster) { if (runner == null || monster == null) return; if (runner.IsDead || monster.IsDead) return; var key = (runner, monster); if (fightLookup.ContainsKey(key)) return; // Already tracking this fight var fight = new ActiveFight { runner = runner, monster = monster, room = monster.HomeRoom, startTime = Time.time }; activeFights.Add(fight); fightLookup[key] = fight; } /// /// Called when a fight ends (runner or monster dies, or they separate) /// public void EndFight(AIAgent runner, Monster monster) { if (runner == null || monster == null) return; var key = (runner, monster); if (fightLookup.TryGetValue(key, out var fight)) { fight.endTime = Time.time; fightLookup.Remove(key); } } /// /// Gets all fights that should currently be displayed (active + recent) /// public List GetDisplayFights() { var displayFights = new List(); // Clean up old completed fights and null entries activeFights.RemoveAll(f => { // Remove if either fighter was destroyed if (f.runner == null || f.monster == null) return true; float timeSinceEnd = Time.time - f.endTime; return !f.isActive && timeSinceEnd > fightDisplayDuration; }); // Convert to display format and sort foreach (var fight in activeFights) { // Skip if either fighter is null (shouldn't happen due to RemoveAll, but safe) if (fight.runner == null || fight.monster == null) continue; displayFights.Add(new FightData { runnerName = fight.runner.AgentName, runnerId = fight.runner.AgentId, monsterType = $"Monster_{fight.monster.GetHashCode()}", room = fight.room, isActive = fight.isActive, timeSinceEnd = fight.isActive ? 0f : Time.time - fight.endTime, runner = fight.runner, monster = fight.monster }); } // Sort by number of participants in same room (descending), then by active status displayFights = displayFights .OrderByDescending(f => CountFightsInRoom(f.room)) .ThenByDescending(f => f.isActive) .ToList(); return displayFights; } /// /// Count how many fights are happening in a specific room /// private int CountFightsInRoom(MazeRoom room) { return activeFights.Count(f => f.room.Id == room.Id); } /// /// Gets total number of fighters in a room (runners + monsters) /// public int GetFighterCountInRoom(MazeRoom room) { var fightersInRoom = activeFights.Where(f => f.room.Id == room.Id).ToList(); var uniqueRunners = new HashSet(fightersInRoom.Select(f => f.runner)); var uniqueMonsters = new HashSet(fightersInRoom.Select(f => f.monster)); return uniqueRunners.Count + uniqueMonsters.Count; } void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else if (instance != this) { Destroy(gameObject); } } void Update() { // Periodically clean up dead fighters activeFights.RemoveAll(f => f.runner == null || f.runner.IsDead || f.monster == null || f.monster.IsDead); } } /// /// Display-friendly fight data /// public struct FightData { public string runnerName; public int runnerId; public string monsterType; public MazeRoom room; public bool isActive; public float timeSinceEnd; public AIAgent runner; public Monster monster; public string GetDisplayText() { return $"{runnerName} vs {monsterType}"; } public string GetRoomInfo() { return $"Room {room.Id}"; } }