work on level generator

This commit is contained in:
Joachim Lusiardi 2024-10-30 08:56:45 +01:00
parent 7f288dbcd1
commit c6492c28c2
5 changed files with 79 additions and 37 deletions

View File

@ -73,7 +73,8 @@ pub fn get_room_type_per_level() -> Vec<HashMap<RoomType, std::ops::RangeInclusi
let tmp = [ let tmp = [
// level 1 // level 1
vec![ vec![
(RoomType::EmptyRoom, 75), (RoomType::EmptyRoom, 50),
(RoomType::ArtifactRoom, 25),
(RoomType::MonsterRoom, 5), (RoomType::MonsterRoom, 5),
(RoomType::BasicRoom, 20), (RoomType::BasicRoom, 20),
], ],

View File

@ -57,6 +57,7 @@ impl Level {
return (None, None, None); return (None, None, None);
} }
if !self.discovered[x][y] { if !self.discovered[x][y] {
#[cfg(test)]
return (Some(StructureElement::Unknown), None, None); return (Some(StructureElement::Unknown), None, None);
} }
let search_pos = &Position::new(self.level, x, y); let search_pos = &Position::new(self.level, x, y);

View File

@ -2,15 +2,19 @@ use petgraph::algo::min_spanning_tree;
use petgraph::data::*; use petgraph::data::*;
use petgraph::graph::Graph; use petgraph::graph::Graph;
use petgraph::graph::UnGraph; use petgraph::graph::UnGraph;
use std::cmp::max;
use std::cmp::min;
use std::ops::Range;
use rand::Rng; use rand::Rng;
use rand::{rngs::ThreadRng, seq::SliceRandom}; use rand::{rngs::ThreadRng, seq::SliceRandom};
use crate::artifacts::Artifact;
use crate::artifacts::Chest;
use crate::artifacts::Potion;
use crate::constants::get_monsters_per_level;
use crate::constants::ROOM_HEIGHT; use crate::constants::ROOM_HEIGHT;
use crate::constants::ROOM_WIDTH; use crate::constants::ROOM_WIDTH;
use crate::monster::create_monster_by_type;
use crate::monster::Monster;
use crate::position::Position;
use crate::room::Connection; use crate::room::Connection;
use crate::{ use crate::{
constants::{ constants::{
@ -23,6 +27,7 @@ use crate::{
pub struct LevelGenerator { pub struct LevelGenerator {
level: usize, level: usize,
rooms: [[Room; ROOMS_VERTICAL]; ROOMS_HORIZONAL], rooms: [[Room; ROOMS_VERTICAL]; ROOMS_HORIZONAL],
rng: ThreadRng,
} }
enum Direction { enum Direction {
Horizontal, Horizontal,
@ -103,7 +108,7 @@ impl LevelGenerator {
} }
} }
// debug print a text view of the dungeon
println!(" 0 1 2 3 4 5 6 7"); println!(" 0 1 2 3 4 5 6 7");
for r in 0..ROOMS_VERTICAL { for r in 0..ROOMS_VERTICAL {
print!("{} ", r); print!("{} ", r);
@ -174,9 +179,9 @@ impl LevelGenerator {
if src_node_col == tgt_node_col { if src_node_col == tgt_node_col {
// println!("Down"); // println!("Down");
let start_col = src_node_col * ROOM_WIDTH+ROOM_WIDTH/2; let start_col = src_node_col * ROOM_WIDTH + ROOM_WIDTH / 2;
let start_row = src_node_row * ROOM_HEIGHT+ROOM_HEIGHT; let start_row = src_node_row * ROOM_HEIGHT + ROOM_HEIGHT;
let end_col = tgt_node_col * ROOM_WIDTH+ROOM_WIDTH/2; let end_col = tgt_node_col * ROOM_WIDTH + ROOM_WIDTH / 2;
let end_row = tgt_node_row * ROOM_HEIGHT; let end_row = tgt_node_row * ROOM_HEIGHT;
rooms[src_node_col][src_node_row].connection_down = Some(Connection { rooms[src_node_col][src_node_row].connection_down = Some(Connection {
start_pos: (start_col, start_row), start_pos: (start_col, start_row),
@ -184,10 +189,10 @@ impl LevelGenerator {
}); });
} else { } else {
// println!("Right"); // println!("Right");
let start_col = src_node_col * ROOM_WIDTH+ROOM_WIDTH; let start_col = src_node_col * ROOM_WIDTH + ROOM_WIDTH;
let start_row = src_node_row * ROOM_HEIGHT+ROOM_HEIGHT/2; let start_row = src_node_row * ROOM_HEIGHT + ROOM_HEIGHT / 2;
let end_col = tgt_node_col * ROOM_WIDTH; let end_col = tgt_node_col * ROOM_WIDTH;
let end_row = tgt_node_row * ROOM_HEIGHT+ROOM_HEIGHT/2; let end_row = tgt_node_row * ROOM_HEIGHT + ROOM_HEIGHT / 2;
rooms[src_node_col][src_node_row].connection_right = Some(Connection { rooms[src_node_col][src_node_row].connection_right = Some(Connection {
start_pos: (start_col, start_row), start_pos: (start_col, start_row),
end_pos: (end_col, end_row), end_pos: (end_col, end_row),
@ -195,7 +200,26 @@ impl LevelGenerator {
} }
} }
LevelGenerator { level, rooms } LevelGenerator {
level,
rooms,
rng,
}
}
fn select_monster(position: Position, rng: &mut ThreadRng) -> Box<dyn Monster> {
let level = position.get_level();
let value = rng.gen_range(1..=100);
let t = get_monsters_per_level();
if level < t.len() {
for (mtype, range) in &t[level] {
if range.contains(&value) {
return create_monster_by_type(mtype, position);
}
}
}
panic!("no monster selectable!");
} }
fn select_room_type(level: usize, rng: &mut ThreadRng) -> RoomType { fn select_room_type(level: usize, rng: &mut ThreadRng) -> RoomType {
@ -216,18 +240,37 @@ impl LevelGenerator {
let mut structure = [[StructureElement::Wall; LEVEL_HEIGHT]; LEVEL_WIDTH]; let mut structure = [[StructureElement::Wall; LEVEL_HEIGHT]; LEVEL_WIDTH];
let mut start_pos = (0, 0); let mut start_pos = (0, 0);
let mut end_pos = (0, 0); let mut end_pos = (0, 0);
let mut monsters: Vec<Box<dyn Monster>> = Vec::with_capacity(10);
let mut artifacts: Vec<Box<dyn Artifact>> = Vec::with_capacity(10);
for col in 0..ROOMS_HORIZONAL { for col in 0..ROOMS_HORIZONAL {
for row in 0..ROOMS_VERTICAL { for row in 0..ROOMS_VERTICAL {
let position = self.rooms[col][row].render(&mut structure, col, row); let room = self.rooms[col][row];
if self.rooms[col][row].kind == RoomType::Start let position = room.render(&mut structure, col, row);
|| self.rooms[col][row].kind == RoomType::StairUp match room.kind {
{ RoomType::Start => {start_pos=position},
start_pos = position; RoomType::End => {end_pos=position},
RoomType::StairUp => {start_pos=position},
RoomType::StairDown => {end_pos=position},
RoomType::BasicRoom => {},
RoomType::ArtifactRoom => {
match self.rng.gen_range(1..=100) {
1..=50 => {
artifacts
.push(Box::new(Chest::new(Position::new(self.level, position.0, position.1))));
} }
if self.rooms[col][row].kind == RoomType::End _ => {
|| self.rooms[col][row].kind == RoomType::StairDown artifacts
{ .push(Box::new(Potion::new(Position::new(self.level, position.0, position.1))));
end_pos = position; }
};
},
RoomType::MonsterRoom => {
monsters.push(LevelGenerator::select_monster(
Position::new(self.level, position.0, position.1),
&mut self.rng,
));
},
RoomType::EmptyRoom => {},
} }
} }
} }
@ -247,8 +290,8 @@ impl LevelGenerator {
level: self.level, level: self.level,
structure, structure,
discovered: [[false; LEVEL_HEIGHT]; LEVEL_WIDTH], discovered: [[false; LEVEL_HEIGHT]; LEVEL_WIDTH],
monsters: vec![], monsters,
artifacts: vec![], artifacts,
start: start_pos, start: start_pos,
end: end_pos, end: end_pos,
rng: rand::thread_rng(), rng: rand::thread_rng(),

View File

@ -1,6 +1,6 @@
use ratatui::buffer::Buffer; use ratatui::buffer::Buffer;
use ratatui::layout::Rect; use ratatui::layout::Rect;
use ratatui::style::Color; use ratatui::style::{Color, Modifier, Style};
use ratatui::widgets::{StatefulWidget, Widget}; use ratatui::widgets::{StatefulWidget, Widget};
use crate::game::Game; use crate::game::Game;
@ -15,6 +15,9 @@ impl LevelWidget {
fn set_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) { fn set_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) {
buf[(x, y)].set_symbol(symbol).set_bg(bg).set_fg(fg); buf[(x, y)].set_symbol(symbol).set_bg(bg).set_fg(fg);
} }
fn set_bold_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) {
buf[(x, y)].set_symbol(symbol).set_bg(bg).set_fg(fg).set_style(Style::new().add_modifier(Modifier::BOLD));
}
} }
impl StatefulWidget for LevelWidget { impl StatefulWidget for LevelWidget {
@ -46,7 +49,7 @@ impl StatefulWidget for LevelWidget {
} }
StructureElement::Wall => { StructureElement::Wall => {
// TODO add fancy walls with https://en.wikipedia.org/wiki/Box-drawing_characters // TODO add fancy walls with https://en.wikipedia.org/wiki/Box-drawing_characters
self.set_cell(buf, x, y, "#", FG_BROWN, Color::Gray); self.set_cell(buf, x, y, "#", FG_BROWN, FG_BROWN);
} }
StructureElement::Floor => { StructureElement::Floor => {
self.set_cell(buf, x, y, " ", FG_BROWN, Color::Gray); self.set_cell(buf, x, y, " ", FG_BROWN, Color::Gray);
@ -68,7 +71,7 @@ impl StatefulWidget for LevelWidget {
} }
(_, _, Some(t)) => { (_, _, Some(t)) => {
let (s, c) = t.get_representation(); let (s, c) = t.get_representation();
self.set_cell(buf, x, y, s, c, Color::Gray); self.set_bold_cell(buf, x, y, s, c, Color::Gray);
} }
(None, None, None) => {} (None, None, None) => {}
}; };

View File

@ -119,26 +119,20 @@ impl Room {
match self.kind { match self.kind {
RoomType::Start => { RoomType::Start => {
tgt[left + self.special.0][top + self.special.1] = StructureElement::Start; tgt[left + self.special.0][top + self.special.1] = StructureElement::Start;
(left + self.special.0, top + self.special.1)
} }
RoomType::End => { RoomType::End => {
tgt[left + self.special.0][top + self.special.1] = StructureElement::End; tgt[left + self.special.0][top + self.special.1] = StructureElement::End;
(left + self.special.0, top + self.special.1)
} }
RoomType::StairUp => { RoomType::StairUp => {
tgt[left + self.special.0][top + self.special.1] = StructureElement::StairUp; tgt[left + self.special.0][top + self.special.1] = StructureElement::StairUp;
(left + self.special.0, top + self.special.1)
} }
RoomType::StairDown => { RoomType::StairDown => {
tgt[left + self.special.0][top + self.special.1] = StructureElement::StairDown; tgt[left + self.special.0][top + self.special.1] = StructureElement::StairDown;
}
_ => {}
};
(left + self.special.0, top + self.special.1) (left + self.special.0, top + self.special.1)
} }
RoomType::BasicRoom => (0, 0),
RoomType::ArtifactRoom => (0, 0),
RoomType::MonsterRoom => (0, 0),
RoomType::EmptyRoom => (0, 0),
}
}
} }
#[test] #[test]