84 lines
3.5 KiB
JavaScript
84 lines
3.5 KiB
JavaScript
import { DIRECTIONS } from './defs.js';
|
||
import { mk } from './util.js';
|
||
|
||
export class CanvasRenderer {
|
||
constructor(tileset) {
|
||
this.tileset = tileset;
|
||
// Default, unfortunately and arbitrarily, to the CC1 size of 9×9. We
|
||
// don't know for sure what size to use until the Game loads a level,
|
||
// and it doesn't do that until creating a renderer! It could be fixed
|
||
// to do so, but then we wouldn't make a canvas so it couldn't be
|
||
// hooked, yadda yadda
|
||
this.viewport_size_x = 10;
|
||
this.viewport_size_y = 10;
|
||
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.viewport_x = 0;
|
||
this.viewport_y = 0;
|
||
}
|
||
|
||
set_level(level) {
|
||
this.level = level;
|
||
// TODO update viewport size... or maybe Game should do that since you might be cheating
|
||
}
|
||
|
||
draw(tic_offset = 0) {
|
||
if (! this.level) {
|
||
console.warn("CanvasRenderer.draw: No level to render");
|
||
return;
|
||
}
|
||
|
||
// FIXME XXX bad dumb hack but man tileset.draw takes a lot of arguments, that'll probably have to change for webgl anyway
|
||
this.level.tic_offset = tic_offset;
|
||
|
||
let ctx = this.canvas.getContext('2d');
|
||
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?
|
||
let xmargin = (this.viewport_size_x - 1) / 2;
|
||
let ymargin = (this.viewport_size_y - 1) / 2;
|
||
let [px, py] = this.level.player.visual_position(tic_offset);
|
||
let x0 = Math.max(0, Math.min(this.level.width - this.viewport_size_x, px - xmargin));
|
||
let y0 = Math.max(0, Math.min(this.level.height - this.viewport_size_y, py - xmargin));
|
||
this.viewport_x = x0;
|
||
this.viewport_y = y0;
|
||
// The viewport might not be aligned to the grid, so split off any fraction
|
||
let xf0 = Math.floor(x0);
|
||
let yf0 = Math.floor(y0);
|
||
let xoff = xf0 - x0;
|
||
let yoff = yf0 - y0;
|
||
let x1 = Math.ceil(x0 + this.viewport_size_x - 1);
|
||
let y1 = Math.ceil(y0 + this.viewport_size_y - 1);
|
||
// Draw in layers, so animated objects aren't overdrawn by neighboring terrain
|
||
let any_drawn = true;
|
||
let i = -1;
|
||
while (any_drawn) {
|
||
i++;
|
||
any_drawn = false;
|
||
for (let x = xf0; x <= x1; x++) {
|
||
for (let y = yf0; y <= y1; y++) {
|
||
let cell = this.level.cells[y][x];
|
||
if (! cell) console.error(x, y);
|
||
let tile = cell[i];
|
||
if (tile) {
|
||
any_drawn = true;
|
||
if (tile.type.is_actor) {
|
||
// Handle smooth scrolling
|
||
let [vx, vy] = tile.visual_position(tic_offset);
|
||
this.tileset.draw(tile, this.level, ctx, vx - x0, vy - y0);
|
||
}
|
||
else {
|
||
// Non-actors can't move
|
||
this.tileset.draw(tile, this.level, ctx, x - x0, y - y0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
export default CanvasRenderer;
|