فهرست منبع

Generated maze with somewhat performance

Axel Nordh 1 ماه پیش
والد
کامیت
f9b4eae875
55فایلهای تغییر یافته به همراه4274 افزوده شده و 433 حذف شده
  1. 6 0
      .claudeignore
  2. 8 3
      Assets/Scenes/SampleScene.unity
  3. 245 0
      Assets/Scripts/Maze/Agent/AutonomousAgentBrain.cs
  4. 2 0
      Assets/Scripts/Maze/Agent/AutonomousAgentBrain.cs.meta
  5. 36 1
      Assets/Scripts/Maze/Agent/MazeAgent.cs
  6. 18 6
      Assets/Scripts/Maze/Agent/MazeAgentController.cs
  7. 9 4
      Assets/Scripts/Maze/Agent/MazeGenerationSettings-1.asset
  8. 36 0
      Assets/Scripts/Maze/Camera/MazeCameraController.cs
  9. 8 0
      Assets/Scripts/Maze/Combat.meta
  10. 234 0
      Assets/Scripts/Maze/Combat/ARCHITECTURE.md
  11. 7 0
      Assets/Scripts/Maze/Combat/ARCHITECTURE.md.meta
  12. 296 0
      Assets/Scripts/Maze/Combat/COPY_PASTE_EXAMPLES.cs
  13. 2 0
      Assets/Scripts/Maze/Combat/COPY_PASTE_EXAMPLES.cs.meta
  14. 165 0
      Assets/Scripts/Maze/Combat/CharacterSheet.cs
  15. 2 0
      Assets/Scripts/Maze/Combat/CharacterSheet.cs.meta
  16. 74 0
      Assets/Scripts/Maze/Combat/CharacterStats.cs
  17. 2 0
      Assets/Scripts/Maze/Combat/CharacterStats.cs.meta
  18. 195 0
      Assets/Scripts/Maze/Combat/CombatResolver.cs
  19. 2 0
      Assets/Scripts/Maze/Combat/CombatResolver.cs.meta
  20. 208 0
      Assets/Scripts/Maze/Combat/IMPLEMENTATION_CHECKLIST.md
  21. 7 0
      Assets/Scripts/Maze/Combat/IMPLEMENTATION_CHECKLIST.md.meta
  22. 242 0
      Assets/Scripts/Maze/Combat/INTEGRATION_EXAMPLE.md
  23. 7 0
      Assets/Scripts/Maze/Combat/INTEGRATION_EXAMPLE.md.meta
  24. 8 0
      Assets/Scripts/Maze/Combat/Items.meta
  25. 44 0
      Assets/Scripts/Maze/Combat/Items/Armor.cs
  26. 2 0
      Assets/Scripts/Maze/Combat/Items/Armor.cs.meta
  27. 38 0
      Assets/Scripts/Maze/Combat/Items/Item.cs
  28. 2 0
      Assets/Scripts/Maze/Combat/Items/Item.cs.meta
  29. 45 0
      Assets/Scripts/Maze/Combat/Items/Weapon.cs
  30. 2 0
      Assets/Scripts/Maze/Combat/Items/Weapon.cs.meta
  31. 8 0
      Assets/Scripts/Maze/Combat/Monsters.meta
  32. 62 0
      Assets/Scripts/Maze/Combat/Monsters/Monster.cs
  33. 2 0
      Assets/Scripts/Maze/Combat/Monsters/Monster.cs.meta
  34. 134 0
      Assets/Scripts/Maze/Combat/Monsters/MonsterEncounter.cs
  35. 2 0
      Assets/Scripts/Maze/Combat/Monsters/MonsterEncounter.cs.meta
  36. 71 0
      Assets/Scripts/Maze/Combat/QUICK_START.md
  37. 7 0
      Assets/Scripts/Maze/Combat/QUICK_START.md.meta
  38. 207 0
      Assets/Scripts/Maze/Combat/README.md
  39. 7 0
      Assets/Scripts/Maze/Combat/README.md.meta
  40. 33 0
      Assets/Scripts/Maze/Combat/RoomEncounterConfig.cs
  41. 2 0
      Assets/Scripts/Maze/Combat/RoomEncounterConfig.cs.meta
  42. 72 10
      Assets/Scripts/Maze/Core/MazeGraph.cs
  43. 6 0
      Assets/Scripts/Maze/Core/Room.cs
  44. 9 1
      Assets/Scripts/Maze/Generation/MazeGenerationSettings.cs
  45. 49 14
      Assets/Scripts/Maze/Generation/MazeGenerator.cs
  46. 12 0
      Assets/Scripts/Maze/Input/MazeInput.cs
  47. 6 8
      Assets/Scripts/Maze/Visualization/ExitMarker.cs
  48. 212 52
      Assets/Scripts/Maze/Visualization/MazeSceneBuilder.cs
  49. 11 3
      Assets/Settings/Mobile_RPAsset.asset
  50. 103 0
      CLAUDE.md
  51. 429 0
      COMBAT_REFERENCE.md
  52. 252 0
      COMBAT_SYSTEM_INDEX.md
  53. 290 0
      COMBAT_SYSTEM_SUMMARY.md
  54. 60 58
      UserSettings/Layouts/CurrentMaximizeLayout.dwlt
  55. 276 273
      UserSettings/Layouts/default-6000.dwlt

+ 6 - 0
.claudeignore

@@ -0,0 +1,6 @@
+Library/
+Temp/
+Logs/
+Packages/
+ProjectSettings/
+*.meta

+ 8 - 3
Assets/Scenes/SampleScene.unity

@@ -413,8 +413,13 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: 1f0deb6ed9b595d46a4e43d7c07dbf57, type: 3}
   m_Name: 
   m_EditorClassIdentifier: Assembly-CSharp::MazeGen.MazeSceneBuilder
-  startRoomCount: 1
-  startRoomSpread: 6
+  agentCount: 30
+  spawnStaggerRange: {x: 0, y: 1}
+  initialDecisionDelayRange: {x: 0.5, y: 2}
+  decisionInterval: 1.5
+  exploreChance: 0.85
+  targetFinishedAgentCount: 20
+  timeLimitSeconds: 150
 --- !u!114 &569980086
 MonoBehaviour:
   m_ObjectHideFlags: 0
@@ -427,7 +432,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: 0bb2e0efd912cb74aa730c7d85bf6b43, type: 3}
   m_Name: 
   m_EditorClassIdentifier: Assembly-CSharp::MazeGen.MazeGenerator
-  settings: {fileID: 0}
+  settings: {fileID: 11400000, guid: 0e7402fed2df46c499f9da60cf76b358, type: 2}
 --- !u!4 &569980087
 Transform:
   m_ObjectHideFlags: 0

+ 245 - 0
Assets/Scripts/Maze/Agent/AutonomousAgentBrain.cs

@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Drives one agent's autonomous exploration: waits out a (randomized) initial delay so
+    /// many agents don't all decide in lockstep, then ticks a steady decision interval -
+    /// each time it fires, picks an exit and asks the MazeAgentController to use it. Stops
+    /// deciding once the agent reaches an exit room or the level has ended.
+    ///
+    /// Agents can "peek" into adjacent rooms before entering to see if there are monsters.
+    /// Based on the perceived threat level, they may choose to retreat rather than fight.
+    /// </summary>
+    public class AutonomousAgentBrain : MonoBehaviour
+    {
+        private MazeAgentController _controller;
+        private MazeGenerator _generator;
+        private float _decisionInterval;
+        private float _exploreChance;
+        private float _retreatThreshold = 1.5f;     // CR above this = retreat (configurable)
+        private Func<bool> _isLevelEnded;
+
+        private float _timer;
+        private Dictionary<int, int> _peekCount = new Dictionary<int, int>(); // Track how many times agent peeked at a room
+
+        public void Initialize(MazeAgentController controller, MazeGenerator generator, float initialDelay, float decisionInterval, float exploreChance, float retreatThreshold, Func<bool> isLevelEnded)
+        {
+            _controller = controller;
+            _generator = generator;
+            _decisionInterval = Mathf.Max(0.05f, decisionInterval);
+            _exploreChance = Mathf.Clamp01(exploreChance);
+            _retreatThreshold = Mathf.Max(0, retreatThreshold);
+            _isLevelEnded = isLevelEnded;
+            _timer = Mathf.Max(0f, initialDelay);
+        }
+
+        private void Update()
+        {
+            if (_controller?.Agent == null || _controller.Agent.IsFinished || _controller.Agent.IsDead) return;
+            if (_isLevelEnded != null && _isLevelEnded()) return;
+
+            _timer -= Time.deltaTime;
+            if (_timer > 0f) return;
+            _timer = _decisionInterval;
+
+            Exit choice = ChooseExit(_controller.Agent, _controller.Agent.CurrentRoom);
+            if (choice != null)
+                _controller.TryUseExit(choice);
+        }
+
+        /// <summary>
+        /// Picks the next exit to take. Strongly prefers an unused exit (fresh, unexplored
+        /// territory); failing that, prefers a used exit into a room this agent personally
+        /// hasn't visited yet (following where another agent has already been); only
+        /// backtracks into a room it's already visited as a last resort. `exploreChance`
+        /// adds some randomness so a "follow" opportunity is occasionally taken even when
+        /// exploring is available, rather than being 100% deterministic.
+        ///
+        /// A few anti-stuck rules keep this from looping forever around a small ring of
+        /// already-explored rooms (e.g. a square formed by a loop-closing connection):
+        /// - The door leading straight back to the room the agent just came from is excluded
+        ///   from consideration unless it's the only door available at all, so agents don't
+        ///   ping-pong between two rooms.
+        /// - When forced to backtrack, exits that "peek" into a room with its own unexplored
+        ///   option beyond it (an unused exit, or one leading somewhere this agent hasn't
+        ///   personally seen) are preferred over ones that only lead deeper into fully-picked-
+        ///   over territory - this steers the agent back out of a dead loop instead of just
+        ///   circling it.
+        /// - Ties are broken toward whichever room this agent has personally visited the
+        ///   fewest times, so a small loop gets cycled through rather than bounced around.
+        ///
+        /// Also evaluates monster threats in adjacent rooms - if a room looks too dangerous,
+        /// the agent will retreat (choose a different exit) if possible.
+        /// </summary>
+        private Exit ChooseExit(MazeAgent agent, Room room)
+        {
+            Exit reverseExit = FindReverseExit(agent, room);
+
+            var explore = new List<Exit>();
+            var follow = new List<Exit>();
+            var backtrack = new List<Exit>();
+
+            foreach (var exit in room.Exits)
+            {
+                if (exit == reverseExit) continue;
+
+                if (!exit.IsUsed)
+                {
+                    explore.Add(exit);
+                    continue;
+                }
+
+                bool leadsToVisited = exit.ConnectedRoomId.HasValue && agent.HasVisited(exit.ConnectedRoomId.Value);
+                (leadsToVisited ? backtrack : follow).Add(exit);
+            }
+
+            // Filter exits by threat level - remove dangerous ones if safer options exist
+            explore = FilterByThreat(explore, agent);
+            follow = FilterByThreat(follow, agent);
+            backtrack = FilterByThreat(backtrack, agent);
+
+            if (explore.Count > 0 && UnityEngine.Random.value < _exploreChance)
+                return RandomPick(explore);
+
+            if (follow.Count > 0)
+                return LeastVisitedPick(agent, follow);
+
+            if (explore.Count > 0)
+                return RandomPick(explore);
+
+            if (backtrack.Count > 0)
+                return ChooseBacktrack(agent, backtrack);
+
+            // Excluding the reverse door left nothing - it's genuinely the only way out
+            // (e.g. a single-exit dead end), so take it (even if dangerous).
+            return reverseExit;
+        }
+
+        /// <summary>The door in `room` that leads straight back to wherever the agent's last move came from, if any.</summary>
+        private static Exit FindReverseExit(MazeAgent agent, Room room)
+        {
+            int? reverseExitId = agent.LastUsedExit?.ConnectedExitId;
+            if (!reverseExitId.HasValue) return null;
+            return room.Exits.Find(e => e.Id == reverseExitId.Value);
+        }
+
+        private Exit ChooseBacktrack(MazeAgent agent, List<Exit> backtrack)
+        {
+            var promising = backtrack.Where(e => LeadsTowardUnexploredOptions(agent, e)).ToList();
+            return LeastVisitedPick(agent, promising.Count > 0 ? promising : backtrack);
+        }
+
+        /// <summary>
+        /// One-step lookahead: does the room beyond `exit` itself have any door (other than
+        /// the one leading straight back here) that's still unused or leads somewhere this
+        /// agent hasn't personally been? If so, backtracking this way still has somewhere
+        /// useful to go; if not, it just leads deeper into already-picked-over territory.
+        /// </summary>
+        private bool LeadsTowardUnexploredOptions(MazeAgent agent, Exit exit)
+        {
+            if (!exit.ConnectedRoomId.HasValue) return false;
+
+            Room target = _generator.Graph.Get(exit.ConnectedRoomId.Value);
+            if (target == null) return false;
+
+            int reverseExitId = exit.ConnectedExitId ?? -1;
+            foreach (var candidate in target.Exits)
+            {
+                if (candidate.Id == reverseExitId) continue;
+                if (!candidate.IsUsed) return true;
+                if (candidate.ConnectedRoomId.HasValue && !agent.HasVisited(candidate.ConnectedRoomId.Value)) return true;
+            }
+            return false;
+        }
+
+        private static Exit LeastVisitedPick(MazeAgent agent, List<Exit> exits)
+        {
+            int bestVisits = int.MaxValue;
+            var bestExits = new List<Exit>();
+
+            foreach (var exit in exits)
+            {
+                int visits = exit.ConnectedRoomId.HasValue ? agent.VisitCount(exit.ConnectedRoomId.Value) : 0;
+                if (visits < bestVisits)
+                {
+                    bestVisits = visits;
+                    bestExits.Clear();
+                    bestExits.Add(exit);
+                }
+                else if (visits == bestVisits)
+                {
+                    bestExits.Add(exit);
+                }
+            }
+
+            return RandomPick(bestExits);
+        }
+
+        private static Exit RandomPick(List<Exit> exits) => exits[UnityEngine.Random.Range(0, exits.Count)];
+
+        /// <summary>
+        /// Peek into a room (via an exit) to see if there are monsters and how many.
+        /// Returns the perceived challenge rating, or 0 if no monsters/already explored.
+        /// Agent sees incomplete info (60-100% of actual monsters, doesn't know about hidden ones).
+        /// </summary>
+        public float PeekThroughExit(Exit exit, MazeAgent agent)
+        {
+            if (!exit.ConnectedRoomId.HasValue)
+                return 0; // Unresolved exit - can't peek yet
+
+            Room nextRoom = _generator.Graph.Get(exit.ConnectedRoomId.Value);
+            if (nextRoom == null || nextRoom.Encounter == null)
+                return 0; // No encounter in this room
+
+            return nextRoom.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet);
+        }
+
+        /// <summary>
+        /// Decide if an exit is worth taking based on threat level inside.
+        /// Returns true if the exit is relatively safe or if the agent has no choice,
+        /// false if the agent decides to retreat from this exit.
+        /// </summary>
+        private bool ShouldTakeExit(Exit exit, MazeAgent agent)
+        {
+            if (!exit.ConnectedRoomId.HasValue)
+                return true; // Unresolved exit - proceed cautiously
+
+            Room nextRoom = _generator.Graph.Get(exit.ConnectedRoomId.Value);
+            if (nextRoom == null || nextRoom.Encounter == null)
+                return true; // No encounter, safe to enter
+
+            // Peek and evaluate threat
+            float perceivedCR = nextRoom.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet);
+
+            // If threat is below threshold, take it. If above, retreat with some randomness.
+            if (perceivedCR <= _retreatThreshold)
+                return true; // Manageable threat
+
+            // Higher difficulty = more likely to retreat, but add randomness for personality
+            float retreatChance = Mathf.Clamp01((perceivedCR - _retreatThreshold) / 2.0f);
+            return UnityEngine.Random.value > retreatChance;
+        }
+
+        /// <summary>
+        /// Filter exits: remove those the agent wants to retreat from due to high threat.
+        /// This is called during exit selection so dangerous rooms are avoided when possible.
+        /// </summary>
+        private List<Exit> FilterByThreat(List<Exit> exits, MazeAgent agent)
+        {
+            var safeExits = new List<Exit>();
+
+            foreach (var exit in exits)
+            {
+                if (ShouldTakeExit(exit, agent))
+                    safeExits.Add(exit);
+            }
+
+            // If all exits are dangerous, agent must take one anyway (don't get stuck)
+            return safeExits.Count > 0 ? safeExits : exits;
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Agent/AutonomousAgentBrain.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: afe515cb2454f584f92583b4eb91a3d3

+ 36 - 1
Assets/Scripts/Maze/Agent/MazeAgent.cs

@@ -8,45 +8,80 @@ namespace MazeGen
     /// (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).</summary>
+        public bool IsDead => !CharacterSheet.Stats.IsAlive;
 
         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>();
 
         public IReadOnlyList<Room> PathHistory => _path;
 
         public event Action<Room> RoomEntered;
         public event Action<Room> ReachedExitRoom;
+        public event Action Died;
 
-        public MazeAgent(string name, Room startRoom)
+        public MazeAgent(string name, Room startRoom, CharacterSheet characterSheet = null)
         {
             Name = name;
+            CharacterSheet = characterSheet ?? new CharacterSheet(new CharacterStats());
             EnterRoom(startRoom, null);
         }
 
         public bool HasVisited(int roomId) => _visitedRoomIds.Contains(roomId);
         public bool HasUsedExit(Exit exit) => _usedExitIds.Contains(exit.Id);
 
+        /// <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;
                 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;
+            CharacterSheet.Stats.CurrentHealth = 0;
+            Died?.Invoke();
         }
     }
 }

+ 18 - 6
Assets/Scripts/Maze/Agent/MazeAgentController.cs

@@ -8,7 +8,12 @@ namespace MazeGen
     /// </summary>
     public class MazeAgentController : MonoBehaviour
     {
+        private const float BaseScale = 0.5f;
+        private const float FollowedScale = 0.7f;
+
         private MazeGenerator _generator;
+        private SpriteRenderer _sprite;
+
         public MazeAgent Agent { get; private set; }
 
         public void Initialize(MazeGenerator generator)
@@ -16,17 +21,24 @@ namespace MazeGen
             _generator = generator;
         }
 
-        public void Spawn(Room startRoom)
+        public void Spawn(Room startRoom, Color color)
         {
             Agent = new MazeAgent(gameObject.name, startRoom);
             transform.position = startRoom.Position;
-            transform.localScale = Vector3.one * 0.5f;
+            transform.localScale = Vector3.one * BaseScale;
             Agent.RoomEntered += room => transform.position = room.Position;
 
-            var sr = gameObject.AddComponent<SpriteRenderer>();
-            sr.sprite = RoomView.SharedSquareSprite;
-            sr.color = Color.white;
-            sr.sortingOrder = 3;
+            _sprite = gameObject.AddComponent<SpriteRenderer>();
+            _sprite.sprite = RoomView.SharedSquareSprite;
+            _sprite.color = color;
+            _sprite.sortingOrder = 3;
+        }
+
+        /// <summary>Visually calls out this agent as the one the camera is currently following.</summary>
+        public void SetFollowed(bool followed)
+        {
+            transform.localScale = Vector3.one * (followed ? FollowedScale : BaseScale);
+            if (_sprite != null) _sprite.sortingOrder = followed ? 5 : 3;
         }
 
         /// <summary>

+ 9 - 4
Assets/Scripts/Maze/Agent/MazeGenerationSettings-1.asset

@@ -14,14 +14,19 @@ MonoBehaviour:
   m_EditorClassIdentifier: Assembly-CSharp::MazeGen.MazeGenerationSettings
   minExitsPerRoom: 1
   maxExitsPerRoom: 4
-  minExitAngleSeparation: 35
+  startRoomCount: 4
+  startRoomSpread: 20
   roomSpacing: 4
   startRoomRadius: 1.2
   normalRoomRadius: 1
   hallwayRadius: 0.5
   exitRoomRadius: 1.2
   hallwayChance: 0.25
-  mergeAngleTolerance: 50
-  minNewRoomsBeforeExitEligible: 5
+  hallwayClearance: 0.1
+  repositionRingStep: 1
+  repositionSamplesPerRing: 12
+  maxRepositionRings: 10
+  minNewRoomsBeforeExitEligible: 20
   exitRoomChanceOnceEligible: 0.2
-  forceExitAfterNewRooms: 20
+  forceExitAfterNewRooms: 50
+  maxExitRooms: 0

+ 36 - 0
Assets/Scripts/Maze/Camera/MazeCameraController.cs

@@ -12,6 +12,9 @@ namespace MazeGen
         [SerializeField] private float panSpeed = 8f;
         [SerializeField] private float focusSharpness = 6f;
         [SerializeField] private float dragSensitivity = 1f;
+        [SerializeField] private float zoomSpeed = 1.5f;
+        [SerializeField] private float minOrthographicSize = 2f;
+        [SerializeField] private float maxOrthographicSize = 60f;
 
         private Camera _camera;
         private Vector2? _focusTarget;
@@ -37,13 +40,46 @@ namespace MazeGen
             transform.position = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
         }
 
+        /// <summary>
+        /// Instantly repositions and (if orthographic) zooms out/in so the whole of `bounds`
+        /// is visible, e.g. for a "zoom to fit maze" button. Cancels any in-progress
+        /// auto-focus and does not itself start following anything.
+        /// </summary>
+        public void FrameBounds(Bounds bounds, float padding = 1.2f)
+        {
+            _focusTarget = null;
+
+            Vector2 center = bounds.center;
+            transform.position = new Vector3(center.x, center.y, transform.position.z);
+
+            if (_camera != null && _camera.orthographic)
+            {
+                float aspect = Mathf.Max(_camera.aspect, 0.01f);
+                float requiredHalfHeight = Mathf.Max(bounds.extents.y, bounds.extents.x / aspect);
+                _camera.orthographicSize = Mathf.Max(requiredHalfHeight * padding, 1f);
+            }
+        }
+
         private void Update()
         {
             HandleDrag();
             HandleKeyboardPan();
+            HandleScrollZoom();
             HandleAutoFocus();
         }
 
