Files
wownerogue/html/js/render/tileRenderer.js
T

116 lines
4.7 KiB
JavaScript

// TileRenderer — draws a Scene as coloured tiles + entity sprites on a 2D canvas.
// The default tier. Uses programmatic tiles for now; a real tileset/atlas can slot in here.
(function (root) {
'use strict';
function facingDelta(f) {
if (f === 'up') return { x: 0, y: -1 };
if (f === 'left') return { x: -1, y: 0 };
if (f === 'right') return { x: 1, y: 0 };
return { x: 0, y: 1 };
}
function TileRenderer(host, opts) {
opts = opts || {};
this.name = 'tiles';
this.host = host;
this.cell = opts.cell || 24;
this.canvas = document.createElement('canvas');
this.canvas.className = 'rk-canvas';
this.ctx = this.canvas.getContext('2d');
host.appendChild(this.canvas);
}
TileRenderer.prototype.render = function (scene) {
if (!scene) return;
var cell = this.cell, ctx = this.ctx;
this.canvas.width = scene.cols * cell;
this.canvas.height = scene.rows * cell;
ctx.fillStyle = scene.background || '#000';
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
if (window.RK && RK.roomReady && RK.roomReady()) {
// A designed room (imported .tmx) is loaded — draw its layered tiles.
RK.drawRoomCanvas(ctx, cell);
} else {
// Theme atlas tile if mapped, else the programmatic coloured tile.
var atlas = (window.RK && RK.themeAtlas) ? RK.themeAtlas() : null;
var tmap = (window.RK && RK.tileMap) ? RK.tileMap() : {};
var useAtlas = atlas && atlas.ready;
for (var y = 0; y < scene.rows; y++) {
for (var x = 0; x < scene.cols; x++) {
var kind = scene.grid[y][x];
var px = x * cell, py = y * cell;
var coord = useAtlas ? tmap[kind] : null;
if (coord) { atlas.draw(ctx, coord[0], coord[1], px, py, cell); continue; }
var def = scene.legend[kind] || { color: '#333' };
ctx.fillStyle = def.color;
ctx.fillRect(px, py, cell, cell);
ctx.strokeStyle = 'rgba(0,0,0,0.25)';
ctx.lineWidth = 1;
ctx.strokeRect(px + 0.5, py + 0.5, cell - 1, cell - 1);
if (def.solid) {
ctx.fillStyle = 'rgba(255,255,255,0.06)';
ctx.fillRect(px, py, cell, Math.max(1, cell * 0.14));
}
}
}
}
var now = Date.now();
for (var i = 0; i < scene.entities.length; i++) {
var e = scene.entities[i];
// Roguelike character sprite, or premium animated skin; else the fallback circle.
if (window.RK && RK.avatarVisuals && RK.avatarVisuals.drawTopdownWorld) {
var visual = RK.avatarVisuals.resolve(e.appearance || { avatar: e.avatar }, {
projection: 'topdown',
context: 'tavern',
entity: e
});
if (RK.avatarVisuals.drawTopdownWorld(ctx, visual, e, {
screenX: e.x,
screenY: e.y,
cell: cell
}, { now: now })) continue;
} else {
if (window.RK && RK.isChar && RK.isChar(e.avatar) && RK.drawCharCanvas(ctx, e, cell, now)) continue;
if (window.RK && RK.isSkin && RK.isSkin(e.avatar) && RK.drawSkinCanvas(ctx, e, cell, now)) continue;
}
var cx = e.x * cell + cell / 2, cy = e.y * cell + cell / 2, r = cell * 0.36;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.fillStyle = e.color || '#fff';
ctx.fill();
// facing pip
var d = facingDelta(e.facing);
ctx.beginPath();
ctx.arc(cx + d.x * r * 0.6, cy + d.y * r * 0.6, cell * 0.08, 0, Math.PI * 2);
ctx.fillStyle = '#0a0c0f';
ctx.fill();
if (e.you) {
ctx.lineWidth = 2;
ctx.strokeStyle = '#ffffff';
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.stroke();
}
if (e.label) {
ctx.fillStyle = '#d7dbe0';
ctx.font = '11px monospace';
ctx.textAlign = 'center';
ctx.fillText(e.label, cx, cy - cell * 0.5);
}
}
};
TileRenderer.prototype.destroy = function () {
if (this.canvas && this.canvas.parentNode) this.canvas.parentNode.removeChild(this.canvas);
this.canvas = null;
this.ctx = null;
};
root.RK = root.RK || {};
root.RK.TileRenderer = TileRenderer;
})(window);