This commit is contained in:
unintendedfraud
2024-01-27 22:47:22 +01:00
parent 227c0021fe
commit 6e7ab861c9
5 changed files with 95 additions and 54 deletions
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "rusty-roguelike"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bracket-lib = "~0.8.1"
+36
View File
@@ -0,0 +1,36 @@
# Rusty Roguelike
Roguelike following the book "Hands-on Rust".
## Short description
A dungeon crawler with procedurally generated levels, monsters of increasing difficulty and turn-based movement.
## Story
The hero's hometown is suffering from a plague of monsters. Welling up from the deep, they seem unstoppable. Legend
tells of the Amulet of Yala that can be used to stem the tide. After a long night at the tavern, the hero promises to
save the day - and set forth into the dungeon.
## Basic game loops
1. Enter dungeon level
2. Explore, revealing the map
3. Encounter ennemies from whom the players decide to fight or flee from
4. Find power-ups and use them to strenghten the player
5. Locate the exit to the level, back to 1
## MVP
1. Create a basic dungeon map
2. Place the player and let them walk around
3. Spawn monsters, draw them, and let the player kill them by walking into them
4. Add health and a combat system that uses it
5. Add healing potions
6. Display a "Game Over" screen when the player dies
7. Add the Amulet of Yala to the level and let the player wins by reaching it
## Stretch goals
1. Add field of view
2. Add more interesting dungeon designs
3. Add some dungeon themes
4. Add multiple layers to the dungeon, with the amulet on the last one
5. Add varied weapons to the game
6. Move to a data driven design for spawning ennemies
7. Consider some visuals effects to make combats more visceral
8. Consider keeping scores
+42
View File
@@ -0,0 +1,42 @@
mod map;
mod player;
mod prelude {
pub use bracket_lib::prelude::*;
pub use crate::map::*;
pub use crate::player::*;
pub const SCREEN_WIDTH: i32 = 80;
pub const SCREEN_HEIGHT: i32 = 50;
}
use prelude::*;
struct State {
map: Map,
player: Player,
}
impl State {
fn new() -> Self {
return Self {
map: Map::new(),
player: Player::new(Point { x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 }),
};
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut BTerm) {
ctx.cls();
self.player.update(ctx, &self.map);
self.map.render(ctx);
self.player.render(ctx);
}
}
fn main() -> BError {
let context = BTermBuilder::simple80x50().with_title("Dungeon Crawler").with_fps_cap(30.0).build()?;
return main_loop(context, State::new());
}
+57
View File
@@ -0,0 +1,57 @@
pub use crate::prelude::*;
const NUM_TILES: usize = (SCREEN_WIDTH * SCREEN_HEIGHT) as usize;
#[derive(Copy, Clone, PartialEq)]
pub enum TileType {
Floor,
Wall,
}
pub struct Map {
pub tiles: Vec<TileType>,
}
pub fn map_idx(x: i32, y: i32) -> usize {
return ((y * SCREEN_WIDTH) + x) as usize;
}
impl Map {
pub fn new() -> Self {
return Self {
tiles: vec![TileType::Floor; NUM_TILES],
}
}
pub fn in_bounds(&self, point: Point) -> bool {
return point.x >= 0 && point.x < SCREEN_WIDTH && point.y >= 0 && point.y < SCREEN_HEIGHT;
}
pub fn can_enter_tile(&self, point: Point) -> bool {
return self.in_bounds(point) && self.tiles[map_idx(point.x, point.y)] == TileType::Floor;
}
pub fn try_idx(&self, point: Point) -> Option<usize> {
if self.in_bounds(point) {
return Some(map_idx(point.x, point.y));
} else {
return None;
}
}
pub fn render(&self, ctx: &mut BTerm) {
for y in 0..SCREEN_HEIGHT {
for x in 0..SCREEN_WIDTH {
let idx = map_idx(x, y);
match self.tiles[idx] {
TileType::Floor => {
ctx.set(x, y, YELLOW, BLACK, to_cp437('.'));
}
TileType::Wall => {
ctx.set(x, y, GREEN, BLACK, to_cp437('#'));
}
}
}
}
}
}
+42
View File
@@ -0,0 +1,42 @@
use crate::prelude::*;
pub struct Player {
pub hp: i32,
pub position: Point,
}
impl Player {
pub fn new(point: Point) -> Self {
return Self {
hp: 100,
position: point,
};
}
pub fn update(&mut self, ctx: &mut BTerm, map: &Map) {
if let Some(key) = ctx.key {
let delta = match key {
VirtualKeyCode::Up => Point::new(0, -1),
VirtualKeyCode::Right => Point::new(1, 0),
VirtualKeyCode::Down => Point::new(0, 1),
VirtualKeyCode::Left => Point::new(-1, 0),
_ => Point::zero(),
};
let new_pos = self.position + delta;
if map.can_enter_tile(new_pos) {
self.position = new_pos;
}
}
}
pub fn render(&self, ctx: &mut BTerm) {
return ctx.set(
self.position.x,
self.position.y,
WHITE,
BLACK,
to_cp437('@')
);
}
}