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}