Source: Entity.js

import Component from './Component';

/**
 * An entity is a object that holds many components. Those components define the behaviour of an entity.
 */
class Entity {

    /**
     *
     * @param {string} name
     */
    constructor(name = 'Entity') {
        /** @var {string} */
        this.name = name;

        /** @var { Component[]} */
        this.components = [];

        /** @var {boolean} */
        this.alive = true;

        /** @var {boolean} */
        this.remove = false;

        /** @var {number | null} */
        this.index = null;

        this.listeners = [];
    }

    /**
     * Add a Component to the entity
     * @param {Component} component
     * @returns {Entity}
     */
    addComponent(component) {
        let slot = this._getFreeSlot();
        if (slot != null) {
            this.components[slot] = component;
        } else {
            this.components.push(component);
        }

        return this;
    }

    /**
     * Add eventlistener to entity
     * @param {string} event
     * @param {function} callback
     * @returns {Entity}
     */
    addListener(event, callback) {
        this.listeners.push({ event, callback });

        return this;
    }

    /**
     * Remove event listener
     * @param {string} event
     * @returns {Entity}
     */
    removeListener(event) {
        const listeners = this.listeners;
        const newListeners = [];
        const len = listeners.length;

        for (let i = 0; i < len; i++) {
            const listener = listeners[i];
            if ((listener.event !== event)) {
                newListeners.push(listeners);
            }
        }

        this.listeners = newListeners;
        return this;
    }

    /**
     * Emit event with data
     * @param {string} event
     * @param {object} data
     * @returns {Entity}
     */
    emit(event, data) {
        const listeners = this.listeners;
        const len = listeners.length;

        for (let i = 0; i < len; i++) {
            const listener = listeners[i];
            if ((listener.event === event)) {
                listener.callback(data);
            }
        }
        return this;
    }

    /**
     * Get all mathing components
     * @param {Function} component component constructor
     * @return {Component[]}
     */
    getComponents(component) {
        let components = [];
        for (let i = 0, len = this.components.length; i < len; i++) {
            let c = this.components[i];
            if (c instanceof component) {
                components.push(c);
            }
        }
        return components;
    }

    /**
     * Get a Component from the entity
     * @param {Function} component component constructor
     * @return {Component}
     * @throws Error
     */
    getComponent(component) {
        for (let i = 0, len = this.components.length; i < len; i++) {
            let c = this.components[i];
            if (c instanceof component) {
                return c
            }
        }
        throw new Error('Component not found');
    }

    /**
     * Get a Component from the entity by its tag name
     * @param {Function} component
     * @returns {boolean}
     */
    hasComponent(component) {
        for (let i = 0, len = this.components.length; i < len; i++) {
            let c = this.components[i];
            if (c instanceof component) {
                return true;
            }
        }
        return false;
    }

    /**
     * remove a Component from the entity
     * @param {Function} component
     * @returns {Entity}
     */
    removeComponent(component) {
        const components = [];
        const currentComponents = this.components;
        const len = currentComponents.length;

        for (let i = 0; i < len; i++) {
            const compo = currentComponents[i];
            if (!(compo instanceof component)) {
                components.push(compo);
            }
        }

        this.components = components;
        return this;
    }

    /**
     * Get all components from the entity
     * @returns { Component[]}
     */
    getAllComponents() {
        return this.components;
    }

    /**
     * Reuses old Component slots in the array
     * @return {number | null}
     * @private
     */
    _getFreeSlot() {
        for (let c = 0, len = this.components.length; c < len; c++) {
            let component = this.components[c];
            if (component == undefined || component == null) {
                return c;
            }
        }
        return null;
    }

    /**
     * Kills the entity
     */
    destroy() {
        this.alive = false;
        this.remove = true;
    }

    /**
     * Set index value of entity
     * @param {number} index
     */
    setIndex(index) {
        this.index = index;
    }

    /**
     * Get index of entity
     * @returns {number | null}
     */
    getIndex() {
        return this.index;
    }

    /**
     * Check if entity is alive
     * @returns {boolean}
     */
    isAlive() {
        return this.alive;
    }

    /**
     * Check if entity should be removed in next cycle
     * @returns {boolean}
     */
    isRemove() {
        return this.remove;
    }
}

export default Entity;