tinyecs/
system.rs

1use entity::*;
2use aspect::Aspect;
3use world::{WorldHandle, EntityManager};
4use std::collections::HashSet;
5
6#[doc(hidden)]
7#[macro_export]
8macro_rules! impl_aspect {
9    ( $( $t:ty ),* ) => {
10        fn aspect(&self) -> Aspect {
11            use std::any::TypeId;
12            Aspect {
13                accept_types : vec!($(TypeId::of::<$t>()),*),
14                not_accept_types : Vec::new()
15            }
16        }
17    }
18}
19
20#[doc(hidden)]
21#[macro_export]
22macro_rules! impl_data_aspect {
23    ( $( $dataaspect:expr ),* ) => {
24        fn data_aspects(&self) -> Vec<Aspect> {
25            vec!($($dataaspect),*)
26        }
27    }
28}
29#[doc(hidden)]
30#[macro_export]
31macro_rules! impl_new {
32    ($name:ident) => {
33        impl $name {
34            pub fn new() -> $name {
35                $name
36            }
37        }
38    }
39}
40
41/// Create struct and impl System trait for it
42///
43/// ```ignore
44/// register_system!((MoveSystem): |_pos: Position, _vel: Velocity| => {
45/// });
46/// ```
47///
48/// ```ignore
49/// register_system!((AiSystem): |_bot: Bot, _pos: Position, _vel: Velocity|
50///                 with (_players: aspect_all!(Player, Position),
51///                       _targets: aspect_all!(SomeTarget, Position)) => {
52/// });
53/// ```
54///
55/// ```ignore
56/// register_system!((BotControlSystem
57///                   aspect aspect_all!(Position, Bot).except2::<Punch, Jump>()):
58///                 |bot : Bot, pos : Position|
59///                 with (scores: aspect_all!(ScoreTarget, Position),
60///                       players: aspect_all!(Player, Position),
61///                       objects: aspect_all!(ScoreObject, RigidBody)) => {
62/// });
63/// ```
64#[macro_export]
65macro_rules! register_system {
66    ( ($name:ident aspect $aspect:expr): $entity:ident |$( $varname:ident: $t:ty ), *| with ($( $datavar:ident: $dataaspect:expr ), *) => $code:expr) => {
67        pub struct $name;
68        impl_new!($name);
69        impl System for $name {
70            fn aspect(&self) -> Aspect {
71                $aspect
72            }
73            impl_data_aspect!($($dataaspect),*);
74
75            fn process_d(&mut self, $entity : &mut Entity, data : &mut DataList) {
76                let mut _n = 0;
77                $( let mut $datavar = data.unwrap_nth(_n); _n += 1; )*
78                $( let mut $varname = $entity.get_component::<$t>(); )*
79                $code
80            }
81        }
82    };
83
84    ( ($name:ident): |$( $varname:ident: $t:ty ), *| with ($( $datavar:ident: $aspect:expr ), *) => $code:expr) => {
85        pub struct $name;
86        impl_new!($name);
87        impl System for $name {
88            impl_aspect!($($t),*);
89            impl_data_aspect!($($aspect),*);
90
91            fn process_d(&mut self, entity : &mut Entity, data : &mut DataList) {
92                let mut _n = 0;
93                $( let mut $datavar = data.unwrap_nth(_n); _n += 1; )*
94                $( let mut $varname = entity.get_component::<$t>(); )*
95                $code
96            }
97        }
98    };
99
100    ( ($name:ident): |$( $varname:ident: $t:ty ), *| => $code:expr) => {
101        pub struct $name;
102        impl_new!($name);
103        impl System for $name {
104            impl_aspect!($($t),*);
105
106            fn process_one(&mut self, entity : &mut Entity) {
107                $( let mut $varname = entity.get_component::<$t>(); )*
108                $code
109            }
110        }
111    };
112}
113
114/// Usefull when you have one component, and want to have another, built on given one.
115/// Like this:
116///
117/// ```ignore
118/// transit_system!(Glutin2HeavySystem: RenderData => HeavyGuiData,
119///    |render_data| { HeavyGuiData::new(&mut render_data.facade.borrow_mut() ) });
120/// ```
121/// With this system, HeavyGuiData will automatically be added to entities with RenderData.
122#[macro_export]
123macro_rules! transit_system {
124    ($name:ident: $from:ty => $to:ty, |$var:ident| $how:expr) => {
125        pub struct $name;
126        impl_new!($name);
127        impl System for $name {
128            fn aspect(&self) -> Aspect {
129                aspect_all!($from).except::<$to>()
130            }
131
132            fn process_one(&mut self, entity : &mut Entity) {
133                let $var = entity.get_component::<$from>();
134                entity.add_component($how);
135                entity.refresh();
136            }
137        }
138    };
139}
140
141/// list with additional entitiy packs from data aspect
142///
143/// Strongly recommends not use this ever, only for macroses!
144pub struct DataList<'a> {
145    data : Vec<Vec<&'a mut Entity>>
146}
147impl<'b> DataList<'b> {
148    pub fn unwrap_entity<'a>(&'a self) -> &'a Entity {
149        &self.data[0][0]
150    }
151
152    pub fn unwrap_entity_nth<'a>(&'a self, n : usize) -> &'a Entity {
153        &self.data[n][0]
154    }
155    pub fn unwrap_entity_mut<'a>(&'a mut self) -> &'a mut Entity {
156        &mut self.data[0][0]
157    }
158
159    pub fn unwrap_all<'a>(&'a mut self) -> &'a mut Vec<&'b mut Entity> {
160        &mut self.data[0]
161    }
162
163    pub fn unwrap_nth<'a>(&'a self, n : usize) -> &'a Vec<&'b mut Entity> {
164        &self.data[n]
165    }
166    pub fn unwrap_mut_nth<'a>(&'a mut self, n : usize) -> &'a mut Vec<&'b mut Entity> {
167        &mut self.data[n]
168    }
169
170
171    pub fn new(entity_manager : &mut EntityManager, ids : &Vec<HashSet<i32>>) -> DataList<'b> {
172        DataList {
173            data : ids.iter().map(|i| {entity_manager.get_entities_by_ids(&i)}).collect()
174        }
175    }
176}
177
178/// System trait
179///
180/// You can implement one of those processes, but if you implement process_all - only it will be called, and if you dont implement process_all - all process_* will be called.
181///
182/// Most common case - implement only process_one.
183pub trait System {
184    /// System will subscribe only on components, sutisfied by this aspect.
185    fn aspect(&self) -> Aspect;
186
187    /// For each returned aspect, one additional entity pack DataList will be received.
188    /// Strongly recomends use it only with registration macro.
189    fn data_aspects(&self) -> Vec<Aspect> {
190        Vec::new()
191    }
192
193    #[cfg(feature = "prof")]
194    fn get_name(&self) -> String {
195        use std::intrinsics::*;
196        let type_name =
197            unsafe {
198                type_name::<Self>()
199            };
200        type_name.to_string()
201
202    }
203    fn on_created(&mut self, _ : &mut EntityManager) {
204
205    }
206    fn on_begin_frame(&mut self) {
207    }
208
209    fn on_added(&mut self, _ : &mut Entity) {
210    }
211
212    fn on_removed(&self, _ : &mut Entity) {
213    }
214
215    fn on_end_frame(&mut self) {
216    }
217    #[inline]
218    fn process_w(&mut self, _ : &mut Entity, _ : &mut WorldHandle) {
219    }
220
221    #[inline]
222    fn process_d(&mut self, _ : &mut Entity, _ : &mut DataList) {
223    }
224    #[inline]
225    fn process_wd(&mut self, _ : &mut Entity, _ : &mut WorldHandle, _ : &mut DataList) {
226    }
227
228    #[inline]
229    fn process_one(&mut self, _ : &mut Entity) {
230    }
231
232    fn process_all(&mut self, entities : &mut Vec<&mut Entity>, world: &mut WorldHandle, data : &mut DataList) {
233        for e in entities.iter_mut() {
234            self.process_one(e);
235            self.process_w(e, world);
236            self.process_d(e, data);
237            self.process_wd(e, world, data);
238        }
239    }
240}