using System.Threading; using System.Threading.Tasks; using Content.Server.NPC.Pathfinding; using Content.Shared.Interaction; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Whitelist; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; /// /// Queries for nearby entities matching a whitelist/blacklist, and then searches for a mob near to that entity. /// public sealed partial class PickEntityNearMobOperator : HTNOperator { [Dependency] private IEntityManager _entManager = default!; private EntityLookupSystem _lookup = default!; private PathfindingSystem _pathfinding = default!; private EntityWhitelistSystem _entityWhitelist = default!; /// /// Range to search for entities /// [DataField(required: true)] public string RangeKey = default!; /// /// Target mob that was found near NearbyEntityTargetKey /// [DataField(required: true)] public string TargetKey = string.Empty; /// /// Target entity that was found /// [DataField(required: true)] public string NearbyEntityTargetKey = string.Empty; /// /// Target entitycoordinates to move to. /// [DataField(required: true)] public string TargetMoveKey = string.Empty; /// /// Whitelist for what entities will get picked /// [DataField] public EntityWhitelist? Whitelist; /// /// Blacklist for what entities will NOT get picked /// [DataField] public EntityWhitelist? Blacklist; /// /// Range to search for mobs near the target entity /// [DataField(required: true)] public string MobRangeKey = default!; /// /// MobState to check for /// [DataField] public MobState? MobState; public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); _lookup = sysManager.GetEntitySystem(); _pathfinding = sysManager.GetEntitySystem(); _entityWhitelist = 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)) return (false, null); if (!blackboard.TryGetValue(MobRangeKey, out var mobRange, _entManager)) return (false, null); var mobState = _entManager.GetEntityQuery(); foreach (var mob in _lookup.GetEntitiesInRange(owner, mobRange, LookupFlags.Uncontained & ~LookupFlags.Sensors)) { if (mob == owner) continue; if (!mobState.TryGetComponent(mob, out var state)) continue; if (MobState != null && state.CurrentState != MobState) continue; foreach (var entity in _lookup.GetEntitiesInRange(mob, range)) { if (!_entityWhitelist.CheckBoth(entity, Blacklist, Whitelist)) continue; var pathRange = SharedInteractionSystem.InteractionRange; var path = await _pathfinding.GetPath(owner, mob, pathRange, cancelToken); if (path.Result == PathResult.NoPath) return (false, null); return (true, new Dictionary() { {TargetKey, mob}, {NearbyEntityTargetKey, entity}, {TargetMoveKey, _entManager.GetComponent(mob).Coordinates}, {NPCBlackboard.PathfindKey, path}, }); } } return (false, null); } }