package net.alcuria.umbracraft.engine.entities; import net.alcuria.umbracraft.Config; import net.alcuria.umbracraft.Game; import net.alcuria.umbracraft.definitions.component.ComponentDefinition; import net.alcuria.umbracraft.definitions.map.EntityReferenceDefinition; import net.alcuria.umbracraft.engine.components.Component; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; /** A top-level game object. Players, enemies, decorations, and so on, all should * be instantiated as Entities with logic separated out in the {@link Component} * objects. TODO: We can use a map instead of an array to get a minor * performance boost out of fetching components. * @author Andrew Keturi */ public class Entity implements BaseEntity, Comparable<Entity> { public static final String PLAYER = "Player"; private final Array<String> args = new Array<String>(); private final Array<Component> components; private boolean isVisible = true; private String name, tag, id; public Vector3 position, velocity; private int renderOffset; public float speedModifier = 1; /** Creates an entity with no components */ public Entity() { components = new Array<Component>(); position = new Vector3(); velocity = new Vector3(); } /** Creates an entity with several pre-defined components * @param components the initial components */ public Entity(Component... components) { this(); for (Component component : components) { component.create(this); this.components.add(component); } } /** Creates an entity with no components and a given name * @param name the name {@link String} */ public Entity(String name) { this(); this.name = name; } /** Creates an entity with no components and a given name + tag * @param name the name {@link String} * @param tag the tag {@link String} */ public Entity(String name, String tag) { this(); this.name = name; this.tag = tag; } /** Adds a single component after instantiation * @param component the component to add */ public void addComponent(Component component) { component.create(this); components.add(component); } /** Adds a component from a definition * @param definition */ public void addComponent(ComponentDefinition definition) { addComponent(definition.create()); } @Override public int compareTo(Entity otherEntity) { return (int) ((otherEntity.position.y - otherEntity.position.z - otherEntity.renderOffset) - (position.y - position.z - renderOffset)); } /** Disposes/kills all components */ public void dispose() { for (int i = 0; i < components.size; i++) { components.get(i).dispose(this); } } /** @return arguments passed into the entity for creation from the map editor */ public Array<String> getArguments() { return args; } /** Gets a component. Returns <code>null</code> if the component is not * present. Be sure to check the return value before making assumptions. * @param clazz the {@link Component} type * @return the component */ public <T extends Component> T getComponent(Class<T> clazz) { for (int i = 0; i < components.size; i++) { if (clazz.isInstance(components.get(i))) { return (T) components.get(i); } } return null; } /** @return a unique identifier for this entity. Set only from * {@link EntityReferenceDefinition}. */ public String getId() { return id; } /** @return the name */ public String getName() { return name; } /** @return the render offset */ public int getRenderOffset() { return renderOffset; } /** @return the tag */ public String getTag() { return tag; } /** A helper function to determine if this Entity is tagged with a particular * {@link String}. A match is returned if and only if the tags are both non * null and match (via String comparison). * @param tag a {@link String} to check * @return <code>true</code> if the entity has this tag */ public boolean isTagged(String tag) { return tag != null && this.tag != null && this.tag.equals(tag); } /** @return the isVisible */ public boolean isVisible() { return isVisible; } /** Removes a component from the entity by iterating through the components * attached to this entity until a class match is found. This is suboptimal. * We can rework this is it becomes a bottleneck. * @param clazz the component type */ public void removeComponent(Class<? extends Component> clazz) { for (Component component : components) { if (clazz.isInstance(component)) { components.removeValue(component, true); return; } } } /** Removes a component by identity comparison * @param component */ public void removeComponent(Component component) { components.removeValue(component, true); } /** Renders all components */ @Override public void render() { if (isVisible) { for (int i = 0; i < components.size; i++) { components.get(i).render(this); } } } /** Given an {@link EntityReferenceDefinition}, sets the relevant fields of * the entity. * @param reference an {@link EntityReferenceDefinition} to use when * updating this entity. * @param mapId */ public void setFromReference(EntityReferenceDefinition reference, String mapId) { setName(reference.name); id = String.format("%s@%s(%d,%d)", reference.name, mapId, reference.x, reference.y); position.x = reference.x * Config.tileWidth + Config.tileWidth / 2; position.y = reference.y * Config.tileWidth + Config.tileWidth / 2; args.clear(); args.addAll(reference.arg1, reference.arg2, reference.arg3); } /** @param name the name to set */ public void setName(String name) { this.name = name; } /** Sets the position vector equal to the value of another vector * @param position a {@link Vector3} */ public void setPosition(Vector3 position) { if (position == null) { Game.error("position is null"); return; } this.position.x = position.x; this.position.y = position.y; } public void setRenderOffset(int renderOffset) { this.renderOffset = renderOffset; } /** @param tag the tag to set */ public void setTag(String tag) { this.tag = tag; } /** @param isVisible the isVisible to set */ public void setVisible(boolean isVisible) { this.isVisible = isVisible; } /** Updates all components */ @Override public void update() { for (int i = 0; i < components.size; i++) { components.get(i).update(this); } } }