using System.Collections.Generic; using UnityEngine; namespace SynapticPro.GOAP { /// /// Base class for all GOAP actions /// Inherit from this to create custom actions /// public abstract class GOAPActionBase : MonoBehaviour { [Header("Action Settings")] [SerializeField] protected string actionName = "Action"; [SerializeField] protected float cost = 1f; [SerializeField] protected float duration = 1f; [Header("Target")] [SerializeField] protected GameObject target; [SerializeField] protected bool requiresTarget = false; [SerializeField] protected float targetRange = 2f; /// /// Preconditions that must be true for this action to run /// public Dictionary Preconditions { get; protected set; } = new Dictionary(); /// /// Effects this action has on the world state /// public Dictionary Effects { get; protected set; } = new Dictionary(); /// /// Cost of performing this action /// public float Cost { get => cost; set => cost = Mathf.Max(0.01f, value); } /// /// Name of this action /// public string ActionName => actionName; /// /// Duration of this action /// public float Duration => duration; /// /// Current target /// public GameObject Target { get => target; set => target = value; } /// /// Whether action is currently running /// public bool IsRunning { get; protected set; } /// /// Whether action has completed /// public bool IsDone { get; protected set; } protected virtual void Awake() { SetupPreconditionsAndEffects(); } /// /// Override to setup preconditions and effects /// protected virtual void SetupPreconditionsAndEffects() { // Override in derived classes } /// /// Add a precondition /// public void AddPrecondition(string key, object value) { if (!Preconditions.ContainsKey(key)) { Preconditions.Add(key, value); } else { Preconditions[key] = value; } } /// /// Remove a precondition /// public void RemovePrecondition(string key) { if (Preconditions.ContainsKey(key)) { Preconditions.Remove(key); } } /// /// Add an effect /// public void AddEffect(string key, object value) { if (!Effects.ContainsKey(key)) { Effects.Add(key, value); } else { Effects[key] = value; } } /// /// Remove an effect /// public void RemoveEffect(string key) { if (Effects.ContainsKey(key)) { Effects.Remove(key); } } /// /// Reset action state /// public virtual void Reset() { IsRunning = false; IsDone = false; } /// /// Check if agent is in range of target /// public bool IsInRange(GOAPAgent agent) { if (!requiresTarget || target == null) { return true; } float distance = Vector3.Distance(agent.transform.position, target.transform.position); return distance <= targetRange; } /// /// Check procedural preconditions (runtime checks) /// Override for custom runtime conditions /// public virtual bool CheckProceduralPrecondition(GOAPAgent agent) { return true; } /// /// Called before action starts /// Return true if action can start /// public virtual bool PrePerform(GOAPAgent agent) { IsRunning = true; IsDone = false; return true; } /// /// Called each frame while action is running /// Return true when action is complete /// public abstract bool Perform(GOAPAgent agent); /// /// Called after action completes /// Return true if action succeeded /// public virtual bool PostPerform(GOAPAgent agent) { IsRunning = false; IsDone = true; return true; } /// /// Called when action is interrupted /// public virtual void OnInterrupted(GOAPAgent agent) { IsRunning = false; IsDone = false; } /// /// Get string representation /// public override string ToString() { return $"{actionName} (Cost: {cost})"; } } /// /// Simple action that completes after duration /// Use for basic actions without complex logic /// public class GOAPSimpleAction : GOAPActionBase { private float elapsedTime; public override bool Perform(GOAPAgent agent) { elapsedTime += Time.deltaTime; if (elapsedTime >= duration) { elapsedTime = 0f; return true; } return false; } public override void Reset() { base.Reset(); elapsedTime = 0f; } } }