diff --git a/Content.Server/NPC/Components/NPCJukeComponent.cs b/Content.Server/NPC/Components/NPCJukeComponent.cs
index 2c4136c24b9..768feeca6fc 100644
--- a/Content.Server/NPC/Components/NPCJukeComponent.cs
+++ b/Content.Server/NPC/Components/NPCJukeComponent.cs
@@ -1,4 +1,3 @@
-using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.NPC.Components;
@@ -6,17 +5,20 @@ namespace Content.Server.NPC.Components;
[RegisterComponent, AutoGenerateComponentPause]
public sealed partial class NPCJukeComponent : Component
{
- [DataField("jukeType")]
+ [DataField]
public JukeType JukeType = JukeType.Away;
- [DataField("jukeDuration")]
+ [DataField]
public float JukeDuration = 0.5f;
- [DataField("nextJuke", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ [DataField]
+ public float JukeCooldown = 3f;
+
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextJuke;
- [DataField("targetTile")]
+ [DataField]
public Vector2i? TargetTile;
}
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs
index 02a3b085104..68029f5a4c2 100644
--- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs
+++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs
@@ -6,17 +6,31 @@ public sealed partial class JukeOperator : HTNOperator, IHtnConditionalShutdown
{
[Dependency] private readonly IEntityManager _entManager = default!;
- [DataField("jukeType")]
+ [DataField]
public JukeType JukeType = JukeType.AdjacentTile;
- [DataField("shutdownState")]
+ [DataField]
public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.PlanFinished;
+ ///
+ /// Controls how long(in seconds) the NPC will move while juking.
+ ///
+ [DataField]
+ public float JukeDuration = 0.5f;
+
+ ///
+ /// Controls how often (in seconds) an NPC will try to juke.
+ ///
+ [DataField]
+ public float JukeCooldown = 3f;
+
public override void Startup(NPCBlackboard blackboard)
{
base.Startup(blackboard);
var juke = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner));
juke.JukeType = JukeType;
+ juke.JukeDuration = JukeDuration;
+ juke.JukeCooldown = JukeCooldown;
}
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs
index da9fa1f7615..5a724762ef6 100644
--- a/Content.Server/NPC/Systems/NPCJukeSystem.cs
+++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs
@@ -3,6 +3,7 @@
using Content.Server.NPC.Events;
using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat;
using Content.Server.Weapons.Melee;
+using Content.Shared.Coordinates.Helpers;
using Content.Shared.NPC;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Collections;
@@ -38,22 +39,19 @@ public override void Initialize()
private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSteeringEvent args)
{
- if (component.JukeType == JukeType.AdjacentTile)
+ if (_timing.CurTime < component.NextJuke)
{
- if (_npcRangedQuery.TryGetComponent(uid, out var ranged) &&
- ranged.Status == CombatStatus.NotInSight)
- {
- component.TargetTile = null;
- return;
- }
+ component.TargetTile = null;
+ return;
+ }
- if (_timing.CurTime < component.NextJuke)
- {
- component.TargetTile = null;
- return;
- }
+ component.NextJuke = _timing.CurTime + TimeSpan.FromSeconds(component.JukeCooldown);
- if (!TryComp(args.Transform.GridUid, out var grid))
+ if (component.JukeType == JukeType.AdjacentTile)
+ {
+ if (_npcRangedQuery.TryGetComponent(uid, out var ranged)
+ && ranged.Status is CombatStatus.NotInSight
+ || !TryComp(args.Transform.GridUid, out var grid))
{
component.TargetTile = null;
return;
@@ -107,12 +105,11 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt
var elapsed = _timing.CurTime - component.NextJuke;
- // Finished juke, reset timer.
- if (elapsed.TotalSeconds > component.JukeDuration ||
- currentTile == component.TargetTile)
+ // Finished juke.
+ if (elapsed.TotalSeconds > component.JukeDuration
+ || currentTile == component.TargetTile)
{
component.TargetTile = null;
- component.NextJuke = _timing.CurTime + TimeSpan.FromSeconds(component.JukeDuration);
return;
}
@@ -155,9 +152,7 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt
var obstacleDirection = _transform.GetWorldPosition(melee.Target) - args.WorldPosition;
if (obstacleDirection == Vector2.Zero)
- {
obstacleDirection = _random.NextVector2();
- }
// If they're moving away then pursue anyway.
// If just hit then always back up a bit.