dungeon-game/src/game.rs

126 lines
4 KiB
Rust
Raw Normal View History

2021-12-28 20:12:45 -06:00
use std::fmt::Display;
2021-12-18 12:22:46 -06:00
use pancurses::Window;
use crate::rooms;
2021-12-18 12:29:07 -06:00
/// A dungeon root.
2021-12-18 12:22:46 -06:00
pub struct Dungeon {
main_branch: DungeonBranch,
}
2021-12-18 12:29:07 -06:00
/// A single branch of a dungeon, which has a number of levels and
/// which can potentially contain passages to other branches.
2021-12-18 12:22:46 -06:00
pub struct DungeonBranch {
config: BranchConfig,
levels: Vec<DungeonLevel>,
}
2021-12-18 12:29:07 -06:00
/// The parameters that characterize a particular dungeon branch.
/// Currently a unit struct because there's only one type of branch,
/// but will later include e.g. architectural styles, good vs. evil &
/// lawful vs. chaotic weights, etc.
2021-12-18 12:22:46 -06:00
pub struct BranchConfig;
2021-12-18 12:29:07 -06:00
/// The size of a dungeon level, in tiles.
2021-12-18 12:22:46 -06:00
pub const LEVEL_SIZE: (usize, usize) = (80, 24);
2021-12-18 12:29:07 -06:00
/// A single level of the dungeon.
2021-12-18 12:22:46 -06:00
pub struct DungeonLevel {
2021-12-18 12:29:07 -06:00
/// The tiles at every position in the level.
2021-12-18 13:06:00 -06:00
tiles: [[DungeonTile; LEVEL_SIZE.0]; LEVEL_SIZE.1],
2021-12-18 12:22:46 -06:00
}
/// The smallest measurable independent location in the dungeon,
2021-12-18 12:29:07 -06:00
/// corresponding to a single character on the screen.
2021-12-28 20:12:45 -06:00
#[derive(Debug, Clone, Copy, PartialEq)]
2021-12-18 12:22:46 -06:00
pub enum DungeonTile {
Floor,
Wall,
Hallway,
}
impl DungeonLevel {
2021-12-18 12:29:07 -06:00
/// Creates a new level in a branch that has the given
/// configuration.
2021-12-28 20:12:45 -06:00
pub fn new(_cfg: &BranchConfig) -> Self {
2021-12-18 13:06:00 -06:00
Self {
2021-12-28 20:12:45 -06:00
tiles: rooms::generate_level(100, &mut rand::thread_rng()),
2021-12-18 13:06:00 -06:00
}
2021-12-18 12:22:46 -06:00
}
2021-12-18 12:29:07 -06:00
/// Draws a level on the display window.
2021-12-18 12:22:46 -06:00
pub fn draw(&self, win: &Window) {
2021-12-28 20:54:01 -06:00
for y in 0..LEVEL_SIZE.1 {
2021-12-18 13:06:00 -06:00
win.mv(y as _, 0);
2021-12-28 20:54:01 -06:00
for x in 0..LEVEL_SIZE.0 {
win.addch(self.render_tile(x, y));
2021-12-18 13:06:00 -06:00
}
}
2021-12-18 12:22:46 -06:00
}
2021-12-28 20:54:01 -06:00
/// Renders the tile at the given coordinates.
pub fn render_tile(&self, x: usize, y: usize) -> char {
match self.tiles[y][x] {
DungeonTile::Floor => '.',
DungeonTile::Wall => {
2022-01-06 14:32:08 -06:00
// Walls are rendered like so:
// - If the wall has any floor tiles to its north or
// south, then it is rendered as '-', because it is
// the north or south wall of a room.
// - Otherwise, if the wall has any floor tiles to its
// east or west, then it is rendered as '|'.
// - Otherwise, if any floor tiles are diagonally
// adjacent to the wall, then the wall is rendered as
// '+', because it is in the corner of a room.
// - Otherwise, no floor tiles are adjacent to the
// wall, therefore it is surrounded by stone and will
// never be discovered by the player, so we don't
// render it at all.
let has_floor = |deltas: &[(i32, i32)]| -> bool {
deltas
.iter()
.map(|(dx, dy)| (x as i32 + dx, y as i32 + dy))
.filter(|(x, y)| {
(0..LEVEL_SIZE.0 as i32).contains(x)
&& (0..LEVEL_SIZE.1 as i32).contains(y)
})
.any(|(x, y)| self.tile(x, y) == &DungeonTile::Floor)
};
if has_floor(&[(0, -1), (0, 1)]) {
2022-01-02 13:17:17 -06:00
'-'
2022-01-06 14:32:08 -06:00
} else if has_floor(&[(-1, 0), (1, 0)]) {
2022-01-02 13:17:17 -06:00
'|'
2022-01-06 14:32:08 -06:00
} else if has_floor(&[(-1, -1), (-1, 1), (1, -1), (1, 1)]) {
'+'
} else {
' '
2021-12-28 20:54:01 -06:00
}
}
DungeonTile::Hallway => '#',
}
}
2022-01-06 14:04:55 -06:00
/// Gets a reference to the tile at the given coordinates. Panics
/// of the coordinates are out of bounds.
pub fn tile(&self, x: i32, y: i32) -> &DungeonTile {
&self.tiles[y as usize][x as usize]
}
2021-12-18 12:22:46 -06:00
}
2021-12-28 20:12:45 -06:00
impl Display for DungeonLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2021-12-28 20:54:01 -06:00
for y in 0..LEVEL_SIZE.1 {
for x in 0..LEVEL_SIZE.0 {
2022-01-01 14:57:33 -06:00
write!(f, "{}", self.render_tile(x, y))?;
2021-12-28 20:12:45 -06:00
}
2022-01-02 13:17:17 -06:00
writeln!(f)?;
2021-12-28 20:12:45 -06:00
}
Ok(())
}
}