MazeAgent.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace MazeGen
  5. {
  6. /// <summary>
  7. /// Tracks one agent's progress through the maze: current room, ordered path history
  8. /// (for visualization/backtracking), and O(1) sets for "have I been here" / "have I
  9. /// used this exit" checks. Plain C# class - no MonoBehaviour/scene dependency, so it's
  10. /// cheap to run many of these (e.g. for future autonomous multi-agent scenarios).
  11. ///
  12. /// Also tracks the agent's combat state (character sheet, health, equipment).
  13. /// </summary>
  14. public class MazeAgent
  15. {
  16. public readonly string Name;
  17. public Room CurrentRoom { get; private set; }
  18. public int NewRoomsVisited { get; private set; }
  19. public bool IsFinished { get; private set; }
  20. /// <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>
  21. public Exit LastUsedExit { get; private set; }
  22. /// <summary>This agent's character sheet (stats, equipment, inventory).</summary>
  23. public CharacterSheet CharacterSheet { get; private set; }
  24. /// <summary>
  25. /// Track if this agent died (different from IsFinished, which means reached the exit).
  26. /// Backed by its own flag rather than derived from health so that Die() stays a
  27. /// once-only, event-firing transition even though combat has already zeroed the health
  28. /// by the time it's called.
  29. /// </summary>
  30. public bool IsDead { get; private set; }
  31. /// <summary>
  32. /// True while this agent is committed to a fight (mustering or being resolved). Its brain
  33. /// parks itself so it can't wander off out of a fight it already joined.
  34. /// </summary>
  35. public bool IsEngaged { get; set; }
  36. /// <summary>What this agent is thinking - decisions, peeks, fight blow-by-blow. Observation only.</summary>
  37. public AgentThoughtLog Thoughts { get; } = new AgentThoughtLog();
  38. /// <summary>Name to show a human: the generated character name, not the GameObject name.</summary>
  39. public string DisplayName => CharacterSheet?.Name ?? Name;
  40. private readonly List<Room> _path = new List<Room>();
  41. private readonly HashSet<int> _visitedRoomIds = new HashSet<int>();
  42. private readonly HashSet<int> _usedExitIds = new HashSet<int>();
  43. private readonly Dictionary<int, int> _visitCounts = new Dictionary<int, int>();
  44. private readonly HashSet<int> _fledRoomIds = new HashSet<int>();
  45. public IReadOnlyList<Room> PathHistory => _path;
  46. public event Action<Room> RoomEntered;
  47. public event Action<Room> ReachedExitRoom;
  48. public event Action Died;
  49. public MazeAgent(GameObject go, Room startRoom, CharacterSheet characterSheet = null)
  50. {
  51. go.tag = "Agent";
  52. Name = go.name;
  53. CharacterSheet = characterSheet ?? new CharacterSheet(go, new CharacterStats());
  54. EnterRoom(startRoom, null);
  55. }
  56. public bool HasVisited(int roomId) => _visitedRoomIds.Contains(roomId);
  57. public bool HasUsedExit(Exit exit) => _usedExitIds.Contains(exit.Id);
  58. /// <summary>
  59. /// Remembers that a fight in this room went nowhere (a stalemate), so the brain leaves
  60. /// instead of re-declaring the same unwinnable fight every decision tick forever.
  61. /// </summary>
  62. public void NoteFledFrom(int roomId) => _fledRoomIds.Add(roomId);
  63. public bool HasFledFrom(int roomId) => _fledRoomIds.Contains(roomId);
  64. /// <summary>How many times this agent has personally entered the given room (0 if never).</summary>
  65. public int VisitCount(int roomId) => _visitCounts.TryGetValue(roomId, out var count) ? count : 0;
  66. public void EnterRoom(Room room, Exit viaExit)
  67. {
  68. // Check if agent is already dead before entering
  69. if (IsDead)
  70. return;
  71. if (viaExit != null)
  72. _usedExitIds.Add(viaExit.Id);
  73. if (_visitedRoomIds.Add(room.Id))
  74. NewRoomsVisited++;
  75. _visitCounts[room.Id] = VisitCount(room.Id) + 1;
  76. CurrentRoom = room;
  77. LastUsedExit = viaExit;
  78. _path.Add(room);
  79. RoomEntered?.Invoke(room);
  80. if (room.Kind == RoomKind.Exit)
  81. {
  82. IsFinished = true;
  83. Thoughts.Add(ThoughtKind.Goal, $"Reached the exit room {room.Id} - done, {NewRoomsVisited} room(s) seen.");
  84. ReachedExitRoom?.Invoke(room);
  85. }
  86. }
  87. /// <summary>Called when the agent dies (defeated in combat or other hazard). Stops them from moving.</summary>
  88. public void Die()
  89. {
  90. if (IsDead) return;
  91. IsDead = true;
  92. IsEngaged = false;
  93. CharacterSheet.Stats.CurrentHealth = 0;
  94. Thoughts.Add(ThoughtKind.Goal, $"Died in room {CurrentRoom?.Id.ToString() ?? "?"}.");
  95. Died?.Invoke();
  96. }
  97. }
  98. }