generated from tipragot/rust
WIP: Merge branch 'main' into menu-ui
All checks were successful
Rust Checks / checks (push) Successful in 3m58s
All checks were successful
Rust Checks / checks (push) Successful in 3m58s
This commit is contained in:
commit
ba953a4fde
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[build]
|
||||||
|
rustflags = ["-Z", "threads=8"]
|
104
Cargo.lock
generated
104
Cargo.lock
generated
|
@ -93,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom 0.2.12",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
|
@ -1136,7 +1136,7 @@ checksum = "7915222f4a08ccc782e08d10b751b42e5f9d786e697d0cb3fd09333cb7e8b6ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bevy_utils_proc_macros",
|
"bevy_utils_proc_macros",
|
||||||
"getrandom",
|
"getrandom 0.2.12",
|
||||||
"hashbrown 0.14.3",
|
"hashbrown 0.14.3",
|
||||||
"instant",
|
"instant",
|
||||||
"nonmax",
|
"nonmax",
|
||||||
|
@ -1326,6 +1326,8 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_egui",
|
"bevy_egui",
|
||||||
|
"noise",
|
||||||
|
"paste",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2115,6 +2117,17 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
|
@ -2124,7 +2137,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2854,7 +2867,7 @@ checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2968,6 +2981,17 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
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]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
@ -3410,6 +3434,19 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b"
|
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]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -3417,8 +3454,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"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]]
|
[[package]]
|
||||||
|
@ -3428,7 +3475,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"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]]
|
[[package]]
|
||||||
|
@ -3437,7 +3493,25 @@ version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -3542,7 +3616,7 @@ dependencies = [
|
||||||
"home",
|
"home",
|
||||||
"log",
|
"log",
|
||||||
"mio",
|
"mio",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
@ -3574,7 +3648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
|
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"getrandom",
|
"getrandom 0.2.12",
|
||||||
"libc",
|
"libc",
|
||||||
"spin",
|
"spin",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
|
@ -4277,7 +4351,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
@ -4365,7 +4439,7 @@ version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.12",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4403,6 +4477,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
|
|
@ -43,7 +43,7 @@ Our team consists of :
|
||||||
- [CoCo_Sol](https://github.com/cocosol007): Owner of this project and the main developper
|
- [CoCo_Sol](https://github.com/cocosol007): Owner of this project and the main developper
|
||||||
- [Raphaël](https://git.tipragot.fr/raphael): Owner of this project and game designer
|
- [Raphaël](https://git.tipragot.fr/raphael): Owner of this project and game designer
|
||||||
- [Tipragot](https://git.tipragot.fr/tipragot): Main reviewer
|
- [Tipragot](https://git.tipragot.fr/tipragot): Main reviewer
|
||||||
- [Arthur](https://www.instagram.com/tuturdu32): Compositeur
|
- [Arthur](https://www.instagram.com/tuturdu32): Composer
|
||||||
- [Racloody](https://linktr.ee/racloody): Sound designer and chief communications officer
|
- [Racloody](https://linktr.ee/racloody): Sound designer and chief communications officer
|
||||||
- [Horoleysh](https://instagram.com/horoleysh): Artist
|
- [Horoleysh](https://instagram.com/horoleysh): Artist
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,6 @@ workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.12.1", features = ["dynamic_linking"] }
|
bevy = { version = "0.12.1", features = ["dynamic_linking"] }
|
||||||
bevy_egui = "0.24.0"
|
bevy_egui = "0.24.0"
|
||||||
|
noise = "0.8.2"
|
||||||
|
paste = "1.0.14"
|
||||||
|
|
BIN
crates/border-wars/assets/tiles/forest.png
Normal file
BIN
crates/border-wars/assets/tiles/forest.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
crates/border-wars/assets/tiles/grass.png
Normal file
BIN
crates/border-wars/assets/tiles/grass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
crates/border-wars/assets/tiles/hill.png
Normal file
BIN
crates/border-wars/assets/tiles/hill.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
123
crates/border-wars/src/camera.rs
Normal file
123
crates/border-wars/src/camera.rs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
//! This module contains the camera systems responsible for movement and
|
||||||
|
//! scaling.
|
||||||
|
|
||||||
|
use bevy::input::mouse::{MouseScrollUnit, MouseWheel};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::CurrentScene;
|
||||||
|
|
||||||
|
/// The speed of camera movement.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct CameraSpeedMouvement(f32);
|
||||||
|
|
||||||
|
/// The speed of camera scaling.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct CameraSpeedScale(f32);
|
||||||
|
|
||||||
|
/// The minimum scale of the camera.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct MinimumScale(f32);
|
||||||
|
|
||||||
|
/// The maximum scale of the camera.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct MaximumScale(f32);
|
||||||
|
|
||||||
|
/// Key settings for camera movement.
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct KeysMovementSettings {
|
||||||
|
/// Key to move the camera up.
|
||||||
|
pub up: KeyCode,
|
||||||
|
|
||||||
|
/// Key to move the camera down.
|
||||||
|
pub down: KeyCode,
|
||||||
|
|
||||||
|
/// Key to move the camera right.
|
||||||
|
pub right: KeyCode,
|
||||||
|
|
||||||
|
/// Key to move the camera left.
|
||||||
|
pub left: KeyCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Bevy plugin for the camera.
|
||||||
|
/// Allows camera movement with the keyboard and scaling with the mouse.
|
||||||
|
pub struct CameraPlugin;
|
||||||
|
|
||||||
|
impl Plugin for CameraPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, init_camera)
|
||||||
|
.add_systems(Startup, init_resources_for_camera)
|
||||||
|
.add_systems(Update, movement_system.run_if(in_state(CurrentScene::Game)))
|
||||||
|
.add_systems(Update, scale_system.run_if(in_state(CurrentScene::Game)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the camera.
|
||||||
|
fn init_camera(mut commands: Commands) {
|
||||||
|
commands.spawn(Camera2dBundle::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the resources related to the camera.
|
||||||
|
///
|
||||||
|
/// - [KeysMovementSettings]: The key settings for camera movement.
|
||||||
|
/// - [CameraSpeedMouvement]: The speed of camera movement.
|
||||||
|
/// - [CameraSpeedScale]: The speed of camera scaling.
|
||||||
|
/// - [MinimumScale]: The minimum scale of the camera.
|
||||||
|
/// - [MaximumScale]: The maximum scale of the camera.
|
||||||
|
fn init_resources_for_camera(mut commands: Commands) {
|
||||||
|
commands.insert_resource(KeysMovementSettings {
|
||||||
|
up: KeyCode::Z,
|
||||||
|
down: KeyCode::S,
|
||||||
|
right: KeyCode::D,
|
||||||
|
left: KeyCode::Q,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.insert_resource(CameraSpeedMouvement(10.0));
|
||||||
|
commands.insert_resource(CameraSpeedScale(0.1));
|
||||||
|
commands.insert_resource(MinimumScale(0.1));
|
||||||
|
commands.insert_resource(MaximumScale(10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the camera with keyboard input.
|
||||||
|
fn movement_system(
|
||||||
|
mut query: Query<&mut Transform, With<Camera>>,
|
||||||
|
keys: Res<Input<KeyCode>>,
|
||||||
|
keys_settings: Res<KeysMovementSettings>,
|
||||||
|
movement_speed: Res<CameraSpeedMouvement>,
|
||||||
|
) {
|
||||||
|
for mut transform in query.iter_mut() {
|
||||||
|
let mut target = Vec3::ZERO;
|
||||||
|
for key in keys.get_pressed() {
|
||||||
|
match *key {
|
||||||
|
value if value == keys_settings.up => target.y += movement_speed.0,
|
||||||
|
value if value == keys_settings.down => target.y -= movement_speed.0,
|
||||||
|
value if value == keys_settings.right => target.x += movement_speed.0,
|
||||||
|
value if value == keys_settings.left => target.x -= movement_speed.0,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform.translation += target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scales the view with mouse input.
|
||||||
|
fn scale_system(
|
||||||
|
mut scroll_event: EventReader<MouseWheel>,
|
||||||
|
mut query: Query<&mut OrthographicProjection, With<Camera>>,
|
||||||
|
min_scale: Res<MinimumScale>,
|
||||||
|
max_scale: Res<MaximumScale>,
|
||||||
|
scale_speed: Res<CameraSpeedScale>,
|
||||||
|
) {
|
||||||
|
for event in scroll_event.read() {
|
||||||
|
for mut projection in query.iter_mut() {
|
||||||
|
if event.unit != MouseScrollUnit::Line {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let future_scale = event.y.mul_add(-scale_speed.0, projection.scale);
|
||||||
|
if min_scale.0 < future_scale && future_scale < max_scale.0 {
|
||||||
|
projection.scale = future_scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub mod camera;
|
||||||
|
pub mod map;
|
||||||
|
pub mod responsive_scale;
|
||||||
pub mod scenes;
|
pub mod scenes;
|
||||||
|
|
||||||
/// The current scene of the game.
|
/// The current scene of the game.
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
//! The main entry point of the game.
|
//! The main entry point of the game.
|
||||||
|
|
||||||
use bevy::{prelude::*, text::TextSettings};
|
use bevy::prelude::*;
|
||||||
|
use bevy::text::TextSettings;
|
||||||
|
use border_wars::camera::CameraPlugin;
|
||||||
|
use border_wars::map::click_tile::TilesClickable;
|
||||||
|
use border_wars::map::renderer::RendererPlugin;
|
||||||
use border_wars::scenes::ScenesPlugin;
|
use border_wars::scenes::ScenesPlugin;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11,5 +15,8 @@ fn main() {
|
||||||
})
|
})
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugins(ScenesPlugin)
|
.add_plugins(ScenesPlugin)
|
||||||
|
.add_plugins(RendererPlugin)
|
||||||
|
.add_plugins(CameraPlugin)
|
||||||
|
.add_plugins(TilesClickable)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
105
crates/border-wars/src/map/click_tile.rs
Normal file
105
crates/border-wars/src/map/click_tile.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
//! All programs related to the clicking on a tile.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use super::Tile;
|
||||||
|
|
||||||
|
/// The event that is triggered when a tile is clicked.
|
||||||
|
///
|
||||||
|
/// The event contains the index (ID) of the clicked tile.
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct TileJustClicked(pub u32);
|
||||||
|
|
||||||
|
/// An event that is triggered when a mouse button is clicked.
|
||||||
|
///
|
||||||
|
/// The event contains the position of the cursor in the world.
|
||||||
|
#[derive(Event)]
|
||||||
|
struct ClickOnTheWorld(Vec2);
|
||||||
|
|
||||||
|
/// A zone that can't be clicked.
|
||||||
|
/// For exemple the UI of the game.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct ZoneNotClickable;
|
||||||
|
|
||||||
|
/// A plugin that handles the selection of tiles.
|
||||||
|
pub struct TilesClickable;
|
||||||
|
|
||||||
|
impl Plugin for TilesClickable {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(PreUpdate, mouse_handler)
|
||||||
|
.add_systems(PreUpdate, select_closest_tile)
|
||||||
|
.add_event::<ClickOnTheWorld>()
|
||||||
|
.add_event::<TileJustClicked>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the mouse click and gets the position of the cursor in the world.
|
||||||
|
/// Finally, it sends an event with the position of the cursor.
|
||||||
|
fn mouse_handler(
|
||||||
|
mouse_button_input: Res<Input<MouseButton>>,
|
||||||
|
windows: Query<&Window>,
|
||||||
|
cameras: Query<(&Camera, &GlobalTransform)>,
|
||||||
|
mut events_writer: EventWriter<ClickOnTheWorld>,
|
||||||
|
not_clickable_zones: Query<(&Node, &GlobalTransform), With<ZoneNotClickable>>,
|
||||||
|
ui_scale: Res<UiScale>,
|
||||||
|
) {
|
||||||
|
if !mouse_button_input.just_pressed(MouseButton::Left) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = windows.get_single().expect("Main window not found");
|
||||||
|
|
||||||
|
let cursor_position_on_screen = window.cursor_position();
|
||||||
|
|
||||||
|
let Some(cursor_position_on_screen) = cursor_position_on_screen else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (node, global_transform) in not_clickable_zones.iter() {
|
||||||
|
let rect = node.physical_rect(global_transform, window.scale_factor(), ui_scale.0);
|
||||||
|
if rect.contains(cursor_position_on_screen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (camera, camera_transform) = cameras.get_single().expect("Camera not found");
|
||||||
|
|
||||||
|
let cursor_position_in_world = camera
|
||||||
|
.viewport_to_world(camera_transform, cursor_position_on_screen)
|
||||||
|
.expect("Failed to convert cursor position")
|
||||||
|
.origin
|
||||||
|
.truncate();
|
||||||
|
|
||||||
|
events_writer.send(ClickOnTheWorld(cursor_position_in_world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the closest tile to the cursor and send it in an event.
|
||||||
|
fn select_closest_tile(
|
||||||
|
tiles: Query<(Entity, &Transform, &Tile)>,
|
||||||
|
mut click_event_reader: EventReader<ClickOnTheWorld>,
|
||||||
|
mut clicked_tile_event_writer: EventWriter<TileJustClicked>,
|
||||||
|
) {
|
||||||
|
for click_event in click_event_reader.read() {
|
||||||
|
// The closest tile and its distance to the cursor.
|
||||||
|
let mut closest_entity: Option<Entity> = None;
|
||||||
|
let mut closest_position: Option<f32> = None;
|
||||||
|
|
||||||
|
for (tile_entity, tile_transform, tile_type) in tiles.iter() {
|
||||||
|
let mut tile_position = tile_transform.translation.truncate();
|
||||||
|
let tile_size = tile_type.get_image_size();
|
||||||
|
let tile_scale = tile_transform.scale.truncate();
|
||||||
|
|
||||||
|
tile_position += (tile_size / 2.0) * tile_scale;
|
||||||
|
|
||||||
|
let distance_to_cursor = tile_position.distance(click_event.0);
|
||||||
|
|
||||||
|
if closest_position.is_none() || closest_position > Some(distance_to_cursor) {
|
||||||
|
closest_entity = Some(tile_entity);
|
||||||
|
closest_position = Some(distance_to_cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(tile_entity) = closest_entity {
|
||||||
|
clicked_tile_event_writer.send(TileJustClicked(tile_entity.index()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
crates/border-wars/src/map/generation.rs
Normal file
98
crates/border-wars/src/map/generation.rs
Normal file
|
@ -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::<StartMapGeneration>()
|
||||||
|
.add_event::<EndMapGeneration>()
|
||||||
|
.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<StartMapGeneration>,
|
||||||
|
mut end_generation_writer: EventWriter<EndMapGeneration>,
|
||||||
|
mut commands: Commands,
|
||||||
|
mut local_noise: Local<Option<Perlin>>,
|
||||||
|
mut local_spiral: Local<Option<HexSpiral<i32>>>,
|
||||||
|
) {
|
||||||
|
// 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<i32>, 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<Entity, With<Tile>>,
|
||||||
|
mut start_generation_events: EventReader<StartMapGeneration>,
|
||||||
|
) {
|
||||||
|
for _ in start_generation_events.read() {
|
||||||
|
for entity in query.iter() {
|
||||||
|
commands.entity(entity).despawn_recursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
383
crates/border-wars/src/map/hex.rs
Normal file
383
crates/border-wars/src/map/hex.rs
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
//! All functions related to calculations in a hexagonal grid.
|
||||||
|
|
||||||
|
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.
|
||||||
|
pub trait Number:
|
||||||
|
Copy
|
||||||
|
+ PartialEq
|
||||||
|
+ PartialOrd
|
||||||
|
+ Add<Output = Self>
|
||||||
|
+ Sub<Output = Self>
|
||||||
|
+ Mul<Output = Self>
|
||||||
|
+ Div<Output = Self>
|
||||||
|
+ Rem<Output = Self>
|
||||||
|
+ Neg<Output = Self>
|
||||||
|
+ AddAssign
|
||||||
|
+ SubAssign
|
||||||
|
+ MulAssign
|
||||||
|
+ DivAssign
|
||||||
|
+ RemAssign
|
||||||
|
+ std::fmt::Debug
|
||||||
|
{
|
||||||
|
/// The number -2.
|
||||||
|
const MINUS_TWO: Self;
|
||||||
|
|
||||||
|
/// The number -1.
|
||||||
|
const MINUS_ONE: Self;
|
||||||
|
|
||||||
|
/// The number 0.
|
||||||
|
const ZERO: Self;
|
||||||
|
|
||||||
|
/// The number 1.
|
||||||
|
const ONE: Self;
|
||||||
|
|
||||||
|
/// The number 2.
|
||||||
|
const TWO: Self;
|
||||||
|
|
||||||
|
/// Returns the maximum of `self` and `other`.
|
||||||
|
fn max(self, other: Self) -> Self {
|
||||||
|
if self > other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum of `self` and `other`.
|
||||||
|
fn min(self, other: Self) -> Self {
|
||||||
|
if self < other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the absolute value of `self`.
|
||||||
|
fn abs(self) -> Self {
|
||||||
|
if self < Self::ZERO { -self } else { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an `usize` to `Self`.
|
||||||
|
fn from_usize(value: usize) -> Self;
|
||||||
|
|
||||||
|
/// Converts `self` to an `f32`.
|
||||||
|
fn to_f32(self) -> f32;
|
||||||
|
|
||||||
|
/// Converts an `f32` to `Self`.
|
||||||
|
fn from_f32(value: f32) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `Number` trait for the given types.
|
||||||
|
macro_rules! number_impl {
|
||||||
|
($($t:ty,)*) => {paste!{$(
|
||||||
|
impl Number for $t {
|
||||||
|
const MINUS_ONE: Self = - [< 1 $t >];
|
||||||
|
const MINUS_TWO: Self = - [< 2 $t >];
|
||||||
|
const ZERO: Self = [< 0 $t >];
|
||||||
|
const ONE: Self = [< 1 $t >];
|
||||||
|
const TWO: Self = [< 2 $t >];
|
||||||
|
|
||||||
|
|
||||||
|
fn from_usize(value: usize) -> Self {
|
||||||
|
value as $t
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_f32(self) -> f32 {
|
||||||
|
self as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_f32(value: f32) -> Self {
|
||||||
|
value as $t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}};
|
||||||
|
}
|
||||||
|
|
||||||
|
number_impl! {
|
||||||
|
i8, i16, i32, i64, i128, isize,
|
||||||
|
f32, f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, Component)]
|
||||||
|
pub struct HexPosition<T: Number>(pub T, pub T);
|
||||||
|
|
||||||
|
/// All possible directions in a hexagonal grid.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum HexDirection {
|
||||||
|
/// The direction right.
|
||||||
|
Right,
|
||||||
|
|
||||||
|
/// The direction up-right.
|
||||||
|
UpRight,
|
||||||
|
|
||||||
|
/// The direction up-left.
|
||||||
|
UpLeft,
|
||||||
|
|
||||||
|
/// The direction left.
|
||||||
|
Left,
|
||||||
|
|
||||||
|
/// The direction down-left.
|
||||||
|
DownLeft,
|
||||||
|
|
||||||
|
/// The direction down-right.
|
||||||
|
DownRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HexDirection {
|
||||||
|
/// Returns the vector ([HexPosition]) of the direction.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::{HexDirection, HexPosition};
|
||||||
|
///
|
||||||
|
/// let direction = HexDirection::Right;
|
||||||
|
/// assert_eq!(direction.to_vector(), HexPosition(1, 0));
|
||||||
|
/// ```
|
||||||
|
pub const fn to_vector<T: Number>(self) -> HexPosition<T> {
|
||||||
|
match self {
|
||||||
|
Self::Right => HexPosition(T::ONE, T::ZERO),
|
||||||
|
Self::UpRight => HexPosition(T::ONE, T::MINUS_ONE),
|
||||||
|
Self::UpLeft => HexPosition(T::ZERO, T::MINUS_ONE),
|
||||||
|
Self::Left => HexPosition(T::MINUS_ONE, T::ZERO),
|
||||||
|
Self::DownLeft => HexPosition(T::MINUS_ONE, T::ONE),
|
||||||
|
Self::DownRight => HexPosition(T::ZERO, T::ONE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hexagonal ring iterator.
|
||||||
|
pub struct HexRing<T: Number> {
|
||||||
|
/// The current position in the ring.
|
||||||
|
current: HexPosition<T>,
|
||||||
|
|
||||||
|
/// The direction of the current position to the next in the ring.
|
||||||
|
direction: HexDirection,
|
||||||
|
|
||||||
|
/// The radius of the ring.
|
||||||
|
radius: usize,
|
||||||
|
|
||||||
|
/// The index of the current position in the ring.
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> Iterator for HexRing<T> {
|
||||||
|
type Item = HexPosition<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index >= self.radius {
|
||||||
|
self.direction = match self.direction {
|
||||||
|
HexDirection::Right => HexDirection::UpRight,
|
||||||
|
HexDirection::UpRight => HexDirection::UpLeft,
|
||||||
|
HexDirection::UpLeft => HexDirection::Left,
|
||||||
|
HexDirection::Left => HexDirection::DownLeft,
|
||||||
|
HexDirection::DownLeft => HexDirection::DownRight,
|
||||||
|
HexDirection::DownRight => return None,
|
||||||
|
};
|
||||||
|
self.index = 0;
|
||||||
|
}
|
||||||
|
let result = self.current;
|
||||||
|
self.current += self.direction.to_vector();
|
||||||
|
self.index += 1;
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let remaining = match self.direction {
|
||||||
|
HexDirection::Right => self.radius * 6,
|
||||||
|
HexDirection::UpRight => self.radius * 5,
|
||||||
|
HexDirection::UpLeft => self.radius * 4,
|
||||||
|
HexDirection::Left => self.radius * 3,
|
||||||
|
HexDirection::DownLeft => self.radius * 2,
|
||||||
|
HexDirection::DownRight => self.radius,
|
||||||
|
} - self.index;
|
||||||
|
(remaining, Some(remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hexagonal spiral iterator.
|
||||||
|
pub struct HexSpiral<T: Number> {
|
||||||
|
/// The origin of the spiral.
|
||||||
|
origin: HexPosition<T>,
|
||||||
|
|
||||||
|
/// The current ring of the spiral.
|
||||||
|
current: HexRing<T>,
|
||||||
|
|
||||||
|
/// The radius of the spiral.
|
||||||
|
radius: usize,
|
||||||
|
|
||||||
|
/// The index of the current ring in the spiral.
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> Iterator for HexSpiral<T> {
|
||||||
|
type Item = HexPosition<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// The origin of the spiral.
|
||||||
|
if self.index == 0 {
|
||||||
|
self.index += 1;
|
||||||
|
return Some(self.origin);
|
||||||
|
}
|
||||||
|
if self.index > self.radius {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut result = self.current.next();
|
||||||
|
if result.is_none() && self.index < self.radius {
|
||||||
|
self.index += 1;
|
||||||
|
self.current = self.origin.ring(self.index);
|
||||||
|
result = self.current.next();
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> HexPosition<T> {
|
||||||
|
/// Creates a new [HexPosition].
|
||||||
|
pub const fn new(x: T, y: T) -> Self {
|
||||||
|
Self(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the current [HexPosition] into a pixel coordinate.
|
||||||
|
///
|
||||||
|
/// If you want to learn more about pixel coordinates conversion,
|
||||||
|
/// you can check the
|
||||||
|
/// [documentation](https://www.redblobgames.com/grids/hexagons/#hex-to-pixel).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use bevy::math::Vec2;
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let position = HexPosition(1, 0);
|
||||||
|
/// assert_eq!(position.to_pixel_coordinates(), (3f32.sqrt(), 0.0).into());
|
||||||
|
/// ```
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the distance between two [HexPosition]s.
|
||||||
|
///
|
||||||
|
/// # How it works
|
||||||
|
///
|
||||||
|
/// In the hexagonal grid, using the
|
||||||
|
/// [cube coordinate system](https://www.redblobgames.com/grids/hexagons/#coordinates),
|
||||||
|
/// it's akin to a cube in 3D space.
|
||||||
|
/// The Manhattan distance between two positions is equal to half of
|
||||||
|
/// the sum of abs(dx) + abs(dy) + abs(dz).
|
||||||
|
/// However, in hexagonal grids, z is defined as -q - r.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let a = HexPosition(0, 0);
|
||||||
|
/// let b = HexPosition(1, 1);
|
||||||
|
///
|
||||||
|
/// assert_eq!(a.distance(b), 2);
|
||||||
|
/// ```
|
||||||
|
pub fn distance(self, other: Self) -> T {
|
||||||
|
let Self(x, y) = self - other;
|
||||||
|
x.abs() + y.abs() + (x + y).abs() / T::TWO
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the hexagonal ring of the given radius.
|
||||||
|
/// If you want to learn more about hexagonal grids, check the
|
||||||
|
/// [documentation](https://www.redblobgames.com/grids/hexagons/#rings)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let position = HexPosition(0, 0);
|
||||||
|
/// let radius = 1;
|
||||||
|
///
|
||||||
|
/// for ring_position in position.ring(radius) {
|
||||||
|
/// println!("{:?}", ring_position);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn ring(self, radius: usize) -> HexRing<T> {
|
||||||
|
HexRing {
|
||||||
|
current: self + HexDirection::DownLeft.to_vector() * T::from_usize(radius),
|
||||||
|
direction: HexDirection::Right,
|
||||||
|
radius,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the hexagonal spiral of the given radius.
|
||||||
|
/// If you want to learn more about hexagonal grids, check the
|
||||||
|
/// [documentation](https://www.redblobgames.com/grids/hexagons/#rings-spiral)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let position = HexPosition(0, 0);
|
||||||
|
/// let radius = 1;
|
||||||
|
///
|
||||||
|
/// for spiral_position in position.spiral(radius) {
|
||||||
|
/// println!("{:?}", spiral_position);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn spiral(self, radius: usize) -> HexSpiral<T> {
|
||||||
|
HexSpiral {
|
||||||
|
origin: self,
|
||||||
|
current: self.ring(1),
|
||||||
|
radius,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of the arithmetic operators for hexagonal positions.
|
||||||
|
macro_rules! impl_ops {
|
||||||
|
($(($t:ty, $n:ident),)*) => {paste!{$(
|
||||||
|
impl<T: Number> $t for HexPosition<T> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn $n(self, rhs: Self) -> Self {
|
||||||
|
Self(self.0.$n(rhs.0), self.1.$n(rhs.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> $t<T> for HexPosition<T> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn $n(self, rhs: T) -> Self {
|
||||||
|
Self(self.0.$n(rhs), self.1.$n(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> [< $t Assign >] for HexPosition<T> {
|
||||||
|
fn [< $n _assign >](&mut self, rhs: Self) {
|
||||||
|
self.0.[< $n _assign >](rhs.0) ;
|
||||||
|
self.1.[< $n _assign >](rhs.1) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number> [< $t Assign >]<T> for HexPosition<T> {
|
||||||
|
fn [< $n _assign >](&mut self, rhs: T) {
|
||||||
|
self.0.[< $n _assign >](rhs);
|
||||||
|
self.1.[< $n _assign >](rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ops! {
|
||||||
|
(Add, add),
|
||||||
|
(Sub, sub),
|
||||||
|
(Mul, mul),
|
||||||
|
(Div, div),
|
||||||
|
(Rem, rem),
|
||||||
|
}
|
26
crates/border-wars/src/map/mod.rs
Normal file
26
crates/border-wars/src/map/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//! Contains all the logic related to the map.
|
||||||
|
|
||||||
|
pub mod click_tile;
|
||||||
|
pub mod generation;
|
||||||
|
pub mod hex;
|
||||||
|
pub mod renderer;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use self::hex::*;
|
||||||
|
|
||||||
|
/// The position of a tile in a hexagonal map.
|
||||||
|
pub type TilePosition = HexPosition<i32>;
|
||||||
|
|
||||||
|
/// The tile of the map.
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub enum Tile {
|
||||||
|
/// The hill tile.
|
||||||
|
Hill,
|
||||||
|
|
||||||
|
/// The grass tile.
|
||||||
|
Grass,
|
||||||
|
|
||||||
|
/// The forest tile.
|
||||||
|
Forest,
|
||||||
|
}
|
102
crates/border-wars/src/map/renderer.rs
Normal file
102
crates/border-wars/src/map/renderer.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
//! All functions related to the rendering of the map.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::sprite::Anchor;
|
||||||
|
|
||||||
|
use crate::map::{Tile, TilePosition};
|
||||||
|
|
||||||
|
/// A plugin to render the map.
|
||||||
|
pub struct RendererPlugin;
|
||||||
|
|
||||||
|
impl Plugin for RendererPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, init_resources_for_rendering)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
render_map.run_if(in_state(crate::CurrentScene::Game)),
|
||||||
|
)
|
||||||
|
.insert_resource(ClearColor(Color::rgb_u8(129, 212, 250)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The gap between the center of the tiles in the map.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct TilesGap(Vec2);
|
||||||
|
|
||||||
|
/// The size of the tiles in the map.
|
||||||
|
#[derive(Resource, Clone, Copy)]
|
||||||
|
struct TilesSize(Vec2);
|
||||||
|
|
||||||
|
impl Tile {
|
||||||
|
/// Returns the handle of the image of the tile.
|
||||||
|
fn get_texture(&self, asset_server: &AssetServer) -> Handle<Image> {
|
||||||
|
match self {
|
||||||
|
Self::Grass => asset_server.load("tiles/grass.png"),
|
||||||
|
Self::Forest => asset_server.load("tiles/forest.png"),
|
||||||
|
Self::Hill => asset_server.load("tiles/hill.png"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the image of the tile.
|
||||||
|
///
|
||||||
|
/// TODO: we are currently using temporary images that will modify
|
||||||
|
/// this function in the future.
|
||||||
|
pub const fn get_image_size(&self) -> Vec2 {
|
||||||
|
match self {
|
||||||
|
Self::Grass => Vec2 { x: 184.0, y: 164.0 },
|
||||||
|
Self::Forest => Vec2 { x: 184.0, y: 138.0 },
|
||||||
|
Self::Hill => Vec2 { x: 184.0, y: 181.0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Init resources related to the rendering of the map.
|
||||||
|
fn init_resources_for_rendering(mut commands: Commands) {
|
||||||
|
commands.insert_resource(TilesGap(Vec2 { x: 70., y: 35. }));
|
||||||
|
commands.insert_resource(TilesSize(Vec2 { x: 125., y: 100. }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the map.
|
||||||
|
fn render_map(
|
||||||
|
query: Query<(Entity, &TilePosition, &Tile), Changed<Tile>>,
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
tiles_gap: Res<TilesGap>,
|
||||||
|
tiles_size: Res<TilesSize>,
|
||||||
|
) {
|
||||||
|
for (entity, position, tile) in query.iter() {
|
||||||
|
let texture = tile.get_texture(&asset_server);
|
||||||
|
|
||||||
|
let translation_2d = tiles_gap.0 * position.to_pixel_coordinates();
|
||||||
|
let translation = Vec3::new(
|
||||||
|
translation_2d.x,
|
||||||
|
translation_2d.y,
|
||||||
|
z_position_from_y(translation_2d.y),
|
||||||
|
);
|
||||||
|
|
||||||
|
let scale_2d = tiles_size.0 / tile.get_image_size();
|
||||||
|
|
||||||
|
// the y scale is the same as the x scale to keep the aspect ratio.
|
||||||
|
let scale = Vec3::new(scale_2d.x, scale_2d.x, 1.0);
|
||||||
|
|
||||||
|
commands.entity(entity).insert(SpriteBundle {
|
||||||
|
sprite: Sprite {
|
||||||
|
anchor: Anchor::BottomLeft,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
texture,
|
||||||
|
transform: Transform {
|
||||||
|
translation,
|
||||||
|
scale,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple sigmoid function to convert y position to z position.
|
||||||
|
/// The return value is between 0 and 1.
|
||||||
|
fn z_position_from_y(y: f32) -> f32 {
|
||||||
|
-1.0 / (1.0 + (-y * 110_f64.powi(-3) as f32).exp())
|
||||||
|
}
|
37
crates/border-wars/src/responsive_scale.rs
Normal file
37
crates/border-wars/src/responsive_scale.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//! The file that contains the responsive scaling logic.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/// The plugin for the responsive scaling.
|
||||||
|
pub struct ResponsiveScalingPlugin;
|
||||||
|
|
||||||
|
impl Plugin for ResponsiveScalingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, init_window_size);
|
||||||
|
app.add_systems(Update, change_scaling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default ui layout size.
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct UILayoutSize(pub Vec2);
|
||||||
|
|
||||||
|
/// Initializes [UILayoutSize].
|
||||||
|
pub fn init_window_size(mut command: Commands) {
|
||||||
|
command.insert_resource(UILayoutSize(Vec2::new(1280., 720.)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the ui_scale.0 depending on the [UILayoutSize]
|
||||||
|
/// in order to make the ui layout responsive.
|
||||||
|
pub fn change_scaling(
|
||||||
|
mut ui_scale: ResMut<UiScale>,
|
||||||
|
windows: Query<&Window>,
|
||||||
|
size: Res<UILayoutSize>,
|
||||||
|
) {
|
||||||
|
let window = windows.get_single().expect("Main window not found");
|
||||||
|
let (a, b) = (
|
||||||
|
window.resolution.width() / size.0.x,
|
||||||
|
window.resolution.height() / size.0.y,
|
||||||
|
);
|
||||||
|
ui_scale.0 = if a < b { a } else { b } as f64
|
||||||
|
}
|
|
@ -248,7 +248,7 @@ fn main_node(main_node: &mut ChildBuilder<'_, '_, '_>, asset_server: &Res<AssetS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
/// The function that destroys the menu.
|
||||||
fn destroy_menu(mut commands: Commands, query: Query<Entity, With<MenuEntity>>) {
|
fn destroy_menu(mut commands: Commands, query: Query<Entity, With<MenuEntity>>) {
|
||||||
for entity in query.iter() {
|
for entity in query.iter() {
|
||||||
commands.entity(entity).despawn_recursive();
|
commands.entity(entity).despawn_recursive();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_egui::EguiPlugin;
|
use bevy_egui::EguiPlugin;
|
||||||
|
|
||||||
use crate::{change_scaling, CurrentScene};
|
use crate::{responsive_scale, CurrentScene};
|
||||||
|
|
||||||
pub mod lobby;
|
pub mod lobby;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
|
@ -17,6 +17,6 @@ impl Plugin for ScenesPlugin {
|
||||||
.add_state::<CurrentScene>()
|
.add_state::<CurrentScene>()
|
||||||
.add_plugins(menu::MenuPlugin)
|
.add_plugins(menu::MenuPlugin)
|
||||||
.add_plugins(lobby::LobbyPlugin)
|
.add_plugins(lobby::LobbyPlugin)
|
||||||
.add_systems(Update, change_scaling);
|
.add_plugins(responsive_scale::ResponsiveScalingPlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue