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.

158 lines
4.2 KiB
Rust

mod blocks;
mod debug;
mod fighter;
use engine::*;
use serde::Deserialize;
use thiserror::Error;
use blocks::*;
use debug::*;
use fighter::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(FeathersPlugins)
.add_plugins((BlocksPlugin, FighterPlugin, DebugPlugin))
.init_state::<Loading>()
.init_state::<GameState>()
.init_resource::<AllAssets>()
.init_resource::<SetupChecklist>()
// Check if assets were added to loading queue
.add_systems(
Update,
loading_check
.run_if(in_state(Loading(true)))
.run_if(resource_changed::<AllAssets>),
)
// Wait for pending assets to be loaded
.add_systems(Update, loading_wait.run_if(in_state(Loading(true))))
// Once done loading, move to the setup state
.add_systems(OnEnter(Loading(false)), setup_game)
// Check if the game is ready to progress
.add_systems(Update, setup_wait.run_if(in_state(GameState::Setup)))
// State toggles
.add_systems(Update, (
(
toggle_state_visibility::<Loading>,
sync_state_to_ui::<Loading>,
).run_if(state_changed::<Loading>),
(
toggle_state_visibility::<GameState>,
sync_state_to_ui::<GameState>,
).run_if(state_changed::<GameState>)
))
.run();
}
/// Reports if the game is loading assets
#[derive(States, Clone, Eq, Debug, PartialEq, Hash, Component)]
struct Loading(bool);
impl Default for Loading {
fn default() -> Self {
Loading(true)
}
}
impl fmt::Display for Loading {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Loading {}", self.0)
}
}
/// Tracks what state the main game loop is in
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash, Component)]
enum GameState {
#[default]
Boot,
Setup,
Run,
}
impl fmt::Display for GameState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GameState::Boot => write!(f, "Game State: Boot"),
GameState::Setup => write!(f, "GameState: Setup"),
GameState::Run => write!(f, "GameState: Run"),
}
}
}
/// A list of all assets so we don't lose them
#[derive(Default, Resource, Debug)]
struct AllAssets {
handles: Vec<UntypedHandle>,
}
/// A "checklist" to know if we can progress from setup to the game
#[derive(Default, Resource)]
struct SetupChecklist {
spawn_shape: bool,
}
impl SetupChecklist {
fn done(&self) -> bool {
self.spawn_shape
}
}
/// Sends the game into Loading::Active if assets are added to the AllAssets list
fn loading_check(mut next: ResMut<NextState<Loading>>, all_assets: Res<AllAssets>) {
debug_assert!(all_assets.is_changed());
next.set(Loading(true));
}
/// Waits in Loading::Active until all assets are loaded then move to Loading(false)
fn loading_wait(
curr: Res<State<Loading>>,
mut next: ResMut<NextState<Loading>>,
server: Res<AssetServer>,
all_assets: Res<AllAssets>,
) {
debug_assert!(*curr.get() == Loading(true));
if all_assets
.handles
.iter()
.all(|h| matches!(server.get_load_state(h.id()), Some(LoadState::Loaded)))
{
next.set(Loading(false));
}
}
/// Moves the game from Boot to Setup
fn setup_game(curr: Res<State<GameState>>, mut next: ResMut<NextState<GameState>>) {
debug_assert!(*curr.get() == GameState::Boot);
next.set(GameState::Setup);
}
/// Wait until all checklist items are complete
fn setup_wait(
curr: Res<State<GameState>>,
mut next: ResMut<NextState<GameState>>,
checklist: Res<SetupChecklist>,
) {
debug_assert!(*curr.get() == GameState::Setup);
// If all checks pass, move on to the run state
if checklist.done() {
next.set(GameState::Run);
}
}
/// A wrapper around a handle for assigning an arbitrary Handle<T> to an entity
#[derive(Debug, Component)]
struct AssetComponent<T: Asset> {
handle: Handle<T>,
}
impl<T: Asset> AssetComponent<T> {
fn new(handle: Handle<T>) -> Self {
Self { handle }
}
}