Implement randomized levels
This commit is contained in:
parent
29a1a2bc7f
commit
62905dc330
4 changed files with 216 additions and 82 deletions
56
src/levels.rs
Normal file
56
src/levels.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{constants::*, tile_data::TileType};
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum Level {
|
||||||
|
Normal([&'static str; MAP_HEIGHT]),
|
||||||
|
Randomized(HashMap<TileType, u32>),
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_level(index: u32) -> Level {
|
||||||
|
match index {
|
||||||
|
0 => Level::Randomized(HashMap::from([
|
||||||
|
(TileType::Slow, 15),
|
||||||
|
(TileType::Whip, 1),
|
||||||
|
(TileType::Stairs, 3),
|
||||||
|
(TileType::Gem, 8),
|
||||||
|
])),
|
||||||
|
1 => Level::Normal([
|
||||||
|
"LXXX2 2 2 2 2 2 2 2 2 2 2 2 2 +",
|
||||||
|
"+XXX 2 2 2 2 2 2 2 2 XXX ",
|
||||||
|
" XXX2 2 2 2 2 2 2 2 2 2 2 XTX ",
|
||||||
|
"+XXX 2 2 2 2 2 2 2 XXX ",
|
||||||
|
" XXX 2 2 2 2 2 2 2 2 2 2 ",
|
||||||
|
"+XXX 2 ",
|
||||||
|
" XXX 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 2 ",
|
||||||
|
"+XXX2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 ",
|
||||||
|
" XXX 2 XXX2 W 2XXX 2 ",
|
||||||
|
"+XXX 2 XXX 2 FFFFF 2 XXX 2 2 ",
|
||||||
|
" XXX2 2 XXX F111F XXX ",
|
||||||
|
"+XXX2 XXX+ 3 3 W F1P1F W 3 3 +XXX 2 ",
|
||||||
|
"XXXX 2 XXX F111F XXX 2 2",
|
||||||
|
"WXXX 2 XXX 2 FFFFF 2 XXX 2 ",
|
||||||
|
"WXXX2 XXX2 W 2XXX 2 ",
|
||||||
|
"WXXX 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 ",
|
||||||
|
"WXXX 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 ",
|
||||||
|
"WXXX2 2 ",
|
||||||
|
"WXXX 2 2 2 2 2 2 2 2 2 2 2 ",
|
||||||
|
"XXXX 2 2 2 2 2 2 2 2 2 2 2 XXX ",
|
||||||
|
"XXXX2 2 2 2 2 2 2 2 2 2 2 2 XTX ",
|
||||||
|
"XXXX 2 2 2 2 2 2 2 2 2 2 2 2 XXX ",
|
||||||
|
"CXXX 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +",
|
||||||
|
]),
|
||||||
|
2 => Level::Randomized(HashMap::from([
|
||||||
|
(TileType::Slow, 60),
|
||||||
|
(TileType::Block, 25),
|
||||||
|
(TileType::Whip, 5),
|
||||||
|
(TileType::Stairs, 2),
|
||||||
|
(TileType::Gem, 10),
|
||||||
|
(TileType::Teleport, 3),
|
||||||
|
])),
|
||||||
|
40 => Level::End,
|
||||||
|
_ => Level::Randomized(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
76
src/main.rs
76
src/main.rs
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod levels;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
mod sidebar;
|
mod sidebar;
|
||||||
mod state;
|
mod state;
|
||||||
|
@ -10,23 +11,17 @@ pub mod tile_data;
|
||||||
pub mod vga_color;
|
pub mod vga_color;
|
||||||
|
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use components::{
|
use components::{Monster, Player, Position, Renderable};
|
||||||
monster::{color_for_kind, glyphs_for_kind, ticks_for_kind, MonsterKind},
|
|
||||||
Monster, Player, Position, Renderable,
|
|
||||||
};
|
|
||||||
use resources::{Clock, LevelNumber, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats};
|
use resources::{Clock, LevelNumber, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use state::State;
|
use state::State;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tile_data::TileType;
|
|
||||||
|
|
||||||
use vga_color as vga;
|
|
||||||
|
|
||||||
fn main() -> BError {
|
fn main() -> BError {
|
||||||
let context = BTermBuilder::simple(80, 25)?
|
let context = BTermBuilder::vga(80, 25)
|
||||||
|
// .with_tile_dimensions(8, 16)
|
||||||
.with_fps_cap(60.0)
|
.with_fps_cap(60.0)
|
||||||
.with_title("Kroz")
|
.with_title("Kroz")
|
||||||
.with_tile_dimensions(8, 16)
|
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let mut ss = SoundOutput::new();
|
let mut ss = SoundOutput::new();
|
||||||
|
@ -57,69 +52,10 @@ fn main() -> BError {
|
||||||
ecs.register::<Monster>();
|
ecs.register::<Monster>();
|
||||||
ecs.register::<Player>();
|
ecs.register::<Player>();
|
||||||
|
|
||||||
let map = Map::new();
|
let map = Map::from_level(levels::get_level(0));
|
||||||
let mut rng = RandomNumberGenerator::new();
|
map.spawn_entities(&mut ecs);
|
||||||
for (i, tile) in &mut map.get_tiles().iter().enumerate() {
|
|
||||||
if rng.roll_dice(1, 16) < 2 && *tile == TileType::Floor {
|
|
||||||
let position = map.index_to_point2d(i);
|
|
||||||
let kind = match rng.range(0, 3) {
|
|
||||||
0 => MonsterKind::Slow,
|
|
||||||
1 => MonsterKind::Medium,
|
|
||||||
2 => MonsterKind::Fast,
|
|
||||||
_ => MonsterKind::Slow,
|
|
||||||
};
|
|
||||||
ecs.create_entity()
|
|
||||||
.with(Position {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y,
|
|
||||||
})
|
|
||||||
.with(Renderable {
|
|
||||||
glyph: *rng.random_slice_entry(&glyphs_for_kind(kind)).unwrap(),
|
|
||||||
fg: color_for_kind(kind),
|
|
||||||
bg: RGB::named(vga::BLACK),
|
|
||||||
})
|
|
||||||
.with(Monster {
|
|
||||||
kind,
|
|
||||||
ticks_until_move: ticks_for_kind(kind),
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let player_start_pos = Point { x: 40, y: 22 };
|
|
||||||
|
|
||||||
ecs.create_entity()
|
|
||||||
.with(Position {
|
|
||||||
x: player_start_pos.x,
|
|
||||||
y: player_start_pos.y,
|
|
||||||
})
|
|
||||||
.with(Renderable {
|
|
||||||
glyph: to_cp437('☻'),
|
|
||||||
fg: RGB::named(vga::YELLOW_BRIGHT),
|
|
||||||
bg: RGB::named(vga::BLACK),
|
|
||||||
})
|
|
||||||
.with(Player {
|
|
||||||
last_moved: Instant::now(),
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ecs.insert(map);
|
ecs.insert(map);
|
||||||
|
|
||||||
ecs.insert(Point::new(player_start_pos.x, player_start_pos.y));
|
|
||||||
|
|
||||||
// for i in 0..10 {
|
|
||||||
// gs.ecs
|
|
||||||
// .create_entity()
|
|
||||||
// .with(Position { x: i * 7, y: 20 })
|
|
||||||
// .with(Renderable {
|
|
||||||
// glyph: to_cp437('Ä'),
|
|
||||||
// fg: RGB::named(vga::RED_BRIGHT),
|
|
||||||
// bg: RGB::named(vga::BLACK),
|
|
||||||
// })
|
|
||||||
// .with(LeftMover {})
|
|
||||||
// .build();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let descent_sounds: Vec<Sound> = (20..100)
|
// let descent_sounds: Vec<Sound> = (20..100)
|
||||||
// .rev()
|
// .rev()
|
||||||
// .flat_map(|x| {
|
// .flat_map(|x| {
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{MAP_HEIGHT, MAP_SIZE, MAP_WIDTH, MAP_X, MAP_Y},
|
components::{monster::*, Monster, Player, Position, Renderable},
|
||||||
|
constants::*,
|
||||||
|
levels::Level,
|
||||||
tile_data::{self, TileType},
|
tile_data::{self, TileType},
|
||||||
vga_color as vga,
|
vga_color as vga,
|
||||||
};
|
};
|
||||||
use bracket_lib::{prelude::*, random::RandomNumberGenerator};
|
use bracket_lib::{prelude::*, random::RandomNumberGenerator};
|
||||||
use specs::Entity;
|
use specs::{Builder, Entity, World, WorldExt};
|
||||||
|
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
tiles: Vec<TileType>,
|
tiles: Vec<TileType>,
|
||||||
|
@ -29,23 +33,137 @@ impl Default for Map {
|
||||||
|
|
||||||
impl Map {
|
impl Map {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut tiles = vec![TileType::Floor; MAP_SIZE];
|
|
||||||
let mut rng = RandomNumberGenerator::new();
|
let mut rng = RandomNumberGenerator::new();
|
||||||
for tile in &mut tiles {
|
|
||||||
if rng.roll_dice(1, 16) < 2 {
|
|
||||||
*tile = TileType::Wall;
|
|
||||||
} else if rng.roll_dice(1, 16) < 2 {
|
|
||||||
*tile = TileType::Block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
tiles,
|
tiles: vec![TileType::Floor; MAP_SIZE],
|
||||||
tile_content: vec![None; MAP_SIZE],
|
tile_content: vec![None; MAP_SIZE],
|
||||||
border_fg: RGB::named(vga::get_by_index(rng.range(8, 15))),
|
border_fg: RGB::named(vga::get_by_index(rng.range(8, 15))),
|
||||||
border_bg: RGB::named(vga::get_by_index(rng.range(1, 7) as usize)),
|
border_bg: RGB::named(vga::get_by_index(rng.range(1, 7) as usize)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_level(level: Level) -> Self {
|
||||||
|
match level {
|
||||||
|
Level::Normal(_data) => todo!(),
|
||||||
|
Level::Randomized(data) => {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
let mut map = Self::new();
|
||||||
|
for (tile, count) in data {
|
||||||
|
for _ in 0..count {
|
||||||
|
loop {
|
||||||
|
let point = Point {
|
||||||
|
x: rng.range(0, MAP_WIDTH as i32),
|
||||||
|
y: rng.range(0, MAP_HEIGHT as i32),
|
||||||
|
};
|
||||||
|
if map.get_tile_at(point) == TileType::Floor {
|
||||||
|
map.set_tile_at(point, tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let point = Point {
|
||||||
|
x: rng.range(0, MAP_WIDTH as i32),
|
||||||
|
y: rng.range(0, MAP_HEIGHT as i32),
|
||||||
|
};
|
||||||
|
if map.get_tile_at(point) == TileType::Floor {
|
||||||
|
map.set_tile_at(point, TileType::Player);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
Level::End => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_entities(&self, ecs: &mut World) {
|
||||||
|
for (index, tile) in self.tiles.iter().enumerate() {
|
||||||
|
let point = self.index_to_point2d(index);
|
||||||
|
match tile {
|
||||||
|
TileType::Player => {
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position {
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
})
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: to_cp437('☻'),
|
||||||
|
fg: RGB::named(vga::YELLOW_BRIGHT),
|
||||||
|
bg: RGB::named(vga::BLACK),
|
||||||
|
})
|
||||||
|
.with(Player {
|
||||||
|
last_moved: Instant::now(),
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
ecs.insert(point);
|
||||||
|
}
|
||||||
|
TileType::Slow => {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position {
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
})
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: *rng
|
||||||
|
.random_slice_entry(&glyphs_for_kind(MonsterKind::Slow))
|
||||||
|
.unwrap(),
|
||||||
|
fg: color_for_kind(MonsterKind::Slow),
|
||||||
|
bg: RGB::named(vga::BLACK),
|
||||||
|
})
|
||||||
|
.with(Monster {
|
||||||
|
kind: MonsterKind::Slow,
|
||||||
|
ticks_until_move: ticks_for_kind(MonsterKind::Slow),
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
TileType::Medium => {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position {
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
})
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: *rng
|
||||||
|
.random_slice_entry(&glyphs_for_kind(MonsterKind::Medium))
|
||||||
|
.unwrap(),
|
||||||
|
fg: color_for_kind(MonsterKind::Medium),
|
||||||
|
bg: RGB::named(vga::BLACK),
|
||||||
|
})
|
||||||
|
.with(Monster {
|
||||||
|
kind: MonsterKind::Medium,
|
||||||
|
ticks_until_move: ticks_for_kind(MonsterKind::Medium),
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
TileType::Fast => {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position {
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
})
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: *rng
|
||||||
|
.random_slice_entry(&glyphs_for_kind(MonsterKind::Fast))
|
||||||
|
.unwrap(),
|
||||||
|
fg: color_for_kind(MonsterKind::Fast),
|
||||||
|
bg: RGB::named(vga::BLACK),
|
||||||
|
})
|
||||||
|
.with(Monster {
|
||||||
|
kind: MonsterKind::Fast,
|
||||||
|
ticks_until_move: ticks_for_kind(MonsterKind::Fast),
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_all_tile_content(&mut self) {
|
pub fn clear_all_tile_content(&mut self) {
|
||||||
self.tile_content.iter_mut().for_each(|tile| {
|
self.tile_content.iter_mut().for_each(|tile| {
|
||||||
*tile = None;
|
*tile = None;
|
||||||
|
|
|
@ -11,9 +11,12 @@ pub struct TileData {
|
||||||
pub blink: bool,
|
pub blink: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(Eq, PartialEq, Copy, Clone, Hash)]
|
||||||
pub enum TileType {
|
pub enum TileType {
|
||||||
Floor, // 0
|
Floor, // 0
|
||||||
|
Slow, // 1
|
||||||
|
Medium, // 2
|
||||||
|
Fast, // 3
|
||||||
Block, // 4
|
Block, // 4
|
||||||
Whip, // 5
|
Whip, // 5
|
||||||
Stairs, // 6
|
Stairs, // 6
|
||||||
|
@ -72,6 +75,27 @@ pub fn tile_data(tile: TileType) -> TileData {
|
||||||
color_bg: vga::BLACK,
|
color_bg: vga::BLACK,
|
||||||
blink: false,
|
blink: false,
|
||||||
},
|
},
|
||||||
|
TileType::Slow => TileData {
|
||||||
|
glyph: 0,
|
||||||
|
serialized_char: '1',
|
||||||
|
color_fg: vga::BLACK,
|
||||||
|
color_bg: vga::BLACK,
|
||||||
|
blink: false,
|
||||||
|
},
|
||||||
|
TileType::Medium => TileData {
|
||||||
|
glyph: 0,
|
||||||
|
serialized_char: '2',
|
||||||
|
color_fg: vga::BLACK,
|
||||||
|
color_bg: vga::BLACK,
|
||||||
|
blink: false,
|
||||||
|
},
|
||||||
|
TileType::Fast => TileData {
|
||||||
|
glyph: 0,
|
||||||
|
serialized_char: '3',
|
||||||
|
color_fg: vga::BLACK,
|
||||||
|
color_bg: vga::BLACK,
|
||||||
|
blink: false,
|
||||||
|
},
|
||||||
TileType::Block => TileData {
|
TileType::Block => TileData {
|
||||||
glyph: to_cp437('▓'),
|
glyph: to_cp437('▓'),
|
||||||
serialized_char: 'X',
|
serialized_char: 'X',
|
||||||
|
|
Loading…
Add table
Reference in a new issue