use bevy::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
pub const IDLE_ROW: Color = Color::srgb(0.66, 0.54, 0.17);
pub const SELECTED_ROW: Color = Color::srgb(0.95, 0.9, 0.6);
pub const CARD_BG: Color = Color::srgb(0.07, 0.08, 0.11);
pub const GOLD: Color = Color::srgb(0.96, 0.83, 0.35);
pub const PARCHMENT: Color = Color::srgb(0.92, 0.89, 0.78);
pub const CARD_FILL: Color = Color::srgba(0.04, 0.07, 0.12, 0.95);
pub const CARD_EDGE: Color = Color::srgba(0.96, 0.83, 0.35, 0.30);
pub const INK_TIDE: Color = Color::srgb(0.55, 0.8, 0.95);
pub const INK_LURE: Color = Color::srgb(0.75, 0.6, 0.95);
pub const INK_SURGE: Color = Color::srgb(0.5, 0.9, 0.7);
pub const INK_RAID: Color = Color::srgb(0.96, 0.45, 0.38);
static COLORBLIND: AtomicBool = AtomicBool::new(false);
pub fn set_colorblind(on: bool) {
COLORBLIND.store(on, Ordering::Relaxed);
}
pub fn player_color(player: u8) -> Color {
if COLORBLIND.load(Ordering::Relaxed) {
safe_color(player)
} else {
classic_color(player)
}
}
pub(crate) fn classic_color(player: u8) -> Color {
match player {
0 => Color::srgb(0.84, 0.27, 0.27), 1 => Color::srgb(0.25, 0.47, 0.79), 2 => Color::srgb(0.30, 0.69, 0.31), 3 => Color::srgb(0.89, 0.73, 0.23), 4 => Color::srgb(0.70, 0.40, 0.82), _ => Color::srgb(0.95, 0.55, 0.20), }
}
fn safe_color(player: u8) -> Color {
match player {
0 => Color::srgb(0.80, 0.40, 0.00), 1 => Color::srgb(0.35, 0.70, 0.90), 2 => Color::srgb(0.95, 0.90, 0.25), 3 => Color::srgb(0.80, 0.60, 0.70), 4 => Color::srgb(0.00, 0.45, 0.70), _ => Color::srgb(0.35, 0.35, 0.38), }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn player_colors_are_distinct() {
for palette in [classic_color, safe_color] {
let colors: Vec<_> = (0..crate::sim::MAX_PLAYERS as u8).map(palette).collect();
for i in 0..colors.len() {
for j in (i + 1)..colors.len() {
assert_ne!(colors[i], colors[j], "seats {i} and {j}");
}
}
}
}
fn dichromat_view(color: Color) -> (f32, f32) {
let c = color.to_srgba();
let brightness = 0.35 * c.red + 0.55 * c.green + 0.10 * c.blue;
let blue_yellow = c.blue - (c.red + c.green) / 2.0;
(brightness, blue_yellow)
}
#[test]
fn the_safe_palette_survives_color_blind_vision() {
let separation = |palette: fn(u8) -> Color| {
let mut worst = f32::INFINITY;
for a in 0..crate::sim::MAX_PLAYERS as u8 {
for b in (a + 1)..crate::sim::MAX_PLAYERS as u8 {
let (ba, ya) = dichromat_view(palette(a));
let (bb, yb) = dichromat_view(palette(b));
worst = worst.min((ba - bb).abs().max((ya - yb).abs()));
}
}
worst
};
let safe = separation(safe_color);
assert!(safe > 0.2, "closest safe pair is only {safe} apart");
assert!(
separation(classic_color) < safe,
"the accessible palette must beat the default it replaces"
);
}
}