package com.artemis;
import java.util.BitSet;
import java.util.HashMap;
import com.artemis.utils.Bag;
import com.artemis.utils.ImmutableBag;
/**
* The most raw entity system. It should not typically be used, but you can create your own
* entity system handling by extending this. It is recommended that you use the other provided
* entity system implementations.
*
* @author Arni Arent
*
*/
public abstract class EntitySystem implements EntityObserver {
private final int systemIndex;
protected World world;
private Bag<Entity> actives;
private Aspect aspect;
private BitSet allSet;
private BitSet exclusionSet;
private BitSet oneSet;
private boolean passive;
private boolean dummy;
/**
* Creates an entity system that uses the specified aspect as a matcher against entities.
* @param aspect to match against entities
*/
public EntitySystem(Aspect aspect) {
actives = new Bag<Entity>();
this.aspect = aspect;
allSet = aspect.getAllSet();
exclusionSet = aspect.getExclusionSet();
oneSet = aspect.getOneSet();
systemIndex = SystemIndexManager.getIndexFor(this.getClass());
dummy = allSet.isEmpty() && oneSet.isEmpty(); // This system can't possibly be interested in any entity, so it must be "dummy"
}
/**
* Called before processing of entities begins.
*/
protected void begin() {
}
public final void process() {
if(checkProcessing()) {
begin();
processEntities(actives);
end();
}
}
/**
* Called after the processing of entities ends.
*/
protected void end() {
}
/**
* Any implementing entity system must implement this method and the logic
* to process the given entities of the system.
*
* @param entities the entities this system contains.
*/
protected abstract void processEntities(ImmutableBag<Entity> entities);
/**
*
* @return true if the system should be processed, false if not.
*/
protected abstract boolean checkProcessing();
/**
* Override to implement code that gets executed when systems are initialized.
*/
protected void initialize() {};
/**
* Called if the system has received a entity it is interested in, e.g. created or a component was added to it.
* @param e the entity that was added to this system.
*/
protected void inserted(Entity e) {};
/**
* Called if a entity was removed from this system, e.g. deleted or had one of it's components removed.
* @param e the entity that was removed from this system.
*/
protected void removed(Entity e) {};
/**
* Will check if the entity is of interest to this system.
* @param e entity to check
*/
protected final void check(Entity e) {
if(dummy) {
return;
}
boolean contains = e.getSystemBits().get(systemIndex);
boolean interested = true; // possibly interested, let's try to prove it wrong.
BitSet componentBits = e.getComponentBits();
// Check if the entity possesses ALL of the components defined in the aspect.
if(!allSet.isEmpty()) {
for (int i = allSet.nextSetBit(0); i >= 0; i = allSet.nextSetBit(i+1)) {
if(!componentBits.get(i)) {
interested = false;
break;
}
}
}
// Check if the entity possesses ANY of the exclusion components, if it does then the system is not interested.
if(!exclusionSet.isEmpty() && interested) {
interested = !exclusionSet.intersects(componentBits);
}
// Check if the entity possesses ANY of the components in the oneSet. If so, the system is interested.
if(!oneSet.isEmpty()) {
interested = oneSet.intersects(componentBits);
}
if (interested && !contains) {
insertToSystem(e);
} else if (!interested && contains) {
removeFromSystem(e);
}
}
private void removeFromSystem(Entity e) {
actives.remove(e);
e.getSystemBits().clear(systemIndex);
removed(e);
}
private void insertToSystem(Entity e) {
actives.add(e);
e.getSystemBits().set(systemIndex);
inserted(e);
}
@Override
public final void added(Entity e) {
check(e);
}
@Override
public final void changed(Entity e) {
check(e);
}
@Override
public final void deleted(Entity e) {
if(e.getSystemBits().get(systemIndex)) {
removeFromSystem(e);
}
}
@Override
public final void disabled(Entity e) {
if(e.getSystemBits().get(systemIndex)) {
removeFromSystem(e);
}
}
@Override
public final void enabled(Entity e) {
check(e);
}
protected final void setWorld(World world) {
this.world = world;
}
protected boolean isPassive() {
return passive;
}
protected void setPassive(boolean passive) {
this.passive = passive;
}
public ImmutableBag<Entity> getActives() {
return actives;
}
/**
* Used to generate a unique bit for each system.
* Only used internally in EntitySystem.
*/
private static class SystemIndexManager {
private static int INDEX = 0;
private static HashMap<Class<? extends EntitySystem>, Integer> indices = new HashMap<Class<? extends EntitySystem>, Integer>();
private static int getIndexFor(Class<? extends EntitySystem> es){
Integer index = indices.get(es);
if(index == null) {
index = INDEX++;
indices.put(es, index);
}
return index;
}
}
}