Add some typescript files and move dungeon.js code to them
This commit is contained in:
parent
778416a43a
commit
209070ac02
4 changed files with 244 additions and 111 deletions
66
assets/scripts/modules/sketches/console.ts
Normal file
66
assets/scripts/modules/sketches/console.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
144
assets/scripts/modules/sketches/geometry.ts
Normal file
144
assets/scripts/modules/sketches/geometry.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
const NUMBER_OF_ROOMS = 12;
|
const NUMBER_OF_ROOMS = 12;
|
||||||
const TUNNEL_PASSES = 3;
|
const TUNNEL_PASSES = 3;
|
||||||
|
|
||||||
|
import {
|
||||||
|
IntegerPoint as Point,
|
||||||
|
IntegerSize as Size,
|
||||||
|
Rect
|
||||||
|
} from "scripts/modules/sketches/geometry.ts";
|
||||||
|
|
||||||
class Cell {
|
class Cell {
|
||||||
static CORRIDOR = "#";
|
static CORRIDOR = "#";
|
||||||
static DOOR_CLOSED = "+";
|
static DOOR_CLOSED = "+";
|
||||||
|
@ -11,6 +17,7 @@ class Cell {
|
||||||
|
|
||||||
constructor(char, charColor) {
|
constructor(char, charColor) {
|
||||||
this.character = char;
|
this.character = char;
|
||||||
|
this.characterColor = charColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
empty() { this.character = " "; }
|
empty() { this.character = " "; }
|
||||||
|
@ -33,104 +40,6 @@ class Cell {
|
||||||
canBecomeDoor() { return this.character === "─" || this.character === "│" }
|
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 {
|
class Grid {
|
||||||
#size;
|
#size;
|
||||||
#cells = [];
|
#cells = [];
|
||||||
|
@ -274,6 +183,7 @@ class NRandomRoomsGenerator {
|
||||||
#generateRooms() {
|
#generateRooms() {
|
||||||
let rects = new Array();
|
let rects = new Array();
|
||||||
|
|
||||||
|
const bounds = this.#bounds;
|
||||||
const sizeRange = NRandomRoomsGenerator.MAX_ROOM_DIMENSION - NRandomRoomsGenerator.MIN_ROOM_DIMENSION;
|
const sizeRange = NRandomRoomsGenerator.MAX_ROOM_DIMENSION - NRandomRoomsGenerator.MIN_ROOM_DIMENSION;
|
||||||
|
|
||||||
while (rects.length < this.#numberOfRooms) {
|
while (rects.length < this.#numberOfRooms) {
|
||||||
|
@ -283,8 +193,8 @@ class NRandomRoomsGenerator {
|
||||||
);
|
);
|
||||||
|
|
||||||
const randomOrigin = new Point(
|
const randomOrigin = new Point(
|
||||||
this.#bounds.minX + randomInt(this.#bounds.maxX - randomSize.width),
|
bounds.minX + randomInt(bounds.maxX - randomSize.width),
|
||||||
this.#bounds.minY + randomInt(this.#bounds.maxY - randomSize.height)
|
bounds.minY + randomInt(bounds.maxY - randomSize.height)
|
||||||
);
|
);
|
||||||
|
|
||||||
const proposedRoomRect = new Rect(randomOrigin, randomSize);
|
const proposedRoomRect = new Rect(randomOrigin, randomSize);
|
||||||
|
@ -294,6 +204,8 @@ class NRandomRoomsGenerator {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Pushing new room rect", proposedRoomRect.toString());
|
||||||
|
|
||||||
rects.push(proposedRoomRect);
|
rects.push(proposedRoomRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,7 +611,7 @@ class TunnelGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomInt(n) {
|
function randomInt(n) {
|
||||||
max = Math.floor(n);
|
const max = Math.floor(n);
|
||||||
return Math.floor(Math.random() * max);
|
return Math.floor(Math.random() * max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,10 +621,14 @@ new p5(p => {
|
||||||
const CELL_WIDTH = 20;
|
const CELL_WIDTH = 20;
|
||||||
const CELL_HEIGHT = Math.floor(CELL_WIDTH * 1.3);
|
const CELL_HEIGHT = Math.floor(CELL_WIDTH * 1.3);
|
||||||
|
|
||||||
|
const cellSize = new Size(CELL_WIDTH, CELL_HEIGHT);
|
||||||
|
|
||||||
p.setup = () => {
|
p.setup = () => {
|
||||||
const container = document.querySelector('#dungeon-background');
|
const container = document.querySelector('#dungeon-background');
|
||||||
canvasWidth = parseFloat(getComputedStyle(container).width);
|
const canvasWidth = parseFloat(getComputedStyle(container).width);
|
||||||
canvasHeight = parseFloat(getComputedStyle(container).height);
|
const canvasHeight = parseFloat(getComputedStyle(container).height);
|
||||||
|
|
||||||
|
console.log(`Canvas size is ${(new Size(canvasWidth, canvasHeight)).toString()}`);
|
||||||
|
|
||||||
let canvas = p.createCanvas(canvasWidth, canvasHeight);
|
let canvas = p.createCanvas(canvasWidth, canvasHeight);
|
||||||
canvas.canvas.removeAttribute('style');
|
canvas.canvas.removeAttribute('style');
|
||||||
|
@ -723,18 +639,25 @@ new p5(p => {
|
||||||
|
|
||||||
const gridBounds = Rect.fromCoordinates(
|
const gridBounds = Rect.fromCoordinates(
|
||||||
0, 0,
|
0, 0,
|
||||||
Math.max(80, Math.ceil(canvasWidth / CELL_WIDTH) - 1),
|
Math.max(80, Math.floor(canvasWidth / CELL_WIDTH) - 1),
|
||||||
Math.max(24, Math.ceil(canvasHeight / CELL_HEIGHT) - 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 = new Grid(gridBounds.size.width, gridBounds.size.height);
|
||||||
grid.generate(p, NRandomRoomsGenerator, TunnelGenerator);
|
grid.generate(p, NRandomRoomsGenerator, TunnelGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.draw = () => {
|
p.draw = () => {
|
||||||
p.textSize(CELL_HEIGHT);
|
p.textSize(cellSize.height);
|
||||||
|
|
||||||
for (let y = 0; y < grid.height; y++) {
|
for (let y = 0; y < grid.height; y++) {
|
||||||
for (let x = 0; x < grid.width; x++) {
|
for (let x = 0; x < grid.width; x++) {
|
||||||
|
@ -744,7 +667,7 @@ new p5(p => {
|
||||||
p.fill(fillColor);
|
p.fill(fillColor);
|
||||||
|
|
||||||
p.textAlign(p.CENTER, p.CENTER);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
<script defer src="{{ .Secure.RelPermalink }}"></script>
|
<script defer src="{{ .Secure.RelPermalink }}"></script>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- with partial "secure_asset.html" "scripts/nethack/dungeon.js" -}}
|
{{- with resources.Get "scripts/nethack/dungeon.js" -}}
|
||||||
<script defer src="{{ .Secure.RelPermalink }}"></script>
|
<script defer type=module src="{{ (. | js.Build).RelPermalink }}"></script>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- range $script := .Resources.Match "*.js" -}}
|
{{- range $script := .Resources.Match "*.js" -}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue