diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8957402..86db61e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,9 +27,7 @@ jobs: sweep-cache: true - name: Run tests run: | - cargo test --locked --workspace --all-features --all-targets - # Workaround for https://github.com/rust-lang/cargo/issues/6669 - cargo test --locked --workspace --all-features --doc + cargo test --locked --workspace --no-default-features # Run clippy lints. clippy: name: Clippy diff --git a/Cargo.lock b/Cargo.lock index cbf1630..6a0317f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2645,6 +2645,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.4.0" @@ -2669,9 +2675,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hexlab" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f91bb99b0ef58661cc4c18e9bab7bba9e66a3d3047fec9af76e31381647442" +checksum = "f19ccfce524b2ad659c08508de50522933909ed9787e4c4001cf80d4d380e789" dependencies = [ "bevy_reflect", "bevy_utils", @@ -3159,7 +3165,7 @@ dependencies = [ [[package]] name = "maze-ascension" -version = "0.2.3" +version = "0.3.0" dependencies = [ "anyhow", "bevy", @@ -3171,6 +3177,7 @@ dependencies = [ "rand", "rstest", "rstest_reuse", + "strum", "test-log", "thiserror 2.0.6", "tracing", @@ -4227,6 +4234,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "rustybuzz" version = "0.14.1" @@ -4465,6 +4478,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "svg_fmt" version = "0.4.4" diff --git a/Cargo.toml b/Cargo.toml index 63fcbd0..850f9e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "maze-ascension" authors = ["Kristofers Solo "] -version = "0.2.3" +version = "0.3.0" edition = "2021" [dependencies] @@ -18,11 +18,12 @@ tracing = { version = "0.1", features = [ "release_max_level_warn", ] } hexx = { version = "0.19", features = ["bevy_reflect", "grid"] } -hexlab = { version = "0.4", features = ["bevy_reflect"] } +hexlab = { version = "0.5", features = ["bevy_reflect"] } bevy-inspector-egui = { version = "0.28", optional = true } bevy_egui = { version = "0.31", optional = true } thiserror = "2.0" anyhow = "1" +strum = { version = "0.26", features = ["derive"] } [dev-dependencies] rstest = "0.23" diff --git a/src/floor/events.rs b/src/floor/events.rs index cae8f22..11b79ab 100644 --- a/src/floor/events.rs +++ b/src/floor/events.rs @@ -14,7 +14,7 @@ impl TransitionFloor { self.into() } - pub fn opposite(&self) -> Self { + pub const fn opposite(&self) -> Self { match self { Self::Ascend => Self::Descend, Self::Descend => Self::Ascend, @@ -31,7 +31,7 @@ impl TransitionFloor { impl From for f32 { fn from(value: TransitionFloor) -> Self { - f32::from(&value) + Self::from(&value) } } diff --git a/src/maze/assets.rs b/src/maze/assets.rs index f80519b..98ea47c 100644 --- a/src/maze/assets.rs +++ b/src/maze/assets.rs @@ -1,8 +1,8 @@ -use crate::theme::palette::rose_pine::{LOVE, PINE}; - use super::resources::GlobalMazeConfig; +use crate::theme::{palette::rose_pine::RosePine, prelude::ColorScheme}; use bevy::{prelude::*, utils::HashMap}; use std::f32::consts::FRAC_PI_2; +use strum::IntoEnumIterator; const WALL_OVERLAP_MODIFIER: f32 = 1.25; const HEX_SIDES: u32 = 6; @@ -13,7 +13,7 @@ pub struct MazeAssets { pub wall_mesh: Handle, pub hex_material: Handle, pub wall_material: Handle, - pub custom_materials: HashMap>, + pub custom_materials: HashMap>, } impl MazeAssets { @@ -22,11 +22,9 @@ impl MazeAssets { materials: &mut ResMut>, global_config: &GlobalMazeConfig, ) -> Self { - let mut custom_materials = HashMap::new(); - custom_materials.extend(vec![ - ("LOVE".to_string(), materials.add(red_material())), - ("PINE".to_string(), materials.add(blue_material())), - ]); + let custom_materials = RosePine::iter() + .map(|color| (color, materials.add(color.to_standart_material()))) + .collect(); Self { hex_mesh: meshes.add(generate_hex_mesh( global_config.hex_size, @@ -73,17 +71,3 @@ pub fn white_material() -> StandardMaterial { ..default() } } - -pub fn red_material() -> StandardMaterial { - StandardMaterial { - emissive: LOVE.to_linear(), - ..default() - } -} - -pub fn blue_material() -> StandardMaterial { - StandardMaterial { - emissive: PINE.to_linear(), - ..default() - } -} diff --git a/src/maze/triggers/spawn.rs b/src/maze/triggers/spawn.rs index 235025f..93e2725 100644 --- a/src/maze/triggers/spawn.rs +++ b/src/maze/triggers/spawn.rs @@ -7,6 +7,7 @@ use crate::{ events::SpawnMaze, resources::GlobalMazeConfig, }, + theme::palette::rose_pine::RosePine, }; use bevy::prelude::*; use hexlab::prelude::*; @@ -100,12 +101,12 @@ pub(super) fn spawn_single_hex_tile( let material = match tile.pos() { pos if pos == maze_config.start_pos => assets .custom_materials - .get("PINE") + .get(&RosePine::Pine) .cloned() .unwrap_or_default(), pos if pos == maze_config.end_pos => assets .custom_materials - .get("LOVE") + .get(&RosePine::Love) .cloned() .unwrap_or_default(), _ => assets.hex_material.clone(), diff --git a/src/player/assets.rs b/src/player/assets.rs index f4be8fa..fe2c77a 100644 --- a/src/player/assets.rs +++ b/src/player/assets.rs @@ -1,6 +1,7 @@ -use crate::theme::palette::rose_pine::PINE; use bevy::prelude::*; +use crate::theme::{palette::rose_pine::RosePine, prelude::ColorScheme}; + pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh { Mesh::from(Capsule3d { radius, @@ -9,9 +10,10 @@ pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh { } pub(super) fn blue_material() -> StandardMaterial { + let color = RosePine::Pine; StandardMaterial { - base_color: PINE, - emissive: PINE.to_linear() * 3., + base_color: color.to_color(), + emissive: color.to_linear_rgba() * 3., ..default() } } diff --git a/src/theme/colorscheme.rs b/src/theme/colorscheme.rs new file mode 100644 index 0000000..2552fc4 --- /dev/null +++ b/src/theme/colorscheme.rs @@ -0,0 +1,104 @@ +use bevy::prelude::*; +use std::ops::Deref; + +/// A trait for types that can be converted to a Bevy `Color`. +/// +/// Implementing this trait allows a type to be easily converted to various Bevy color types. +/// +/// # Examples +/// +/// ``` +/// use bevy::prelude::*; +/// use maze_ascension::theme::prelude::ColorScheme; +/// +/// struct MyColor(u8, u8, u8); +/// +/// impl ColorScheme for MyColor { +/// fn to_color(&self) -> Color { +/// Color::srgb( +/// self.0 as f32 / 255.0, +/// self.1 as f32 / 255.0, +/// self.2 as f32 / 255.0 +/// ) +/// } +/// } +/// +/// let my_color = MyColor(255, 0, 0); +/// let bevy_color: Color = my_color.to_color(); +/// assert_eq!(bevy_color, Color::srgb(1., 0., 0.)); +/// ``` +pub trait ColorScheme { + /// Converts the implementing type to a Bevy `Color`. + fn to_color(&self) -> Color; + + /// Converts the implementing type to a Bevy `LinearRgba`. + /// + /// This method provides a default implementation based on `to_color()`. + fn to_linear_rgba(&self) -> LinearRgba { + self.to_color().to_linear() + } + + /// Converts the implementing type to a Bevy `StandardMaterial`. + /// + /// This method provides a default implementation that sets the emissive color. + fn to_standart_material(&self) -> StandardMaterial { + StandardMaterial { + emissive: self.to_linear_rgba(), + ..default() + } + } +} + +/// A wrapper type that implements `From` traits for types implementing `ColorScheme`. +/// +/// This wrapper allows for easy conversion from `ColorScheme` types to Bevy color types. +/// +/// # Examples +/// +/// ``` +/// use bevy::prelude::*; +/// use maze_ascension::theme::prelude::{ColorScheme, ColorSchemeWrapper}; +/// +/// struct MyColor(u8, u8, u8); +/// +/// impl ColorScheme for MyColor { +/// fn to_color(&self) -> Color { +/// Color::srgb( +/// self.0 as f32 / 255.0, +/// self.1 as f32 / 255.0, +/// self.2 as f32 / 255.0 +/// ) +/// } +/// } +/// +/// let my_color = MyColor(0, 255, 0); +/// let wrapper = ColorSchemeWrapper(my_color); +/// let bevy_color: Color = wrapper.into(); +/// assert_eq!(bevy_color, Color::srgb(0., 1., 0.)); +/// ``` +pub struct ColorSchemeWrapper(pub T); + +impl From for ColorSchemeWrapper { + fn from(value: T) -> Self { + Self(value) + } +} + +impl Deref for ColorSchemeWrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for Color { + fn from(value: ColorSchemeWrapper) -> Self { + value.to_color() + } +} + +impl From> for LinearRgba { + fn from(value: ColorSchemeWrapper) -> Self { + value.to_linear_rgba() + } +} diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 1d0e466..86f1e5c 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -2,6 +2,7 @@ // Unused utilities may trigger this lints undesirably. +mod colorscheme; pub mod interaction; pub mod palette; mod widgets; @@ -9,6 +10,7 @@ mod widgets; #[allow(unused_imports)] pub mod prelude { pub use super::{ + colorscheme::{ColorScheme, ColorSchemeWrapper}, interaction::{InteractionPalette, OnPress}, palette as ui_palette, widgets::{Containers as _, Widgets as _}, diff --git a/src/theme/palette/rose_pine.rs b/src/theme/palette/rose_pine.rs index e69a56b..5d1fa3e 100644 --- a/src/theme/palette/rose_pine.rs +++ b/src/theme/palette/rose_pine.rs @@ -1,18 +1,45 @@ use super::rgb_u8; +use crate::theme::prelude::ColorScheme; use bevy::prelude::*; +use strum::EnumIter; -pub const BASE: Color = rgb_u8(25, 23, 36); -pub const SURFACE: Color = rgb_u8(31, 29, 46); -pub const OVERLAY: Color = rgb_u8(38, 35, 58); -pub const MUTED: Color = rgb_u8(110, 106, 134); -pub const SUBTLE: Color = rgb_u8(144, 140, 170); -pub const TEXT: Color = rgb_u8(224, 222, 244); -pub const LOVE: Color = rgb_u8(235, 111, 146); -pub const GOLD: Color = rgb_u8(246, 193, 119); -pub const ROSE: Color = rgb_u8(235, 188, 186); -pub const PINE: Color = rgb_u8(49, 116, 143); -pub const FOAM: Color = rgb_u8(156, 207, 216); -pub const IRIS: Color = rgb_u8(196, 167, 231); -pub const HIGHLIGHT_LOW: Color = rgb_u8(33, 32, 46); -pub const HIGHLIGHT_MED: Color = rgb_u8(64, 61, 82); -pub const HIGHLIGHT_HIGH: Color = rgb_u8(82, 79, 103); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)] +pub enum RosePine { + Base, + Surface, + Overlay, + Muted, + Subtle, + Text, + Love, + Gold, + Rose, + Pine, + Foam, + Iris, + HighlightLow, + HighlightMed, + HighlightHigh, +} + +impl ColorScheme for RosePine { + fn to_color(&self) -> Color { + match self { + Self::Base => rgb_u8(25, 23, 36), + Self::Surface => rgb_u8(31, 29, 46), + Self::Overlay => rgb_u8(38, 35, 58), + Self::Muted => rgb_u8(110, 106, 134), + Self::Subtle => rgb_u8(144, 140, 170), + Self::Text => rgb_u8(224, 222, 244), + Self::Love => rgb_u8(235, 111, 146), + Self::Gold => rgb_u8(246, 193, 119), + Self::Rose => rgb_u8(235, 188, 186), + Self::Pine => rgb_u8(49, 116, 143), + Self::Foam => rgb_u8(156, 207, 216), + Self::Iris => rgb_u8(196, 167, 231), + Self::HighlightLow => rgb_u8(33, 32, 46), + Self::HighlightMed => rgb_u8(64, 61, 82), + Self::HighlightHigh => rgb_u8(82, 79, 103), + } + } +}