Skip to content
Snippets Groups Projects
Verified Commit 263bfac0 authored by Louis's avatar Louis :fire:
Browse files

Clean up generators, add blobular & drunkard gens

parent a7fff25f
No related branches found
No related tags found
No related merge requests found
Pipeline #172 passed with stages
in 2 minutes and 34 seconds
Showing
with 512 additions and 284 deletions
...@@ -20,6 +20,7 @@ pub fn start_load_resources(mut loader: AssetTypeLoader) { ...@@ -20,6 +20,7 @@ pub fn start_load_resources(mut loader: AssetTypeLoader) {
loader.load_images(&[("splash.png", "splash")]); loader.load_images(&[("splash.png", "splash")]);
loader.load_audio(&[("splash_sting.mp3", "splash_sting")]); loader.load_audio(&[("splash_sting.mp3", "splash_sting")]);
loader.load_font(&[("fonts/Kaph.ttf", "main")]);
let sheet_config = SpriteSheetConfig::squares(16, SHEET_WIDTH, SHEET_HEIGHT); let sheet_config = SpriteSheetConfig::squares(16, SHEET_WIDTH, SHEET_HEIGHT);
loader.load_spritesheet( loader.load_spritesheet(
&sheet_config, &sheet_config,
......
pub mod actions;
pub mod player; pub mod player;
mod __plugin {
use bevy::app::{App, CoreStage};
use bevy::prelude::Plugin;
use iyes_loopless::prelude::ConditionSet;
use crate::system::flow::AppState;
pub struct ControlPlugin;
impl Plugin for ControlPlugin {
fn build(&self, app: &mut App) {
app.add_system_set_to_stage(
CoreStage::PreUpdate,
ConditionSet::new()
.run_in_state(AppState::InGame)
.with_system(super::player::handle_player_input)
.into(),
);
}
}
}
pub use __plugin::ControlPlugin;
use bevy::math::ivec2;
use bevy::prelude::*;
use crate::entities::lifecycle::Player;
use crate::world::level_map::GridPosition;
pub fn handle_player_input(
input: Res<Input<KeyCode>>,
mut query: Query<&mut GridPosition, With<Player>>,
) {
let mut dx = 0;
let mut dy = 0;
if input.just_released(KeyCode::D) || input.just_released(KeyCode::Right) {
dx += 1;
}
if input.just_released(KeyCode::W) || input.just_released(KeyCode::Up) {
dy += 1;
}
if input.just_released(KeyCode::A) || input.just_released(KeyCode::Left) {
dx -= 1;
}
if input.just_released(KeyCode::S) || input.just_released(KeyCode::Down) {
dy -= 1;
}
for mut position in &mut query {
let next_position = (position.0.as_ivec2()) + ivec2(dx, dy);
**position = next_position.as_uvec2();
}
}
...@@ -3,17 +3,64 @@ use bevy::prelude::*; ...@@ -3,17 +3,64 @@ use bevy::prelude::*;
use iyes_loopless::prelude::AppLooplessStateExt; use iyes_loopless::prelude::AppLooplessStateExt;
use iyes_loopless::state::NextState; use iyes_loopless::state::NextState;
use crate::entities::spawner::EntitySpawner; use crate::assets::AssetHandles;
use crate::system::flow::AppState; use crate::system::flow::AppState;
use crate::world::generation::generate_map; use crate::world::generators::drunkard_corridor::DrunkardGenerator;
use crate::world::level_map::LevelMapBundle; use crate::world::level_map::LevelMapBundle;
pub fn spawn_player(mut spawner: EntitySpawner) { pub fn spawn_player(mut commands: Commands) {
log::info!("Spawning player"); commands.spawn_bundle(LevelMapBundle::generate::<DrunkardGenerator>(150, 150));
spawner.spawn_player(uvec2(25, 25)); }
spawner
.commands #[derive(Component)]
.spawn_bundle(LevelMapBundle::generate(50, 50)); pub struct FpsText;
pub fn spawn_fps_overlay(mut commands: Commands, assets: Res<AssetHandles>) {
commands
.spawn_bundle(NodeBundle {
style: Style {
position_type: PositionType::Absolute,
position: UiRect::new(Val::Px(20.0), Val::Auto, Val::Px(20.0), Val::Auto),
flex_direction: FlexDirection::Row,
..Default::default()
},
color: Color::rgba(0.0, 0.0, 0.0, 0.0).into(),
..Default::default()
})
.with_children(|child_builder| {
child_builder.spawn_bundle(TextBundle {
text: Text::from_section(
"FPS: ",
TextStyle {
font_size: 20.0,
font: assets.font("main"),
color: Color::WHITE,
},
),
..Default::default()
});
child_builder
.spawn_bundle(TextBundle {
text: Text::from_section(
"000",
TextStyle {
font_size: 20.0,
font: assets.font("main"),
color: Color::YELLOW,
},
),
..Default::default()
})
.insert(FpsText);
});
}
pub fn update_fps_text(time: Res<Time>, mut text_query: Query<&mut Text, With<FpsText>>) {
let fps = format!("{:03.0}", 1.0 / time.delta_seconds());
for mut text in &mut text_query {
text.sections[0].value = fps.clone();
}
} }
pub fn skip_menu(mut commands: Commands) { pub fn skip_menu(mut commands: Commands) {
...@@ -24,6 +71,8 @@ pub struct DebugPlugin; ...@@ -24,6 +71,8 @@ pub struct DebugPlugin;
impl Plugin for DebugPlugin { impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_enter_system(AppState::Menu, skip_menu) app.add_enter_system(AppState::Menu, skip_menu)
.add_enter_system(AppState::InGame, spawn_player); .add_enter_system(AppState::Menu, spawn_fps_overlay)
.add_enter_system(AppState::InGame, spawn_player)
.add_system(update_fps_text);
} }
} }
...@@ -5,6 +5,8 @@ use crate::system::flow::AppState; ...@@ -5,6 +5,8 @@ use crate::system::flow::AppState;
#[derive(Debug, Clone, Copy, Component)] #[derive(Debug, Clone, Copy, Component)]
pub struct GameEntity; pub struct GameEntity;
#[derive(Debug, Clone, Copy, Component)]
pub struct Player;
pub fn remove_game_entities(mut commands: Commands, query: Query<Entity, With<GameEntity>>) { pub fn remove_game_entities(mut commands: Commands, query: Query<Entity, With<GameEntity>>) {
for entity in &query { for entity in &query {
......
...@@ -2,7 +2,7 @@ use bevy::ecs::system::SystemParam; ...@@ -2,7 +2,7 @@ use bevy::ecs::system::SystemParam;
use bevy::prelude::*; use bevy::prelude::*;
use crate::assets::AssetHandles; use crate::assets::AssetHandles;
use crate::entities::lifecycle::GameEntity; use crate::entities::lifecycle::{GameEntity, Player};
use crate::system::camera::ChaseCam; use crate::system::camera::ChaseCam;
use crate::system::graphics::LAYER_CREATURE; use crate::system::graphics::LAYER_CREATURE;
use crate::world::level_map::GridPosition; use crate::world::level_map::GridPosition;
...@@ -22,6 +22,7 @@ impl<'w, 's> EntitySpawner<'w, 's> { ...@@ -22,6 +22,7 @@ impl<'w, 's> EntitySpawner<'w, 's> {
entity entity
.insert(ChaseCam) .insert(ChaseCam)
.insert(GameEntity) .insert(GameEntity)
.insert(Player)
.insert(GridPosition(grid_position)); .insert(GridPosition(grid_position));
entity.insert_bundle(SpriteSheetBundle { entity.insert_bundle(SpriteSheetBundle {
......
...@@ -21,5 +21,6 @@ fn main() { ...@@ -21,5 +21,6 @@ fn main() {
>::new()) >::new())
.add_plugin(game_core::debug::DebugPlugin) .add_plugin(game_core::debug::DebugPlugin)
.add_plugin(game_core::world::WorldPlugin) .add_plugin(game_core::world::WorldPlugin)
.add_plugin(game_core::control::ControlPlugin)
.run(); .run();
} }
use fastrand::Rng;
use crate::world::level_map::{Indexer, LevelMap, MapLayer, MapTile};
const TMP_FLOOR_GROUP: usize = 6 * 64;
const TMP_WALL_GROUP: usize = 3 * 64 + 28;
#[derive(Copy, Clone, Debug, Default)]
enum GenerationDirection {
Top,
Bottom,
Left,
Right,
#[default]
None,
}
pub fn generate_map(width: usize, height: usize) -> LevelMap {
generate_map_with_seed(fastrand::u64(0..u64::MAX), width, height)
}
pub fn generate_map_with_seed(seed: u64, width: usize, height: usize) -> LevelMap {
let rng = fastrand::Rng::with_seed(seed);
generate_map_with_rng(&rng, width, height)
}
pub fn generate_map_with_rng(rng: &Rng, width: usize, height: usize) -> LevelMap {
let indexer = Indexer::new(width, height);
let mut floor_layer = vec![0; width * height];
let mut wall_layer = vec![0; width * height];
let mut decoration_layer = vec![0; width * height];
let initial_x = width / 2;
let initial_y = width / 2;
let initial_width = rng.usize(3..=5);
let initial_height = rng.usize(3..=5);
draw_box(
initial_x - initial_width / 2,
initial_y - initial_height / 2,
initial_width,
initial_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
for _ in 0..1000 {
let initial = find_next_door(1000, &floor_layer, &rng, &indexer);
if let Some((next_x, next_y, direction)) = initial {
let size_selection = rng.usize(1..=10);
let (room_width, room_height) = if size_selection < 8 {
(rng.usize(3..=6), rng.usize(3..=6))
} else {
(rng.usize(7..=12), rng.usize(7..=12))
};
// Align room drawing to top left corner of new box
match direction {
GenerationDirection::Top => {
let start_x = next_x - (room_width / 4);
let start_y = next_y + room_height;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Bottom => {
let start_x = next_x - (room_width / 4);
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Left => {
let start_x = next_x - room_width;
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Right => {
let start_x = next_x;
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::None => {}
}
}
}
LevelMap {
width,
height,
layers: vec![
MapLayer::from_sized_list(
width,
height,
floor_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_floor(rest)),
})
.collect(),
),
MapLayer::from_sized_list(
width,
height,
wall_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_wall(rest)),
})
.collect(),
),
MapLayer::from_sized_list(
width,
height,
decoration_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_obstacle(rest)),
})
.collect(),
),
],
}
}
fn find_next_door(
max_attempts: usize,
floor: &Vec<usize>,
rng: &Rng,
idx: &Indexer,
) -> Option<(usize, usize, GenerationDirection)> {
let mut remaining = max_attempts;
let mut found = None;
let mut last_direction = GenerationDirection::None;
while remaining > 0 && found.is_none() {
remaining = remaining.saturating_sub(1);
let target_index = rng.usize(0..floor.len());
let (x, y) = idx.reverse(target_index);
// Edges of the map are unsuitable for new rooms
if x == 0 || x == idx.width() - 1 || y == 0 || y == idx.height() - 1 {
continue;
}
let mut adjacents = 0;
// Check Left
if floor[idx.index(x - 1, y)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Right;
}
// Check Right
if floor[idx.index(x + 1, y)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Left;
}
// Check Top
if floor[idx.index(x, y + 1)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Bottom;
}
// Check Bottom
if floor[idx.index(x, y - 1)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Top;
}
if adjacents == 1 {
found = Some((x, y, last_direction));
break;
}
}
found
}
fn draw_box(
start_x: usize,
start_y: usize,
width: usize,
height: usize,
value: usize,
layer: &mut Vec<usize>,
indexer: &Indexer,
) {
if !validate_box(start_x, start_y, width, height, layer, indexer) {
return;
}
for x in start_x..(start_x + width) {
for y in start_y..(start_y + height) {
let idx = indexer.index(x, y);
layer[idx] = value;
}
}
}
fn validate_box(
start_x: usize,
start_y: usize,
width: usize,
height: usize,
layer: &Vec<usize>,
indexer: &Indexer,
) -> bool {
let map_width = indexer.width();
let map_height = indexer.height();
if start_y + height >= map_height {
return false;
}
if start_x + width >= map_width {
return false;
}
for x in start_x..(start_x + width) {
for y in start_y..(start_y + height) {
let idx = indexer.index(x, y);
if layer[idx] != 0 {
return false;
}
}
}
true
}
use bevy::math::uvec2;
use fastrand::Rng;
use crate::world::generators::utils::{draw_box, GenerationDirection};
use crate::world::generators::{MapGenerator, TMP_FLOOR_GROUP};
use crate::world::level_map::{Indexer, LevelMap, MapLayer, MapTile};
pub struct Blobular;
impl MapGenerator for Blobular {
fn generate(indexer: &Indexer, rng: &Rng) -> LevelMap {
let width = indexer.width();
let height = indexer.height();
let mut floor_layer = vec![0; width * height];
let mut wall_layer = vec![0; width * height];
let mut decoration_layer = vec![0; width * height];
let initial_x = width / 2;
let initial_y = width / 2;
let initial_width = rng.usize(3..=5);
let initial_height = rng.usize(3..=5);
draw_box(
initial_x - (6 / 2),
initial_y - (4 / 2),
6,
4,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
draw_box(0, 0, 6, 10, TMP_FLOOR_GROUP, &mut floor_layer, &indexer);
// draw_box(
// initial_x - initial_width / 2,
// initial_y - initial_height / 2,
// initial_width,
// initial_height,
// TMP_FLOOR_GROUP,
// &mut floor_layer,
// &indexer,
// );
//
for _ in 0..1000 {
let initial = find_next_door(1000, &floor_layer, &rng, &indexer);
if let Some((next_x, next_y, direction)) = initial {
let size_selection = rng.usize(1..=10);
let (room_width, room_height) = if size_selection < 8 {
(rng.usize(3..=6), rng.usize(3..=6))
} else {
(rng.usize(7..=12), rng.usize(7..=12))
};
// Align room drawing to top left corner of new box
match direction {
GenerationDirection::Top => {
let start_x = next_x - (room_width / 4);
let start_y = next_y + room_height - 1;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Bottom => {
let start_x = next_x - (room_width / 4);
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Left => {
let start_x = next_x - (room_width - 1);
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::Right => {
let start_x = next_x;
let start_y = next_y;
draw_box(
start_x,
start_y,
room_width,
room_height,
TMP_FLOOR_GROUP,
&mut floor_layer,
&indexer,
);
}
GenerationDirection::None => {}
}
}
}
LevelMap {
spawn: uvec2(initial_x as u32, initial_y as u32),
width,
height,
layers: vec![
MapLayer::from_sized_list(
width,
height,
floor_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_floor(rest)),
})
.collect(),
),
MapLayer::from_sized_list(
width,
height,
wall_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_wall(rest)),
})
.collect(),
),
MapLayer::from_sized_list(
width,
height,
decoration_layer
.into_iter()
.map(|tile| match tile {
0 => None,
rest => Some(MapTile::new_obstacle(rest)),
})
.collect(),
),
],
}
}
}
fn find_next_door(
max_attempts: usize,
floor: &Vec<usize>,
rng: &Rng,
idx: &Indexer,
) -> Option<(usize, usize, GenerationDirection)> {
let mut remaining = max_attempts;
let mut found = None;
let mut last_direction = GenerationDirection::None;
while remaining > 0 && found.is_none() {
remaining = remaining.saturating_sub(1);
let target_index = rng.usize(0..floor.len());
let (x, y) = idx.reverse(target_index);
// Edges of the map are unsuitable for new rooms
if x == 0 || x == idx.width() - 1 || y == 0 || y == idx.height() - 1 {
continue;
}
let mut adjacents = 0;
// Check Left
if floor[idx.index(x - 1, y)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Right;
}
// Check Right
if floor[idx.index(x + 1, y)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Left;
}
// Check Top
if floor[idx.index(x, y + 1)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Bottom;
}
// Check Bottom
if floor[idx.index(x, y - 1)] != 0 {
adjacents += 1;
last_direction = GenerationDirection::Top;
}
if adjacents == 1 {
found = Some((x, y, last_direction));
break;
}
}
found
}
use bevy::math::uvec2;
use fastrand::Rng;
use crate::world::generators::{MapGenerator, TMP_FLOOR_GROUP};
use crate::world::level_map::{Indexer, LevelMap, MapLayer, MapTile};
pub struct DrunkardGenerator;
impl MapGenerator for DrunkardGenerator {
fn generate(indexer: &Indexer, rng: &Rng) -> LevelMap {
let map_width = indexer.width();
let map_height = indexer.height();
let mut iterations = rng.usize(700..1500);
let mut floor = vec![0; map_width * map_height];
let start = uvec2(map_width as u32 / 2, map_height as u32 / 2);
let mut previous = start.clone();
floor[indexer.index(start.x as usize, start.y as usize)] = TMP_FLOOR_GROUP;
while iterations > 0 {
iterations -= 1;
let mut dx = 0;
let mut dy = 0;
match rng.u8(0..4) {
0 => {
dx += 1;
}
1 => {
dx -= 1;
}
2 => {
dy += 1;
}
3 => {
dy -= 1;
}
_ => unreachable!(),
}
let target_x = (previous.x as i32 + dx).max(0) as usize;
let target_y = (previous.y as i32 + dy).max(0) as usize;
let target_idx = indexer.index(target_x, target_y);
if indexer.index_within(target_idx) {
floor[target_idx] = TMP_FLOOR_GROUP;
previous = uvec2(target_x as u32, target_y as u32);
}
}
LevelMap::new(
map_width,
map_height,
start,
vec![MapLayer::from_sized_list(
map_width,
map_height,
floor
.into_iter()
.map(|tile| {
if tile == 0 {
None
} else {
Some(MapTile::new_floor(tile))
}
})
.collect(),
)],
)
}
}
use fastrand::Rng;
use crate::world::level_map::{Indexer, LevelMap};
pub mod blobular;
pub mod drunkard_corridor;
pub(crate) mod utils;
pub(crate) const TMP_FLOOR_GROUP: usize = 6 * 64;
pub(crate) const TMP_WALL_GROUP: usize = 3 * 64 + 28;
pub trait MapGenerator {
fn generate(indexer: &Indexer, rng: &Rng) -> LevelMap;
}
use crate::world::level_map::Indexer;
#[derive(Copy, Clone, Debug, Default)]
pub enum GenerationDirection {
Top,
Bottom,
Left,
Right,
#[default]
None,
}
pub fn draw_box(
start_x: usize,
start_y: usize,
width: usize,
height: usize,
value: usize,
layer: &mut Vec<usize>,
indexer: &Indexer,
) {
if !validate_box(start_x, start_y, width, height, layer, indexer) {
return;
}
for y in start_y..(start_y + height) {
for x in start_x..(start_x + width) {
let idx = indexer.index(x, y);
layer[idx] = value;
}
}
}
pub fn validate_box(
start_x: usize,
start_y: usize,
width: usize,
height: usize,
layer: &Vec<usize>,
indexer: &Indexer,
) -> bool {
let map_width = indexer.width();
let map_height = indexer.height();
if start_y + height >= map_height {
return false;
}
if start_x + width >= map_width {
return false;
}
for x in start_x..(start_x + width) {
for y in start_y..(start_y + height) {
let idx = indexer.index(x, y);
if layer[idx] != 0 {
return false;
}
}
}
true
}
...@@ -3,6 +3,7 @@ use bevy::prelude::*; ...@@ -3,6 +3,7 @@ use bevy::prelude::*;
use crate::assets::AssetHandles; use crate::assets::AssetHandles;
use crate::entities::lifecycle::GameEntity; use crate::entities::lifecycle::GameEntity;
use crate::entities::spawner::EntitySpawner;
use crate::system::graphics::LAYER_TILE; use crate::system::graphics::LAYER_TILE;
use crate::world::adjacency::get_floor_sprite_offset; use crate::world::adjacency::get_floor_sprite_offset;
use crate::world::level_map::{GridPosition, Indexer, LevelMap, Tile, WORLD_TILE_SIZE}; use crate::world::level_map::{GridPosition, Indexer, LevelMap, Tile, WORLD_TILE_SIZE};
...@@ -10,6 +11,7 @@ use crate::world::level_map::{GridPosition, Indexer, LevelMap, Tile, WORLD_TILE_ ...@@ -10,6 +11,7 @@ use crate::world::level_map::{GridPosition, Indexer, LevelMap, Tile, WORLD_TILE_
pub fn spawn_new_world( pub fn spawn_new_world(
mut commands: Commands, mut commands: Commands,
assets: Res<AssetHandles>, assets: Res<AssetHandles>,
mut spawner: EntitySpawner,
query: Query<(Entity, &LevelMap), Added<LevelMap>>, query: Query<(Entity, &LevelMap), Added<LevelMap>>,
) { ) {
for (entity, map) in &query { for (entity, map) in &query {
...@@ -41,5 +43,7 @@ pub fn spawn_new_world( ...@@ -41,5 +43,7 @@ pub fn spawn_new_world(
} }
}); });
} }
spawner.spawn_player(map.spawn);
} }
} }
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::ops::Deref; use std::ops::{Deref, DerefMut};
use bevy::math::UVec2; use bevy::math::UVec2;
use bevy::prelude::*; use bevy::prelude::*;
use fastrand::Rng; use fastrand::Rng;
use crate::world::adjacency::{BOTTOM, LEFT, NONE, RIGHT, TOP}; use crate::world::adjacency::{BOTTOM, LEFT, NONE, RIGHT, TOP};
use crate::world::generation::{generate_map, generate_map_with_rng, generate_map_with_seed}; use crate::world::generators::MapGenerator;
pub const WORLD_TILE_SIZE: f32 = 16.0; pub const WORLD_TILE_SIZE: f32 = 16.0;
...@@ -24,6 +24,11 @@ impl Deref for GridPosition { ...@@ -24,6 +24,11 @@ impl Deref for GridPosition {
&self.0 &self.0
} }
} }
impl DerefMut for GridPosition {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Take an entity's position on a grid, and sync it up to the transform used to render /// Take an entity's position on a grid, and sync it up to the transform used to render
/// the sprite /// the sprite
...@@ -147,6 +152,18 @@ pub struct LevelMap { ...@@ -147,6 +152,18 @@ pub struct LevelMap {
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub layers: Vec<MapLayer>, pub layers: Vec<MapLayer>,
pub spawn: UVec2,
}
impl LevelMap {
pub fn new(width: usize, height: usize, spawn: UVec2, layers: Vec<MapLayer>) -> Self {
LevelMap {
width,
height,
layers,
spawn,
}
}
} }
#[derive(Bundle)] #[derive(Bundle)]
...@@ -167,14 +184,15 @@ impl LevelMapBundle { ...@@ -167,14 +184,15 @@ impl LevelMapBundle {
} }
} }
pub fn generate(width: usize, height: usize) -> Self { pub fn generate<Gen: MapGenerator>(width: usize, height: usize) -> Self {
Self::new(generate_map(width, height)) Self::generate_with_rng::<Gen>(&Rng::new(), width, height)
} }
pub fn generate_with_seed(seed: u64, width: usize, height: usize) -> Self { pub fn generate_with_seed<Gen: MapGenerator>(seed: u64, width: usize, height: usize) -> Self {
Self::new(generate_map_with_seed(seed, width, height)) Self::generate_with_rng::<Gen>(&Rng::with_seed(seed), width, height)
} }
pub fn generate_with_rng(rng: &Rng, width: usize, height: usize) -> Self { pub fn generate_with_rng<Gen: MapGenerator>(rng: &Rng, width: usize, height: usize) -> Self {
Self::new(generate_map_with_rng(rng, width, height)) let indexer = Indexer::new(width, height);
LevelMapBundle::new(Gen::generate(&indexer, rng))
} }
} }
...@@ -192,7 +210,11 @@ impl Indexer { ...@@ -192,7 +210,11 @@ impl Indexer {
(y * self.width) + x (y * self.width) + x
} }
pub fn reverse(&self, index: usize) -> (usize, usize) { pub fn reverse(&self, index: usize) -> (usize, usize) {
(index / self.width, index % self.width) (index % self.width, index / self.width)
}
pub fn index_within(&self, idx: usize) -> bool {
let (x, y) = self.reverse(idx);
x >= 0 && x < self.width && y >= 0 && y < self.height
} }
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
self.width self.width
......
pub mod adjacency; pub mod adjacency;
pub mod generation; pub mod generators;
pub mod handlers; pub mod handlers;
pub mod level_map; pub mod level_map;
mod __internal { mod __plugin {
use bevy::app::{App, CoreStage, Plugin}; use bevy::app::{App, CoreStage, Plugin};
use iyes_loopless::condition::ConditionSet; use iyes_loopless::condition::ConditionSet;
...@@ -24,4 +24,4 @@ mod __internal { ...@@ -24,4 +24,4 @@ mod __internal {
} }
} }
pub use __internal::WorldPlugin; pub use __plugin::WorldPlugin;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment