using System;
using System.Collections.Generic;
using UnityEngine;
namespace MazeGen
{
///
/// Autonomous agent decision-making system based on a prioritized decision tree.
///
/// Each decision encapsulates:
/// - A precondition (CanExecute)
/// - An action (Execute)
/// - Narration (Think)
///
/// The decision tree evaluates decisions in priority order and returns the first
/// one that can execute. This keeps behavior and narration synchronized and makes
/// the agent's decisions easy to understand and extend.
///
/// To extend agent behavior, simply create a new AgentDecision subclass and add
/// it to BuildDecisionTree(). No need to modify the Update loop or narration logic.
///
/// All timing runs off SimulationClock, not Time.deltaTime, so paused simulation
/// genuinely freezes every agent.
///
public class AutonomousAgentBrain : MonoBehaviour
{
private MazeAgentController _controller;
private MazeGenerator _generator;
private CombatDirector _director;
private float _decisionInterval;
private float _exploreChance;
private float _retreatThreshold = 1.5f;
private Func _isLevelEnded;
private float _timer;
private AgentDecisionTree _decisionTree;
public void Initialize(MazeAgentController controller, MazeGenerator generator, CombatDirector director, float initialDelay, float decisionInterval, float exploreChance, float retreatThreshold, Func isLevelEnded)
{
_controller = controller;
_generator = generator;
_director = director;
_decisionInterval = Mathf.Max(0.05f, decisionInterval);
_exploreChance = Mathf.Clamp01(exploreChance);
_retreatThreshold = Mathf.Max(0, retreatThreshold);
_isLevelEnded = isLevelEnded;
_timer = Mathf.Max(0f, initialDelay);
// Build the decision tree in priority order
BuildDecisionTree();
}
///
/// Constructs the decision tree with all available decisions in priority order.
/// Add new decisions here to extend agent behavior without modifying Update().
///
/// Example of adding a custom decision:
/// _decisionTree.Add(new MyCustomDecision(...));
///
private void BuildDecisionTree()
{
_decisionTree = new AgentDecisionTree();
// Priority order:
// 1. Heal if needed (health is more important than combat)
_decisionTree.Add(new UseHealthPotionDecision());
// 2. Handle existing fights (join or sneak past)
_decisionTree.Add(new HandleExistingFightDecision(_director, _retreatThreshold));
// 3. Declare new fights if appropriate
_decisionTree.Add(new DeclareFightDecision(_director, _retreatThreshold));
// 4. Sneak away if threatened
_decisionTree.Add(new SneakAwayDecision(_director, _retreatThreshold));
// 5. Catch-all: move to next room (always succeeds)
_decisionTree.Add(new MoveOnDecision(_generator, _controller, _exploreChance, _retreatThreshold));
}
private void Update()
{
MazeAgent agent = _controller?.Agent;
if (agent == null || agent.IsFinished || agent.IsDead) return;
if (_isLevelEnded != null && _isLevelEnded()) return;
// Committed to a fight: the CombatDirector owns this agent until it resolves.
if (agent.IsEngaged) return;
_timer -= SimulationClock.DeltaTime;
if (_timer > 0f) return;
_timer = _decisionInterval;
// Evaluate decision tree: returns true if a decision executed and was narrated
_decisionTree?.TryDecide(agent);
}
}
}