diff --git a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs index 07eeb3e911..2916474724 100644 --- a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs +++ b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs @@ -1,4 +1,5 @@ -using Content.Server.AI.Utility; +#nullable enable +using Content.Server.AI.Utility; using Content.Server.AI.WorldState.States.Inventory; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.Utility; diff --git a/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs b/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs index 6b5b438663..a31042b2af 100644 --- a/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs +++ b/Content.Server/AI/Operators/Inventory/PickupEntityOperator.cs @@ -1,3 +1,4 @@ +#nullable enable using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems.Click; @@ -20,11 +21,9 @@ namespace Content.Server.AI.Operators.Inventory _target = target; } - // TODO: When I spawn new entities they seem to duplicate clothing or something? public override Outcome Execute(float frameTime) { - if (_target == null || - _target.Deleted || + if (_target.Deleted || !_target.HasComponent() || ContainerHelpers.IsInContainer(_target) || !InteractionChecks.InRangeUnobstructed(_owner, _target.Transform.MapPosition)) diff --git a/Content.Server/AI/Operators/Inventory/UseItemInHandsOperator.cs b/Content.Server/AI/Operators/Inventory/UseItemInInventoryOperator.cs similarity index 85% rename from Content.Server/AI/Operators/Inventory/UseItemInHandsOperator.cs rename to Content.Server/AI/Operators/Inventory/UseItemInInventoryOperator.cs index 130b139ad0..7f35892984 100644 --- a/Content.Server/AI/Operators/Inventory/UseItemInHandsOperator.cs +++ b/Content.Server/AI/Operators/Inventory/UseItemInInventoryOperator.cs @@ -1,3 +1,4 @@ +#nullable enable using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Robust.Shared.Interfaces.GameObjects; @@ -7,12 +8,12 @@ namespace Content.Server.AI.Operators.Inventory /// /// Will find the item in storage, put it in an active hand, then use it /// - public class UseItemInHandsOperator : AiOperator + public class UseItemInInventoryOperator : AiOperator { private readonly IEntity _owner; private readonly IEntity _target; - public UseItemInHandsOperator(IEntity owner, IEntity target) + public UseItemInInventoryOperator(IEntity owner, IEntity target) { _owner = owner; _target = target; @@ -20,11 +21,6 @@ namespace Content.Server.AI.Operators.Inventory public override Outcome Execute(float frameTime) { - if (_target == null) - { - return Outcome.Failed; - } - // TODO: Also have this check storage a la backpack etc. if (!_owner.TryGetComponent(out HandsComponent handsComponent)) { diff --git a/Content.Server/AI/Operators/Movement/MoveToEntityOperator.cs b/Content.Server/AI/Operators/Movement/MoveToEntityOperator.cs index dd1d2120c8..35a15293e7 100644 --- a/Content.Server/AI/Operators/Movement/MoveToEntityOperator.cs +++ b/Content.Server/AI/Operators/Movement/MoveToEntityOperator.cs @@ -16,12 +16,20 @@ namespace Content.Server.AI.Operators.Movement public float ArrivalDistance { get; } public float PathfindingProximity { get; } - public MoveToEntityOperator(IEntity owner, IEntity target, float arrivalDistance = 1.0f, float pathfindingProximity = 1.5f) + private bool _requiresInRangeUnobstructed; + + public MoveToEntityOperator( + IEntity owner, + IEntity target, + float arrivalDistance = 1.0f, + float pathfindingProximity = 1.5f, + bool requiresInRangeUnobstructed = false) { _owner = owner; _target = target; ArrivalDistance = arrivalDistance; PathfindingProximity = pathfindingProximity; + _requiresInRangeUnobstructed = requiresInRangeUnobstructed; } public override bool TryStartup() @@ -32,7 +40,7 @@ namespace Content.Server.AI.Operators.Movement } var steering = EntitySystem.Get(); - _request = new EntityTargetSteeringRequest(_target, ArrivalDistance, PathfindingProximity); + _request = new EntityTargetSteeringRequest(_target, ArrivalDistance, PathfindingProximity, _requiresInRangeUnobstructed); steering.Register(_owner, _request); return true; } diff --git a/Content.Server/AI/Operators/Nutrition/UseDrinkInInventoryOperator.cs b/Content.Server/AI/Operators/Nutrition/UseDrinkInInventoryOperator.cs new file mode 100644 index 0000000000..366197a2fd --- /dev/null +++ b/Content.Server/AI/Operators/Nutrition/UseDrinkInInventoryOperator.cs @@ -0,0 +1,73 @@ +#nullable enable +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.Components.Nutrition; +using Content.Shared.GameObjects.Components.Nutrition; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Random; + +namespace Content.Server.AI.Operators.Nutrition +{ + public class UseDrinkInInventoryOperator : AiOperator + { + private readonly IEntity _owner; + private readonly IEntity _target; + private float _interactionCooldown; + + public UseDrinkInInventoryOperator(IEntity owner, IEntity target) + { + _owner = owner; + _target = target; + } + + public override Outcome Execute(float frameTime) + { + if (_interactionCooldown >= 0) + { + _interactionCooldown -= frameTime; + return Outcome.Continuing; + } + + // TODO: Also have this check storage a la backpack etc. + if (_target.Deleted || + !_owner.TryGetComponent(out HandsComponent handsComponent) || + !_target.TryGetComponent(out ItemComponent itemComponent)) + { + return Outcome.Failed; + } + + DrinkComponent? drinkComponent = null; + + foreach (var slot in handsComponent.ActivePriorityEnumerable()) + { + if (handsComponent.GetItem(slot) != itemComponent) continue; + handsComponent.ActiveHand = slot; + if (!_target.TryGetComponent(out drinkComponent)) + { + return Outcome.Failed; + } + + // This should also implicitly open it. + handsComponent.ActivateItem(); + _interactionCooldown = IoCManager.Resolve().NextFloat() + 0.5f; + } + + if (drinkComponent == null) + { + return Outcome.Failed; + } + + if (drinkComponent.Deleted || + drinkComponent.Empty || + _owner.TryGetComponent(out ThirstComponent thirstComponent) && + thirstComponent.CurrentThirst >= thirstComponent.ThirstThresholds[ThirstThreshold.Okay]) + { + return Outcome.Success; + } + + return Outcome.Continuing; + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Operators/Nutrition/UseFoodInInventoryOperator.cs b/Content.Server/AI/Operators/Nutrition/UseFoodInInventoryOperator.cs new file mode 100644 index 0000000000..7814a42d7e --- /dev/null +++ b/Content.Server/AI/Operators/Nutrition/UseFoodInInventoryOperator.cs @@ -0,0 +1,73 @@ +#nullable enable +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.Components.Nutrition; +using Content.Shared.GameObjects.Components.Nutrition; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Random; + +namespace Content.Server.AI.Operators.Nutrition +{ + public class UseFoodInInventoryOperator : AiOperator + { + private readonly IEntity _owner; + private readonly IEntity _target; + private float _interactionCooldown; + + public UseFoodInInventoryOperator(IEntity owner, IEntity target) + { + _owner = owner; + _target = target; + } + + public override Outcome Execute(float frameTime) + { + if (_interactionCooldown >= 0) + { + _interactionCooldown -= frameTime; + return Outcome.Continuing; + } + + // TODO: Also have this check storage a la backpack etc. + if (_target.Deleted || + !_owner.TryGetComponent(out HandsComponent handsComponent) || + !_target.TryGetComponent(out ItemComponent itemComponent)) + { + return Outcome.Failed; + } + + FoodComponent? foodComponent = null; + + foreach (var slot in handsComponent.ActivePriorityEnumerable()) + { + if (handsComponent.GetItem(slot) != itemComponent) continue; + handsComponent.ActiveHand = slot; + if (!_target.TryGetComponent(out foodComponent)) + { + return Outcome.Failed; + } + + // This should also implicitly open it. + handsComponent.ActivateItem(); + _interactionCooldown = IoCManager.Resolve().NextFloat() + 0.5f; + } + + if (foodComponent == null) + { + return Outcome.Failed; + } + + if (_target.Deleted || + foodComponent.UsesRemaining == 0 || + _owner.TryGetComponent(out HungerComponent hungerComponent) && + hungerComponent.CurrentHunger >= hungerComponent.HungerThresholds[HungerThreshold.Okay]) + { + return Outcome.Success; + } + + return Outcome.Continuing; + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Operators/Sequences/GoPickupEntitySequence.cs b/Content.Server/AI/Operators/Sequences/GoPickupEntitySequence.cs index 65922d2366..48cf084cbe 100644 --- a/Content.Server/AI/Operators/Sequences/GoPickupEntitySequence.cs +++ b/Content.Server/AI/Operators/Sequences/GoPickupEntitySequence.cs @@ -11,7 +11,7 @@ namespace Content.Server.AI.Operators.Sequences { Sequence = new Queue(new AiOperator[] { - new MoveToEntityOperator(owner, target), + new MoveToEntityOperator(owner, target, requiresInRangeUnobstructed: true), new OpenStorageOperator(owner, target), new PickupEntityOperator(owner, target), }); diff --git a/Content.Server/AI/Utility/Actions/Clothing/Gloves/EquipGloves.cs b/Content.Server/AI/Utility/Actions/Clothing/Gloves/EquipGloves.cs index fcd63a2da6..1804f2e798 100644 --- a/Content.Server/AI/Utility/Actions/Clothing/Gloves/EquipGloves.cs +++ b/Content.Server/AI/Utility/Actions/Clothing/Gloves/EquipGloves.cs @@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Gloves ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseItemInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Actions/Clothing/Head/EquipHead.cs b/Content.Server/AI/Utility/Actions/Clothing/Head/EquipHead.cs index 8e37b88f5d..144aa99430 100644 --- a/Content.Server/AI/Utility/Actions/Clothing/Head/EquipHead.cs +++ b/Content.Server/AI/Utility/Actions/Clothing/Head/EquipHead.cs @@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Head ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseItemInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Actions/Clothing/OuterClothing/EquipOuterClothing.cs b/Content.Server/AI/Utility/Actions/Clothing/OuterClothing/EquipOuterClothing.cs index 53e83765a1..4a2460e63c 100644 --- a/Content.Server/AI/Utility/Actions/Clothing/OuterClothing/EquipOuterClothing.cs +++ b/Content.Server/AI/Utility/Actions/Clothing/OuterClothing/EquipOuterClothing.cs @@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.OuterClothing ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseItemInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Actions/Clothing/Shoes/EquipShoes.cs b/Content.Server/AI/Utility/Actions/Clothing/Shoes/EquipShoes.cs index 07f0da5f8b..b15bb5e82b 100644 --- a/Content.Server/AI/Utility/Actions/Clothing/Shoes/EquipShoes.cs +++ b/Content.Server/AI/Utility/Actions/Clothing/Shoes/EquipShoes.cs @@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Shoes ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseItemInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Actions/Nutrition/Drink/PickUpDrink.cs b/Content.Server/AI/Utility/Actions/Nutrition/Drink/PickUpDrink.cs index 0549fc1ed7..d6a13d6794 100644 --- a/Content.Server/AI/Utility/Actions/Nutrition/Drink/PickUpDrink.cs +++ b/Content.Server/AI/Utility/Actions/Nutrition/Drink/PickUpDrink.cs @@ -40,10 +40,6 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Drink return new[] { - considerationsManager.Get() - .BoolCurve(context), - considerationsManager.Get() - .PresetCurve(context, PresetCurve.Nutrition), considerationsManager.Get() .PresetCurve(context, PresetCurve.Distance), considerationsManager.Get() diff --git a/Content.Server/AI/Utility/Actions/Nutrition/Drink/UseDrinkInInventory.cs b/Content.Server/AI/Utility/Actions/Nutrition/Drink/UseDrinkInInventory.cs index f05838ce22..e94cd74356 100644 --- a/Content.Server/AI/Utility/Actions/Nutrition/Drink/UseDrinkInInventory.cs +++ b/Content.Server/AI/Utility/Actions/Nutrition/Drink/UseDrinkInInventory.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Content.Server.AI.Operators; using Content.Server.AI.Operators.Inventory; +using Content.Server.AI.Operators.Nutrition; using Content.Server.AI.Utility.Considerations; using Content.Server.AI.Utility.Considerations.Inventory; using Content.Server.AI.Utility.Considerations.Nutrition.Drink; @@ -27,7 +28,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Drink ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseDrinkInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Actions/Nutrition/Food/UseFoodInInventory.cs b/Content.Server/AI/Utility/Actions/Nutrition/Food/UseFoodInInventory.cs index 6edcbb9c43..b12c40dbf8 100644 --- a/Content.Server/AI/Utility/Actions/Nutrition/Food/UseFoodInInventory.cs +++ b/Content.Server/AI/Utility/Actions/Nutrition/Food/UseFoodInInventory.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Content.Server.AI.Operators; using Content.Server.AI.Operators.Inventory; +using Content.Server.AI.Operators.Nutrition; using Content.Server.AI.Utility.Considerations; using Content.Server.AI.Utility.Considerations.Inventory; using Content.Server.AI.Utility.Considerations.Nutrition.Food; @@ -27,7 +28,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Food ActionOperators = new Queue(new AiOperator[] { new EquipEntityOperator(Owner, _entity), - new UseItemInHandsOperator(Owner, _entity), + new UseFoodInInventoryOperator(Owner, _entity), }); } diff --git a/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs b/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs index 64fce8bf56..021e35de23 100644 --- a/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs +++ b/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs @@ -11,7 +11,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat { var target = context.GetState().GetValue(); - if (target == null || !target.TryGetComponent(out IDamageableComponent damageableComponent)) + if (target == null || target.Deleted || !target.TryGetComponent(out IDamageableComponent damageableComponent)) { return 0.0f; } diff --git a/Content.Server/AI/Utility/Considerations/Movement/TargetDistanceCon.cs b/Content.Server/AI/Utility/Considerations/Movement/TargetDistanceCon.cs index ca63985179..a22fcc17e8 100644 --- a/Content.Server/AI/Utility/Considerations/Movement/TargetDistanceCon.cs +++ b/Content.Server/AI/Utility/Considerations/Movement/TargetDistanceCon.cs @@ -9,7 +9,7 @@ namespace Content.Server.AI.Utility.Considerations.Movement { var self = context.GetState().GetValue(); var target = context.GetState().GetValue(); - if (target == null || target.Transform.GridID != self.Transform.GridID) + if (target == null || target.Deleted || target.Transform.GridID != self.Transform.GridID) { return 0.0f; } diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index a52ebc1d48..5bfbebb9b1 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] public ReagentUnit TransferAmount { get; private set; } = ReagentUnit.New(2); [ViewVariables] - protected bool Opened { get; set; } + public bool Opened { get; protected set; } [ViewVariables] public bool Empty => _contents.CurrentVolume.Float() <= 0; diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs index 2d955dbe82..27b85bc7af 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs @@ -7,6 +7,7 @@ using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders; using Content.Server.GameObjects.EntitySystems.JobQueues; +using Content.Server.Utility; using Content.Shared.GameObjects.EntitySystems; using Robust.Server.Interfaces.Timing; using Robust.Shared.GameObjects.Components; @@ -42,6 +43,11 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering /// private const float TileTolerance = 0.8f; + /// + /// How long to wait between checks (if necessary). + /// + private const float InRangeUnobstructedCooldown = 0.25f; + private Dictionary RunningAgents => _agentLists[_listIndex]; // We'll cycle the running list every tick as all we're doing is getting a vector2 for the @@ -208,7 +214,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering foreach (var (agent, steering) in RunningAgents) { - var result = Steer(agent, steering); + // Yeah look it's not true frametime but good enough. + var result = Steer(agent, steering, frameTime * RunningAgents.Count); steering.Status = result; switch (result) @@ -236,9 +243,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering /// /// /// + /// /// /// - private SteeringStatus Steer(IEntity entity, IAiSteeringRequest steeringRequest) + private SteeringStatus Steer(IEntity entity, IAiSteeringRequest steeringRequest, float frameTime) { // Main optimisation to be done below is the redundant calls and adding more variables if (!entity.TryGetComponent(out AiControllerComponent controller) || !ActionBlockerSystem.CanMove(entity)) @@ -262,13 +270,27 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering // Check if we have arrived var targetDistance = (entity.Transform.MapPosition.Position - steeringRequest.TargetMap.Position).Length; - if (targetDistance <= steeringRequest.ArrivalDistance) + steeringRequest.TimeUntilInteractionCheck -= frameTime; + + if (targetDistance <= steeringRequest.ArrivalDistance && steeringRequest.TimeUntilInteractionCheck <= 0.0f) + { + if (!steeringRequest.RequiresInRangeUnobstructed || + InteractionChecks.InRangeUnobstructed(entity, steeringRequest.TargetMap, steeringRequest.ArrivalDistance, ignoredEnt: entity)) + { + // TODO: Need cruder LOS checks for ranged weaps + controller.VelocityDir = Vector2.Zero; + return SteeringStatus.Arrived; + } + + steeringRequest.TimeUntilInteractionCheck = InRangeUnobstructedCooldown; + // Welp, we'll keep on moving. + } + + // If we're really close don't swiggity swoogity back and forth and just wait for the interaction check maybe? + if (steeringRequest.TimeUntilInteractionCheck > 0.0f && targetDistance <= 0.1f) { - // TODO: If we need LOS and are moving to an entity then we may not be in range yet - // Chuck out a ray every half second or so and keep moving until we are? - // Alternatively could use tile-based LOS checks via the pathfindingsystem I guess controller.VelocityDir = Vector2.Zero; - return SteeringStatus.Arrived; + return SteeringStatus.Moving; } // Handle pathfinding job diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/EntityTargetSteeringRequest.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/EntityTargetSteeringRequest.cs index 8b5c221a5b..06da9d3bda 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/EntityTargetSteeringRequest.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/EntityTargetSteeringRequest.cs @@ -10,20 +10,26 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering public GridCoordinates TargetGrid => _target.Transform.GridPosition; public IEntity Target => _target; private IEntity _target; + /// public float ArrivalDistance { get; } + /// public float PathfindingProximity { get; } + /// - /// How far the target can move before we re-path + /// How far the target can move before we re-path /// public float TargetMaxMove { get; } = 1.5f; - /// - /// If we need LOS on the entity first before interaction - /// + /// public bool RequiresInRangeUnobstructed { get; } + /// + /// To avoid spamming InRangeUnobstructed we'll apply a cd to it. + /// + public float TimeUntilInteractionCheck { get; set; } + public EntityTargetSteeringRequest(IEntity target, float arrivalDistance, float pathfindingProximity = 0.5f, bool requiresInRangeUnobstructed = false) { _target = target; diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/GridTargetSteeringRequest.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/GridTargetSteeringRequest.cs index d054e77623..7a0efe74a7 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/GridTargetSteeringRequest.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/GridTargetSteeringRequest.cs @@ -14,7 +14,12 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering /// public float PathfindingProximity { get; } - public GridTargetSteeringRequest(GridCoordinates targetGrid, float arrivalDistance, float pathfindingProximity = 0.5f) + public bool RequiresInRangeUnobstructed { get; } + + public float TimeUntilInteractionCheck { get; set; } = 0.0f; + + + public GridTargetSteeringRequest(GridCoordinates targetGrid, float arrivalDistance, float pathfindingProximity = 0.5f, bool requiresInRangeUnobstructed = false) { // Get it once up front so we the manager doesn't have to continuously get it var mapManager = IoCManager.Resolve(); @@ -22,6 +27,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering TargetGrid = targetGrid; ArrivalDistance = arrivalDistance; PathfindingProximity = pathfindingProximity; + RequiresInRangeUnobstructed = requiresInRangeUnobstructed; } } } \ No newline at end of file diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/IAiSteeringRequest.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/IAiSteeringRequest.cs index da22b77b1c..264c675b2c 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/IAiSteeringRequest.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/IAiSteeringRequest.cs @@ -8,13 +8,23 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering MapCoordinates TargetMap { get; } GridCoordinates TargetGrid { get; } /// - /// How close we have to get before we've arrived + /// How close we have to get before we've arrived /// float ArrivalDistance { get; } /// - /// How close the pathfinder needs to get. Typically you want this set lower than ArrivalDistance + /// How close the pathfinder needs to get. Typically you want this set lower than ArrivalDistance /// float PathfindingProximity { get; } + + /// + /// If we need LOS on the entity first before interaction + /// + bool RequiresInRangeUnobstructed { get; } + + /// + /// To avoid spamming InRangeUnobstructed we'll apply a cd to it. + /// + public float TimeUntilInteractionCheck { get; set; } } } \ No newline at end of file