Static World Gen and Display for the Overworld (#5)
Co-authored-by: luckyturtledev <git@lukas1818.de> Reviewed-on: #5 Co-authored-by: Dominic <git@msrd0.de> Co-committed-by: Dominic <git@msrd0.de>
This commit is contained in:
parent
5a4451d6a7
commit
8143a99843
13 changed files with 928 additions and 21 deletions
31
src/activities/overworld/mod.rs
Normal file
31
src/activities/overworld/mod.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use comfy::{
|
||||
draw_rect_outline, draw_sprite, main_camera_mut, EngineContext, IVec2, Vec2, RED,
|
||||
WHITE
|
||||
};
|
||||
|
||||
use crate::game::ZLayer;
|
||||
|
||||
pub mod worldgen;
|
||||
|
||||
pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) {
|
||||
for (coords, tile) in state.overworld.iter_tiles() {
|
||||
for (i, texture) in tile.textures().iter().rev().enumerate() {
|
||||
let i = i as i32;
|
||||
draw_sprite(
|
||||
*texture,
|
||||
coords.as_vec2(),
|
||||
WHITE,
|
||||
ZLayer::MapMax - i,
|
||||
Vec2::ONE
|
||||
);
|
||||
draw_rect_outline(coords.as_vec2(), Vec2::ONE, 0.1, RED, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(state: &mut crate::State, _engine: &mut EngineContext<'_>) {
|
||||
let mut camera = main_camera_mut();
|
||||
camera.center = Vec2::ZERO;
|
||||
camera.zoom = 30.0;
|
||||
state.overworld.get_or_generate_tile(IVec2::ZERO);
|
||||
}
|
316
src/activities/overworld/worldgen.rs
Normal file
316
src/activities/overworld/worldgen.rs
Normal file
|
@ -0,0 +1,316 @@
|
|||
// TODO remove this
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::assets::ASSETS;
|
||||
use comfy::{IVec2, TextureHandle, UVec2};
|
||||
use log::info;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub enum MovementCost {
|
||||
/// No movement possible - cost infinitely high.
|
||||
Infinite,
|
||||
/// There is a path for this movement - movement is cheap.
|
||||
Path,
|
||||
/// There is no path and no obstacle.
|
||||
Default,
|
||||
/// There is an obstacle (i.e. fence) - movement is expensive.
|
||||
Obstacle
|
||||
}
|
||||
|
||||
#[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<TextureHandle> {
|
||||
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!()
|
||||
}
|
||||
pub fn movement_cost_left(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn movement_cost_right(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn movement_cost_up(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn movement_cost_down(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn can_enter_house(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of a chunk (both width and height). This value squared gives the amount of
|
||||
/// tiles in the chunk.
|
||||
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],
|
||||
/// All paths that leave this chunk on the left hand side.
|
||||
paths_left: Vec<u32>,
|
||||
/// All paths that leave this chunk on the right hand side.
|
||||
paths_right: Vec<u32>,
|
||||
/// All paths that leave this chunk on the bottom side.
|
||||
paths_bottom: Vec<u32>,
|
||||
/// All paths that leave this chunk on the top side.
|
||||
paths_top: Vec<u32>
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub fn generate_chunk() -> Self {
|
||||
info!("gen chunk");
|
||||
// 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)
|
||||
}
|
||||
|
||||
/// iterate over all tiles and its local chunk coords
|
||||
pub fn iter_tiles(&self) -> impl Iterator<Item = (UVec2, &Tile)> {
|
||||
self.tiles.iter().enumerate().map(|(i, tile)| {
|
||||
let i = i as u32;
|
||||
(UVec2::new(i % CHUNK_SIZE, i / CHUNK_SIZE), tile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Overworld {
|
||||
chunks: HashMap<IVec2, Chunk>
|
||||
}
|
||||
|
||||
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 (chunk_coords, local_chunk_coords) =
|
||||
world_to_chunk_and_local_coords(world_coords);
|
||||
|
||||
let chunk = self.chunks.get(&chunk_coords)?;
|
||||
chunk.get_tile(local_chunk_coords)
|
||||
}
|
||||
|
||||
pub 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
|
||||
pub fn iter_tiles(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||
self.chunks.iter().flat_map(|(chunk_coords, chunk)| {
|
||||
chunk.iter_tiles().map(|(local_coords, tile)| {
|
||||
// never fail because chunksize fits alswas into i32
|
||||
let local_coords: IVec2 = local_coords.try_into().unwrap();
|
||||
(local_coords + (*chunk_coords * CHUNK_SIZE as i32), tile)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue