Skip to main content

suon_movement/
lib.rs

1//! Movement systems for stepping and teleporting entities across the Suon world.
2//!
3//! This crate owns intent-driven movement behaviors that update
4//! [`suon_position::position::Position`] and record
5//! [`suon_position::previous_position::PreviousPosition`] so downstream crates can
6//! react consistently to movement.
7//!
8//! # Modules
9//!
10//! - Step flow via [`prelude::Step`], [`prelude::StepIntent`], and
11//!   [`prelude::StepPath`]
12//! - Teleport flow via [`prelude::Teleport`] and [`prelude::TeleportIntent`]
13//!
14//! # Examples
15//! ```no_run
16//! use bevy::prelude::*;
17//! use suon_chunk::{Chunk, ChunkPlugin, chunks::Chunks};
18//! use suon_movement::prelude::{MovementPlugins, StepIntent};
19//! use suon_position::{direction::Direction, floor::Floor, position::Position};
20//!
21//! let mut app = App::new();
22//! app.add_plugins(MinimalPlugins);
23//! app.add_plugins(ChunkPlugin);
24//! app.add_plugins(MovementPlugins);
25//!
26//! let chunk = app.world_mut().spawn(Chunk).id();
27//! app.insert_resource(Chunks::from_iter([
28//!     (Position { x: 1, y: 1 }, chunk),
29//!     (Position { x: 2, y: 1 }, chunk),
30//! ]));
31//!
32//! let entity = app.world_mut().spawn((Position { x: 1, y: 1 }, Floor { z: 0 })).id();
33//! app.world_mut().trigger(StepIntent {
34//!     to: Direction::East,
35//!     entity,
36//! });
37//! app.update();
38//!
39//! assert_eq!(
40//!     *app.world().get::<Position>(entity).unwrap(),
41//!     Position { x: 2, y: 1 }
42//! );
43//! ```
44
45use bevy::{app::PluginGroupBuilder, prelude::*};
46
47mod step;
48mod teleport;
49
50pub mod prelude {
51    pub use super::{
52        MovementPlugins,
53        step::{Step, StepAcrossChunk, StepIntent, path::StepPath},
54        teleport::{Teleport, TeleportAcrossChunk, TeleportIntent},
55    };
56
57    pub(crate) use super::step::timer::StepTimer;
58}
59
60pub struct MovementPlugins;
61
62impl PluginGroup for MovementPlugins {
63    fn build(self) -> PluginGroupBuilder {
64        PluginGroupBuilder::start::<Self>()
65            .add(step::StepPlugin)
66            .add(teleport::TeleportPlugin)
67    }
68}
69
70#[cfg(test)]
71mod tests {
72
73    use super::*;
74
75    #[test]
76    fn should_build_movement_plugin_group() {
77        let mut app = App::new();
78
79        app.add_plugins(MinimalPlugins);
80        app.add_plugins(MovementPlugins);
81        app.update();
82
83        assert!(
84            std::mem::size_of::<MovementPlugins>() == 0,
85            "The movement plugin group should remain a zero-sized configuration marker"
86        );
87    }
88
89    #[test]
90    fn should_allow_prelude_imports_for_public_movement_types() {
91        use crate::prelude::{
92            MovementPlugins, Step, StepAcrossChunk, StepIntent, StepPath, Teleport,
93            TeleportAcrossChunk, TeleportIntent,
94        };
95        use suon_position::direction::Direction;
96
97        let _ = std::mem::size_of::<MovementPlugins>();
98        let _ = std::mem::size_of::<Step>();
99        let _ = std::mem::size_of::<StepAcrossChunk>();
100        let _ = std::mem::size_of::<StepIntent>();
101        let _ = std::mem::size_of::<StepPath>();
102        let _ = std::mem::size_of::<Teleport>();
103        let _ = std::mem::size_of::<TeleportAcrossChunk>();
104        let _ = std::mem::size_of::<TeleportIntent>();
105
106        assert_eq!(
107            Direction::North.offset(),
108            (0, 1),
109            "The prelude should expose step direction helpers"
110        );
111    }
112}