API Docs for:
Show:

File: src/renderer.js

(function(root) {
    'use strict';

    /**
    * Renders the current game state using html5 canvas.
    * @class Renderer
    * @constructor
    * @param {Game} game - Game instance this obj is attached to.
    * @param {Number} width - Width of the map view in tiles.
    * @param {Number} height - Height of the map view in tiles.
    * @param {Number} tileSize - Width and height of tiles when drawn.
    * @param {String} [canvasClassName='renderer'] - Css class name for the canvas element.
    */
    var Renderer = function Renderer(game, width, height, tileSize, canvasClassName) {
        this.layers = [];
        this.game = game;
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d');
        this.canvas.className = canvasClassName || 'renderer';
        this.buffer = this.canvas.cloneNode();
        this.bufferCtx = this.buffer.getContext('2d');
        this.tileSize = tileSize || this.tileSize;
        this.resize(width, height);

    };

    Renderer.prototype = {
        constructor: Renderer,

        /**
        * Game instance this obj is attached to.
        * @property game
        * @type Game
        */
        game: null,

        /**
         * Tile data layers to draw
         * @type {Array}
         */
        layers: null,

        /**
        * Canvas element this renderer draws to.
        * @property canvas
        * @type HTMLCanvasElement
        */
        canvas: null,

        /**
        * Drawing context of this.canvas
        * @property ctx
        * @type CanvasRenderingContext2D
        */
        ctx: null,

        /**
        * Canvas element this renderer draws to as a buffer for this.canvas.
        * @property buffer
        * @type HTMLCanvasElement
        */
        buffer: null,

        /**
        * Drawing context of this.buffer
        * @property bufferCtx
        * @type CanvasRenderingContext2D
        */
        bufferCtx: null,

        /**
        * Background color of map view.
        * @property bgColor
        * @type String
        */
        bgColor: '#000',

        /**
        * Color overlayed when mouse is over a tile.
        * @property hoverColor
        * @type String
        */
        hoverColor: 'rgba(0,0,200, 0.5)',

        /**
        * Alpha value applied to non-visible tiles
        * @property nonVisibleTileAlpha
        * @type Number
        */
        nonVisibleTileAlpha: 0.36,

        /**
        * Size of each tile is drawn.
        * @property tileSize
        * @type Number
        */
        tileSize: 16,

        /**
        * Font used to render tile characters.
        * @property font
        * @type String
        */
        font: 'monospace',// "DejaVuSansMono",

        /**
        * Device pixel ratio for high dpi screens.
        * @property devicePixelRatio
        * @type Number
        */
        devicePixelRatio: 1,

        /**
        * Map view width in tiles.
        * @property width
        * @type Number
        */
        width: 20,

        /**
        * Map view height in tiles.
        * @property height
        * @type Number
        */
        height: 20,

        /**
        * The x distance in tiles from center to upper left corner of map view.
        * @property offsetX
        * @type Number
        */
        offsetX: null,

        /**
        * The y distance in tiles from center to upper left corner of map view.
        * @property offsetY
        * @type Number
        */
        offsetY: null,

        /**
        * The map tile x coord of the tile drawn in the upper left corner of the map view.
        * @property originX
        * @type Number
        */
        originX: null,

        /**
        * The map tile y coord of the tile drawn in the upper left corner of the map view.
        * @property originY
        * @type Number
        */
        originY: null,

        /**
        * The map tile x coord of the tile currently being hovered by the mouse.
        * @property hoveredTileX
        * @type Number|Null
        */
        hoveredTileX: null,

        /**
        * The map tile y coord of the tile currently being hovered by the mouse.
        * @property hoveredTileY
        * @type Number|Null
        */
        hoveredTileY: null,

        /**
         * Placeholder to add extra draw functionality.
         * Same params as this.draw
         * function(ctx, map, entityManager, player, fov, lighting)
         * @param drawExtra
         * @type {Function}
         */
        drawExtra: false,

        /**
        * Resizes canvas elements to match the tileSize and map view with/height. Also adjusts behavior to accomodate high pixel density screens.
        * @method resize
        */
        resize: function(width, height){
            if(width !== void 0){
                this.width = width;
            }
            if(height !== void 0){
                this.height = height;
            }

            width = this.width * this.tileSize;
            height = this.height * this.tileSize;

            var devicePixelRatio = window.devicePixelRatio || 1;

            if(devicePixelRatio !== 1){

                this.canvas.style.width = width + 'px';
                this.canvas.style.height = height + 'px';

                this.buffer.style.width = width + 'px';
                this.buffer.style.height = height + 'px';

                width = Math.round(width * devicePixelRatio);
                height = Math.round(height * devicePixelRatio);
            }

            this.devicePixelRatio = devicePixelRatio;
            this.canvas.width = width;
            this.canvas.height = height;

            this.buffer.width = width;
            this.buffer.height = height;
            this.bufferCtx.scale(devicePixelRatio, devicePixelRatio);
            this.offsetX = Math.floor(this.width * 0.5);
            this.offsetY = Math.floor(this.height * 0.5);

        },

        /**
        * Draws map and entity tiles. All parameters will fall back to this.game.<param> if not provided.
        * @method draw
        */
        draw: function(){
            this.fillBg();
            for (var x = this.width - 1; x >= 0; x--) {
                for (var y = this.height - 1; y >= 0; y--) {
                    // get the actual map tile coord from view coord using offset
                    var tileX = x + this.originX,
                        tileY = y + this.originY;
                    this.drawTile(tileX, tileY);
                }
            }

            this.drawBufferToCanvas();
        },

        drawTile: function(x, y, layers){
            layers = layers || this.layers;
            var tileData = {};
            for (var i = 0; i < layers.length; i++) {
                var layer = layers[i];

                tileData = layer.getModifiedTileData(x, y, tileData);

                if(layer.cancelTileDrawWhenNotFound && !tileData){
                    return false;
                }

                if(tileData && layer.draw){
                    if(layer.beforeDraw){
                        layer.beforeDraw(x, y, tileData, this.bufferCtx);
                    }
                    this.drawTileToCanvas(x, y, tileData, this.bufferCtx);
                    if(layer.afterDraw){
                        layer.afterDraw(x, y, tileData, this.bufferCtx);
                    }
                }
            }
        },

        /**
        * Draws a single tile to the map view.
        * @method drawTileToCanvas
        * @param {Number} x - Map tile coord on the x axis.
        * @param {Number} y - Map tile coord on the y axis.
        * @param {Object} tileData - Object containing tile draw settings.
        * @param {Object} [tileData.char] - The character to draw.
        * @param {Object} [tileData.color] - The color of the character displayed.
        * @param {Object} [tileData.bgColor] - The background color of the tile.
        * @param {Object} [tileData.borderColor] - The border color of the tile.
        * @param {Object} [tileData.borderWidth=1] - The border width of the tile.
        * @param {CanvasRenderingContext2D} [ctx=this.bufferCtx] - The canvas context to draw to.
        */
        drawTileToCanvas: function(x, y, tileData, ctx) {
            ctx = ctx || this.bufferCtx;

            var originalX = x,
                originalY = y;

            x -= this.originX;
            y -= this.originY;

            if(tileData.bgColor){
                ctx.fillStyle = tileData.bgColor;
                ctx.fillRect(
                    x * this.tileSize,
                    y * this.tileSize,
                    this.tileSize,
                    this.tileSize
                );
            }

            if(tileData.before !== void 0){
                this.drawTileToCanvas(originalX, originalY, tileData.before, ctx);
            }

            if(tileData.char && tileData.color){

                if(tileData.mask){
                    ctx.save();
                    ctx.beginPath();
                    ctx.rect(
                        x * this.tileSize,
                        y * this.tileSize,
                        this.tileSize,
                        this.tileSize
                    );
                    ctx.clip();
                    ctx.closePath();
                }

                var fontSize = tileData.fontSize || this.tileSize;
                var textX = x * (this.tileSize) + (this.tileSize * 0.5) + (tileData.offsetX || 0);
                var textY = y * (this.tileSize) + (this.tileSize * 0.5) + (tileData.offsetY || 0);

                ctx.fillStyle = tileData.color;
                ctx.textAlign = tileData.textAlign || 'center';
                ctx.textBaseline = tileData.textBaseline || 'middle';

                ctx.font = fontSize + 'px ' + (tileData.font || this.font);
                if(tileData.charStrokeColor){
                    ctx.strokeStyle = tileData.charStrokeColor;
                    ctx.lineWidth = tileData.charStrokeWidth || 1;
                    ctx.strokeText(
                        tileData.char,
                        textX,
                        textY
                    );
                    ctx.strokeText(
                        tileData.char,
                        textX,
                        textY+1
                    );
                }

                ctx.fillText(
                    tileData.char,
                    textX,
                    textY
                );

                if(tileData.mask){
                    ctx.restore();
                }

            }

            if(tileData.after !== void 0){
                this.drawTileToCanvas(originalX, originalY, tileData.after, ctx);
            }

            if(tileData.borderColor){
                var borderWidth = tileData.borderWidth || 1;
                var borderOffset = Math.floor(borderWidth * 0.5);
                var borderRectSize = this.tileSize - borderWidth;
                if(borderWidth % 2 !== 0){
                    borderOffset += 0.5;
                }
                ctx.lineWidth = borderWidth;
                ctx.strokeStyle = tileData.borderColor;

                var bx = x * this.tileSize + borderOffset;
                var by = y * this.tileSize + borderOffset;
                ctx.strokeRect(bx, by, borderRectSize, borderRectSize);
            }
        },

        /**
        * Converts mouse pixel coords to map tile coords. Mouse pixel coords must be relative to the current window.
        * @method mouseToTileCoords
        * @param {Number} x - Mouse pixel x coord.
        * @param {Number} y - Mouse pixel y coord.
        * @return {Object|False} {x: 0, y: 0}
        */
        mouseToTileCoords: function(x, y){
            var pos = this.canvas.getBoundingClientRect(),
                mx = x - pos.left,
                my = y - pos.top;
            return this.pixelToTileCoords(mx, my);
        },

        /**
        * Converts map view pixel coords to map tile coords. Map view pixel coords are relative to the top left of the canvas element.
        * @method pixelToTileCoords
        * @param {Number} x - Map view pixel x coord.
        * @param {Number} y - Map view pixel y coord.
        * @return {Object|False}  {x: 0, y: 0}
        */
        pixelToTileCoords: function(x, y){
            return {
                x: Math.floor(x / this.tileSize) + this.originX,
                y: Math.floor(y / this.tileSize) + this.originY
            };
        },

        /**
        * Sets the center map tile of the view.
        * @method setCenter
        * @param {Number} centerX - Center map tile x coord.
        * @param {Number} centerY - Center map tile y coord.
        */
        setCenter: function(centerX, centerY){
            // origin = map tile coords of the tile in the upper left of view
            this.originX = centerX - this.offsetX;
            this.originY = centerY - this.offsetY;
        },

        /**
         * Fills the canvas with a given color.
         * @method fillBg
         * @param {String} [color=this.bgColor]
         * @param {CanvasRenderingContext2D} [ctx=this.bufferCtx]
         */
        fillBg: function(color, ctx){
            ctx = ctx || this.bufferCtx;
            ctx.fillStyle = color || this.bgColor;
            ctx.fillRect(
                0,
                0,
                this.canvas.width,
                this.canvas.height
            );
        },

        /**
         * Copies pixel data from the buffer canvas to the game canvas.
         * @method drawBufferToCanvas
         */
        drawBufferToCanvas: function(){
            // draw from buffer canvas to canvas in DOM only once all buffer draws are complete
            this.ctx.drawImage(this.buffer, 0, 0, this.canvas.width, this.canvas.height);
        },
    };

    root.RL.Renderer = Renderer;

    /*
        The following describes an object litteral used by Renderer for the benefit of api doc generation.
     */

    /**
     * An object litteral containing data used by 'Renderer' to draw a map tile.
     * Only one `TileDrawData` object is used per tile when rendering.
     * The final `TileDrawData` object used to draw may have been created by merging multiple `TileDrawData` objects from multiple sources.
     * @class TileDrawData
     * @static
     */

    /**
     * The character to be drawn.
     * @property char
     * @type {String|false}
     */

    /**
     * Character color.
     * @property color
     * @type {css color|false}
     */

    /**
     * Background color.
     * @property bgColor
     * @type {css color|false}
     */

    /**
     * Border color.
     * If false no border will be drawn.
     * @property borderColor
     * @type {css color|false}
     */

    /**
     * Border width.
     * If `this.borderColor` is set `this.borderWidth` will default to 1 if not set.
     * @property borderWidth
     * @type {Number|false}
     */

    /**
     * Character stroke color.
     * @property charStrokeColor
     * @type {css color|false}
     */

    /**
     * Character stroke color.
     * @property charStrokeColor
     * @type {css color|false}
     */

    /**
     * Character stroke width.
     * If `this.charStrokeColor` is set `this.charStrokeWidth` will default to 1 if not set.
     * @property charStrokeWidth
     * @type {Number|false}
     */

    /**
     * Font to be used when drawing character.
     * If not set `Renderer.font` is used.
     * @property font
     * @type {String|false}
     */

    /**
     * Font size to be used when drawing character.
     * If not set `Renderer.fontSize` is used.
     * @property fontSize
     * @type {Number|false}
     */

    /**
     * Text alignment of character. Valid values: 'left', 'right', 'center', 'start', 'end'.
     * @property textAlign
     * @type {String|false}
     */

    /**
     * Text baseling of character. Valid values: 'alphabetic', 'top', 'hanging', 'middle', 'ideographic', 'bottom'.
     * @property textBaseline
     * @type {String|false}
     */

    /**
     * Pixel offset of character.
     * @property offsetX
     * @type {Number|false}
     */

    /**
     * Pixel offset of character.
     * @property offsetY
     * @type {Number|false}
     */

}(this));