use crate::app::{Screen, Sim, layout};
use bevy::asset::uuid_handle;
use bevy::prelude::*;
use bevy::text::FontCx;
use parlance::Script;
pub(super) fn setup_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}
pub const ITALIC_FONT: Handle<Font> = uuid_handle!("9b451db2-8b49-4ad5-9794-ac1ce2ac943e");
pub const JP_FONT: Handle<Font> = uuid_handle!("6c2f0e33-3d4b-4d2e-9a58-6f2b2c9d4d71");
const JP_FAMILY: &str = "Noto Sans Mono CJK JP";
const JP_SCRIPTS: [&str; 3] = ["Hira", "Kana", "Hani"];
pub(super) fn install_ui_font(mut fonts: ResMut<Assets<Font>>) {
let upright = include_bytes!("../../assets/fonts/DejaVuSansMono.ttf");
if fonts
.insert(
&Handle::<Font>::default(),
Font::from_bytes(upright.to_vec()),
)
.is_err()
{
warn!("UI font failed to install; accented glyphs will be missing");
}
let slanted = include_bytes!("../../assets/fonts/DejaVuSansMono-Oblique.ttf");
if fonts
.insert(&ITALIC_FONT, Font::from_bytes(slanted.to_vec()))
.is_err()
{
warn!("italic UI font failed to install; notices will read upright");
}
let kanji = include_bytes!("../../assets/fonts/NotoSansMonoCJKjp-Subset.otf");
if fonts
.insert(&JP_FONT, Font::from_bytes(kanji.to_vec()))
.is_err()
{
warn!("Japanese font failed to install; Japanese will draw blank");
}
}
pub(super) fn teach_the_kanji_fallback(
mut fonts: ResMut<FontCx>,
mut lines: Query<&mut TextFont>,
mut taught: Local<bool>,
) {
if *taught {
return;
}
let Some(family) = fonts.collection.family_id(JP_FAMILY) else {
return;
};
for script in JP_SCRIPTS {
fonts
.collection
.append_fallbacks(Script::from_str_unchecked(script), std::iter::once(family));
}
for mut line in &mut lines {
line.set_changed();
}
*taught = true;
}
pub(super) fn fit_camera(
sim: Res<Sim>,
screen: Res<State<Screen>>,
ui_scale: Res<UiScale>,
windows: Query<&Window>,
mut cameras: Query<(&mut Projection, &mut Transform), With<Camera2d>>,
) {
let Ok(window) = windows.single() else {
return;
};
let menu = matches!(screen.get(), Screen::Menu);
let chrome = screen.get().chrome();
let ui = ui_scale.0;
let (chrome_w, chrome_top, chrome_bottom) =
(chrome.width * ui, chrome.top * ui, chrome.bottom * ui);
let chrome_h = chrome_top + chrome_bottom;
let board_w = f32::from(sim.0.width()) * layout::TILE;
let board_h = f32::from(sim.0.height()) * layout::TILE;
let fit_w = (window.width() - chrome_w).max(layout::TILE);
let fit_h = (window.height() - chrome_h).max(layout::TILE);
let scale = if menu {
1.0
} else {
(board_w / fit_w).max(board_h / fit_h).max(1.0)
};
let offset = Vec2::new(
0.0,
if menu {
0.0
} else {
(chrome_top - chrome_bottom) / 2.0
},
);
for (mut projection, mut transform) in &mut cameras {
if let Projection::Orthographic(ortho) = &mut *projection
&& (ortho.scale - scale).abs() > 0.001
{
ortho.scale = scale;
}
let target = Vec2::new(-offset.x, offset.y) * scale;
if transform.translation.truncate().distance(target) > 0.5 {
transform.translation.x = target.x;
transform.translation.y = target.y;
}
}
}