Mercurial > parpg-core
diff src/parpg/bGrease/component/general.py @ 66:96af64cf3b81
Renamed grease to bGrease (Basic Grease) to get rid of conflicts with an already installed grease.
author | KarstenBock@gmx.net |
---|---|
date | Mon, 05 Sep 2011 15:00:34 +0200 |
parents | src/parpg/grease/component/general.py@09b581087d68 |
children | 0f659c7675f6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parpg/bGrease/component/general.py Mon Sep 05 15:00:34 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 bGrease.component import base +from bGrease.component import field +from bGrease.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__) + + +