implement game menu
This commit is contained in:
parent
2cf10f2395
commit
eb681999f8
|
@ -35,7 +35,7 @@ impl Plugin for ActorPlugin {
|
|||
.after(PhysicsSet::Sync)
|
||||
.after(sync::position_to_transform));
|
||||
app.add_systems(Update, (
|
||||
handle_input.run_if(alive),
|
||||
handle_input.run_if(in_control),
|
||||
handle_collisions,
|
||||
handle_damage,
|
||||
));
|
||||
|
|
14
src/audio.rs
14
src/audio.rs
|
@ -19,7 +19,7 @@ pub struct AudioPlugin;
|
|||
impl Plugin for AudioPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, handle_input.run_if(alive));
|
||||
app.add_systems(Update, handle_input.run_if(in_control));
|
||||
app.add_systems(Update, respawn_sinks.run_if(on_event::<RespawnSinksEvent>()));
|
||||
app.add_systems(Update, play_zoom_sfx);
|
||||
app.add_systems(Update, pause_all.run_if(on_event::<PauseAllSfxEvent>()));
|
||||
|
@ -160,19 +160,19 @@ pub fn respawn_sinks(
|
|||
|
||||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut evwriter_toggle: EventWriter<ToggleMusicEvent>,
|
||||
mut evwriter_sfx: EventWriter<PlaySfxEvent>,
|
||||
mut ew_toggle: EventWriter<ToggleMusicEvent>,
|
||||
mut ew_sfx: EventWriter<PlaySfxEvent>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_toggle_music) {
|
||||
settings.mute_music ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
ew_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
ew_toggle.send(ToggleMusicEvent());
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_toggle_sfx) {
|
||||
settings.mute_sfx ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
ew_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
ew_toggle.send(ToggleMusicEvent());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ pub struct CameraPlugin;
|
|||
impl Plugin for CameraPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup_camera);
|
||||
app.add_systems(Update, handle_input.run_if(alive));
|
||||
app.add_systems(Update, handle_input.run_if(in_control));
|
||||
app.add_systems(Update, update_map_only_object_visibility.run_if(alive));
|
||||
app.add_systems(Update, manage_player_actor.after(handle_input));
|
||||
app.add_systems(PostUpdate, sync_camera_to_player
|
||||
|
@ -391,7 +391,7 @@ pub fn apply_input_to_player(
|
|||
Option<&actor::PlayerDrivesThis>,
|
||||
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
) {
|
||||
if settings.map_active {
|
||||
if settings.map_active || !settings.in_control() {
|
||||
return;
|
||||
}
|
||||
let dt = time.delta_seconds();
|
||||
|
|
|
@ -59,3 +59,7 @@ pub fn style_centered() -> Style {
|
|||
pub fn alive(settings: Res<Settings>) -> bool {
|
||||
return settings.alive;
|
||||
}
|
||||
|
||||
pub fn in_control(settings: Res<Settings>) -> bool {
|
||||
return settings.in_control();
|
||||
}
|
||||
|
|
78
src/game.rs
78
src/game.rs
|
@ -14,24 +14,100 @@ use crate::prelude::*;
|
|||
use bevy::prelude::*;
|
||||
use bevy::pbr::ExtendedMaterial;
|
||||
use bevy::scene::SceneInstance;
|
||||
use bevy::window::{Window, WindowMode, PrimaryWindow};
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct GamePlugin;
|
||||
impl Plugin for GamePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, handle_cheats);
|
||||
app.add_systems(Update, handle_cheats.run_if(in_control));
|
||||
app.add_systems(Update, debug);
|
||||
app.add_systems(Update, handle_game_event);
|
||||
app.add_systems(PreUpdate, handle_player_death);
|
||||
app.add_systems(PostUpdate, update_id2pos);
|
||||
app.insert_resource(Id2Pos(HashMap::new()));
|
||||
app.add_event::<PlayerDiesEvent>();
|
||||
app.add_event::<GameEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType);
|
||||
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||
|
||||
#[derive(Event)]
|
||||
pub enum GameEvent {
|
||||
SetAR(Turn),
|
||||
SetMusic(Turn),
|
||||
SetSound(Turn),
|
||||
SetMap(Turn),
|
||||
SetFullscreen(Turn),
|
||||
}
|
||||
|
||||
pub enum Turn {
|
||||
On,
|
||||
Off,
|
||||
Toggle,
|
||||
}
|
||||
|
||||
impl Turn {
|
||||
pub fn to_bool(&self, current_state: bool) -> bool {
|
||||
match self {
|
||||
Turn::On => true,
|
||||
Turn::Off => false,
|
||||
Turn::Toggle => !current_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_game_event(
|
||||
mut settings: ResMut<Settings>,
|
||||
mut er_game: EventReader<GameEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_updateoverlays: EventWriter<hud::UpdateOverlayVisibility>,
|
||||
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
|
||||
mut q_window: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut mapcam: ResMut<camera::MapCam>,
|
||||
opt: Res<var::CommandLineOptions>,
|
||||
) {
|
||||
for event in er_game.read() {
|
||||
match event {
|
||||
GameEvent::SetAR(turn) => {
|
||||
settings.hud_active = turn.to_bool(settings.hud_active);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||
}
|
||||
GameEvent::SetMusic(turn) => {
|
||||
// TODO invert "mute_music" to "music_active"
|
||||
settings.mute_music = turn.to_bool(settings.mute_music);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
}
|
||||
GameEvent::SetSound(turn) => {
|
||||
// TODO invert "mute_sfx" to "sfx_active"
|
||||
settings.mute_sfx = turn.to_bool(settings.mute_sfx);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
}
|
||||
GameEvent::SetMap(turn) => {
|
||||
settings.map_active = turn.to_bool(settings.map_active);
|
||||
if settings.map_active {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Woosh));
|
||||
}
|
||||
*mapcam = camera::MapCam::default();
|
||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||
}
|
||||
GameEvent::SetFullscreen(turn) => {
|
||||
for mut window in &mut q_window {
|
||||
let current_state = window.mode != WindowMode::Windowed;
|
||||
window.mode = match turn.to_bool(current_state) {
|
||||
true => opt.window_mode_fullscreen,
|
||||
false => WindowMode::Windowed
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_player_death(
|
||||
mut cmd: Commands,
|
||||
mut er_playerdies: EventReader<PlayerDiesEvent>,
|
||||
|
|
11
src/hud.rs
11
src/hud.rs
|
@ -41,7 +41,7 @@ impl Plugin for HudPlugin {
|
|||
update_dashboard,
|
||||
update_speedometer,
|
||||
update_gauges,
|
||||
handle_input.run_if(alive),
|
||||
handle_input.run_if(in_control),
|
||||
handle_target_event,
|
||||
));
|
||||
app.add_systems(PostUpdate, (
|
||||
|
@ -938,12 +938,11 @@ fn update_hud(
|
|||
fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||
mut settings: ResMut<Settings>,
|
||||
settings: Res<Settings>,
|
||||
mut log: ResMut<Log>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
|
||||
mut ew_target: EventWriter<TargetEvent>,
|
||||
mut ew_updateoverlays: EventWriter<UpdateOverlayVisibility>,
|
||||
mut ew_game: EventWriter<GameEvent>,
|
||||
q_objects: Query<(Entity, &Transform), (With<IsClickable>, Without<IsTargeted>, Without<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
||||
q_camera: Query<&Transform, With<Camera>>,
|
||||
) {
|
||||
|
@ -954,10 +953,8 @@ fn handle_input(
|
|||
}
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_togglehud) {
|
||||
settings.hud_active ^= true;
|
||||
ew_game.send(GameEvent::SetAR(Turn::Toggle));
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
ew_updateoverlays.send(UpdateOverlayVisibility);
|
||||
}
|
||||
if settings.hud_active && mouse_input.just_pressed(settings.key_selectobject) {
|
||||
if let Ok(camtrans) = q_camera.get_single() {
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -33,6 +33,7 @@ pub mod prelude {
|
|||
pub use crate::common::*;
|
||||
pub use crate::var::Settings;
|
||||
pub use crate::load::load_asset;
|
||||
pub use game::{GameEvent, Turn};
|
||||
}
|
||||
|
||||
use bevy::window::{Window, WindowMode, PrimaryWindow, CursorGrabMode};
|
||||
|
@ -54,7 +55,7 @@ Note: borderless fullscreen is the default, but it crashes on some systems.";
|
|||
|
||||
fn main() {
|
||||
let prefs = var::load_prefs();
|
||||
let mut opt = CommandLineOptions {
|
||||
let mut opt = var::CommandLineOptions {
|
||||
window_mode_fullscreen: prefs.get_fullscreen_mode(),
|
||||
window_mode_initial: prefs.get_window_mode(),
|
||||
use_gl: prefs.render_mode_is_gl(),
|
||||
|
@ -146,16 +147,9 @@ impl Plugin for OutFlyPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct CommandLineOptions {
|
||||
window_mode_fullscreen: WindowMode,
|
||||
window_mode_initial: WindowMode,
|
||||
use_gl: bool,
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
opt: Res<CommandLineOptions>,
|
||||
opt: Res<var::CommandLineOptions>,
|
||||
) {
|
||||
for mut window in &mut windows {
|
||||
window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||
|
@ -168,14 +162,10 @@ fn setup(
|
|||
fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
settings: Res<var::Settings>,
|
||||
opt: Res<CommandLineOptions>,
|
||||
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
|
||||
opt: Res<var::CommandLineOptions>,
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if keyboard_input.pressed(settings.key_exit) {
|
||||
app_exit_events.send(bevy::app::AppExit);
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_fullscreen) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
for mut window in &mut windows {
|
||||
|
|
163
src/menu.rs
163
src/menu.rs
|
@ -22,22 +22,44 @@ impl Plugin for MenuPlugin {
|
|||
app.add_systems(Startup, setup.after(hud::setup));
|
||||
app.add_systems(PreUpdate, show_deathscreen.run_if(on_event::<DeathScreenEvent>()));
|
||||
app.add_systems(Update, handle_deathscreen_input);
|
||||
app.add_systems(PostUpdate, update_menu);
|
||||
app.add_systems(Update, handle_input.run_if(alive));
|
||||
app.insert_resource(MenuState::default());
|
||||
app.add_event::<DeathScreenEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)] pub struct MenuElement;
|
||||
#[derive(Component)] pub struct MenuTopLevel;
|
||||
#[derive(Component)] pub struct DeathScreenElement;
|
||||
#[derive(Component)] pub struct BlackScreen;
|
||||
#[derive(Component)] pub struct DeathText;
|
||||
#[derive(Event, PartialEq)] pub enum DeathScreenEvent { Show, Hide }
|
||||
|
||||
pub const MENUDEF: &[(&str, MenuAction)] = &[
|
||||
("Open Map", MenuAction::ToggleMap),
|
||||
("Toggle Augmented Reality", MenuAction::ToggleAR),
|
||||
("Toggle Sound", MenuAction::ToggleSound),
|
||||
("Toggle Music", MenuAction::ToggleMusic),
|
||||
("Toggle Fullscreen", MenuAction::ToggleFullscreen),
|
||||
("Quit", MenuAction::Quit),
|
||||
];
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum MenuAction {
|
||||
ToggleMap,
|
||||
ToggleAR,
|
||||
ToggleSound,
|
||||
ToggleMusic,
|
||||
ToggleFullscreen,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
commands.spawn((
|
||||
BlackScreen,
|
||||
DeathScreenElement,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
|
@ -98,6 +120,54 @@ pub fn setup(
|
|||
},
|
||||
));
|
||||
});
|
||||
|
||||
let style_menu = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_hud,
|
||||
color: settings.hud_color,
|
||||
..default()
|
||||
};
|
||||
|
||||
let sections: Vec<TextSection> = Vec::from_iter(MENUDEF.iter().map(|(label, _)|
|
||||
TextSection::new(label.to_string() + "\n", style_menu.clone())
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
background_color: Color::rgba(0.0, 0.0, 0.0, 0.8).into(),
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
MenuTopLevel,
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections,
|
||||
justify: JustifyText::Center,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn show_deathscreen(
|
||||
|
@ -151,3 +221,92 @@ pub fn handle_deathscreen_input(
|
|||
ew_deathscreen.send(DeathScreenEvent::Hide);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Debug, Default)]
|
||||
pub struct MenuState {
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
pub fn update_menu(
|
||||
mut q_text: Query<&mut Text, With<MenuTopLevel>>,
|
||||
menustate: Res<MenuState>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
if !settings.menu_active {
|
||||
return;
|
||||
}
|
||||
if let Ok(mut text) = q_text.get_single_mut() {
|
||||
for i in 0..text.sections.len() {
|
||||
if menustate.cursor == i {
|
||||
text.sections[i].style.color = settings.hud_color_subtitles;
|
||||
} else {
|
||||
text.sections[i].style.color = settings.hud_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<Settings>,
|
||||
mut q_vis: Query<&mut Visibility, With<MenuElement>>,
|
||||
mut menustate: ResMut<MenuState>,
|
||||
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
|
||||
mut ew_game: EventWriter<game::GameEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_menu)
|
||||
|| keyboard_input.just_pressed(settings.key_vehicle) && settings.menu_active
|
||||
{
|
||||
settings.menu_active ^= true;
|
||||
for mut vis in &mut q_vis {
|
||||
*vis = bool2vis(settings.menu_active);
|
||||
}
|
||||
}
|
||||
if !settings.menu_active {
|
||||
return;
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_forward)
|
||||
|| keyboard_input.just_pressed(settings.key_mouseup)
|
||||
|| keyboard_input.just_pressed(KeyCode::ArrowUp)
|
||||
{
|
||||
menustate.cursor = menustate.cursor.saturating_sub(1);
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_back)
|
||||
|| keyboard_input.just_pressed(settings.key_mousedown)
|
||||
|| keyboard_input.just_pressed(KeyCode::ArrowDown)
|
||||
{
|
||||
menustate.cursor = (menustate.cursor + 1).min(MENUDEF.len() - 1);
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_interact)
|
||||
|| keyboard_input.just_pressed(KeyCode::Enter)
|
||||
{
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
match MENUDEF[menustate.cursor].1 {
|
||||
MenuAction::ToggleMap => {
|
||||
ew_game.send(GameEvent::SetMap(Turn::Toggle));
|
||||
settings.menu_active = false;
|
||||
for mut vis in &mut q_vis {
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
},
|
||||
MenuAction::ToggleAR => {
|
||||
ew_game.send(GameEvent::SetAR(Turn::Toggle));
|
||||
},
|
||||
MenuAction::ToggleMusic => {
|
||||
ew_game.send(GameEvent::SetMusic(Turn::Toggle));
|
||||
},
|
||||
MenuAction::ToggleSound => {
|
||||
ew_game.send(GameEvent::SetSound(Turn::Toggle));
|
||||
},
|
||||
MenuAction::ToggleFullscreen => {
|
||||
ew_game.send(GameEvent::SetFullscreen(Turn::Toggle));
|
||||
},
|
||||
MenuAction::Quit => {
|
||||
app_exit_events.send(bevy::app::AppExit);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
17
src/var.rs
17
src/var.rs
|
@ -71,6 +71,7 @@ pub struct Settings {
|
|||
pub hud_active: bool,
|
||||
pub map_active: bool,
|
||||
pub deathscreen_active: bool,
|
||||
pub menu_active: bool,
|
||||
pub death_cause: String,
|
||||
pub is_zooming: bool,
|
||||
pub third_person: bool,
|
||||
|
@ -87,7 +88,7 @@ pub struct Settings {
|
|||
//pub key_map_zoom_out_wheel: MouseButton,
|
||||
//pub key_map_zoom_in_wheel: MouseButton,
|
||||
pub key_togglehud: KeyCode,
|
||||
pub key_exit: KeyCode,
|
||||
pub key_menu: KeyCode,
|
||||
pub key_restart: KeyCode,
|
||||
pub key_fullscreen: KeyCode,
|
||||
pub key_help: KeyCode,
|
||||
|
@ -187,6 +188,7 @@ impl Default for Settings {
|
|||
hud_active: true,
|
||||
map_active: false,
|
||||
deathscreen_active: false,
|
||||
menu_active: false,
|
||||
death_cause: "Unknown".to_string(),
|
||||
is_zooming: false,
|
||||
third_person: true,
|
||||
|
@ -203,7 +205,7 @@ impl Default for Settings {
|
|||
//key_map_zoom_out_wheel: KeyCode::Shift,
|
||||
//key_map_zoom_in_wheel: KeyCode::Shift,
|
||||
key_togglehud: KeyCode::Tab,
|
||||
key_exit: KeyCode::Escape,
|
||||
key_menu: KeyCode::Escape,
|
||||
key_restart: KeyCode::F7,
|
||||
key_fullscreen: KeyCode::F11,
|
||||
key_help: KeyCode::F1,
|
||||
|
@ -287,6 +289,10 @@ impl Settings {
|
|||
self.key_reply10,
|
||||
];
|
||||
}
|
||||
|
||||
pub fn in_control(&self) -> bool {
|
||||
return self.alive && !self.menu_active;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Deserialize, Debug, Default)]
|
||||
|
@ -574,3 +580,10 @@ impl GameVars {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct CommandLineOptions {
|
||||
pub window_mode_fullscreen: WindowMode,
|
||||
pub window_mode_initial: WindowMode,
|
||||
pub use_gl: bool,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue