diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..43649ae --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,58 @@ +use std::{collections::HashMap, ops::RangeInclusive}; + +use crate::monster::MonsterTypes; + +/// the number of rooms in vertical direction +pub const ROOMS_VERTICAL: usize = 7; +/// the number of rooms in horizontal direction +pub const ROOMS_HORIZONTAL: usize = 7; + +/// the width of a room in the grid of rooms (number of characters) +pub const ROOM_WIDTH: usize = 9; +/// the height of a room in the grid of rooms (number of characters) +pub const ROOM_HEIGHT: usize = 6; + +/// How many levels does the dungeon have? +pub const LEVELS: usize = 3; + +/// length of a game frame in ms +pub const FRAME_LENGTH: u64 = 100; + +/// define the minimum width of a terminal to run the game, must be at least the level width plus some space for stats and messages +pub const MIN_WIDTH: u16 = 120; +/// define the minimum height of a terminal to run the game, this must be at least the level height! +pub const MIN_HEIGHT: u16 = LEVEL_HEIGHT as u16; + +/// the calculated width of a level +pub const LEVEL_WIDTH: usize = 1 + ROOMS_VERTICAL * ROOM_WIDTH; +/// the calculated height of a level +pub const LEVEL_HEIGHT: usize = 1 + ROOMS_HORIZONTAL * ROOM_HEIGHT; + +pub fn get_monsters_per_level() -> Vec>> { + let tmp = vec![ + // level 1 + vec![(MonsterTypes::Rat, 50), (MonsterTypes::Spider, 50)], + // level 2 + vec![(MonsterTypes::Rat, 50), (MonsterTypes::Snake, 50)], + // level 3 + vec![(MonsterTypes::Orc, 33), (MonsterTypes::Skeleton, 33), (MonsterTypes::Snake, 33)], + ]; + if tmp.len() != LEVELS { + panic!("Only {} monster sets defined for {} levels!", tmp.len(), LEVELS); + } + let mut result: Vec>> = vec![]; + for (idx, level) in tmp.iter().enumerate() { + let mut sum = 0; + let mut map: HashMap> = HashMap::new(); + for monster in level { + map.insert(monster.0, RangeInclusive::new(sum+1, sum+monster.1)); + sum += monster.1; + + } + if sum != 100 { + panic!("all percentages must add to 100 (was {}) per level, error in level {}!", sum, idx+1); + } + result.push(map); + } + result +} diff --git a/src/game.rs b/src/game.rs index 5918854..a96cfe8 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,11 +1,9 @@ +use crate::constants::LEVELS; use crate::level::{Level, StructureElement}; use crate::level_generator::LevelGenerator; use crate::player::Player; use crate::position::{HasPosition, Position}; -/// How many levels does the dungeon have? -pub const LEVELS: usize = 2; - #[derive(PartialEq)] /// represents a state of a game pub enum GameState { diff --git a/src/level.rs b/src/level.rs index b53514e..738755f 100644 --- a/src/level.rs +++ b/src/level.rs @@ -6,10 +6,8 @@ use rand::Rng; use crate::artifacts::Artifact; #[cfg(test)] use crate::artifacts::{Chest, Potion}; -use crate::level_generator::ROOMS_HORIZONTAL; -use crate::level_generator::ROOMS_VERTICAL; -use crate::level_generator::ROOM_HEIGHT; -use crate::level_generator::ROOM_WIDTH; +use crate::constants::LEVEL_HEIGHT; +use crate::constants::LEVEL_WIDTH; use crate::monster::Monster; #[cfg(test)] use crate::monster::{Orc, Rat}; @@ -17,9 +15,6 @@ use crate::player::Player; use crate::position::HasPosition; use crate::position::Position; -pub const LEVEL_WIDTH: usize = 1 + ROOMS_VERTICAL * ROOM_WIDTH; -pub const LEVEL_HEIGHT: usize = 1 + ROOMS_HORIZONTAL * ROOM_HEIGHT; - #[derive(Copy, Clone, Debug, PartialEq)] pub enum StructureElement { Start, diff --git a/src/level_generator.rs b/src/level_generator.rs index 0ff81c6..1a80d1f 100644 --- a/src/level_generator.rs +++ b/src/level_generator.rs @@ -1,5 +1,4 @@ use std::cmp::{max, min}; -use std::collections::HashMap; use std::ops::Range; use petgraph::algo::min_spanning_tree; @@ -11,16 +10,13 @@ use rand::rngs::ThreadRng; use rand::Rng; use crate::artifacts::{Artifact, Chest, Potion}; +use crate::constants::{ + get_monsters_per_level, ROOMS_HORIZONTAL, ROOMS_VERTICAL, ROOM_HEIGHT, ROOM_WIDTH, +}; use crate::level::{Level, StructureElement}; -use crate::monster::{create_monster_by_type, Monster, MonsterTypes, Orc, Rat, Snake}; +use crate::monster::{create_monster_by_type, Monster, Orc, Rat, Snake}; use crate::position::Position; -pub const ROOMS_VERTICAL: usize = 7; -pub const ROOMS_HORIZONTAL: usize = 7; - -pub const ROOM_WIDTH: usize = 9; -pub const ROOM_HEIGHT: usize = 6; - #[derive(PartialEq, Copy, Clone)] enum RoomType { Start, @@ -266,15 +262,7 @@ impl LevelGenerator { let level = position.get_level(); let value = rng.gen_range(1..=100); - let t = [ - // level 0 - HashMap::from([(MonsterTypes::Rat, 1..=100)]), - // level 1 - HashMap::from([(MonsterTypes::Rat, 1..=50), (MonsterTypes::Snake, 51..=100)]), - // level 2 - HashMap::from([(MonsterTypes::Orc, 1..=100)]), - HashMap::from([(MonsterTypes::Skeleton, 1..=100)]), - ]; + let t = get_monsters_per_level(); if level < t.len() { for (mtype, range) in &t[level] { if range.contains(&value) { diff --git a/src/main.rs b/src/main.rs index d99aefc..e1e64eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,12 @@ use std::io::stdout; use std::io::Result; use std::time::Instant; +use constants::FRAME_LENGTH; +use constants::LEVELS; +use constants::LEVEL_HEIGHT; +use constants::LEVEL_WIDTH; +use constants::MIN_HEIGHT; +use constants::MIN_WIDTH; use crossterm::{ event::{self, KeyCode, KeyEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, @@ -22,6 +28,7 @@ use crate::player::Player; use crate::position::HasPosition; mod artifacts; +mod constants; mod game; mod level; mod level_generator; @@ -30,12 +37,6 @@ mod monster; mod player; mod position; -/// length of a game frame in ms -pub const FRAME_LENGTH: u64 = 100; - -pub const MIN_WIDTH: u16 = 120; -pub const MIN_HEIGHT: u16 = 30; - // fn main() -> Result<()> { let mut game = Game::new(Player::new(realname().as_str(), 30)); @@ -79,15 +80,15 @@ fn main() -> Result<()> { area.width = MIN_WIDTH; } if area.height > MIN_HEIGHT { - area.y = (area.height - level::LEVEL_HEIGHT as u16) / 2; - area.height = level::LEVEL_HEIGHT as u16; + area.y = (area.height - LEVEL_HEIGHT as u16) / 2; + area.height = LEVEL_HEIGHT as u16; } let map_area = Rect { x: area.x, y: area.y, - width: level::LEVEL_WIDTH as u16, - height: level::LEVEL_HEIGHT as u16, + width: LEVEL_WIDTH as u16, + height: LEVEL_HEIGHT as u16, }; frame.render_stateful_widget(LevelWidget {}, map_area, &mut game); @@ -109,12 +110,13 @@ fn main() -> Result<()> { .style(Style::default().bg(Color::Blue)); frame.render_widget( Paragraph::new(format!( - "Health: {}/{}\nExp: {}\nGold: {}\nLevel: {}\nInventory: {}/{}", + "Health: {} of {}\nExp: {}\nGold: {}\nLevel: {} of {}\nInventory used: {} of {}", game.get_player().get_life(), game.get_player().get_max_life(), game.get_player().get_experience(), game.get_player().get_gold(), game.get_player().get_immutable_position().get_level() + 1, + LEVELS, game.get_player().inventory_size().0, game.get_player().inventory_size().1, )) diff --git a/src/monster.rs b/src/monster.rs index 82fbb97..947061f 100644 --- a/src/monster.rs +++ b/src/monster.rs @@ -18,12 +18,13 @@ pub trait Monster: HasPosition { fn damage(&self) -> usize; } -#[derive(CreateMonsters, PartialEq, Eq, Hash)] +#[derive(CreateMonsters, PartialEq, Eq, Hash, Clone, Copy)] pub enum MonsterTypes { Rat, Orc, Snake, Skeleton, + Spider, } macro_rules! create_monster { @@ -43,13 +44,24 @@ create_monster!( Rat, name:"rat".to_string(), life: 2, - symbol: String::from("R"), + symbol: String::from("r"), color: Color::Black, experience_gain: 5, ticks_between_steps: 5, damage_range: 1..=2, ); +create_monster!( + Spider, + name:"spider".to_string(), + life: 3, + symbol: String::from("s"), + color: Color::Blue, + experience_gain: 7, + ticks_between_steps: 7, + damage_range: 2..=3, +); + create_monster!( Orc, name: "orc".to_string(), @@ -89,6 +101,7 @@ pub fn create_monster_by_type(monster_type: &MonsterTypes, position: Position) - MonsterTypes::Orc => Box::new(Orc::new_with_position(position)), MonsterTypes::Snake => Box::new(Snake::new_with_position(position)), MonsterTypes::Skeleton => Box::new(Skeleton::new_with_position(position)), + MonsterTypes::Spider => Box::new(Spider::new_with_position(position)), } }