You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.6 KiB
Rust
169 lines
4.6 KiB
Rust
#![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::<GameState>();
|
|
app.init_state::<DisplayState>();
|
|
app.add_systems(
|
|
Update,
|
|
(
|
|
debug_state::<DisplayState>.run_if(resource_changed::<State<DisplayState>>),
|
|
debug_state::<GameState>.run_if(resource_changed::<State<GameState>>),
|
|
debug_state::<tutorial::TutorialState>
|
|
.run_if(resource_changed::<State<tutorial::TutorialState>>),
|
|
),
|
|
);
|
|
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,
|
|
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<S: States>(state: Res<State<S>>) {
|
|
info!("State change {:?}", *state);
|
|
}
|
|
|
|
pub(crate) fn manage_state_entities<Marker>() -> impl FnMut(
|
|
Query<
|
|
(&Marker, &mut Visibility),
|
|
(
|
|
With<Marker>,
|
|
Without<game::Captured>,
|
|
Without<game::ValidMove>,
|
|
),
|
|
>,
|
|
Res<State<Marker>>,
|
|
)
|
|
where
|
|
Marker: Component + States + PartialEq + std::fmt::Debug,
|
|
{
|
|
move |mut entities: Query<
|
|
(&Marker, &mut Visibility),
|
|
(
|
|
With<Marker>,
|
|
Without<game::Captured>,
|
|
Without<game::ValidMove>,
|
|
),
|
|
>,
|
|
curr_state: Res<State<Marker>>| {
|
|
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<C: Component>(
|
|
) -> impl Fn(Query<Entity, Changed<C>>) -> bool + Clone {
|
|
|q: Query<Entity, Changed<C>>| -> bool { !q.is_empty() }
|
|
}
|
|
|
|
pub(crate) fn any_component_added<C: Component>() -> impl Fn(Query<Entity, Added<C>>) -> bool + Clone
|
|
{
|
|
|q: Query<Entity, Added<C>>| -> bool { !q.is_empty() }
|
|
}
|
|
|
|
pub(crate) fn _any_component_added_or_changed<C: Component>(
|
|
q: Query<Entity, Or<(Added<C>, Changed<C>)>>,
|
|
) -> bool {
|
|
!q.is_empty()
|
|
}
|
|
|
|
fn set_window_icon(
|
|
// we have to use `NonSend` here
|
|
windows: NonSend<WinitWindows>,
|
|
) {
|
|
// 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<T>(button: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
|
|
where
|
|
T: Copy + Eq + Hash + Send + Sync + 'static,
|
|
{
|
|
Box::new(move |buttons: Res<ButtonInput<T>>| -> bool { buttons.just_pressed(button) })
|
|
}
|