| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace MazeGen
- {
- /// <summary>
- /// 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).
- /// </summary>
- 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; }
- /// <summary>The exit the agent walked through to reach CurrentRoom, if any (null for the starting room). Used by AutonomousAgentBrain to avoid immediately reversing.</summary>
- public Exit LastUsedExit { get; private set; }
- /// <summary>This agent's character sheet (stats, equipment, inventory).</summary>
- public CharacterSheet CharacterSheet { get; private set; }
- /// <summary>
- /// 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.
- /// </summary>
- public bool IsDead { get; private set; }
- /// <summary>
- /// 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.
- /// </summary>
- public bool IsEngaged { get; set; }
- /// <summary>What this agent is thinking - decisions, peeks, fight blow-by-blow. Observation only.</summary>
- public AgentThoughtLog Thoughts { get; } = new AgentThoughtLog();
- /// <summary>Name to show a human: the generated character name, not the GameObject name.</summary>
- public string DisplayName => CharacterSheet?.Name ?? Name;
- private readonly List<Room> _path = new List<Room>();
- private readonly HashSet<int> _visitedRoomIds = new HashSet<int>();
- private readonly HashSet<int> _usedExitIds = new HashSet<int>();
- private readonly Dictionary<int, int> _visitCounts = new Dictionary<int, int>();
- private readonly HashSet<int> _fledRoomIds = new HashSet<int>();
- public IReadOnlyList<Room> PathHistory => _path;
- public event Action<Room> RoomEntered;
- public event Action<Room> 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);
- /// <summary>
- /// 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.
- /// </summary>
- public void NoteFledFrom(int roomId) => _fledRoomIds.Add(roomId);
- public bool HasFledFrom(int roomId) => _fledRoomIds.Contains(roomId);
- /// <summary>How many times this agent has personally entered the given room (0 if never).</summary>
- 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);
- }
- }
- /// <summary>Called when the agent dies (defeated in combat or other hazard). Stops them from moving.</summary>
- 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();
- }
- }
- }
|