From 89a9cf03ff09d82dcb050d27dfaf50f1144587c5 Mon Sep 17 00:00:00 2001
From: slweeb <91897291+slweeb@users.noreply.github.com>
Date: Tue, 2 Jul 2024 14:35:58 -0400
Subject: [PATCH] doom.js
---
mod-list.html | 2 +-
mods/doom.js | 935 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 936 insertions(+), 1 deletion(-)
create mode 100644 mods/doom.js
diff --git a/mod-list.html b/mod-list.html
index 2298a079..0b5c089c 100644
--- a/mod-list.html
+++ b/mod-list.html
@@ -288,7 +288,7 @@
| amogus.js | Adds a small amogus structure | Alice |
| citybuilding.js | Adds seeds that create miniature buildings and other city-related items | SquareScreamYT |
| collab_mod.js | Created by multiple people, adds random things | mrapple, ilikepizza, stefanblox |
-| Doom Mod (Unreleased) | As seen on TikTok - Not yet available! | ggod |
+| doom.js | As seen on TikTok - Select the Doom element to start, WASD | ggod |
| elem3.js | Adds all elements and combinations from Elemental 3 [Very Large] | Sophie |
| fools+.js | improves and makes fools.js EXTREMELY annoying. | SquareScreamYT |
| funny elements 2022-11-15.js | Adds a few curated randomly-generated elements | Alice |
diff --git a/mods/doom.js b/mods/doom.js
new file mode 100644
index 00000000..1305e647
--- /dev/null
+++ b/mods/doom.js
@@ -0,0 +1,935 @@
+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);
+ }
+})
\ No newline at end of file