comparison bGrease/world.py @ 65:e856b604b650

Changed "import bGrease" to "import parpg.bGrease".
author KarstenBock@gmx.net
date Wed, 21 Sep 2011 16:10:14 +0200
parents ff3e395abf91
children
comparison
equal deleted inserted replaced
64:b73050f98411 65:e856b604b650
22 """ 22 """
23 23
24 __version__ = '$Id$' 24 __version__ = '$Id$'
25 25
26 import itertools 26 import itertools
27 from bGrease import mode 27 from parpg.bGrease import mode
28 from bGrease.component import ComponentError 28 from parpg.bGrease.component import ComponentError
29 from bGrease.entity import Entity, ComponentEntitySet 29 from parpg.bGrease.entity import Entity, ComponentEntitySet
30 30
31 31
32 class BaseWorld(object): 32 class BaseWorld(object):
33 """A coordinated collection of components, systems and entities 33 """A coordinated collection of components, systems and entities
34 34
35 A world is also a mode that may be pushed onto a 35 A world is also a mode that may be pushed onto a
36 :class:`grease.mode.Manager` 36 :class:`grease.mode.Manager`
37 """ 37 """
38 38
39 components = None 39 components = None
40 """:class:`ComponentParts` object containing all world components. 40 """:class:`ComponentParts` object containing all world components.
41 :class:`grease.component.Component` objects define and contain all entity data 41 :class:`grease.component.Component` objects define and contain all entity data
42 """ 42 """
43 43
44 systems = None 44 systems = None
45 """:class:`Parts` object containing all world systems. 45 """:class:`Parts` object containing all world systems.
46 :class:`grease.System` objects define world and entity behavior 46 :class:`grease.System` objects define world and entity behavior
47 """ 47 """
48 48
49 renderers = None 49 renderers = None
50 """:class:`Parts` object containing all world renderers. 50 """:class:`Parts` object containing all world renderers.
51 :class:`grease.Renderer` objects define world presentation 51 :class:`grease.Renderer` objects define world presentation
52 """ 52 """
53 53
54 entities = None 54 entities = None
55 """Set of all entities that exist in the world""" 55 """Set of all entities that exist in the world"""
56 56
57 def __init__(self): 57 def __init__(self):
58 self.components = ComponentParts(self) 58 self.components = ComponentParts(self)
59 self.systems = Parts(self) 59 self.systems = Parts(self)
60 self.renderers = Parts(self) 60 self.renderers = Parts(self)
61 self.new_entity_id = itertools.count().next 61 self.new_entity_id = itertools.count().next
62 self.new_entity_id() # skip id 0 62 self.new_entity_id() # skip id 0
63 self.entities = WorldEntitySet(self) 63 self.entities = WorldEntitySet(self)
64 self._full_extent = EntityExtent(self, self.entities) 64 self._full_extent = EntityExtent(self, self.entities)
65 self._extents = {} 65 self._extents = {}
66 self.configure() 66 self.configure()
67 67
68 def configure(self): 68 def configure(self):
69 """Hook to configure the world after construction. This method 69 """Hook to configure the world after construction. This method
70 is called immediately after the world is initialized. Override 70 is called immediately after the world is initialized. Override
71 in a subclass to configure the world's components, systems, 71 in a subclass to configure the world's components, systems,
72 and renderers. 72 and renderers.
73 73
74 The default implementation does nothing. 74 The default implementation does nothing.
75 """ 75 """
76 76
77 def __getitem__(self, entity_class): 77 def __getitem__(self, entity_class):
78 """Return an :class:`EntityExtent` for the given entity class. This extent 78 """Return an :class:`EntityExtent` for the given entity class. This extent
79 can be used to access the set of entities of that class in the world 79 can be used to access the set of entities of that class in the world
80 or to query these entities via their components. 80 or to query these entities via their components.
81 81
82 Examples:: 82 Examples::
83 83
84 world[MyEntity] 84 world[MyEntity]
85 world[...] 85 world[...]
86 86
87 :param entity_class: The entity class for the extent. 87 :param entity_class: The entity class for the extent.
88 88
89 May also be a tuple of entity classes, in which case 89 May also be a tuple of entity classes, in which case
90 the extent returned contains union of all entities of the classes 90 the extent returned contains union of all entities of the classes
91 in the world. 91 in the world.
92 92
93 May also be the special value ellipsis (``...``), which 93 May also be the special value ellipsis (``...``), which
94 returns an extent containing all entities in the world. This allows 94 returns an extent containing all entities in the world. This allows
95 you to conveniently query all entities using ``world[...]``. 95 you to conveniently query all entities using ``world[...]``.
96 """ 96 """
97 if isinstance(entity_class, tuple): 97 if isinstance(entity_class, tuple):
98 entities = set() 98 entities = set()
99 for cls in entity_class: 99 for cls in entity_class:
100 if cls in self._extents: 100 if cls in self._extents:
101 entities |= self._extents[cls].entities 101 entities |= self._extents[cls].entities
102 return EntityExtent(self, entities) 102 return EntityExtent(self, entities)
103 elif entity_class is Ellipsis: 103 elif entity_class is Ellipsis:
104 return self._full_extent 104 return self._full_extent
105 try: 105 try:
106 return self._extents[entity_class] 106 return self._extents[entity_class]
107 except KeyError: 107 except KeyError:
108 extent = self._extents[entity_class] = EntityExtent(self, set()) 108 extent = self._extents[entity_class] = EntityExtent(self, set())
109 return extent 109 return extent
110 110
111 def draw_renderers(self): 111 def draw_renderers(self):
112 """Draw all renderers""" 112 """Draw all renderers"""
113 for renderer in self.renderers: 113 for renderer in self.renderers:
114 renderer.draw() 114 renderer.draw()
115 115
116 class WorldEntitySet(set): 116 class WorldEntitySet(set):
117 """Entity set for a :class:`World`""" 117 """Entity set for a :class:`World`"""
118 118
119 def __init__(self, world): 119 def __init__(self, world):
120 self.world = world 120 self.world = world
121 121
122 def add(self, entity): 122 def add(self, entity):
123 """Add the entity to the set and all necessary class sets 123 """Add the entity to the set and all necessary class sets
124 Return the unique entity id for the entity, creating one 124 Return the unique entity id for the entity, creating one
125 as needed. 125 as needed.
126 """ 126 """
127 super(WorldEntitySet, self).add(entity) 127 super(WorldEntitySet, self).add(entity)
128 for cls in entity.__class__.__mro__: 128 for cls in entity.__class__.__mro__:
129 if issubclass(cls, Entity): 129 if issubclass(cls, Entity):
130 self.world[cls].entities.add(entity) 130 self.world[cls].entities.add(entity)
131 131
132 def remove(self, entity): 132 def remove(self, entity):
133 """Remove the entity from the set and, world components, 133 """Remove the entity from the set and, world components,
134 and all necessary class sets 134 and all necessary class sets
135 """ 135 """
136 super(WorldEntitySet, self).remove(entity) 136 super(WorldEntitySet, self).remove(entity)
137 for component in self.world.components: 137 for component in self.world.components:
138 try: 138 try:
139 del component[entity] 139 del component[entity]
140 except KeyError: 140 except KeyError:
141 pass 141 pass
142 for cls in entity.__class__.__mro__: 142 for cls in entity.__class__.__mro__:
143 if issubclass(cls, Entity): 143 if issubclass(cls, Entity):
144 self.world[cls].entities.discard(entity) 144 self.world[cls].entities.discard(entity)
145 145
146 def discard(self, entity): 146 def discard(self, entity):
147 """Remove the entity from the set if it exists, if not, 147 """Remove the entity from the set if it exists, if not,
148 do nothing 148 do nothing
149 """ 149 """
150 try: 150 try:
151 self.remove(entity) 151 self.remove(entity)
152 except KeyError: 152 except KeyError:
153 pass 153 pass
154 154
155 155
156 class EntityExtent(object): 156 class EntityExtent(object):
157 """Encapsulates a set of entities queriable by component. Extents 157 """Encapsulates a set of entities queriable by component. Extents
158 are accessed by using an entity class as a key on the :class:`World`:: 158 are accessed by using an entity class as a key on the :class:`World`::
159 159
160 extent = world[MyEntity] 160 extent = world[MyEntity]
161 """ 161 """
162 162
163 entities = None 163 entities = None
164 """The full set of entities in the extent""" 164 """The full set of entities in the extent"""
165 165
166 def __init__(self, world, entities): 166 def __init__(self, world, entities):
167 self.__world = world 167 self.__world = world
168 self.entities = entities 168 self.entities = entities
169 169
170 def __getattr__(self, name): 170 def __getattr__(self, name):
171 """Return a queriable :class:`ComponentEntitySet` for the named component 171 """Return a queriable :class:`ComponentEntitySet` for the named component
172 172
173 Example:: 173 Example::
174 174
175 world[MyEntity].movement.velocity > (0, 0) 175 world[MyEntity].movement.velocity > (0, 0)
176 176
177 Returns a set of entities where the value of the :attr:`velocity` field 177 Returns a set of entities where the value of the :attr:`velocity` field
178 of the :attr:`movement` component is greater than ``(0, 0)``. 178 of the :attr:`movement` component is greater than ``(0, 0)``.
179 """ 179 """
180 component = getattr(self.__world.components, name) 180 component = getattr(self.__world.components, name)
181 return ComponentEntitySet(component, self.entities & component.entities) 181 return ComponentEntitySet(component, self.entities & component.entities)
182 182
183 183
184 class Parts(object): 184 class Parts(object):
185 """Maps world parts to attributes. The parts are kept in the 185 """Maps world parts to attributes. The parts are kept in the
186 order they are set. Parts may also be inserted out of order. 186 order they are set. Parts may also be inserted out of order.
187 187
188 Used for: 188 Used for:
189 189
190 - :attr:`World.systems` 190 - :attr:`World.systems`
191 - :attr:`World.renderers` 191 - :attr:`World.renderers`
192 """ 192 """
193 193
194 _world = None 194 _world = None
195 _parts = None 195 _parts = None
196 _reserved_names = ('entities', 'entity_id', 'world') 196 _reserved_names = ('entities', 'entity_id', 'world')
197 197
198 def __init__(self, world): 198 def __init__(self, world):
199 self._world = world 199 self._world = world
200 self._parts = [] 200 self._parts = []
201 201
202 def _validate_name(self, name): 202 def _validate_name(self, name):
203 if (name in self._reserved_names or name.startswith('_') 203 if (name in self._reserved_names or name.startswith('_')
204 or hasattr(self.__class__, name)): 204 or hasattr(self.__class__, name)):
205 raise ComponentError('illegal part name: %s' % name) 205 raise ComponentError('illegal part name: %s' % name)
206 return name 206 return name
207 207
208 def __setattr__(self, name, part): 208 def __setattr__(self, name, part):
209 if not hasattr(self.__class__, name): 209 if not hasattr(self.__class__, name):
210 self._validate_name(name) 210 self._validate_name(name)
211 if not hasattr(self, name): 211 if not hasattr(self, name):
212 self._parts.append(part) 212 self._parts.append(part)
213 else: 213 else:
214 old_part = getattr(self, name) 214 old_part = getattr(self, name)
215 self._parts[self._parts.index(old_part)] = part 215 self._parts[self._parts.index(old_part)] = part
216 super(Parts, self).__setattr__(name, part) 216 super(Parts, self).__setattr__(name, part)
217 if hasattr(part, 'set_world'): 217 if hasattr(part, 'set_world'):
218 part.set_world(self._world) 218 part.set_world(self._world)
219 elif name.startswith("_"): 219 elif name.startswith("_"):
220 super(Parts, self).__setattr__(name, part) 220 super(Parts, self).__setattr__(name, part)
221 else: 221 else:
222 raise AttributeError("%s attribute is read only" % name) 222 raise AttributeError("%s attribute is read only" % name)
223 223
224 def __delattr__(self, name): 224 def __delattr__(self, name):
225 self._validate_name(name) 225 self._validate_name(name)
226 part = getattr(self, name) 226 part = getattr(self, name)
227 self._parts.remove(part) 227 self._parts.remove(part)
228 super(Parts, self).__delattr__(name) 228 super(Parts, self).__delattr__(name)
229 229
230 def insert(self, name, part, before=None, index=None): 230 def insert(self, name, part, before=None, index=None):
231 """Add a part with a particular name at a particular index. 231 """Add a part with a particular name at a particular index.
232 If a part by that name already exists, it is replaced. 232 If a part by that name already exists, it is replaced.
233 233
234 :arg name: The name of the part. 234 :arg name: The name of the part.
235 :type name: str 235 :type name: str
236 236
237 :arg part: The component, system, or renderer part to insert 237 :arg part: The component, system, or renderer part to insert
238 238
239 :arg before: A part object or name. If specified, the part is 239 :arg before: A part object or name. If specified, the part is
240 inserted before the specified part in order. 240 inserted before the specified part in order.
241 241
242 :arg index: If specified, the part is inserted in the position 242 :arg index: If specified, the part is inserted in the position
243 specified. You cannot specify both before and index. 243 specified. You cannot specify both before and index.
244 :type index: int 244 :type index: int
245 """ 245 """
246 assert before is not None or index is not None, ( 246 assert before is not None or index is not None, (
247 "Must specify a value for 'before' or 'index'") 247 "Must specify a value for 'before' or 'index'")
248 assert before is None or index is None, ( 248 assert before is None or index is None, (
249 "Cannot specify both 'before' and 'index' arguments when inserting") 249 "Cannot specify both 'before' and 'index' arguments when inserting")
250 self._validate_name(name) 250 self._validate_name(name)
251 if before is not None: 251 if before is not None:
252 if isinstance(before, str): 252 if isinstance(before, str):
253 before = getattr(self, before) 253 before = getattr(self, before)
254 index = self._parts.index(before) 254 index = self._parts.index(before)
255 if hasattr(self, name): 255 if hasattr(self, name):
256 old_part = getattr(self, name) 256 old_part = getattr(self, name)
257 self._parts.remove(old_part) 257 self._parts.remove(old_part)
258 self._parts.insert(index, part) 258 self._parts.insert(index, part)
259 super(Parts, self).__setattr__(name, part) 259 super(Parts, self).__setattr__(name, part)
260 if hasattr(part, 'set_world'): 260 if hasattr(part, 'set_world'):
261 part.set_world(self._world) 261 part.set_world(self._world)
262 262
263 def __iter__(self): 263 def __iter__(self):
264 """Iterate the parts in order""" 264 """Iterate the parts in order"""
265 return iter(tuple(self._parts)) 265 return iter(tuple(self._parts))
266 266
267 def __len__(self): 267 def __len__(self):
268 return len(self._parts) 268 return len(self._parts)
269 269
270 270
271 class ComponentParts(Parts): 271 class ComponentParts(Parts):
272 """Maps world components to attributes. The components are kept in the 272 """Maps world components to attributes. The components are kept in the
273 order they are set. Components may also be inserted out of order. 273 order they are set. Components may also be inserted out of order.
274 274
275 Used for: :attr:`World.components` 275 Used for: :attr:`World.components`
276 """ 276 """
277 277
278 def join(self, *component_names): 278 def join(self, *component_names):
279 """Join and iterate entity data from multiple components together. 279 """Join and iterate entity data from multiple components together.
280 280
281 For each entity in all of the components named, yield a tuple containing 281 For each entity in all of the components named, yield a tuple containing
282 the entity data from each component specified. 282 the entity data from each component specified.
283 283
284 This is useful in systems that pull data from multiple components. 284 This is useful in systems that pull data from multiple components.
285 285
286 Typical Usage:: 286 Typical Usage::
287 287
288 for position, movement in world.components.join("position", "movement"): 288 for position, movement in world.components.join("position", "movement"):
289 # Do something with each entity's position and movement data 289 # Do something with each entity's position and movement data
290 """ 290 """
291 if component_names: 291 if component_names:
292 components = [getattr(self, self._validate_name(name)) 292 components = [getattr(self, self._validate_name(name))
293 for name in component_names] 293 for name in component_names]
294 if len(components) > 1: 294 if len(components) > 1:
295 entities = components[0].entities & components[1].entities 295 entities = components[0].entities & components[1].entities
296 for comp in components[2:]: 296 for comp in components[2:]:
297 entities &= comp.entities 297 entities &= comp.entities
298 else: 298 else:
299 entities = components[0].entities 299 entities = components[0].entities
300 for entity in entities: 300 for entity in entities:
301 yield tuple(comp[entity] for comp in components) 301 yield tuple(comp[entity] for comp in components)
302