INTEGRATION_EXAMPLE.md 7.0 KB

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

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:

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:

[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:

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:

// 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:

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:

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.