'use strict'; import * as util from '../util'; import EntityPosition from '../model/EntityPosition'; import MapData from '../model/MapData'; const tileSize = 32; const body = document.getElementsByTagName('body')[0]; function loadImage(url: string): Promise { return new Promise(function(resolve, reject) { var img = new Image(); img.addEventListener('load', () => { resolve(img); }); img.addEventListener('error', () => { reject(Error('Failed to load ' + url)); }); img.src = url; }); } function loadImages(imgs: {[key: string]: string}): Promise<{[key: string]: HTMLImageElement}> { return util.mapPromises(_.mapValues(imgs, loadImage)); } function loadTiles(tiles: {[key: string]: {file: string}}): Promise<{[key: string]: HTMLImageElement}> { return loadImages(_.mapValues(tiles, (t) => `resources/sprite/tile/${t.file}.png`)); } function loadEntities(entities: EntityPosition[]): Promise<{[key: string]: HTMLImageElement}> { var p: {[key: string]: Promise} = {}; entities.forEach(e => { p[e.entity.name] = loadImage(`resources/sprite/entity/${e.entity.name}.png`); }); return util.mapPromises(p); } export default class MapView { redrawPending: boolean = false; canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D; tiles: {[key: string]: HTMLImageElement}; entitySprites: {[key: string]: HTMLImageElement}; constructor(private map: MapData, private entities: {[key: string]: EntityPosition}) { this.canvas = document.createElement('canvas'); this.canvas.style.position = 'absolute'; body.appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); window.addEventListener('resize', () => this.setSize()); this.setSize(); var tilesReady = loadTiles(map.tiles).then((tiles) => { this.tiles = tiles; }); var entitiesReady = loadEntities(this.getEntities()).then((entities) => { this.entitySprites = entities; }); Promise.all([tilesReady, entitiesReady]).then(() => { this.redraw(); }); } getEntities(): EntityPosition[] { return _.valuesIn(this.entities); } setSize() { var e = document.documentElement; this.canvas.width = window.innerWidth || e.clientWidth || body.clientWidth; this.canvas.height = window.innerHeight || e.clientHeight || body.clientHeight; this.redraw() } drawTile(x: number, y: number, tile: HTMLImageElement) { if (!tile) return; this.ctx.drawImage(tile, x, y); } drawEntity(e: EntityPosition) { var sprite = this.entitySprites[e.entity.name]; if (!sprite) return; this.ctx.drawImage( sprite, e.direction*tileSize, 0, tileSize, tileSize, e.position.x*tileSize, e.position.y*tileSize, tileSize, tileSize ); } draw() { this.redrawPending = false; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); if (!this.tiles || !this.entitySprites) return; this.map.layers.forEach((layer) => { let y = 0; layer.forEach((row) => { let x = 0; for (let tile in row) { this.drawTile(x, y, this.tiles[row[tile]]); x += tileSize; } y += tileSize; }); }); this.getEntities().forEach(e => { this.drawEntity(e); }); } redraw() { if (this.redrawPending) return; this.redrawPending = true; window.requestAnimationFrame(() => this.draw()); } }