diff grease/component/general.py @ 5:bc88f7d5ca8b

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/general.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,143 @@
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan and contributors
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+
+__version__ = '$Id$'
+
+from grease.component import base
+from grease.component import field
+from grease.entity import ComponentEntitySet
+
+
+class Component(dict):
+	"""General component with a configurable schema
+
+	The field schema is defined via keyword args where the 
+	arg name is the field name and the value is the type object.
+
+	The following types are supported for fields:
+
+	- :class:`int`
+	- :class:`float`
+	- :class:`bool`
+	- :class:`str`
+	- :class:`object`
+	- |Vec2d|
+	- |Vec2dArray|
+	- |RGBA|
+	- |Rect|
+	"""
+
+	deleted_entities = ()
+	"""List of entities deleted from the component since the last time step"""
+
+	new_entities = ()
+	"""List of entities added to the component since the last time step"""
+
+	def __init__(self, **fields):
+		self.fields = {}
+		for fname, ftype in fields.items():
+			assert ftype in field.types, fname + " has an illegal field type"
+			self.fields[fname] = field.Field(self, fname, ftype)
+		self.entities = ComponentEntitySet(self)
+		self._added = []
+		self._deleted = []
+	
+	def set_world(self, world):
+		self.world = world
+	
+	def step(self, dt):
+		"""Update the component for the next timestep"""
+		delitem = super(Component, self).__delitem__
+		for entity in self._deleted:
+			delitem(entity)
+		self.new_entities = self._added
+		self.deleted_entities = self._deleted
+		self._added = []
+		self._deleted = []
+	
+	def set(self, entity, data=None, **data_kw):
+		"""Set the component data for an entity, adding it to the
+		component if it is not already a member.
+
+		If data is specified, its data for the new entity's fields are
+		copied from its attributes, making it easy to copy another
+		entity's data. Keyword arguments are also matched to fields.
+		If both a data attribute and keyword argument are supplied for
+		a single field, the keyword arg is used.
+		"""
+		if data is not None:
+			for fname, field in self.fields.items():
+				if fname not in data_kw and hasattr(data, fname):
+					data_kw[fname] = getattr(data, fname)
+		data = self[entity] = Data(self.fields, entity, **data_kw)
+		return data
+	
+	def __setitem__(self, entity, data):
+		assert entity.world is self.world, "Entity not in component's world"
+		if entity not in self.entities:
+			self._added.append(entity)
+			self.entities.add(entity)
+		super(Component, self).__setitem__(entity, data)
+	
+	def remove(self, entity):
+		if entity in self.entities:
+			self._deleted.append(entity)
+			self.entities.remove(entity)
+			return True
+		return False
+	
+	__delitem__ = remove
+
+	def __repr__(self):
+		return '<%s %x of %r>' % (
+			self.__class__.__name__, id(self), getattr(self, 'world', None))
+
+
+class Singleton(Component):
+	"""Component that may contain only a single entity"""
+
+	def add(self, entity_id, data=None, **data_kw):
+		if entity_id not in self._data:
+			self.entity_id_set.clear()
+			self._data.clear()
+		Component.add(self, entity_id, data, **data_kw)
+	
+	@property
+	def entity(self):
+		"""Return the entity in the component, or None if empty"""
+		if self._data:
+			return self.manager[self._data.keys()[0]]
+	
+
+class Data(object):
+
+	def __init__(self, fields, entity, **data):
+		self.__dict__['_Data__fields'] = fields
+		self.__dict__['entity'] = entity
+		for field in fields.values():
+			if field.name in data:
+				setattr(self, field.name, data[field.name])
+			else:
+				setattr(self, field.name, field.default())
+	
+	def __setattr__(self, name, value):
+		if name in self.__fields:
+			self.__dict__[name] = self.__fields[name].cast(value)
+		else:
+			raise AttributeError("Invalid data field: " + name)
+	
+	def __repr__(self):
+		return '<%s(%r)>' % (self.__class__.__name__, self.__dict__)
+
+			
+