From a5b04b49b6c7eee95c9f4d33d6d95b5ce69fcc3f Mon Sep 17 00:00:00 2001
From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Date: Thu, 15 Dec 2022 15:30:28 +1100
Subject: [PATCH] Fix NPC obstacle handling (#13007)
---
.../NPC/Components/NPCRVOComponent.cs | 38 ---------------
Content.Server/NPC/NPCBlackboard.cs | 5 ++
Content.Server/NPC/NPCBlackboardSerializer.cs | 15 +++++-
.../Systems/NPCSteeringSystem.Avoidance.cs | 38 ---------------
.../NPC/Systems/NPCSteeringSystem.Context.cs | 12 +++--
.../Systems/NPCSteeringSystem.Obstacles.cs | 48 +++++++++++--------
.../NPC/Systems/NPCSteeringSystem.cs | 12 +++--
Content.Shared/CCVar/CCVars.cs | 2 -
8 files changed, 63 insertions(+), 107 deletions(-)
delete mode 100644 Content.Server/NPC/Components/NPCRVOComponent.cs
delete mode 100644 Content.Server/NPC/Systems/NPCSteeringSystem.Avoidance.cs
diff --git a/Content.Server/NPC/Components/NPCRVOComponent.cs b/Content.Server/NPC/Components/NPCRVOComponent.cs
deleted file mode 100644
index 3fe76fc24e..0000000000
--- a/Content.Server/NPC/Components/NPCRVOComponent.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Content.Server.NPC.Systems;
-
-namespace Content.Server.NPC.Components;
-
-///
-/// Stores data for RVO collision avoidance
-///
-[RegisterComponent]
-public sealed class NPCRVOComponent : Component
-{
- ///
- /// Maximum number of dynamic neighbors to consider for collision avoidance.
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("maxNeighbors")]
- public int MaxNeighbors = 5;
-
- ///
- /// Time horizon to consider for dynamic neighbor collision
- ///
- [ViewVariables(VVAccess.ReadWrite)] public float TimeHorizon = 3f;
-
- ///
- /// Time horizon to consider for static neighbor collision.
- ///
- [ViewVariables(VVAccess.ReadWrite)] public float ObstacleTimeHorizon = 3f;
-
- ///
- /// Range considered for neighbor agents
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("neighborRange")]
- public float NeighborRange = 3f;
-
- [ViewVariables]
- public readonly HashSet ObstacleNeighbors = new();
-
- [ViewVariables]
- public readonly HashSet AgentNeighbors = new();
-}
diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs
index f0a87a2098..e60be3fb96 100644
--- a/Content.Server/NPC/NPCBlackboard.cs
+++ b/Content.Server/NPC/NPCBlackboard.cs
@@ -42,6 +42,11 @@ public sealed class NPCBlackboard : IEnumerable>
///
public bool ReadOnly = false;
+ public void Clear()
+ {
+ _blackboard.Clear();
+ }
+
public NPCBlackboard ShallowClone()
{
var dict = new NPCBlackboard();
diff --git a/Content.Server/NPC/NPCBlackboardSerializer.cs b/Content.Server/NPC/NPCBlackboardSerializer.cs
index e2335d5aad..ec7a0756c2 100644
--- a/Content.Server/NPC/NPCBlackboardSerializer.cs
+++ b/Content.Server/NPC/NPCBlackboardSerializer.cs
@@ -8,7 +8,7 @@ using Robust.Shared.Utility;
namespace Content.Server.NPC;
-public sealed class NPCBlackboardSerializer : ITypeReader
+public sealed class NPCBlackboardSerializer : ITypeReader, ITypeCopier
{
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
IDependencyCollection dependencies, ISerializationContext? context = null)
@@ -79,4 +79,17 @@ public sealed class NPCBlackboardSerializer : ITypeReader();
- configManager.OnValueChanged(CCVars.NPCCollisionAvoidance, SetCollisionAvoidance);
- }
-
- private void ShutdownAvoidance()
- {
- var configManager = IoCManager.Resolve();
- configManager.UnsubValueChanged(CCVars.NPCCollisionAvoidance, SetCollisionAvoidance);
- }
-
- // I deleted all of my relevant code for now as I only had dynamic body avoidance working and not static
- // but it will be added back real soon.
- private void SetCollisionAvoidance(bool obj)
- {
- CollisionAvoidanceEnabled = obj;
- }
-}
diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs
index 14a087b6f3..34c53b8e98 100644
--- a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs
+++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs
@@ -86,7 +86,7 @@ public sealed partial class NPCSteeringSystem
}
else
{
- arrivalDistance = SharedInteractionSystem.InteractionRange - 0.8f;
+ arrivalDistance = SharedInteractionSystem.InteractionRange - 0.65f;
}
// Check if mapids match.
@@ -105,9 +105,15 @@ public sealed partial class NPCSteeringSystem
if (direction.Length <= arrivalDistance)
{
// Node needs some kind of special handling like access or smashing.
- if (steering.CurrentPath.TryPeek(out var node))
+ if (steering.CurrentPath.TryPeek(out var node) && !node.Data.IsFreeSpace)
{
- var status = TryHandleFlags(steering, node, bodyQuery);
+ SteeringObstacleStatus status;
+
+ // Breaking behaviours and the likes.
+ lock (_obstacles)
+ {
+ status = TryHandleFlags(steering, node, bodyQuery);
+ }
// TODO: Need to handle re-pathing in case the target moves around.
switch (status)
diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs
index 042e9df734..03f1fb06f9 100644
--- a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs
+++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs
@@ -1,11 +1,12 @@
+using Content.Server.CombatMode;
using Content.Server.Destructible;
using Content.Server.NPC.Components;
using Content.Server.NPC.Pathfinding;
using Content.Shared.Doors.Components;
using Content.Shared.NPC;
-using Content.Shared.Weapons.Melee;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
+using Robust.Shared.Utility;
namespace Content.Server.NPC.Systems;
@@ -33,9 +34,7 @@ public sealed partial class NPCSteeringSystem
private SteeringObstacleStatus TryHandleFlags(NPCSteeringComponent component, PathPoly poly, EntityQuery bodyQuery)
{
- if (poly.Data.IsFreeSpace)
- return SteeringObstacleStatus.Completed;
-
+ DebugTools.Assert(!poly.Data.IsFreeSpace);
// TODO: Store PathFlags on the steering comp
// and be able to re-check it.
@@ -111,26 +110,35 @@ public sealed partial class NPCSteeringSystem
return SteeringObstacleStatus.Completed;
}
// Try smashing obstacles.
- else if ((component.Flags & PathFlags.Smashing) != 0x0 && TryComp(component.Owner, out var melee) &&
- TryComp(melee.Weapon, out var meleeWeapon))
+ else if ((component.Flags & PathFlags.Smashing) != 0x0)
{
- var destructibleQuery = GetEntityQuery();
+ var meleeWeapon = _melee.GetWeapon(component.Owner);
- // TODO: This is a hack around grilles and windows.
- _random.Shuffle(obstacleEnts);
-
- foreach (var ent in obstacleEnts)
+ if (meleeWeapon != null && meleeWeapon.NextAttack <= _timing.CurTime && TryComp(component.Owner, out var combatMode))
{
- // TODO: Validate we can damage it
- if (destructibleQuery.HasComponent(ent))
- {
- _melee.AttemptLightAttack(component.Owner, meleeWeapon, ent);
- return SteeringObstacleStatus.Continuing;
- }
- }
+ combatMode.IsInCombatMode = true;
+ var destructibleQuery = GetEntityQuery();
- if (obstacleEnts.Count == 0)
- return SteeringObstacleStatus.Completed;
+ // TODO: This is a hack around grilles and windows.
+ _random.Shuffle(obstacleEnts);
+
+ foreach (var ent in obstacleEnts)
+ {
+ // TODO: Validate we can damage it
+ if (destructibleQuery.HasComponent(ent))
+ {
+ _melee.AttemptLightAttack(component.Owner, meleeWeapon, ent);
+ break;
+ }
+ }
+
+ combatMode.IsInCombatMode = false;
+
+ if (obstacleEnts.Count == 0)
+ return SteeringObstacleStatus.Completed;
+
+ return SteeringObstacleStatus.Continuing;
+ }
}
return SteeringObstacleStatus.Failed;
diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs
index 1f5a369188..6423d026f3 100644
--- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs
+++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs
@@ -41,6 +41,7 @@ namespace Content.Server.NPC.Systems
[Dependency] private readonly IAdminManager _admin = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
+ [Dependency] private readonly IDependencyCollection _dependencies = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
@@ -66,6 +67,8 @@ namespace Content.Server.NPC.Systems
private readonly HashSet _subscribedSessions = new();
+ private object _obstacles = new();
+
public override void Initialize()
{
base.Initialize();
@@ -76,7 +79,6 @@ namespace Content.Server.NPC.Systems
}
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
- InitializeAvoidance();
_configManager.OnValueChanged(CCVars.NPCEnabled, SetNPCEnabled);
_configManager.OnValueChanged(CCVars.NPCPathfinding, SetNPCPathfinding);
@@ -114,7 +116,6 @@ namespace Content.Server.NPC.Systems
public override void Shutdown()
{
base.Shutdown();
- ShutdownAvoidance();
_configManager.UnsubValueChanged(CCVars.NPCEnabled, SetNPCEnabled);
}
@@ -131,6 +132,7 @@ namespace Content.Server.NPC.Systems
private void OnSteeringShutdown(EntityUid uid, NPCSteeringComponent component, ComponentShutdown args)
{
+ // Cancel any active pathfinding jobs as they're irrelevant.
component.PathfindToken?.Cancel();
}
@@ -151,7 +153,6 @@ namespace Content.Server.NPC.Systems
component.Flags = _pathfindingSystem.GetFlags(uid);
}
- EnsureComp(uid);
component.Coordinates = coordinates;
return component;
}
@@ -185,7 +186,6 @@ namespace Content.Server.NPC.Systems
component.PathfindToken?.Cancel();
component.PathfindToken = null;
- RemComp(uid);
RemComp(uid);
}
@@ -203,7 +203,7 @@ namespace Content.Server.NPC.Systems
var npcs = EntityQuery()
.ToArray();
- var options = new ParallelOptions()
+ var options = new ParallelOptions
{
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
};
@@ -261,6 +261,8 @@ namespace Content.Server.NPC.Systems
EntityQuery xformQuery,
float frameTime)
{
+ IoCManager.InitThread(_dependencies, replaceExisting: true);
+
if (Deleted(steering.Coordinates.EntityId))
{
SetDirection(mover, steering, Vector2.Zero);
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index 461246482d..36931b3eb6 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -542,8 +542,6 @@ namespace Content.Shared.CCVar
///
public static readonly CVarDef NPCPathfinding = CVarDef.Create("npc.pathfinding", true);
- public static readonly CVarDef NPCCollisionAvoidance = CVarDef.Create("npc.collision_avoidance", true);
-
/*
* Net
*/