@squid-dev/cc-web-term
Version:
A ComputerCraft terminal for the internet
113 lines (112 loc) • 4.18 kB
JavaScript
import { defaultPalette } from "./data";
export const cellWidth = 6;
export const cellHeight = 9;
export const terminalMargin = 4;
const fonts = {};
const loadPalette = ({ image, paletteCache }, colour) => {
const cached = paletteCache[colour];
if (cached)
return cached;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = image.width;
canvas.height = image.height;
context.globalCompositeOperation = "destination-atop";
context.fillStyle = colour;
context.globalAlpha = 1.0;
context.fillRect(0, 0, image.width, image.height);
context.drawImage(image, 0, 0);
paletteCache[colour] = canvas;
return canvas;
};
export const loadFont = (path) => {
const cached = fonts[path];
if (cached)
return cached;
const image = new Image();
image.src = path;
const font = fonts[path] = {
path,
image,
scale: 1,
margin: 1,
paletteCache: {},
};
font.promise = new Promise(resolve => {
image.onload = () => {
for (const key in defaultPalette) {
if (!Object.prototype.hasOwnProperty.call(defaultPalette, key))
continue;
loadPalette(font, defaultPalette[key]);
}
font.scale = font.margin = image.width / 256;
font.promise = undefined;
resolve(font);
};
});
return font;
};
export const background = (ctx, x, y, color, scale, width, height, palette) => {
let actualWidth = cellWidth * scale;
let actualHeight = cellHeight * scale;
let cellX = x * actualWidth + terminalMargin;
let cellY = y * actualHeight + terminalMargin;
if (x === 0) {
cellX -= terminalMargin;
actualWidth += terminalMargin;
}
if (x === width - 1) {
actualWidth += terminalMargin;
}
if (y === 0) {
cellY -= terminalMargin;
actualHeight += terminalMargin;
}
if (y === height - 1) {
actualHeight += terminalMargin;
}
ctx.beginPath();
ctx.rect(cellX, cellY, actualWidth, actualHeight);
ctx.fillStyle = palette[color] || palette.f;
ctx.fill();
};
export const foreground = (ctx, x, y, color, chr, palette, scale, font) => {
if (font.promise)
return;
const actualWidth = cellWidth * scale;
const actualHeight = cellHeight * scale;
const cellX = x * actualWidth + terminalMargin;
const cellY = y * actualHeight + terminalMargin;
const charcode = chr.charCodeAt(0);
const imageW = cellWidth * font.scale;
const imageH = cellHeight * font.scale;
const imgX = font.margin + (charcode % 16) * (imageW + font.margin * 2);
const imgY = font.margin + Math.floor(charcode / 16) * (imageH + font.margin * 2);
ctx.drawImage(loadPalette(font, palette[color] || palette["0"]), imgX, imgY, imageW, imageH, cellX, cellY, cellWidth * scale, cellHeight * scale);
};
export const terminal = (ctx, term, blink, scale, font) => {
const sizeX = term.sizeX;
const sizeY = term.sizeY;
for (let y = 0; y < sizeY; y++) {
for (let x = 0; x < sizeX; x++) {
background(ctx, x, y, term.back[y].charAt(x), scale, term.sizeX, term.sizeY, term.palette);
foreground(ctx, x, y, term.fore[y].charAt(x), term.text[y].charAt(x), term.palette, scale, font);
}
}
if (blink && term.cursorBlink &&
term.cursorX >= 0 && term.cursorX < sizeX &&
term.cursorY >= 0 && term.cursorY < sizeY) {
foreground(ctx, term.cursorX, term.cursorY, term.currentFore, "_", term.palette, scale, font);
}
};
export const bsod = (ctx, width, height, text, scale, font) => {
ctx.beginPath();
ctx.rect(0, 0, width * cellWidth * scale + terminalMargin * 2, height * cellHeight * scale + terminalMargin * 2);
ctx.fillStyle = defaultPalette.b;
ctx.fill();
const startX = Math.floor((width - text.length) / 2);
const startY = Math.floor((height - 1) / 2);
for (let x = 0; x < text.length; x++) {
foreground(ctx, startX + x, startY, "0", text.charAt(x), defaultPalette, scale, font);
}
};