From 209070ac022364f5ef05f9a2b8416544fa7bd476 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Tue, 26 Dec 2023 21:41:39 -0700 Subject: [PATCH] Add some typescript files and move dungeon.js code to them --- assets/scripts/modules/sketches/console.ts | 66 +++++++++ assets/scripts/modules/sketches/geometry.ts | 144 ++++++++++++++++++++ assets/scripts/nethack/dungeon.js | 141 +++++-------------- layouts/nethack/single.html | 4 +- 4 files changed, 244 insertions(+), 111 deletions(-) create mode 100644 assets/scripts/modules/sketches/console.ts create mode 100644 assets/scripts/modules/sketches/geometry.ts diff --git a/assets/scripts/modules/sketches/console.ts b/assets/scripts/modules/sketches/console.ts new file mode 100644 index 0000000..089f85a --- /dev/null +++ b/assets/scripts/modules/sketches/console.ts @@ -0,0 +1,66 @@ +import { + IntegerPoint as Point, + IntegerSize as Size, + Rect, +} from "./geometry.js"; + +export class Console { + #screen: Cell[]; + #size: Size; + + constructor(size: Size) { + const length = size.width * size.height; + this.#size = size + + this.#screen = new Array(length); + for (let i = 0; i < length; i++) { + this.#screen[i] = new Cell(' ') + } + } + + drawInRect(rect: Rect) { + const cellSize = new Size( + rect.size.width / this.#size.width, + rect.size.height / this.#size.height + ); + + for (let y = 0; y < this.#size.height; y++) { + for (let x = 0; x < this.#size.width; x++) { + const pt = new Point(x, y); + const character = this.characterAt(pt); + } + } + } + + characterAt(pt: Point) { + return this.#cellAt(pt).character; + } + + #cellAt(pt: Point): Cell { + return this.#screen[this.#size.width * pt.y + pt.x]; + } +} + +export class Color { + red: number; + green: number; + blue: number; + + constructor(r: number, g: number, b: number) { + this.red = Math.min(1, Math.max(0, r)); + this.green = Math.min(1, Math.max(0, g)); + this.blue = Math.min(1, Math.max(0, b)); + } +} + +export class Cell { + character: String; + foregroundColor: Color; + backgroundColor?: Color; + + constructor(character: String, foregroundColor?: Color, backgroundColor?: Color) { + this.character = character ? character : " "; + this.foregroundColor = foregroundColor ? foregroundColor : new Color(1, 1, 1); + this.backgroundColor = backgroundColor; + } +} diff --git a/assets/scripts/modules/sketches/geometry.ts b/assets/scripts/modules/sketches/geometry.ts new file mode 100644 index 0000000..92f4608 --- /dev/null +++ b/assets/scripts/modules/sketches/geometry.ts @@ -0,0 +1,144 @@ +export class Point { + x = 0; + y = 0; + + constructor(x: number = 0, y: number = 0) { + if (x !== null && x !== undefined) { + this.x = x; + } + if (y !== null && y !== undefined) { + this.y = y; + } + } + + toString() { + return `(${this.x}, ${this.y})`; + } + + equalsPoint(other: Point): boolean { + return this.x === other.x && this.y === other.y; + } +} + + +export class IntegerPoint extends Point { + constructor(x: number = 0, y: number = 0) { + super(x ? Math.floor(x) : 0, y ? Math.floor(y) : 0); + } + + *neighbors() { + const x = this.x; + const y = this.y; + + yield new IntegerPoint(x - 1, y - 1); + yield new IntegerPoint(x, y - 1); + yield new IntegerPoint(x + 1, y - 1); + yield new IntegerPoint(x - 1, y); + yield new IntegerPoint(x + 1, y); + yield new IntegerPoint(x - 1, y + 1); + yield new IntegerPoint(x, y + 1); + yield new IntegerPoint(x + 1, y + 1); + } +} + + +export class Size { + width = 0; + height = 0; + + constructor(width: number = 0, height: number = 0) { + if (width !== null && width !== undefined) { + this.width = width; + } + if (height !== null && height !== undefined) { + this.height = height; + } + } + + toString() { + return `(${this.width}, ${this.height})` + } +} + + +export class IntegerSize extends Size { + constructor(w: number = 0, h: number = 0) { + super(w ? Math.floor(w) : 0, h ? Math.floor(h) : 0); + } +} + + +export class Rect { + origin = new Point(); + size = new Size(); + + static fromCoordinates(x: number, y: number, w: number, h: number) { + return new Rect(new Point(x, y), new Size(w, h)); + } + + constructor(origin: Point, size: Size) { + if (origin instanceof Point) { + this.origin = origin; + } else { + throw new Error("Invalid origin value"); + } + + if (size instanceof Size) { + this.size = size; + } else { + throw new Error("Invalid size value"); + } + } + + toString() { + return `{${this.origin.toString()}, ${this.size.toString()}}` + } + + get minX() { return this.origin.x; } + get minY() { return this.origin.y; } + get maxX() { return this.origin.x + this.size.width; } + get maxY() { return this.origin.y + this.size.height; } + + get width() { return this.size.width; } + get height() { return this.size.height; } + get area() { return this.size.width * this.size.height; } + + *xCoordinates() { + for (let x = this.minX; x <= this.maxX; x++) { + yield x; + } + } + + *yCoordinates() { + for (let y = this.minY; y <= this.maxY; y++) { + yield y; + } + } + + insetRect(inset: number) { + const twiceInset = 2 * inset; + + return Rect.fromCoordinates( + this.origin.x + inset, + this.origin.y + inset, + this.size.width - twiceInset, + this.size.height - twiceInset + ); + } + + intersects(other: Rect) { + if (other.minX > this.maxX) + return false; + + if (other.maxX < this.minX) + return false; + + if (other.minY > this.maxY) + return false; + + if (other.maxY < this.minY) + return false; + + return true; + } +} diff --git a/assets/scripts/nethack/dungeon.js b/assets/scripts/nethack/dungeon.js index 0c8eabb..32b3dd7 100644 --- a/assets/scripts/nethack/dungeon.js +++ b/assets/scripts/nethack/dungeon.js @@ -1,6 +1,12 @@ const NUMBER_OF_ROOMS = 12; const TUNNEL_PASSES = 3; +import { + IntegerPoint as Point, + IntegerSize as Size, + Rect +} from "scripts/modules/sketches/geometry.ts"; + class Cell { static CORRIDOR = "#"; static DOOR_CLOSED = "+"; @@ -11,6 +17,7 @@ class Cell { constructor(char, charColor) { this.character = char; + this.characterColor = charColor; } empty() { this.character = " "; } @@ -33,104 +40,6 @@ class Cell { canBecomeDoor() { return this.character === "─" || this.character === "│" } } -class Point { - x = 0; - y = 0; - - constructor(x, y) { - if (x) { this.x = x; } - if (y) { this.y = y; } - } - - *neighbors() { - const x = this.x; - const y = this.y; - - yield new Point(x - 1, y - 1); - yield new Point(x, y - 1); - yield new Point(x + 1, y - 1); - yield new Point(x - 1, y); - yield new Point(x + 1, y); - yield new Point(x - 1, y + 1); - yield new Point(x, y + 1); - yield new Point(x + 1, y + 1); - } - - equalsPoint(other) { return this.x === other.x && this.y === other.y; } -} - -class Size { - width = 0; - height = 0; - - constructor(width, height) { - this.width = width; - this.height = height; - } -} - -class Rect { - origin = new Point(); - size = new Size(); - - static fromCoordinates(x, y, w, h) { - return new Rect(new Point(x, y), new Size(w, h)); - } - - constructor(origin, size) { - this.origin = origin; - this.size = size; - } - - get minX() { return this.origin.x; } - get minY() { return this.origin.y; } - get maxX() { return this.origin.x + this.size.width; } - get maxY() { return this.origin.y + this.size.height; } - - get width() { return this.size.width; } - get height() { return this.size.height; } - get area() { return this.size.width * this.size.height; } - - *xCoordinates() { - for (let x = this.minX; x <= this.maxX; x++) { - yield x; - } - } - - *yCoordinates() { - for (let y = this.minY; y <= this.maxY; y++) { - yield y; - } - } - - insetRect(inset) { - const twiceInset = 2 * inset; - - return Rect.fromCoordinates( - this.origin.x + inset, - this.origin.y + inset, - this.size.width - twiceInset, - this.size.height - twiceInset - ); - } - - intersects(otherRect) { - if (otherRect.minX > this.maxX) - return false; - - if (otherRect.maxX < this.minX) - return false; - - if (otherRect.minY > this.maxY) - return false; - - if (otherRect.maxY < this.minY) - return false; - - return true; - } -} - class Grid { #size; #cells = []; @@ -274,6 +183,7 @@ class NRandomRoomsGenerator { #generateRooms() { let rects = new Array(); + const bounds = this.#bounds; const sizeRange = NRandomRoomsGenerator.MAX_ROOM_DIMENSION - NRandomRoomsGenerator.MIN_ROOM_DIMENSION; while (rects.length < this.#numberOfRooms) { @@ -283,8 +193,8 @@ class NRandomRoomsGenerator { ); const randomOrigin = new Point( - this.#bounds.minX + randomInt(this.#bounds.maxX - randomSize.width), - this.#bounds.minY + randomInt(this.#bounds.maxY - randomSize.height) + bounds.minX + randomInt(bounds.maxX - randomSize.width), + bounds.minY + randomInt(bounds.maxY - randomSize.height) ); const proposedRoomRect = new Rect(randomOrigin, randomSize); @@ -294,6 +204,8 @@ class NRandomRoomsGenerator { continue; } + console.log("Pushing new room rect", proposedRoomRect.toString()); + rects.push(proposedRoomRect); } @@ -531,7 +443,7 @@ class TunnelGenerator { let toPoint; if (fromRoomBounds.maxX < toRoomBounds.minX) { // fromRoom is farther left than toRoom - + fromPoint = new Point(fromRoomBounds.maxX, fromRoomBounds.minY + 1 + randomInt(fromRoomBounds.height - 2)); foundFromPoint = this.#canPlaceDoorAt(fromPoint); @@ -699,7 +611,7 @@ class TunnelGenerator { } function randomInt(n) { - max = Math.floor(n); + const max = Math.floor(n); return Math.floor(Math.random() * max); } @@ -709,10 +621,14 @@ new p5(p => { const CELL_WIDTH = 20; const CELL_HEIGHT = Math.floor(CELL_WIDTH * 1.3); + const cellSize = new Size(CELL_WIDTH, CELL_HEIGHT); + p.setup = () => { const container = document.querySelector('#dungeon-background'); - canvasWidth = parseFloat(getComputedStyle(container).width); - canvasHeight = parseFloat(getComputedStyle(container).height); + const canvasWidth = parseFloat(getComputedStyle(container).width); + const canvasHeight = parseFloat(getComputedStyle(container).height); + + console.log(`Canvas size is ${(new Size(canvasWidth, canvasHeight)).toString()}`); let canvas = p.createCanvas(canvasWidth, canvasHeight); canvas.canvas.removeAttribute('style'); @@ -723,18 +639,25 @@ new p5(p => { const gridBounds = Rect.fromCoordinates( 0, 0, - Math.max(80, Math.ceil(canvasWidth / CELL_WIDTH) - 1), - Math.max(24, Math.ceil(canvasHeight / CELL_HEIGHT) - 1) + Math.max(80, Math.floor(canvasWidth / CELL_WIDTH) - 1), + Math.max(24, Math.floor(canvasHeight / CELL_HEIGHT) - 1) ); - console.log(`Generating grid with size ${gridBounds.size.width} x ${gridBounds.size.height}`); + if (gridBounds.width === 80) { + cellSize.width = Math.floor(canvasWidth / gridBounds.width); + } + if (gridBounds.height === 24) { + cellSize.height = Math.floor(canvasHeight / gridBounds.height); + } + + console.log(`Generating grid with size ${gridBounds.size.toString()}, cell: ${cellSize.toString()}`); grid = new Grid(gridBounds.size.width, gridBounds.size.height); grid.generate(p, NRandomRoomsGenerator, TunnelGenerator); } p.draw = () => { - p.textSize(CELL_HEIGHT); + p.textSize(cellSize.height); for (let y = 0; y < grid.height; y++) { for (let x = 0; x < grid.width; x++) { @@ -744,7 +667,7 @@ new p5(p => { p.fill(fillColor); p.textAlign(p.CENTER, p.CENTER); - p.text(cell.character, x * CELL_WIDTH, y * CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT); + p.text(cell.character, x * cellSize.width, y * cellSize.height, cellSize.width, cellSize.height); } } diff --git a/layouts/nethack/single.html b/layouts/nethack/single.html index 8c8591d..726da62 100644 --- a/layouts/nethack/single.html +++ b/layouts/nethack/single.html @@ -25,8 +25,8 @@ {{- end -}} - {{- with partial "secure_asset.html" "scripts/nethack/dungeon.js" -}} - + {{- with resources.Get "scripts/nethack/dungeon.js" -}} + {{- end -}} {{- range $script := .Resources.Match "*.js" -}}