Add pathfinding support for NPCs climbing tables (#17415)
This commit is contained in:
@@ -29,7 +29,7 @@ public sealed class ClimbingTest : MovementTest
|
|||||||
|
|
||||||
// Try to start climbing
|
// Try to start climbing
|
||||||
var sys = SEntMan.System<ClimbSystem>();
|
var sys = SEntMan.System<ClimbSystem>();
|
||||||
await Server.WaitPost(() => sys.TryClimb(Player, Player, Target.Value));
|
await Server.WaitPost(() => sys.TryClimb(Player, Player, Target.Value, out _));
|
||||||
await AwaitDoAfters();
|
await AwaitDoAfters();
|
||||||
|
|
||||||
// Player should now be climbing
|
// Player should now be climbing
|
||||||
@@ -56,7 +56,7 @@ public sealed class ClimbingTest : MovementTest
|
|||||||
Assert.That(Delta(), Is.LessThan(0));
|
Assert.That(Delta(), Is.LessThan(0));
|
||||||
|
|
||||||
// Start climbing
|
// Start climbing
|
||||||
await Server.WaitPost(() => sys.TryClimb(Player, Player, Target.Value));
|
await Server.WaitPost(() => sys.TryClimb(Player, Player, Target.Value, out _));
|
||||||
await AwaitDoAfters();
|
await AwaitDoAfters();
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
// TODO VERBS ICON add a climbing icon?
|
// TODO VERBS ICON add a climbing icon?
|
||||||
args.Verbs.Add(new AlternativeVerb
|
args.Verbs.Add(new AlternativeVerb
|
||||||
{
|
{
|
||||||
Act = () => TryClimb(args.User, args.User, args.Target, component),
|
Act = () => TryClimb(args.User, args.User, args.Target, out _, component),
|
||||||
Text = Loc.GetString("comp-climbable-verb-climb")
|
Text = Loc.GetString("comp-climbable-verb-climb")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -106,22 +106,25 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
// but don't have computer access and i have to do this without syntax
|
// but don't have computer access and i have to do this without syntax
|
||||||
if (args.Handled || args.User != args.Dragged && !HasComp<HandsComponent>(args.User))
|
if (args.Handled || args.User != args.Dragged && !HasComp<HandsComponent>(args.User))
|
||||||
return;
|
return;
|
||||||
TryClimb(args.User, args.Dragged, uid, component);
|
TryClimb(args.User, args.Dragged, uid, out _, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TryClimb(EntityUid user,
|
public bool TryClimb(EntityUid user,
|
||||||
EntityUid entityToMove,
|
EntityUid entityToMove,
|
||||||
EntityUid climbable,
|
EntityUid climbable,
|
||||||
|
out DoAfterId? id,
|
||||||
ClimbableComponent? comp = null,
|
ClimbableComponent? comp = null,
|
||||||
ClimbingComponent? climbing = null)
|
ClimbingComponent? climbing = null)
|
||||||
{
|
{
|
||||||
|
id = null;
|
||||||
|
|
||||||
if (!Resolve(climbable, ref comp) || !Resolve(entityToMove, ref climbing))
|
if (!Resolve(climbable, ref comp) || !Resolve(entityToMove, ref climbing))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// Note, IsClimbing does not mean a DoAfter is active, it means the target has already finished a DoAfter and
|
// Note, IsClimbing does not mean a DoAfter is active, it means the target has already finished a DoAfter and
|
||||||
// is currently on top of something..
|
// is currently on top of something..
|
||||||
if (climbing.IsClimbing)
|
if (climbing.IsClimbing)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
var args = new DoAfterArgs(user, comp.ClimbDelay, new ClimbDoAfterEvent(), entityToMove, target: climbable, used: entityToMove)
|
var args = new DoAfterArgs(user, comp.ClimbDelay, new ClimbDoAfterEvent(), entityToMove, target: climbable, used: entityToMove)
|
||||||
{
|
{
|
||||||
@@ -130,7 +133,8 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
BreakOnDamage = true
|
BreakOnDamage = true
|
||||||
};
|
};
|
||||||
|
|
||||||
_doAfterSystem.TryStartDoAfter(args);
|
_doAfterSystem.TryStartDoAfter(args, out id);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, ClimbingComponent component, ClimbDoAfterEvent args)
|
private void OnDoAfter(EntityUid uid, ClimbingComponent component, ClimbDoAfterEvent args)
|
||||||
@@ -279,7 +283,7 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
/// <param name="target">The object that is being vaulted</param>
|
/// <param name="target">The object that is being vaulted</param>
|
||||||
/// <param name="reason">The reason why it cant be dropped</param>
|
/// <param name="reason">The reason why it cant be dropped</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool CanVault(ClimbableComponent component, EntityUid user, EntityUid target, out string reason)
|
public bool CanVault(ClimbableComponent component, EntityUid user, EntityUid target, out string reason)
|
||||||
{
|
{
|
||||||
if (!_actionBlockerSystem.CanInteract(user, target))
|
if (!_actionBlockerSystem.CanInteract(user, target))
|
||||||
{
|
{
|
||||||
@@ -315,7 +319,7 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
/// <param name="target">The object that is being vaulted onto</param>
|
/// <param name="target">The object that is being vaulted onto</param>
|
||||||
/// <param name="reason">The reason why it cant be dropped</param>
|
/// <param name="reason">The reason why it cant be dropped</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool CanVault(ClimbableComponent component, EntityUid user, EntityUid dragged, EntityUid target,
|
public bool CanVault(ClimbableComponent component, EntityUid user, EntityUid dragged, EntityUid target,
|
||||||
out string reason)
|
out string reason)
|
||||||
{
|
{
|
||||||
if (!_actionBlockerSystem.CanInteract(user, dragged) || !_actionBlockerSystem.CanInteract(user, target))
|
if (!_actionBlockerSystem.CanInteract(user, dragged) || !_actionBlockerSystem.CanInteract(user, target))
|
||||||
|
|||||||
@@ -225,6 +225,11 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string NavSmash = "NavSmash";
|
public const string NavSmash = "NavSmash";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can the NPC climb obstacles for steering.
|
||||||
|
/// </summary>
|
||||||
|
public const string NavClimb = "NavClimb";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default key storage for a movement pathfind.
|
/// Default key storage for a movement pathfind.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ public enum PathFlags : byte
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Smashing = 1 << 2,
|
Smashing = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can we climb it like a table or railing.
|
||||||
|
/// </summary>
|
||||||
|
Climbing = 1 << 3,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can we open stuff that requires interaction (e.g. click-open doors).
|
/// Can we open stuff that requires interaction (e.g. click-open doors).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Interact = 1 << 3,
|
Interact = 1 << 4,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public sealed partial class PathfindingSystem
|
|||||||
{
|
{
|
||||||
var isDoor = (end.Data.Flags & PathfindingBreadcrumbFlag.Door) != 0x0;
|
var isDoor = (end.Data.Flags & PathfindingBreadcrumbFlag.Door) != 0x0;
|
||||||
var isAccess = (end.Data.Flags & PathfindingBreadcrumbFlag.Access) != 0x0;
|
var isAccess = (end.Data.Flags & PathfindingBreadcrumbFlag.Access) != 0x0;
|
||||||
|
var isClimb = (end.Data.Flags & PathfindingBreadcrumbFlag.Climb) != 0x0;
|
||||||
|
|
||||||
// TODO: Handling power + door prying
|
// TODO: Handling power + door prying
|
||||||
// Door we should be able to open
|
// Door we should be able to open
|
||||||
@@ -71,6 +72,10 @@ public sealed partial class PathfindingSystem
|
|||||||
{
|
{
|
||||||
modifier += 10f + end.Data.Damage / 100f;
|
modifier += 10f + end.Data.Damage / 100f;
|
||||||
}
|
}
|
||||||
|
else if (isClimb && (request.Flags & PathFlags.Climbing) != 0x0)
|
||||||
|
{
|
||||||
|
modifier += 0.5f;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0f;
|
return 0f;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible;
|
using Content.Server.Destructible;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Climbing;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.NPC;
|
using Content.Shared.NPC;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
@@ -154,11 +155,12 @@ public sealed partial class PathfindingSystem
|
|||||||
var accessQuery = GetEntityQuery<AccessReaderComponent>();
|
var accessQuery = GetEntityQuery<AccessReaderComponent>();
|
||||||
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
||||||
var doorQuery = GetEntityQuery<DoorComponent>();
|
var doorQuery = GetEntityQuery<DoorComponent>();
|
||||||
|
var climbableQuery = GetEntityQuery<ClimbableComponent>();
|
||||||
var fixturesQuery = GetEntityQuery<FixturesComponent>();
|
var fixturesQuery = GetEntityQuery<FixturesComponent>();
|
||||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
BuildBreadcrumbs(dirt[i], mapGridComp, accessQuery, destructibleQuery, doorQuery, fixturesQuery,
|
BuildBreadcrumbs(dirt[i], mapGridComp, accessQuery, destructibleQuery, doorQuery, climbableQuery,
|
||||||
physicsQuery, xformQuery);
|
fixturesQuery, physicsQuery, xformQuery);
|
||||||
});
|
});
|
||||||
|
|
||||||
const int Division = 4;
|
const int Division = 4;
|
||||||
@@ -423,6 +425,7 @@ public sealed partial class PathfindingSystem
|
|||||||
EntityQuery<AccessReaderComponent> accessQuery,
|
EntityQuery<AccessReaderComponent> accessQuery,
|
||||||
EntityQuery<DestructibleComponent> destructibleQuery,
|
EntityQuery<DestructibleComponent> destructibleQuery,
|
||||||
EntityQuery<DoorComponent> doorQuery,
|
EntityQuery<DoorComponent> doorQuery,
|
||||||
|
EntityQuery<ClimbableComponent> climbableQuery,
|
||||||
EntityQuery<FixturesComponent> fixturesQuery,
|
EntityQuery<FixturesComponent> fixturesQuery,
|
||||||
EntityQuery<PhysicsComponent> physicsQuery,
|
EntityQuery<PhysicsComponent> physicsQuery,
|
||||||
EntityQuery<TransformComponent> xformQuery)
|
EntityQuery<TransformComponent> xformQuery)
|
||||||
@@ -540,6 +543,11 @@ public sealed partial class PathfindingSystem
|
|||||||
flags |= PathfindingBreadcrumbFlag.Door;
|
flags |= PathfindingBreadcrumbFlag.Door;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (climbableQuery.HasComponent(ent))
|
||||||
|
{
|
||||||
|
flags |= PathfindingBreadcrumbFlag.Climb;
|
||||||
|
}
|
||||||
|
|
||||||
if (destructibleQuery.TryGetComponent(ent, out var damageable))
|
if (destructibleQuery.TryGetComponent(ent, out var damageable))
|
||||||
{
|
{
|
||||||
damage += _destructible.DestroyedAt(ent, damageable).Float();
|
damage += _destructible.DestroyedAt(ent, damageable).Float();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Server.Administration.Managers;
|
|||||||
using Content.Server.Destructible;
|
using Content.Server.Destructible;
|
||||||
using Content.Server.NPC.Components;
|
using Content.Server.NPC.Components;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.Climbing;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.NPC;
|
using Content.Shared.NPC;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
@@ -436,6 +437,11 @@ namespace Content.Server.NPC.Pathfinding
|
|||||||
flags |= PathFlags.Smashing;
|
flags |= PathFlags.Smashing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blackboard.TryGetValue<bool>(NPCBlackboard.NavClimb, out var climb, EntityManager) && climb)
|
||||||
|
{
|
||||||
|
flags |= PathFlags.Climbing;
|
||||||
|
}
|
||||||
|
|
||||||
if (blackboard.TryGetValue<bool>(NPCBlackboard.NavInteract, out var interact, EntityManager) && interact)
|
if (blackboard.TryGetValue<bool>(NPCBlackboard.NavInteract, out var interact, EntityManager) && interact)
|
||||||
{
|
{
|
||||||
flags |= PathFlags.Interact;
|
flags |= PathFlags.Interact;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
|||||||
using Content.Server.Examine;
|
using Content.Server.Examine;
|
||||||
using Content.Server.NPC.Components;
|
using Content.Server.NPC.Components;
|
||||||
using Content.Server.NPC.Pathfinding;
|
using Content.Server.NPC.Pathfinding;
|
||||||
|
using Content.Shared.Climbing;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.NPC;
|
using Content.Shared.NPC;
|
||||||
@@ -35,6 +36,30 @@ public sealed partial class NPCSteeringSystem
|
|||||||
|
|
||||||
#region Seek
|
#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>
|
/// <summary>
|
||||||
/// Attempts to head to the target destination, either via the next pathfinding node or the final target.
|
/// Attempts to head to the target destination, either via the next pathfinding node or the final target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -90,7 +115,7 @@ public sealed partial class NPCSteeringSystem
|
|||||||
}
|
}
|
||||||
// If next node is a free tile then get within its bounds.
|
// If next node is a free tile then get within its bounds.
|
||||||
// This is to avoid popping it too early
|
// 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;
|
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)
|
if (direction.Length() <= arrivalDistance)
|
||||||
{
|
{
|
||||||
// Node needs some kind of special handling like access or smashing.
|
// 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.
|
// Ignore stuck while handling obstacles.
|
||||||
ResetStuck(steering, ourCoordinates);
|
ResetStuck(steering, ourCoordinates);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Server.Destructible;
|
using Content.Server.Destructible;
|
||||||
using Content.Server.NPC.Components;
|
using Content.Server.NPC.Components;
|
||||||
using Content.Server.NPC.Pathfinding;
|
using Content.Server.NPC.Pathfinding;
|
||||||
|
using Content.Shared.Climbing;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
@@ -74,6 +75,7 @@ public sealed partial class NPCSteeringSystem
|
|||||||
GetObstacleEntities(poly, mask, layer, obstacleEnts);
|
GetObstacleEntities(poly, mask, layer, obstacleEnts);
|
||||||
var isDoor = (poly.Data.Flags & PathfindingBreadcrumbFlag.Door) != 0x0;
|
var isDoor = (poly.Data.Flags & PathfindingBreadcrumbFlag.Door) != 0x0;
|
||||||
var isAccessRequired = (poly.Data.Flags & PathfindingBreadcrumbFlag.Access) != 0x0;
|
var isAccessRequired = (poly.Data.Flags & PathfindingBreadcrumbFlag.Access) != 0x0;
|
||||||
|
var isClimbable = (poly.Data.Flags & PathfindingBreadcrumbFlag.Climb) != 0x0;
|
||||||
|
|
||||||
// Just walk into it stupid
|
// Just walk into it stupid
|
||||||
if (isDoor && !isAccessRequired)
|
if (isDoor && !isAccessRequired)
|
||||||
@@ -121,6 +123,38 @@ public sealed partial class NPCSteeringSystem
|
|||||||
if (obstacleEnts.Count == 0)
|
if (obstacleEnts.Count == 0)
|
||||||
return SteeringObstacleStatus.Completed;
|
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.
|
// Try smashing obstacles.
|
||||||
else if ((component.Flags & PathFlags.Smashing) != 0x0)
|
else if ((component.Flags & PathFlags.Smashing) != 0x0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
|
using Content.Server.Climbing;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Doors.Systems;
|
using Content.Server.Doors.Systems;
|
||||||
using Content.Server.NPC.Components;
|
using Content.Server.NPC.Components;
|
||||||
@@ -50,6 +51,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
|||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly ClimbSystem _climb = default!;
|
||||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
[Dependency] private readonly DoorSystem _doors = default!;
|
[Dependency] private readonly DoorSystem _doors = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
|||||||
@@ -115,4 +115,9 @@ public enum PathfindingBreadcrumbFlag : ushort
|
|||||||
/// Is there access required
|
/// Is there access required
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Access = 1 << 3,
|
Access = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is there climbing involved
|
||||||
|
/// </summary>
|
||||||
|
Climb = 1 << 4,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user