-
Notifications
You must be signed in to change notification settings - Fork 16
/
ecs.rs
194 lines (170 loc) · 6.06 KB
/
ecs.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
use std::collections::HashMap;
use world;
use entity::{Entity};
use components;
use component_ref::{ComponentRef, ComponentRefMut};
use stats;
use world::{WorldState};
/// Entity component system.
#[derive(RustcDecodable, RustcEncodable)]
pub struct Ecs {
next_idx: usize,
reusable_idxs: Vec<usize>,
// Could use Bitv for active, but I can't bother to write the serializer...
active: Vec<bool>,
parent: HashMap<usize, usize>,
}
impl Ecs {
pub fn new() -> Ecs {
Ecs {
next_idx: 0,
reusable_idxs: vec![],
active: vec![],
parent: HashMap::new(),
}
}
pub fn new_entity(&mut self, parent: Option<Entity>) -> Entity {
// Get the entity idx, reuse old ones to keep the indexing compact.
let idx = match self.reusable_idxs.pop() {
None => {
let ret = self.next_idx;
self.next_idx += 1;
ret
}
Some(idx) => idx
};
if let Some(Entity(p_idx)) = parent {
assert!(self.active[p_idx]);
self.parent.insert(idx, p_idx);
}
if self.active.len() <= idx {
self.active.resize(idx + 1, false);
}
assert!(!self.active[idx]);
self.active[idx] = true;
Entity(idx)
}
/// Delete an entity from the entity component system.
///
/// XXX: The user is currently responsible for never using an entity
/// handle again after delete_entity has been called on it. Using an
/// entity handle after deletion may return another entity's contents.
pub fn delete(&mut self, Entity(idx): Entity) {
assert!(self.active[idx]);
self.parent.remove(&idx);
self.reusable_idxs.push(idx);
self.active[idx] = false;
}
/// Return an iterator for the entities. The iterator will not be
/// invalidated if entities are added or removed during iteration. The
/// iterator also won't maintain a lock on the world singleton outside
/// calling next.
///
/// XXX: It is currently unspecified whether entities added during
/// iteration will show up in the iteration or not.
pub fn iter(&self) -> EntityIter {
EntityIter(0)
}
/// Return the optional parent entity of an entity.
pub fn parent(&self, Entity(idx): Entity) -> Option<Entity> {
self.parent.get(&idx).map(|&idx| Entity(idx))
}
/// Change the parent of a live entity
pub fn reparent(&mut self, Entity(idx): Entity, Entity(new_parent_idx): Entity) {
self.parent.insert(idx, new_parent_idx);
}
}
pub struct EntityIter(usize);
impl Iterator for EntityIter {
type Item = Entity;
fn next(&mut self) -> Option<Entity> {
world::with(|w| {
let &mut EntityIter(ref mut idx) = self;
loop {
if *idx >= w.ecs.active.len() { return None; }
let ret = Entity(*idx);
*idx += 1;
if !w.ecs.active[*idx - 1] { continue; }
return Some(ret);
}
})
}
}
////////////////////////////////////////////////////////////////////////
// The one big macro for defining the full set of available entity components
// in one place.
macro_rules! components {
{
// Declare the list of types which are included as components in the
// game's entity component system. Also declare the non-mutable and
// mutable accessor names for them. Example
//
// ```notrust
// [Mesh, meshes, meshes_mut],
// ```
$([$comp:ty, $access:ident, $access_mut:ident],)+
} => {
// The master container for all the components.
#[derive(RustcEncodable, RustcDecodable)]
pub struct Comps {
$($access: HashMap<usize, Option<$comp>>,)+
}
/// Container for all regular entity components.
impl Comps {
pub fn new() -> Comps {
Comps {
$($access: HashMap::new(),)+
}
}
/// Remove the given entity from all the contained components.
pub fn remove(&mut self, Entity(idx): Entity) {
$(self.$access.remove(&idx);)+
}
}
// Implement the Componet trait for the type, this provides an uniform
// syntax for adding component values to entities used by the entity
// factory.
$(
impl Component for $comp {
// XXX: Figure out how to move self into the closure to
// get rid of the .clone.
fn add_to(self, e: Entity) { world::with_mut(|w| w.$access_mut().insert(e, self.clone())) }
}
)+
// Implement the trait for accessing all the components that
// WorldState will implement
pub trait ComponentAccess<'a> {
$(
fn $access(&'a self) -> ComponentRef<'a, $comp>;
fn $access_mut(&'a mut self) -> ComponentRefMut<'a, $comp>;
)+
}
impl<'a> ComponentAccess<'a> for WorldState {
$(
fn $access(&'a self) -> ComponentRef<'a, $comp> {
ComponentRef::new(&self.ecs, &self.comps.$access)
}
fn $access_mut(&'a mut self) -> ComponentRefMut<'a, $comp> {
ComponentRefMut::new(&mut self.ecs, &mut self.comps.$access)
}
)+
}
}
}
pub trait Component {
/// Create an uniform syntax for attaching components to entities to allow
/// a fluent API for constructing prototypes.
fn add_to(self, e: Entity);
}
// Component loadout for the game.
components! {
[components::IsPrototype, prototypes, prototypes_mut],
[components::Desc, descs, descs_mut],
[components::MapMemory, map_memories, map_memories_mut],
[stats::Stats, stats, stats_mut],
[components::Spawn, spawns, spawns_mut],
[components::Health, healths, healths_mut],
[components::Brain, brains, brains_mut],
[components::Item, items, items_mut],
[components::StatsCache, stats_caches, stats_caches_mut],
}