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