diff --git a/assets/fonts/roboto.ttf b/assets/fonts/roboto.ttf new file mode 100644 index 0000000..01656a3 Binary files /dev/null and b/assets/fonts/roboto.ttf differ diff --git a/assets/game.stylesheet.ron b/assets/game.stylesheet.ron new file mode 100644 index 0000000..21cdbad --- /dev/null +++ b/assets/game.stylesheet.ron @@ -0,0 +1,47 @@ +( + button_styles: { + MainMenu: ( + focus_outline_width_px: 4, + focus_outline_offset_px: 2, + focus_outline_color: Srgba((red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0)), + normal: ( + background_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + border_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ), + press: ( + background_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + border_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ), + hover: ( + background_color: Srgba((red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)), + border_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ) + ), + CharacterSelectCharacter: ( + focus_outline_width_px: 4, + focus_outline_offset_px: 2, + focus_outline_color: Srgba((red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0)), + normal: ( + background_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ), + press: ( + background_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ), + hover: ( + background_color: Srgba((red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)), + text_color: Srgba((red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)), + box_shadow: (), + ) + ) + } +) \ No newline at end of file diff --git a/src/debug_tools.rs b/src/debug_tools.rs index 35e9c1f..78d0816 100644 --- a/src/debug_tools.rs +++ b/src/debug_tools.rs @@ -11,7 +11,7 @@ use bevy_ggrs::RollbackFrameRate; use enum_cycling::EnumCycle; use enum_cycling_derive::EnumCycle; -use crate::{player::Player, replay::StopReplayRecording}; +use crate::{player::Player, replay::StopReplayRecording, AppState}; pub const PLAYER_DEBUG_ROW_COUNT: usize = 6; @@ -365,7 +365,9 @@ impl Plugin for DebugToolsPlugin { app.init_resource::() .add_systems( Update, - (handle_palette_input, sync_gizmo_depth_bias).chain(), + (handle_palette_input, sync_gizmo_depth_bias) + .chain() + .run_if(in_state(AppState::InMatch)), ) .add_systems(EguiPrimaryContextPass, draw_palette); } diff --git a/src/fighter/manifest.rs b/src/fighter/manifest.rs index d2224c7..3dff536 100644 --- a/src/fighter/manifest.rs +++ b/src/fighter/manifest.rs @@ -73,8 +73,8 @@ impl AssetCollection for FighterManifestAssets { } } -#[derive(Resource)] -pub struct FighterManifestRegistry(HashMap>); +#[derive(Resource, Deref)] +pub struct FighterManifestRegistry(pub HashMap>); impl FighterManifestRegistry { pub fn get(&self, fighter: FighterId) -> Option<&Handle> { diff --git a/src/game_settings.rs b/src/game_settings.rs index 5850f4b..c090379 100644 --- a/src/game_settings.rs +++ b/src/game_settings.rs @@ -2,7 +2,7 @@ use bevy::prelude::*; use bevy_asset_loader::asset_collection::AssetCollection; use serde::{Deserialize, Serialize}; -use crate::math::int::FGi32; +use crate::{math::int::FGi32, menus::style::FGUiStyle}; #[derive(Reflect, Serialize, Deserialize, Clone)] #[reflect(opaque)] @@ -34,7 +34,9 @@ pub struct GameSettings { #[derive(AssetCollection, Resource)] pub struct CommonAssets { #[asset(path = "game_settings.ron")] - game_settings: Handle, + pub game_settings: Handle, + #[asset(path = "game.stylesheet.ron")] + pub ui_style: Handle, } impl FromWorld for GameSettings { diff --git a/src/input/mod.rs b/src/input/mod.rs index d7deb86..90df781 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -21,6 +21,27 @@ pub use map::{ KeyboardInputMapElement, }; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LocalInputSource { + Keyboard, + Gamepad(Entity), +} + +/// Maps an input source to a player index +#[derive(Resource, Clone, Debug, Default, Deref, DerefMut)] +pub struct LocalInputAssignments(pub Vec<(usize, LocalInputSource)>); + +impl LocalInputAssignments { + pub fn get_free_player_index(&self) -> Option { + for i in 0..16 { + if let None = self.iter().find(|(idx, _)| *idx == i) { + return Some(i); + } + } + None + } +} + use crate::game_settings::GameSettings; use crate::math::int::FGi32; use crate::netcode::GGRSCfg; @@ -102,6 +123,7 @@ impl Plugin for FighterInputPlugin { }, ], }) + .init_resource::() .add_plugins(gamecube::GamecubeAdapterPlugin) .add_systems(ReadInputs, read_local_inputs) .add_systems( @@ -128,22 +150,29 @@ fn read_local_inputs( input_map: Res, gc_ports: Res, gamepads: Query<&Gamepad>, + assignments: Res, local_players: Res, game_settings: Res ) { let mut local_inputs = HashMap::new(); for handle in &local_players.0 { - let gamepad = gc_ports - .entities - .get(*handle) - .copied() - .flatten() - .and_then(|entity| gamepads.get(entity).ok()); + let assigned = assignments.0.iter().find(|(player_handle, _)| player_handle == handle); + let gamepad = match assigned { + Some((_, LocalInputSource::Gamepad(entity))) => gamepads.get(*entity).ok(), + Some((_, LocalInputSource::Keyboard)) => None, + None => gc_ports.entities.get(*handle).copied().flatten().and_then(|entity| gamepads.get(entity).ok()), + }; - let mut input_frame = match gamepad { + let mut input_frame = match assigned { + Some((_, LocalInputSource::Keyboard)) => keyboard::sample_keyboard(&key, &input_map), + Some((_, LocalInputSource::Gamepad(_))) => gamepad + .map(|pad| gamepad::sample_gamepad(pad, &input_map)) + .unwrap_or_default(), + None => match gamepad { Some(pad) => gamepad::sample_gamepad(pad, &input_map), None => keyboard::sample_keyboard(&key, &input_map), + }, }; // preprocess frame diff --git a/src/lib.rs b/src/lib.rs index 40087a8..6e76f30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ pub mod replay; pub mod schedule; pub mod scripting; pub mod stage; +pub mod menus; #[derive(States, Debug, Clone, Copy, Eq, PartialEq, Hash, Default)] pub enum AppState { @@ -59,7 +60,9 @@ pub enum AppState { LoadStageManifests, PrepareStageManifests, CommonAssetLoadFailed, - Idle, + MainMenu, + CharacterSelect, + StageSelect, LoadingMatch, PreparingMatch, InMatch, @@ -96,6 +99,7 @@ impl Plugin for GamePlugin { bevy_common_assets::ron::RonAssetPlugin::::new(&["stage.ron"]), bevy_common_assets::ron::RonAssetPlugin::::new(&["ron"]), )) + .add_plugins(menus::MenuPlugin) .add_loading_state( LoadingState::new(AppState::LoadingMatch) .continue_to_state(AppState::PreparingMatch) @@ -134,7 +138,7 @@ impl Plugin for GamePlugin { stage::manifest::prepare_stage_manifests, ) .add_systems( - OnEnter(AppState::Idle), + OnEnter(AppState::MainMenu), match_loading::initiate_default_match, ) .add_systems( @@ -142,7 +146,6 @@ impl Plugin for GamePlugin { match_loading::prepare_match, ) .add_systems(OnExit(AppState::InMatch), match_loading::cleanup_match) - .add_systems(Update, spin_cubes) .insert_resource(args::Args::parse()); } } @@ -155,28 +158,4 @@ fn wait_for_asset_processor( { next_state.set(AppState::LoadCommonAssets); } -} - -/// Spin-rate in radians per second. Attach it in the inspector; the -/// editor sees this component with no registration code because Bevy's -/// `reflect_auto_register` picks up the `Reflect` derive. `Default` + -/// `#[reflect(Default)]` let the editor add it and the game reconstruct -/// it with sensible starting values. -#[derive(Component, Reflect, Default)] -#[reflect(Component, Default, @EditorCategory::new("Actor"))] -pub struct SpinningCube { - pub speed: f32, -} - -fn spin_cubes(time: Res