From f3b8d4403425b357a2eec28a389c6a956cb2d76b Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 6 Jul 2024 18:45:54 +0200 Subject: [PATCH] very basic worldgen --- build.rs | 4 +- src/activities/overworld.rs | 2 +- src/activities/overworld/worldgen.rs | 231 +++++++++++++++++++++++++-- 3 files changed, 218 insertions(+), 19 deletions(-) diff --git a/build.rs b/build.rs index 3615f37..f0593cb 100644 --- a/build.rs +++ b/build.rs @@ -75,14 +75,14 @@ impl AssetsWriter { for asset_name in root.assets.keys() { writeln!( file, - "{indent}\t{}: comfy::TextureHandle,", + "pub {indent}\t{}: comfy::TextureHandle,", asset_name.to_snake_case() )?; } for group_name in root.groups.keys() { writeln!( file, - "{indent}\t{group_name}: &'static {group_name}::Assets," + "pub {indent}\t{group_name}: &'static {group_name}::Assets," )?; } writeln!(file, "{indent}}}")?; diff --git a/src/activities/overworld.rs b/src/activities/overworld.rs index 185c66e..32d4317 100644 --- a/src/activities/overworld.rs +++ b/src/activities/overworld.rs @@ -7,7 +7,7 @@ pub mod worldgen; pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) { draw_circle(vec2(0.0, 0.0), 0.5, GREEN, 0); for (coords, tile) in state.overworld.iter_tiles() { - for (i, texture) in tile.textures.iter().rev().enumerate() { + for (i, texture) in tile.textures().iter().rev().enumerate() { let i = i as i32; draw_sprite( *texture, diff --git a/src/activities/overworld/worldgen.rs b/src/activities/overworld/worldgen.rs index 53f633a..c4aab95 100644 --- a/src/activities/overworld/worldgen.rs +++ b/src/activities/overworld/worldgen.rs @@ -1,5 +1,5 @@ +use crate::assets::ASSETS; use comfy::{IVec2, TextureHandle, UVec2}; - use std::collections::HashMap; pub enum MovementCost { @@ -13,12 +13,66 @@ pub enum MovementCost { Obstacle } -#[derive(Debug)] -pub struct Tile { - pub textures: Vec +#[derive(Clone, Copy, Debug)] +pub enum Tile { + Grass, + + Path { + left: bool, + right: bool, + top: bool, + bottom: bool + }, + + House { + texture: TextureHandle, + door: bool + } } impl Tile { + pub fn textures(&self) -> Vec { + match self { + Self::Grass => vec![ASSETS.overworld.grass], + + Self::Path { + left, + right, + top, + bottom + } => { + let path_texture = match (left, right, top, bottom) { + (true, true, false, false) => ASSETS.overworld.path_horiz, + (false, false, true, true) => ASSETS.overworld.path_vert, + + (true, false, true, false) => ASSETS.overworld.path_top_left, + (true, false, false, true) => ASSETS.overworld.path_bottom_left, + (false, true, true, false) => ASSETS.overworld.path_top_right, + (false, true, false, true) => ASSETS.overworld.path_bottom_right, + + (true, true, true, false) => ASSETS.overworld.path_top_left_right, + (true, true, false, true) => ASSETS.overworld.path_bottom_left_right, + (true, false, true, true) => ASSETS.overworld.path_top_left_bottom, + (false, true, true, true) => ASSETS.overworld.path_top_right_bottom, + + (true, true, true, true) => ASSETS.overworld.path_crossing, + + (true, false, false, false) + | (false, true, false, false) + | (false, false, true, false) + | (false, false, false, true) => panic!("We don't have no dead ends"), + (false, false, false, false) => panic!("I think you meant grass?!?") + }; + + vec![ASSETS.overworld.grass, path_texture] + }, + + Self::House { texture, .. } => { + vec![ASSETS.overworld.grass, *texture] + } + } + } + pub fn can_stand_inside(&self) -> bool { unimplemented!() } @@ -45,6 +99,7 @@ const CHUNK_SIZE: u32 = 100; /// Chunks #[derive(Debug)] +#[allow(dead_code)] pub struct Chunk { /// All tiles within this chunk. tiles: [Tile; (CHUNK_SIZE * CHUNK_SIZE) as usize], @@ -59,6 +114,137 @@ pub struct Chunk { } impl Chunk { + pub fn generate_chunk() -> Self { + // TODO real worldgen + // for the time being we just copy this pre-made house block into the chunk + + fn path(left: bool, right: bool, top: bool, bottom: bool) -> Tile { + Tile::Path { + left, + right, + top, + bottom + } + } + + fn house(texture: TextureHandle) -> Tile { + Tile::House { + texture, + door: false + } + } + + let path_horiz = path(true, true, false, false); + let path_vert = path(false, false, true, true); + let path_crossing = path(true, true, true, true); + let grass = Tile::Grass; + + let block = [ + [ + path_crossing, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_crossing + ], + [ + path_vert, + grass, + grass, + grass, + house(ASSETS.overworld.house_roof_top), + grass, + grass, + grass, + grass, + grass, + path_vert + ], + [ + path_vert, + grass, + grass, + house(ASSETS.overworld.house_roof_mid_left), + house(ASSETS.overworld.house_mid_window), + house(ASSETS.overworld.house_roof_mid_right), + grass, + grass, + grass, + grass, + path_vert + ], + [ + path_vert, + grass, + house(ASSETS.overworld.house_roof_left), + house(ASSETS.overworld.house_mid_window), + house(ASSETS.overworld.house_mid), + house(ASSETS.overworld.house_mid_window), + house(ASSETS.overworld.house_roof_right), + grass, + grass, + grass, + path_vert + ], + [ + path_vert, + grass, + house(ASSETS.overworld.house_bottom_left), + house(ASSETS.overworld.house_bottom_door), + house(ASSETS.overworld.house_bottom_window), + house(ASSETS.overworld.house_bottom_window), + house(ASSETS.overworld.house_bottom_right), + grass, + grass, + grass, + path_vert + ], + [ + path_vert, + grass, + grass, + path(false, true, true, false), + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path_horiz, + path(true, false, true, true) + ], + [ + path_vert, grass, grass, grass, grass, grass, grass, grass, grass, grass, + path_vert + ], + [ + path_vert, grass, grass, grass, grass, grass, grass, grass, grass, grass, + path_vert + ] + ]; + + // and then we copy this pre-made block into our chunk + let mut tiles = [Tile::Grass; (CHUNK_SIZE * CHUNK_SIZE) as usize]; + for y in 0 .. CHUNK_SIZE { + for x in 0 .. CHUNK_SIZE { + let row = block[y as usize % block.len()]; + tiles[(y * CHUNK_SIZE + x) as usize] = row[x as usize % row.len()]; + } + } + Self { + tiles, + paths_left: vec![], + paths_right: vec![], + paths_bottom: vec![], + paths_top: vec![] + } + } + pub fn get_tile(&self, local_chunk_coords: UVec2) -> Option<&Tile> { self.tiles .get((local_chunk_coords.y * CHUNK_SIZE + local_chunk_coords.x) as usize) @@ -78,25 +264,38 @@ pub struct Overworld { chunks: HashMap } +fn world_to_chunk_and_local_coords(world_coords: IVec2) -> (IVec2, UVec2) { + let chunk_coords = IVec2 { + x: world_coords.x.div_euclid(CHUNK_SIZE as _), + y: world_coords.y.div_euclid(CHUNK_SIZE as _) + }; + let local_chunk_coords = UVec2 { + x: world_coords.x.rem_euclid(CHUNK_SIZE as _) as _, + y: world_coords.y.rem_euclid(CHUNK_SIZE as _) as _ + }; + (chunk_coords, local_chunk_coords) +} + impl Overworld { /// Return a [`Tile`] at the given world coordinates, or `None` if that tile has not /// been generated yet. pub fn get_tile(&self, world_coords: IVec2) -> Option<&Tile> { - let tile_coords = IVec2 { - x: world_coords.x.div_euclid(CHUNK_SIZE as _), - y: world_coords.y.div_euclid(CHUNK_SIZE as _) - }; - let local_coords = UVec2 { - x: world_coords.x.rem_euclid(CHUNK_SIZE as _) as _, - y: world_coords.y.rem_euclid(CHUNK_SIZE as _) as _ - }; + let (chunk_coords, local_chunk_coords) = + world_to_chunk_and_local_coords(world_coords); - let chunk = self.chunks.get(&tile_coords)?; - chunk.get_tile(local_coords) + let chunk = self.chunks.get(&chunk_coords)?; + chunk.get_tile(local_chunk_coords) } - fn get_or_generate_tile(&self, world_coords: IVec2) -> &Tile { - unimplemented!() + fn get_or_generate_tile(&mut self, world_coords: IVec2) -> &Tile { + let (chunk_coords, local_chunk_coords) = + world_to_chunk_and_local_coords(world_coords); + + let chunk = self + .chunks + .entry(chunk_coords) + .or_insert_with(Chunk::generate_chunk); + chunk.get_tile(local_chunk_coords).unwrap() } /// iterate over all tiles and its global coords