using System; using System.Collections.Generic; using Goap.Agent.Core; using Goap.Agent.Runtime; using Goap.Core; namespace Goap.Runtime { public class ActionBuilder : ActionBuilder where T : IAction { public ActionBuilder(WorldKeyBuilder worldKeyBuilder, TargetKeyBuilder targetKeyBuilder) : base(typeof(T), worldKeyBuilder, targetKeyBuilder) { } /// /// Sets the target key for the action. /// /// The type of the target key. /// The current instance of . public ActionBuilder SetTarget() where TTargetKey : ITargetKey { this.config.Target = this.targetKeyBuilder.GetKey(); return this; } /// /// Sets the base cost for the action. /// /// The base cost. /// The current instance of . public ActionBuilder SetBaseCost(float baseCost) { this.config.BaseCost = baseCost; return this; } /// /// Sets whether the target should be validated when running the action. /// /// True if the target should be validated; otherwise, false. /// The current instance of . public ActionBuilder SetValidateTarget(bool validate) { this.config.ValidateTarget = validate; return this; } /// /// Sets whether the action requires a target. /// /// True if the action requires a target; otherwise, false. /// The current instance of . public ActionBuilder SetRequiresTarget(bool requiresTarget) { this.config.RequiresTarget = requiresTarget; return this; } /// /// Sets whether the conditions should be validated when running the action. /// /// True if the conditions should be validated; otherwise, false. /// The current instance of . public ActionBuilder SetValidateConditions(bool validate) { this.config.ValidateConditions = validate; return this; } /// /// Sets the stopping distance for the action. This is the distance at which the action will stop moving towards the /// target. /// /// The stopping distance. /// The current instance of . public ActionBuilder SetStoppingDistance(float inRange) { this.config.StoppingDistance = inRange; return this; } [Obsolete("Use `SetStoppingDistance(float inRange)` instead.")] public ActionBuilder SetInRange(float inRange) { this.config.StoppingDistance = inRange; return this; } /// /// Sets the move mode for the action. /// /// The move mode. /// The current instance of . public ActionBuilder SetMoveMode(ActionMoveMode moveMode) { this.config.MoveMode = moveMode; return this; } /// /// Adds a condition to the action. /// /// The type of the world key. /// The comparison type. /// The amount for the condition. /// The current instance of . public ActionBuilder AddCondition(Comparison comparison, int amount) where TWorldKey : IWorldKey { this.conditions.Add(new Condition { WorldKey = this.worldKeyBuilder.GetKey(), Comparison = comparison, Amount = amount, }); return this; } [Obsolete("Use `AddEffect(EffectType type)` instead.")] public ActionBuilder AddEffect(bool increase) where TWorldKey : IWorldKey { this.effects.Add(new Effect { WorldKey = this.worldKeyBuilder.GetKey(), Increase = increase, }); return this; } /// /// Adds an effect to the action. /// /// The type of the world key. /// The effect type. /// The current instance of . public ActionBuilder AddEffect(EffectType type) where TWorldKey : IWorldKey { this.effects.Add(new Effect { WorldKey = this.worldKeyBuilder.GetKey(), Increase = type == EffectType.Increase, }); return this; } /// /// Sets the properties for the action. /// /// The action properties. /// The current instance of . public ActionBuilder SetProperties(IActionProperties properties) { this.ValidateProperties(properties); this.config.Properties = properties; return this; } /// /// Sets the callback for when the action is created. This can be used to set up the action with custom data. /// /// The callback action. /// The current instance of . public ActionBuilder SetCallback(Action callback) { this.config.Callback = (obj) => callback((T) obj); return this; } } public class ActionBuilder { protected readonly ActionConfig config; protected readonly List conditions = new(); protected readonly List effects = new(); protected readonly WorldKeyBuilder worldKeyBuilder; protected readonly TargetKeyBuilder targetKeyBuilder; protected readonly Type actionType; public ActionBuilder(Type actionType, WorldKeyBuilder worldKeyBuilder, TargetKeyBuilder targetKeyBuilder) { this.actionType = actionType; this.worldKeyBuilder = worldKeyBuilder; this.targetKeyBuilder = targetKeyBuilder; var propType = this.GetPropertiesType(); this.config = new ActionConfig { Name = actionType.Name, ClassType = actionType.AssemblyQualifiedName, BaseCost = 1, StoppingDistance = 0.5f, RequiresTarget = true, ValidateConditions = true, ValidateTarget = true, Properties = (IActionProperties) Activator.CreateInstance(propType), }; } protected void ValidateProperties(IActionProperties properties) { var actionPropsType = this.GetPropertiesType(); if (actionPropsType == TypeReSolveHelper.ResolveType(properties)) return; throw new ArgumentException( $"The provided properties do not match the expected type '{actionPropsType.Name}'.", nameof(properties)); } protected Type GetPropertiesType() { var baseType = this.actionType.BaseType; while (baseType != null) { if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(GoapActionBase<,>)) return baseType.GetGenericArguments()[1]; if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(GoapActionBase<>)) return typeof(EmptyActionProperties); baseType = baseType.BaseType; } return null; } public IActionConfig Build() { this.config.Conditions = this.conditions.ToArray(); this.config.Effects = this.effects.ToArray(); return this.config; } public static ActionBuilder Create(WorldKeyBuilder worldKeyBuilder, TargetKeyBuilder targetKeyBuilder) where TAction : IAction { return new ActionBuilder(worldKeyBuilder, targetKeyBuilder); } public static ActionBuilder Create(Type _type, WorldKeyBuilder worldKeyBuilder, TargetKeyBuilder targetKeyBuilder) { return new ActionBuilder(_type, worldKeyBuilder, targetKeyBuilder); } public ActionBuilder AddEffect(Type _worldKeyType, EffectType _type) { this.effects.Add(new Effect { WorldKey = this.worldKeyBuilder.GetKey(_worldKeyType), Increase = _type == EffectType.Increase, }); return this; } public ActionBuilder SetTarget(Type _targetKeyType) { this.config.Target = this.targetKeyBuilder.GetKey(_targetKeyType); return this; } public ActionBuilder SetBaseCost(float baseCost) { this.config.BaseCost = baseCost; return this; } public ActionBuilder AddCondition(Type _worldKeyType, Comparison comparison, int amount) { this.conditions.Add(new Condition { WorldKey = this.worldKeyBuilder.GetKey(_worldKeyType), Comparison = comparison, Amount = amount, }); return this; } public ActionBuilder SetValidateTarget(bool validate) { this.config.ValidateTarget = validate; return this; } public ActionBuilder SetRequiresTarget(bool requiresTarget) { this.config.RequiresTarget = requiresTarget; return this; } public ActionBuilder SetValidateConditions(bool validate) { this.config.ValidateConditions = validate; return this; } public ActionBuilder SetStoppingDistance(float inRange) { this.config.StoppingDistance = inRange; return this; } public ActionBuilder SetMoveMode(ActionMoveMode moveMode) { this.config.MoveMode = moveMode; return this; } public ActionBuilder SetProperties(IActionProperties properties) { this.ValidateProperties(properties); this.config.Properties = properties; return this; } public ActionBuilder SetCallback(Action callback) { this.config.Callback = callback; return this; } } }