engine.js

import App from "./app.js";
import SpriteRegistry from "./registry/sprite_registry.js";
import ControllerRegistry from "./registry/controller_registry.js";
import Vec2 from "./game/vector/vec2.js";
import SoundRegistry from "./registry/sound/sound_registry.js";
import SpaceManager from "./game/space/space_manager.js";
import TaskManager from "./util/task/task_manager.js";
import InputManager from "./input/input_manager.js";
import CookieManager from "./util/data/cookie_manager.js";
import PrefabRegistry from "./registry/prefab_registry.js";

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

/**
 * @class
 * Global root object for the entire engine.
 */
class Engine {
    /**
     * Create a new Engine.
     * @constructor
     */
    constructor() {
        this._loadCounter = 0;

        this._canvasID = "";
        this._onLoadFunction = () => {};

        /** App for holding game data */
        this._app = null;

        /** Registry holder */
        this._reg = {
            sprites: new SpriteRegistry(),
            sound: new SoundRegistry(),
            controller: new ControllerRegistry(),
            prefab: new PrefabRegistry()
        };

        /** Global cookie manager */
        this._cookies = new CookieManager();

        /** Global task manager */
        this._tasks = new TaskManager();

        /** Global space manager */
        this._spaces = new SpaceManager();

        /** Global input manager */
        this._input = new InputManager();

        this._offscreenCanvas = new OffscreenCanvas(1024, 1024);
        this._offscreenContext = this._offscreenCanvas.getContext("2d");
        this._offscreenContext.willReadFrequently = true;

        /** Width of the canvas being used. */
        this.width = 0;

        /** Height of the canvas being used. */
        this.height = 0;

        /** The current mouse position relative to the canvas. */
        this.mousePos = new Vec2(0, 0);

        /** Fonts to be used throughout the engine in drawing on the canvas. */
        this.fonts = {
            debug: "roboto"
        };

        /** Colors to be used throughout the engine. */
        this.colors = {
            debug: "rgb(255,92,75)",
            debug2: "rgb(243,198,71)",
            debug_collision: "rgb(130,234,167)",
            debug_body: "rgb(80,147,241)",
            debug_body_connection: "rgb(234,97,149)",
            debug_physics: "rgb(143,229,56)",
            debug_path: "rgb(255,118,171)",
            debug_path2: "rgba(255,118,171,0.69)",
            debug_path_complete: "rgba(162,135,149,0.54)"
        };

        /** Global flags. */
        this.flags = {
            chunks: {
                cache: true
            },

            timeScale: 1,
            pauseOnBlur: true,

            fillScreen: true
        };

        /** Global debugging flags. Can be enabled to display debugging information. */
        this.debug = {
            actors: false,
                chunks: false,
                particles: false,
                physics: false,

                enableAll: function() {
                this.actors = true;
                this.chunks = true;
                this.particles = true;
                this.physics = true;
            },
            disableAll: function() {
                this.actors = false;
                this.chunks = false;
                this.particles = false;
                this.physics = false;
            },
            toggleAll: function() {
                let which = true;
                if (this.actors)
                    which = false;
                if (this.chunks)
                    which = false;
                if (this.particles)
                    which = false;
                if (this.physics)
                    which = false;

                if (which)
                    this.enableAll();
                else
                    this.disableAll();
            }
        };
    }


    //
    // Initialization and Loading
    //

    /**
     * Initialize the engine.
     * @param {string} canvasID The ID of the canvas to draw on
     * @param {function} onload Function to call once all assets are loaded
     * @param {function} preload Function for loading assets
     */
    init(canvasID, onload = () => {}, preload = () => {}) {
        this._onLoadFunction = onload;
        this._canvasID = canvasID;

        preload();
        this._checkReady();
    }

    /**
     * Increment the amount of assets loading.
     */
    loadInc() {
        this._loadCounter++;
    }

    /**
     * Decrement the amount of assets loading, and start the
     * application if it reaches 0.
     */
    loadDec() {
        this._loadCounter--;
        this._checkReady();
    }

    /**
     * Try to start the engine if all assets are loaded.
     * @private
     */
    _checkReady() {
        if (this._loadCounter === 0) {
            this._app = new App(this._canvasID, true);

            this._app.start();

            this._onLoadFunction(this._app);
        }
    }


    //
    // Getters
    //

    /**
     * Get the root application.
     * @returns {App} Root application
     */
    get app() {
        return this._app;
    }

    /**
     * Get the registries for holding assets and more.
     * @returns {{controller: ControllerRegistry, sound: SoundRegistry, sprites: SpriteRegistry, prefab: PrefabRegistry}} Global registries
     */
    get reg() {
        return this._reg;
    }

    /**
     * Get the global cookie manager.
     * @returns {CookieManager} Global cookie manager
     */
    get cookies() {
        return this._cookies;
    }

    /**
     * Get the global task manager.
     * @returns {TaskManager} Global task manager
     */
    get tasks() {
        return this._tasks;
    }

    /**
     * Get the global space manager.
     * @returns {SpaceManager} Global space manager
     */
    get spaces() {
        return this._spaces;
    }

    /**
     * Get the global input handler.
     * @returns {InputManager} Global input handler
     */
    get input() {
        return this._input;
    }


    //
    // Miscellaneous
    //

    /**
     * Generate an image using a custom function on an offscreen canvas.
     * @param {function} func Function to run (passes in context)
     * @param {function} onDone Function to run when complete (passes in image)
     * @param {number} width Width of image (starts at x = 0)
     * @param {number} height Height of image (starts at y = 0)
     */
    createImage(func = ctx => {}, onDone = image => {}, width, height) {
        this._offscreenContext.clearRect(0, 0, this._offscreenCanvas.width, this._offscreenCanvas.height);
        this._offscreenCanvas.width = width;
        this._offscreenCanvas.height = height;

        func(this._offscreenContext);

        this._offscreenCanvas.convertToBlob()
            .then(blob => createImageBitmap(blob))
            .then(image => onDone(image));
    }

    /**
     * Generate image data using a custom function on an offscreen canvas.
     * @param {function} func Function to run (passes in context)
     * @param {number} width Width of data (starts at x = 0)
     * @param {number} height Height of data (starts at y = 0)
     * @returns {ImageData} Generated image data
     */
    createImageData(func = ctx => {}, width, height) {
        this._offscreenContext.clearRect(0, 0, this._offscreenCanvas.width, this._offscreenCanvas.height);
        this._offscreenCanvas.width = width;
        this._offscreenCanvas.height = height;

        func(this._offscreenContext);

        return this._offscreenContext.getImageData(0, 0, width, height);
    }
}

window.$$ = new Engine();