Move responsibility for drawing out of Tileset and into the renderer
This commit is contained in:
parent
2e0519f802
commit
e70e92b931
24
js/main.js
24
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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user