Skip to main content

nightshade/plugins/navmesh/
components.rs

1//! Navigation mesh component definitions.
2
3use nalgebra_glm::Vec3;
4use serde::{Deserialize, Serialize};
5
6/// Navigation agent component for navmesh-based movement.
7///
8/// Attach to an entity with a transform to enable pathfinding. Set a destination
9/// via [`set_destination`](NavMeshAgent::set_destination) and the navigation system
10/// will compute and follow a path.
11#[derive(Debug, Clone, Serialize, Deserialize, enum2schema::Schema)]
12pub struct NavMeshAgent {
13    /// Computed path as a list of waypoints.
14    pub current_path: Vec<Vec3>,
15    /// Index of the next waypoint to reach.
16    pub current_waypoint_index: usize,
17    /// Target destination (if any).
18    pub target_position: Option<Vec3>,
19    /// Movement speed in units per second.
20    pub movement_speed: f32,
21    /// Distance at which waypoints are considered reached.
22    pub arrival_threshold: f32,
23    /// Distance target must move before path is recalculated.
24    pub path_recalculation_threshold: f32,
25    /// Agent collision radius for navmesh queries.
26    pub agent_radius: f32,
27    /// Agent height for navmesh queries.
28    pub agent_height: f32,
29    /// Current navmesh triangle (for height sampling).
30    pub current_triangle: Option<usize>,
31    /// Current navigation state.
32    pub state: NavMeshAgentState,
33    /// Remaining distance to final destination.
34    pub distance_to_destination: f32,
35    /// Velocity adjustment for local obstacle avoidance.
36    pub avoidance_velocity: Vec3,
37}
38
39impl Default for NavMeshAgent {
40    fn default() -> Self {
41        Self {
42            current_path: Vec::new(),
43            current_waypoint_index: 0,
44            target_position: None,
45            movement_speed: 5.0,
46            arrival_threshold: 0.3,
47            path_recalculation_threshold: 2.0,
48            agent_radius: 0.3,
49            agent_height: 1.8,
50            current_triangle: None,
51            state: NavMeshAgentState::Idle,
52            distance_to_destination: 0.0,
53            avoidance_velocity: Vec3::zeros(),
54        }
55    }
56}
57
58impl NavMeshAgent {
59    /// Creates a new navigation agent with default settings.
60    pub fn new() -> Self {
61        Self::default()
62    }
63
64    /// Sets the movement speed.
65    pub fn with_speed(mut self, speed: f32) -> Self {
66        self.movement_speed = speed;
67        self
68    }
69
70    /// Sets the agent collision radius.
71    pub fn with_radius(mut self, radius: f32) -> Self {
72        self.agent_radius = radius;
73        self
74    }
75
76    /// Sets the agent height.
77    pub fn with_height(mut self, height: f32) -> Self {
78        self.agent_height = height;
79        self
80    }
81
82    /// Sets the distance threshold for reaching waypoints.
83    pub fn with_arrival_threshold(mut self, threshold: f32) -> Self {
84        self.arrival_threshold = threshold;
85        self
86    }
87
88    /// Sets a new destination and triggers pathfinding.
89    pub fn set_destination(&mut self, destination: Vec3) {
90        self.target_position = Some(destination);
91        self.state = NavMeshAgentState::PathPending;
92    }
93
94    /// Clears the current destination and stops movement.
95    pub fn clear_destination(&mut self) {
96        self.target_position = None;
97        self.current_path.clear();
98        self.current_waypoint_index = 0;
99        self.state = NavMeshAgentState::Idle;
100    }
101
102    /// Returns true if a valid path exists.
103    pub fn has_path(&self) -> bool {
104        !self.current_path.is_empty()
105    }
106
107    /// Returns true if the agent is actively navigating.
108    pub fn is_moving(&self) -> bool {
109        self.state == NavMeshAgentState::Moving
110    }
111
112    /// Returns the current waypoint position.
113    pub fn current_waypoint(&self) -> Option<Vec3> {
114        self.current_path.get(self.current_waypoint_index).copied()
115    }
116
117    /// Returns the number of waypoints remaining.
118    pub fn remaining_waypoints(&self) -> usize {
119        if self.current_waypoint_index >= self.current_path.len() {
120            0
121        } else {
122            self.current_path.len() - self.current_waypoint_index
123        }
124    }
125
126    /// Advances to the next waypoint. Returns true if destination reached.
127    pub fn advance_waypoint(&mut self) -> bool {
128        self.current_waypoint_index += 1;
129        if self.current_waypoint_index >= self.current_path.len() {
130            self.state = NavMeshAgentState::Arrived;
131            true
132        } else {
133            false
134        }
135    }
136}
137
138/// Navigation agent state machine.
139#[derive(
140    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, enum2schema::Schema,
141)]
142#[schema(string_enum)]
143pub enum NavMeshAgentState {
144    /// No destination set.
145    #[default]
146    Idle,
147    /// Waiting for pathfinding to complete.
148    PathPending,
149    /// Following path to destination.
150    Moving,
151    /// Reached the destination.
152    Arrived,
153    /// No valid path could be found.
154    NoPath,
155}