diff --git a/Demo/Assets/CrashKonijn/GOAP/Demos/Complex/Actions/EatAction.cs b/Demo/Assets/CrashKonijn/GOAP/Demos/Complex/Actions/EatAction.cs index b7563f51..e8027889 100644 --- a/Demo/Assets/CrashKonijn/GOAP/Demos/Complex/Actions/EatAction.cs +++ b/Demo/Assets/CrashKonijn/GOAP/Demos/Complex/Actions/EatAction.cs @@ -5,7 +5,6 @@ using CrashKonijn.Goap.Demos.Complex.Goap; using CrashKonijn.Goap.Demos.Complex.Interfaces; using CrashKonijn.Goap.Runtime; -using UnityEngine; namespace CrashKonijn.Goap.Demos.Complex.Actions { @@ -17,10 +16,8 @@ public void Inject(GoapInjector injector) { this.instanceHandler = injector.instanceHandler; } - - public override void Created() - { - } + + public override void Created() { } public override void Start(IMonoAgent agent, Data data) { @@ -32,7 +29,7 @@ public override bool IsValid(IActionReceiver agent, Data data) { if (data.Eatable == null) return false; - + return true; } @@ -47,18 +44,20 @@ public override IActionRunState Perform(IMonoAgent agent, Data data, IActionCont if (data.Eatable.NutritionValue > 0) return ActionRunState.Continue; - + data.Inventory.Remove(data.Eatable); this.instanceHandler.QueueForDestroy(data.Eatable); - + return ActionRunState.Completed; } public override void End(IMonoAgent agent, Data data) { + this.Disable(ActionDisabler.ForTime(5f)); + if (data.Eatable == null) return; - + if (data.Eatable.NutritionValue > 0) data.Inventory.Add(data.Eatable); } @@ -67,12 +66,12 @@ public class Data : IActionData { public ITarget Target { get; set; } public IEatable Eatable { get; set; } - + [GetComponent] public ComplexInventoryBehaviour Inventory { get; set; } - + [GetComponent] public ComplexHungerBehaviour ComplexHunger { get; set; } } } -} \ No newline at end of file +} diff --git a/Demo/Assets/CrashKonijn/GOAP/Demos/Simple/Behaviours/SimpleHungerBehaviour.cs b/Demo/Assets/CrashKonijn/GOAP/Demos/Simple/Behaviours/SimpleHungerBehaviour.cs index 479581a6..ecb6c78f 100644 --- a/Demo/Assets/CrashKonijn/GOAP/Demos/Simple/Behaviours/SimpleHungerBehaviour.cs +++ b/Demo/Assets/CrashKonijn/GOAP/Demos/Simple/Behaviours/SimpleHungerBehaviour.cs @@ -7,19 +7,19 @@ namespace CrashKonijn.Goap.Demos.Simple.Behaviours { public class SimpleHungerBehaviour : MonoBehaviour { - private AgentBehaviour agent; + private GoapActionProvider actionProvider; public float hunger = 50; [Button(nameof(EnablePickup))] public int enableButton; - + [Button(nameof(DisablePickup))] public int disableButton; private void Awake() { this.hunger = Random.Range(0, 100f); - this.agent = this.GetComponent(); + this.actionProvider = this.GetComponent(); } private void FixedUpdate() @@ -29,12 +29,18 @@ private void FixedUpdate() public void EnablePickup() { - this.agent.EnableAction(); + foreach (var pickupAppleAction in this.actionProvider.GetActions()) + { + pickupAppleAction.Enable(); + } } public void DisablePickup() { - this.agent.DisableAction(); + foreach (var pickupAppleAction in this.actionProvider.GetActions()) + { + pickupAppleAction.Disable(ActionDisabler.ForTime(1f)); + } } } -} \ No newline at end of file +} diff --git a/Demo/CrashKonijn.Agent.Runtime.csproj.DotSettings b/Demo/CrashKonijn.Agent.Runtime.csproj.DotSettings index f51e5ca7..61b8d0ab 100644 --- a/Demo/CrashKonijn.Agent.Runtime.csproj.DotSettings +++ b/Demo/CrashKonijn.Agent.Runtime.csproj.DotSettings @@ -1,2 +1,3 @@  + True True \ No newline at end of file diff --git a/Package/Documentation/Classes/Actions.md b/Package/Documentation/Classes/Actions.md index 1dd782fd..65e83a66 100644 --- a/Package/Documentation/Classes/Actions.md +++ b/Package/Documentation/Classes/Actions.md @@ -97,6 +97,44 @@ The action class defines the behavior of the action. It should be stateless sinc The `IActionRunState` interface is a crucial component in the GOAP system. It defines the contract for action run states, which are responsible for determining the behavior of actions during their execution. These states decide when an action should be updated, stopped, performed, completed, or even resolved. They can also be used to 'pause' running an action, and come back later to continue it. +### Enabling/Disabling Actions +Each action can be enabled or disabled using the `action.Enable()` or `action.Disable(IActionDisabler)` methods. By default the following disablers are available: + +{% code lineNumbers="true" %} +```csharp +public static class ActionDisabler +{ + public static IActionDisabler Forever => new ForeverActionDisabler(); + public static IActionDisabler ForTime(float time) => new ForTimeActionDisabler(time); +} +``` +{% endcode %} + +#### Examples + +{% code lineNumbers="true" %} +```csharp +foreach (var pickupAppleAction in this.actionProvider.GetActions()) +{ + pickupAppleAction.Disable(ActionDisabler.Forever); +} +``` +{% endcode %} + +{% code lineNumbers="true" %} +```csharp +public class EatAction : GoapActionBase +{ + // Other methods omitted for brevity + public override void End(IMonoAgent agent, Data data) + { + // This will disable the action for 5 seconds + this.Disable(ActionDisabler.ForTime(5f)); + } +} +``` +{% endcode %} + #### Methods - `void Update(IAgent agent, IActionContext context)`: Updates the state of the action based on the current context and agent state. This method is called every frame during the action's execution. diff --git a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAction.cs b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAction.cs index cf4e1646..4f94d16a 100644 --- a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAction.cs +++ b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAction.cs @@ -44,5 +44,7 @@ public interface IAction bool IsExecutable(IActionReceiver agent, bool conditionsMet); bool IsEnabled(IActionReceiver agent); + void Enable(); + void Disable(IActionDisabler disabler); } } diff --git a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs new file mode 100644 index 00000000..32c13c34 --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs @@ -0,0 +1,7 @@ +namespace CrashKonijn.Agent.Core +{ + public interface IActionDisabler + { + bool IsDisabled(IAgent agent); + } +} diff --git a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs.meta b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs.meta new file mode 100644 index 00000000..fd8ca54a --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IActionDisabler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56ba43858e4b45f4863d858cc8ad7a3f +timeCreated: 1731487316 \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAgent.cs b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAgent.cs index 058680d0..6fbda071 100644 --- a/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAgent.cs +++ b/Package/Runtime/CrashKonijn.Agent.Core/Interfaces/IAgent.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; namespace CrashKonijn.Agent.Core { public interface IAgent : IActionReceiver { - List DisabledActions { get; } AgentState State { get; } AgentMoveState MoveState { get; } @@ -26,8 +24,6 @@ public interface IAgent : IActionReceiver void CompleteAction(bool resolveAction = true); void ResolveAction(); - void EnableAction() where TAction : IAction; - void DisableAction() where TAction : IAction; [Obsolete("Use GoapActionProvider.CurrentPlan.Goal instead")] object CurrentGoal { get; set; } diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/ActionBase.cs b/Package/Runtime/CrashKonijn.Agent.Runtime/ActionBase.cs index bf6452aa..3ffef73d 100644 --- a/Package/Runtime/CrashKonijn.Agent.Runtime/ActionBase.cs +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/ActionBase.cs @@ -6,6 +6,8 @@ public abstract class AgentActionBase where TActionData : IActionData, new() where TActionProperties : class, IActionProperties, new() { + private IActionDisabler disabler; + public IActionData GetData() { return this.CreateData(); @@ -29,11 +31,28 @@ public bool IsEnabled(IActionReceiver agent) return this.IsEnabled(agent, agent.Injector); } + public void Enable() + { + this.disabler = null; + } + + public void Disable(IActionDisabler disabler) + { + this.disabler = disabler; + } + public virtual bool IsEnabled(IActionReceiver receiver, IComponentReference references) { - if (receiver is IMonoAgent agent) - return !agent.DisabledActions.Contains(this.GetType()); + if (this.disabler == null) + return true; + + if (receiver is not IMonoAgent agent) + return true; + + if (this.disabler.IsDisabled(agent)) + return false; + this.Enable(); return true; } diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/AgentBehaviour.cs b/Package/Runtime/CrashKonijn.Agent.Runtime/AgentBehaviour.cs index e802c41d..015f715c 100644 --- a/Package/Runtime/CrashKonijn.Agent.Runtime/AgentBehaviour.cs +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/AgentBehaviour.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using CrashKonijn.Agent.Core; using UnityEngine; @@ -30,7 +29,6 @@ public IActionProvider ActionProvider public AgentState State { get; private set; } = AgentState.NoAction; public AgentMoveState MoveState { get; private set; } = AgentMoveState.Idle; - public List DisabledActions { get; } = new(); [Obsolete("Use ActionState.Action instead.")] public IAction CurrentAction => this.ActionState.Action; @@ -232,22 +230,18 @@ private void ResetAction() this.UpdateTarget(); } + [Obsolete("Enable actions from within the action itself, or disable using actionProvider.GetActions.ForEach((action) => action.Enable(IActionDisabler))")] public void EnableAction() where TAction : IAction { - if (!this.DisabledActions.Contains(typeof(TAction))) - return; - - this.DisabledActions.Remove(typeof(TAction)); + throw new Exception("Enable actions from within the action itself, or disable using actionProvider.GetActions.ForEach((action) => action.Enable(IActionDisabler))"); } + [Obsolete("Disable actions from within the action itself, or disable using actionProvider.GetActions.ForEach((action) => action.Disable(IActionDisabler))")] public void DisableAction() where TAction : IAction { - if (this.DisabledActions.Contains(typeof(TAction))) - return; - - this.DisabledActions.Add(typeof(TAction)); + throw new Exception("Disable actions from within the action itself, or disable using actionProvider.GetActions.ForEach((action) => action.Disable(IActionDisabler))"); } } } diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers.meta b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers.meta new file mode 100644 index 00000000..d5ef8bbe --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e444390a58940c3b1e2e6931cc7de17 +timeCreated: 1731489590 \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs new file mode 100644 index 00000000..e2023f7b --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs @@ -0,0 +1,10 @@ +using CrashKonijn.Agent.Core; + +namespace CrashKonijn.Agent.Runtime +{ + public static class ActionDisabler + { + public static IActionDisabler Forever => new ForeverActionDisabler(); + public static IActionDisabler ForTime(float time) => new ForTimeActionDisabler(time); + } +} diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs.meta b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs.meta new file mode 100644 index 00000000..92a5dcaf --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ActionDisabler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 278a5df3910047df8819228b1bd19015 +timeCreated: 1731487385 \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs new file mode 100644 index 00000000..cf73ca7e --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs @@ -0,0 +1,20 @@ +using CrashKonijn.Agent.Core; +using UnityEngine; + +namespace CrashKonijn.Agent.Runtime +{ + public class ForTimeActionDisabler : IActionDisabler + { + private readonly float enableAt; + + public ForTimeActionDisabler(float time) + { + this.enableAt = Time.time + time; + } + + public bool IsDisabled(IAgent agent) + { + return Time.time < this.enableAt; + } + } +} \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs.meta b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs.meta new file mode 100644 index 00000000..4ab1c943 --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForTimeActionDisabler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0a75579c9fdc4a08b02c88d78bdd7911 +timeCreated: 1731489603 \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs new file mode 100644 index 00000000..db1e65dc --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs @@ -0,0 +1,9 @@ +using CrashKonijn.Agent.Core; + +namespace CrashKonijn.Agent.Runtime +{ + public class ForeverActionDisabler : IActionDisabler + { + public bool IsDisabled(IAgent agent) => true; + } +} \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs.meta b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs.meta new file mode 100644 index 00000000..6310e3a0 --- /dev/null +++ b/Package/Runtime/CrashKonijn.Agent.Runtime/Disablers/ForeverActionDisabler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c7b5d4794ebb4efbb8c05ce01a1a7f7d +timeCreated: 1731489603 \ No newline at end of file diff --git a/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IAgentType.cs b/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IAgentType.cs index 8ac686e6..f00d3f4f 100644 --- a/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IAgentType.cs +++ b/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IAgentType.cs @@ -14,6 +14,7 @@ public interface IAgentType void Unregister(IMonoGoapActionProvider actionProvider); List GetAllNodes(); List GetActions(); + List GetActions() where TAction : IGoapAction; List GetGoals(); TGoal ResolveGoal() @@ -21,4 +22,4 @@ TGoal ResolveGoal() bool AllConditionsMet(IGoapActionProvider actionProvider, IGoapAction action); } -} \ No newline at end of file +} diff --git a/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IGoapActionProvider.cs b/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IGoapActionProvider.cs index 532e8b40..2c507c2b 100644 --- a/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IGoapActionProvider.cs +++ b/Package/Runtime/CrashKonijn.Goap.Core/Interfaces/IGoapActionProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using CrashKonijn.Agent.Core; using UnityEngine; @@ -53,6 +54,7 @@ public void RequestGoal(bool endAction) void ClearGoal(); void StopAction(bool resolveAction = true); void SetDistanceMultiplierSpeed(float speed); + List GetActions() where TAction : IGoapAction; #region Obsolete diff --git a/Package/Runtime/CrashKonijn.Goap.Runtime/Behaviours/GoapActionProvider.cs b/Package/Runtime/CrashKonijn.Goap.Runtime/Behaviours/GoapActionProvider.cs index 46d6192f..7a384666 100644 --- a/Package/Runtime/CrashKonijn.Goap.Runtime/Behaviours/GoapActionProvider.cs +++ b/Package/Runtime/CrashKonijn.Goap.Runtime/Behaviours/GoapActionProvider.cs @@ -296,6 +296,8 @@ private void ValidateSetup() throw new GoapException($"There is no ActionReceiver assigned to the agent '{this.name}'! You're probably missing the ActionProvider on the AgentBehaviour."); } + public List GetActions() where TAction : IGoapAction => this.AgentType.GetActions(); + #region Obsolete Methods [Obsolete("Use CurrentPlan.Goal instead")] diff --git a/Package/Runtime/CrashKonijn.Goap.Runtime/Classes/AgentType.cs b/Package/Runtime/CrashKonijn.Goap.Runtime/Classes/AgentType.cs index 8ee13c69..510ac6c2 100644 --- a/Package/Runtime/CrashKonijn.Goap.Runtime/Classes/AgentType.cs +++ b/Package/Runtime/CrashKonijn.Goap.Runtime/Classes/AgentType.cs @@ -66,6 +66,7 @@ public bool AllConditionsMet(IGoapActionProvider actionProvider, IGoapAction act public List GetAllNodes() => this.actions.Cast().Concat(this.goals).ToList(); public List GetActions() => this.actions; + public List GetActions() where TAction : IGoapAction => this.actions.OfType().ToList(); public List GetGoals() => this.goals; } }