diff --git a/Cargo.lock b/Cargo.lock index bb750c8..33c901a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.12", "once_cell", "version_check", "zerocopy", @@ -1126,7 +1126,7 @@ checksum = "7915222f4a08ccc782e08d10b751b42e5f9d786e697d0cb3fd09333cb7e8b6ea" dependencies = [ "ahash", "bevy_utils_proc_macros", - "getrandom", + "getrandom 0.2.12", "hashbrown 0.14.3", "instant", "nonmax", @@ -1316,6 +1316,7 @@ version = "0.1.0" dependencies = [ "bevy", "bevy_egui", + "noise", "num", "partial-min-max", "paste", @@ -2108,6 +2109,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -2117,7 +2129,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2847,7 +2859,7 @@ checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -2961,6 +2973,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "noise" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba869e17168793186c10ca82c7079a4ffdeac4f1a7d9e755b9491c028180e40" +dependencies = [ + "num-traits", + "rand 0.7.3", + "rand_xorshift", +] + [[package]] name = "nom" version = "7.1.3" @@ -3475,6 +3498,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b" +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -3482,8 +3518,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -3493,7 +3539,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -3502,7 +3557,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -3607,7 +3680,7 @@ dependencies = [ "home", "log", "mio", - "rand", + "rand 0.8.5", "tungstenite", "uuid", ] @@ -3639,7 +3712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", - "getrandom", + "getrandom 0.2.12", "libc", "spin", "untrusted", @@ -4342,7 +4415,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.8.5", "rustls", "rustls-native-certs", "rustls-pki-types", @@ -4430,7 +4503,7 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ - "getrandom", + "getrandom 0.2.12", "serde", ] @@ -4468,6 +4541,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/border-wars/Cargo.toml b/crates/border-wars/Cargo.toml index 70e1543..ccb73ba 100644 --- a/crates/border-wars/Cargo.toml +++ b/crates/border-wars/Cargo.toml @@ -13,6 +13,7 @@ workspace = true [dependencies] bevy = "0.12.1" bevy_egui = "0.24.0" +noise = "0.8.2" num = "0.4.1" partial-min-max = "0.4.0" paste = "1.0.14" diff --git a/crates/border-wars/src/map/generation.rs b/crates/border-wars/src/map/generation.rs new file mode 100644 index 0000000..f1ce9ee --- /dev/null +++ b/crates/border-wars/src/map/generation.rs @@ -0,0 +1,98 @@ +//! All functions related to the generation of the map. + +use bevy::prelude::*; +use noise::{NoiseFn, Perlin}; + +use super::hex::*; +use super::{Tile, TilePosition}; + +/// A plugin to handle the map generation. +pub struct MapGenerationPlugin; + +/// The zoom of the map during the generation. +const MAP_GENERATION_SCALE: f32 = 5.; + +impl Plugin for MapGenerationPlugin { + fn build(&self, app: &mut App) { + app.add_event::() + .add_event::() + .add_systems( + Update, + (delete_map, generate_map.after(delete_map)) + .run_if(in_state(crate::CurrentScene::Game)), + ); + } +} + +/// An event to trigger the generation of the map. +#[derive(Event)] +pub struct StartMapGeneration { + /// The seed used to generate the map. + pub seed: u32, + + /// The radius of the map. + pub radius: u16, +} + +/// An event send when the map is generated. +#[derive(Event)] +pub struct EndMapGeneration; + +/// Generate each tiles of the map if the [StartMapGeneration] is received. +/// +/// The map is generated using a [Perlin] noise and a [HexSpiral]. +/// +/// It's generated one tile at a time, until the spiral is finished. +fn generate_map( + mut start_generation_events: EventReader, + mut end_generation_writer: EventWriter, + mut commands: Commands, + mut local_noise: Local>, + mut local_spiral: Local>>, +) { + // Handle map generation events and create the spiral and the noise. + for event in start_generation_events.read() { + *local_noise = Some(Perlin::new(event.seed)); + *local_spiral = Some(TilePosition::new(0, 0).spiral(event.radius as usize)); + } + + // Check if the map is being generated. + let (Some(noise), Some(spiral)) = (local_noise.as_ref(), local_spiral.as_mut()) else { + return; + }; + + // Spawn a tile until the spiral is finished + // If the map is generated, we send [EndMapGeneration] and set the local + // variables to None. + if let Some(position) = spiral.next() { + commands.spawn((get_tile_type(position, noise), position as TilePosition)); + } else { + end_generation_writer.send(EndMapGeneration); + *local_noise = None; + *local_spiral = None; + } +} + +/// Returns the type of the [HexPosition] with the given noise. +fn get_tile_type(position: HexPosition, noise: &Perlin) -> Tile { + let pixel_position = position.to_pixel_coordinates() / MAP_GENERATION_SCALE; + let value = noise.get([pixel_position.x as f64, pixel_position.y as f64]); + match value { + v if v <= -0.4 => Tile::Hill, + v if v >= 0.4 => Tile::Forest, + _ => Tile::Grass, + } +} + +/// Despawns the tiles if the event [StartMapGeneration] is received. +fn delete_map( + mut commands: Commands, + query: Query>, + mut start_generation_events: EventReader, +) { + for _ in start_generation_events.read() { + for entity in query.iter() { + commands.entity(entity).despawn_recursive(); + } + } +} diff --git a/crates/border-wars/src/map/hex.rs b/crates/border-wars/src/map/hex.rs index f94e292..415bdfc 100644 --- a/crates/border-wars/src/map/hex.rs +++ b/crates/border-wars/src/map/hex.rs @@ -4,6 +4,7 @@ use std::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; +use bevy::prelude::*; use paste::paste; /// Represents a number that can be used in calculations for hexagonal grids. @@ -98,7 +99,7 @@ number_impl! { /// Represents a position in a hexagonal grid. /// We use the axial coordinate system explained in this /// [documentation](https://www.redblobgames.com/grids/hexagons/#coordinates). -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Component)] pub struct HexPosition(pub T, pub T); /// All possible directions in a hexagonal grid. @@ -233,8 +234,12 @@ impl Iterator for HexSpiral { } impl HexPosition { + /// Creates a new [HexPosition]. + pub const fn new(x: T, y: T) -> Self { + Self(x, y) + } + /// Converts the current [HexPosition] into a pixel coordinate. - /// Input: The size of the hexagon in pixels (witdh, height). /// /// If you want to learn more about pixel coordinates conversion, /// you can check the @@ -243,21 +248,17 @@ impl HexPosition { /// # Example /// /// ```no_run + /// use bevy::math::Vec2; /// use border_wars::map::hex::HexPosition; /// /// let position = HexPosition(1, 0); - /// assert_eq!( - /// position.to_pixel_coordinates((1.0, 1.0)), - /// (3f32.sqrt(), 0.0) - /// ); + /// assert_eq!(position.to_pixel_coordinates(), (3f32.sqrt(), 0.0).into()); /// ``` - pub fn to_pixel_coordinates(&self, size: (f32, f32)) -> (f32, f32) { - ( - size.0 - * 3f32 - .sqrt() - .mul_add(T::to_f32(self.0), 3f32.sqrt() / 2.0 * T::to_f32(self.1)), - size.1 * (3.0 / 2.0 * T::to_f32(self.1)), + pub fn to_pixel_coordinates(&self) -> Vec2 { + Vec2::new( + 3f32.sqrt() + .mul_add(T::to_f32(self.0), 3f32.sqrt() / 2.0 * T::to_f32(self.1)), + 3.0 / 2.0 * T::to_f32(self.1), ) } diff --git a/crates/border-wars/src/map/mod.rs b/crates/border-wars/src/map/mod.rs index 515d0c2..5265d18 100644 --- a/crates/border-wars/src/map/mod.rs +++ b/crates/border-wars/src/map/mod.rs @@ -1,3 +1,24 @@ //! Contains all the logic related to the map. +pub mod generation; pub mod hex; + +use bevy::prelude::*; + +use self::hex::*; + +/// The position of a tile in a hexagonal map. +pub type TilePosition = HexPosition; + +/// The tile of the map. +#[derive(Component, Debug)] +pub enum Tile { + /// The hill tile. + Hill, + + /// The grass tile. + Grass, + + /// The forest tile. + Forest, +}