using System; using System.Collections.Generic; using UnityEngine; namespace SynapticPro.GOAP { /// /// Dynamic GOAP Action - Can be configured at runtime via code /// Used for procedurally generated behaviors from natural language /// public class GOAPDynamicAction : GOAPActionBase { private Func performFunc; private Func checkPreconditionFunc; private Action onCompleteFunc; private float elapsedTime; /// /// Configure the action at runtime /// public void Configure( string name, float actionCost, float actionDuration, Dictionary preconditions, Dictionary effects) { actionName = name; cost = actionCost; duration = actionDuration; if (preconditions != null) { foreach (var kvp in preconditions) { AddPrecondition(kvp.Key, kvp.Value); } } if (effects != null) { foreach (var kvp in effects) { AddEffect(kvp.Key, kvp.Value); } } } /// /// Set custom perform function /// public void SetPerformFunction(Func func) { performFunc = func; } /// /// Set custom precondition check /// public void SetPreconditionCheck(Func func) { checkPreconditionFunc = func; } /// /// Set on complete callback /// public void SetOnComplete(Action func) { onCompleteFunc = func; } public override bool CheckProceduralPrecondition(GOAPAgent agent) { if (checkPreconditionFunc != null) { return checkPreconditionFunc(agent); } return base.CheckProceduralPrecondition(agent); } public override bool Perform(GOAPAgent agent) { if (performFunc != null) { return performFunc(agent); } // Default behavior: wait for duration elapsedTime += Time.deltaTime; if (elapsedTime >= duration) { elapsedTime = 0f; return true; } return false; } public override bool PostPerform(GOAPAgent agent) { onCompleteFunc?.Invoke(agent); return base.PostPerform(agent); } public override void Reset() { base.Reset(); elapsedTime = 0f; } } /// /// Factory for creating dynamic GOAP actions /// public static class GOAPActionFactory { /// /// Create a simple wait action /// public static GOAPDynamicAction CreateWaitAction(GameObject parent, string name, float waitTime) { var action = parent.AddComponent(); action.Configure(name, 0.1f, waitTime, null, null); return action; } /// /// Create a movement action /// public static GOAPDynamicAction CreateMoveAction( GameObject parent, string name, float cost, string[] preconditions, string[] effects) { var action = parent.AddComponent(); var precondDict = new Dictionary(); foreach (var p in preconditions) { precondDict[p] = true; } var effectDict = new Dictionary(); foreach (var e in effects) { effectDict[e] = true; } action.Configure(name, cost, 1f, precondDict, effectDict); return action; } /// /// Create action from parsed behavior data /// public static GOAPDynamicAction CreateFromBehaviorData( GameObject parent, string name, float cost, string[] preconditions, string[] effects, float duration = 1f) { var action = parent.AddComponent(); var precondDict = new Dictionary(); if (preconditions != null) { foreach (var p in preconditions) { if (!string.IsNullOrEmpty(p)) { precondDict[p] = true; } } } var effectDict = new Dictionary(); if (effects != null) { foreach (var e in effects) { if (!string.IsNullOrEmpty(e)) { effectDict[e] = true; } } } action.Configure(name, cost, duration, precondDict, effectDict); return action; } /// /// Create patrol action with waypoints /// public static GOAPDynamicAction CreatePatrolAction(GameObject parent, Transform[] waypoints) { var action = parent.AddComponent(); action.Configure("Patrol", 1f, 2f, null, new Dictionary { ["patrolling"] = true }); int currentWaypoint = 0; action.SetPerformFunction((agent) => { if (waypoints == null || waypoints.Length == 0) { return true; } var target = waypoints[currentWaypoint]; if (target == null) { return true; } float distance = Vector3.Distance(agent.transform.position, target.position); if (distance < 1f) { currentWaypoint = (currentWaypoint + 1) % waypoints.Length; return true; } // Move towards waypoint Vector3 direction = (target.position - agent.transform.position).normalized; agent.transform.position += direction * agent.MoveSpeed * Time.deltaTime; return false; }); return action; } /// /// Create attack action /// public static GOAPDynamicAction CreateAttackAction(GameObject parent, float damage, float attackRange) { var action = parent.AddComponent(); action.Configure("Attack", 1.5f, 1f, new Dictionary { ["enemy_in_range"] = true, ["has_weapon"] = true }, new Dictionary { ["damage_dealt"] = true }); action.SetPerformFunction((agent) => { // Attack logic here Debug.Log($"[GOAP] {agent.name} performing attack"); return true; // Attack is instant }); return action; } /// /// Create flee action /// public static GOAPDynamicAction CreateFleeAction(GameObject parent) { var action = parent.AddComponent(); action.Configure("Flee", 0.5f, 3f, new Dictionary { ["health_low"] = true }, new Dictionary { ["is_safe"] = true }); action.SetPerformFunction((agent) => { // Find escape direction (opposite of threat) var threat = GameObject.FindGameObjectWithTag("Enemy"); if (threat != null) { Vector3 fleeDirection = (agent.transform.position - threat.transform.position).normalized; agent.transform.position += fleeDirection * agent.MoveSpeed * Time.deltaTime; } return false; // Keep fleeing }); return action; } /// /// Create collect resource action /// public static GOAPDynamicAction CreateCollectAction(GameObject parent) { var action = parent.AddComponent(); action.Configure("Collect", 1f, 2f, new Dictionary { ["resource_nearby"] = true }, new Dictionary { ["resource_collected"] = true }); return action; } } }