using System; using System.Collections.Generic; using UnityEngine; namespace SynapticPro.GOAP { /// /// GOAP Goal - Represents a goal the agent wants to achieve /// [Serializable] public class GOAPGoal { [SerializeField] private string goalName = "Goal"; [SerializeField] private int priority = 1; [SerializeField] private bool isActive = true; /// /// Desired world state to achieve this goal /// public Dictionary DesiredState { get; private set; } = new Dictionary(); /// /// Goal name for identification /// public string GoalName { get => goalName; set => goalName = value; } /// /// Priority (higher = more important) /// public int Priority { get => priority; set => priority = Mathf.Max(0, value); } /// /// Whether this goal is currently active /// public bool IsActive { get => isActive; set => isActive = value; } /// /// Create empty goal /// public GOAPGoal() { } /// /// Create goal with name and priority /// public GOAPGoal(string name, int priority = 1) { this.goalName = name; this.priority = priority; } /// /// Add a condition to the desired state /// public void AddCondition(string key, object value) { if (string.IsNullOrEmpty(key)) return; if (DesiredState.ContainsKey(key)) { DesiredState[key] = value; } else { DesiredState.Add(key, value); } } /// /// Remove a condition from desired state /// public void RemoveCondition(string key) { if (DesiredState.ContainsKey(key)) { DesiredState.Remove(key); } } /// /// Check if goal is satisfied by current world state /// public bool IsSatisfied(WorldState worldState) { if (worldState == null) return false; return worldState.Satisfies(DesiredState); } /// /// Get relevance of this goal (can be overridden for dynamic priority) /// public virtual float GetRelevance(GOAPAgent agent) { return isActive ? priority : 0; } /// /// Called when goal is activated /// public virtual void OnActivate(GOAPAgent agent) { } /// /// Called when goal is deactivated /// public virtual void OnDeactivate(GOAPAgent agent) { } /// /// Called when goal is achieved /// public virtual void OnAchieved(GOAPAgent agent) { } /// /// Clone this goal /// public GOAPGoal Clone() { var clone = new GOAPGoal(goalName, priority); clone.isActive = isActive; foreach (var kvp in DesiredState) { clone.DesiredState[kvp.Key] = kvp.Value; } return clone; } public override string ToString() { return $"{goalName} (Priority: {priority})"; } } /// /// Goal that changes priority based on conditions /// public class DynamicGOAPGoal : GOAPGoal { private Func relevanceCalculator; public DynamicGOAPGoal(string name, Func calculator) : base(name) { relevanceCalculator = calculator; } public override float GetRelevance(GOAPAgent agent) { if (!IsActive) return 0; return relevanceCalculator?.Invoke(agent) ?? Priority; } } /// /// MonoBehaviour wrapper for GOAPGoal /// Attach to agent to define goals in Inspector /// public class GOAPGoalComponent : MonoBehaviour { [Header("Goal Settings")] [SerializeField] private string goalName = "Goal"; [SerializeField] private int priority = 1; [SerializeField] private bool isActive = true; [Header("Desired State")] [SerializeField] private List conditions = new List(); private GOAPGoal _goal; /// /// Get the GOAPGoal instance /// public GOAPGoal Goal { get { if (_goal == null) { BuildGoal(); } return _goal; } } private void Awake() { BuildGoal(); } private void BuildGoal() { _goal = new GOAPGoal(goalName, priority); _goal.IsActive = isActive; foreach (var condition in conditions) { if (!string.IsNullOrEmpty(condition.key)) { _goal.AddCondition(condition.key, condition.GetValue()); } } } /// /// Refresh goal from inspector values /// public void RefreshGoal() { BuildGoal(); } } /// /// Serializable state condition for Inspector /// [Serializable] public class StateCondition { public string key; public StateValueType valueType = StateValueType.Bool; public bool boolValue = true; public int intValue = 0; public float floatValue = 0f; public string stringValue = ""; public object GetValue() { switch (valueType) { case StateValueType.Bool: return boolValue; case StateValueType.Int: return intValue; case StateValueType.Float: return floatValue; case StateValueType.String: return stringValue; default: return null; } } } public enum StateValueType { Bool, Int, Float, String } }