Skip to content

Commit

Permalink
GameMap, a random map generator, and collisions.
Browse files Browse the repository at this point in the history
  • Loading branch information
avinashv committed Jul 13, 2023
1 parent a6507af commit d642997
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 8 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ This repository is participating in the 2023 edition of the [RoguelikeDev event]
- Lots of mistakes in the tutorial--in Chapter 3, for example, the entire input handling code is missing imports and declarations.
- I prefered implementing game states in `game-states.ts` as an enum to reference.

### Week 2 - 11 July 2023 - The object system and generating your first map
### [Week 2](https://www.reddit.com/r/roguelikedev/comments/14wuzqa/roguelikedev_does_the_complete_roguelike_tutorial/) - 11 July 2023 - The object system and generating your first map

- Generics? Might be a bit out of my depth here. Currently unclear on how this is anything more than just typing the elements in `Struct.Table`?
- Class boilerplate is really verbose. Set up properties, then tag them in the constructor method arguments, then assign the arguments to the class properties.
- The setup to get a map working with collision detection is a lot faster and easier than the `libtcod` Python tutorial. The rooms-and-corridors method used there is useful, but most of these libraries have excellent map generation methods built-in, and getting something working should be the priority. This tutorial should lean on that too.
- Not a huge fan of just increasing the size of the generated map and terminal size by ~50% for no discernable reason. I assume there is a camera system incoming shortly, but this is going to require a massive refactor anyway, and this simple thing could have waited anyway. I opted to stick with the same size of screen as before, and lowered the number of random walls added to 200.

### Week 3 - 18 July 2023 - Field of view, placing enemies, and attacking

Expand Down
26 changes: 19 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { World, Entity } from 'ecsy';
import * as Components from './components';
import * as Systems from './systems';
import { GameStates } from './game-states';
import { GameMap, TileType } from './map';

/**
* The main game engine.
Expand All @@ -24,6 +25,8 @@ export class Game {

gameState: GameStates;

map = GameMap.CreateRandom(Game.WIDTH, Game.HEIGHT);

constructor() {
this.world = new World();
this.terminal = this.createTerminal();
Expand Down Expand Up @@ -93,16 +96,16 @@ export class Game {
// create an actions map of potential keypresses
const actions = new Map<number, () => void>();
actions.set(Input.KeyCode.LeftArrow, () =>
tryMoveEntity(this.player, { x: -1, y: 0 })
tryMoveEntity(this.player, { x: -1, y: 0 }, this)
);
actions.set(Input.KeyCode.RightArrow, () =>
tryMoveEntity(this.player, { x: 1, y: 0 })
tryMoveEntity(this.player, { x: 1, y: 0 }, this)
);
actions.set(Input.KeyCode.UpArrow, () =>
tryMoveEntity(this.player, { x: 0, y: -1 })
tryMoveEntity(this.player, { x: 0, y: -1 }, this)
);
actions.set(Input.KeyCode.DownArrow, () =>
tryMoveEntity(this.player, { x: 0, y: 1 })
tryMoveEntity(this.player, { x: 0, y: 1 }, this)
);

// if the keypair in the map matches an input key, run the command
Expand Down Expand Up @@ -136,16 +139,25 @@ export class Game {
* @param entity Entity with a Position component
* @param delta Movement vector delta
*/
function tryMoveEntity(entity: Entity, delta: Vector2) {
function tryMoveEntity(entity: Entity, delta: Vector2, game: Game) {
const position = entity.getMutableComponent(Components.Position);

if (!position) {
console.warn('WARNING: Can\'t move an entity without a Position!');
return;
}

position.x += delta.x;
position.y += delta.y;
// calculate the destination
const destination = {
x: position.x + delta.x,
y: position.y + delta.y,
};

// process the movement if the destination is a floor tile
if (game.map.tiles.get(destination) === TileType.Floor) {
position.x = destination.x;
position.y = destination.y;
}
}

// Modify the Window to include the Game object.
Expand Down
51 changes: 51 additions & 0 deletions src/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Struct, Rand } from "malwoden";

export enum TileType {
// all tiles should be truthy
Floor = 1,
Wall,
}

export class GameMap {
tiles: Struct.Table<TileType>;
width: number;
height: number;

constructor(tiles: Struct.Table<TileType>, width: number, height: number) {
this.tiles = tiles;
this.width = width;
this.height = height;
}

static CreateRandom(width: number, height: number): GameMap {
const tiles = new Struct.Table<TileType>(width, height);
const rng = new Rand.AleaRNG();
const randomWalls = 200;

// fill the whole map with floor tiles
tiles.fill(TileType.Floor);

// fill the top and bottom with walls
for (let x = 0; x < width; x++) {
tiles.set({ x, y: 0 }, TileType.Wall);
tiles.set({ x, y: height - 1 }, TileType.Wall);
}

// fill the left and right with walls
for (let y = 0; y < height; y++) {
tiles.set({ x: 0, y }, TileType.Wall);
tiles.set({ x: width - 1, y }, TileType.Wall);
}

// fill random spots with walls
for (let i = 0; i < randomWalls; i++) {
const x = rng.nextInt(1, width - 2);
const y = rng.nextInt(1, height - 2);

tiles.set({ x, y }, TileType.Wall);
}

// return the new GameMap
return new GameMap(tiles, width, height);
}
}
21 changes: 21 additions & 0 deletions src/systems/render.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Color, Terminal } from 'malwoden';
import { World, System, SystemQueries } from 'ecsy';

import { Game } from '../main';
import * as Components from '../components';
import { TileType } from '../map';

/**
* Rendering system
Expand All @@ -26,10 +28,29 @@ export class RenderSystem extends System {
execute() {
// run the query
const { results } = this.queries.renderables;

// declare new glyphs for the map
const floorGlyph = new Terminal.Glyph('.', Color.Green);
const wallGlyph = new Terminal.Glyph('#', Color.Green);

// clear before the render pass
this.game.terminal.clear();

// draw map
for (let x = 0; x < this.game.map.width; x++) {
for (let y = 0; y < this.game.map.height; y++) {
// get the current tile
const tile = this.game.map.tiles.get({ x, y });

// draw the correct tile
if (tile === TileType.Floor) {
this.game.terminal.drawGlyph({ x, y }, floorGlyph);
} else if (tile === TileType.Wall) {
this.game.terminal.drawGlyph({ x, y }, wallGlyph);
}
}
}

// draw each entity
for (const entity of results) {
const position = entity.getComponent(Components.Position)!;
Expand Down

0 comments on commit d642997

Please sign in to comment.