comparison src/parpg/grease/component/general.py @ 27:09b581087d68

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents
children
comparison
equal deleted inserted replaced
26:5529dd5644b8 27:09b581087d68
1 #############################################################################
2 #
3 # Copyright (c) 2010 by Casey Duncan and contributors
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the MIT License
7 # A copy of the license should accompany this distribution.
8 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11 #
12 #############################################################################
13
14 __version__ = '$Id$'
15
16 from grease.component import base
17 from grease.component import field
18 from grease.entity import ComponentEntitySet
19
20
21 class Component(dict):
22 """General component with a configurable schema
23
24 The field schema is defined via keyword args where the
25 arg name is the field name and the value is the type object.
26
27 The following types are supported for fields:
28
29 - :class:`int`
30 - :class:`float`
31 - :class:`bool`
32 - :class:`str`
33 - :class:`object`
34 - |Vec2d|
35 - |Vec2dArray|
36 - |RGBA|
37 - |Rect|
38 """
39
40 deleted_entities = ()
41 """List of entities deleted from the component since the last time step"""
42
43 new_entities = ()
44 """List of entities added to the component since the last time step"""
45
46 def __init__(self, **fields):
47 self.fields = {}
48 for fname, ftype in fields.items():
49 assert ftype in field.types, fname + " has an illegal field type"
50 self.fields[fname] = field.Field(self, fname, ftype)
51 self.entities = ComponentEntitySet(self)
52 self._added = []
53 self._deleted = []
54
55 def set_world(self, world):
56 self.world = world
57
58 def step(self, dt):
59 """Update the component for the next timestep"""
60 delitem = super(Component, self).__delitem__
61 for entity in self._deleted:
62 delitem(entity)
63 self.new_entities = self._added
64 self.deleted_entities = self._deleted
65 self._added = []
66 self._deleted = []
67
68 def set(self, entity, data=None, **data_kw):
69 """Set the component data for an entity, adding it to the
70 component if it is not already a member.
71
72 If data is specified, its data for the new entity's fields are
73 copied from its attributes, making it easy to copy another
74 entity's data. Keyword arguments are also matched to fields.
75 If both a data attribute and keyword argument are supplied for
76 a single field, the keyword arg is used.
77 """
78 if data is not None:
79 for fname, field in self.fields.items():
80 if fname not in data_kw and hasattr(data, fname):
81 data_kw[fname] = getattr(data, fname)
82 data = self[entity] = Data(self.fields, entity, **data_kw)
83 return data
84
85 def __setitem__(self, entity, data):
86 assert entity.world is self.world, "Entity not in component's world"
87 if entity not in self.entities:
88 self._added.append(entity)
89 self.entities.add(entity)
90 super(Component, self).__setitem__(entity, data)
91
92 def remove(self, entity):
93 if entity in self.entities:
94 self._deleted.append(entity)
95 self.entities.remove(entity)
96 return True
97 return False
98
99 __delitem__ = remove
100
101 def __repr__(self):
102 return '<%s %x of %r>' % (
103 self.__class__.__name__, id(self), getattr(self, 'world', None))
104
105
106 class Singleton(Component):
107 """Component that may contain only a single entity"""
108
109 def add(self, entity_id, data=None, **data_kw):
110 if entity_id not in self._data:
111 self.entity_id_set.clear()
112 self._data.clear()
113 Component.add(self, entity_id, data, **data_kw)
114
115 @property
116 def entity(self):
117 """Return the entity in the component, or None if empty"""
118 if self._data:
119 return self.manager[self._data.keys()[0]]
120
121
122 class Data(object):
123
124 def __init__(self, fields, entity, **data):
125 self.__dict__['_Data__fields'] = fields
126 self.__dict__['entity'] = entity
127 for field in fields.values():
128 if field.name in data:
129 setattr(self, field.name, data[field.name])
130 else:
131 setattr(self, field.name, field.default())
132
133 def __setattr__(self, name, value):
134 if name in self.__fields:
135 self.__dict__[name] = self.__fields[name].cast(value)
136 else:
137 raise AttributeError("Invalid data field: " + name)
138
139 def __repr__(self):
140 return '<%s(%r)>' % (self.__class__.__name__, self.__dict__)
141
142
143