const Node = require('./core/Node'); const Sprite = require('./core/sprites/Sprite'); const Texture = require('./core/textures/Texture'); const { textureFromData } = require('./utils'); const { filmstrip } = require('./utils'); const TILESETS = {}; const POOL = []; /** * Tilemap node */ class BackgroundMap extends Node { /** * @constructor * @param {Number} tilesize Size of a single tile(in pixel) * @param {Array} data Map ata * @param {Texture} tileset Tileset texture */ constructor(tilesize, data, tileset) { super(); if (!Number.isFinite(tilesize) || tilesize <= 0) { console.log('Invalid tilesize!'); return; } if (!Array.isArray(data) || (data.length === 0) || !Array.isArray(data[0])) { console.log('Invalid data format!'); return; } if (!tileset || !(tileset instanceof Texture)) { console.log('Invalid tileset!'); return; } this.tilesize = tilesize; this.data = data; this.tileset = tileset; this.tilesetTextures = null; this.tileSprites = null; this._width = data[0].length; this._height = data.length; this.parseTileset(); this.drawTiles(); } /** * Width of this map (in tile) * @readonly */ get width() { return this._width; } /** * Height of this map (in tile) * @readonly */ get height() { return this._height; } /** * Get the tile with its row and column * @param {Number} r Row * @param {Number} q Column * @return {Number} Tile index */ getTile(r, q) { return (q >= 0 && q < this._width && r >= 0 && r < this._height) ? this.data[r][q] : 0; } /** * Get the tile at a specific position * @param {Number} x X position * @param {Number} y Y position * @return {Number} Tile index */ getTileAt(x, y) { const q = Math.floor(x / this.tilesize); const r = Math.floor(y / this.tilesize); return (q >= 0 && q < this._width && r >= 0 && r < this._height) ? this.data[r][q] : 0; } /** * Set the tile at (row, column) * @param {Number} r Row * @param {Number} q Column * @param {Number} tile Tile index to set */ setTile(r, q, tile) { if (q >= 0 && q < this._width && r >= 0 && r < this._height) { this.data[r][q] = tile; if (tile > 0) { this.tileSprites[r][q].visible = true; this.tileSprites[r][q].texture = this.tilesetTextures[tile - 1]; } else { this.tileSprites[r][q].visible = false; } } } /** * Set the tile at a specific position * @param {Number} x X position * @param {Number} y Y position * @param {Number} tile Tile index */ setTileAt(x, y, tile) { const q = Math.floor(x / this.tilesize); const r = Math.floor(y / this.tilesize); if (q >= 0 && q < this._width && r >= 0 && r < this._height) { this.data[r][q] = tile; if (tile > 0) { this.tileSprites[r][q].visible = true; this.tileSprites[r][q].texture = this.tilesetTextures[tile - 1]; } else { this.tileSprites[r][q].visible = false; } } } /** * Parse the tileset of this map * @private */ parseTileset() { let tileList; let uid = this.tileset.baseTexture.uid; if (TILESETS.hasOwnProperty(uid) && Array.isArray(TILESETS[uid])) { tileList = TILESETS[uid]; } else { tileList = filmstrip(this.tileset, this.tilesize, this.tilesize); TILESETS[uid] = tileList; } this.tilesetTextures = tileList; } /** * Draw tiles of this map * @private */ drawTiles() { // Draw nothing if tileset is invalid if (!this.tileset || !this.tilesetTextures || this.tilesetTextures.length === 0) { return; } // Create sprites to draw the map this.createTileSprites(); // Update texture of each tile let q, r, tile, idx; for (r = 0; r < this._height; r++) { for (q = 0; q < this._width; q++) { idx = this.data[r][q] - 1; tile = this.tileSprites[r][q]; if (idx < 0) { tile.visible = false; } else { tile.visible = true; tile.texture = this.tilesetTextures[idx]; } } } } /** * Create sprites for drawing * @private */ createTileSprites() { this.tileSprites = new Array(this._height); // Insert tiles to fit map size let q, r, tile, row; for (r = 0; r < this._height; r++) { row = new Array(this._width); for (q = 0; q < this._width; q++) { tile = POOL.pop(); if (!tile) { tile = new Sprite(); } tile.position.set(q * this.tilesize, r * this.tilesize); this.addChild(tile); row[q] = tile; } this.tileSprites[r] = row; } } } /** * BackgroundMap factory * @param {Number} tilesize Size of a single tile * @param {Array} data Map data * @param {Texture} tileset Tileset texture * @return {BackgroundMap} BackgroundMap instance */ module.exports = function(tilesize = 8, data = [[]], tileset = null) { return new BackgroundMap(tilesize, data, textureFromData(tileset)); };