Add pathfinding support for NPCs climbing tables (#17415)
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using Content.Server.Examine;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.Pathfinding;
|
||||
using Content.Shared.Climbing;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.NPC;
|
||||
@@ -35,6 +36,30 @@ public sealed partial class NPCSteeringSystem
|
||||
|
||||
#region Seek
|
||||
|
||||
/// <summary>
|
||||
/// Takes into account agent-specific context that may allow it to bypass a node which is not FreeSpace.
|
||||
/// </summary>
|
||||
private bool IsFreeSpace(
|
||||
EntityUid uid,
|
||||
NPCSteeringComponent steering,
|
||||
PathPoly node)
|
||||
{
|
||||
if (node.Data.IsFreeSpace)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Handle the case where the node is a climb, we can climb, and we are climbing.
|
||||
else if ((node.Data.Flags & PathfindingBreadcrumbFlag.Climb) != 0x0 &&
|
||||
(steering.Flags & PathFlags.Climbing) != 0x0 &&
|
||||
TryComp<ClimbingComponent>(uid, out var climbing) &&
|
||||
climbing.IsClimbing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to head to the target destination, either via the next pathfinding node or the final target.
|
||||
/// </summary>
|
||||
@@ -90,7 +115,7 @@ public sealed partial class NPCSteeringSystem
|
||||
}
|
||||
// If next node is a free tile then get within its bounds.
|
||||
// This is to avoid popping it too early
|
||||
else if (steering.CurrentPath.TryPeek(out var node) && node.Data.IsFreeSpace)
|
||||
else if (steering.CurrentPath.TryPeek(out var node) && IsFreeSpace(uid, steering, node))
|
||||
{
|
||||
arrivalDistance = MathF.Min(node.Box.Width / 2f, node.Box.Height / 2f) - 0.01f;
|
||||
}
|
||||
@@ -117,7 +142,7 @@ 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) && !node.Data.IsFreeSpace)
|
||||
if (steering.CurrentPath.TryPeek(out var node) && !IsFreeSpace(uid, steering, node))
|
||||
{
|
||||
// Ignore stuck while handling obstacles.
|
||||
ResetStuck(steering, ourCoordinates);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Destructible;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.Pathfinding;
|
||||
using Content.Shared.Climbing;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Doors.Components;
|
||||
@@ -74,6 +75,7 @@ public sealed partial class NPCSteeringSystem
|
||||
GetObstacleEntities(poly, mask, layer, obstacleEnts);
|
||||
var isDoor = (poly.Data.Flags & PathfindingBreadcrumbFlag.Door) != 0x0;
|
||||
var isAccessRequired = (poly.Data.Flags & PathfindingBreadcrumbFlag.Access) != 0x0;
|
||||
var isClimbable = (poly.Data.Flags & PathfindingBreadcrumbFlag.Climb) != 0x0;
|
||||
|
||||
// Just walk into it stupid
|
||||
if (isDoor && !isAccessRequired)
|
||||
@@ -121,6 +123,38 @@ public sealed partial class NPCSteeringSystem
|
||||
if (obstacleEnts.Count == 0)
|
||||
return SteeringObstacleStatus.Completed;
|
||||
}
|
||||
// Try climbing obstacles
|
||||
else if ((component.Flags & PathFlags.Climbing) != 0x0 && isClimbable)
|
||||
{
|
||||
if (TryComp<ClimbingComponent>(uid, out var climbing))
|
||||
{
|
||||
if (climbing.IsClimbing)
|
||||
{
|
||||
return SteeringObstacleStatus.Completed;
|
||||
}
|
||||
else if (climbing.OwnerIsTransitioning)
|
||||
{
|
||||
return SteeringObstacleStatus.Continuing;
|
||||
}
|
||||
|
||||
var climbableQuery = GetEntityQuery<ClimbableComponent>();
|
||||
|
||||
// Get the relevant obstacle
|
||||
foreach (var ent in obstacleEnts)
|
||||
{
|
||||
if (climbableQuery.TryGetComponent(ent, out var table) &&
|
||||
_climb.CanVault(table, uid, uid, out _) &&
|
||||
_climb.TryClimb(uid, uid, ent, out id, table, climbing))
|
||||
{
|
||||
component.DoAfterId = id;
|
||||
return SteeringObstacleStatus.Continuing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obstacleEnts.Count == 0)
|
||||
return SteeringObstacleStatus.Completed;
|
||||
}
|
||||
// Try smashing obstacles.
|
||||
else if ((component.Flags & PathFlags.Smashing) != 0x0)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Climbing;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.NPC.Components;
|
||||
@@ -50,6 +51,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ClimbSystem _climb = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly DoorSystem _doors = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
Reference in New Issue
Block a user