Skip to content

Commit 2eed6cb

Browse files
committed
Implement path clearing between game objects during room generation and add basis for a generation visualiser mode.
1 parent edbe9ca commit 2eed6cb

File tree

9 files changed

+255
-96
lines changed

9 files changed

+255
-96
lines changed

src/Game.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { KeyQueue } from './util';
22
import { IO } from './Systems/io';
3+
import { Renderer } from './Systems/renderer';
4+
import { World } from './world';
35

46
export abstract class Game {
57

@@ -10,8 +12,13 @@ export abstract class Game {
1012
private last;
1113
private step;
1214

15+
protected algoVis = false;
16+
1317
constructor() {}
1418

19+
abstract renderer: Renderer;
20+
abstract world: World;
21+
1522
// used to load json configs, audio samples, etc. Called one time after invoking run().
1623
abstract load();
1724

src/Level.ts

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { World } from "./world";
33
import { Player } from "./Actors/Player";
44
import { Tile } from "./tile";
55
import { DoorType, Door } from "./Rooms/Door";
6+
import { Game } from "./Game";
7+
import { PathQueue } from './util';
68

79
// A level is an arrangement of Rooms representing a "full" dungeon floor, or layer of the world
810
export class Level {
@@ -14,15 +16,16 @@ export class Level {
1416

1517
private defaultRoom: Room;
1618
// the 2d grid of rooms representing this level
17-
private roomCount = 4; // the number of rooms in this levl
18-
private rooms: Room[] = [];
19+
private roomCount = 4; // the number of rooms in this level
20+
public rooms: Room[] = [];
1921
private activeRoom: Room;
2022
private activeRoomChanged: boolean = false;
2123

22-
// private roomHeight = 50;
23-
// private roomWidth = 30;
24+
private world; // the world that this level is a part of
25+
// public pathQueues: Record<number, PathQueue> = {};
2426

25-
constructor(name: string, depth: number, defaultRoom: Room) {
27+
constructor(world: World, name: string, depth: number, defaultRoom: Room) {
28+
this.world = world;
2629
this.name = name;
2730
this.depth = depth;
2831
this.defaultRoom = defaultRoom;
@@ -32,15 +35,24 @@ export class Level {
3235
this.initializeRooms();
3336
this.activeRoom = this.rooms[0];
3437
this.generateRoomLayout();
38+
this.guaranteePathsToDoors();
3539
}
3640

3741
// Deterime the arrangement of rooms in this level.
3842
// This should be called once when the level is initially generated
3943
private initializeRooms() {
4044
for (let i = 0; i < this.roomCount; i++) {
4145
let room = new Room(this.defaultRoom.getWidth(), this.defaultRoom.getHeight(), "Room ("+i+")");
46+
room.wallTile = this.defaultRoom.wallTile;
47+
room.floorTile = this.defaultRoom.floorTile;
48+
4249
room.init(0, 12);
43-
if (i == 0) room.addActor(new Player(20, 20, new Tile('@', 'red', 'black')));
50+
if (i == 0) {
51+
// create a player!
52+
let player = new Player(20, 20, new Tile('C', 'purple', 'black'));
53+
this.world.setPlayer(player);
54+
room.addActor(player);
55+
}
4456
this.rooms.push(room);
4557
}
4658
}
@@ -51,22 +63,40 @@ export class Level {
5163
let roomB = this.rooms[i+1];
5264

5365
// set a door from A to B
66+
let xr = Math.floor(Math.random() * this.getActiveRoom().getWidth());
67+
let yr = Math.floor(Math.random() * this.getActiveRoom().getHeight());
5468
let dir = this.directions[Math.floor(Math.random() * this.directions.length)];
55-
let door1 = roomA.placeDoor(roomB, dir);
69+
roomA.placeDoor(roomB, dir, xr, yr);
5670

5771
// set the complementary door from B to A
5872
if (dir == DoorType.NorthDoor)
59-
roomB.placeDoor(roomA, DoorType.SouthDoor, door1.x, door1.y);
73+
roomB.placeDoor(roomA, DoorType.SouthDoor, xr, yr);
6074
else if (dir == DoorType.SouthDoor)
61-
roomB.placeDoor(roomA, DoorType.NorthDoor, door1.x, door1.y);
75+
roomB.placeDoor(roomA, DoorType.NorthDoor, xr, yr);
6276
else if (dir == DoorType.WestDoor)
63-
roomB.placeDoor(roomA, DoorType.EastDoor, door1.x, door1.y);
77+
roomB.placeDoor(roomA, DoorType.EastDoor, xr, yr);
6478
else if (dir == DoorType.EastDoor)
65-
roomB.placeDoor(roomA, DoorType.WestDoor, door1.x, door1.y);
79+
roomB.placeDoor(roomA, DoorType.WestDoor, xr, yr);
6680

6781
}
6882
}
6983

84+
public guaranteePathsToDoors() {
85+
for (let i = 0; i < this.rooms.length; i++) {
86+
let room = this.rooms[i];
87+
let doors = room.getDoors();
88+
if (room == this.getActiveRoom()) {
89+
// just make sure the player can reach the first door
90+
room.clearPathBetween(this.world.getPlayer(), doors[0]);
91+
}
92+
else if (doors.length >= 2) {
93+
// make a path between two doors
94+
room.clearPathBetween(doors[0], doors[1]);
95+
}
96+
97+
}
98+
}
99+
70100
takeTurn(world: World) {
71101
this.activeRoom.handleActorTurns(world);
72102
}
@@ -84,17 +114,11 @@ export class Level {
84114
}
85115

86116
setActiveRoom(room: Room) {
87-
console.log("active room invoked")
88117
this.activeRoom = null;
89118
this.activeRoom = room;
90-
console.log("new active room: ", this.activeRoom);
91119
this.activeRoomChanged = true;
92120
}
93121

94-
// addAvailableRoom(room: Room) {
95-
// this.availableRooms.push(room);
96-
// }
97-
98122
/*addRoom(room: Room) {
99123
if (this.rooms.length == 0) this.activeRoom = room;
100124
else {
@@ -108,8 +132,4 @@ export class Level {
108132
109133
this.rooms.push(room);
110134
}*/
111-
112-
/*getRooms() {
113-
return this.rooms;
114-
}*/
115135
}

src/Rooms/Door.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Room } from "./Room";
2-
import { Player } from "../Actors/Player";
32
import { Tile } from "../tile";
43
import { GameObject } from "../GameObject";
54

@@ -25,6 +24,7 @@ export class Door extends GameObject {
2524
super(x, y, tile);
2625
this.toRoom = toRoom;
2726
this.type = type;
27+
this.name = 'Door';
2828
}
2929

3030
}

src/Rooms/Room.ts

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { Actor } from '../Actors/Actor';
44
import { World } from '../world';
55
import { Door, DoorType } from './Door';
66
import { Wall, Floor, Tree } from './Environment';
7-
import { BSPTree } from '../util';
7+
import { BSPTree, PathQueue } from '../util';
88
import { Item } from '../Items/Item';
9+
import { Game } from '../Game';
910

1011
// An instance of Area represents some area of a Room, usually walled off
1112
// Generally we apply our proc gen algorithms to Areas rather than ActionDirection to Rooms
@@ -32,10 +33,7 @@ export class Room {
3233
public actors: Actor[];
3334
public objects: GameObject[][];
3435

35-
public northDoor: Door = null;
36-
public southDoor: Door = null;
37-
public westDoor: Door = null;
38-
public eastDoor: Door = null;
36+
private doors: Door[];
3937

4038
private wallDoorTile: Tile = new Tile('*', 'orange', 'black');
4139
private trapDoorTile: Tile = new Tile('#', 'orange', 'black');
@@ -49,10 +47,13 @@ export class Room {
4947
public floorTile = new Tile('.', this.defaultFgColor, this.defaultBgColor);
5048
public wallTile = new Tile('#', this.defaultFgColor, this.defaultBgColor);
5149

50+
public fullyGenerated = false;
51+
5252
constructor(width: number, height: number, name: string) {
5353
this.width = width;
5454
this.height = height;
5555
this.objects = [];
56+
this.doors = [];
5657
this.actors = [];
5758
this.name = name;
5859

@@ -89,27 +90,31 @@ export class Room {
8990

9091
if (dir == DoorType.NorthDoor) {
9192
let doorx = x || Math.floor(Math.random() * this.getWidth());
92-
let doory = this.getHeight() - 1;
93+
let doory = 0;
9394
this.objects[doorx][doory] = new Door(DoorType.NorthDoor, doorx, doory, this.wallDoorTile, toRoom);
95+
this.doors.push(<Door>this.objects[doorx][doory]);
9496
return this.objects[doorx][doory];
9597
}
9698
if (dir == DoorType.SouthDoor) {
9799
let doorx = x || Math.floor(Math.random() * this.getWidth());
98-
let doory = 0;
100+
let doory = this.getHeight() - 1;
99101
this.objects[doorx][doory] = new Door(DoorType.SouthDoor, doorx, doory, this.wallDoorTile, toRoom);
102+
this.doors.push(<Door>this.objects[doorx][doory]);
100103
return this.objects[doorx][doory];
101104
}
102105
if (dir == DoorType.WestDoor) {
103106
let doorx = 0;
104107
let doory = y || Math.floor(Math.random() * this.getHeight());
105108
this.objects[doorx][doory] = new Door(DoorType.WestDoor, doorx, doory, this.wallDoorTile, toRoom);
109+
this.doors.push(<Door>this.objects[doorx][doory]);
106110
return this.objects[doorx][doory];
107111

108112
}
109113
if (dir == DoorType.EastDoor) {
110114
let doorx = this.getWidth() - 1;
111115
let doory = y || Math.floor(Math.random() * this.getHeight());
112116
this.objects[doorx][doory] = new Door(DoorType.EastDoor, doorx, doory, this.wallDoorTile, toRoom);
117+
this.doors.push(<Door>this.objects[doorx][doory]);
113118
return this.objects[doorx][doory];
114119
}
115120

@@ -164,6 +169,10 @@ export class Room {
164169
(<Floor>this.objects[actor.x][actor.y]).setOccupation(actor);
165170
}
166171

172+
getDoors() {
173+
return this.doors;
174+
}
175+
167176
getActors() {
168177
return this.actors;
169178
}
@@ -514,4 +523,54 @@ export class Room {
514523
}
515524

516525
}
526+
527+
/**
528+
* Method to clear a path between two game objects.
529+
* This is useful for making sure the player can reach the door, and there is at least one path
530+
* between two doors or objectives in the room.
531+
*/
532+
clearPathBetween(obj1: GameObject, obj2: GameObject) {
533+
// let pathQueue = new PathQueue();
534+
535+
// The cursor which clears the path will be a cross: 1 tiles tall and 1 tiles wide
536+
let dx = obj2.x - obj1.x;
537+
let dy = obj2.y - obj1.y;
538+
539+
let cursorX = obj1.x;
540+
let cursorY = obj1.y;
541+
542+
while(!(Math.abs(dx) <= 1 && Math.abs(dy) <= 1)) {
543+
544+
let stepX = 0;
545+
let stepY = 0;
546+
547+
dx = obj2.x - cursorX;
548+
dy = obj2.y - cursorY;
549+
550+
// correct cursor being on the edge of the room
551+
if (cursorX == 0) cursorX = 1;
552+
if (cursorY == 0) cursorY = 1;
553+
if (cursorX == this.getWidth() - 1) cursorX -= 1;
554+
if (cursorY == this.getHeight() - 1) cursorY -= 1;
555+
556+
if (Math.abs(dx) > Math.abs(dy)) {
557+
// adjust the x pos
558+
cursorX += 1 * (dx > 0 ? 1 : -1);
559+
// stepX = 1 * (dx > 0 ? 1 : -1);
560+
561+
} else if (Math.abs(dy) >= Math.abs(dx)) {
562+
// if dx == dy or dy > dx then adjust the y pos
563+
cursorY += 1 * (dy > 0 ? 1 : - 1);
564+
// stepY = 1 * (dy > 0 ? 1 : - 1);
565+
}
566+
567+
// pathQueue.enqueue([cursorX, cursorY]);
568+
569+
// clear the actual area around the 1x1 cursor
570+
// this.objects[cursorX][cursorY] = new Floor(cursorX, cursorY, new Tile(this.floorTile.ascii, 'black', 'yellow'));
571+
this.objects[cursorX][cursorY] = new Floor(cursorX, cursorY, this.floorTile);
572+
}
573+
574+
// return pathQueue;
575+
}
517576
}

src/Systems/renderer.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ export class Renderer {
100100
}
101101
}
102102

103-
public renderRoom(room: Room, context: HTMLElement) {
103+
public renderRoom(room: Room, contextName: string) {
104104
for (let i = 0; i < room.getWidth(); i++) {
105105
for (let j = 0; j < room.getHeight(); j++) {
106-
this.renderGameObject(room.getObject(i, j), context);
106+
this.renderGameObject(room.getObject(i, j), this.windows[contextName].getContext());
107107
}
108108
}
109109
}
@@ -219,26 +219,40 @@ export class Renderer {
219219
public renderObjectContext(obj: GameObject, room: Room, context: HTMLElement) {
220220
// If the player is in debug render their movements and local contexts in yellow
221221
if (obj instanceof Actor && (<Actor>obj).debug) {
222-
this.updateTile(obj.x - 1, obj.y, new Tile(room.getObject(obj.x - 1, obj.y).getTile().ascii, room.getObject(obj.x - 1, obj.y).getTile().fg, 'yellow'), context);
223-
this.updateTile(obj.x + 1, obj.y, new Tile(room.getObject(obj.x + 1, obj.y).getTile().ascii, room.getObject(obj.x + 1, obj.y).getTile().fg, 'yellow'), context);
224-
this.updateTile(obj.x, obj.y - 1, new Tile(room.getObject(obj.x, obj.y - 1).getTile().ascii, room.getObject(obj.x, obj.y - 1).getTile().fg, 'yellow'), context);
225-
this.updateTile(obj.x, obj.y + 1, new Tile(room.getObject(obj.x, obj.y + 1).getTile().ascii, room.getObject(obj.x, obj.y + 1).getTile().fg, 'yellow'), context);
226-
227-
this.updateTile(obj.x - 1, obj.y - 1, new Tile(room.getObject(obj.x - 1, obj.y - 1).getTile().ascii, room.getObject(obj.x - 1, obj.y - 1).getTile().fg, 'yellow'), context);
228-
this.updateTile(obj.x + 1, obj.y - 1, new Tile(room.getObject(obj.x + 1, obj.y - 1).getTile().ascii, room.getObject(obj.x + 1, obj.y - 1).getTile().fg, 'yellow'), context);
229-
this.updateTile(obj.x - 1, obj.y + 1, new Tile(room.getObject(obj.x - 1, obj.y + 1).getTile().ascii, room.getObject(obj.x - 1, obj.y + 1).getTile().fg, 'yellow'), context);
230-
this.updateTile(obj.x + 1, obj.y + 1, new Tile(room.getObject(obj.x - 1, obj.y + 1).getTile().ascii, room.getObject(obj.x - 1, obj.y + 1).getTile().fg, 'yellow'), context);
222+
if (!(obj.x == 0))
223+
this.updateTile(obj.x - 1, obj.y, new Tile(room.getObject(obj.x - 1, obj.y).getTile().ascii, room.getObject(obj.x - 1, obj.y).getTile().fg, 'yellow'), context);
224+
if (!(obj.x == room.getWidth() - 1))
225+
this.updateTile(obj.x + 1, obj.y, new Tile(room.getObject(obj.x + 1, obj.y).getTile().ascii, room.getObject(obj.x + 1, obj.y).getTile().fg, 'yellow'), context);
226+
if (!(obj.y == 0))
227+
this.updateTile(obj.x, obj.y - 1, new Tile(room.getObject(obj.x, obj.y - 1).getTile().ascii, room.getObject(obj.x, obj.y - 1).getTile().fg, 'yellow'), context);
228+
if (!(obj.y == room.getHeight() - 1))
229+
this.updateTile(obj.x, obj.y + 1, new Tile(room.getObject(obj.x, obj.y + 1).getTile().ascii, room.getObject(obj.x, obj.y + 1).getTile().fg, 'yellow'), context);
230+
231+
if (!(obj.x == 0 || obj.y == 0))
232+
this.updateTile(obj.x - 1, obj.y - 1, new Tile(room.getObject(obj.x - 1, obj.y - 1).getTile().ascii, room.getObject(obj.x - 1, obj.y - 1).getTile().fg, 'yellow'), context);
233+
if (!(obj.x == room.getWidth() - 1|| obj.y == 0))
234+
this.updateTile(obj.x + 1, obj.y - 1, new Tile(room.getObject(obj.x + 1, obj.y - 1).getTile().ascii, room.getObject(obj.x + 1, obj.y - 1).getTile().fg, 'yellow'), context);
235+
if (!(obj.x == 0 || obj.x == room.getHeight() - 1))
236+
this.updateTile(obj.x - 1, obj.y + 1, new Tile(room.getObject(obj.x - 1, obj.y + 1).getTile().ascii, room.getObject(obj.x - 1, obj.y + 1).getTile().fg, 'yellow'), context);
237+
if (!(obj.x == room.getWidth() - 1 || obj.y == room.getHeight() - 1))
238+
this.updateTile(obj.x + 1, obj.y + 1, new Tile(room.getObject(obj.x - 1, obj.y + 1).getTile().ascii, room.getObject(obj.x - 1, obj.y + 1).getTile().fg, 'yellow'), context);
231239
}
232240
else {
233-
this.renderGameObject(room.getObject(obj.x - 1, obj.y), context);
234-
this.renderGameObject(room.getObject(obj.x + 1, obj.y), context);
235-
this.renderGameObject(room.getObject(obj.x, obj.y - 1), context);
236-
this.renderGameObject(room.getObject(obj.x, obj.y + 1), context);
237-
238-
this.renderGameObject(room.getObject(obj.x - 1, obj.y - 1), context);
239-
this.renderGameObject(room.getObject(obj.x + 1, obj.y - 1), context);
240-
this.renderGameObject(room.getObject(obj.x - 1, obj.y + 1), context);
241-
this.renderGameObject(room.getObject(obj.x + 1, obj.y + 1), context);
241+
// Render in all 4 cardinal directions
242+
if (!(obj.x == 0)) this.renderGameObject(room.getObject(obj.x - 1, obj.y), context);
243+
if (!(obj.x == room.getWidth() - 1)) this.renderGameObject(room.getObject(obj.x + 1, obj.y), context);
244+
if (!(obj.y == 0)) this.renderGameObject(room.getObject(obj.x, obj.y - 1), context);
245+
if (!(obj.y == room.getHeight() - 1)) this.renderGameObject(room.getObject(obj.x, obj.y + 1), context);
246+
247+
// Render diagonals
248+
if (!(obj.x == 0 || obj.y == 0))
249+
this.renderGameObject(room.getObject(obj.x - 1, obj.y - 1), context);
250+
if (!(obj.x == room.getWidth() - 1|| obj.y == 0))
251+
this.renderGameObject(room.getObject(obj.x + 1, obj.y - 1), context);
252+
if (!(obj.x == 0 || obj.x == room.getHeight() - 1))
253+
this.renderGameObject(room.getObject(obj.x - 1, obj.y + 1), context);
254+
if (!(obj.x == room.getWidth() - 1 || obj.y == room.getHeight() - 1))
255+
this.renderGameObject(room.getObject(obj.x + 1, obj.y + 1), context);
242256
}
243257
}
244258

0 commit comments

Comments
 (0)