diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs index 979c40fe31..58fc578845 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs @@ -42,7 +42,7 @@ public sealed class MoveToOperator : HTNOperator /// Where the pathfinding result will be stored (if applicable). This gets removed after execution. /// [ViewVariables, DataField("pathfindKey")] - public string PathfindKey = "MovementPathfind"; + public string PathfindKey = NPCBlackboard.PathfindKey; /// /// How close we need to get before considering movement finished. diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleComponentOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleComponentOperator.cs index 5640d39eff..9d84be5f89 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleComponentOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleComponentOperator.cs @@ -30,7 +30,7 @@ public sealed class PickAccessibleComponentOperator : HTNOperator /// Where the pathfinding result will be stored (if applicable). This gets removed after execution. /// [ViewVariables, DataField("pathfindKey")] - public string PathfindKey = "MovementPathfind"; + public string PathfindKey = NPCBlackboard.PathfindKey; public override void Initialize(IEntitySystemManager sysManager) { diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleOperator.cs index f0e1f7869d..441b34dbb0 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleOperator.cs @@ -23,7 +23,7 @@ public sealed class PickAccessibleOperator : HTNOperator /// Where the pathfinding result will be stored (if applicable). This gets removed after execution. /// [ViewVariables, DataField("pathfindKey")] - public string PathfindKey = "MovementPathfind"; + public string PathfindKey = NPCBlackboard.PathfindKey; public override void Initialize(IEntitySystemManager sysManager) { diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs index 326fd20131..7a584524e6 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs @@ -50,6 +50,9 @@ public sealed class MedibotInjectOperator : HTNOperator if (!_entManager.TryGetComponent(owner, out var botComp)) return HTNOperatorStatus.Failed; + // To avoid spam, the rest of this needs fixing. + _entManager.EnsureComponent(target); + if (!_entManager.TryGetComponent(target, out var damage)) return HTNOperatorStatus.Failed; @@ -62,20 +65,18 @@ public sealed class MedibotInjectOperator : HTNOperator if (damage.TotalDamage == 0) return HTNOperatorStatus.Failed; - if (damage.TotalDamage <= MedibotComponent.StandardMedDamageThreshold) + if (damage.TotalDamage >= MedibotComponent.EmergencyMedDamageThreshold) { - _solutionSystem.TryAddReagent(target, injectable, botComp.StandardMed, botComp.StandardMedInjectAmount, out var accepted); - _entManager.EnsureComponent(target); + _solutionSystem.TryAddReagent(target, injectable, botComp.EmergencyMed, botComp.EmergencyMedInjectAmount, out var accepted); _popupSystem.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, Filter.Entities(target)); SoundSystem.Play("/Audio/Items/hypospray.ogg", Filter.Pvs(target), target); _chat.TrySendInGameICMessage(owner, Loc.GetString("medibot-finish-inject"), InGameICChatType.Speak, false); return HTNOperatorStatus.Finished; } - if (damage.TotalDamage >= MedibotComponent.EmergencyMedDamageThreshold) + if (damage.TotalDamage >= MedibotComponent.StandardMedDamageThreshold) { - _solutionSystem.TryAddReagent(target, injectable, botComp.EmergencyMed, botComp.EmergencyMedInjectAmount, out var accepted); - _entManager.EnsureComponent(target); + _solutionSystem.TryAddReagent(target, injectable, botComp.StandardMed, botComp.StandardMedInjectAmount, out var accepted); _popupSystem.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, Filter.Entities(target)); SoundSystem.Play("/Audio/Items/hypospray.ogg", Filter.Pvs(target), target); _chat.TrySendInGameICMessage(owner, Loc.GetString("medibot-finish-inject"), InGameICChatType.Speak, false); diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickNearbyInjectableOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickNearbyInjectableOperator.cs index f7e9621118..5ebc4ca72d 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickNearbyInjectableOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickNearbyInjectableOperator.cs @@ -2,7 +2,9 @@ using System.Threading; using System.Threading.Tasks; using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.NPC.Components; +using Content.Server.NPC.Pathfinding; using Content.Shared.Damage; +using Content.Shared.Interaction; using Content.Shared.MobState.Components; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; @@ -11,6 +13,7 @@ public sealed class PickNearbyInjectableOperator : HTNOperator { [Dependency] private readonly IEntityManager _entManager = default!; private EntityLookupSystem _lookup = default!; + private PathfindingSystem _pathfinding = default!; [ViewVariables, DataField("rangeKey")] public string RangeKey = NPCBlackboard.MedibotInjectRange; @@ -30,6 +33,7 @@ public sealed class PickNearbyInjectableOperator : HTNOperator { base.Initialize(sysManager); _lookup = sysManager.GetEntitySystem(); + _pathfinding = sysManager.GetEntitySystem(); } public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, @@ -53,10 +57,16 @@ public sealed class PickNearbyInjectableOperator : HTNOperator damage.TotalDamage > 0 && !recentlyInjected.HasComponent(entity)) { + var path = await _pathfinding.GetPath(owner, entity, SharedInteractionSystem.InteractionRange, cancelToken); + + if (path.Result == PathResult.NoPath) + continue; + return (true, new Dictionary() { {TargetKey, entity}, - {TargetMoveKey, _entManager.GetComponent(entity).Coordinates} + {TargetMoveKey, _entManager.GetComponent(entity).Coordinates}, + {NPCBlackboard.PathfindKey, path}, }); } } diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs index 9cc6bee96d..4f3c97dd2b 100644 --- a/Content.Server/NPC/NPCBlackboard.cs +++ b/Content.Server/NPC/NPCBlackboard.cs @@ -196,6 +196,11 @@ public sealed class NPCBlackboard : IEnumerable> public const string OwnerCoordinates = "OwnerCoordinates"; public const string MovementTarget = "MovementTarget"; + /// + /// Can the NPC click open entities such as doors. + /// + public const string NavInteract = "NavInteract"; + /// /// Can the NPC pry open doors for steering. /// @@ -205,6 +210,12 @@ public sealed class NPCBlackboard : IEnumerable> /// Can the NPC smash obstacles for steering. /// public const string NavSmash = "NavSmash"; + + /// + /// Default key storage for a movement pathfind. + /// + public const string PathfindKey = "MovementPathfind"; + public const string RotateSpeed = "RotateSpeed"; public const string VisionRadius = "VisionRadius"; public const float MeleeRange = 1f; diff --git a/Content.Server/NPC/Pathfinding/PathFlags.cs b/Content.Server/NPC/Pathfinding/PathFlags.cs index a30592ae21..1b60e187b8 100644 --- a/Content.Server/NPC/Pathfinding/PathFlags.cs +++ b/Content.Server/NPC/Pathfinding/PathFlags.cs @@ -19,4 +19,9 @@ public enum PathFlags : byte /// Can stuff like walls be broken. /// Smashing = 1 << 2, + + /// + /// Can we open stuff that requires interaction (e.g. click-open doors). + /// + Interact = 1 << 3, } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Common.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Common.cs index 1be578d385..918276a3b8 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Common.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Common.cs @@ -58,7 +58,7 @@ public sealed partial class PathfindingSystem // TODO: Handling power + door prying // Door we should be able to open - if (isDoor && !isAccess) + if (isDoor && !isAccess && (request.Flags & PathFlags.Interact) != 0x0) { modifier += 0.5f; } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs index 0253bfdae9..1392da58b8 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs @@ -280,6 +280,21 @@ namespace Content.Server.NPC.Pathfinding return distance; } + public async Task GetPath( + EntityUid entity, + EntityUid target, + float range, + CancellationToken cancelToken, + PathFlags flags = PathFlags.None) + { + if (!TryComp(entity, out var xform) || + !TryComp(target, out var targetXform)) + return new PathResultEvent(PathResult.NoPath, new Queue()); + + var request = GetRequest(entity, xform.Coordinates, targetXform.Coordinates, range, cancelToken, flags); + return await GetPath(request); + } + public async Task GetPath( EntityUid entity, EntityCoordinates start, @@ -385,16 +400,21 @@ namespace Content.Server.NPC.Pathfinding { var flags = PathFlags.None; - if (blackboard.TryGetValue(NPCBlackboard.NavPry, out var pry)) + if (blackboard.TryGetValue(NPCBlackboard.NavPry, out var pry) && pry) { flags |= PathFlags.Prying; } - if (blackboard.TryGetValue(NPCBlackboard.NavSmash, out var smash)) + if (blackboard.TryGetValue(NPCBlackboard.NavSmash, out var smash) && smash) { flags |= PathFlags.Smashing; } + if (blackboard.TryGetValue(NPCBlackboard.NavInteract, out var interact) && interact) + { + flags |= PathFlags.Interact; + } + return flags; } diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs index 86c8335527..dafa524cf8 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs @@ -63,7 +63,7 @@ public sealed partial class NPCSteeringSystem if (!doorQuery.TryGetComponent(ent, out var door)) continue; - if (!door.BumpOpen) + if (!door.BumpOpen && (component.Flags & PathFlags.Interact) != 0x0) { if (door.State != DoorState.Opening) { @@ -71,6 +71,10 @@ public sealed partial class NPCSteeringSystem return SteeringObstacleStatus.Continuing; } } + else + { + return SteeringObstacleStatus.Failed; + } } return SteeringObstacleStatus.Completed; diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index ee258c52ab..ae92371085 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -15,6 +15,8 @@ - type: HTN rootTask: XenoCompound blackboard: + NavInteract: !type:Bool + true NavPry: !type:Bool true NavSmash: !type:Bool