mod camera; mod game; mod input; mod inspector; mod main_menu; mod map; mod movement; mod player; mod prelude; mod statue; use bevy::asset::load_internal_binary_asset; use bevy::audio::{AudioPlugin, SpatialScale}; use bevy::window::{PrimaryWindow, WindowMode}; use bevy_ecs_tilemap::TilemapPlugin; use input::Input; use prelude::*; use statue::SpawnStatueEvent; #[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] pub enum Game { #[default] Running, Paused, } #[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] pub enum View { #[default] LoadingMenu, MainMenu, LoadingGame, InGame, } #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] struct MainMenuSet; #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] struct InGameSet; const AUDIO_SCALE: f32 = 1. / 200.0; fn main() -> AppExit { let mut app = App::new(); app.add_plugins(( DefaultPlugins .set(WindowPlugin { primary_window: Some(Window { title: "Kodo Tag".into(), // mode: bevy::window::WindowMode::BorderlessFullscreen, mode: bevy::window::WindowMode::Windowed, // resolution: (640. * 2., 360. * 2.).into(), ..default() }), ..default() }) .set(AudioPlugin { default_spatial_scale: SpatialScale::new_2d(AUDIO_SCALE), global_volume: GlobalVolume::new(1.), ..default() }) .set(ImagePlugin::default_nearest()), bevy::diagnostic::FrameTimeDiagnosticsPlugin, bevy_framepace::FramepacePlugin, inspector::InspectorPlugin, TilemapPlugin, )); load_internal_binary_asset!( app, TextStyle::default().font, "../assets/font/iosevka-lyteterm-regular.subset.ttf", |bytes: &[u8], _path: String| { Font::try_from_bytes(bytes.to_vec()).unwrap() } ); app.init_resource::() .init_state::() .init_state::(); app.enable_state_scoped_entities::(); app.configure_sets( Update, ( MainMenuSet.run_if(in_state(View::MainMenu)), InGameSet .run_if(in_state(View::InGame)) .after(input::process_input), ), ); app.add_event::(); app.add_systems( OnEnter(View::LoadingMenu), ((startup, camera::startup), main_menu::startup), ) .add_systems(OnExit(View::MainMenu), main_menu::exit) .add_systems( OnEnter(View::LoadingGame), (player::startup, statue::startup, map::spawn), ) .add_systems(OnEnter(View::InGame), map::finalize) .add_systems( OnExit(View::InGame), (player::exit, statue::exit, map::exit), ) .add_systems( Update, ( load_menu.run_if(in_state(View::LoadingMenu)), load_game.run_if(in_state(View::LoadingGame)), input::process_input, (update,).after(input::process_input), ( // map::update, player::control, player::meow_on_r, player::player_debug_info, statue::spawn_statue, movement::update_velocity_by_heading, movement::resolve_velocity.after(movement::update_velocity_by_heading), movement::ysort.after(movement::update_velocity_by_heading), camera::update .after(player::control) .after(movement::resolve_velocity), ) .after(input::process_input) .in_set(InGameSet), (main_menu::update).in_set(MainMenuSet), ), ) .insert_resource(ClearColor(Color::srgb(0.3, 0.1, 0.5))); app.run() } #[derive(Component)] struct FpsText; fn load_menu(mut app_state: ResMut>) { app_state.set(View::MainMenu); } fn load_game(mut app_state: ResMut>) { // need to check if the map is fully loaded before switching app_state.set(View::InGame); } fn startup(mut commands: Commands) { let font_size = 48.; commands.spawn(( FpsText, Name::new("FpsText"), TextBundle::from_sections([ TextSection::new( "FPS: ", TextStyle { font_size, ..default() }, ), TextSection::from_style(TextStyle { font_size, ..default() }), ]) .with_style(Style { position_type: PositionType::Absolute, bottom: Val::Px(5.0), right: Val::Px(5.0), ..default() }), )); } fn update( view: Res>, input: Res, mut next_state: ResMut>, diagnostics: Res, mut query: Query<&mut Text, With>, mut window: Query<&mut Window, With>, keys: Res>, mut app_exit_events: ResMut>, ) { if input.cancel { match view.get() { View::InGame => { next_state.set(crate::View::LoadingMenu); } View::MainMenu => { app_exit_events.send(AppExit::Success); } View::LoadingMenu => {} View::LoadingGame => {} } } else if input.action { match view.get() { View::InGame => {} View::MainMenu => { next_state.set(crate::View::LoadingGame); } View::LoadingMenu => {} View::LoadingGame => {} } } if keys.just_pressed(KeyCode::Enter) && (keys.pressed(KeyCode::AltLeft) || keys.pressed(KeyCode::AltRight)) { if let Ok(window) = window.get_single_mut() { let mut window = window; if window.mode == WindowMode::Windowed { window.mode = WindowMode::BorderlessFullscreen } else { window.mode = WindowMode::Windowed } } } for mut text in &mut query { if let Some(fps) = diagnostics.get(&bevy::diagnostic::FrameTimeDiagnosticsPlugin::FPS) { if let Some(value) = fps.smoothed() { text.sections[1].value = format!("{value:.2}"); } } } }