Source: game_objects/tiled_game_object.js

/* File: tiled_game_objects.js 
 *
 * Infinitely tiled, assume X/Y alignments
 */

"use strict";  // Operate in Strict mode such that variables must be declared before used!

import GameObject from "./game_object.js";

class TiledGameObject extends GameObject {
    /**
     * @classdesc Support for repeating renderable tiles, infinitely tileable.
     * Assumes X/Y alignments.
     * <p>Found in Chapter 11, page 674 of the textbook  </p>
     * Example:
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter11/11.1.tiled_objects/index.html 11.1 Tiled Objects}
     * @constructor
     * @extends GameObject
     * @param {Renderable} renderableObj - the Renderable associated with this TiledGameObject
     * @returns {TiledGameObject} a new TileGameObject instance
     */
    constructor(renderableObj) {
        super(renderableObj);

        this.mShouldTile = true; // can switch this off if desired
    }

    /**
     * Set the tiling property to true or false
     * @method
     * @param {boolean} t - the tiling state
     */
    setIsTiled(t) {
        this.mShouldTile = t;
    }
    /**
     * Returns whether or not tiling is active for this TiledGameObject
     * @method
     * @returns {boolean} mShouldTile - true if tiling is active
     */
    shouldTile() {
        return this.mShouldTile;
    }

    /**
     * Draws a grid of tiles filling every availble space within the Camera
     * @method
     * @param {Camera} aCamera - the Camera to draw to 
     */
    _drawTile(aCamera) {
        // Step A: Compute the positions and dimensions of tiling object.
        let xf = this.getXform();
        let w = xf.getWidth();
        let h = xf.getHeight();
        let pos = xf.getPosition();
        let left = pos[0] - (w / 2);
        let right = left + w;
        let top = pos[1] + (h / 2);
        let bottom = top - h;

        // Step B: Get the world positions and dimensions of the drawing camera.
        let wcPos = aCamera.getWCCenter();
        let wcLeft = wcPos[0] - (aCamera.getWCWidth() / 2);
        let wcRight = wcLeft + aCamera.getWCWidth();
        let wcBottom = wcPos[1] - (aCamera.getWCHeight() / 2);
        let wcTop = wcBottom + aCamera.getWCHeight();

        // Step C: Determine the offset to the camera window's lower left corner.
        let dx = 0, dy = 0; // offset to the lower left corner
        // left/right boundary?
        if (right < wcLeft) { // left of WC left
            dx = Math.ceil((wcLeft - right) / w) * w;
        } else {
            if (left > wcLeft) { // not touching the left side
                dx = -Math.ceil((left - wcLeft) / w) * w;
            }
        }
        // top/bottom boundary
        if (top < wcBottom) { // Lower than the WC bottom
            dy = Math.ceil((wcBottom - top) / h) * h;
        } else {
            if (bottom > wcBottom) {  // not touching the bottom
                dy = -Math.ceil((bottom - wcBottom) / h) * h;
            }
        }

        // Step D: Save the original position of the tiling object.
        let sX = pos[0];
        let sY = pos[1];

        // Step E: Offset tiling object and modify the related position variables.
        xf.incXPosBy(dx);
        xf.incYPosBy(dy);
        right = pos[0] + (w / 2);
        top = pos[1] + (h / 2);

        // Step F: Determine the number of times to tile in the x and y directions.
        let nx = 1, ny = 1; // number of times to draw in the x and y directions
        nx = Math.ceil((wcRight - right) / w);
        ny = Math.ceil((wcTop - top) / h);

        // Step G: Loop through each location to draw a tile
        let cx = nx;
        let xPos = pos[0];
        while (ny >= 0) {
            cx = nx;
            pos[0] = xPos;
            while (cx >= 0) {
                this.mRenderComponent.draw(aCamera);
                xf.incXPosBy(w);
                --cx;
            }
            xf.incYPosBy(h);
            --ny;
        }

        // Step H: Reset the tiling object to its original position.
        pos[0] = sX;
        pos[1] = sY;
    }

    /**
     * Draws this TiledGameObject if visible and executes tiling if it should tile
     * @method
     * @param {Camera} aCamera - the Camera to draw to
     */
    draw(aCamera) {
        if (this.isVisible() && (this.mDrawRenderable)) {
            if (this.shouldTile()) {
                // find out where we should be drawing   
                this._drawTile(aCamera);
            } else {
                this.mRenderComponent.draw(aCamera);
            }
        }
    }
}

export default TiledGameObject;