# !/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import abstractmethod, ABCMeta
from uuid import uuid1
from hashlib import md5
from .exceptions import (DuplicateEntityError, DuplicateSystemError,
UnmanagedEntityError, UnmanagedSystemError,
NonUniqueTagError, DeadEntityError)
[docs]class World(object):
'''
A World holds all entities, groups, and systems
'''
def __init__(self, delta=1):
self._delta = delta
self._entities = list()
self._systems = list()
self._groups = dict()
[docs] def add_entity(self, entity, second=False):
''' Add entity to world.
entity is of type Entity
'''
if not entity in self._entities:
if second:
self._entities.append(entity)
else:
entity.set_world(self)
else:
raise DuplicateEntityError(entity)
[docs] def add_entities(self, *entities):
''' Add multiple entities to world
All members of entities are of type Entity
'''
for entity in entities:
self.add_entity(entity)
[docs] def add_system(self, system):
'''
Add system to the world.
All systems will be processed on World.process()
system is of type System
'''
if system not in self._systems:
system.set_world(self)
self._systems.append(system)
else:
raise DuplicateSystemError(system)
[docs] def register_entity_to_group(self, entity, group):
'''
Add entity to a group.
If group does not exist, entity will be added as first member
entity is of type Entity
group is a string that is the name of the group
'''
if entity in self._entities:
if group in self._groups:
self._groups[group].append(entity)
else:
self._groups[group] = [entity]
else:
raise UnmanagedEntityError(entity)
[docs] def deregister_entity_from_group(self, entity, group):
'''
Removes entity from group
'''
if entity in self._entities:
if entity in self._groups[group]:
self._groups[group].remove(entity)
else:
raise UnmanagedEntityError(entity)
[docs] def create_entity(self, tag=''):
'''
Creates Entity
(optionally) tag is a string that is the tag of the Entity.
'''
return Entity(tag)
[docs] def remove_entity(self, entity, second=False):
'''
Removes entity from world and kills entity
'''
if entity in self._entities:
if second:
for group in self._groups.keys():
if entity in self._groups[group]:
self.deregister_entity_from_group(entity, group)
self._entities.remove(entity)
else:
entity.kill()
else:
raise UnmanagedEntityError(entity)
[docs] def remove_system(self, system):
'''
Removes system from world and kills system
'''
if system in self._systems:
self._systems.remove(system)
else:
raise UnmanagedSystemError(system)
[docs] def get_entity_by_tag(self, tag):
'''
Get entity by tag
tag is a string that is the tag of the Entity.
'''
matching_entities = list(filter(lambda entity: entity.get_tag() == tag,
self._entities))
if matching_entities:
return matching_entities[0]
else:
return None
[docs] def get_entities_by_components(self, *components):
'''
Get entity by list of components
All members of components must be of type Component
'''
return list(filter(lambda entity:
set(components) <=
set(map(type, entity.get_components())),
self._entities))
[docs] def get_entities(self):
'''
Gets all entities
'''
return self._entities
[docs] def get_group(self, group):
'''
Gets a specific group
group is the string of a Group
'''
if group in self._groups:
return self._groups[group]
else:
return []
[docs] def get_delta(self):
'''
Returns delta
'''
return self._delta
[docs] def set_delta(self, delta):
'''
Sets delta
'''
self._delta = delta
[docs] def process(self):
'''
Processes entire world and all systems in it
'''
for system in self._systems:
system.process(self._delta)
[docs]class Entity(object):
'''
Instances of Entity are unique IDs that hold Components.
(optionally) tag is a string that refers to the entity
'''
def __init__(self, tag=''):
self._tag = tag
self._uuid = uuid1().int
self._components = list()
self._world = None
[docs] def check_alive(function):
def check_and_call(self, *args, **kwargs):
'''
Checks if alive before doing something
'''
if self._uuid:
return function(self, *args, **kwargs)
else:
raise DeadEntityError()
return check_and_call
@check_alive
[docs] def set_world(self, world):
'''
Sets the world an entity belongs to.
Checks for tag conflicts before adding.
'''
if world.get_entity_by_tag(self._tag) and self._tag != '':
raise NonUniqueTagError(self._tag)
else:
self._world = world
world.add_entity(self, True)
@check_alive
[docs] def get_uuid(self):
'''
Returns uuid
'''
return self._uuid
@check_alive
[docs] def get_tag(self):
'''
Returns tag
'''
return self._tag
@check_alive
[docs] def set_tag(self, tag):
'''
Sets the tag.
If the Entity belongs to the world it will check for tag conflicts.
'''
if self._world:
if self._world.get_entity_by_tag(tag):
raise NonUniqueTagError(tag)
self._tag = tag
@check_alive
[docs] def kill(self):
'''Kills Entity'''
if self._world:
self._world.remove_entity(self, True)
self._world = None
self._tag = None
self._uuid = None
self._components = None
@check_alive
[docs] def add_component(self, component):
'''
Adds a Component to an Entity
'''
if component not in self._components:
self._components.append(component)
else: # Replace Component
self._components[self._components.index(component)] = component
@check_alive
[docs] def get_component(self, component_type):
'''
Gets component of component_type or returns None
'''
matching_components = list(filter(lambda component:
isinstance(component,
component_type),
self._components))
if matching_components:
return matching_components[0]
else:
return None
@check_alive
[docs] def get_components(self):
'''
Returns all components
'''
return self._components
def __str__(self):
return 'Entity {0}'.format(self.__class__)
def __repr__(self):
return '{0} {1}'.format(self.__class__, self._uuid)
def __eq__(self, other):
return self.get_uuid() == other.get_uuid()
def __ne__(self, other):
return self.get_uuid() != other.get_uuid()
def __hash__(self):
return int(md5(self.__repr__()).hexdigest(), 16)
[docs]class Component(object):
def __str__(self):
return 'Component {0}'.format(self.__class__)
def __repr__(self):
return '{0}'.format(self.__class__)
def __eq__(self, other):
return type(self) == type(other)
def __ne__(self, other):
return type(self) != type(other)
def __hash__(self):
return int(md5(self.__repr__()).hexdigest(), 16)
[docs]class System(object):
__metaclass__ = ABCMeta
[docs] def set_world(self, world):
'''
Sets the world this system belongs to
'''
self.world = world
@abstractmethod
[docs] def process(self, delta):
'''Update the system'''
def __str__(self):
return 'System {0}'.format(self.__class__)
def __repr__(self):
return '{0}'.format(self.__class__)
def __eq__(self, other):
return type(self) == type(other)
def __ne__(self, other):
return type(self) == type(other)
def __hash__(self):
return int(md5(self.__repr__()).hexdigest(), 16)