#![allow(clippy::type_complexity, clippy::too_many_arguments)] #![feature(iter_array_chunks)] // used in ray.rs #![feature(iter_intersperse)] // used in debug.rs #![feature(async_closure)] // Loading tweakfiles mod audio; mod credits; mod debug; mod display3d; mod game; mod hit3d; mod intro; mod loading; mod menu; mod prelude; mod tutorial; mod tweak; mod ui; use crate::prelude::*; fn main() { if std::env::var_os("CARGO_MANIFEST_DIR").is_none() { std::env::set_var("CARGO_MANIFEST_DIR", "."); } let mut app = App::new(); app.init_state::(); app.init_state::(); app.add_systems( Update, ( debug_state::.run_if(resource_changed::>), debug_state::.run_if(resource_changed::>), debug_state:: .run_if(resource_changed::>), ), ); app.add_systems(Startup, set_window_icon); app.add_plugins( DefaultPlugins .set(ImagePlugin::default_nearest()) .set(WindowPlugin { primary_window: Some(Window { title: "Martian Chess".into(), ..default() }), ..default() }), ); app.add_plugins(credits::CreditsPlugin); app.add_plugins(debug::DebugPlugin); app.add_plugins(display3d::Display3dPlugin); app.add_plugins(game::GamePlugin); app.add_plugins(loading::LoadingPlugin); app.add_plugins(menu::MenuPlugin); app.add_plugins(audio::AudioPlugin); app.add_plugins(ui::UiPlugin); app.add_plugins(tweak::TweakPlugin); app.add_plugins(intro::IntroPlugin); app.add_plugins(tutorial::TutorialPlugin); app.run(); } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)] pub enum GameState { #[default] Loading, Credits, Intro, Title, Play, Endgame, Restart, Quit, } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)] pub(crate) enum DisplayState { #[default] Display3d, } /// System for printing the current state /// /// Only runs when state is modified. fn debug_state(state: Res>) { info!("State change {:?}", *state); } pub(crate) fn manage_state_entities() -> impl FnMut( Query< (&Marker, &mut Visibility), ( With, Without, Without, ), >, Res>, ) where Marker: Component + States + PartialEq + std::fmt::Debug, { move |mut entities: Query< (&Marker, &mut Visibility), ( With, Without, Without, ), >, curr_state: Res>| { info!("Activating state {:?}", *curr_state); entities.iter_mut().for_each(|(mark, mut visibility)| { *visibility = if mark == curr_state.get() { Visibility::Inherited } else { Visibility::Hidden }; }); } } pub(crate) fn any_component_changed( ) -> impl Fn(Query>) -> bool + Clone { |q: Query>| -> bool { !q.is_empty() } } pub(crate) fn any_component_added() -> impl Fn(Query>) -> bool + Clone { |q: Query>| -> bool { !q.is_empty() } } pub(crate) fn _any_component_added_or_changed( q: Query, Changed)>>, ) -> bool { !q.is_empty() } fn set_window_icon( // we have to use `NonSend` here windows: NonSend, ) { // here we use the `image` crate to load our icon data from a png file // this is not a very bevy-native solution, but it will do let (icon_rgba, icon_width, icon_height) = { let image = image::open("assets/images/3DIcon.png") .expect("Failed to open icon path") .into_rgba8(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); (rgba, width, height) }; let icon = Icon::from_rgba(icon_rgba, icon_width, icon_height).unwrap(); // do it for all windows for window in windows.windows.values() { window.set_window_icon(Some(icon.clone())); } } pub(crate) fn just_pressed(button: T) -> impl FnMut(Res>) -> bool + Clone where T: Copy + Eq + Hash + Send + Sync + 'static, { Box::new(move |buttons: Res>| -> bool { buttons.just_pressed(button) }) } pub(crate) fn just_released(button: T) -> impl FnMut(Res>) -> bool + Clone where T: Copy + Eq + Hash + Send + Sync + 'static, { Box::new(move |buttons: Res>| -> bool { buttons.just_released(button) }) } pub(crate) fn pressed(button: T) -> impl FnMut(Res>) -> bool + Clone where T: Copy + Eq + Hash + Send + Sync + 'static, { Box::new(move |buttons: Res>| -> bool { buttons.pressed(button) }) }