+        private void HandleScrollZoom()
+        {
+            if (_camera == null || !_camera.orthographic) return;
+
+            float scroll = MazeInput.GetScrollDelta();
+            if (Mathf.Approximately(scroll, 0f)) return;
+
+            _camera.orthographicSize = Mathf.Clamp(
+                _camera.orthographicSize - scroll * zoomSpeed,
+                minOrthographicSize, maxOrthographicSize);
+        }
+
         private void HandleKeyboardPan()
         {
             Vector2 input = MazeInput.ReadWasdAxis();

+ 8 - 0
Assets/Scripts/Maze/Combat.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a4968ebb99be0ef4f8dd293b76487e9f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 234 - 0
Assets/Scripts/Maze/Combat/ARCHITECTURE.md

@@ -0,0 +1,234 @@
+# Combat System Architecture
+
+## Class Relationships
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                    MazeAgent (Agent.cs)                         │
+│  ┌──────────────────────────────────────────────────────────┐   │
+│  │ + CurrentRoom: Room                                      │   │
+│  │ + IsFinished: bool                                       │   │
+│  │ + IsDead: bool                                           │   │
+│  │ + CharacterSheet: CharacterSheet ◄──┐                   │   │
+│  │ + PathHistory: List<Room>            │                   │   │
+│  │ + HasVisited(roomId): bool           │                   │   │
+│  └──────────────────────────────────────┼───────────────────┘   │
+│                                         │                        │
+└─────────────────────────────────────────┼────────────────────────┘
+                                          │
+                                          │ owns
+                                          ▼
+        ┌──────────────────────────────────────────────────────────┐
+        │            CharacterSheet (CharacterSheet.cs)            │
+        │  ┌─────────────────────────────────────────────────────┐ │
+        │  │ - baseStats: CharacterStats                         │ │
+        │  │ - equippedWeapon: Weapon◄──────┐                   │ │
+        │  │ - equippedArmor: Armor◄────┐   │                   │ │
+        │  │ - inventory: List<Item>     │   │                   │ │
+        │  │ - tempBonuses: Dict         │   │                   │ │
+        │  │                             │   │                   │ │
+        │  │ + GetEffectiveAttack()      │   │                   │ │
+        │  │ + GetEffectiveDefense()     │   │                   │ │
+        │  │ + GetEffectiveSpeed()       │   │                   │ │
+        │  │ + EquipWeapon()             │   │                   │ │
+        │  │ + EquipArmor()              │   │                   │ │
+        │  │ + AddToInventory()          │   │                   │ │
+        │  │ + ApplyTemporaryBonus()     │   │                   │ │
+        │  └─────────────────────────────────┼───────────────────┘ │
+        │                                    │                      │
+        └────────────────────────────────────┼──────────────────────┘
+                                             │
+                            ┌────────────────┴────────────────┐
+                            │                                 │
+                            ▼                                 ▼
+        ┌──────────────────────────────┐  ┌──────────────────────────┐
+        │     CharacterStats           │  │        Item (base)        │
+        │  (CharacterStats.cs)         │  │   (Items/Item.cs)        │
+        │  ┌────────────────────────┐  │  │  ┌────────────────────┐   │
+        │  │ - maxHealth            │  │  │  │ - itemName         │   │
+        │  │ - speed                │  │  │  │ - description      │   │
+        │  │ - attack               │  │  │  │ - icon             │   │
+        │  │ - defense              │  │  │  │ - rarity           │   │
+        │  │ - magic                │  │  │  │                    │   │
+        │  │ - currentHealth        │  │  │  │ + OnEquip()        │   │
+        │  │                        │  │  │  │ + OnUnequip()      │   │
+        │  │ + Clone()              │  │  │  │ + GetLabel()       │   │
+        │  │ + TakeDamage()         │  │  │  └────────────────────┘   │
+        │  │ + IsAlive              │  │  │           ▲                │
+        │  └────────────────────────┘  │  │           │                │
+        │                              │  │    ┌──────┴──────┐         │
+        └──────────────────────────────┘  │    │             │         │
+                                          │    ▼             ▼         │
+                                          │ ┌────────┐  ┌────────┐    │
+                                          │ │Weapon  │  │Armor   │    │
+                                          │ │(Items/ │  │(Items/ │    │
+                                          │ │Weapon) │  │Armor)  │    │
+                                          │ └────────┘  └────────┘    │
+                                          └──────────────────────────┘
+
+┌──────────────────────────────────────────────────────────────────────┐
+│                Room (Core/Room.cs)                                   │
+│  + Encounter: MonsterEncounter ◄──┐                                 │
+│  + Exits: List<Exit>              │                                 │
+│  + Position: Vector2              │                                 │
+│  + DepthFromStart: int            │                                 │
+└──────────────────────────────────────────────────────────────────────┘
+                                    │ has
+                                    │
+                                    ▼
+        ┌──────────────────────────────────────────────────────┐
+        │    MonsterEncounter (Monsters/MonsterEncounter.cs)  │
+        │  ┌───────────────────────────────────────────────┐  │
+        │  │ - monsters: List<MonsterInstance>             │  │
+        │  │ - visibleMonsterCount: int (incomplete info)  │  │
+        │  │                                               │  │
+        │  │ + SpawnMonsters(templates, min, max)          │  │
+        │  │ + GetPerceivedChallengeRating()               │  │
+        │  │ + GetActualChallengeRating()                  │  │
+        │  │ + HasLivingMonsters: bool                     │  │
+        │  │ + AliveMonsterCount: int                      │  │
+        │  └───────────────────────────────────────────────┘  │
+        │           │                                          │
+        │           │ contains                                 │
+        │           ▼                                          │
+        │  ┌──────────────────────────────────────────────┐   │
+        │  │    MonsterInstance                          │   │
+        │  │  ┌──────────────────────────────────────┐   │   │
+        │  │  │ + Template: Monster                  │   │   │
+        │  │  │ + CharacterSheet: CharacterSheet     │   │   │
+        │  │  │ + IsDefeated: bool                   │   │   │
+        │  │  └──────────────────────────────────────┘   │   │
+        │  └──────────────────────────────────────────────┘   │
+        │           │                                          │
+        │           │ uses                                     │
+        │           ▼                                          │
+        │  ┌──────────────────────────────────────────────┐   │
+        │  │       Monster (Monsters/Monster.cs)         │   │
+        │  │    [ScriptableObject Template]              │   │
+        │  │  ┌──────────────────────────────────────┐   │   │
+        │  │  │ + monsterName: string                │   │   │
+        │  │  │ + baseStats: CharacterStats          │   │   │
+        │  │  │ + startingWeapon: Weapon (optional)  │   │   │
+        │  │  │ + startingArmor: Armor (optional)    │   │   │
+        │  │  │ + possibleLoot: Item[]               │   │   │
+        │  │  │                                      │   │   │
+        │  │  │ + CreateCharacterSheet()             │   │   │
+        │  │  │ + GetRandomLoot()                    │   │   │
+        │  │  └──────────────────────────────────────┘   │   │
+        │  └──────────────────────────────────────────────┘   │
+        │                                                      │
+        └──────────────────────────────────────────────────────┘
+
+┌──────────────────────────────────────────────────────────────────────┐
+│   AutonomousAgentBrain (Agent/AutonomousAgentBrain.cs)              │
+│  ┌──────────────────────────────────────────────────────────────┐   │
+│  │ - _controller: MazeAgentController                           │   │
+│  │ - _retreatThreshold: float                                   │   │
+│  │                                                              │   │
+│  │ + ChooseExit(agent, room): Exit                             │   │
+│  │ + PeekThroughExit(exit, agent): float [CR]                  │   │
+│  │ + ShouldTakeExit(exit, agent): bool                         │   │
+│  │ + FilterByThreat(exits, agent): List<Exit>                  │   │
+│  │                                                              │   │
+│  │ PEEK MECHANIC:                                              │   │
+│  │ 1. Before choosing an exit, peek into adjacent rooms        │   │
+│  │ 2. Get perceived challenge rating (incomplete info)         │   │
+│  │ 3. Compare to retreat threshold                             │   │
+│  │ 4. Avoid dangerous exits if safer options exist             │   │
+│  └──────────────────────────────────────────────────────────────┘   │
+└──────────────────────────────────────────────────────────────────────┘
+
+┌──────────────────────────────────────────────────────────────────────┐
+│   CombatResolver (CombatResolver.cs) [SKELETON - EXPAND ME]         │
+│  ┌──────────────────────────────────────────────────────────────┐   │
+│  │ + ResolveCombat(agent, encounter): CombatOutcome            │   │
+│  │                                                              │   │
+│  │ - BuildTurnOrder(agent, encounter): List<Turn>              │   │
+│  │ - ExecuteAgentTurn(agent, encounter)                        │   │
+│  │ - ExecuteMonsterTurn(monster, agent, encounter)             │   │
+│  │ - AttackMonster(agent, encounter, targetIdx)                │   │
+│  │ - AttackAgent(monster, agent)                               │   │
+│  │ - AwardLoot(monster, agent)                                 │   │
+│  │                                                              │   │
+│  │ TODO: Full implementation with:                             │   │
+│  │ - Turn order calculation                                    │   │
+│  │ - Multiple damage types                                     │   │
+│  │ - Special abilities                                         │   │
+│  │ - Status effects                                            │   │
+│  │ - AI decision making                                        │   │
+│  └──────────────────────────────────────────────────────────────┘   │
+└──────────────────────────────────────────────────────────────────────┘
+```
+
+## Data Flow
+
+### 1. Agent Encounters a Room with Monsters
+
+```
+Agent.EnterRoom(room)
+  └─> room.Encounter exists?
+      └─> Yes: Trigger combat
+          └─> CombatResolver.ResolveCombat(agent, encounter)
+              ├─> BuildTurnOrder()
+              ├─> Loop: ExecuteAgentTurn() / ExecuteMonsterTurn()
+              ├─> Return: Victory / Defeat / Fleeing
+              └─> Apply outcome (agent dies / loot awarded / etc.)
+      └─> No: Continue normally
+```
+
+### 2. Agent Makes Decision (Peek & Retreat)
+
+```
+AutonomousAgentBrain.ChooseExit()
+  └─> For each exit:
+      ├─> PeekThroughExit()
+      │   └─> room.Encounter.GetPerceivedChallengeRating()
+      └─> ShouldTakeExit()
+          ├─> If CR < retreatThreshold: take it
+          └─> Else: maybe retreat (randomness)
+  └─> FilterByThreat() removes dangerous exits
+  └─> Return safest valid exit
+```
+
+### 3. Item Equipping Flow
+
+```
+CharacterSheet.EquipWeapon(weapon)
+  ├─> Weapon.OnUnequip() old weapon (if any)
+  │   └─> RemoveTemporaryBonus()
+  ├─> Weapon.OnEquip(this)
+  │   └─> ApplyTemporaryBonus("weapon_attack", bonus)
+  └─> GetEffectiveAttack() now includes bonus
+```
+
+## Stat Calculation Example
+
+```
+Agent with:
+  - Base Attack: 10
+  - Equipped Weapon: +5 Attack bonus
+  - Temporary Buff: +3 Attack
+
+GetEffectiveAttack() = 10 + 5 + 3 = 18
+```
+
+Defense reduction calculation:
+```
+Monster attacks for 20 base damage
+Agent defense: 5
+Defense reduction: min(10% × 5, 90%) = 50%
+Actual damage taken: 20 × (1 - 0.5) = 10
+```
+
+## Missing Pieces (You'll Implement)
+
+1. **Combat UI** — Show health bars, stats, action buttons
+2. **Ability System** — Special attacks, spells, items with effects
+3. **Status Effects** — Poison, stun, buff/debuff system
+4. **Item Pickup** — Drop loot when monsters die
+5. **Inventory UI** — Manage equipment, consumables
+6. **Audio/VFX** — Combat feedback (hit sounds, damage numbers)
+7. **AI** — Monster targeting, ability selection
+8. **Difficulty Scaling** — Scale encounters by depth or agent power
+
+See `CombatResolver.cs` for where to add these.

+ 7 - 0
Assets/Scripts/Maze/Combat/ARCHITECTURE.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 3611740e61ff6a9468ecf2b02566de1a
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 296 - 0
Assets/Scripts/Maze/Combat/COPY_PASTE_EXAMPLES.cs

@@ -0,0 +1,296 @@
+// This file contains copy-paste examples for setting up the combat system.
+// Replace YOUR_NAMESPACE with your actual namespace.
+
+using UnityEngine;
+using MazeGen;
+
+namespace MazeGen
+{
+    // EXAMPLE 1: Creating a weapon in editor via script
+    [System.Obsolete("This is just an example - create weapons via Create > Weapon in editor instead")]
+    public class WeaponCreationExample : MonoBehaviour
+    {
+        public void CreateExampleWeapon()
+        {
+            var weapon = ScriptableObject.CreateInstance<Weapon>();
+            weapon.name = "Iron Sword";
+            // Note: You'll need to expose attack bonus as public or via reflection to set it
+            // Best practice: create via editor!
+        }
+    }
+
+    // EXAMPLE 2: Creating a monster programmatically
+    public class MonsterCreationExample : MonoBehaviour
+    {
+        public Monster CreateGoblin()
+        {
+            var goblin = ScriptableObject.CreateInstance<Monster>();
+            goblin.name = "Goblin";
+
+            // Create stats
+            var stats = new CharacterStats();
+            // stats fields are private - you'd need public setters or reflection
+            // Best practice: create via editor!
+
+            return goblin;
+        }
+    }
+
+    // EXAMPLE 3: Setting up agents with character sheets in MazeSceneBuilder
+    public class AgentSetupExample : MonoBehaviour
+    {
+        private Weapon[] _startingWeapons;
+        private Armor[] _startingArmor;
+
+        private void LoadResources()
+        {
+            // Load weapon templates
+            _startingWeapons = Resources.LoadAll<Weapon>("Items/Weapons/");
+            if (_startingWeapons.Length == 0)
+                Debug.LogWarning("No weapons found in Resources/Items/Weapons/");
+
+            // Load armor templates
+            _startingArmor = Resources.LoadAll<Armor>("Items/Armor/");
+            if (_startingArmor.Length == 0)
+                Debug.LogWarning("No armor found in Resources/Items/Armor/");
+        }
+
+        private MazeAgent CreateAgentWithEquipment(string name, Room startRoom)
+        {
+            // Create base stats
+            var stats = new CharacterStats();
+            var sheet = new CharacterSheet(stats);
+
+            // Equip weapon
+            if (_startingWeapons.Length > 0)
+            {
+                var weapon = _startingWeapons[Random.Range(0, _startingWeapons.Length)];
+                sheet.EquipWeapon(weapon);
+            }
+
+            // Equip armor
+            if (_startingArmor.Length > 0)
+            {
+                var armor = _startingArmor[Random.Range(0, _startingArmor.Length)];
+                sheet.EquipArmor(armor);
+            }
+
+            // Create agent with character sheet
+            var agent = new MazeAgent(name, startRoom, sheet);
+            return agent;
+        }
+    }
+
+    // EXAMPLE 4: Spawning encounters when generating rooms
+    public class EncounterSpawningExample : MonoBehaviour
+    {
+        private Monster[] _monsterPool;
+        private float _encounterChance = 0.7f;
+        private int _minMonstersPerRoom = 1;
+        private int _maxMonstersPerRoom = 3;
+
+        private void LoadMonsters()
+        {
+            _monsterPool = Resources.LoadAll<Monster>("Monsters/");
+            if (_monsterPool.Length == 0)
+                Debug.LogWarning("No monsters found in Resources/Monsters/");
+        }
+
+        private void SpawnEncounterInRoom(Room room)
+        {
+            // Don't spawn in start or exit rooms
+            if (room.Kind == RoomKind.Start || room.Kind == RoomKind.Exit)
+                return;
+
+            // Random chance to spawn
+            if (Random.value > _encounterChance)
+                return;
+
+            if (_monsterPool.Length == 0)
+                return;
+
+            // Create and populate encounter
+            var encounter = new MonsterEncounter();
+            encounter.SpawnMonsters(
+                _monsterPool,
+                _minMonstersPerRoom,
+                _maxMonstersPerRoom
+            );
+
+            room.Encounter = encounter;
+
+            Debug.Log($"Spawned encounter in {room.Id} with {encounter.AliveMonsterCount} monsters");
+        }
+    }
+
+    // EXAMPLE 5: Triggering combat when agent enters a room
+    public class CombatTriggerExample : MonoBehaviour
+    {
+        private MazeAgent _agent;
+
+        private void UpdateAgentCombat()
+        {
+            // Check if agent entered a room with monsters
+            if (_agent.CurrentRoom.Encounter != null &&
+                _agent.CurrentRoom.Encounter.HasLivingMonsters &&
+                !_agent.IsDead)
+            {
+                ResolveCombat();
+            }
+        }
+
+        private void ResolveCombat()
+        {
+            var outcome = CombatResolver.ResolveCombat(_agent, _agent.CurrentRoom.Encounter);
+
+            switch (outcome)
+            {
+                case CombatResolver.CombatOutcome.AgentVictory:
+                    Debug.Log($"{_agent.Name} won!");
+                    // TODO: Award loot
+                    // TODO: Update UI
+                    break;
+
+                case CombatResolver.CombatOutcome.AgentDefeated:
+                    Debug.Log($"{_agent.Name} was defeated!");
+                    // Agent is now dead - they stop automatically
+                    // TODO: Update UI (show death)
+                    break;
+
+                case CombatResolver.CombatOutcome.Fleeing:
+                    Debug.Log($"{_agent.Name} fled!");
+                    // TODO: Force retreat back through entrance
+                    break;
+            }
+        }
+    }
+
+    // EXAMPLE 6: Peeking at a room's threat level
+    public class PeekingExample : MonoBehaviour
+    {
+        private MazeAgent _agent;
+        private AutonomousAgentBrain _brain;
+
+        private void CheckThreatOfAdjacentRooms()
+        {
+            foreach (var exit in _agent.CurrentRoom.Exits)
+            {
+                float threatLevel = _brain.PeekThroughExit(exit, _agent);
+                string threatDescription = threatLevel < 0.5f ? "Safe" :
+                                          threatLevel < 1.5f ? "Moderate" :
+                                          threatLevel < 3.0f ? "Dangerous" : "Deadly";
+
+                Debug.Log($"Exit leads to {threatDescription} room (CR: {threatLevel:F2})");
+            }
+        }
+    }
+
+    // EXAMPLE 7: Setting up weapon with specific bonuses (for reference)
+    [System.Obsolete("Example only - create Weapons via editor")]
+    public class WeaponBonusExample : MonoBehaviour
+    {
+        private void ExampleWeaponBonuses()
+        {
+            // When you create a Weapon, these are the fields:
+            // - itemName: "Iron Sword"
+            // - description: "A sturdy iron sword"
+            // - icon: (sprite reference)
+            // - rarity: 1 (common)
+            // - attackBonus: 5.0f
+            // - magicBonus: 0.0f (no magic effect)
+            // - defenseBonus: 1.0f (slight parry bonus)
+            // - speedBonus: 0.0f (no speed effect)
+
+            // When equipped, applies:
+            // - +5 attack
+            // - +1 defense
+            // Modifiers stack with other equipment
+        }
+    }
+
+    // EXAMPLE 8: Armor with penalties
+    [System.Obsolete("Example only - create Armor via editor")]
+    public class ArmorExample : MonoBehaviour
+    {
+        private void ExampleArmorStats()
+        {
+            // Heavy Armor example:
+            // - defenseBonus: 10.0f (good protection)
+            // - healthBonus: 5.0f (extra durability)
+            // - speedPenalty: 2.0f (slow movement)
+            // - magicResistance: 0.0f (no special resistances)
+
+            // Light Armor example:
+            // - defenseBonus: 3.0f (basic protection)
+            // - healthBonus: 0.0f
+            // - speedPenalty: 0.0f (no penalty)
+            // - magicResistance: 2.0f (magic resistance)
+        }
+    }
+
+    // EXAMPLE 9: Initializing AutonomousAgentBrain with retreat settings
+    public class BrainInitializationExample : MonoBehaviour
+    {
+        private void InitBrainBrave(AutonomousAgentBrain brain, MazeAgentController controller, MazeGenerator gen)
+        {
+            // Brave agent - will enter dangerous rooms
+            brain.Initialize(
+                controller,
+                gen,
+                initialDelay: 0.5f,
+                decisionInterval: 0.5f,
+                exploreChance: 0.8f,
+                retreatThreshold: 3.0f, // Very high threshold - rarely retreats
+                isLevelEnded: () => false
+            );
+        }
+
+        private void InitBrainCautious(AutonomousAgentBrain brain, MazeAgentController controller, MazeGenerator gen)
+        {
+            // Cautious agent - avoids danger
+            brain.Initialize(
+                controller,
+                gen,
+                initialDelay: 0.5f,
+                decisionInterval: 0.5f,
+                exploreChance: 0.5f,
+                retreatThreshold: 0.8f, // Low threshold - retreats often
+                isLevelEnded: () => false
+            );
+        }
+
+        private void InitBrainNormal(AutonomousAgentBrain brain, MazeAgentController controller, MazeGenerator gen)
+        {
+            // Balanced agent
+            brain.Initialize(
+                controller,
+                gen,
+                initialDelay: 0.5f,
+                decisionInterval: 0.5f,
+                exploreChance: 0.7f,
+                retreatThreshold: 1.5f, // Medium threshold
+                isLevelEnded: () => false
+            );
+        }
+    }
+
+    // EXAMPLE 10: Monster loot table setup (in editor)
+    [System.Obsolete("Example only - create Monsters via editor")]
+    public class MonsterLootExample : MonoBehaviour
+    {
+        private void ExampleLootTables()
+        {
+            // Goblin's loot table might include:
+            // [0] Common Dagger
+            // [1] Leather Armor
+            // [2] Health Potion
+            // [3] (empty for "no drop" chance)
+
+            // Boss monster's loot table might include:
+            // [0] Rare Sword
+            // [1] Legendary Armor
+            // [2] Magic Scroll
+            // (no empty slots - always drops something)
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/COPY_PASTE_EXAMPLES.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4cee38a05c8c8cf4a8af1e1620e1f5d3

+ 165 - 0
Assets/Scripts/Maze/Combat/CharacterSheet.cs

@@ -0,0 +1,165 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Represents a character's complete combat state: base stats, equipped items,
+    /// temporary bonuses, and inventory. Both agents and monsters use this.
+    /// </summary>
+    public class CharacterSheet
+    {
+        private CharacterStats _baseStats;
+        private Weapon _equippedWeapon;
+        private Armor _equippedArmor;
+        private List<Item> _inventory = new List<Item>();
+
+        // Temporary stat bonuses (key = source name, value = bonus amount)
+        private Dictionary<string, float> _attackBonuses = new Dictionary<string, float>();
+        private Dictionary<string, float> _defenseBonuses = new Dictionary<string, float>();
+        private Dictionary<string, float> _magicBonuses = new Dictionary<string, float>();
+        private Dictionary<string, float> _speedBonuses = new Dictionary<string, float>();
+        private Dictionary<string, float> _healthBonuses = new Dictionary<string, float>();
+
+        public CharacterStats Stats => _baseStats;
+        public Weapon EquippedWeapon => _equippedWeapon;
+        public Armor EquippedArmor => _equippedArmor;
+        public IReadOnlyList<Item> Inventory => _inventory.AsReadOnly();
+
+        public CharacterSheet(CharacterStats baseStats)
+        {
+            _baseStats = baseStats;
+        }
+
+        /// <summary>
+        /// Get the effective attack stat including all bonuses.
+        /// This is what's actually used in combat calculations.
+        /// </summary>
+        public float GetEffectiveAttack()
+        {
+            float bonus = 0;
+            foreach (var b in _attackBonuses.Values) bonus += b;
+            return _baseStats.Attack + bonus;
+        }
+
+        /// <summary>Get effective defense after all bonuses.</summary>
+        public float GetEffectiveDefense()
+        {
+            float bonus = 0;
+            foreach (var b in _defenseBonuses.Values) bonus += b;
+            return _baseStats.Defense + bonus;
+        }
+
+        /// <summary>Get effective magic after all bonuses.</summary>
+        public float GetEffectiveMagic()
+        {
+            float bonus = 0;
+            foreach (var b in _magicBonuses.Values) bonus += b;
+            return _baseStats.Magic + bonus;
+        }
+
+        /// <summary>Get effective speed (affects initiative and movement).</summary>
+        public float GetEffectiveSpeed()
+        {
+            float bonus = 0;
+            foreach (var b in _speedBonuses.Values) bonus += b;
+            return Mathf.Max(0.1f, _baseStats.Speed + bonus);
+        }
+
+        /// <summary>Get effective max health including armor bonuses.</summary>
+        public float GetEffectiveMaxHealth()
+        {
+            float bonus = 0;
+            foreach (var b in _healthBonuses.Values) bonus += b;
+            return _baseStats.MaxHealth + bonus;
+        }
+
+        /// <summary>Equip a weapon (unequips the previous one).</summary>
+        public bool EquipWeapon(Weapon weapon)
+        {
+            if (_equippedWeapon != null)
+                _equippedWeapon.OnUnequip(this);
+
+            if (weapon != null && !weapon.OnEquip(this))
+                return false;
+
+            _equippedWeapon = weapon;
+            return true;
+        }
+
+        /// <summary>Equip armor (unequips the previous armor).</summary>
+        public bool EquipArmor(Armor armor)
+        {
+            if (_equippedArmor != null)
+                _equippedArmor.OnUnequip(this);
+
+            if (armor != null && !armor.OnEquip(this))
+                return false;
+
+            _equippedArmor = armor;
+            return true;
+        }
+
+        /// <summary>Add an item to inventory without equipping it.</summary>
+        public void AddToInventory(Item item)
+        {
+            if (item != null && !_inventory.Contains(item))
+                _inventory.Add(item);
+        }
+
+        /// <summary>Apply a temporary stat bonus (e.g., from a weapon, buff, or potion).</summary>
+        public void ApplyTemporaryBonus(string key, float attackBonus = 0, float defenseBonus = 0, float magicBonus = 0, float speedBonus = 0, float healthBonus = 0)
+        {
+            if (attackBonus != 0)
+                _attackBonuses[key] = attackBonus;
+            if (defenseBonus != 0)
+                _defenseBonuses[key] = defenseBonus;
+            if (magicBonus != 0)
+                _magicBonuses[key] = magicBonus;
+            if (speedBonus != 0)
+                _speedBonuses[key] = speedBonus;
+            if (healthBonus != 0)
+                _healthBonuses[key] = healthBonus;
+        }
+
+        /// <summary>Overload for single-stat bonus (most common case).</summary>
+        public void ApplyTemporaryBonus(string key, float bonus, string statType = "attack")
+        {
+            switch (statType.ToLower())
+            {
+                case "attack": _attackBonuses[key] = bonus; break;
+                case "defense": _defenseBonuses[key] = bonus; break;
+                case "magic": _magicBonuses[key] = bonus; break;
+                case "speed": _speedBonuses[key] = bonus; break;
+                case "health": _healthBonuses[key] = bonus; break;
+            }
+        }
+
+        /// <summary>Remove a temporary bonus by its source key.</summary>
+        public void RemoveTemporaryBonus(string key)
+        {
+            _attackBonuses.Remove(key);
+            _defenseBonuses.Remove(key);
+            _magicBonuses.Remove(key);
+            _speedBonuses.Remove(key);
+            _healthBonuses.Remove(key);
+        }
+
+        /// <summary>Reset this character for a new fight (restore health, clear temporary bonuses, keep equipment).</summary>
+        public void ResetForNewFight()
+        {
+            _baseStats.RestoreHealth();
+            _attackBonuses.Clear();
+            _defenseBonuses.Clear();
+            _magicBonuses.Clear();
+            _speedBonuses.Clear();
+            _healthBonuses.Clear();
+
+            // Re-apply equipment bonuses
+            if (_equippedWeapon != null)
+                _equippedWeapon.OnEquip(this);
+            if (_equippedArmor != null)
+                _equippedArmor.OnEquip(this);
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/CharacterSheet.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 00f9f759cdca0614c9a9c38711bb9316

+ 74 - 0
Assets/Scripts/Maze/Combat/CharacterStats.cs

@@ -0,0 +1,74 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Core character/monster statistics. Stores both base stats and current values
+    /// (health can be reduced in combat, speed affects action order, attack/defense/magic
+    /// are used in combat resolution).
+    /// </summary>
+    [System.Serializable]
+    public class CharacterStats
+    {
+        [SerializeField] private float _maxHealth = 100f;
+        [SerializeField] private float _speed = 10f;           // Initiative and movement speed
+        [SerializeField] private float _attack = 10f;          // Physical damage output
+        [SerializeField] private float _defense = 5f;          // Physical damage reduction
+        [SerializeField] private float _magic = 5f;            // Magical power (casting damage/resistance)
+
+        private float _currentHealth;
+
+        public float MaxHealth => _maxHealth;
+        public float CurrentHealth
+        {
+            get => _currentHealth;
+            set => _currentHealth = Mathf.Clamp(value, 0, _maxHealth);
+        }
+        public float Speed => _speed;
+        public float Attack => _attack;
+        public float Defense => _defense;
+        public float Magic => _magic;
+
+        public bool IsAlive => _currentHealth > 0;
+
+        public CharacterStats()
+        {
+            _currentHealth = _maxHealth;
+        }
+
+        /// <summary>Create a copy of this stat block (useful for monster instances).</summary>
+        public CharacterStats Clone()
+        {
+            var clone = new CharacterStats
+            {
+                _maxHealth = _maxHealth,
+                _speed = _speed,
+                _attack = _attack,
+                _defense = _defense,
+                _magic = _magic,
+                _currentHealth = _currentHealth
+            };
+            return clone;
+        }
+
+        /// <summary>Reset health to max (for a fresh combat or new agent spawn).</summary>
+        public void RestoreHealth()
+        {
+            _currentHealth = _maxHealth;
+        }
+
+        /// <summary>Take damage (already accounting for defense).</summary>
+        public void TakeDamage(float damage)
+        {
+            CurrentHealth -= Mathf.Max(0, damage);
+        }
+
+        /// <summary>Calculate effective damage taken after defense reduction.</summary>
+        public float CalculateDamageReduction(float incomingDamage)
+        {
+            // Simple formula: defense reduces damage by 10% per defense point, up to 90% reduction
+            float reductionPercent = Mathf.Clamp01(_defense * 0.1f);
+            return incomingDamage * (1f - reductionPercent);
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/CharacterStats.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 8039fde98a48fa24b96ae3a00d46dec1

+ 195 - 0
Assets/Scripts/Maze/Combat/CombatResolver.cs

@@ -0,0 +1,195 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Skeleton for resolving combat between an agent and monsters in a room.
+    /// This is NOT a full implementation - you'll need to expand this with:
+    /// - Turn order calculation
+    /// - Action resolution (attack, defend, ability)
+    /// - Damage calculation
+    /// - Status effects
+    /// - Ability cooldowns
+    ///
+    /// This provides just the structure and basic helper functions.
+    /// </summary>
+    public class CombatResolver
+    {
+        public enum CombatOutcome
+        {
+            AgentVictory,
+            AgentDefeated,
+            Fleeing // Agent escaped before resolution
+        }
+
+        /// <summary>
+        /// Simulate combat between an agent and the monsters in this room.
+        /// Returns the outcome. You'll need to expand this significantly for a real system.
+        /// </summary>
+        public static CombatOutcome ResolveCombat(MazeAgent agent, MonsterEncounter encounter)
+        {
+            if (encounter == null || !encounter.HasLivingMonsters)
+                return CombatOutcome.AgentVictory; // No fight needed
+
+            // TODO: Implement full combat loop
+            // 1. Build turn order from Speed stats
+            var turnOrder = BuildTurnOrder(agent, encounter);
+
+            // 2. Simulate turns until victory/defeat
+            int maxRounds = 100; // Prevent infinite loops during development
+            int round = 0;
+
+            while (round < maxRounds && agent.CharacterSheet.Stats.IsAlive && encounter.HasLivingMonsters)
+            {
+                round++;
+
+                // Execute each turn in order
+                foreach (var turn in turnOrder)
+                {
+                    if (turn.IsAgent)
+                    {
+                        ExecuteAgentTurn(agent, encounter);
+                    }
+                    else
+                    {
+                        ExecuteMonsterTurn(turn.MonsterIndex, agent, encounter);
+                    }
+
+                    // Check win conditions each turn
+                    if (!agent.CharacterSheet.Stats.IsAlive)
+                        return CombatOutcome.AgentDefeated;
+                    if (!encounter.HasLivingMonsters)
+                        return CombatOutcome.AgentVictory;
+                }
+            }
+
+            if (agent.CharacterSheet.Stats.IsAlive && !encounter.HasLivingMonsters)
+                return CombatOutcome.AgentVictory;
+
+            return CombatOutcome.AgentDefeated;
+        }
+
+        /// <summary>
+        /// Determine turn order based on Speed stats.
+        /// Higher speed = earlier in turn order.
+        /// </summary>
+        private static List<(bool IsAgent, int MonsterIndex)> BuildTurnOrder(MazeAgent agent, MonsterEncounter encounter)
+        {
+            var order = new List<(bool, int)>();
+
+            // Agent's speed
+            float agentInitiative = agent.CharacterSheet.GetEffectiveSpeed();
+            order.Add((true, -1)); // IsAgent=true, MonsterIndex unused
+
+            // Each monster's speed
+            for (int i = 0; i < encounter.Monsters.Count; i++)
+            {
+                if (!encounter.Monsters[i].IsDefeated)
+                {
+                    float monsterInitiative = encounter.Monsters[i].CharacterSheet.GetEffectiveSpeed();
+                    order.Add((false, i));
+                }
+            }
+
+            // Sort by initiative (higher = first)
+            // TODO: Implement proper sorting by speed
+            // order.Sort((a, b) => ...)
+
+            return order;
+        }
+
+        /// <summary>
+        /// Execute the agent's turn. Pick a target and attack.
+        /// This is a stub - implement your action selection here.
+        /// </summary>
+        private static void ExecuteAgentTurn(MazeAgent agent, MonsterEncounter encounter)
+        {
+            // TODO: Implement agent decision (which monster to target, what ability to use)
+
+            // For now, just attack the first alive monster
+            for (int i = 0; i < encounter.Monsters.Count; i++)
+            {
+                if (!encounter.Monsters[i].IsDefeated)
+                {
+                    AttackMonster(agent, encounter, i);
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Execute one monster's turn. They attack the agent.
+        /// This is a stub - add AI/decision logic here.
+        /// </summary>
+        private static void ExecuteMonsterTurn(int monsterIndex, MazeAgent agent, MonsterEncounter encounter)
+        {
+            if (monsterIndex < 0 || monsterIndex >= encounter.Monsters.Count)
+                return;
+
+            var monster = encounter.Monsters[monsterIndex];
+            if (monster.IsDefeated)
+                return;
+
+            // TODO: Implement monster AI (target selection, ability choice, etc.)
+
+            // For now, just attack the agent
+            AttackAgent(monster, agent);
+        }
+
+        /// <summary>
+        /// Calculate and apply damage when agent attacks a monster.
+        /// Stub - expand with damage types, abilities, modifiers, etc.
+        /// </summary>
+        private static void AttackMonster(MazeAgent agent, MonsterEncounter encounter, int targetIndex)
+        {
+            var target = encounter.Monsters[targetIndex];
+
+            // Simple attack: agent's attack vs target's defense
+            float baseDamage = agent.CharacterSheet.GetEffectiveAttack();
+            float damage = target.CharacterSheet.Stats.CalculateDamageReduction(baseDamage);
+
+            Debug.Log($"{agent.Name} attacks {target.Template.MonsterName} for {damage} damage");
+
+            target.CharacterSheet.Stats.TakeDamage(damage);
+
+            if (target.IsDefeated)
+            {
+                Debug.Log($"{target.Template.MonsterName} defeated!");
+                // TODO: Award loot to agent
+            }
+        }
+
+        /// <summary>
+        /// Calculate and apply damage when monster attacks agent.
+        /// Stub - expand similarly to AttackMonster.
+        /// </summary>
+        private static void AttackAgent(MonsterEncounter.MonsterInstance attacker, MazeAgent target)
+        {
+            float baseDamage = attacker.CharacterSheet.GetEffectiveAttack();
+            float damage = target.CharacterSheet.Stats.CalculateDamageReduction(baseDamage);
+
+            Debug.Log($"{attacker.Template.MonsterName} attacks {target.Name} for {damage} damage");
+
+            target.CharacterSheet.Stats.TakeDamage(damage);
+
+            if (!target.CharacterSheet.Stats.IsAlive)
+            {
+                Debug.Log($"{target.Name} defeated!");
+                target.Die();
+            }
+        }
+
+        /// <summary>
+        /// Award loot from a defeated monster to the agent.
+        /// Stub - implement inventory/pickup logic.
+        /// </summary>
+        private static void AwardLoot(MonsterEncounter.MonsterInstance monster, MazeAgent agent)
+        {
+            // TODO: Implement loot pickup
+            // var loot = monster.Template.GetRandomLoot();
+            // if (loot != null)
+            //     agent.CharacterSheet.AddToInventory(loot);
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/CombatResolver.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 39f10417e0962234bb15f671b810b0ab

+ 208 - 0
Assets/Scripts/Maze/Combat/IMPLEMENTATION_CHECKLIST.md

@@ -0,0 +1,208 @@
+# Implementation Checklist
+
+## ✅ Complete (Already Implemented)
+
+### Core Systems
+- [x] **CharacterStats** — Base stats (health, speed, attack, defense, magic)
+- [x] **CharacterSheet** — Stat management with equipment and bonuses
+- [x] **Item base class** — Extensible equipment system
+- [x] **Weapon** — Attack bonuses + secondary stat effects
+- [x] **Armor** — Defense bonuses + penalties/resistances
+- [x] **Monster template** — Scriptable monster definitions
+- [x] **MonsterEncounter** — Room encounter management
+- [x] **RoomEncounterConfig** — Encounter spawning configuration
+- [x] **MazeAgent integration** — Character sheet + death tracking
+- [x] **Peek mechanic** — Challenge rating evaluation
+- [x] **Retreat logic** — Threat-based exit filtering
+- [x] **AutonomousAgentBrain updates** — Dead agent detection, retreat threshold
+
+### Documentation
+- [x] Architecture diagram (ARCHITECTURE.md)
+- [x] Quick start guide (QUICK_START.md)
+- [x] Detailed README (README.md)
+- [x] Integration example (INTEGRATION_EXAMPLE.md)
+
+---
+
+## 🔨 TODO (Implementation Tasks)
+
+### Priority 1: Core Combat
+- [ ] **Full combat resolver**
+  - [ ] Turn order calculation (speed-based initiative)
+  - [ ] Combat loop (multiple rounds)
+  - [ ] Victory/defeat conditions
+  - [ ] Damage calculation (attack vs defense)
+  - [ ] Loot award on victory
+
+### Priority 2: Game Integration
+- [ ] **MazeSceneBuilder updates**
+  - [ ] Load/assign monster templates
+  - [ ] Load/assign weapon/armor templates
+  - [ ] Pass encounter config to brain
+  - [ ] Create agent character sheets with starting equipment
+
+- [ ] **MazeGenerator updates**
+  - [ ] Spawn encounters when generating rooms
+  - [ ] Exclude start/exit rooms from encounters (or customize)
+  - [ ] Scale encounter difficulty by room depth (optional)
+
+- [ ] **MazeAgentController updates**
+  - [ ] Detect agent entering room with encounter
+  - [ ] Trigger combat resolution
+  - [ ] Handle victory/defeat outcomes
+  - [ ] Update UI based on combat result
+
+### Priority 3: Features
+- [ ] **Monster AI** — Decision making during combat
+  - [ ] Target selection (damaged enemies? weakest? random?)
+  - [ ] Ability choice (basic attack? special? defend?)
+  - [ ] Retreat logic (flee at low health?)
+
+- [ ] **Ability system** — Special attacks/spells
+  - [ ] Define ability type (physical/magical/healing)
+  - [ ] Energy/mana cost
+  - [ ] Cooldown tracking
+  - [ ] Effect application (damage, heal, buff, debuff)
+
+- [ ] **Status effects** — Temporary debuffs/buffs
+  - [ ] Poison (damage over time)
+  - [ ] Stun (skip turn)
+  - [ ] Buff/debuff (stat modifiers)
+  - [ ] Duration tracking
+
+- [ ] **Item drops** — Loot system
+  - [ ] Roll on monster's loot table
+  - [ ] Add to agent inventory
+  - [ ] Option to equip immediately or carry
+
+### Priority 4: Polish
+- [ ] **UI**
+  - [ ] Health bars (agent + monsters)
+  - [ ] Combat log (what happened each turn)
+  - [ ] Stat display (show effective vs base stats)
+  - [ ] Inventory screen
+  - [ ] Equipment screen
+
+- [ ] **Audio/VFX**
+  - [ ] Hit/miss sound effects
+  - [ ] Damage number popups
+  - [ ] Hit impact VFX
+  - [ ] Victory/defeat music/effects
+
+- [ ] **Difficulty scaling**
+  - [ ] Monster stat scaling by room depth
+  - [ ] Encounter size scaling
+  - [ ] Difficulty presets (easy/normal/hard)
+
+### Priority 5: Advanced Features
+- [ ] **Combat variants**
+  - [ ] 1v1 duels
+  - [ ] 1vN multi-monster battles
+  - [ ] Team battles (multiple agents vs monsters)
+
+- [ ] **Item types beyond weapon/armor**
+  - [ ] Consumables (potions, scrolls)
+  - [ ] Accessories (rings, amulets)
+  - [ ] Questionnaire items
+
+- [ ] **AI decision trees** — Complex monster behavior
+  - [ ] Formation tactics
+  - [ ] Spell casting priority
+  - [ ] Mob coordination
+
+- [ ] **Procedural monster generation** — Randomize stats/loot
+- [ ] **Difficulty ramp** — Get harder as agents progress
+- [ ] **Unique monsters** — Boss encounters with special abilities
+
+---
+
+## Getting Started (30 min)
+
+1. **Create test assets** (5 min)
+   - 1 Weapon
+   - 1 Armor
+   - 1 Monster
+
+2. **Wire up MazeSceneBuilder** (10 min)
+   - Load resources
+   - Create agent character sheets
+   - Pass retreat threshold to brain
+
+3. **Add encounter spawning** (10 min)
+   - Update MazeGenerator to spawn encounters
+   - Test in Unity
+
+4. **Implement basic combat** (5 min)
+   - Add combat trigger to MazeAgentController
+   - Run simple damage calculation
+   - Test victory condition
+
+---
+
+## File Structure
+
+```
+Assets/Scripts/Maze/
+├── Combat/
+│   ├── CharacterStats.cs          [✅ Done]
+│   ├── CharacterSheet.cs          [✅ Done]
+│   ├── CombatResolver.cs          [🔨 TODO: Full impl]
+│   ├── RoomEncounterConfig.cs     [✅ Done]
+│   ├── Items/
+│   │   ├── Item.cs                [✅ Done]
+│   │   ├── Weapon.cs              [✅ Done]
+│   │   └── Armor.cs               [✅ Done]
+│   ├── Monsters/
+│   │   ├── Monster.cs             [✅ Done]
+│   │   └── MonsterEncounter.cs    [✅ Done]
+│   ├── README.md                  [✅ Done]
+│   ├── QUICK_START.md             [✅ Done]
+│   ├── ARCHITECTURE.md            [✅ Done]
+│   ├── INTEGRATION_EXAMPLE.md     [✅ Done]
+│   └── IMPLEMENTATION_CHECKLIST.md[✅ You're reading this]
+├── Agent/
+│   ├── MazeAgent.cs               [✅ Updated]
+│   ├── AutonomousAgentBrain.cs    [✅ Updated]
+│   └── MazeAgentController.cs     [🔨 TODO: Combat trigger]
+├── Core/
+│   └── Room.cs                    [✅ Updated]
+└── ... (other existing files)
+
+Resources/
+├── Items/
+│   ├── Weapons/
+│   │   └── IronSword.asset        [🔨 TODO: Create]
+│   └── Armor/
+│       └── LeatherArmor.asset     [🔨 TODO: Create]
+└── Monsters/
+    └── Goblin.asset               [🔨 TODO: Create]
+```
+
+---
+
+## Testing Checklist
+
+After implementing each piece, verify:
+
+- [ ] Weapon/Armor assets load and apply bonuses
+- [ ] Monster template creates instances with correct stats
+- [ ] Agents get character sheets with starting equipment
+- [ ] Agents see monsters when entering rooms
+- [ ] Agents peek at threat and retreat if dangerous
+- [ ] Combat resolves when agent enters monster room
+- [ ] Agent health decreases during combat
+- [ ] Dead agents stop moving
+- [ ] Loot drops when monster dies (if implemented)
+- [ ] New agents start fresh (not carrying over damage)
+
+---
+
+## Notes
+
+- The system is designed to be **extensible** — add new item types, abilities, effects without breaking existing code
+- **Incomplete information** is by design — agents see 60-100% of monsters, creating uncertainty and risk
+- **Difficulty balance** is your responsibility — tune `retreatThreshold`, encounter chance, monster stats
+- **Performance** should be fine even with 50+ agents since stats are just numbers
+- **No runtime state** in the template assets — each instance gets its own CharacterSheet copy
+
+Good luck! 🎮

+ 7 - 0
Assets/Scripts/Maze/Combat/IMPLEMENTATION_CHECKLIST.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2087361fbafb79c40b884922d292aa67
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 242 - 0
Assets/Scripts/Maze/Combat/INTEGRATION_EXAMPLE.md

@@ -0,0 +1,242 @@
+# Integration Example: Wiring Combat into Your Maze
+
+This document shows how to integrate the combat system into `MazeSceneBuilder` and `MazeGenerator`.
+
+## Step 1: Load Resources in MazeSceneBuilder
+
+```csharp
+public class MazeSceneBuilder : MonoBehaviour
+{
+    [SerializeField] private int agentCount = 5;
+    [SerializeField] private float initialAgentDelay = 1f;
+    [SerializeField] private float decisionInterval = 0.5f;
+    [SerializeField] private float exploreChance = 0.7f;
+    [SerializeField] private float retreatThreshold = 1.5f;
+
+    // NEW: Combat system setup
+    [SerializeField] private Monster[] monsterPool;
+    [SerializeField] private Weapon[] startingWeapons;
+    [SerializeField] private Armor[] startingArmor;
+    [SerializeField] private float encounterChance = 0.7f;
+    [SerializeField] private int minMonstersPerRoom = 1;
+    [SerializeField] private int maxMonstersPerRoom = 3;
+
+    private void Start()
+    {
+        // ... existing code ...
+
+        // NEW: Load resources if not assigned in inspector
+        if (monsterPool == null || monsterPool.Length == 0)
+            monsterPool = Resources.LoadAll<Monster>("Monsters/");
+
+        if (startingWeapons == null || startingWeapons.Length == 0)
+            startingWeapons = Resources.LoadAll<Weapon>("Items/Weapons/");
+
+        if (startingArmor == null || startingArmor.Length == 0)
+            startingArmor = Resources.LoadAll<Armor>("Items/Armor/");
+
+        // ... generate maze, then spawn agents with equipment ...
+        GenerateAgents();
+    }
+
+    private void GenerateAgents()
+    {
+        for (int i = 0; i < agentCount; i++)
+        {
+            var startRoom = graph.StartRooms[i % graph.StartRooms.Count];
+
+            // NEW: Create character sheet with starting equipment
+            var stats = new CharacterStats();
+            var sheet = new CharacterSheet(stats);
+
+            // Randomly assign starting equipment
+            if (startingWeapons.Length > 0)
+                sheet.EquipWeapon(startingWeapons[Random.Range(0, startingWeapons.Length)]);
+            if (startingArmor.Length > 0)
+                sheet.EquipArmor(startingArmor[Random.Range(0, startingArmor.Length)]);
+
+            // Create agent with character sheet
+            var agent = new MazeAgent($"Agent_{i}", startRoom, sheet);
+
+            // ... rest of agent setup ...
+
+            var controller = agentGOs[i].AddComponent<MazeAgentController>();
+            controller.Initialize(agent, generator, agentColor);
+
+            var brain = agentGOs[i].AddComponent<AutonomousAgentBrain>();
+            brain.Initialize(
+                controller,
+                generator,
+                initialAgentDelay + i * 0.2f,
+                decisionInterval,
+                exploreChance,
+                retreatThreshold,
+                () => IsLevelEnded()
+            );
+        }
+    }
+}
+```
+
+## Step 2: Spawn Encounters in MazeGenerator
+
+In `MazeGenerator.BuildRoom()` or `GenerateConnectedRoom()`, add:
+
+```csharp
+private void BuildRoom(...)
+{
+    // ... existing room creation code ...
+
+    // NEW: Spawn encounter if configured
+    if (ShouldSpawnEncounter())
+    {
+        var encounter = new MonsterEncounter();
+        encounter.SpawnMonsters(
+            monsterPool,
+            minMonstersPerRoom,
+            maxMonstersPerRoom
+        );
+        room.Encounter = encounter;
+    }
+
+    return room;
+}
+
+private bool ShouldSpawnEncounter()
+{
+    if (monsterPool == null || monsterPool.Length == 0)
+        return false;
+
+    // Don't spawn in start/exit rooms
+    // (You can customize this logic)
+    return Random.value < encounterChance;
+}
+```
+
+Add config fields to `MazeGenerator`:
+
+```csharp
+[SerializeField] private Monster[] monsterPool;
+[SerializeField] private float encounterChance = 0.7f;
+[SerializeField] private int minMonstersPerRoom = 1;
+[SerializeField] private int maxMonstersPerRoom = 3;
+```
+
+## Step 3: Trigger Combat in MazeAgentController
+
+When an agent enters a room, check for combat:
+
+```csharp
+public class MazeAgentController : MonoBehaviour
+{
+    private void Update()
+    {
+        // ... existing movement code ...
+
+        // NEW: Check for combat when entering a room
+        if (ReachedCurrentRoom())
+        {
+            if (_agent.CurrentRoom.Encounter != null &&
+                _agent.CurrentRoom.Encounter.HasLivingMonsters)
+            {
+                StartCombat();
+            }
+        }
+    }
+
+    private void StartCombat()
+    {
+        var outcome = CombatResolver.ResolveCombat(_agent, _agent.CurrentRoom.Encounter);
+
+        switch (outcome)
+        {
+            case CombatResolver.CombatOutcome.AgentVictory:
+                Debug.Log($"{_agent.Name} won the fight!");
+                // Award loot, heal, etc.
+                break;
+
+            case CombatResolver.CombatOutcome.AgentDefeated:
+                Debug.Log($"{_agent.Name} was defeated!");
+                // Agent is now dead (IsFinished=true or manually marked)
+                // They stop moving automatically
+                break;
+
+            case CombatResolver.CombatOutcome.Fleeing:
+                Debug.Log($"{_agent.Name} fled the encounter!");
+                // Force agent back through previous exit (optional)
+                break;
+        }
+
+        // TODO: Update UI, audio, etc.
+    }
+
+    private bool ReachedCurrentRoom()
+    {
+        // Check if agent's position matches their current room's position
+        float distance = Vector2.Distance(transform.position, _agent.CurrentRoom.Position);
+        return distance < 0.5f;
+    }
+}
+```
+
+## Step 4: Wire Up the Peek Mechanic (Already Active)
+
+The `AutonomousAgentBrain` now automatically:
+1. Evaluates threat in adjacent rooms
+2. Prefers safer exits when possible
+3. Only enters dangerous rooms if it's the only option
+
+To adjust agent personality:
+
+```csharp
+// Brave agents with low retreat threshold
+brain.Initialize(..., retreatThreshold: 0.5f, ...);
+
+// Cautious agents with high threshold
+brain.Initialize(..., retreatThreshold: 3.0f, ...);
+```
+
+## Step 5: Test
+
+1. Open the scene in Unity
+2. Create some Monster, Weapon, and Armor assets via Create menu
+3. Assign them to the inspector fields (or save to Resources/ paths)
+4. Play
+5. Watch agents avoid dangerous rooms and engage in combat
+
+## Advanced: Per-Agent Difficulty
+
+Scale monster stats by depth:
+
+```csharp
+private void ScaleMonsterByDepth(MonsterEncounter.MonsterInstance monster, Room room)
+{
+    float depthScale = 1f + (room.DepthFromStart * 0.1f);
+    monster.CharacterSheet.ApplyTemporaryBonus("depth_scale", monster.CharacterSheet.Stats.Attack * depthScale, "attack");
+}
+```
+
+## Advanced: Dynamic Loot
+
+After combat victory:
+
+```csharp
+private void AwardLoot(MazeAgent victor, MonsterEncounter encounter)
+{
+    foreach (var monster in encounter.Monsters)
+    {
+        if (monster.IsDefeated)
+        {
+            var loot = monster.Template.GetRandomLoot();
+            if (loot != null)
+                victor.CharacterSheet.AddToInventory(loot);
+        }
+    }
+}
+```
+
+Then implement inventory UI to let agents view/manage items.
+
+---
+
+See `Combat/README.md` for detailed architecture, and `CombatResolver.cs` for where to expand combat logic.

+ 7 - 0
Assets/Scripts/Maze/Combat/INTEGRATION_EXAMPLE.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e22f01fe786b46040b8f07fd4a2150f9
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/Scripts/Maze/Combat/Items.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d254584e705bf5441b11efb5e03a6b39
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 44 - 0
Assets/Scripts/Maze/Combat/Items/Armor.cs

@@ -0,0 +1,44 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Protective gear that boosts defense and can provide secondary bonuses.
+    /// Heavier armor provides more protection but may reduce speed.
+    /// </summary>
+    public class Armor : Item
+    {
+        [SerializeField] private float _defenseBonus = 5f;
+        [SerializeField] private float _healthBonus = 0f;       // Some armors grant extra durability
+        [SerializeField] private float _speedPenalty = 0f;      // Heavy armor slows movement
+        [SerializeField] private float _magicResistance = 0f;   // Some armor resists magic damage
+
+        public float DefenseBonus => _defenseBonus;
+        public float HealthBonus => _healthBonus;
+        public float SpeedPenalty => _speedPenalty;
+        public float MagicResistance => _magicResistance;
+
+        public override bool OnEquip(CharacterSheet character)
+        {
+            if (!base.OnEquip(character)) return false;
+
+            character.ApplyTemporaryBonus("armor_defense", _defenseBonus, "defense");
+            if (_healthBonus != 0) character.ApplyTemporaryBonus("armor_health", _healthBonus, "health");
+            if (_speedPenalty != 0) character.ApplyTemporaryBonus("armor_speed", -_speedPenalty, "speed");
+            if (_magicResistance != 0) character.ApplyTemporaryBonus("armor_magic", _magicResistance, "magic");
+
+            return true;
+        }
+
+        public override void OnUnequip(CharacterSheet character)
+        {
+            character.RemoveTemporaryBonus("armor_defense");
+            character.RemoveTemporaryBonus("armor_health");
+            character.RemoveTemporaryBonus("armor_speed");
+            character.RemoveTemporaryBonus("armor_magic");
+            base.OnUnequip(character);
+        }
+
+        public override string GetLabel() => $"{ItemName} (+{_defenseBonus} DEF)";
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/Items/Armor.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 53a075599a74c6b4ea91be7c20e5dcf1

+ 38 - 0
Assets/Scripts/Maze/Combat/Items/Item.cs

@@ -0,0 +1,38 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Base class for all equippable/inventory items. Weapons, armor, and consumables
+    /// should derive from this. Stored as ScriptableObjects for easy editor configuration.
+    /// </summary>
+    public abstract class Item : ScriptableObject
+    {
+        [SerializeField] private string _itemName = "New Item";
+        [SerializeField] private string _description = "";
+        [SerializeField] private Sprite _icon;
+        [SerializeField] private int _rarity = 1; // 1=common, 2=uncommon, 3=rare, 4=epic, 5=legendary
+
+        public string ItemName => _itemName;
+        public string Description => _description;
+        public Sprite Icon => _icon;
+        public int Rarity => _rarity;
+
+        /// <summary>
+        /// Called when this item is equipped. Override in subclasses to apply bonuses.
+        /// Return false if the item cannot be equipped right now (e.g., armor too heavy for current level).
+        /// </summary>
+        public virtual bool OnEquip(CharacterSheet character)
+        {
+            return true;
+        }
+
+        /// <summary>Called when this item is unequipped. Override to remove bonuses.</summary>
+        public virtual void OnUnequip(CharacterSheet character)
+        {
+        }
+
+        /// <summary>Short label for UI - override if needed.</summary>
+        public virtual string GetLabel() => _itemName;
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/Items/Item.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: a3bdb0912a0faca45b6c862c4bb15331

+ 45 - 0
Assets/Scripts/Maze/Combat/Items/Weapon.cs

@@ -0,0 +1,45 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// A weapon that can be equipped to boost attack power. Can also provide
+    /// secondary effects (e.g., magic boost, defense buff).
+    /// </summary>
+    public class Weapon : Item
+    {
+        [SerializeField] private float _attackBonus = 5f;
+        [SerializeField] private float _magicBonus = 0f;       // Some weapons add spell power
+        [SerializeField] private float _defenseBonus = 0f;      // Some weapons grant parry/shield bonuses
+        [SerializeField] private float _speedBonus = 0f;        // Heavy weapons might slow you down
+
+        public float AttackBonus => _attackBonus;
+        public float MagicBonus => _magicBonus;
+        public float DefenseBonus => _defenseBonus;
+        public float SpeedBonus => _speedBonus;
+
+        public override bool OnEquip(CharacterSheet character)
+        {
+            if (!base.OnEquip(character)) return false;
+
+            // Apply stat bonuses to the character
+            character.ApplyTemporaryBonus("weapon_attack", _attackBonus, "attack");
+            if (_magicBonus != 0) character.ApplyTemporaryBonus("weapon_magic", _magicBonus, "magic");
+            if (_defenseBonus != 0) character.ApplyTemporaryBonus("weapon_defense", _defenseBonus, "defense");
+            if (_speedBonus != 0) character.ApplyTemporaryBonus("weapon_speed", _speedBonus, "speed");
+
+            return true;
+        }
+
+        public override void OnUnequip(CharacterSheet character)
+        {
+            character.RemoveTemporaryBonus("weapon_attack");
+            character.RemoveTemporaryBonus("weapon_magic");
+            character.RemoveTemporaryBonus("weapon_defense");
+            character.RemoveTemporaryBonus("weapon_speed");
+            base.OnUnequip(character);
+        }
+
+        public override string GetLabel() => $"{ItemName} (+{_attackBonus} ATK)";
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/Items/Weapon.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 6b3a08b2878b73c42a665da9e02be28f

+ 8 - 0
Assets/Scripts/Maze/Combat/Monsters.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6a8296d9dbbf9dc4893d10caa3c39971
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 62 - 0
Assets/Scripts/Maze/Combat/Monsters/Monster.cs

@@ -0,0 +1,62 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// A ScriptableObject that defines a monster template (stats, appearance, loot).
+    /// Monsters are instantiated from this template when they spawn in a room.
+    /// </summary>
+    public class Monster : ScriptableObject
+    {
+        [SerializeField] private string _monsterName = "Goblin";
+        [SerializeField] private Color _color = Color.green;
+        [SerializeField] private CharacterStats _baseStats = new CharacterStats();
+
+        // Optional starting equipment
+        [SerializeField] private Weapon _startingWeapon;
+        [SerializeField] private Armor _startingArmor;
+
+        // Loot: what items might this monster drop when defeated (rough probabilities)
+        [SerializeField] private Item[] _possibleLoot;
+
+        public string MonsterName => _monsterName;
+        public Color Color => _color;
+        public CharacterStats BaseStats => _baseStats;
+        public Weapon StartingWeapon => _startingWeapon;
+        public Armor StartingArmor => _startingArmor;
+        public Item[] PossibleLoot => _possibleLoot;
+
+        /// <summary>Create a fresh character sheet for a monster instance (with a copy of stats).</summary>
+        public CharacterSheet CreateCharacterSheet()
+        {
+            var sheet = new CharacterSheet(_baseStats.Clone());
+
+            if (_startingWeapon != null)
+                sheet.EquipWeapon(_startingWeapon);
+            if (_startingArmor != null)
+                sheet.EquipArmor(_startingArmor);
+
+            return sheet;
+        }
+
+        /// <summary>
+        /// Get a random loot item from this monster's loot table (null if no loot).
+        /// You can extend this with probability tiers or rarity weighting later.
+        /// </summary>
+        public Item GetRandomLoot()
+        {
+            if (_possibleLoot == null || _possibleLoot.Length == 0)
+                return null;
+
+            return _possibleLoot[Random.Range(0, _possibleLoot.Length)];
+        }
+
+#if UNITY_EDITOR
+        private void OnValidate()
+        {
+            if (_baseStats == null)
+                _baseStats = new CharacterStats();
+        }
+#endif
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/Monsters/Monster.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 30bb08bf6a5534343a5ce2330c7a3b8a

+ 134 - 0
Assets/Scripts/Maze/Combat/Monsters/MonsterEncounter.cs

@@ -0,0 +1,134 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Represents a single monster encounter in a room: which monsters are present,
+    /// their health state, and visibility to the agent (for the peek mechanic).
+    /// </summary>
+    public class MonsterEncounter
+    {
+        public class MonsterInstance
+        {
+            public Monster Template;
+            public CharacterSheet CharacterSheet;
+            public bool IsDefeated => !CharacterSheet.Stats.IsAlive;
+
+            public MonsterInstance(Monster template)
+            {
+                Template = template;
+                CharacterSheet = template.CreateCharacterSheet();
+            }
+        }
+
+        private List<MonsterInstance> _monsters = new List<MonsterInstance>();
+        private int _visibleMonsterCount; // Partial info: agent sees this many, not the full count
+
+        public IReadOnlyList<MonsterInstance> Monsters => _monsters.AsReadOnly();
+        public int AliveMonsterCount
+        {
+            get
+            {
+                int count = 0;
+                foreach (var m in _monsters)
+                    if (!m.IsDefeated) count++;
+                return count;
+            }
+        }
+
+        /// <summary>
+        /// How many monsters the agent can actually see in the room.
+        /// This is less than or equal to AliveMonsterCount (incomplete information).
+        /// </summary>
+        public int VisibleMonsterCount => _visibleMonsterCount;
+
+        public bool HasLivingMonsters => AliveMonsterCount > 0;
+
+        /// <summary>Spawn monsters for this encounter based on a template.</summary>
+        public void SpawnMonsters(Monster[] monsterTemplates, int minCount, int maxCount)
+        {
+            _monsters.Clear();
+
+            int count = Random.Range(minCount, maxCount + 1);
+            for (int i = 0; i < count; i++)
+            {
+                Monster template = monsterTemplates[Random.Range(0, monsterTemplates.Length)];
+                _monsters.Add(new MonsterInstance(template));
+            }
+
+            // Agent sees 60-100% of the actual monsters (incomplete info)
+            _visibleMonsterCount = Mathf.RoundToInt(AliveMonsterCount * Random.Range(0.6f, 1.0f));
+            _visibleMonsterCount = Mathf.Max(1, _visibleMonsterCount); // At least see 1 if any exist
+        }
+
+        /// <summary>Mark a monster as defeated (called after combat resolution).</summary>
+        public void DefeateMonster(int index)
+        {
+            if (index >= 0 && index < _monsters.Count)
+            {
+                _monsters[index].CharacterSheet.Stats.CurrentHealth = 0;
+            }
+        }
+
+        /// <summary>
+        /// Get the perceived challenge level from the agent's perspective.
+        /// This is based on visible monsters only, not actual count.
+        /// </summary>
+        public float GetPerceivedChallengeRating(CharacterSheet agentStats)
+        {
+            if (_visibleMonsterCount == 0)
+                return 0;
+
+            // Simple calculation: sum the "threat" of visible monsters
+            float totalThreat = 0;
+            int counted = 0;
+
+            foreach (var monster in _monsters)
+            {
+                if (monster.IsDefeated) continue;
+                if (counted >= _visibleMonsterCount) break;
+
+                float monsterThreat = monster.CharacterSheet.GetEffectiveAttack() +
+                                    monster.CharacterSheet.Stats.CurrentHealth * 0.1f;
+                totalThreat += monsterThreat;
+                counted++;
+            }
+
+            float agentPower = agentStats.GetEffectiveAttack() + agentStats.GetEffectiveDefense();
+            return agentPower > 0 ? totalThreat / agentPower : totalThreat;
+        }
+
+        /// <summary>
+        /// Get the actual challenge rating (true difficulty, not visible to agent).
+        /// This is what combat system would use internally.
+        /// </summary>
+        public float GetActualChallengeRating(CharacterSheet agentStats)
+        {
+            if (_monsters.Count == 0)
+                return 0;
+
+            float totalThreat = 0;
+            foreach (var monster in _monsters)
+            {
+                if (monster.IsDefeated) continue;
+
+                float monsterThreat = monster.CharacterSheet.GetEffectiveAttack() +
+                                    monster.CharacterSheet.Stats.CurrentHealth * 0.1f;
+                totalThreat += monsterThreat;
+            }
+
+            float agentPower = agentStats.GetEffectiveAttack() + agentStats.GetEffectiveDefense();
+            return agentPower > 0 ? totalThreat / agentPower : totalThreat;
+        }
+
+        /// <summary>Reset all monsters' health for a retry.</summary>
+        public void ResetEncounter()
+        {
+            foreach (var monster in _monsters)
+            {
+                monster.CharacterSheet.ResetForNewFight();
+            }
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/Monsters/MonsterEncounter.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: fca63682d3ab79b4db817e02f8293db7

+ 71 - 0
Assets/Scripts/Maze/Combat/QUICK_START.md

@@ -0,0 +1,71 @@
+# Quick Start: Adding Combat to Your Maze
+
+## TL;DR Setup (5 min)
+
+### 1. Create a Weapon
+- Right-click in Assets > Create > Weapon
+- Set: Name = "Iron Sword", Attack Bonus = 5
+- Save as `Assets/Resources/Items/IronSword.asset`
+
+### 2. Create an Armor
+- Right-click > Create > Armor
+- Set: Name = "Leather Armor", Defense Bonus = 3, Speed Penalty = 0
+- Save as `Assets/Resources/Items/LeatherArmor.asset`
+
+### 3. Create a Monster
+- Right-click > Create > Monster
+- Set:
+  - Name = "Goblin"
+  - Color = Green
+  - Max Health = 15
+  - Speed = 8
+  - Attack = 4
+  - Defense = 1
+  - Magic = 0
+  - Starting Weapon = (drag in the Iron Sword)
+- Save as `Assets/Resources/Monsters/Goblin.asset`
+
+### 4. Wire Up Agents with Character Sheets
+In `MazeSceneBuilder` (or wherever you spawn agents):
+
+```csharp
+// Create agent with starting equipment
+var agentStats = new CharacterStats();
+var agentSheet = new CharacterSheet(agentStats);
+agentSheet.EquipWeapon(iw); // Load from resources
+agentSheet.EquipArmor(la);
+
+var agent = new MazeAgent("Agent_0", startRoom, agentSheet);
+```
+
+### 5. Spawn Monsters in Rooms
+In `MazeGenerator.GenerateConnectedRoom()` or room creation code:
+
+```csharp
+var goblin = Resources.Load<Monster>("Monsters/Goblin");
+var encounter = new MonsterEncounter();
+encounter.SpawnMonsters(new[] { goblin }, 1, 2);
+room.Encounter = encounter;
+```
+
+### 6. Agents Now Auto-Peek & Retreat
+- Call `Initialize()` on AutonomousAgentBrain with a retreat threshold
+- Agents will avoid high-threat rooms when safer exits exist
+- If cornered, they'll fight (you'll need to implement combat)
+
+## Next: Implement Combat
+
+When an agent enters a room with monsters, add combat logic:
+
+```csharp
+if (room.Encounter != null && room.Encounter.HasLivingMonsters)
+{
+    // TODO: Trigger combat
+    // - Resolve turns based on Speed stat
+    // - Apply damage (attack vs defense)
+    // - Award loot on victory
+    // - Mark agent as dead on defeat
+}
+```
+
+See `Combat/README.md` for the full architecture.

+ 7 - 0
Assets/Scripts/Maze/Combat/QUICK_START.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c052a8eb10906134cb8fea6e94992919
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 207 - 0
Assets/Scripts/Maze/Combat/README.md

@@ -0,0 +1,207 @@
+# Combat & Monster System
+
+This folder contains the foundation for a turn-based combat system with agents, monsters, items, and encounters.
+
+## Overview
+
+### Core Components
+
+- **CharacterStats** — Base statistics for any character (Agent or Monster)
+  - `MaxHealth` — Total durability
+  - `Speed` — Initiative and movement speed
+  - `Attack` — Physical damage output
+  - `Defense` — Physical damage reduction
+  - `Magic` — Magical power and spell effectiveness
+
+- **CharacterSheet** — Wraps stats with equipment and temporary bonuses
+  - Manages equipped weapons, armor, inventory
+  - Applies stat bonuses from items
+  - Tracks effective stats (base + bonuses)
+
+- **Items** (Weapon, Armor)
+  - ScriptableObjects you create in the editor
+  - Automatically apply bonuses when equipped
+  - Can have rarity, icons, descriptions for UI
+
+- **Monster** — A template for spawning monster instances
+  - Defines base stats, starting equipment, loot table
+  - Created as ScriptableObjects in the editor
+  - Instances get fresh stat copies when spawned
+
+- **MonsterEncounter** — Represents monsters in a single room
+  - Tracks alive/defeated monsters
+  - Provides challenge rating (perceived vs. actual)
+  - Agents don't see the true count (60-100% visibility)
+
+- **RoomEncounterConfig** — Configures encounter spawning
+  - Probability a room has an encounter
+  - Min/max monster count per room
+  - Which monsters can spawn here
+
+## Setup Walkthrough
+
+### 1. Create Base Weapons & Armor
+
+In the editor, create ScriptableObjects of type `Weapon` and `Armor`:
+
+1. Right-click in Project > Create > Weapon (or Armor)
+2. Fill in stats:
+   - **Weapon**: Attack bonus, optional magic/defense/speed bonuses
+   - **Armor**: Defense bonus, optional health/magic/speed effects
+3. Give it a name and set rarity (1-5)
+4. Save to `Assets/Resources/Items/` or similar
+
+**Example weapon config:**
+- Name: "Iron Sword"
+- Attack Bonus: +5
+- Rarity: 1
+
+**Example armor config:**
+- Name: "Leather Armor"
+- Defense Bonus: +3
+- Speed Penalty: -0.5
+- Rarity: 1
+
+### 2. Create Monsters
+
+Create ScriptableObjects of type `Monster`:
+
+1. Right-click in Project > Create > Monster
+2. Set the base stats (CharacterStats)
+3. Optionally assign starting weapon/armor
+4. Add items to the loot table (things it drops when defeated)
+5. Save to `Assets/Resources/Monsters/` or similar
+
+**Example monster config:**
+- Name: "Goblin"
+- Color: Green
+- Stats:
+  - Max Health: 15
+  - Speed: 8
+  - Attack: 4
+  - Defense: 1
+  - Magic: 0
+- Starting Weapon: Iron Sword (optional)
+- Possible Loot: [Leather Armor, Health Potion]
+
+### 3. Create Agent Character Sheets
+
+When spawning agents, you can give them starting equipment:
+
+```csharp
+var stats = new CharacterStats();  // Uses defaults or configure manually
+var sheet = new CharacterSheet(stats);
+sheet.EquipWeapon(playerWeapon);
+sheet.EquipArmor(playerArmor);
+
+var agent = new MazeAgent(agentName, startRoom, sheet);
+```
+
+Or agents can pick up loot after defeating monsters (integrate with your combat system).
+
+### 4. Configure Room Encounters
+
+Update `MazeSceneBuilder` to pass `RoomEncounterConfig` to `AutonomousAgentBrain.Initialize()`:
+
+```csharp
+var encounterConfig = new RoomEncounterConfig()
+{
+    EnableEncounters = true,
+    EncounterChance = 0.7f,
+    MinMonstersPerRoom = 1,
+    MaxMonstersPerRoom = 3,
+    MonsterPool = new[] { goblin, orc, troll }
+};
+```
+
+Then in the generator or brain, when a new room is created:
+
+```csharp
+if (encounterConfig.ShouldHaveEncounter())
+{
+    var encounter = new MonsterEncounter();
+    encounter.SpawnMonsters(
+        encounterConfig.MonsterPool,
+        encounterConfig.MinMonstersPerRoom,
+        encounterConfig.MaxMonstersPerRoom
+    );
+    room.Encounter = encounter;
+}
+```
+
+### 5. Integrate Peek & Retreat (Already in AutonomousAgentBrain)
+
+The `AutonomousAgentBrain` now has:
+
+- **`PeekThroughExit(exit, agent)`** — Returns perceived challenge rating
+- **`ShouldTakeExit(exit, agent)`** — Evaluates if exit is worth taking
+- **`FilterByThreat(exits, agent)`** — Removes dangerous exits from choice list
+
+This is automatically called during `ChooseExit()` — agents will naturally avoid high-threat rooms if safer exits exist.
+
+## Combat Skeleton (Not Yet Implemented)
+
+You'll need to add:
+
+1. **Combat resolution** — When an agent enters a room with monsters
+   - Initiative order (based on Speed stat)
+   - Attack vs. Defense calculation
+   - Damage application
+   - Victory/defeat conditions
+
+2. **Loot distribution** — When a monster dies
+   - Roll on loot table
+   - Add items to agent inventory or drop them in room
+
+3. **UI integration**
+   - Show health bars during combat
+   - Display stats, equipment, inventory
+   - Ability to view perceived vs. actual monster counts
+
+4. **Difficulty scaling**
+   - Adjust monster stats by room depth
+   - Scale encounter size with agent progression
+
+## Extensibility
+
+### Add a New Stat Type
+
+1. Add field to `CharacterStats`
+2. Add bonus dictionary to `CharacterSheet`
+3. Add `GetEffectiveStat()` method
+4. Add override in `Weapon`/`Armor` if applicable
+
+### Create a New Item Type
+
+Inherit from `Item`:
+
+```csharp
+public class Consumable : Item
+{
+    [SerializeField] private int _healthRestore = 20;
+
+    public override bool OnEquip(CharacterSheet character)
+    {
+        // Use immediately instead of equipping
+        character.Stats.CurrentHealth += _healthRestore;
+        return false; // Don't stay equipped
+    }
+}
+```
+
+### Customize Challenge Rating
+
+Override `MonsterEncounter.GetPerceivedChallengeRating()` for more complex threat evaluation:
+
+```csharp
+// E.g., factor in armor, magic resistance, count of monsters, etc.
+```
+
+## Current Limitations & TODOs
+
+- **No actual combat system** — Just stat blocks and threat evaluation
+- **Monster encounters spawn once** — No respawning, re-rolls, or dynamic generation
+- **No item drop/pickup** — Loot tables defined but not used
+- **Incomplete information** — Agent sees 60-100% of monsters; could be more nuanced
+- **No special abilities** — Just base attack/defense stats (extensible via items)
+- **No status effects** — Poison, stun, buff/debuff system not yet added

+ 7 - 0
Assets/Scripts/Maze/Combat/README.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a953678b16267824ba26c14cb55fb76b
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 33 - 0
Assets/Scripts/Maze/Combat/RoomEncounterConfig.cs

@@ -0,0 +1,33 @@
+using UnityEngine;
+
+namespace MazeGen
+{
+    /// <summary>
+    /// Configuration for monster encounters in a room. Attached to MazeSceneBuilder settings
+    /// to control if/how many monsters spawn in newly generated rooms.
+    /// </summary>
+    [System.Serializable]
+    public class RoomEncounterConfig
+    {
+        [SerializeField] private bool _enableEncounters = true;
+        [SerializeField] private float _encounterChance = 0.7f;      // Probability this room has monsters (0-1)
+        [SerializeField] private int _minMonstersPerRoom = 1;
+        [SerializeField] private int _maxMonstersPerRoom = 3;
+        [SerializeField] private Monster[] _monsterPool;            // Which monsters can spawn here
+
+        public bool EnableEncounters => _enableEncounters;
+        public float EncounterChance => _encounterChance;
+        public int MinMonstersPerRoom => _minMonstersPerRoom;
+        public int MaxMonstersPerRoom => _maxMonstersPerRoom;
+        public Monster[] MonsterPool => _monsterPool ?? new Monster[0];
+
+        /// <summary>Determine if this room should have an encounter.</summary>
+        public bool ShouldHaveEncounter()
+        {
+            if (!_enableEncounters || _monsterPool == null || _monsterPool.Length == 0)
+                return false;
+
+            return Random.value < _encounterChance;
+        }
+    }
+}

+ 2 - 0
Assets/Scripts/Maze/Combat/RoomEncounterConfig.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 17bac8cc9c9adaa40a7231e823e82fce

+ 72 - 10
Assets/Scripts/Maze/Core/MazeGraph.cs

@@ -7,6 +7,13 @@ namespace MazeGen
     /// The full set of generated rooms so far, plus O(1)-ish id lookup, spatial queries and
     /// exit-usage bookkeeping. Everything else (agent tracking, visualization, collision/merge
     /// logic) reads from here instead of walking the scene hierarchy.
+    ///
+    /// Also tracks connected components (union-find over rooms, merged whenever two exits get
+    /// connected) so "does an exit already exist" and "are there still unused exits" can be
+    /// asked per-component instead of globally. That distinction matters once there are
+    /// multiple start rooms: a global answer would let one isolated branch's safety valves get
+    /// silently satisfied by an exit room that lives in a totally different, disconnected part
+    /// of the maze - which is exactly how a branch can end up with no way out at all.
     /// </summary>
     public class MazeGraph
     {
@@ -16,16 +23,17 @@ namespace MazeGen
 
         private readonly SpatialRoomIndex _spatialIndex;
 
+        // Union-find over room ids, keyed by root id.
+        private readonly Dictionary<int, int> _componentParent = new Dictionary<int, int>();
+        private readonly Dictionary<int, bool> _componentHasExit = new Dictionary<int, bool>();
+        private readonly Dictionary<int, int> _componentUnusedExitCount = new Dictionary<int, int>();
+
         private int _nextRoomId;
         private int _nextExitId;
 
         public int RoomCount => Rooms.Count;
 
-        /// <summary>
-        /// Total exits across the whole graph that are not yet used. This must never be
-        /// allowed to reach 0 while ExitRooms.Count == 0 - if it did, the maze would have no
-        /// remaining way to grow and could never produce a reachable exit.
-        /// </summary>
+        /// <summary>Total exits across the whole graph that are not yet used (informational only - generation decisions should use ComponentUnusedExitCount instead).</summary>
         public int UnusedExitCount { get; private set; }
 
         public MazeGraph(float spatialCellSize)
@@ -40,7 +48,11 @@ namespace MazeGen
         {
             Rooms[room.Id] = room;
             if (room.Kind == RoomKind.Start) StartRooms.Add(room);
-            if (room.Kind == RoomKind.Exit) ExitRooms.Add(room);
+            if (room.Kind == RoomKind.Exit)
+            {
+                ExitRooms.Add(room);
+                _componentHasExit[Find(room.Id)] = true;
+            }
             _spatialIndex.Add(room);
         }
 
@@ -49,19 +61,24 @@ namespace MazeGen
         public IEnumerable<Room> QueryRoomsNear(Vector2 position, float radius) =>
             _spatialIndex.QueryNearby(position, radius);
 
-        /// <summary>Adds an exit to a room and keeps the unused-exit counter in sync.</summary>
+        /// <summary>Adds an exit to a room and keeps the unused-exit counters (global and per-component) in sync.</summary>
         public void AddExit(Room room, Exit exit)
         {
             room.Exits.Add(exit);
-            if (!exit.IsUsed) UnusedExitCount++;
+            if (!exit.IsUsed)
+            {
+                UnusedExitCount++;
+                _componentUnusedExitCount[Find(room.Id)]++;
+            }
         }
 
-        /// <summary>Marks an exit as used (idempotent) and keeps the unused-exit counter in sync.</summary>
+        /// <summary>Marks an exit as used (idempotent) and keeps the unused-exit counters in sync.</summary>
         public void MarkExitUsed(Exit exit)
         {
             if (exit.IsUsed) return;
             exit.IsUsed = true;
             UnusedExitCount--;
+            _componentUnusedExitCount[Find(exit.OwnerRoomId)]--;
         }
 
         /// <summary>
@@ -72,7 +89,52 @@ namespace MazeGen
         public void RemoveUnusedExit(Room room, Exit exit)
         {
             if (exit.IsUsed) return;
-            if (room.Exits.Remove(exit)) UnusedExitCount--;
+            if (room.Exits.Remove(exit))
+            {
+                UnusedExitCount--;
+                _componentUnusedExitCount[Find(room.Id)]--;
+            }
+        }
+
+        /// <summary>Merges the connected components containing the two rooms - call whenever a connection is made between them.</summary>
+        public void Union(int roomIdA, int roomIdB)
+        {
+            int a = Find(roomIdA);
+            int b = Find(roomIdB);
+            if (a == b) return;
+
+            _componentParent[b] = a;
+            _componentHasExit[a] = _componentHasExit[a] || _componentHasExit[b];
+            _componentUnusedExitCount[a] += _componentUnusedExitCount[b];
+            _componentHasExit.Remove(b);
+            _componentUnusedExitCount.Remove(b);
+        }
+
+        public bool AreSameComponent(int roomIdA, int roomIdB) => Find(roomIdA) == Find(roomIdB);
+
+        /// <summary>True if an Exit room already exists anywhere in the same connected component as this room.</summary>
+        public bool ComponentHasExit(int roomId) => _componentHasExit[Find(roomId)];
+
+        /// <summary>Unused exits remaining within this room's connected component only.</summary>
+        public int ComponentUnusedExitCount(int roomId) => _componentUnusedExitCount[Find(roomId)];
+
+        private int Find(int roomId)
+        {
+            if (!_componentParent.ContainsKey(roomId))
+            {
+                _componentParent[roomId] = roomId;
+                _componentHasExit[roomId] = false;
+                _componentUnusedExitCount[roomId] = 0;
+                return roomId;
+            }
+
+            int parent = _componentParent[roomId];
+            if (parent != roomId)
+            {
+                parent = Find(parent); // path compression
+                _componentParent[roomId] = parent;
+            }
+            return parent;
         }
     }
 }

+ 6 - 0
Assets/Scripts/Maze/Core/Room.cs

@@ -33,6 +33,12 @@ namespace MazeGen
         /// </summary>
         public Vector2[] CorridorWaypoints;
 
+        /// <summary>
+        /// Optional: monsters/encounter in this room (null if no encounter, or if the room
+        /// hasn't been explored yet for encounter generation).
+        /// </summary>
+        public MonsterEncounter Encounter { get; set; }
+
         public Room(int id, RoomKind kind, Vector2 position, float radius, int depthFromStart)
         {
             Id = id;

+ 9 - 1
Assets/Scripts/Maze/Generation/MazeGenerationSettings.cs

@@ -10,6 +10,12 @@ namespace MazeGen
         [Range(1, 4)] public int minExitsPerRoom = 1;
         [Range(1, 4)] public int maxExitsPerRoom = 4;
 
+        [Header("Start locations")]
+        [Tooltip("Number of separate start rooms generated at level start. Spawning agents are distributed across these.")]
+        [Range(1, 16)] public int startRoomCount = 1;
+        [Tooltip("World-space radius of the disk start rooms are randomly scattered within. Ignored when startRoomCount is 1 (that one spawns at the origin).")]
+        public float startRoomSpread = 6f;
+
         [Header("Layout")]
         [Tooltip("World-space distance between a room's center and the next room it spawns.")]
         public float roomSpacing = 4f;
@@ -37,7 +43,9 @@ namespace MazeGen
         [Tooltip("Number of NEW (never-before-visited) rooms the agent must have entered before an Exit room is allowed to appear.")]
         public int minNewRoomsBeforeExitEligible = 5;
         [Range(0f, 1f)] public float exitRoomChanceOnceEligible = 0.2f;
-        [Tooltip("Safety valve: if no Exit room has appeared naturally after this many new rooms, force the next generated room to be one, guaranteeing every maze is solvable in bounded steps.")]
+        [Tooltip("Safety valve: if no Exit room has appeared naturally in the current branch after this many new rooms, force the next generated room in it to be one, guaranteeing every maze branch is solvable in bounded steps.")]
         public int forceExitAfterNewRooms = 20;
+        [Tooltip("Maximum number of Exit rooms allowed to exist in the whole maze (0 = unlimited). Once reached, branches that still need an exit can no longer mint their own and must instead find/grow into one of the existing ones via loop-closing door connections - this is what pushes separate maze branches to join up instead of each getting their own exit.")]
+        public int maxExitRooms = 0;
     }
 }

+ 49 - 14
Assets/Scripts/Maze/Generation/MazeGenerator.cs

@@ -19,6 +19,7 @@ namespace MazeGen
         private MazeGraph _graph;
 
         public MazeGraph Graph => _graph;
+        public MazeGenerationSettings Settings => settings;
 
         /// <summary>Fired whenever a new room (including start rooms) is created.</summary>
         public event Action<Room> RoomGenerated;
@@ -64,7 +65,7 @@ namespace MazeGen
             if (fromExit.ConnectedRoomId.HasValue)
                 return _graph.Get(fromExit.ConnectedRoomId.Value);
 
-            RoomKind kind = DecideNextRoomKind(agent);
+            RoomKind kind = DecideNextRoomKind(agent, fromRoom);
             float entryAngle = NormalizeAngle(fromExit.DirectionDegrees + 180f);
             Vector2 position = fromRoom.Position + fromExit.Direction * settings.roomSpacing;
             float radius = RadiusFor(kind);
@@ -117,10 +118,12 @@ namespace MazeGen
                     ? 1
                     : Mathf.Max(0, UnityEngine.Random.Range(settings.minExitsPerRoom, settings.maxExitsPerRoom + 1) - 1);
 
-                // Safety net: never let this generation be the one that drains the maze's
-                // last unused exit before an Exit room exists anywhere.
-                int projectedPool = (_graph.UnusedExitCount - 1) + extraExits;
-                if (projectedPool <= 0 && _graph.ExitRooms.Count == 0)
+                // Safety net: never let this generation be the one that drains this branch's
+                // last unused exit before an Exit room exists anywhere in it. Scoped to
+                // connectingRoom's own connected component - a different, disconnected branch
+                // already having an exit doesn't help this one.
+                int projectedPool = _graph.ComponentUnusedExitCount(connectingRoom.Id) - 1 + extraExits;
+                if (projectedPool <= 0 && !_graph.ComponentHasExit(connectingRoom.Id))
                     extraExits = Mathf.Max(extraExits, 1);
 
                 var extraAngles = PickCardinalAngles(extraExits, entryAngle);
@@ -283,15 +286,18 @@ namespace MazeGen
                     // one here, this room can't be the merge target.
                     bool sideOccupied = other.Exits.Exists(e => Mathf.Approximately(NormalizeAngle(e.DirectionDegrees), desiredAngle));
                     if (sideOccupied) continue;
+
+                    // Respect the configured per-room exit cap even for doors added on the fly.
+                    if (other.Exits.Count >= settings.maxExitsPerRoom) continue;
                 }
 
                 // A connection consumes fromExit (+ targetExit, unless it's a freshly added
-                // door, which nets to zero). Refuse it if that would leave zero unused exits
-                // anywhere with no Exit room placed yet - fall back to normal generation
-                // instead, which always can add fresh exits.
+                // door, which nets to zero). Refuse it if that would leave the merged
+                // component (fromRoom's + other's, which this connection is about to join)
+                // with zero unused exits and no Exit room in it - fall back to normal
+                // generation instead, which always can add fresh exits.
                 int exitsConsumed = needsNewExit ? 1 : 2;
-                bool wouldStarveMaze = _graph.UnusedExitCount - exitsConsumed < 1 && _graph.ExitRooms.Count == 0;
-                if (wouldStarveMaze) continue;
+                if (WouldStarveComponent(fromRoom, other, exitsConsumed)) continue;
 
                 if (needsNewExit)
                 {
@@ -456,6 +462,25 @@ namespace MazeGen
             return origin;
         }
 
+        /// <summary>
+        /// True if connecting fromRoom and other (consuming `exitsConsumed` unused exits in
+        /// the process) would leave their merged connected component with no unused exits and
+        /// no Exit room - i.e. a dead branch that could never produce a reachable exit.
+        /// </summary>
+        private bool WouldStarveComponent(Room fromRoom, Room other, int exitsConsumed)
+        {
+            bool sameComponent = _graph.AreSameComponent(fromRoom.Id, other.Id);
+
+            bool mergedHasExit = _graph.ComponentHasExit(fromRoom.Id)
+                || (!sameComponent && _graph.ComponentHasExit(other.Id));
+            if (mergedHasExit) return false;
+
+            int mergedUnused = _graph.ComponentUnusedExitCount(fromRoom.Id);
+            if (!sameComponent) mergedUnused += _graph.ComponentUnusedExitCount(other.Id);
+
+            return mergedUnused - exitsConsumed < 1;
+        }
+
         private void ConnectExits(Exit a, Exit b)
         {
             _graph.MarkExitUsed(a);
@@ -464,15 +489,25 @@ namespace MazeGen
             a.ConnectedExitId = b.Id;
             b.ConnectedRoomId = a.OwnerRoomId;
             b.ConnectedExitId = a.Id;
+            _graph.Union(a.OwnerRoomId, b.OwnerRoomId);
         }
 
-        private RoomKind DecideNextRoomKind(MazeAgent agent)
+        private RoomKind DecideNextRoomKind(MazeAgent agent, Room fromRoom)
         {
+            // Scoped to fromRoom's own connected component - a different, disconnected branch
+            // already having an exit room doesn't make this one reachable.
+            bool componentHasExit = _graph.ComponentHasExit(fromRoom.Id);
             bool eligible = agent.NewRoomsVisited >= settings.minNewRoomsBeforeExitEligible;
-            bool mustForce = agent.NewRoomsVisited >= settings.forceExitAfterNewRooms && _graph.ExitRooms.Count == 0;
+            bool mustForce = agent.NewRoomsVisited >= settings.forceExitAfterNewRooms && !componentHasExit;
+
+            // A maxExitRooms cap can block both the safety-valve force and the random roll
+            // below - once hit, a branch without its own exit has to grow toward and merge
+            // into an existing one (via TryConnectViaExistingDoor) instead of minting a new
+            // one. This is what pushes separate maze branches to join up.
+            bool exitRoomsAvailable = settings.maxExitRooms <= 0 || _graph.ExitRooms.Count < settings.maxExitRooms;
 
-            if (mustForce) return RoomKind.Exit;
-            if (eligible && UnityEngine.Random.value < settings.exitRoomChanceOnceEligible) return RoomKind.Exit;
+            if (mustForce && exitRoomsAvailable) return RoomKind.Exit;
+            if (eligible && exitRoomsAvailable && UnityEngine.Random.value < settings.exitRoomChanceOnceEligible) return RoomKind.Exit;
             if (UnityEngine.Random.value < settings.hallwayChance) return RoomKind.Hallway;
             return RoomKind.Normal;
         }

+ 12 - 0
Assets/Scripts/Maze/Input/MazeInput.cs

@@ -41,6 +41,18 @@ namespace MazeGen
 #endif
         }
 
+        /// <summary>Mouse wheel movement this frame, normalized to roughly "notches" on both input backends (positive = scroll up/away from the user).</summary>
+        public static float GetScrollDelta()
+        {
+#if ENABLE_INPUT_SYSTEM
+            // The new Input System reports raw OS wheel delta (~120 per notch on Windows),
+            // not pre-normalized like the legacy API - scale it down to match.
+            return Mouse.current != null ? Mouse.current.scroll.ReadValue().y / 120f : 0f;
+#else
+            return Input.mouseScrollDelta.y;
+#endif
+        }
+
         /// <summary>Normalized WASD movement axis: (right/left, up/down).</summary>
         public static Vector2 ReadWasdAxis()
         {

+ 6 - 8
Assets/Scripts/Maze/Visualization/ExitMarker.cs

@@ -4,14 +4,12 @@ namespace MazeGen
 {
     /// <summary>
     /// Placeholder marker for a single Exit, positioned just outside its owning room's
-    /// edge in the exit's direction. Colored relative to the agent's current room so the
-    /// doors that actually matter right now stand out among a cluster of nearby rooms:
-    /// green = usable from here, not yet leading anywhere explored; blue = usable from here
-    /// but leads to an already-visited room; transparent gray = not on the current room at
-    /// all (regardless of its own used/unused state). Click detection is driven externally
-    /// (see MazeSceneBuilder) via a raycast, not the legacy OnMouseDown message - that
-    /// message silently does nothing when the project's Active Input Handling is set to
-    /// "Input System Package (New)" only.
+    /// edge in the exit's direction. Purely informational now that agents move
+    /// autonomously - colored relative to whichever agent the camera is currently
+    /// following, so the doors that actually matter stand out among a cluster of nearby
+    /// rooms: green = usable from the followed agent's room, not yet leading anywhere
+    /// explored; blue = usable from there but leads to an already-visited room; transparent
+    /// gray = not on the followed room at all (or no agent is being followed).
     /// </summary>
     [RequireComponent(typeof(SpriteRenderer), typeof(CircleCollider2D))]
     public class ExitMarker : MonoBehaviour

+ 212 - 52
Assets/Scripts/Maze/Visualization/MazeSceneBuilder.cs

@@ -1,25 +1,53 @@
+using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 using UnityEngine;
 
 namespace MazeGen
 {
     /// <summary>
-    /// Single entry point for milestone 1: drop this on one empty GameObject in the scene
-    /// and press Play. It creates the generator, agent and path visualizer, spawns the
-    /// start room(s), and lets you click yellow exit markers to walk the agent through them.
+    /// Scene entry point: drop this on one empty GameObject in the scene and press Play.
+    /// Generates the configured start rooms, then spawns and drives a configurable number
+    /// of autonomous agents that explore the maze on their own (see AutonomousAgentBrain).
+    /// The level ends when enough agents reach an exit room or a time limit is hit. A simple
+    /// IMGUI panel lists all agents (click to have the camera follow one) and offers a
+    /// "zoom to fit maze" button.
     /// </summary>
     [RequireComponent(typeof(MazeGenerator))]
     public class MazeSceneBuilder : MonoBehaviour
     {
-        [SerializeField, Min(1)] private int startRoomCount = 1;
-        [SerializeField] private float startRoomSpread = 6f;
+        [Header("Agents")]
+        [SerializeField, Min(1)] private int agentCount = 1;
+        [Tooltip("Each agent's spawn is delayed by a random amount in this range (seconds), so they enter the maze staggered over time instead of all at once.")]
+        [SerializeField] private Vector2 spawnStaggerRange = new Vector2(0f, 3f);
+        [Tooltip("Each agent's FIRST decision is delayed by a random amount in this range (seconds); every decision after that uses the steady decisionInterval.")]
+        [SerializeField] private Vector2 initialDecisionDelayRange = new Vector2(0.5f, 2f);
+        [Tooltip("Steady-state time (seconds) between an agent's decisions once it gets going.")]
+        [SerializeField] private float decisionInterval = 1.5f;
+        [Tooltip("Chance an agent explores a brand-new exit even when a 'follow' (known but personally-unvisited) option is also available.")]
+        [SerializeField, Range(0f, 1f)] private float exploreChance = 0.85f;
+
+        [Header("Win conditions (at least one must be > 0)")]
+        [SerializeField] private int targetFinishedAgentCount = 1;
+        [SerializeField] private float timeLimitSeconds = 0f;
+
+        private class AgentEntry
+        {
+            public MazeAgentController Controller;
+            public PathVisualizer Path;
+            public Color Color;
+        }
 
         private MazeGenerator _generator;
-        private MazeAgentController _agentController;
-        private PathVisualizer _pathVisualizer;
         private MazeCameraController _cameraController;
 
         private readonly Dictionary<int, ExitMarker> _exitMarkers = new Dictionary<int, ExitMarker>();
+        private readonly List<AgentEntry> _agents = new List<AgentEntry>();
+        private AgentEntry _followedAgent;
+
+        private bool _levelEnded;
+        private float _elapsedTime;
+        private Vector2 _hudScroll;
 
         private void Awake()
         {
@@ -27,16 +55,6 @@ namespace MazeGen
             _generator.RoomGenerated += OnRoomGenerated;
             _generator.ExitAdded += OnExitAdded;
 
-            var agentGO = new GameObject("Agent");
-            agentGO.transform.SetParent(transform, false);
-            _agentController = agentGO.AddComponent<MazeAgentController>();
-            _agentController.Initialize(_generator);
-
-            var pathGO = new GameObject("PathVisualizer");
-            pathGO.transform.SetParent(transform, false);
-            pathGO.AddComponent<LineRenderer>();
-            _pathVisualizer = pathGO.AddComponent<PathVisualizer>();
-
             var cam = Camera.main;
             if (cam != null)
             {
@@ -44,30 +62,97 @@ namespace MazeGen
                 if (_cameraController == null)
                     _cameraController = cam.gameObject.AddComponent<MazeCameraController>();
             }
+
+            if (targetFinishedAgentCount <= 0 && timeLimitSeconds <= 0f)
+            {
+                Debug.LogWarning("[MazeGen] No end condition configured (targetFinishedAgentCount and timeLimitSeconds are both 0) - defaulting targetFinishedAgentCount to agentCount so the level can still end.");
+                targetFinishedAgentCount = agentCount;
+            }
         }
 
         private void Start()
         {
-            Room firstStartRoom = null;
+            var settings = _generator.Settings;
+            int startRoomCount = Mathf.Max(1, settings.startRoomCount);
+            var startRooms = new List<Room>();
             for (int i = 0; i < startRoomCount; i++)
             {
-                float angle = startRoomCount > 1 ? (360f / startRoomCount) * i : 0f;
-                Vector2 pos = startRoomCount > 1
-                    ? new Vector2(Mathf.Cos(angle * Mathf.Deg2Rad), Mathf.Sin(angle * Mathf.Deg2Rad)) * startRoomSpread
-                    : Vector2.zero;
+                Vector2 pos = startRoomCount > 1 ? PickRandomStartPosition(settings, startRooms) : Vector2.zero;
+                startRooms.Add(_generator.GenerateStartRoom(pos));
+            }
+
+            _cameraController?.FrameBounds(ComputeMazeBounds());
+            StartCoroutine(SpawnAgentsRoutine(startRooms));
+        }
 
-                var room = _generator.GenerateStartRoom(pos);
-                firstStartRoom ??= room;
+        /// <summary>
+        /// Picks a uniformly random point within a disk of radius `startRoomSpread` (instead
+        /// of evenly around its edge), retrying a few times to avoid landing on top of a
+        /// start room already placed this call.
+        /// </summary>
+        private static Vector2 PickRandomStartPosition(MazeGenerationSettings settings, List<Room> placed)
+        {
+            float spread = Mathf.Max(settings.startRoomSpread, 0.01f);
+            float minDistance = settings.startRoomRadius * 2f;
+
+            for (int attempt = 0; attempt < 30; attempt++)
+            {
+                Vector2 candidate = Random.insideUnitCircle * spread;
+                bool overlapsPlaced = false;
+                foreach (var room in placed)
+                {
+                    if (Vector2.Distance(candidate, room.Position) < minDistance)
+                    {
+                        overlapsPlaced = true;
+                        break;
+                    }
+                }
+                if (!overlapsPlaced) return candidate;
             }
 
-            // Milestone 1: single agent spawns in the first start room.
-            _agentController.Spawn(firstStartRoom);
-            _agentController.Agent.RoomEntered += room => _pathVisualizer.AppendPoint(room.Position);
-            _agentController.Agent.RoomEntered += room => _cameraController?.FocusOn(room.Position);
-            _agentController.Agent.ReachedExitRoom += room => Debug.Log($"[MazeGen] Agent reached the exit room (id {room.Id})!");
-            _pathVisualizer.AppendPoint(firstStartRoom.Position);
-            _cameraController?.SnapTo(firstStartRoom.Position);
-            RefreshMarkerVisuals();
+            return Random.insideUnitCircle * spread; // give up avoiding overlap, use whatever we last rolled
+        }
+
+        private IEnumerator SpawnAgentsRoutine(List<Room> startRooms)
+        {
+            for (int i = 0; i < agentCount; i++)
+            {
+                float delay = Random.Range(spawnStaggerRange.x, spawnStaggerRange.y);
+                if (delay > 0f) yield return new WaitForSeconds(delay);
+
+                Room startRoom = startRooms[Random.Range(0, startRooms.Count)];
+                SpawnAgent(startRoom, i);
+            }
+        }
+
+        private void SpawnAgent(Room startRoom, int index)
+        {
+            var agentGO = new GameObject($"Agent_{index}");
+            agentGO.transform.SetParent(transform, false);
+            var controller = agentGO.AddComponent<MazeAgentController>();
+            controller.Initialize(_generator);
+
+            // Golden-ratio hue spread gives visually distinct colors even for many agents.
+            Color color = Color.HSVToRGB((index * 0.618034f) % 1f, 0.65f, 1f);
+            controller.Spawn(startRoom, color);
+
+            var pathGO = new GameObject("Path");
+            pathGO.transform.SetParent(agentGO.transform, false);
+            pathGO.AddComponent<LineRenderer>();
+            var path = pathGO.AddComponent<PathVisualizer>();
+            path.AppendPoint(startRoom.Position);
+            pathGO.SetActive(false); // only the followed agent's trail is shown
+
+            var brain = agentGO.AddComponent<AutonomousAgentBrain>();
+            float initialDelay = Random.Range(initialDecisionDelayRange.x, initialDecisionDelayRange.y);
+            brain.Initialize(controller, _generator, initialDelay, decisionInterval, exploreChance, 1.0f, () => _levelEnded);
+
+            controller.Agent.RoomEntered += room => path.AppendPoint(room.Position);
+            controller.Agent.RoomEntered += _ => RefreshMarkerVisuals();
+            controller.Agent.RoomEntered += _ => CheckFinishCondition();
+            controller.Agent.ReachedExitRoom += room => Debug.Log($"[MazeGen] {agentGO.name} reached the exit room (id {room.Id})!");
+
+            _agents.Add(new AgentEntry { Controller = controller, Path = path, Color = color });
         }
 
         private void OnRoomGenerated(Room room)
@@ -95,37 +180,112 @@ namespace MazeGen
             _exitMarkers[exit.Id] = marker;
         }
 
+        private void RefreshMarkerVisuals()
+        {
+            Room currentRoom = _followedAgent?.Controller.Agent?.CurrentRoom;
+            MazeAgent agent = _followedAgent?.Controller.Agent;
+            foreach (var marker in _exitMarkers.Values)
+                marker.UpdateVisual(currentRoom, agent);
+        }
+
         private void Update()
         {
-            if (!MazeInput.WasLeftClickedThisFrame()) return;
+            if (_levelEnded) return;
 
-            Camera cam = Camera.main;
-            if (cam == null) return;
+            _elapsedTime += Time.deltaTime;
+            if (timeLimitSeconds > 0f && _elapsedTime >= timeLimitSeconds)
+                EndLevel($"Time limit of {timeLimitSeconds:0.0}s reached.");
+        }
 
-            Ray ray = cam.ScreenPointToRay(MazeInput.GetPointerScreenPosition());
-            RaycastHit2D hit = Physics2D.GetRayIntersection(ray, Mathf.Infinity);
-            if (hit.collider != null && hit.collider.TryGetComponent(out ExitMarker marker))
-                OnExitClicked(marker.ExitData);
+        private void CheckFinishCondition()
+        {
+            if (_levelEnded) return;
+
+            int finished = _agents.Count(a => a.Controller.Agent.IsFinished);
+            if (targetFinishedAgentCount > 0 && finished >= targetFinishedAgentCount)
+                EndLevel($"{finished} agent(s) reached the exit.");
         }
 
-        private void OnExitClicked(Exit exit)
+        private void EndLevel(string reason)
         {
-            if (_agentController.Agent == null || exit.OwnerRoomId != _agentController.Agent.CurrentRoom.Id)
-            {
-                Debug.Log("[MazeGen] That exit isn't reachable from your current room.");
-                return;
-            }
+            _levelEnded = true;
+            Debug.Log($"[MazeGen] LEVEL COMPLETE - {reason}");
+        }
+
+        private void FollowAgent(AgentEntry entry)
+        {
+            if (_followedAgent == entry) return;
+            StopFollowing();
 
-            if (_agentController.TryUseExit(exit))
-                RefreshMarkerVisuals();
+            _followedAgent = entry;
+            entry.Controller.SetFollowed(true);
+            entry.Path.gameObject.SetActive(true);
+            entry.Controller.Agent.RoomEntered += OnFollowedAgentMoved;
+            _cameraController?.FocusOn(entry.Controller.Agent.CurrentRoom.Position);
+            RefreshMarkerVisuals();
         }
 
-        private void RefreshMarkerVisuals()
+        private void StopFollowing()
         {
-            Room currentRoom = _agentController.Agent?.CurrentRoom;
-            MazeAgent agent = _agentController.Agent;
-            foreach (var marker in _exitMarkers.Values)
-                marker.UpdateVisual(currentRoom, agent);
+            if (_followedAgent == null) return;
+
+            _followedAgent.Controller.SetFollowed(false);
+            _followedAgent.Path.gameObject.SetActive(false);
+            _followedAgent.Controller.Agent.RoomEntered -= OnFollowedAgentMoved;
+            _followedAgent = null;
+            RefreshMarkerVisuals();
+        }
+
+        private void OnFollowedAgentMoved(Room room) => _cameraController?.FocusOn(room.Position);
+
+        private Bounds ComputeMazeBounds()
+        {
+            var rooms = _generator.Graph.Rooms.Values;
+            if (rooms.Count == 0) return new Bounds(Vector3.zero, Vector3.one * 8f);
+
+            var bounds = new Bounds(rooms.First().Position, Vector3.zero);
+            foreach (var room in rooms)
+                bounds.Encapsulate(new Bounds(room.Position, Vector3.one * (room.Radius * 2f)));
+            return bounds;
+        }
+
+        private void OnGUI()
+        {
+            GUILayout.BeginArea(new Rect(10, 10, 260, Screen.height - 20), GUI.skin.box);
+
+            string timeLabel = timeLimitSeconds > 0f ? $"{_elapsedTime:0.0}s / {timeLimitSeconds:0.0}s" : $"{_elapsedTime:0.0}s";
+            GUILayout.Label($"Elapsed: {timeLabel}");
+
+            int finishedCount = _agents.Count(a => a.Controller.Agent.IsFinished);
+            string finishLabel = targetFinishedAgentCount > 0 ? $"{finishedCount} / {targetFinishedAgentCount}" : $"{finishedCount}";
+            GUILayout.Label($"Finished: {finishLabel}");
+
+            if (_levelEnded)
+                GUILayout.Label("LEVEL COMPLETE", GUI.skin.box);
+
+            if (GUILayout.Button("Zoom to fit maze"))
+                _cameraController?.FrameBounds(ComputeMazeBounds());
+
+            if (_followedAgent != null && GUILayout.Button("Stop following"))
+                StopFollowing();
+
+            GUILayout.Space(6);
+            GUILayout.Label("Agents (click to follow):");
+            _hudScroll = GUILayout.BeginScrollView(_hudScroll);
+            foreach (var entry in _agents)
+            {
+                var agent = entry.Controller.Agent;
+                string status = agent.IsFinished ? "reached exit" : "exploring";
+                bool isFollowed = entry == _followedAgent;
+
+                GUI.color = entry.Color;
+                if (GUILayout.Button($"{(isFollowed ? "> " : "")}{entry.Controller.gameObject.name} - {status}"))
+                    FollowAgent(entry);
+            }
+            GUI.color = Color.white;
+            GUILayout.EndScrollView();
+
+            GUILayout.EndArea();
         }
     }
 }

+ 11 - 3
Assets/Settings/Mobile_RPAsset.asset

@@ -12,8 +12,8 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
   m_Name: Mobile_RPAsset
   m_EditorClassIdentifier: 
-  k_AssetVersion: 12
-  k_AssetPreviousVersion: 12
+  k_AssetVersion: 13
+  k_AssetPreviousVersion: 13
   m_RendererType: 1
   m_RendererData: {fileID: 0}
   m_RendererDataList:
@@ -53,6 +53,7 @@ MonoBehaviour:
   m_AdditionalLightsShadowResolutionTierHigh: 1024
   m_ReflectionProbeBlending: 1
   m_ReflectionProbeBoxProjection: 1
+  m_ReflectionProbeAtlas: 1
   m_ShadowDistance: 50
   m_ShadowCascadeCount: 1
   m_Cascade2Split: 0.25
@@ -78,11 +79,11 @@ MonoBehaviour:
   m_UseAdaptivePerformance: 1
   m_ColorGradingMode: 0
   m_ColorGradingLutSize: 32
+  m_AllowPostProcessAlphaOutput: 0
   m_UseFastSRGBLinearConversion: 1
   m_SupportDataDrivenLensFlare: 1
   m_SupportScreenSpaceLensFlare: 1
   m_GPUResidentDrawerMode: 0
-  m_UseLegacyLightmaps: 0
   m_SmallMeshScreenPercentage: 0
   m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0
   m_ShadowType: 1
@@ -109,6 +110,7 @@ MonoBehaviour:
   m_PrefilterDebugKeywords: 1
   m_PrefilterWriteRenderingLayers: 1
   m_PrefilterHDROutput: 1
+  m_PrefilterAlphaOutput: 0
   m_PrefilterSSAODepthNormals: 1
   m_PrefilterSSAOSourceDepthLow: 1
   m_PrefilterSSAOSourceDepthMedium: 0
@@ -126,8 +128,14 @@ MonoBehaviour:
   m_PrefilterSoftShadowsQualityHigh: 1
   m_PrefilterSoftShadows: 0
   m_PrefilterScreenCoord: 1
+  m_PrefilterScreenSpaceIrradiance: 0
   m_PrefilterNativeRenderPass: 1
   m_PrefilterUseLegacyLightmaps: 0
+  m_PrefilterBicubicLightmapSampling: 0
+  m_PrefilterReflectionProbeRotation: 0
+  m_PrefilterReflectionProbeBlending: 0
+  m_PrefilterReflectionProbeBoxProjection: 0
+  m_PrefilterReflectionProbeAtlas: 0
   m_ShaderVariantLogLevel: 0
   m_ShadowCascades: 0
   m_Textures:

+ 103 - 0
CLAUDE.md

@@ -0,0 +1,103 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project
+
+A Unity project (Editor version `6000.3.7f1`, Universal Render Pipeline, 2D/top-down via `Physics2D`) that
+procedurally generates a maze of connected rooms and populates it with autonomous agents that explore it. All
+project-specific code lives under `Assets/Scripts/Maze/`, namespace `MazeGen`. There is no code outside that
+folder worth touching except `Assets/Scenes/SampleScene.unity`, which wires up the single `MazeSceneBuilder`
+entry point component.
+
+## Commands
+
+This is a pure Unity Editor project — there is no CLI build, lint, or test setup in the repo (no `package.json`,
+`Makefile`, `.asmdef`, or test assemblies). The only way to run/verify changes is:
+
+1. Open the project in Unity Editor `6000.3.7f1` (or compatible 6000.3.x).
+2. Open `Assets/Scenes/SampleScene.unity`.
+3. Press Play. The `MazeSceneBuilder` component on the scene's root object generates the maze and spawns
+   agents automatically.
+
+Compile errors surface in the Unity Console; there's no separate `dotnet build`/`csc` step to invoke. If you
+need to sanity-check C# syntax without the Editor, treat the generated `Assembly-CSharp.csproj` as a rough
+reference for compiled files, but it's Editor-generated and shouldn't be hand-edited.
+
+## Architecture
+
+The maze is **not** a pre-built grid — it's a lazily-grown graph of `Room` nodes connected by `Exit` doors,
+generated on demand as agents actually walk through unresolved exits. Nothing beyond an unused exit exists
+until something tries to go through it. This is what makes "many agents, huge maze" cheap.
+
+### Core data model (`Core/`)
+- `Room` — a node with `Position` (free `Vector2`, not grid-snapped), `Radius`, `Kind` (Start/Normal/Hallway/Exit),
+  and a list of `Exit`s. `CorridorWaypoints` is set only on elbow-hallway rooms (see below) for stretched
+  rendering.
+- `Exit` — one doorway. **Grid-locked to the 4 cardinal directions** (0/90/180/270°, `DirectionDegrees`), so a
+  room has at most one exit per side and "the door facing room X" always has a single, exact answer. Exits are
+  matched in connected pairs (`ConnectedRoomId`/`ConnectedExitId`) once resolved; `IsUsed` marks that.
+- `MazeGraph` — the registry of all rooms, id allocation, `UnusedExitCount` bookkeeping (must never hit 0
+  before an Exit room exists, or the maze could never be solvable), and spatial lookup via `SpatialRoomIndex`
+  (a uniform grid bucket index, cell size = `roomSpacing`, used for overlap/collision queries — not a maze grid).
+
+### Generation (`Generation/`)
+- `MazeGenerationSettings` — a `ScriptableObject` with all tunables (room sizes, exit counts, hallway chance,
+  start room count/spread, collision-avoidance search parameters, exit-room eligibility odds).
+- `MazeGenerator` — the core algorithm, called lazily from `MazeAgentController.TryUseExit` /
+  `AutonomousAgentBrain` whenever an agent uses an unresolved exit (`GenerateConnectedRoom`). On each call it:
+  1. Computes where a new room *would* go (`fromRoom.Position + exit.Direction * roomSpacing`).
+  2. **`TryConnectViaExistingDoor`**: if that spot overlaps an existing room, looks for (or adds on-the-fly) a
+     door on that room facing back toward `fromRoom`, and links into it instead of creating an overlapping
+     room — this is what turns the tree into a maze with loops. The link is either direct (`ConnectStraight`,
+     doors face each other head-on) or via a single 90°-bend elbow hallway room (`ConnectWithElbow`,
+     perpendicular doors). All paths run `IsPathClear` (point-to-segment distance vs. every other room) so a
+     corridor can never cut through unrelated geometry.
+  3. If no door connection works but the spot still overlaps something, `TryRouteAroundObstruction` tries
+     extending further out, then a single 90° bend, before falling back to `FindClearPosition` (a ring/spiral
+     search) as a last resort.
+  4. Otherwise builds the room normally via `BuildRoom`, rolling cardinal-direction exits (`PickCardinalAngles`).
+  - Known limitation: an elbow hallway's collision footprint is only a small circle at its bend corner, not its
+    full rendered length — something can in rare cases still slip past a corridor without being caught.
+
+### Visualization (`Visualization/`)
+- `MazeSceneBuilder` — the scene entry point (`[RequireComponent(MazeGenerator)]`). Generates the configured
+  start rooms, spawns `agentCount` agents on staggered coroutine delays, wires each agent's
+  `AutonomousAgentBrain`, tracks win conditions (target finished-agent count and/or a time limit — at least one
+  must be set, else it warns and defaults to requiring all agents), and draws a simple IMGUI HUD (elapsed
+  time/finished count, a clickable agent list that makes the camera follow one, a "zoom to fit maze" button).
+- `RoomView` — placeholder square sprite per room; renders elbow hallways as stretched/rotated rectangles along
+  `CorridorWaypoints` instead of a single small square.
+- `ExitMarker` — placeholder door sprite. Purely informational (not clickable — movement is autonomous now):
+  green/blue/gray coloring is relative to whichever agent the camera is currently following.
+- `PathVisualizer` — one `LineRenderer` per hop; only the followed agent's trail is shown, with the most recent
+  hop bright and earlier hops dimmed.
+
+### Agents (`Agent/`)
+- `MazeAgent` — plain C# (no `MonoBehaviour`) state: current room, path history, visited-room/used-exit sets,
+  `IsFinished`. Cheap enough to run many of.
+- `MazeAgentController` — scene-facing wrapper: positions a GameObject, tints it a per-agent color, and drives
+  `TryUseExit` (which calls `MazeGenerator.GenerateConnectedRoom` lazily if the exit isn't resolved yet).
+- `AutonomousAgentBrain` — per-agent decision loop. Waits a randomized initial delay (so agents don't all
+  decide in lockstep), then ticks a steady `decisionInterval`. `ChooseExit` is explore-biased: prefers an
+  unused exit (fresh territory) > a used exit into a room this agent hasn't personally visited (following
+  another agent's discovery) > backtracking into an already-visited room, with `exploreChance` adding
+  randomness. This is the intended extension point for smarter decisions later (e.g. "peek" a room and weigh
+  danger before choosing).
+
+### Camera & input (`Camera/`, `Input/`)
+- `MazeCameraController` — manual WASD pan / middle-mouse drag, plus `FocusOn` (smooth glide, used only while
+  following an agent) and `FrameBounds` (instant zoom-to-fit, used by the HUD button). The camera does nothing
+  on its own unless something calls one of those.
+- `MazeInput` — input-backend-agnostic helpers (`ENABLE_INPUT_SYSTEM` vs legacy `Input`), since this project has
+  the Input System package installed and legacy APIs like `OnMouseDown`/`Input.GetKey` silently break if Active
+  Input Handling is set to "Input System Package (New)" only.
+
+## Conventions worth preserving
+
+- Rooms/exits are **cardinal-only** by design (not free-angle) — this is what makes "which side faces which
+  room" and 90°-bend hallway routing well-defined. Don't reintroduce arbitrary exit angles without reworking
+  the routing/merge logic that depends on exact cardinal matching.
+- All visuals (`RoomView`, `ExitMarker`, agent sprites) are intentionally flat placeholder sprites sharing
+  `RoomView.SharedSquareSprite` — swappable for real art later without touching generation logic.
+- Generation must stay lazy: don't pre-generate rooms beyond an unused exit "just in case."

+ 429 - 0
COMBAT_REFERENCE.md

@@ -0,0 +1,429 @@
+# Combat System Reference
+
+Quick lookup for all classes, methods, and stat calculations.
+
+## Core Classes at a Glance
+
+| Class | Purpose | Location |
+|-------|---------|----------|
+| `CharacterStats` | Base stat container (health, speed, attack, defense, magic) | `Combat/CharacterStats.cs` |
+| `CharacterSheet` | Equipment + bonuses management | `Combat/CharacterSheet.cs` |
+| `Item` | Base equipment class | `Combat/Items/Item.cs` |
+| `Weapon` | Attack-boosting equipment | `Combat/Items/Weapon.cs` |
+| `Armor` | Defense-boosting equipment | `Combat/Items/Armor.cs` |
+| `Monster` | ScriptableObject monster template | `Combat/Monsters/Monster.cs` |
+| `MonsterEncounter` | Room encounter state | `Combat/Monsters/MonsterEncounter.cs` |
+| `CombatResolver` | Combat resolution (skeleton) | `Combat/CombatResolver.cs` |
+| `RoomEncounterConfig` | Encounter spawn config | `Combat/RoomEncounterConfig.cs` |
+| `MazeAgent` | Updated with CharacterSheet | `Agent/MazeAgent.cs` |
+| `AutonomousAgentBrain` | Updated with peek & retreat | `Agent/AutonomousAgentBrain.cs` |
+
+---
+
+## CharacterStats API
+
+```csharp
+// Properties
+float MaxHealth { get; }              // Total durability
+float CurrentHealth { get; set; }     // Current (0 = dead)
+float Speed { get; }                  // Initiative and movement
+float Attack { get; }                 // Damage output
+float Defense { get; }                // Damage reduction
+float Magic { get; }                  // Spell power
+
+// Methods
+CharacterStats Clone()                // Create a copy
+void RestoreHealth()                  // Reset to max
+void TakeDamage(float damage)        // Reduce health
+float CalculateDamageReduction(float incomingDamage) // Returns reduced damage
+bool IsAlive { get; }                 // health > 0
+```
+
+---
+
+## CharacterSheet API
+
+```csharp
+// Properties
+CharacterStats Stats { get; }                    // Base stats
+Weapon EquippedWeapon { get; }                   // Current weapon
+Armor EquippedArmor { get; }                     // Current armor
+IReadOnlyList<Item> Inventory { get; }           // Items in inventory
+
+// Effective stat getters (include all bonuses)
+float GetEffectiveAttack()                       // Base + equipment + buffs
+float GetEffectiveDefense()
+float GetEffectiveSpeed()
+float GetEffectiveMagic()
+float GetEffectiveMaxHealth()                    // For armor bonuses
+
+// Equipment management
+bool EquipWeapon(Weapon weapon)                  // Returns false if can't equip
+bool EquipArmor(Armor armor)
+void AddToInventory(Item item)
+
+// Bonus management
+void ApplyTemporaryBonus(string key, float bonus, string statType = "attack")
+void ApplyTemporaryBonus(string key, float attackBonus = 0, float defenseBonus = 0, float magicBonus = 0, float speedBonus = 0, float healthBonus = 0)
+void RemoveTemporaryBonus(string key)
+
+// Combat
+void ResetForNewFight()                         // Reset bonuses, keep equipment
+```
+
+---
+
+## Weapon/Armor API
+
+```csharp
+// Weapon specific
+class Weapon : Item
+{
+    float AttackBonus { get; }
+    float MagicBonus { get; }
+    float DefenseBonus { get; }          // Parry bonus
+    float SpeedBonus { get; }            // Can be negative
+}
+
+// Armor specific
+class Armor : Item
+{
+    float DefenseBonus { get; }
+    float HealthBonus { get; }           // Extra max health
+    float SpeedPenalty { get; }          // Heavy armor slows you
+    float MagicResistance { get; }
+}
+
+// Base Item class
+abstract class Item
+{
+    string ItemName { get; }
+    string Description { get; }
+    Sprite Icon { get; }
+    int Rarity { get; }                  // 1-5
+
+    virtual bool OnEquip(CharacterSheet character)  // Returns false = can't equip
+    virtual void OnUnequip(CharacterSheet character)
+    virtual string GetLabel()                       // For UI display
+}
+```
+
+---
+
+## Monster API
+
+```csharp
+// ScriptableObject template
+class Monster : ScriptableObject
+{
+    string MonsterName { get; }
+    Color Color { get; }                        // For visualization
+    CharacterStats BaseStats { get; }
+    Weapon StartingWeapon { get; }
+    Armor StartingArmor { get; }
+    Item[] PossibleLoot { get; }
+
+    // Methods
+    CharacterSheet CreateCharacterSheet()       // Create instance with fresh stats
+    Item GetRandomLoot()                        // Random drop (can be null)
+}
+```
+
+---
+
+## MonsterEncounter API
+
+```csharp
+// Holds all monsters in one room
+class MonsterEncounter
+{
+    class MonsterInstance
+    {
+        Monster Template { get; }
+        CharacterSheet CharacterSheet { get; }
+        bool IsDefeated { get; }                // health == 0
+    }
+
+    // Properties
+    IReadOnlyList<MonsterInstance> Monsters { get; }
+    int AliveMonsterCount { get; }
+    int VisibleMonsterCount { get; }            // Incomplete info
+    bool HasLivingMonsters { get; }
+
+    // Methods
+    void SpawnMonsters(Monster[] templates, int minCount, int maxCount)
+    void DefeateMonster(int index)
+    float GetPerceivedChallengeRating(CharacterSheet agentStats)  // What agent sees
+    float GetActualChallengeRating(CharacterSheet agentStats)     // Real difficulty
+    void ResetEncounter()                       // For retries
+}
+```
+
+---
+
+## MazeAgent Combat API (Updated)
+
+```csharp
+// New properties
+CharacterSheet CharacterSheet { get; }          // Agent's stats + equipment
+bool IsDead { get => !CharacterSheet.Stats.IsAlive; }
+
+// New method
+void Die()                                      // Called when defeated
+
+// New event
+event Action Died                               // Fired when agent dies
+
+// Constructor now accepts CharacterSheet
+MazeAgent(string name, Room startRoom, CharacterSheet characterSheet = null)
+```
+
+---
+
+## AutonomousAgentBrain Combat API (Updated)
+
+```csharp
+// Updated Initialize signature
+void Initialize(
+    MazeAgentController controller,
+    MazeGenerator generator,
+    float initialDelay,
+    float decisionInterval,
+    float exploreChance,
+    float retreatThreshold,                     // NEW: threat threshold (default 1.5)
+    Func<bool> isLevelEnded
+)
+
+// Peek mechanic (public)
+float PeekThroughExit(Exit exit, MazeAgent agent)  // Returns challenge rating
+
+// Private retreat helpers
+bool ShouldTakeExit(Exit exit, MazeAgent agent)
+List<Exit> FilterByThreat(List<Exit> exits, MazeAgent agent)
+```
+
+---
+
+## Room API (Updated)
+
+```csharp
+class Room
+{
+    // New property
+    MonsterEncounter Encounter { get; set; }    // null if no encounter
+    
+    // Rest of API unchanged
+}
+```
+
+---
+
+## CombatResolver API (Skeleton - Expand Me!)
+
+```csharp
+// Main entry point
+enum CombatOutcome { AgentVictory, AgentDefeated, Fleeing }
+
+static CombatOutcome ResolveCombat(MazeAgent agent, MonsterEncounter encounter)
+
+// TODO: Implement these private methods
+static List<(bool IsAgent, int MonsterIndex)> BuildTurnOrder(...)
+static void ExecuteAgentTurn(MazeAgent agent, MonsterEncounter encounter)
+static void ExecuteMonsterTurn(int monsterIndex, MazeAgent agent, MonsterEncounter encounter)
+static void AttackMonster(MazeAgent agent, MonsterEncounter encounter, int targetIndex)
+static void AttackAgent(MonsterEncounter.MonsterInstance attacker, MazeAgent target)
+static void AwardLoot(MonsterEncounter.MonsterInstance monster, MazeAgent agent)
+```
+
+---
+
+## Stat Calculation Examples
+
+### Effective Attack with Equipment
+```
+Base Attack: 10
+Weapon Bonus: +5
+Buff Bonus: +3
+─────────────────
+Effective Attack: 18
+```
+
+### Damage with Defense
+```
+Attacker's effective attack: 15
+Defender's defense: 5
+Defense reduction: min(defense × 10%, 90%) = 50%
+Damage taken: 15 × (1 - 0.5) = 7.5 health
+```
+
+### Challenge Rating
+```
+Monster attack: 8, Monster health: 20
+Monster threat: 8 + (20 × 0.1) = 10
+
+Agent attack: 12, Agent defense: 4
+Agent power: 12 + 4 = 16
+
+Perceived CR: 10 / 16 = 0.625
+(Below 1.5 threshold = manageable threat)
+```
+
+---
+
+## Common Usage Patterns
+
+### Create a Character Sheet
+```csharp
+var stats = new CharacterStats();
+var sheet = new CharacterSheet(stats);
+sheet.EquipWeapon(mySword);
+sheet.EquipArmor(myArmor);
+```
+
+### Check Effective Stats
+```csharp
+float atk = sheet.GetEffectiveAttack();      // Includes equipment
+float def = sheet.GetEffectiveDefense();
+float spd = sheet.GetEffectiveSpeed();
+```
+
+### Spawn Monsters in a Room
+```csharp
+var encounter = new MonsterEncounter();
+encounter.SpawnMonsters(monsterTemplates, minCount, maxCount);
+room.Encounter = encounter;
+```
+
+### Evaluate Threat
+```csharp
+float cr = encounter.GetPerceivedChallengeRating(agentStats);
+if (cr > 1.5f) {
+    // Dangerous - retreat if possible
+}
+```
+
+### Create Agent with Equipment
+```csharp
+var agent = new MazeAgent("Agent_0", startRoom, characterSheet);
+```
+
+### Check if Agent is Dead
+```csharp
+if (agent.IsDead) {
+    agent.Die();  // Mark as defeated
+}
+```
+
+---
+
+## Default Values
+
+| Setting | Value | Adjustable |
+|---------|-------|-----------|
+| Max Health | 100 | Per agent/monster |
+| Speed | 10 | Per agent/monster |
+| Attack | 10 | Per agent/monster |
+| Defense | 5 | Per agent/monster |
+| Magic | 5 | Per agent/monster |
+| Retreat Threshold | 1.5 | Via brain Initialize |
+| Encounter Chance | 70% | Via config |
+| Min Monsters/Room | 1 | Via config |
+| Max Monsters/Room | 3 | Via config |
+| Monster Visibility | 60-100% | Per encounter (random) |
+| Defense Reduction Cap | 90% | Hardcoded |
+
+---
+
+## Integration Checklist
+
+- [ ] Create Monster ScriptableObject (Assets/Resources/Monsters/)
+- [ ] Create Weapon ScriptableObject (Assets/Resources/Items/Weapons/)
+- [ ] Create Armor ScriptableObject (Assets/Resources/Items/Armor/)
+- [ ] Load resources in MazeSceneBuilder
+- [ ] Create agents with CharacterSheet
+- [ ] Pass retreatThreshold to AutonomousAgentBrain.Initialize()
+- [ ] Spawn encounters in rooms (MazeGenerator)
+- [ ] Implement combat resolution (CombatResolver.cs)
+- [ ] Trigger combat in MazeAgentController
+- [ ] Test agent peek & retreat logic
+- [ ] Test combat flow (victory/defeat)
+- [ ] Add UI for combat feedback
+
+---
+
+## File Dependencies
+
+```
+MazeAgent.cs
+  └─ CharacterSheet.cs
+     ├─ CharacterStats.cs
+     ├─ Weapon.cs
+     │  └─ Item.cs
+     └─ Armor.cs
+        └─ Item.cs
+
+AutonomousAgentBrain.cs
+  └─ MonsterEncounter.cs
+     └─ Monster.cs
+        ├─ CharacterStats.cs
+        └─ Weapon/Armor.cs
+
+Room.cs
+  └─ MonsterEncounter.cs
+
+CombatResolver.cs
+  └─ MazeAgent.cs
+     └─ MonsterEncounter.cs
+```
+
+---
+
+## Common Mistakes & Fixes
+
+| Problem | Cause | Fix |
+|---------|-------|-----|
+| "CharacterSheet not found" | Missing `using MazeGen;` | Add namespace |
+| Agent doesn't have equipment | Created with null CharacterSheet | Pass sheet to constructor |
+| Monsters don't spawn | monsterPool is empty | Load via Resources.LoadAll |
+| Agents don't retreat | retreatThreshold too high | Lower it (e.g., 0.8) |
+| Combat doesn't resolve | CombatResolver not called | Add trigger in MazeAgentController |
+| Dead agents still moving | Die() not called | Call in combat victory/defeat |
+
+---
+
+## Want to Extend?
+
+### Add a New Stat
+1. Add field to `CharacterStats`
+2. Add bonus dict to `CharacterSheet`
+3. Add `GetEffectiveStat()` method
+4. Update `Weapon`/`Armor` OnEquip() if applicable
+
+### Add a New Item Type
+```csharp
+public class Accessory : Item
+{
+    [SerializeField] private float _magicBonus;
+    
+    public override bool OnEquip(CharacterSheet character)
+    {
+        character.ApplyTemporaryBonus("accessory_magic", _magicBonus, "magic");
+        return true;
+    }
+}
+```
+
+### Add Status Effects
+Extend MonsterInstance to track:
+- `List<StatusEffect> activeEffects`
+- Tick them down each turn
+- Apply temporary bonuses based on effect
+
+### Add Combat Abilities
+Extend character sheet with:
+- `List<Ability> availableAbilities`
+- Ability class with: cost, damage, target type, cooldown
+
+---
+
+**See INTEGRATION_EXAMPLE.md for full implementation walkthrough!**

+ 252 - 0
COMBAT_SYSTEM_INDEX.md

@@ -0,0 +1,252 @@
+# Combat System - Complete Index
+
+Everything you need is here. Start with **START HERE** below.
+
+## 🎯 START HERE
+
+1. **[COMBAT_SYSTEM_SUMMARY.md](./COMBAT_SYSTEM_SUMMARY.md)** ← Read this first
+   - Overview of what's been created
+   - Quick 15-minute setup guide
+   - Next steps to get running
+
+2. **[Assets/Scripts/Maze/Combat/QUICK_START.md](./Assets/Scripts/Maze/Combat/QUICK_START.md)**
+   - Copy-paste setup code
+   - Create your first weapon/armor/monster
+   - Get agents fighting in 5 minutes
+
+3. **[COMBAT_REFERENCE.md](./COMBAT_REFERENCE.md)** ← Keep this open
+   - API documentation for all classes
+   - Common usage patterns
+   - Quick lookup table
+
+## 📚 Detailed Guides
+
+| Document | Purpose | Read When |
+|----------|---------|-----------|
+| [Combat/README.md](./Assets/Scripts/Maze/Combat/README.md) | Full feature overview + setup | Understanding the system deeply |
+| [Combat/ARCHITECTURE.md](./Assets/Scripts/Maze/Combat/ARCHITECTURE.md) | Class diagrams + data flow | Understanding relationships |
+| [Combat/INTEGRATION_EXAMPLE.md](./Assets/Scripts/Maze/Combat/INTEGRATION_EXAMPLE.md) | Code snippets to copy-paste | Implementing integration |
+| [Combat/IMPLEMENTATION_CHECKLIST.md](./Assets/Scripts/Maze/Combat/IMPLEMENTATION_CHECKLIST.md) | What's done, what's left | Planning your work |
+| [Combat/COPY_PASTE_EXAMPLES.cs](./Assets/Scripts/Maze/Combat/COPY_PASTE_EXAMPLES.cs) | Code templates | Quick examples |
+
+## 🧩 Core Classes Created
+
+### Stats & Equipment
+- **CharacterStats.cs** — Base stats (health, speed, attack, defense, magic)
+- **CharacterSheet.cs** — Equipment + bonuses management
+- **Items/Item.cs** — Base equipment class
+- **Items/Weapon.cs** — Attack-boosting equipment
+- **Items/Armor.cs** — Defense-boosting equipment
+
+### Monsters & Encounters
+- **Monsters/Monster.cs** — ScriptableObject monster template
+- **Monsters/MonsterEncounter.cs** — Room encounter state + threat calculation
+- **RoomEncounterConfig.cs** — Encounter spawning config
+
+### Combat (Skeleton)
+- **CombatResolver.cs** — Combat resolution (you expand this!)
+
+### AI & Integration
+- **AutonomousAgentBrain.cs** (updated) — Peek & retreat mechanics
+- **MazeAgent.cs** (updated) — Character sheet + health tracking
+- **Room.cs** (updated) — Can hold MonsterEncounter
+
+## 🚀 Quick Setup (15 min)
+
+```
+1. Create Monster asset (2 min)
+   Right-click > Create > Monster > Set stats > Save to Resources/Monsters/
+
+2. Create Weapon asset (2 min)
+   Right-click > Create > Weapon > Set bonuses > Save to Resources/Items/Weapons/
+
+3. Create Armor asset (2 min)
+   Right-click > Create > Armor > Set defense > Save to Resources/Items/Armor/
+
+4. Update MazeSceneBuilder (5 min)
+   Load resources, create agents with equipment, pass retreatThreshold
+
+5. Update MazeGenerator (2 min)
+   Spawn encounters in rooms
+
+6. Test (2 min)
+   Play & watch agents interact with monsters
+```
+
+See **COMBAT_SYSTEM_SUMMARY.md** for full code.
+
+## 📋 Implementation Order
+
+**Phase 1: Setup (Today)**
+- [ ] Read COMBAT_SYSTEM_SUMMARY.md
+- [ ] Create test assets (1 monster, 1 weapon, 1 armor)
+- [ ] Update MazeSceneBuilder to load and use assets
+- [ ] Update MazeGenerator to spawn encounters
+- [ ] Verify agents spawn with equipment ✓
+
+**Phase 2: Combat (Next)**
+- [ ] Implement CombatResolver.cs full combat loop
+- [ ] Integrate into MazeAgentController
+- [ ] Test agent vs monster combat
+- [ ] Implement victory/defeat outcomes
+
+**Phase 3: Polish (Later)**
+- [ ] Add UI (health bars, combat log)
+- [ ] Implement monster AI
+- [ ] Add loot drops
+- [ ] Balance difficulty
+
+## 🎮 Key Features
+
+✅ **Done**
+- Character stats system with multiple stat types
+- Equipment system (weapons, armor, general items)
+- Monster templates and spawning
+- Rooms with monster encounters
+- Agent intelligence (peek at threats, retreat from danger)
+- Incomplete information (agents see 60-100% of monsters)
+- Death tracking for agents
+
+🔨 **Skeleton (Expand)**
+- Combat resolution (basic structure, implement turns)
+- Monster AI (currently just attacks)
+- Ability system (structure for items to have effects)
+- UI integration
+
+❌ **Not Done Yet**
+- Special abilities/spells
+- Status effects
+- Inventory UI
+- Loot pickup
+- Difficulty scaling
+- Boss encounters
+
+## 📝 Class Reference
+
+**Quick lookup:**
+```csharp
+// Create a character
+var stats = new CharacterStats();
+var sheet = new CharacterSheet(stats);
+
+// Equip items
+sheet.EquipWeapon(ironSword);
+sheet.EquipArmor(leatherArmor);
+
+// Get effective stats (includes bonuses)
+float atk = sheet.GetEffectiveAttack();
+
+// Create agent with character
+var agent = new MazeAgent("Agent_0", startRoom, sheet);
+
+// Spawn monsters
+var encounter = new MonsterEncounter();
+encounter.SpawnMonsters(new[] { goblin }, 1, 3);
+room.Encounter = encounter;
+
+// Evaluate threat
+float cr = encounter.GetPerceivedChallengeRating(agent.CharacterSheet);
+
+// Resolve combat (when implemented)
+var outcome = CombatResolver.ResolveCombat(agent, encounter);
+```
+
+See **COMBAT_REFERENCE.md** for full API.
+
+## 🐛 Common Issues
+
+| Issue | Solution |
+|-------|----------|
+| Compile errors about CharacterSheet | Unity hasn't recompiled yet. Wait or press Ctrl+Shift+R |
+| Resources not loading | Ensure assets are in `Resources/Monsters/`, `Resources/Items/Weapons/`, etc. |
+| Agents don't have equipment | Pass CharacterSheet to MazeAgent constructor |
+| Agents don't retreat | Set lower retreatThreshold (e.g., 0.8 instead of 1.5) |
+| Combat doesn't happen | Implement MazeAgentController trigger for room with encounter |
+| Dead agents still moving | Call agent.Die() when defeated |
+
+## 📞 Design Decisions Made
+
+1. **Incomplete information** — Agents see 60-100% of monsters (not exact count)
+   - Creates strategic risk
+   - Agents make decisions without perfect info
+   - Customizable per encounter
+
+2. **Retreatable** — Agents can choose not to fight
+   - Uses challenge rating threshold
+   - Only if safer exits exist
+   - Cornered agents will fight anyway
+
+3. **Extensible equipment** — Easy to add new item types
+   - Base Item class is abstract
+   - Weapon/Armor derive from it
+   - Can add Consumable, Accessory, etc.
+
+4. **Per-instance stats** — Monsters are templates, instances get copied stats
+   - Allows variation (same goblin can have different stats)
+   - Instances don't affect templates
+   - Modular and safe
+
+5. **Separate combat resolution** — CombatResolver is independent
+   - Can test separately
+   - Easy to replace with different system
+   - No coupling to scene/UI
+
+## 🔗 Dependencies
+
+```
+MazeAgent
+  └─ CharacterSheet
+     └─ CharacterStats
+     └─ Weapon/Armor
+        └─ Item
+
+AutonomousAgentBrain (peek & retreat)
+  └─ MonsterEncounter
+     └─ Monster
+        └─ CharacterStats + Items
+
+Room
+  └─ MonsterEncounter
+
+CombatResolver (skeleton)
+  └─ MazeAgent + MonsterEncounter
+```
+
+## ✨ What Makes This System Good
+
+- **Modular** — Each piece is independent and testable
+- **Extensible** — Easy to add new items, stats, effects
+- **Data-driven** — All content is ScriptableObjects (no code changes for new monsters)
+- **Agent-intelligent** — Agents make decisions based on actual threat
+- **Performance-conscious** — Stats are just numbers, support many agents
+- **Incomplete info** — Creates real strategy, not just stats
+
+## 🎓 Learning Path
+
+1. Create one weapon, armor, monster → understand ScriptableObjects
+2. Create agent with equipment → understand CharacterSheet
+3. Spawn encounter in room → understand MonsterEncounter
+4. Implement simple attack resolution → understand combat loop
+5. Add turn order → understand initiative systems
+6. Add status effects → understand effect composition
+7. Add monster AI → understand decision making
+8. Expand to full system → you're a game dev now! 🚀
+
+## 📞 Support
+
+All documentation is in this folder:
+- `COMBAT_SYSTEM_SUMMARY.md` — Big picture
+- `COMBAT_REFERENCE.md` — API lookup
+- `QUICK_START.md` — Immediate setup
+- `INTEGRATION_EXAMPLE.md` — Code examples
+- `ARCHITECTURE.md` — Class diagrams
+- `README.md` — Detailed guide
+- `IMPLEMENTATION_CHECKLIST.md` — Progress tracking
+
+**Start with COMBAT_SYSTEM_SUMMARY.md** ← You are here 👈
+
+---
+
+**Version:** 1.0
+**Last Updated:** 2026-08-25
+**Status:** ✅ All foundation classes complete, skeleton for combat ready

+ 290 - 0
COMBAT_SYSTEM_SUMMARY.md

@@ -0,0 +1,290 @@
+# Combat System Setup Complete ✅
+
+I've set up a complete foundation for agent combat, monsters, and equipment. Here's what you got:
+
+## What's Been Created
+
+### Core Systems (8 new classes)
+1. **CharacterStats** — Base stats (health, speed, attack, defense, magic)
+2. **CharacterSheet** — Equipment + stat bonuses management
+3. **Item, Weapon, Armor** — Equipment system with automatic bonuses
+4. **Monster** — ScriptableObject templates for spawning monsters
+5. **MonsterEncounter** — Manages monsters in a room with partial info
+6. **RoomEncounterConfig** — Configuration for encounter spawning
+7. **CombatResolver** — Skeleton for combat resolution (you'll expand this)
+8. **AutonomousAgentBrain** — Updated with peek & retreat mechanics
+
+### Updated Existing Classes
+- **MazeAgent** — Now has a CharacterSheet, health tracking, death state
+- **Room** — Can now hold a MonsterEncounter
+- **AutonomousAgentBrain** — Agents now peek at threats and retreat strategically
+
+### Documentation (5 comprehensive guides)
+- `README.md` — Full architecture and feature overview
+- `QUICK_START.md` — 5-minute setup guide
+- `ARCHITECTURE.md` — Class diagrams and data flow
+- `INTEGRATION_EXAMPLE.md` — Copy-paste integration code
+- `IMPLEMENTATION_CHECKLIST.md` — What's done, what's next
+- `COPY_PASTE_EXAMPLES.cs` — Code templates
+
+---
+
+## Quick Start (Get It Running in 15 min)
+
+### 1. Create Monster Assets
+In Unity editor:
+- Right-click Project > Create > Monster
+- Name: "Goblin"
+- Set Max Health: 15, Speed: 8, Attack: 4, Defense: 1
+- Save to `Assets/Resources/Monsters/Goblin.asset`
+
+### 2. Create Equipment
+- Create > Weapon
+  - Name: "Iron Sword"
+  - Attack Bonus: 5
+  - Save to `Assets/Resources/Items/Weapons/IronSword.asset`
+- Create > Armor
+  - Name: "Leather Armor"
+  - Defense Bonus: 3
+  - Save to `Assets/Resources/Items/Armor/LeatherArmor.asset`
+
+### 3. Update MazeSceneBuilder
+```csharp
+private Monster[] _monsterPool;
+private Weapon[] _startingWeapons;
+private Armor[] _startingArmor;
+
+private void Start()
+{
+    _monsterPool = Resources.LoadAll<Monster>("Monsters/");
+    _startingWeapons = Resources.LoadAll<Weapon>("Items/Weapons/");
+    _startingArmor = Resources.LoadAll<Armor>("Items/Armor/");
+    
+    // ... rest of your code ...
+}
+
+private void SpawnAgent(int index, Room startRoom)
+{
+    // Create character sheet with equipment
+    var stats = new CharacterStats();
+    var sheet = new CharacterSheet(stats);
+    
+    if (_startingWeapons.Length > 0)
+        sheet.EquipWeapon(_startingWeapons[Random.Range(0, _startingWeapons.Length)]);
+    if (_startingArmor.Length > 0)
+        sheet.EquipArmor(_startingArmor[Random.Range(0, _startingArmor.Length)]);
+    
+    // Create agent with sheet
+    var agent = new MazeAgent($"Agent_{index}", startRoom, sheet);
+    
+    // ... rest of agent setup ...
+    
+    brain.Initialize(
+        controller,
+        generator,
+        initialDelay + index * 0.2f,
+        decisionInterval,
+        exploreChance,
+        1.5f,  // retreatThreshold - new parameter!
+        () => IsLevelEnded()
+    );
+}
+```
+
+### 4. Update MazeGenerator
+In `BuildRoom()` or similar:
+```csharp
+private void SpawnEncounter(Room room)
+{
+    if (room.Kind == RoomKind.Start || room.Kind == RoomKind.Exit)
+        return;  // Don't spawn in start/exit rooms
+        
+    if (_monsterPool == null || _monsterPool.Length == 0)
+        return;
+        
+    if (Random.value > 0.7f)  // 70% chance
+        return;
+    
+    var encounter = new MonsterEncounter();
+    encounter.SpawnMonsters(_monsterPool, 1, 3);  // 1-3 monsters
+    room.Encounter = encounter;
+}
+```
+
+### 5. Test
+- Play in Unity
+- Agents should spawn with equipment
+- Open a room with a Goblin - agent's brain will peek and decide whether to enter
+- If you've wired combat, fight should trigger
+
+---
+
+## Key Features Included
+
+### ✅ Character System
+- Agents and monsters both have stats (health, speed, attack, defense, magic)
+- Effective stats calculated with equipment bonuses
+- Support for temporary buffs/debuffs
+- Health tracking and death state
+
+### ✅ Equipment System
+- Weapons: boost attack (+ optional secondary effects)
+- Armor: boost defense (with penalties like slowness)
+- Automatic stat bonus application when equipped
+- Extensible for new item types (rings, accessories, etc.)
+
+### ✅ Monster System
+- ScriptableObject templates for each monster type
+- Loot tables (items that drop when defeated)
+- Starting equipment (weapons/armor monsters carry)
+- Color-coded for visualization
+
+### ✅ Encounter System
+- Rooms can have monster encounters
+- Encounters track alive/defeated monsters
+- **Incomplete information**: agents see 60-100% of actual monsters (don't know the real count)
+- Challenge rating calculation (perceived vs actual)
+
+### ✅ Agent Intelligence
+- **Peek mechanic**: agents look into rooms before entering
+- **Threat evaluation**: calculate danger level before committing
+- **Retreat logic**: avoid dangerous exits if safer options exist
+- Uses challenge rating to make decisions
+- Falls back to exploration if cornered
+
+---
+
+## What Still Needs Implementation
+
+### Priority 1: Combat Resolution
+The `CombatResolver` is a skeleton. You'll add:
+1. Turn order (based on Speed stat)
+2. Attack resolution (attack vs defense damage)
+3. Multiple rounds until victory/defeat
+4. Loot distribution when agent wins
+
+### Priority 2: MazeAgentController Integration
+Add combat triggering:
+1. Detect agent entering a room with living monsters
+2. Call CombatResolver
+3. Handle victory (reward, continue) or defeat (agent dies, stops moving)
+
+### Priority 3: UI & Feedback
+- Health bars for agent + monsters
+- Combat log showing what happened each turn
+- Equipment/inventory screens
+- Audio cues for hits, misses, victories
+
+### Priority 4: Monster AI
+- Decide which agent to target
+- Choose between attack/defend/ability
+- Retreat when badly wounded
+
+### Priority 5: Advanced Features
+- Special abilities (spells, tactics)
+- Status effects (poison, stun, buffs)
+- Difficulty scaling by room depth
+- Boss encounters with unique abilities
+
+---
+
+## File Structure
+
+```
+Assets/Scripts/Maze/Combat/
+├── CharacterStats.cs              ← Base stats
+├── CharacterSheet.cs              ← Equipment + bonuses
+├── CombatResolver.cs              ← Combat skeleton (expand me!)
+├── RoomEncounterConfig.cs         ← Spawn config
+├── Items/
+│   ├── Item.cs                    ← Base class
+│   ├── Weapon.cs                  ← +Attack equipment
+│   └── Armor.cs                   ← +Defense equipment
+├── Monsters/
+│   ├── Monster.cs                 ← Template
+│   └── MonsterEncounter.cs        ← Room combat state
+└── [Documentation files]
+
+Assets/Resources/
+├── Items/
+│   ├── Weapons/
+│   │   └── [Your weapons here]
+│   └── Armor/
+│       └── [Your armor here]
+└── Monsters/
+    └── [Your monsters here]
+```
+
+---
+
+## Architecture Overview
+
+```
+Agent → CharacterSheet → [Equipment Bonuses] → Effective Stats
+                             ↑
+                        Weapon/Armor
+                        (apply modifiers)
+
+Room → MonsterEncounter → [Monster Instances] → Combat when Agent enters
+                             ↑
+                          Monster Template
+                          (loot table, AI)
+
+AutonomousAgentBrain → Peek at adjacent rooms
+                    → Get perceived threat (60-100% of actual)
+                    → Compare to retreat threshold
+                    → Choose safe exit or fight
+```
+
+---
+
+## Next Steps
+
+1. **Read `QUICK_START.md`** — Copy-paste your first setup
+2. **Create test assets** — One weapon, armor, monster (5 min)
+3. **Wire up MazeSceneBuilder** — Load resources and create agents with equipment (5 min)
+4. **Add encounter spawning** — Monsters appear in rooms (5 min)
+5. **Implement basic combat** — Fill in CombatResolver.cs (30 min+)
+6. **Integrate into MazeAgentController** — Trigger combat when agent enters room (10 min)
+7. **Test and iterate** — Tweak numbers, add features
+
+See `INTEGRATION_EXAMPLE.md` for detailed copy-paste code for each step.
+
+---
+
+## Tips & Best Practices
+
+✅ **Do:**
+- Create assets in the editor (Create > Monster/Weapon/Armor) — it's easier
+- Start with simple monsters (low stats, no special abilities)
+- Test with 2-3 agents first before ramping up to 10+
+- Save weapon/armor/monster assets to `Resources/` for easy loading
+- Use different colors for different monster types (helps visualize)
+- Add Debug.Log statements in CombatResolver to see what's happening
+
+❌ **Don't:**
+- Try to implement full combat AI before basic damage works
+- Create hundreds of asset variants right away — start simple
+- Forget to handle dead agents (they should stop moving)
+- Assume balanced numbers first time — you'll need to tweak
+- Implement every feature at once — do it incrementally
+
+---
+
+## Questions?
+
+- **How do agents pick loot?** — Implement in CombatResolver.AwardLoot() — currently just a stub
+- **How do monsters decide targets?** — Implement in CombatResolver.ExecuteMonsterTurn() — currently just attacks
+- **Can I scale difficulty?** — Yes! Modify monster stats by room depth in the spawning code
+- **Can multiple agents fight together?** — Architecture supports it, but combat resolve is 1v1 currently
+- **How do special abilities work?** — Not yet implemented, but CharacterSheet supports temporary bonuses as a foundation
+
+---
+
+## You Got This! 🎮
+
+You have all the pieces. Now implement the combat logic and watch your agents fight their way through the maze!
+
+Start with `Assets/Scripts/Maze/Combat/QUICK_START.md` → then `INTEGRATION_EXAMPLE.md` → then fill in `CombatResolver.cs`.
+
+Good luck! 🚀

+ 60 - 58
UserSettings/Layouts/CurrentMaximizeLayout.dwlt

@@ -19,12 +19,12 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 36
-    width: 1215
+    width: 1714
     height: 1284
   m_MinSize: {x: 400, y: 112}
   m_MaxSize: {x: 32384, y: 16192}
   vertical: 0
-  controlID: 823
+  controlID: 4542
   draggingID: 0
 --- !u!114 &2
 MonoBehaviour:
@@ -47,9 +47,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Game\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 2816
+    x: 2347
     y: 87
-    width: 395
+    width: 775
     height: 919
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -70,7 +70,7 @@ MonoBehaviour:
   m_ShowGizmos: 0
   m_TargetDisplay: 0
   m_ClearColor: {r: 0, g: 0, b: 0, a: 0}
-  m_TargetSize: {x: 395, y: 898}
+  m_TargetSize: {x: 775, y: 898}
   m_TextureFilterMode: 0
   m_TextureHideFlags: 61
   m_RenderIMGUI: 1
@@ -85,8 +85,8 @@ MonoBehaviour:
     m_VRangeLocked: 0
     hZoomLockedByDefault: 0
     vZoomLockedByDefault: 0
-    m_HBaseRangeMin: -197.5
-    m_HBaseRangeMax: 197.5
+    m_HBaseRangeMin: -387.5
+    m_HBaseRangeMax: 387.5
     m_VBaseRangeMin: -449
     m_VBaseRangeMax: 449
     m_HAllowExceedBaseRangeMin: 1
@@ -106,23 +106,23 @@ MonoBehaviour:
       serializedVersion: 2
       x: 0
       y: 21
-      width: 395
+      width: 775
       height: 898
     m_Scale: {x: 1, y: 1}
-    m_Translation: {x: 197.5, y: 449}
+    m_Translation: {x: 387.5, y: 449}
     m_MarginLeft: 0
     m_MarginRight: 0
     m_MarginTop: 0
     m_MarginBottom: 0
     m_LastShownAreaInsideMargins:
       serializedVersion: 2
-      x: -197.5
+      x: -387.5
       y: -449
-      width: 395
+      width: 775
       height: 898
     m_MinimalGUI: 1
   m_defaultScale: 1
-  m_LastWindowPixelSize: {x: 395, y: 919}
+  m_LastWindowPixelSize: {x: 775, y: 919}
   m_ClearInEditMode: 1
   m_NoCameraWarning: 1
   m_LowResolutionForAspectRatios: 01000000000000000000
@@ -148,12 +148,12 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 989
+    width: 1399
     height: 1284
   m_MinSize: {x: 300, y: 112}
   m_MaxSize: {x: 24288, y: 16192}
   vertical: 1
-  controlID: 824
+  controlID: 4543
   draggingID: 0
 --- !u!114 &4
 MonoBehaviour:
@@ -175,12 +175,12 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 989
+    width: 1399
     height: 945
   m_MinSize: {x: 300, y: 56}
   m_MaxSize: {x: 24288, y: 8096}
   vertical: 0
-  controlID: 825
+  controlID: 4544
   draggingID: 0
 --- !u!114 &5
 MonoBehaviour:
@@ -199,10 +199,10 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 214
+    width: 296
     height: 945
-  m_MinSize: {x: 201, y: 226}
-  m_MaxSize: {x: 4001, y: 4026}
+  m_MinSize: {x: 200, y: 200}
+  m_MaxSize: {x: 4000, y: 4000}
   m_ActualView: {fileID: 6}
   m_Panes:
   - {fileID: 6}
@@ -229,9 +229,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Hierarchy\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 2224
+    x: 1725
     y: 87
-    width: 213
+    width: 295
     height: 919
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -250,10 +250,12 @@ MonoBehaviour:
     m_TreeViewState:
       scrollPos: {x: 0, y: 0}
       m_SelectedIDs:
-      - m_Data: -2678
+      - m_Data: 45734
       m_LastClickedID:
-        m_Data: 0
+        m_Data: 45734
       m_ExpandedIDs:
+      - m_Data: -68256
+      - m_Data: -68248
       - m_Data: -1298
       - m_Data: 45752
       m_RenameOverlay:
@@ -296,12 +298,12 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 214
+    x: 296
     y: 0
-    width: 378
+    width: 326
     height: 945
-  m_MinSize: {x: 200, y: 200}
-  m_MaxSize: {x: 4000, y: 4000}
+  m_MinSize: {x: 202, y: 226}
+  m_MaxSize: {x: 4002, y: 4026}
   m_ActualView: {fileID: 8}
   m_Panes:
   - {fileID: 8}
@@ -330,9 +332,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Scene\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 2438
+    x: 2021
     y: 87
-    width: 376
+    width: 324
     height: 919
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -946,9 +948,9 @@ MonoBehaviour:
   m_AudioPlay: 0
   m_DebugDrawModesUseInteractiveLightBakingData: 0
   m_Position:
-    m_Target: {x: 4.9675283, y: 4.275687, z: -1.619735}
+    m_Target: {x: -62.52057, y: 44.618496, z: 56.724976}
     speed: 2
-    m_Value: {x: 4.9675283, y: 4.275687, z: -1.619735}
+    m_Value: {x: -62.52057, y: 44.618496, z: 56.724976}
   m_RenderMode: 0
   m_CameraMode:
     drawMode: 0
@@ -994,20 +996,20 @@ MonoBehaviour:
     m_GridAxis: 1
     m_gridOpacity: 0.5
   m_Rotation:
-    m_Target: {x: -0.11120786, y: 0.86695325, z: -0.22350314, w: -0.43136704}
+    m_Target: {x: -0.028693603, y: 0.9368253, z: -0.07917021, w: -0.33952686}
     speed: 2
-    m_Value: {x: -0.111207515, y: 0.8669506, z: -0.22350246, w: -0.4313657}
+    m_Value: {x: -0.02881216, y: 0.9363096, z: -0.079126626, w: -0.34092978}
   m_Size:
-    m_Target: 11.092366
+    m_Target: 138.42363
     speed: 2
-    m_Value: 11.092366
+    m_Value: 138.42363
   m_Ortho:
     m_Target: 0
     speed: 2
     m_Value: 0
   m_CameraSettings:
-    m_Speed: 1.0005
-    m_SpeedNormalized: 0.49999997
+    m_Speed: 1.06047
+    m_SpeedNormalized: 0.53
     m_SpeedMin: 0.001
     m_SpeedMax: 2
     m_EasingEnabled: 1
@@ -1145,12 +1147,12 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 592
+    x: 622
     y: 0
-    width: 397
+    width: 777
     height: 945
-  m_MinSize: {x: 50, y: 50}
-  m_MaxSize: {x: 4000, y: 4000}
+  m_MinSize: {x: 52, y: 76}
+  m_MaxSize: {x: 4002, y: 4026}
   m_ActualView: {fileID: 2}
   m_Panes:
   - {fileID: 2}
@@ -1214,12 +1216,12 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 945
-    width: 989
+    width: 1399
     height: 339
   m_MinSize: {x: 200, y: 56}
   m_MaxSize: {x: 16192, y: 8096}
   vertical: 0
-  controlID: 937
+  controlID: 4620
   draggingID: 0
 --- !u!114 &14
 MonoBehaviour:
@@ -1238,7 +1240,7 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 603
+    width: 861
     height: 339
   m_MinSize: {x: 231, y: 276}
   m_MaxSize: {x: 10001, y: 10026}
@@ -1268,9 +1270,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Project\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 2224
+    x: 1725
     y: 1032
-    width: 602
+    width: 860
     height: 313
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -1387,8 +1389,8 @@ MonoBehaviour:
       m_ResourceFile: 
   m_ListAreaState:
     m_SelectedInstanceIDs:
-    - m_Data: -2678
-    m_LastClickedInstanceID: -2678
+    - m_Data: 45734
+    m_LastClickedInstanceID: 45734
     m_HadKeyboardFocusLastEvent: 0
     m_ExpandedInstanceIDs:
     - m_Data: 70016
@@ -1438,12 +1440,12 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 603
+    x: 861
     y: 0
-    width: 386
+    width: 538
     height: 339
-  m_MinSize: {x: 102, y: 126}
-  m_MaxSize: {x: 4002, y: 4026}
+  m_MinSize: {x: 100, y: 100}
+  m_MaxSize: {x: 4000, y: 4000}
   m_ActualView: {fileID: 17}
   m_Panes:
   - {fileID: 17}
@@ -1470,9 +1472,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Console\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 2827
+    x: 2586
     y: 1032
-    width: 384
+    width: 536
     height: 313
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -1502,9 +1504,9 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 989
+    x: 1399
     y: 0
-    width: 226
+    width: 315
     height: 1284
   m_MinSize: {x: 276, y: 76}
   m_MaxSize: {x: 4001, y: 4026}
@@ -1534,9 +1536,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Inspector\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 3213
+    x: 3124
     y: 87
-    width: 225
+    width: 314
     height: 1258
   m_SerializedDataModeController:
     m_DataMode: 0

+ 276 - 273
UserSettings/Layouts/default-6000.dwlt

@@ -14,96 +14,17 @@ MonoBehaviour:
   m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.ContainerWindow
   m_PixelRect:
     serializedVersion: 2
-    x: 1721
-    y: 51
-    width: 1718
-    height: 1340
+    x: 0
+    y: 43
+    width: 3440
+    height: 1349
   m_ShowMode: 4
-  m_Title: Game
-  m_RootView: {fileID: 5}
+  m_Title: Hierarchy
+  m_RootView: {fileID: 2}
   m_MinSize: {x: 875, y: 300}
   m_MaxSize: {x: 10000, y: 10000}
-  m_Maximized: 0
+  m_Maximized: 1
 --- !u!114 &2
-MonoBehaviour:
-  m_ObjectHideFlags: 52
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 0}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
-  m_Name: ConsoleWindow
-  m_EditorClassIdentifier: 
-  m_Children: []
-  m_Position:
-    serializedVersion: 2
-    x: 847
-    y: 0
-    width: 551
-    height: 342
-  m_MinSize: {x: 102, y: 126}
-  m_MaxSize: {x: 4002, y: 4026}
-  m_ActualView: {fileID: 23}
-  m_Panes:
-  - {fileID: 23}
-  m_Selected: 0
-  m_LastSelected: 0
---- !u!114 &3
-MonoBehaviour:
-  m_ObjectHideFlags: 52
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 0}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_Children:
-  - {fileID: 13}
-  - {fileID: 2}
-  m_Position:
-    serializedVersion: 2
-    x: 0
-    y: 942
-    width: 1398
-    height: 342
-  m_MinSize: {x: 200, y: 56}
-  m_MaxSize: {x: 16192, y: 8096}
-  vertical: 0
-  controlID: 75
-  draggingID: 0
---- !u!114 &4
-MonoBehaviour:
-  m_ObjectHideFlags: 52
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 0}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
-  m_Name: GameView
-  m_EditorClassIdentifier: 
-  m_Children: []
-  m_Position:
-    serializedVersion: 2
-    x: 846
-    y: 0
-    width: 552
-    height: 942
-  m_MinSize: {x: 52, y: 76}
-  m_MaxSize: {x: 4002, y: 4026}
-  m_ActualView: {fileID: 17}
-  m_Panes:
-  - {fileID: 17}
-  - {fileID: 16}
-  m_Selected: 0
-  m_LastSelected: 1
---- !u!114 &5
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -116,22 +37,22 @@ MonoBehaviour:
   m_Name: 
   m_EditorClassIdentifier: 
   m_Children:
-  - {fileID: 6}
-  - {fileID: 8}
-  - {fileID: 7}
+  - {fileID: 3}
+  - {fileID: 5}
+  - {fileID: 4}
   m_Position:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 1718
-    height: 1340
+    width: 3440
+    height: 1349
   m_MinSize: {x: 875, y: 300}
   m_MaxSize: {x: 10000, y: 10000}
   m_UseTopView: 1
   m_TopViewHeight: 36
   m_UseBottomView: 1
   m_BottomViewHeight: 20
---- !u!114 &6
+--- !u!114 &3
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -148,12 +69,12 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 1718
+    width: 3440
     height: 36
   m_MinSize: {x: 50, y: 50}
   m_MaxSize: {x: 4000, y: 4000}
   m_ActualView: {fileID: 15}
---- !u!114 &7
+--- !u!114 &4
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -169,12 +90,12 @@ MonoBehaviour:
   m_Position:
     serializedVersion: 2
     x: 0
-    y: 1320
-    width: 1718
+    y: 1329
+    width: 3440
     height: 20
   m_MinSize: {x: 0, y: 0}
   m_MaxSize: {x: 0, y: 0}
---- !u!114 &8
+--- !u!114 &5
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -187,20 +108,20 @@ MonoBehaviour:
   m_Name: 
   m_EditorClassIdentifier: 
   m_Children:
-  - {fileID: 9}
+  - {fileID: 6}
   - {fileID: 14}
   m_Position:
     serializedVersion: 2
     x: 0
     y: 36
-    width: 1718
-    height: 1284
+    width: 3440
+    height: 1293
   m_MinSize: {x: 400, y: 112}
   m_MaxSize: {x: 32384, y: 16192}
   vertical: 0
-  controlID: 48
+  controlID: 22172
   draggingID: 0
---- !u!114 &9
+--- !u!114 &6
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -213,20 +134,20 @@ MonoBehaviour:
   m_Name: 
   m_EditorClassIdentifier: 
   m_Children:
-  - {fileID: 10}
-  - {fileID: 3}
+  - {fileID: 7}
+  - {fileID: 11}
   m_Position:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 1398
-    height: 1284
+    width: 2808
+    height: 1293
   m_MinSize: {x: 300, y: 112}
   m_MaxSize: {x: 24288, y: 16192}
   vertical: 1
-  controlID: 49
+  controlID: 22173
   draggingID: 0
---- !u!114 &10
+--- !u!114 &7
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -239,21 +160,21 @@ MonoBehaviour:
   m_Name: 
   m_EditorClassIdentifier: 
   m_Children:
-  - {fileID: 11}
-  - {fileID: 12}
-  - {fileID: 4}
+  - {fileID: 8}
+  - {fileID: 9}
+  - {fileID: 10}
   m_Position:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 1398
-    height: 942
+    width: 2808
+    height: 951
   m_MinSize: {x: 300, y: 56}
   m_MaxSize: {x: 24288, y: 8096}
   vertical: 0
-  controlID: 50
+  controlID: 22174
   draggingID: 0
---- !u!114 &11
+--- !u!114 &8
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -270,16 +191,16 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 309
-    height: 942
+    width: 594
+    height: 951
   m_MinSize: {x: 201, y: 226}
   m_MaxSize: {x: 4001, y: 4026}
-  m_ActualView: {fileID: 19}
+  m_ActualView: {fileID: 17}
   m_Panes:
-  - {fileID: 19}
+  - {fileID: 17}
   m_Selected: 0
   m_LastSelected: 0
---- !u!114 &12
+--- !u!114 &9
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -294,20 +215,73 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 309
+    x: 594
     y: 0
-    width: 537
-    height: 942
+    width: 1072
+    height: 951
   m_MinSize: {x: 202, y: 226}
   m_MaxSize: {x: 4002, y: 4026}
-  m_ActualView: {fileID: 20}
+  m_ActualView: {fileID: 18}
   m_Panes:
-  - {fileID: 20}
-  - {fileID: 21}
   - {fileID: 18}
+  - {fileID: 19}
+  - {fileID: 20}
   m_Selected: 0
   m_LastSelected: 2
---- !u!114 &13
+--- !u!114 &10
+MonoBehaviour:
+  m_ObjectHideFlags: 52
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
+  m_Name: GameView
+  m_EditorClassIdentifier: 
+  m_Children: []
+  m_Position:
+    serializedVersion: 2
+    x: 1666
+    y: 0
+    width: 1142
+    height: 951
+  m_MinSize: {x: 52, y: 76}
+  m_MaxSize: {x: 4002, y: 4026}
+  m_ActualView: {fileID: 16}
+  m_Panes:
+  - {fileID: 16}
+  - {fileID: 21}
+  m_Selected: 0
+  m_LastSelected: 1
+--- !u!114 &11
+MonoBehaviour:
+  m_ObjectHideFlags: 52
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Children:
+  - {fileID: 12}
+  - {fileID: 13}
+  m_Position:
+    serializedVersion: 2
+    x: 0
+    y: 951
+    width: 2808
+    height: 342
+  m_MinSize: {x: 200, y: 56}
+  m_MaxSize: {x: 16192, y: 8096}
+  vertical: 0
+  controlID: 22303
+  draggingID: 0
+--- !u!114 &12
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -324,7 +298,7 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 847
+    width: 1729
     height: 342
   m_MinSize: {x: 231, y: 276}
   m_MaxSize: {x: 10001, y: 10026}
@@ -333,6 +307,32 @@ MonoBehaviour:
   - {fileID: 22}
   m_Selected: 0
   m_LastSelected: 0
+--- !u!114 &13
+MonoBehaviour:
+  m_ObjectHideFlags: 52
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
+  m_Name: ConsoleWindow
+  m_EditorClassIdentifier: 
+  m_Children: []
+  m_Position:
+    serializedVersion: 2
+    x: 1729
+    y: 0
+    width: 1079
+    height: 342
+  m_MinSize: {x: 102, y: 126}
+  m_MaxSize: {x: 4002, y: 4026}
+  m_ActualView: {fileID: 23}
+  m_Panes:
+  - {fileID: 23}
+  m_Selected: 0
+  m_LastSelected: 0
 --- !u!114 &14
 MonoBehaviour:
   m_ObjectHideFlags: 52
@@ -348,10 +348,10 @@ MonoBehaviour:
   m_Children: []
   m_Position:
     serializedVersion: 2
-    x: 1398
+    x: 2808
     y: 0
-    width: 320
-    height: 1284
+    width: 632
+    height: 1293
   m_MinSize: {x: 276, y: 76}
   m_MaxSize: {x: 4001, y: 4026}
   m_ActualView: {fileID: 24}
@@ -382,7 +382,7 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 0
-    width: 1718
+    width: 3440
     height: 36
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -608,12 +608,12 @@ MonoBehaviour:
       displayed: 1
       id: Play Mode Controls
       index: 0
-      contents: '{"m_Layout":4,"m_Collapsed":false,"m_Folded":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":250.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}'
+      contents: '{"m_Layout":4,"m_Collapsed":false,"m_Folded":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-359.0,"y":-36.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}'
       floating: 0
       collapsed: 0
-      snapOffset: {x: 250, y: 0}
+      snapOffset: {x: -359, y: -36}
       snapOffsetDelta: {x: 0, y: 0}
-      snapCorner: 0
+      snapCorner: 3
       layout: 4
       size: {x: 0, y: 0}
       sizeOverridden: 0
@@ -626,44 +626,6 @@ MonoBehaviour:
     m_OverlaysVisible: 1
     m_DynamicPanelBehavior: 0
 --- !u!114 &16
-MonoBehaviour:
-  m_ObjectHideFlags: 52
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 0}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 13999, guid: 0000000000000000e000000000000000, type: 0}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_MinSize: {x: 972, y: 400}
-  m_MaxSize: {x: 4000, y: 4000}
-  m_TitleContent:
-    m_Text: UI Builder
-    m_Image: {fileID: 8683992553321208622, guid: 0000000000000000d000000000000000, type: 0}
-    m_Tooltip: 
-    m_TextWithWhitespace: "UI Builder\u200B"
-  m_Pos:
-    serializedVersion: 2
-    x: 1548
-    y: 79
-    width: 1250
-    height: 923
-  m_SerializedDataModeController:
-    m_DataMode: 0
-    m_PreferredDataMode: 0
-    m_SupportedDataModes: 
-    isAutomatic: 1
-  m_ViewDataDictionary: {fileID: 0}
-  m_OverlayCanvas:
-    m_LastAppliedPresetName: Default
-    m_SaveData: []
-    m_ContainerData: []
-    m_DynamicPanelContainerData: []
-    m_OverlaysVisible: 1
-    m_DynamicPanelBehavior: 0
---- !u!114 &17
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -684,10 +646,10 @@ MonoBehaviour:
     m_TextWithWhitespace: "Game\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 847
+    x: 1667
     y: 24
-    width: 550
-    height: 916
+    width: 1140
+    height: 925
   m_SerializedDataModeController:
     m_DataMode: 0
     m_PreferredDataMode: 0
@@ -707,7 +669,7 @@ MonoBehaviour:
   m_ShowGizmos: 0
   m_TargetDisplay: 0
   m_ClearColor: {r: 0, g: 0, b: 0, a: 0}
-  m_TargetSize: {x: 550, y: 895}
+  m_TargetSize: {x: 1140, y: 904}
   m_TextureFilterMode: 0
   m_TextureHideFlags: 61
   m_RenderIMGUI: 1
@@ -722,10 +684,10 @@ MonoBehaviour:
     m_VRangeLocked: 0
     hZoomLockedByDefault: 0
     vZoomLockedByDefault: 0
-    m_HBaseRangeMin: -275
-    m_HBaseRangeMax: 275
-    m_VBaseRangeMin: -447.5
-    m_VBaseRangeMax: 447.5
+    m_HBaseRangeMin: -570
+    m_HBaseRangeMax: 570
+    m_VBaseRangeMin: -452
+    m_VBaseRangeMax: 452
     m_HAllowExceedBaseRangeMin: 1
     m_HAllowExceedBaseRangeMax: 1
     m_VAllowExceedBaseRangeMin: 1
@@ -743,71 +705,30 @@ MonoBehaviour:
       serializedVersion: 2
       x: 0
       y: 21
-      width: 550
-      height: 895
+      width: 1140
+      height: 904
     m_Scale: {x: 1, y: 1}
-    m_Translation: {x: 275, y: 447.5}
+    m_Translation: {x: 570, y: 452}
     m_MarginLeft: 0
     m_MarginRight: 0
     m_MarginTop: 0
     m_MarginBottom: 0
     m_LastShownAreaInsideMargins:
       serializedVersion: 2
-      x: -275
-      y: -447.5
-      width: 550
-      height: 895
+      x: -570
+      y: -452
+      width: 1140
+      height: 904
     m_MinimalGUI: 1
   m_defaultScale: 1
-  m_LastWindowPixelSize: {x: 550, y: 916}
+  m_LastWindowPixelSize: {x: 1140, y: 925}
   m_ClearInEditMode: 1
   m_NoCameraWarning: 1
   m_LowResolutionForAspectRatios: 01000000000000000000
   m_XRRenderMode: 0
   m_RenderTexture: {fileID: 0}
   m_showToolbar: 1
---- !u!114 &18
-MonoBehaviour:
-  m_ObjectHideFlags: 52
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 0}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 12071, guid: 0000000000000000e000000000000000, type: 0}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_MinSize: {x: 50, y: 50}
-  m_MaxSize: {x: 4000, y: 4000}
-  m_TitleContent:
-    m_Text: Animation
-    m_Image: {fileID: -8166618308981325432, guid: 0000000000000000d000000000000000, type: 0}
-    m_Tooltip: 
-    m_TextWithWhitespace: "Animation\u200B"
-  m_Pos:
-    serializedVersion: 2
-    x: 625
-    y: 79
-    width: 974
-    height: 828
-  m_SerializedDataModeController:
-    m_DataMode: 0
-    m_PreferredDataMode: 0
-    m_SupportedDataModes: 
-    isAutomatic: 1
-  m_ViewDataDictionary: {fileID: 0}
-  m_OverlayCanvas:
-    m_LastAppliedPresetName: Default
-    m_SaveData: []
-    m_ContainerData: []
-    m_DynamicPanelContainerData: []
-    m_OverlaysVisible: 1
-    m_DynamicPanelBehavior: 0
-  m_LockTracker:
-    m_IsLocked: 0
-  m_LastSelectedObjectID: 0
---- !u!114 &19
+--- !u!114 &17
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -830,8 +751,8 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 24
-    width: 308
-    height: 916
+    width: 593
+    height: 925
   m_SerializedDataModeController:
     m_DataMode: 0
     m_PreferredDataMode: 0
@@ -852,8 +773,11 @@ MonoBehaviour:
       m_LastClickedID:
         m_Data: 0
       m_ExpandedIDs:
-      - m_Data: -25426
-      - m_Data: -19960
+      - m_Data: -150104
+      - m_Data: -150096
+      - m_Data: -68256
+      - m_Data: -68248
+      - m_Data: -1298
       m_RenameOverlay:
         m_UserAcceptedRename: 0
         m_Name: 
@@ -871,7 +795,7 @@ MonoBehaviour:
         m_OriginalEventType: 11
         m_IsRenamingFilename: 0
         m_TrimLeadingAndTrailingWhitespace: 0
-        m_ClientGUIView: {fileID: 11}
+        m_ClientGUIView: {fileID: 8}
       m_SearchString: 
     m_ExpandedScenes: []
     m_CurrenRootInstanceID: 0
@@ -879,7 +803,7 @@ MonoBehaviour:
       m_IsLocked: 0
     m_CurrentSortingName: TransformSorting
   m_WindowGUID: 4c969a2b90040154d917609493e03593
---- !u!114 &20
+--- !u!114 &18
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -900,10 +824,10 @@ MonoBehaviour:
     m_TextWithWhitespace: "Scene\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 310
+    x: 595
     y: 24
-    width: 535
-    height: 916
+    width: 1070
+    height: 925
   m_SerializedDataModeController:
     m_DataMode: 0
     m_PreferredDataMode: 0
@@ -1516,9 +1440,9 @@ MonoBehaviour:
   m_AudioPlay: 0
   m_DebugDrawModesUseInteractiveLightBakingData: 0
   m_Position:
-    m_Target: {x: 2.7589223, y: 1.1619864, z: 2.534564}
+    m_Target: {x: -16.798956, y: 4.9354935, z: -48.153038}
     speed: 2
-    m_Value: {x: 2.7589223, y: 1.1619864, z: 2.534564}
+    m_Value: {x: -16.798956, y: 4.9354935, z: -48.153038}
   m_RenderMode: 0
   m_CameraMode:
     drawMode: 0
@@ -1564,20 +1488,20 @@ MonoBehaviour:
     m_GridAxis: 1
     m_gridOpacity: 0.5
   m_Rotation:
-    m_Target: {x: -0.0153777, y: 0.960933, z: -0.27092808, w: -0.0545426}
+    m_Target: {x: -0.00019804298, y: 0.9997966, z: -0.016807096, w: -0.011696933}
     speed: 2
-    m_Value: {x: -0.015377701, y: 0.960933, z: -0.2709281, w: -0.0545426}
+    m_Value: {x: -0.00019804298, y: 0.9997966, z: -0.016807096, w: -0.011696933}
   m_Size:
-    m_Target: 5.9895763
+    m_Target: 67.092354
     speed: 2
-    m_Value: 5.9895763
+    m_Value: 67.092354
   m_Ortho:
     m_Target: 0
     speed: 2
     m_Value: 0
   m_CameraSettings:
-    m_Speed: 1.0005
-    m_SpeedNormalized: 0.49999997
+    m_Speed: 1.06047
+    m_SpeedNormalized: 0.53
     m_SpeedMin: 0.001
     m_SpeedMax: 2
     m_EasingEnabled: 1
@@ -1591,7 +1515,7 @@ MonoBehaviour:
   m_LastSceneViewRotation: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226}
   m_LastSceneViewOrtho: 0
   m_Viewpoint:
-    m_SceneView: {fileID: 20}
+    m_SceneView: {fileID: 18}
     m_CameraOverscanSettings:
       m_Opacity: 50
       m_Scale: 1
@@ -1604,7 +1528,7 @@ MonoBehaviour:
     name: Contributors / Receivers
     section: Lighting
   m_ViewIsLockedToObject: 0
---- !u!114 &21
+--- !u!114 &19
 MonoBehaviour:
   m_ObjectHideFlags: 52
   m_CorrespondingSourceObject: {fileID: 0}
@@ -1659,6 +1583,85 @@ MonoBehaviour:
   m_CurrentEditor: 0
   m_LayerEditor:
     m_SelectedLayerIndex: 0
+--- !u!114 &20
+MonoBehaviour:
+  m_ObjectHideFlags: 52
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 12071, guid: 0000000000000000e000000000000000, type: 0}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_MinSize: {x: 50, y: 50}
+  m_MaxSize: {x: 4000, y: 4000}
+  m_TitleContent:
+    m_Text: Animation
+    m_Image: {fileID: -8166618308981325432, guid: 0000000000000000d000000000000000, type: 0}
+    m_Tooltip: 
+    m_TextWithWhitespace: "Animation\u200B"
+  m_Pos:
+    serializedVersion: 2
+    x: 625
+    y: 79
+    width: 974
+    height: 828
+  m_SerializedDataModeController:
+    m_DataMode: 0
+    m_PreferredDataMode: 0
+    m_SupportedDataModes: 
+    isAutomatic: 1
+  m_ViewDataDictionary: {fileID: 0}
+  m_OverlayCanvas:
+    m_LastAppliedPresetName: Default
+    m_SaveData: []
+    m_ContainerData: []
+    m_DynamicPanelContainerData: []
+    m_OverlaysVisible: 1
+    m_DynamicPanelBehavior: 0
+  m_LockTracker:
+    m_IsLocked: 0
+  m_LastSelectedObjectID: 0
+--- !u!114 &21
+MonoBehaviour:
+  m_ObjectHideFlags: 52
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 13999, guid: 0000000000000000e000000000000000, type: 0}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_MinSize: {x: 972, y: 400}
+  m_MaxSize: {x: 4000, y: 4000}
+  m_TitleContent:
+    m_Text: UI Builder
+    m_Image: {fileID: 8683992553321208622, guid: 0000000000000000d000000000000000, type: 0}
+    m_Tooltip: 
+    m_TextWithWhitespace: "UI Builder\u200B"
+  m_Pos:
+    serializedVersion: 2
+    x: 1548
+    y: 79
+    width: 1250
+    height: 923
+  m_SerializedDataModeController:
+    m_DataMode: 0
+    m_PreferredDataMode: 0
+    m_SupportedDataModes: 
+    isAutomatic: 1
+  m_ViewDataDictionary: {fileID: 0}
+  m_OverlayCanvas:
+    m_LastAppliedPresetName: Default
+    m_SaveData: []
+    m_ContainerData: []
+    m_DynamicPanelContainerData: []
+    m_OverlaysVisible: 1
+    m_DynamicPanelBehavior: 0
 --- !u!114 &22
 MonoBehaviour:
   m_ObjectHideFlags: 52
@@ -1682,7 +1685,7 @@ MonoBehaviour:
     serializedVersion: 2
     x: 0
     y: 24
-    width: 846
+    width: 1728
     height: 316
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -1706,9 +1709,9 @@ MonoBehaviour:
     m_SceneHandles: []
     m_ShowAllHits: 0
     m_SkipHidden: 0
-    m_SearchArea: 1
+    m_SearchArea: 0
     m_Folders:
-    - Assets/Scripts/Maze
+    - Assets/Scripts/Maze/Agent
     m_Globs: []
     m_ProductIds: 
     m_AnyWithAssetOrigin: 0
@@ -1718,23 +1721,23 @@ MonoBehaviour:
   m_ViewMode: 1
   m_StartGridSize: 67
   m_LastFolders:
-  - Assets/Scripts/Maze
+  - Assets/Scripts/Maze/Agent
   m_LastFoldersGridSize: 67
   m_LastProjectPath: C:\Users\Axel-PC\MazeGenerator
   m_LockTracker:
     m_IsLocked: 0
   m_LastLocalAssetsSearchArea: 0
   m_FolderTreeState:
-    scrollPos: {x: 0, y: 0}
+    scrollPos: {x: 0, y: 44}
     m_SelectedIDs:
-    - m_Data: 54652
+    - m_Data: 46148
     m_LastClickedID:
-      m_Data: 54652
+      m_Data: 46148
     m_ExpandedIDs:
     - m_Data: 0
-    - m_Data: 45830
-    - m_Data: 45832
-    - m_Data: 45834
+    - m_Data: 45876
+    - m_Data: 45878
+    - m_Data: 45880
     m_RenameOverlay:
       m_UserAcceptedRename: 0
       m_Name: 
@@ -1752,7 +1755,7 @@ MonoBehaviour:
       m_OriginalEventType: 11
       m_IsRenamingFilename: 1
       m_TrimLeadingAndTrailingWhitespace: 0
-      m_ClientGUIView: {fileID: 13}
+      m_ClientGUIView: {fileID: 12}
     m_SearchString: 
     m_CreateAssetUtility:
       m_EndAction: {fileID: 0}
@@ -1767,9 +1770,9 @@ MonoBehaviour:
       m_Data: 0
     m_ExpandedIDs:
     - m_Data: 0
-    - m_Data: 45830
-    - m_Data: 45832
-    - m_Data: 45834
+    - m_Data: 45876
+    - m_Data: 45878
+    - m_Data: 45880
     m_RenameOverlay:
       m_UserAcceptedRename: 0
       m_Name: 
@@ -1797,9 +1800,9 @@ MonoBehaviour:
       m_ResourceFile: 
   m_ListAreaState:
     m_SelectedInstanceIDs:
-    - m_Data: 54364
-    m_LastClickedInstanceID: 54364
-    m_HadKeyboardFocusLastEvent: 0
+    - m_Data: -2678
+    m_LastClickedInstanceID: -2678
+    m_HadKeyboardFocusLastEvent: 1
     m_ExpandedInstanceIDs:
     - m_Data: 70016
     - m_Data: 49188
@@ -1821,7 +1824,7 @@ MonoBehaviour:
       m_OriginalEventType: 11
       m_IsRenamingFilename: 1
       m_TrimLeadingAndTrailingWhitespace: 0
-      m_ClientGUIView: {fileID: 13}
+      m_ClientGUIView: {fileID: 12}
     m_CreateAssetUtility:
       m_EndAction: {fileID: 0}
       m_InstanceID: 0
@@ -1854,9 +1857,9 @@ MonoBehaviour:
     m_TextWithWhitespace: "Console\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 848
+    x: 1730
     y: 24
-    width: 549
+    width: 1077
     height: 316
   m_SerializedDataModeController:
     m_DataMode: 0
@@ -1892,10 +1895,10 @@ MonoBehaviour:
     m_TextWithWhitespace: "Inspector\u200B"
   m_Pos:
     serializedVersion: 2
-    x: 1399
+    x: 2809
     y: 24
-    width: 319
-    height: 1258
+    width: 631
+    height: 1267
   m_SerializedDataModeController:
     m_DataMode: 0
     m_PreferredDataMode: 0