diff --git a/js/main.js b/js/main.js index 9df28f4..e7db469 100644 --- a/js/main.js +++ b/js/main.js @@ -229,11 +229,6 @@ class Player extends PrimaryView { this._redraw(); }); - // Populate inventory - this._inventory_tiles = {}; - let floor_tile = this.render_inventory_tile('floor'); - this.inventory_el.style.backgroundImage = `url(${floor_tile})`; - this.renderer = new CanvasRenderer(this.conductor.tileset); this.level_el.append(this.renderer.canvas); this.renderer.canvas.addEventListener('auxclick', ev => { @@ -244,6 +239,11 @@ class Player extends PrimaryView { this.level.move_to(this.level.player, this.level.cells[y][x], 1); }); + // Populate inventory + this._inventory_tiles = {}; + let floor_tile = this.render_inventory_tile('floor'); + this.inventory_el.style.backgroundImage = `url(${floor_tile})`; + let last_key; this.pending_player_move = null; this.next_player_move = null; @@ -526,10 +526,8 @@ class Player extends PrimaryView { render_inventory_tile(name) { if (! this._inventory_tiles[name]) { - // TODO put this on the renderer // TODO reuse the canvas for data urls - let canvas = mk('canvas', {width: this.conductor.tileset.size_x, height: this.conductor.tileset.size_y}); - this.conductor.tileset.draw({type: TILE_TYPES[name]}, null, canvas.getContext('2d'), 0, 0); + let canvas = this.renderer.create_tile_type_canvas(name); this._inventory_tiles[name] = canvas.toDataURL(); } return this._inventory_tiles[name]; @@ -1026,13 +1024,9 @@ class Editor extends PrimaryView { let section_el = mk('section'); palette_el.append(mk('h2', sectiondef.title), section_el); for (let name of sectiondef.tiles) { - let entry = mk('canvas.palette-entry', { - width: this.conductor.tileset.size_x, - height: this.conductor.tileset.size_y, - 'data-tile-name': name, - }); - let ctx = entry.getContext('2d'); - this.conductor.tileset.draw_type(name, null, null, ctx, 0, 0); + let entry = this.renderer.create_tile_type_canvas(name); + entry.setAttribute('data-tile-name', name); + entry.classList = 'palette-entry'; this.palette[name] = entry; section_el.append(entry); } diff --git a/js/renderer-canvas.js b/js/renderer-canvas.js index ae12b11..58ef87f 100644 --- a/js/renderer-canvas.js +++ b/js/renderer-canvas.js @@ -22,6 +22,7 @@ export class CanvasRenderer { this.canvas = mk('canvas', {width: tileset.size_x * this.viewport_size_x, height: tileset.size_y * this.viewport_size_y}); this.canvas.style.setProperty('--viewport-width', this.viewport_size_x); this.canvas.style.setProperty('--viewport-height', this.viewport_size_y); + this.ctx = this.canvas.getContext('2d'); this.viewport_x = 0; this.viewport_y = 0; } @@ -40,6 +41,16 @@ export class CanvasRenderer { return [x, y]; } + // Draw to a canvas using tile coordinates + blit(ctx, sx, sy, dx, dy, w = 1, h = w) { + let tw = this.tileset.size_x; + let th = this.tileset.size_y; + ctx.drawImage( + this.tileset.image, + sx * tw, sy * th, w * tw, h * th, + dx * tw, dy * th, w * tw, h * th); + } + draw(tic_offset = 0) { if (! this.level) { console.warn("CanvasRenderer.draw: No level to render"); @@ -48,9 +59,9 @@ export class CanvasRenderer { // TODO StoredLevel may not have a tic_counter let tic = (this.level.tic_counter ?? 0) + tic_offset; - - let ctx = this.canvas.getContext('2d'); - ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + let tw = this.tileset.size_x; + let th = this.tileset.size_y; + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // TODO only recompute if the player moved? // TODO what about levels smaller than the viewport...? shrink the canvas in set_level? @@ -68,8 +79,8 @@ export class CanvasRenderer { let x0 = Math.max(0, Math.min(this.level.size_x - this.viewport_size_x, px - xmargin)); let y0 = Math.max(0, Math.min(this.level.size_y - this.viewport_size_y, py - ymargin)); // Round to the pixel grid - x0 = Math.floor(x0 * this.tileset.size_x + 0.5) / this.tileset.size_x; - y0 = Math.floor(y0 * this.tileset.size_y + 0.5) / this.tileset.size_y; + x0 = Math.floor(x0 * tw + 0.5) / tw; + y0 = Math.floor(y0 * th + 0.5) / th; this.viewport_x = x0; this.viewport_y = y0; // The viewport might not be aligned to the grid, so split off any fractional part. @@ -89,10 +100,11 @@ export class CanvasRenderer { let y1 = Math.min(this.level.size_y - 1, Math.ceil(y0 + this.viewport_size_y)); // Draw one layer at a time, so animated objects aren't overdrawn by // neighboring terrain + let x, y; // XXX layer count hardcoded here for (let layer = 0; layer < 4; layer++) { - for (let x = xf0; x <= x1; x++) { - for (let y = yf0; y <= y1; y++) { + for (x = xf0; x <= x1; x++) { + for (y = yf0; y <= y1; y++) { for (let tile of this.level.cells[y][x]) { if (tile.type.draw_layer !== layer) continue; @@ -104,19 +116,29 @@ export class CanvasRenderer { // Handle smooth scrolling let [vx, vy] = tile.visual_position(tic_offset); // Round this to the pixel grid too! - vx = Math.floor(vx * this.tileset.size_x + 0.5) / this.tileset.size_x; - vy = Math.floor(vy * this.tileset.size_y + 0.5) / this.tileset.size_y; - this.tileset.draw(tile, tic, ctx, vx - x0, vy - y0); + vx = Math.floor(vx * tw + 0.5) / tw; + vy = Math.floor(vy * th + 0.5) / th; + this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) => + this.blit(this.ctx, sx, sy, vx - x0 + dx, vy - y0 + dy, w, h)); } else { // Non-actors can't move - this.tileset.draw(tile, tic, ctx, x - x0, y - y0); + this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) => + this.blit(this.ctx, sx, sy, x - x0 + dx, y - y0 + dy, w, h)); } } } } } } + + create_tile_type_canvas(name) { + let canvas = mk('canvas', {width: this.tileset.size_x, height: this.tileset.size_y}); + let ctx = canvas.getContext('2d'); + this.tileset.draw_type(name, null, 0, (sx, sy, dx = 0, dy = 0, w = 1, h = w) => + this.blit(ctx, sx, sy, dx, dy, w, h)); + return canvas; + } } export default CanvasRenderer; diff --git a/js/tileset.js b/js/tileset.js index 2274821..2f4ddd6 100644 --- a/js/tileset.js +++ b/js/tileset.js @@ -491,23 +491,13 @@ export class Tileset { this.size_y = size_y; } - // Helper to draw to a canvas using tile coordinates - blit(ctx, sx, sy, dx, dy, scale_x = 1, scale_y = scale_x) { - let w = this.size_x * scale_x; - let h = this.size_y * scale_y; - ctx.drawImage( - this.image, - sx * this.size_x, sy * this.size_y, w, h, - dx * this.size_x, dy * this.size_y, w, h); - } - - draw(tile, tic, ctx, x, y) { - this.draw_type(tile.type.name, tile, tic, ctx, x, y); + draw(tile, tic, blit) { + this.draw_type(tile.type.name, tile, tic, blit); } // Draws a tile type, given by name. Passing in a tile is optional, but // without it you'll get defaults. - draw_type(name, tile, tic, ctx, x, y) { + draw_type(name, tile, tic, blit) { let drawspec = this.layout[name]; if (! drawspec) { console.error(`Don't know how to draw tile type ${name}!`); @@ -519,9 +509,9 @@ export class Tileset { // southeast thin walls. Draw the base (a type name), then draw // the overlay (either a type name or a regular draw spec). // TODO chance of infinite recursion here - this.draw_type(drawspec.base, tile, tic, ctx, x, y); + this.draw_type(drawspec.base, tile, tic, blit); if (typeof drawspec.overlay === 'string') { - this.draw_type(drawspec.overlay, tile, tic, ctx, x, y); + this.draw_type(drawspec.overlay, tile, tic, blit); return; } else { @@ -539,7 +529,7 @@ export class Tileset { if (tile && tile.wire_directions !== undefined && tile.wire_directions !== 0) { // TODO all four is a different thing entirely // Draw the appropriate wire underlay - this.draw_type('#unpowered', tile, tic, ctx, x, y); + this.draw_type('#unpowered', tile, tic, blit); // Draw a masked part of the base tile let wiredir = tile.wire_directions; @@ -548,16 +538,16 @@ export class Tileset { let wire1 = 0.5 + wire_radius; let [bx, by] = drawspec.base; if ((wiredir & DIRECTIONS['north'].bit) === 0) { - this.blit(ctx, bx, by, x, y, 1, wire0); + blit(bx, by, 0, 0, 1, wire0); } if ((wiredir & DIRECTIONS['south'].bit) === 0) { - this.blit(ctx, bx, by + wire1, x, y + wire1, 1, wire0); + blit(bx, by + wire1, 0, wire1, 1, wire0); } if ((wiredir & DIRECTIONS['west'].bit) === 0) { - this.blit(ctx, bx, by, x, y, wire0, 1); + blit(bx, by, 0, 0, wire0, 1); } if ((wiredir & DIRECTIONS['east'].bit) === 0) { - this.blit(ctx, bx + wire1, by, x + wire1, y, wire0, 1); + blit(bx + wire1, by, wire1, 0, wire0, 1); } // Then draw the wired tile as normal @@ -571,7 +561,7 @@ export class Tileset { coords = drawspec.base; } else { - this.blit(ctx, drawspec.base[0], drawspec.base[1], x, y); + blit(drawspec.base[0], drawspec.base[1]); coords = drawspec.wired; } } @@ -621,17 +611,11 @@ export class Tileset { // Continue on with masking coords = drawspec.tile; let [x0, y0, w, h] = drawspec.mask; - this.blit( - ctx, - coords[0] + x0, - coords[1] + y0, - x + x0, - y + y0, - w, h); + blit(coords[0] + x0, coords[1] + y0, x0, y0, w, h); } else { if (!coords) console.error(name, tile); - this.blit(ctx, coords[0], coords[1], x, y); + blit(coords[0], coords[1]); } // Special behavior for special objects @@ -661,9 +645,7 @@ export class Tileset { sy = (letter_spec.y0 + Math.floor(n / w)) * scale; } let offset = (1 - scale) / 2; - this.blit( - ctx, sx, sy, - x + offset, y + offset, scale); + blit(sx, sy, offset, offset, scale, scale); } } }