221 lines
7.5 KiB
Rust
221 lines
7.5 KiB
Rust
|
use bevy::prelude::*;
|
||
|
|
||
|
use crate::{loading::TextureAssets, GameState};
|
||
|
|
||
|
pub struct MenuPlugin;
|
||
|
|
||
|
impl Plugin for MenuPlugin {
|
||
|
fn build(&self, app: &mut App) {
|
||
|
app.add_systems(OnEnter(GameState::Menu), setup)
|
||
|
.add_systems(Update, click_button.run_if(in_state(GameState::Menu)))
|
||
|
.add_systems(OnExit(GameState::Menu), cleanup);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Component)]
|
||
|
struct ButtonColours {
|
||
|
normal: Color,
|
||
|
hovered: Color,
|
||
|
}
|
||
|
|
||
|
impl Default for ButtonColours {
|
||
|
fn default() -> Self {
|
||
|
Self {
|
||
|
normal: Color::srgb(88.0 / 255.0, 91.0 / 255.0, 112.0 / 255.0),
|
||
|
hovered: Color::srgb(116.0 / 255.0, 199.0 / 255.0, 236.0 / 255.0),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Component)]
|
||
|
struct Menu;
|
||
|
|
||
|
fn setup(mut commands: Commands, textures: Res<TextureAssets>) {
|
||
|
commands.spawn((Camera2dBundle::default(), Menu));
|
||
|
commands
|
||
|
.spawn((
|
||
|
NodeBundle {
|
||
|
style: Style {
|
||
|
width: Val::Percent(100.0),
|
||
|
height: Val::Percent(100.0),
|
||
|
flex_direction: FlexDirection::Column,
|
||
|
align_items: AlignItems::Center,
|
||
|
justify_content: JustifyContent::Center,
|
||
|
..default()
|
||
|
},
|
||
|
..default()
|
||
|
},
|
||
|
Menu,
|
||
|
))
|
||
|
.with_children(|children| {
|
||
|
let button_colours = ButtonColours::default();
|
||
|
children
|
||
|
.spawn((
|
||
|
ButtonBundle {
|
||
|
style: Style {
|
||
|
width: Val::Px(140.0),
|
||
|
height: Val::Px(90.0),
|
||
|
justify_content: JustifyContent::Center,
|
||
|
align_items: AlignItems::Center,
|
||
|
..default()
|
||
|
},
|
||
|
background_color: button_colours.normal.into(),
|
||
|
..default()
|
||
|
},
|
||
|
button_colours,
|
||
|
ChangeState(GameState::Playing),
|
||
|
))
|
||
|
.with_children(|parent| {
|
||
|
parent.spawn(TextBundle::from_section(
|
||
|
"Play",
|
||
|
TextStyle {
|
||
|
font_size: 40.0,
|
||
|
color: Color::srgb(205.0 / 255.0, 214.0 / 255.0, 244.0 / 255.0),
|
||
|
..default()
|
||
|
},
|
||
|
));
|
||
|
});
|
||
|
});
|
||
|
commands
|
||
|
.spawn((
|
||
|
NodeBundle {
|
||
|
style: Style {
|
||
|
flex_direction: FlexDirection::Row,
|
||
|
align_items: AlignItems::Center,
|
||
|
justify_content: JustifyContent::SpaceAround,
|
||
|
bottom: Val::Px(5.),
|
||
|
width: Val::Percent(100.),
|
||
|
position_type: PositionType::Absolute,
|
||
|
..default()
|
||
|
},
|
||
|
..default()
|
||
|
},
|
||
|
Menu,
|
||
|
))
|
||
|
.with_children(|children| {
|
||
|
children
|
||
|
.spawn((
|
||
|
ButtonBundle {
|
||
|
style: Style {
|
||
|
width: Val::Px(170.0),
|
||
|
height: Val::Px(50.0),
|
||
|
justify_content: JustifyContent::SpaceAround,
|
||
|
align_items: AlignItems::Center,
|
||
|
padding: UiRect::all(Val::Px(5.)),
|
||
|
..Default::default()
|
||
|
},
|
||
|
background_color: Color::NONE.into(),
|
||
|
..Default::default()
|
||
|
},
|
||
|
ButtonColours {
|
||
|
normal: Color::NONE,
|
||
|
..default()
|
||
|
},
|
||
|
OpenLink("https://bevyengine.org"),
|
||
|
))
|
||
|
.with_children(|parent| {
|
||
|
parent.spawn(TextBundle::from_section(
|
||
|
"Made with Bevy",
|
||
|
TextStyle {
|
||
|
font_size: 15.0,
|
||
|
color: Color::srgb(205.0 / 255.0, 214.0 / 255.0, 244.0 / 255.0),
|
||
|
..default()
|
||
|
},
|
||
|
));
|
||
|
parent.spawn(ImageBundle {
|
||
|
image: textures.bevy.clone().into(),
|
||
|
style: Style {
|
||
|
width: Val::Px(32.),
|
||
|
..default()
|
||
|
},
|
||
|
..default()
|
||
|
});
|
||
|
});
|
||
|
children
|
||
|
.spawn((
|
||
|
ButtonBundle {
|
||
|
style: Style {
|
||
|
width: Val::Px(170.0),
|
||
|
height: Val::Px(50.0),
|
||
|
justify_content: JustifyContent::SpaceAround,
|
||
|
align_items: AlignItems::Center,
|
||
|
padding: UiRect::all(Val::Px(5.)),
|
||
|
..default()
|
||
|
},
|
||
|
background_color: Color::NONE.into(),
|
||
|
..Default::default()
|
||
|
},
|
||
|
ButtonColours {
|
||
|
normal: Color::NONE,
|
||
|
..default()
|
||
|
},
|
||
|
OpenLink("https://git.ragarock.moe/silvana/yuno"),
|
||
|
))
|
||
|
.with_children(|parent| {
|
||
|
parent.spawn(TextBundle::from_section(
|
||
|
"repository",
|
||
|
TextStyle {
|
||
|
font_size: 15.0,
|
||
|
color: Color::srgb(205.0 / 255.0, 214.0 / 255.0, 244.0 / 255.0),
|
||
|
..default()
|
||
|
},
|
||
|
));
|
||
|
parent.spawn(ImageBundle {
|
||
|
image: textures.forgejo.clone().into(),
|
||
|
style: Style {
|
||
|
width: Val::Px(32.),
|
||
|
..default()
|
||
|
},
|
||
|
..default()
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#[derive(Component)]
|
||
|
struct ChangeState(GameState);
|
||
|
|
||
|
#[derive(Component)]
|
||
|
struct OpenLink(&'static str);
|
||
|
|
||
|
fn click_button(
|
||
|
mut next_state: ResMut<NextState<GameState>>,
|
||
|
mut interaction_query: Query<
|
||
|
(
|
||
|
&Interaction,
|
||
|
&mut BackgroundColor,
|
||
|
&ButtonColours,
|
||
|
Option<&ChangeState>,
|
||
|
Option<&OpenLink>,
|
||
|
),
|
||
|
(Changed<Interaction>, With<Button>),
|
||
|
>,
|
||
|
) {
|
||
|
for (interaction, mut colour, button_colours, change_state, open_link) in &mut interaction_query
|
||
|
{
|
||
|
match *interaction {
|
||
|
Interaction::Pressed => {
|
||
|
if let Some(state) = change_state {
|
||
|
next_state.set(state.0.clone());
|
||
|
} else if let Some(link) = open_link {
|
||
|
if let Err(error) = webbrowser::open(link.0) {
|
||
|
warn!("failed to open link {error:?}");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Interaction::Hovered => {
|
||
|
*colour = button_colours.hovered.into();
|
||
|
}
|
||
|
Interaction::None => {
|
||
|
*colour = button_colours.normal.into();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn cleanup(mut commands: Commands, menu: Query<Entity, With<Menu>>) {
|
||
|
for entity in menu.iter() {
|
||
|
commands.entity(entity).despawn_recursive();
|
||
|
}
|
||
|
}
|