using System.Threading; using System.Threading.Tasks; using Content.Server.NPC.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Robust.Shared.Audio; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Ranged; public sealed class RangedOperator : HTNOperator { [Dependency] private readonly IEntityManager _entManager = default!; /// /// Key that contains the target entity. /// [DataField("targetKey", required: true)] public string TargetKey = default!; /// /// Minimum damage state that the target has to be in for us to consider attacking. /// [DataField("targetState")] public MobState TargetState = MobState.Alive; // Like movement we add a component and pass it off to the dedicated system. public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken) { // Don't attack if they're already as wounded as we want them. if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) { return (false, null); } if (_entManager.TryGetComponent(target, out var mobState) && mobState.CurrentState != null && mobState.CurrentState > TargetState) { return (false, null); } return (true, null); } public override void Startup(NPCBlackboard blackboard) { base.Startup(blackboard); var ranged = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner)); ranged.Target = blackboard.GetValue(TargetKey); if (blackboard.TryGetValue(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager)) { ranged.RotationSpeed = new Angle(rotSpeed); } if (blackboard.TryGetValue("SoundTargetInLOS", out var losSound, _entManager)) { ranged.SoundTargetInLOS = losSound; } } public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status) { base.Shutdown(blackboard, status); _entManager.RemoveComponent(blackboard.GetValue(NPCBlackboard.Owner)); blackboard.Remove(TargetKey); } public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) { base.Update(blackboard, frameTime); var owner = blackboard.GetValue(NPCBlackboard.Owner); var status = HTNOperatorStatus.Continuing; if (_entManager.TryGetComponent(owner, out var combat)) { // Success if (_entManager.TryGetComponent(combat.Target, out var mobState) && mobState.CurrentState != null && mobState.CurrentState > TargetState) { status = HTNOperatorStatus.Finished; } else { switch (combat.Status) { case CombatStatus.TargetUnreachable: case CombatStatus.NotInSight: status = HTNOperatorStatus.Failed; break; case CombatStatus.Normal: status = HTNOperatorStatus.Continuing; break; default: status = HTNOperatorStatus.Failed; break; } } } if (status != HTNOperatorStatus.Continuing) { _entManager.RemoveComponent(owner); } return status; } }