using System; using System.Collections.Generic; using UnityEngine; namespace MazeGen { /// /// Tracks one agent's progress through the maze: current room, ordered path history /// (for visualization/backtracking), and O(1) sets for "have I been here" / "have I /// used this exit" checks. Plain C# class - no MonoBehaviour/scene dependency, so it's /// cheap to run many of these (e.g. for future autonomous multi-agent scenarios). /// /// Also tracks the agent's combat state (character sheet, health, equipment). /// public class MazeAgent { public readonly string Name; public Room CurrentRoom { get; private set; } public int NewRoomsVisited { get; private set; } public bool IsFinished { get; private set; } /// The exit the agent walked through to reach CurrentRoom, if any (null for the starting room). Used by AutonomousAgentBrain to avoid immediately reversing. public Exit LastUsedExit { get; private set; } /// This agent's character sheet (stats, equipment, inventory). public CharacterSheet CharacterSheet { get; private set; } /// /// Track if this agent died (different from IsFinished, which means reached the exit). /// Backed by its own flag rather than derived from health so that Die() stays a /// once-only, event-firing transition even though combat has already zeroed the health /// by the time it's called. /// public bool IsDead { get; private set; } /// /// True while this agent is committed to a fight (mustering or being resolved). Its brain /// parks itself so it can't wander off out of a fight it already joined. /// public bool IsEngaged { get; set; } /// What this agent is thinking - decisions, peeks, fight blow-by-blow. Observation only. public AgentThoughtLog Thoughts { get; } = new AgentThoughtLog(); /// Name to show a human: the generated character name, not the GameObject name. public string DisplayName => CharacterSheet?.Name ?? Name; private readonly List _path = new List(); private readonly HashSet _visitedRoomIds = new HashSet(); private readonly HashSet _usedExitIds = new HashSet(); private readonly Dictionary _visitCounts = new Dictionary(); private readonly HashSet _fledRoomIds = new HashSet(); public IReadOnlyList PathHistory => _path; public event Action RoomEntered; public event Action ReachedExitRoom; public event Action Died; public MazeAgent(GameObject go, Room startRoom, CharacterSheet characterSheet = null) { go.tag = "Agent"; Name = go.name; CharacterSheet = characterSheet ?? new CharacterSheet(go, new CharacterStats()); EnterRoom(startRoom, null); } public bool HasVisited(int roomId) => _visitedRoomIds.Contains(roomId); public bool HasUsedExit(Exit exit) => _usedExitIds.Contains(exit.Id); /// /// Remembers that a fight in this room went nowhere (a stalemate), so the brain leaves /// instead of re-declaring the same unwinnable fight every decision tick forever. /// public void NoteFledFrom(int roomId) => _fledRoomIds.Add(roomId); public bool HasFledFrom(int roomId) => _fledRoomIds.Contains(roomId); /// How many times this agent has personally entered the given room (0 if never). public int VisitCount(int roomId) => _visitCounts.TryGetValue(roomId, out var count) ? count : 0; public void EnterRoom(Room room, Exit viaExit) { // Check if agent is already dead before entering if (IsDead) return; if (viaExit != null) _usedExitIds.Add(viaExit.Id); if (_visitedRoomIds.Add(room.Id)) NewRoomsVisited++; _visitCounts[room.Id] = VisitCount(room.Id) + 1; CurrentRoom = room; LastUsedExit = viaExit; _path.Add(room); RoomEntered?.Invoke(room); if (room.Kind == RoomKind.Exit) { IsFinished = true; Thoughts.Add(ThoughtKind.Goal, $"Reached the exit room {room.Id} - done, {NewRoomsVisited} room(s) seen."); ReachedExitRoom?.Invoke(room); } } /// Called when the agent dies (defeated in combat or other hazard). Stops them from moving. public void Die() { if (IsDead) return; IsDead = true; IsEngaged = false; CharacterSheet.Stats.CurrentHealth = 0; Thoughts.Add(ThoughtKind.Goal, $"Died in room {CurrentRoom?.Id.ToString() ?? "?"}."); Died?.Invoke(); } } }