elements.screen = { name: "Screen", color: "#000000", hidden: true } elements.doom = { color: "#000000", onSelect: function() { startDoom() } } let running = false; const offsetX = 0; const offsetY = 0; const screenWidth = 166 - (2 * offsetX); const screenHeight = 82 - (2 * offsetY); const halfHeight = screenHeight / 2 + offsetY; const fov = 50; const halfFov = fov / 2; const showDebugText = true; const map = [ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1], [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1], [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ]; const colors = ["#ff0000", "#0000ff", "#ffffff", "#808080", "#ff5000"]; const defaults = { x: 2, y: 2, angle: 90 } const colorSettings = { floor: "#cccccc", ceiling: "#1e1e1e", } const mapHeight = map.length; const mapWidth = map[0].length; const minimapOffset = 5; const speed = { movementSpeed: 0.5, rotationalSpeed: 5, verticalRotationalSpeed: 5, } const planeX = 0; const planeY = 0.66; const inc = fov / screenWidth; const precision_ = 64; const maxDist = 25; const accuracy = 1; // const colorSettings = { // floor: "#d1bd62", // ceiling: "#f0c743" // } const degToRad = (deg) => deg * (Math.PI / 180); const radToDeg = (rad) => rad * (180 / Math.PI); const splitHex = (hex) => hex.slice(1).match(/../g).map(a => Math.floor(parseInt(a, 16))); const hexify = (rgb) => rgb.map(a => Math.floor(a).toString(16).padStart(2, "0")).join(""); function colorLerp(color_, color2_, t) { const color = splitHex(color_); const color2 = splitHex(color2_); const r = (1 - t) * color[0] + t * color2[0]; const g = (1 - t) * color[1] + t * color2[1]; const b = (1 - t) * color[2] + t * color2[2]; return hexify([r, g, b]); } function clamp(x, min, max) { return Math.max(min, Math.min(x, max)); } class Player { constructor(x, y, angle) { this.x = x; this.y = y; this.angle = angle; this.verticalOffset = 0; } update(key) { // if (key.keyCode == 37) { // left if (key.key == "a") { this.angle -= speed.rotationalSpeed; // } else if (key.keyCode == 39) { // right } else if (key.key == "d") { this.angle += speed.rotationalSpeed; } else if (key.key == "b") { // up this.verticalOffset = clamp(this.verticalOffset + 5, -45, 45); } else if (key.key == "n") { // down this.verticalOffset = clamp(this.verticalOffset - 5, -45, 45); } if (key.key == "w") { console.log(this.angle); const playerCos = Math.cos(degToRad(this.angle)) * speed.movementSpeed; const playerSin = Math.sin(degToRad(this.angle)) * speed.movementSpeed; const newX = this.x + playerCos; const newY = this.y + playerSin; const oldX = this.x; const oldY = this.y; if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { this.y = newY; } if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { this.x = newX; } } else if (key.key == "s") { const playerCos = Math.cos(degToRad(this.angle)) * speed.movementSpeed; const playerSin = Math.sin(degToRad(this.angle)) * speed.movementSpeed; const newX = this.x - playerCos; const newY = this.y - playerSin; const oldX = this.x; const oldY = this.y; if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { this.y = newY; } if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { this.x = newX; } } else if (key.key == "a" && shiftDown) { const playerCos = Math.cos(degToRad(this.angle + 90)) * speed.movementSpeed; const playerSin = Math.sin(degToRad(this.angle + 90)) * speed.movementSpeed; const newX = this.x - playerCos; const newY = this.y - playerSin; const oldX = this.x; const oldY = this.y; if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { this.y = newY; } if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { this.x = newX; } } else if (key.key == "d" && shiftDown) { const playerCos = Math.cos(degToRad(this.angle - 90)) * speed.movementSpeed; const playerSin = Math.sin(degToRad(this.angle - 90)) * speed.movementSpeed; const newX = this.x - playerCos; const newY = this.y - playerSin; const oldX = this.x; const oldY = this.y; if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { this.y = newY; } if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { this.x = newX; } } } } const player = new Player(defaults.x, defaults.y, defaults.angle); // 5x5 const font = { a: [ 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1 ], b: [ 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 ], c: [ 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 ], d: [ 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 ], e: [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1 ], f: [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], g: [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], h: [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1 ], i: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], j: [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0 ], k: [ 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0 ], l: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0 ], m: [ 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1 ], n: [ 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1 ], o: [ 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0 ], p: [ 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], q: [ 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ], r: [ 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], s: [ 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 ], t: [ 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ], u: [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], v: [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 ], w: [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 ], x: [ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1 ], y: [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ], z: [ 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0 ], "0": [ 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0 ], "1": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], "2": [ 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1 ], "3": [ 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], "4": [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ], "5": [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], "6": [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], "7": [ 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ], "8": [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], "9": [ 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ], ".": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], ":": [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "-": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "+": [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ], ",": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, ], "[": [ 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 ], "]": [ 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0 ], "(": [ 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ], ")": [ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 ], ";": [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], "!": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ], "{": [ 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0 ], "}": [ 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0 ], "_": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 ], "°": [ 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "|": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ] } const customWidth = { i: 1, l: 4, z: 4, "0": 4, "1": 1, ".": 1, ":": 1, "-": 3, "+": 3, ",": 1, "[": 2, "]": 2, "(": 2, ")": 2, ";": 1, "!": 1, "{": 3, "}": 3, "_": 3, "°": 3, " ": 3, "|": 1, } class TextRenderer { static getCharWidth(char) { return customWidth[char] ?? 5; } static drawChar(char, x, y, color) { if (!font[char]) return; const width = this.getCharWidth(); for (let i = 0; i < width; i++) { for (let j = 0; j < 5; j++) { if (font[char][j * 5 + i] == 1) { pixelMap[x + offsetX + i][y + offsetY + j].color = color; } } } } static drawText(text, x, y, color) { let offset = 0; for (const char of text.toLowerCase().split("")) { const width = this.getCharWidth(char); this.drawChar(char, x + offset, y, color); offset += width + 1; } } static getStringWidth(text) { return text.split("").map(a => this.getCharWidth(a)).reduce((a, b) => a + b, 0) + text.length - 1; } static drawCenteredText(text, x1, y, color) { const x = Math.floor(x1 - (this.getStringWidth(text) / 2)); this.drawText(text, x, y, color); } } class ButtonRegistry { constructor (screen, buttons) { this.screen = screen; this.buttons = buttons; this.currentButton = 0; if (this.buttons.length > 0) { this.buttons[this.currentButton].toggleSelection(); } } next() { this.buttons[this.currentButton].toggleSelection(); this.currentButton++; if (this.currentButton >= this.buttons.length) { this.currentButton %= this.buttons.length; } this.buttons[this.currentButton].toggleSelection(); } prev() { this.buttons[this.currentButton].toggleSelection(); this.currentButton--; if (this.currentButton < 0) { this.currentButton = this.buttons.length - 1; } this.buttons[this.currentButton].toggleSelection(); } current() { return this.buttons[this.currentButton]; } } class GameMenu { constructor (name, screen, width, height, offsets = null) { this.name = name; this.screen = screen; this.width = width; this.height = height; this.offsetX = Math.floor((screenWidth - width - (offsets ? offsets.x : 0)) / 2) + (offsets ? offsets.x : 0); this.offsetY = Math.floor((screenHeight - height - 10 - (offsets ? offsets.y : 0)) / 2) + (offsets ? offsets.y : 0) + 10; this.buttons = []; this.buttonRegistry = new ButtonRegistry(this, []); } draw() { for (const button of this.buttons) { button.draw(); } } getOffsetX() { return offsetX + this.offsetX; } getOffsetY() { return offsetY + this.offsetY; } onKey(ev) { if (ev.key == "b") { // up this.buttonRegistry.prev(); } else if (ev.key == "n") { // down this.buttonRegistry.next(); } else if (ev.key == "Enter") { this.buttonRegistry.current().click(); } } addButtons(...buttons) { this.buttons.push(...buttons); this.updateButtonRegistry(); } getButtons() { return this.buttons; } updateButtonRegistry() { this.buttonRegistry = new ButtonRegistry(this, this.buttons); } } class GuiButton { constructor (x, y, width, height, text, color, screen) { this.x = x + screen.getOffsetX(); this.y = y + screen.getOffsetY(); this.width = width; this.height = height; this.text = text; this.color = color; this.onPressed = () => {}; this.selected = false; this.screen = screen; } draw() { for (let i = this.x; i < this.x + this.width; i++) { for (let j = this.y; j < this.y + this.height; j++) { if (this.selected && (i == this.x || j == this.y || i == this.x + this.width - 1 || j == this.y + this.height - 1)) { pixelMap[i][j] = "#0080000"; } else pixelMap[i][j] = this.color } } TextRenderer.drawCenteredText(this.text, this.x + (this.width / 2), Math.floor(this.y + (this.height / 2) - 5/2), "#ffffff"); } onClick(cb) { this.onPressed = cb; } click() { this.onPressed(); } toggleSelection() { this.selected = !this.selected; } } class GuiUtils { static drawRect(x1, y1, x2, y2, color) { for (let i = Math.max(Math.min(x1, x2), 0); i < Math.min(Math.max(x1, x2), width); i++) { for (let j = Math.max(Math.min(y1, y2), 0); j < Math.min(Math.max(y1, y2), height); j++) { pixelMap[i][j].color = color; } } } static drawOutline(x1, y1, x2, y2, color) { const initI = Math.max(Math.min(x1, x2), 0); const endI = Math.min(Math.max(x1, x2), width); const initJ = Math.max(Math.min(y1, y2), 0); const endJ = Math.min(Math.max(y1, y2), height); for (let i = initI; i < endI; i++) { for (let j = initJ; j < endJ; j++) { if (i == initI || i == endI - 1 || j == initJ || j == endJ - 1) { pixelMap[i][j].color = color; } } } } static drawVerticalLine(x, y1, y2, color) { for (let i = Math.max(Math.min(y1, y2), 0); i <= Math.min(Math.max(y1, y2), height); i++) { if (!pixelMap[x][i]) continue; pixelMap[x][i].color = color; } } } class TestGameMenu extends GameMenu { constructor (screen, width, height, offsets = null) { super("test menu screen", screen, width, height, offsets); const button1 = new GuiButton(1, 1, width - 2, 20, "button", "#005555", this); button1.onClick(() => { button1.color = "#ff0000"; button2.color = "#005555"; }) const button2 = new GuiButton(1, 22, width - 2, 20, "button 2", "#005555", this); button2.onClick(() => { button2.color = "#ff0000"; button1.color = "#005555"; }) this.addButtons( button1, button2 ) } draw() { for (let i = this.getOffsetX(); i < this.width + this.getOffsetX(); i++) { for (let j = this.getOffsetY(); j < this.height + this.getOffsetY(); j++) { pixelMap[i][j].color = "#ffffff"; } } super.draw(); } } class GameScreen { clock() { this.fps = this.currentFrames; this.currentFrames = 0; } clear() { for (let i = offsetX; i < width - offsetX; i++) { for (let j = offsetY; j < height - offsetY; j++) { pixelMap[i][j].color = "#ffffff"; } } } draw() { if (!this.currentFrames) this.currentFrames = 0; this.currentFrames++; this.clear(); this.drawLevel(); this.drawMinimap(); if (showDebugText) { TextRenderer.drawText(`angle: ${Math.floor(player.angle)}°`, 1, 1, "#ffffff"); TextRenderer.drawText(`pos: ${player.x.toFixed(2)} | ${player.y.toFixed(2)}`, 1, 7, "#ffffff"); TextRenderer.drawText(`fps: ${this.fps}`, 1, 13, "#ffffff"); TextRenderer.drawText(`vertangle: ${player.verticalOffset}`, 1, 19, "#ffffff"); } if (this.menuScreen) { this.drawMenu(); } } drawMenu() { this.drawOverlay(); GuiUtils.drawRect(this.menuScreen.getOffsetX(), Math.floor((screenHeight - this.menuScreen.height) / 2) - 2, this.menuScreen.getOffsetX() + this.menuScreen.width, this.menuScreen.getOffsetY(), "#1e1e1e"); TextRenderer.drawCenteredText(this.menuScreen.name, (this.menuScreen.width / 2) + this.menuScreen.getOffsetX(), Math.floor((screenHeight - this.menuScreen.height) / 2) - 1, "#ffffff"); this.menuScreen.draw(); } drawOverlay() { for (let i = offsetX; i < width - offsetX; i++) { for (let j = offsetY; j < height - offsetY; j++) { pixelMap[i][j].color = "#" + colorLerp(pixelMap[i][j].color, "#000000", 0.5); } } } drawLevel() { let rayAngle = player.angle - halfFov; for (let i = 0; i < screenWidth; i += accuracy) { let rayX = player.x; let rayY = player.y; const rayCos = Math.cos(degToRad(rayAngle)) / precision_; const raySin = Math.sin(degToRad(rayAngle)) / precision_; let wall = 0; while (wall == 0) { rayX += rayCos; rayY += raySin; wall = map[Math.floor(rayY)][Math.floor(rayX)]; } const angle = radToDeg(Math.atan2(rayY, rayX)); const side = (angle >= 45 && angle <= 135) || (angle >= 275 && angle <= 315); const distance = Math.sqrt((player.x - rayX) ** 2 + (player.y - rayY) ** 2) * Math.cos(degToRad(rayAngle - player.angle)); const wallHeight = Math.floor(halfHeight / distance); let t = Math.min(1, (1 / maxDist) * distance); const color = "#" + colorLerp(colors[wall - 1], "#000000", t); const ceilingColor = colorSettings.ceiling; const floorColor = colorSettings.floor; GuiUtils.drawVerticalLine(offsetX + i, halfHeight - wallHeight - player.verticalOffset, halfHeight + wallHeight - player.verticalOffset, color); GuiUtils.drawVerticalLine(offsetX + i, offsetY, halfHeight - wallHeight - 1 - player.verticalOffset, ceilingColor); GuiUtils.drawVerticalLine(offsetX + i, halfHeight + wallHeight + 1 - player.verticalOffset, height - offsetY - 1, floorColor); rayAngle += inc; } } drawMinimap() { for (let i = 0; i < mapWidth; i++) { for (let j = 0; j < mapHeight; j++) { const x = width - offsetX - minimapOffset - mapWidth + i; const y = offsetY + minimapOffset + j; if (map[j][i] == 0) { pixelMap[x][y].color = colorSettings.floor; } else { pixelMap[x][y].color = colors[map[j][i] - 1]; } if (i == Math.floor(player.x) && j == Math.floor(player.y)) { pixelMap[x][y].color = "#00ffff"; } } } } onKey(ev) { // if (ev.key == "Escape") { // this.menuScreen = this.menuScreen ? null : new TestGameMenu(this, Math.floor(0.75 * screenWidth), Math.floor(0.75 * screenHeight)); // } if (!this.menuScreen) { player.update(ev); } else { this.menuScreen.onKey(ev); } } } const game = new GameScreen(); setInterval(cellTick, (1000/(tps*4))); setInterval(() => {game.clock()}, 1000); function cellTick() { if (running && !paused) { game.draw(); } } function startDoom() { if (!running) { videoFrame = 0; for (let i = offsetX; i < width - offsetX; i++) { for (let j = offsetY; j < height - offsetY; j++) { if (pixelMap[i][j]) deletePixel(i, j); createPixel("screen", i, j); } } } running = !running; } window.addEventListener("keydown", (ev) => { if (ev.key == "u") { startDoom(); } else { game.onKey(ev); } })