Skip to content

Commit

Permalink
Variable NPC Juke Aggression (#935)
Browse files Browse the repository at this point in the history
# Description

This PR un-hardcodes the JukeSystem timer, such that individual NPC HTN
Blackboards can directly state how often, and how far they wish to juke.
The effect of this is that NPCs are no longer completely impossible to
hit with left-clicks in melee combat, while also allowing for more
"Elite" enemies that juke more aggressively and more often to be
created.

Additionally, by introducing an exit condition based on this new "Juke
Cooldown", I have made the Juke Operator run a metric shitload of
expensive calculations 5000 times less often.

<details><summary><h1>Media</h1></summary>
<p>

Melee enemy with the new default juke settings, 0.5 second juke
duration, with 5 second juke Cooldown. The reagent slime will attempt to
close to melee with its enemy, but will now only attempt to evade melee
attacks once per 5 seconds. They will otherwise attempt to stay within
melee range.


https://github.com/user-attachments/assets/653e2064-e404-4be6-a958-da43096de502

</p>
</details>

# Changelog

:cl:
- tweak: JukeOperator now allows for JukeDuration and JukeCooldown
arguments. JukeCooldown is a new feature where enemies must wait an
amount of time in seconds equal to the JukeCooldown, before they are
allowed to attempt to dodge a melee attack.
- tweak: By default, NPCs will only attempt to dodge attacks once every
5 seconds.
- fix: JukeOperator now performs extremely expensive math 5000 times
less often. EXIT CONDITIONS PEOPLE!
  • Loading branch information
VMSolidus authored Sep 20, 2024
1 parent 6a653c5 commit 104ee3e
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 26 deletions.
12 changes: 7 additions & 5 deletions Content.Server/NPC/Components/NPCJukeComponent.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Controls how long(in seconds) the NPC will move while juking.
/// </summary>
[DataField]
public float JukeDuration = 0.5f;

/// <summary>
/// Controls how often (in seconds) an NPC will try to juke.
/// </summary>
[DataField]
public float JukeCooldown = 3f;

public override void Startup(NPCBlackboard blackboard)
{
base.Startup(blackboard);
var juke = _entManager.EnsureComponent<NPCJukeComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
juke.JukeType = JukeType;
juke.JukeDuration = JukeDuration;
juke.JukeCooldown = JukeCooldown;
}

public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
Expand Down
33 changes: 14 additions & 19 deletions Content.Server/NPC/Systems/NPCJukeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<MapGridComponent>(args.Transform.GridUid, out var grid))
if (component.JukeType == JukeType.AdjacentTile)
{
if (_npcRangedQuery.TryGetComponent(uid, out var ranged)
&& ranged.Status is CombatStatus.NotInSight
|| !TryComp<MapGridComponent>(args.Transform.GridUid, out var grid))
{
component.TargetTile = null;
return;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 104ee3e

Please sign in to comment.