Initial work on menus

This commit is contained in:
2026-09-10 23:26:17 +02:00
parent 71820a25dd
commit 3dbda96d31
22 changed files with 1093 additions and 44 deletions
@@ -0,0 +1,41 @@
use bevy::{prelude::*, text::FontSourceTemplate};
use crate::{fighter::manifest::FighterManifest, menus::{button::FGUiButton, style::FGUiButtonType}};
#[derive(Component, Clone, Default, FromTemplate)]
pub struct CharacterBox {
fighter_manifest: Handle<FighterManifest>,
}
impl CharacterBox {
pub fn scene(manifest_handle: Handle<FighterManifest>, manifests: &Res<Assets<FighterManifest>>) -> impl Scene {
let manifest = manifests.get(&manifest_handle).expect("Fighter manifest should be valid");
bsn! {
Node {
width: px(128),
height: px(128),
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::FlexEnd
}
FGUiButton {
button_type: FGUiButtonType::CharacterSelectCharacter
}
CharacterBox {
fighter_manifest: manifest_handle
}
BackgroundColor(Color::srgb(1.0, 1.0, 1.0))
Children [
Node {
width: percent(100)
}
Text("El Grande Padre")
TextFont {
font: FontSourceTemplate::Handle("fonts/roboto.ttf"),
font_size: px(crate::menus::style::TEXT_SIZE),
}
TextColor(Color::srgb(0.9, 0.9, 0.9))
BackgroundColor(Color::srgb(0.0, 0.0, 1.0))
]
}
}
}
@@ -0,0 +1,18 @@
use bevy::prelude::*;
pub struct FighterPortrait {
pub player_slot: usize
}
impl FighterPortrait {
pub fn create(player_slot: usize) -> impl Scene {
bsn![
Node {
max_width: px(300),
height: px(400),
flex_grow: 1.0
}
BackgroundColor(bevy::color::palettes::css::ORANGE)
]
}
}
+151
View File
@@ -0,0 +1,151 @@
use bevy::prelude::*;
use crate::{
fighter::manifest::{FighterManifest, FighterManifestRegistry},
input::{LocalInputAssignments, LocalInputSource},
menus::{MenuMarker, build_menu_with_props, cursor::FGMenuCursor, spawn_menu, wrap_menu},
};
pub mod character_box;
pub mod fighter_portrait;
#[derive(Component, FromTemplate)]
pub struct CharacterSelectScreen {
pub fighter_portait_container: Entity,
}
pub fn setup_character_select(
existing_menu: Option<Single<Entity, With<MenuMarker>>>,
commands: Commands,
manifest_registry: Res<FighterManifestRegistry>,
assignments: Res<LocalInputAssignments>,
manifests: Res<Assets<FighterManifest>>,
) {
let character_boxes = manifest_registry
.iter()
.map(|(_, v)| character_box::CharacterBox::scene(v.clone(), &manifests))
.collect::<Vec<_>>();
// Create existing portraits
let fighter_portraits = assignments
.iter()
.map(|(slot, _)| fighter_portrait::FighterPortrait::create(*slot))
.collect::<Vec<_>>();
let menu = bsn! {
// Characters
CharacterSelectScreen {
fighter_portait_container: #PortraitContainer
}
Node {
flex_direction: FlexDirection::Column
}
Children [
(Node {
flex_direction: FlexDirection::Column,
width: percent(100),
height: percent(100),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center
}
Children [
{character_boxes}
]
),
(
#PortraitContainer
Node {
flex_direction: FlexDirection::Row,
width: percent(100),
justify_content: JustifyContent::Center
}
Children [
{fighter_portraits}
]
)
]
};
spawn_menu(existing_menu, commands, menu);
}
pub fn create_portrait(
player_slot: usize,
container: Entity,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
mapping: Res<LocalInputAssignments>,
) {
let entity = commands
.spawn_scene(fighter_portrait::FighterPortrait::create(player_slot))
.id();
commands.entity(container).add_child(entity);
commands.spawn(
FGMenuCursor::create_cursor(player_slot, meshes, materials, mapping)
);
}
#[derive(Event)]
pub struct FighterSlotAssigned {
pub slot: usize,
}
pub fn handle_fighter_slot_addition(
addition: On<FighterSlotAssigned>,
query: Single<&CharacterSelectScreen>,
commands: Commands,
meshes: ResMut<Assets<Mesh>>,
materials: ResMut<Assets<ColorMaterial>>,
mapping: Res<LocalInputAssignments>,
) {
let slot = addition.slot;
create_portrait(
slot,
query.fighter_portait_container,
commands,
meshes,
materials,
mapping,
);
}
pub fn css_input(
key: Res<ButtonInput<KeyCode>>,
pads: Query<(Entity, &Gamepad)>,
mut assignments: ResMut<LocalInputAssignments>,
mut commands: Commands,
) {
if key.get_pressed().len() != 0 {
if let None = assignments
.iter()
.find(|(_, source)| matches!(source, LocalInputSource::Keyboard))
{
if let Some(free_idx) = assignments.get_free_player_index() {
assignments.push((free_idx, LocalInputSource::Keyboard));
info!("New keyboard assignment");
commands.trigger(FighterSlotAssigned { slot: free_idx });
}
}
}
for (entity, pad) in pads {
if pad.any_pressed(GamepadButton::all()) {
let existed = assignments
.iter()
.find(|(_, assignment)| {
if let LocalInputSource::Gamepad(assignment_entity) = assignment {
return entity == *assignment_entity;
}
false
})
.is_some();
if !existed && let Some(free_idx) = assignments.get_free_player_index() {
assignments.push((free_idx, LocalInputSource::Gamepad(entity)));
commands.trigger(FighterSlotAssigned { slot: free_idx });
}
}
}
}