game_actor_component_component_manager.js

import DrawableComponent from "./drawable_component.js";
import CollisionComponent from "./collision_component.js";
import PhysicsComponent from "./physics_component.js";

/**
 * @module ComponentManager
 * @fileoverview Contains ComponentManager class.
 */

/**
 * @class
 * Manager for components of an actor.
 */
class ComponentManager {
    /**
     * Create a new component manager.
     * @param {Actor} actor Actor to be a part of
     * @constructor
     */
    constructor(actor) {
        this._actor = actor;

        this._components = new Map();

        this._drawables = new Map();
        this._collisions = new Map();
        this._physics = new Map();
    }

    /**
     * Call the destructor for all attached components.
     */
    destructor() {
        this._components.forEach(comp => comp.destructor());
    }


    //
    // Management
    //

    /**
     * Add a component to the actor.
     * @typedef T
     * @param {string} id Identifier of component
     * @param {ActorComponent|T} comp Component to add
     * @returns {T} Component that was added
     */
    add(id, comp) {
        this._components.set(id, comp);

        if (comp instanceof DrawableComponent)
            this._drawables.set(id, comp);
        else if (comp instanceof CollisionComponent)
            this._collisions.set(id, comp);
        else if (comp instanceof PhysicsComponent)
            this._physics.set(id, comp);

        comp.actor = this._actor;
        if (this._actor.initialized)
            comp.init();

        return comp;
    }

    /**
     * Remove a component from the actor.
     * @param {string} id Identifier of component to remove
     */
    remove(id) {
        const comp = this._components.get(id);
        if (!comp)
            return;

        comp.destructor();

        this._components.delete(id);

        if (comp instanceof DrawableComponent)
            this._drawables.delete(id);
        else if (comp instanceof CollisionComponent)
            this._collisions.delete(id);
        else if (comp instanceof PhysicsComponent)
            this._physics.delete(id);
    }

    /**
     * Get a specific component.
     * @param {string} id Identifier of component
     * @returns {ActorComponent} Specified component
     */
    get(id) {
        if (!this._components.has(id))
            return null;

        return this._components.get(id);
    }

    /**
     * Check if the actor has a component with a specified identifier.
     * @param {string} id Identifier to check for
     * @returns {boolean} Whether actor has component with specified identifier
     */
    has(id) {
        return this._components.has(id);
    }

    /**
     * Get the total number of components.
     * @returns {number} Number of components
     */
    get count() {
        return this._components.size;
    }

    /**
     * Get an iterable iterator of all components.
     * @returns {IterableIterator<ActorComponent>} All components
     */
    getAll() {
        return this._components.values();
    }

    /**
     * Run a function for each component.
     * @param {function} func Function to run for each component
     */
    forAll(func = (comp, id) => {}) {
        this._components.forEach(func);
    }


    //
    // Specific Types
    //

    /**
     * Get an iterable iterator of all DrawableComponents.
     * @returns {IterableIterator<DrawableComponent>} All drawable components
     */
    getDrawables() {
        return this._drawables.values();
    }

    /**
     * Get the amount of DrawableComponents.
     * @returns {number} Number of drawable components
     */
    get drawableCount() {
        return this._drawables.size;
    }

    /**
     * Get an iterable iterator of all CollisionComponents.
     * @returns {IterableIterator<CollisionComponent>} All collision components
     */
    getCollisions() {
        return this._collisions.values();
    }

    /**
     * Get the amount of CollisionComponents
     * @returns {number} Number of collision components
     */
    get collisionCount() {
        return this._collisions.size;
    }

    /**
     * Get an iterable iterator of all PhysicsComponents.
     * @returns {IterableIterator<PhysicsComponent>} All physics components
     */
    getPhysics() {
        return this._physics.values();
    }

    /**
     * Get the amount of PhysicsComponents
     * @returns {number} Number of physics components
     */
    get physicsCount() {
        return this._physics.size;
    }


    //
    // Update & Draw
    //

    /**
     * Update all components.
     * @param {number} elapsed Time since last update frame in seconds
     */
    updateAll(elapsed) {
        this._components.forEach(comp => comp.update(elapsed));
    }

    /**
     * Draw all drawable components.
     * @param {CanvasRenderingContext2D} ctx Canvas context for drawing
     */
    drawAll(ctx) {
        this._drawables.forEach(comp => comp.draw(ctx));
    }

    /**
     * Draw debug info for all drawable components.
     * @param {CanvasRenderingContext2D} ctx Canvas context for drawing
     */
    drawDebugAll(ctx) {
        this._components.forEach(comp => comp.drawDebug(ctx));
    }
}

export default ComponentManager;