mod camera; mod game; mod input; mod main_menu; mod movement; mod player; mod prelude; mod statue; use bevy::asset::load_internal_binary_asset; use bevy::audio::{AudioPlugin, SpatialScale}; use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; use bevy::window::{PrimaryWindow, WindowMode}; 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] LoadingScreen, MainMenu, 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()), FrameTimeDiagnosticsPlugin, bevy_framepace::FramepacePlugin, bevy_inspector_egui::quick::WorldInspectorPlugin::new(), )); 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.insert_resource(AmbientLight { color: Color::srgb(1., 1., 1.), brightness: 1., }); 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::MainMenu), main_menu::startup) .add_systems(OnExit(View::MainMenu), main_menu::exit) .add_systems(OnEnter(View::InGame), (player::startup, statue::startup)) .add_systems(OnExit(View::InGame), (player::exit, statue::exit)) .add_systems(OnEnter(View::LoadingScreen), (startup, camera::startup)) .add_systems( Update, ( finish_setup.run_if(in_state(View::LoadingScreen)), input::process_input, (update,).after(input::process_input), ( 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 finish_setup(mut app_state: ResMut>) { app_state.set(View::MainMenu); } fn startup(mut commands: Commands) { let font_size = 48.; commands.spawn(( FpsText, TextBundle::from_sections([ TextSection::new( "FPS: ", TextStyle { font_size, ..default() }, ), TextSection::from_style(TextStyle { font_size, color: Color::hsla(0.5, 0.5, 0.5, 0.5), ..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::MainMenu); } View::MainMenu => { app_exit_events.send(AppExit::Success); } View::LoadingScreen => {} } } else if input.action { match view.get() { View::InGame => {} View::MainMenu => { next_state.set(crate::View::InGame); } View::LoadingScreen => {} } } 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(&FrameTimeDiagnosticsPlugin::FPS) { if let Some(value) = fps.smoothed() { text.sections[1].value = format!("{value:.2}"); } } } }