1
0

NPCSteeringComponent.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. using System.Numerics;
  2. using System.Threading;
  3. using Content.Server.NPC.Pathfinding;
  4. using Content.Shared.DoAfter;
  5. using Content.Shared.NPC;
  6. using Robust.Shared.Map;
  7. using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
  8. namespace Content.Server.NPC.Components;
  9. /// <summary>
  10. /// Added to NPCs that are moving.
  11. /// </summary>
  12. [RegisterComponent, AutoGenerateComponentPause]
  13. public sealed partial class NPCSteeringComponent : Component
  14. {
  15. #region Context Steering
  16. /// <summary>
  17. /// Used to override seeking behavior for context steering.
  18. /// </summary>
  19. [ViewVariables]
  20. public bool CanSeek = true;
  21. /// <summary>
  22. /// Radius for collision avoidance.
  23. /// </summary>
  24. [ViewVariables(VVAccess.ReadWrite)]
  25. public float Radius = 0.35f;
  26. [ViewVariables, DataField]
  27. public float[] Interest = new float[SharedNPCSteeringSystem.InterestDirections];
  28. [ViewVariables, DataField]
  29. public float[] Danger = new float[SharedNPCSteeringSystem.InterestDirections];
  30. // TODO: Update radius, also danger points debug only
  31. public readonly List<Vector2> DangerPoints = new();
  32. #endregion
  33. /// <summary>
  34. /// Set to true from other systems if you wish to force the NPC to move closer.
  35. /// </summary>
  36. [DataField("forceMove")]
  37. public bool ForceMove = false;
  38. [DataField("lastSteerDirection")]
  39. public Vector2 LastSteerDirection = Vector2.Zero;
  40. /// <summary>
  41. /// Last position we considered for being stuck.
  42. /// </summary>
  43. [DataField("lastStuckCoordinates")]
  44. public EntityCoordinates LastStuckCoordinates;
  45. [DataField("lastStuckTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
  46. [AutoPausedField]
  47. public TimeSpan LastStuckTime;
  48. public const float StuckDistance = 1f;
  49. /// <summary>
  50. /// Have we currently requested a path.
  51. /// </summary>
  52. [ViewVariables]
  53. public bool Pathfind => PathfindToken != null;
  54. /// <summary>
  55. /// Are we considered arrived if we have line of sight of the target.
  56. /// </summary>
  57. [DataField("arriveOnLineOfSight")]
  58. public bool ArriveOnLineOfSight = false;
  59. /// <summary>
  60. /// How long the target has been in line of sight if applicable.
  61. /// </summary>
  62. [DataField("lineOfSightTimer")]
  63. public float LineOfSightTimer = 0f;
  64. [DataField("lineOfSightTimeRequired")]
  65. public float LineOfSightTimeRequired = 0.5f;
  66. [ViewVariables] public CancellationTokenSource? PathfindToken = null;
  67. /// <summary>
  68. /// Current path we're following to our coordinates.
  69. /// </summary>
  70. [ViewVariables] public Queue<PathPoly> CurrentPath = new();
  71. /// <summary>
  72. /// End target that we're trying to move to.
  73. /// </summary>
  74. [ViewVariables(VVAccess.ReadWrite)] public EntityCoordinates Coordinates;
  75. /// <summary>
  76. /// How close are we trying to get to the coordinates before being considered in range.
  77. /// </summary>
  78. [ViewVariables(VVAccess.ReadWrite)] public float Range = 0.2f;
  79. /// <summary>
  80. /// How far does the last node in the path need to be before considering re-pathfinding.
  81. /// </summary>
  82. [ViewVariables(VVAccess.ReadWrite)] public float RepathRange = 1.5f;
  83. public const int FailedPathLimit = 3;
  84. /// <summary>
  85. /// How many times we've failed to pathfind. Once this hits the limit we'll stop steering.
  86. /// </summary>
  87. [ViewVariables] public int FailedPathCount;
  88. [ViewVariables] public SteeringStatus Status = SteeringStatus.Moving;
  89. [ViewVariables(VVAccess.ReadWrite)] public PathFlags Flags = PathFlags.None;
  90. /// <summary>
  91. /// If the NPC is using a do_after to clear an obstacle.
  92. /// </summary>
  93. [DataField("doAfterId")]
  94. public DoAfterId? DoAfterId = null;
  95. }
  96. public enum SteeringStatus : byte
  97. {
  98. /// <summary>
  99. /// If we can't reach the target (e.g. different map).
  100. /// </summary>
  101. NoPath,
  102. /// <summary>
  103. /// Are we moving towards our target
  104. /// </summary>
  105. Moving,
  106. /// <summary>
  107. /// Are we currently in range of our target.
  108. /// </summary>
  109. InRange,
  110. }