using System.Threading; using System.Threading.Tasks; using Content.Server.Disposal.Unit.Components; using Content.Server.NPC.Pathfinding; using Content.Shared.Body.Part; using Content.Shared.DeviceLinking; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Materials; using Content.Shared.Silicons.Bots; using Content.Shared.Tag; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; public sealed partial class PickNearbyFillableItemOperator : HTNOperator { [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private SharedMaterialStorageSystem _sharedMaterialStorage = default!; private EntityLookupSystem _lookup = default!; private PathfindingSystem _pathfinding = default!; private SharedHandsSystem _sharedHandsSystem = default!; private TagSystem _tagSystem = default!; [DataField] public string RangeKey = NPCBlackboard.FillbotPickupRange; /// Target entity to pick up [DataField(required: true)] public string TargetKey = string.Empty; /// Target entitycoordinates to move to. [DataField(required: true)] public string TargetMoveKey = string.Empty; /// Target tag prototype to look for when trying to find trash. [DataField] public ProtoId TrashProto = "Trash"; public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); _lookup = sysManager.GetEntitySystem(); _pathfinding = sysManager.GetEntitySystem(); _sharedMaterialStorage = sysManager.GetEntitySystem(); _sharedHandsSystem = sysManager.GetEntitySystem(); _tagSystem = sysManager.GetEntitySystem(); } public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken) { var owner = blackboard.GetValue(NPCBlackboard.Owner); if (!blackboard.TryGetValue(RangeKey, out var range, _entManager) || !_entManager.TryGetComponent(owner, out var fillbot) || !_entManager.TryGetComponent(owner, out var fillbotlinks) || fillbotlinks.LinkedPorts.Count != 1 || fillbot.LinkedSinkEntity == null) return (false, null); _entManager.TryGetComponent(fillbot.LinkedSinkEntity, out MaterialStorageComponent? linkedStorage); _entManager.TryGetComponent(fillbot.LinkedSinkEntity, out DisposalUnitComponent? disposalUnit); foreach (var target in _lookup.GetEntitiesInRange(owner, range)) { // only things the robot can actually pick up if (!_sharedHandsSystem.CanPickupAnyHand(owner, target)) continue; // only things not currently contained by something else if (_entManager.TryGetComponent(target, out var meta) && meta.Flags.HasFlag(MetaDataFlags.InContainer)) continue; // only things that can go inside if (linkedStorage != null && !_sharedMaterialStorage.CanInsertMaterialEntity(target, fillbot.LinkedSinkEntity!.Value)) continue; // trash only if (disposalUnit != null && (_whitelistSystem.IsWhitelistFail(disposalUnit.Whitelist, target) || !_tagSystem.HasTag(target, TrashProto) || _entManager.HasComponent(target))) // Robot is unable to insert bodyparts into Disposals for some reason continue; const float pathRange = SharedInteractionSystem.InteractionRange - 1; var path = await _pathfinding.GetPath(owner, target, pathRange, cancelToken); if (path.Result == PathResult.NoPath) continue; return (true, new() { {TargetKey, target}, {TargetMoveKey, _entManager.GetComponent(target).Coordinates}, {NPCBlackboard.PathfindKey, path}, }); } return (false, null); } }