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;
}
}
}