2024-05-12 20:17:17 +00:00
|
|
|
// ▄████████▄ + ███ + ▄█████████ ███ +
|
|
|
|
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
|
|
|
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
|
|
|
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
|
|
|
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
|
|
|
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
|
|
|
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
|
|
|
// + + + ███
|
|
|
|
// + ▀████████████████████████████████████████████████████▀
|
|
|
|
//
|
|
|
|
// This plugin manages game menus and the player death screen
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
use bevy::prelude::*;
|
2024-05-12 23:42:22 +00:00
|
|
|
use fastrand;
|
|
|
|
|
|
|
|
pub const POEMS: &str = &include_str!("data/deathpoems.in");
|
2024-05-12 20:17:17 +00:00
|
|
|
|
|
|
|
pub struct MenuPlugin;
|
|
|
|
impl Plugin for MenuPlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
app.add_systems(Startup, setup.after(hud::setup));
|
2024-05-22 02:57:20 +00:00
|
|
|
app.add_systems(
|
|
|
|
PreUpdate,
|
|
|
|
show_deathscreen.run_if(on_event::<DeathScreenEvent>()),
|
|
|
|
);
|
2024-05-12 20:17:17 +00:00
|
|
|
app.add_systems(Update, handle_deathscreen_input);
|
2024-05-22 02:57:20 +00:00
|
|
|
app.add_systems(
|
|
|
|
PostUpdate,
|
|
|
|
update_menu
|
|
|
|
.after(game::handle_game_event)
|
|
|
|
.run_if(on_event::<UpdateMenuEvent>()),
|
|
|
|
);
|
2024-05-13 18:21:56 +00:00
|
|
|
app.add_systems(Update, handle_input.run_if(alive));
|
2024-05-22 02:57:20 +00:00
|
|
|
app.insert_resource(DeathScreenInputDelayTimer(Timer::from_seconds(
|
|
|
|
1.0,
|
|
|
|
TimerMode::Once,
|
|
|
|
)));
|
2024-05-13 18:21:56 +00:00
|
|
|
app.insert_resource(MenuState::default());
|
2024-05-12 20:17:17 +00:00
|
|
|
app.add_event::<DeathScreenEvent>();
|
2024-05-13 19:37:57 +00:00
|
|
|
app.add_event::<UpdateMenuEvent>();
|
2024-05-12 20:17:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
#[derive(Resource)]
|
|
|
|
pub struct DeathScreenInputDelayTimer(pub Timer);
|
|
|
|
#[derive(Component)]
|
|
|
|
pub struct MenuElement;
|
|
|
|
#[derive(Component)]
|
|
|
|
pub struct MenuTopLevel;
|
|
|
|
#[derive(Component)]
|
2024-10-26 19:22:03 +00:00
|
|
|
pub struct FooterElement;
|
|
|
|
#[derive(Component)]
|
2024-05-22 02:57:20 +00:00
|
|
|
pub struct MenuAchievements;
|
|
|
|
#[derive(Component)]
|
|
|
|
pub struct DeathScreenElement;
|
|
|
|
#[derive(Component)]
|
|
|
|
pub struct DeathText;
|
|
|
|
#[derive(Event)]
|
|
|
|
pub struct UpdateMenuEvent;
|
|
|
|
#[derive(Event, PartialEq)]
|
|
|
|
pub enum DeathScreenEvent {
|
|
|
|
Show,
|
|
|
|
Hide,
|
|
|
|
}
|
2024-05-12 20:17:17 +00:00
|
|
|
|
2024-05-13 18:21:56 +00:00
|
|
|
pub const MENUDEF: &[(&str, MenuAction)] = &[
|
2024-10-26 17:36:54 +00:00
|
|
|
("Phone Call", MenuAction::PhoneCall),
|
2024-05-13 19:37:57 +00:00
|
|
|
("", MenuAction::ToggleAR),
|
2024-05-23 03:02:59 +00:00
|
|
|
("", MenuAction::ChangeARAvatar),
|
2024-10-12 03:16:04 +00:00
|
|
|
("", MenuAction::ChangePointer),
|
2024-09-22 14:21:18 +00:00
|
|
|
("", MenuAction::ToggleMap),
|
2024-09-22 01:10:11 +00:00
|
|
|
("", MenuAction::ModLightAmp),
|
2024-09-22 01:40:12 +00:00
|
|
|
("", MenuAction::ModFlashlightPower),
|
2024-09-22 02:34:36 +00:00
|
|
|
("", MenuAction::ModThrusterBoost),
|
2024-09-22 04:38:44 +00:00
|
|
|
("", MenuAction::ModReactor),
|
2024-05-13 19:37:57 +00:00
|
|
|
("", MenuAction::ToggleSound),
|
|
|
|
("", MenuAction::ToggleMusic),
|
2024-05-13 21:51:18 +00:00
|
|
|
("", MenuAction::ToggleCamera),
|
2024-05-13 19:37:57 +00:00
|
|
|
("", MenuAction::ToggleShadows),
|
2024-09-22 14:21:18 +00:00
|
|
|
("Fullscreen [F11]", MenuAction::ToggleFullscreen),
|
2024-05-14 17:12:36 +00:00
|
|
|
("Take Off Helmet", MenuAction::Restart),
|
2024-05-13 18:21:56 +00:00
|
|
|
("Quit", MenuAction::Quit),
|
|
|
|
];
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
pub enum MenuAction {
|
|
|
|
ToggleMap,
|
|
|
|
ToggleAR,
|
2024-05-23 03:02:59 +00:00
|
|
|
ChangeARAvatar,
|
2024-10-12 03:16:04 +00:00
|
|
|
ChangePointer,
|
2024-09-22 01:10:11 +00:00
|
|
|
ModLightAmp,
|
2024-09-22 01:40:12 +00:00
|
|
|
ModFlashlightPower,
|
2024-09-22 02:34:36 +00:00
|
|
|
ModThrusterBoost,
|
2024-09-22 04:38:44 +00:00
|
|
|
ModReactor,
|
2024-05-13 18:21:56 +00:00
|
|
|
ToggleSound,
|
|
|
|
ToggleMusic,
|
2024-10-26 17:36:54 +00:00
|
|
|
PhoneCall,
|
2024-05-13 21:51:18 +00:00
|
|
|
ToggleCamera,
|
2024-05-13 18:21:56 +00:00
|
|
|
ToggleFullscreen,
|
2024-05-13 19:11:27 +00:00
|
|
|
ToggleShadows,
|
2024-05-13 18:53:08 +00:00
|
|
|
Restart,
|
2024-05-13 18:21:56 +00:00
|
|
|
Quit,
|
|
|
|
}
|
|
|
|
|
2024-05-12 20:17:17 +00:00
|
|
|
pub fn setup(
|
|
|
|
mut commands: Commands,
|
|
|
|
asset_server: Res<AssetServer>,
|
2024-05-14 03:33:35 +00:00
|
|
|
achievement_tracker: Res<var::AchievementTracker>,
|
2024-05-12 20:17:17 +00:00
|
|
|
settings: Res<Settings>,
|
|
|
|
) {
|
|
|
|
commands.spawn((
|
|
|
|
DeathScreenElement,
|
|
|
|
NodeBundle {
|
2024-05-12 22:44:03 +00:00
|
|
|
style: style_fullscreen(),
|
2024-05-12 20:17:17 +00:00
|
|
|
background_color: Color::BLACK.into(),
|
|
|
|
visibility: Visibility::Hidden,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
|
|
|
|
let font_handle = asset_server.load(FONT);
|
|
|
|
let style_death = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_deathtext,
|
2024-05-14 04:28:14 +00:00
|
|
|
color: settings.hud_color_death,
|
2024-05-12 20:17:17 +00:00
|
|
|
..default()
|
|
|
|
};
|
2024-05-12 23:42:22 +00:00
|
|
|
let style_death_poem = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_deathpoem,
|
|
|
|
color: settings.hud_color_deathpoem,
|
|
|
|
..default()
|
|
|
|
};
|
2024-05-12 20:17:17 +00:00
|
|
|
let style_death_subtext = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_deathsubtext,
|
2024-05-14 04:28:14 +00:00
|
|
|
color: settings.hud_color_death,
|
2024-05-12 20:17:17 +00:00
|
|
|
..default()
|
|
|
|
};
|
|
|
|
let style_death_subsubtext = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_deathsubtext * 0.8,
|
2024-05-14 04:28:14 +00:00
|
|
|
color: settings.hud_color_death,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
let style_death_achievements = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_death_achievements,
|
|
|
|
color: settings.hud_color_death_achievements,
|
2024-05-12 20:17:17 +00:00
|
|
|
..default()
|
|
|
|
};
|
2024-05-22 02:57:20 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
DeathScreenElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: style_centered(),
|
|
|
|
visibility: Visibility::Hidden,
|
2024-05-12 20:17:17 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
|
|
|
builder.spawn((
|
|
|
|
DeathText,
|
|
|
|
TextBundle {
|
|
|
|
text: Text {
|
|
|
|
sections: vec![
|
|
|
|
TextSection::new("", style_death_poem),
|
|
|
|
TextSection::new("You are dead.\n", style_death),
|
|
|
|
TextSection::new("Cause: ", style_death_subtext.clone()),
|
|
|
|
TextSection::new("Unknown", style_death_subtext),
|
|
|
|
TextSection::new("", style_death_achievements),
|
|
|
|
TextSection::new(
|
|
|
|
"\n\n\n\nPress E to begin anew.",
|
|
|
|
style_death_subsubtext,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
justify: JustifyText::Center,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
});
|
2024-05-13 18:21:56 +00:00
|
|
|
|
|
|
|
let style_menu = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_hud,
|
|
|
|
color: settings.hud_color,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
let sections: Vec<TextSection> = Vec::from_iter(
|
|
|
|
MENUDEF
|
|
|
|
.iter()
|
|
|
|
.map(|(label, _)| TextSection::new(label.to_string() + "\n", style_menu.clone())),
|
|
|
|
);
|
2024-05-13 18:21:56 +00:00
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
MenuElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: style_fullscreen(),
|
2024-07-09 00:32:38 +00:00
|
|
|
background_color: Color::srgba(0.0, 0.0, 0.0, 0.8).into(),
|
2024-05-13 18:21:56 +00:00
|
|
|
visibility: Visibility::Hidden,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
MenuElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
height: Val::Percent(100.0),
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
justify_content: JustifyContent::SpaceAround,
|
2024-05-13 18:21:56 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
visibility: Visibility::Hidden,
|
2024-05-13 18:21:56 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
|
|
|
builder.spawn((
|
|
|
|
MenuTopLevel,
|
|
|
|
TextBundle {
|
|
|
|
text: Text {
|
|
|
|
sections,
|
|
|
|
justify: JustifyText::Center,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
});
|
2024-05-14 03:17:32 +00:00
|
|
|
|
|
|
|
let style_achievement_header = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_achievement_header,
|
|
|
|
color: settings.hud_color_achievement_header,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
let style_achievement = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_achievement,
|
|
|
|
color: settings.hud_color_achievement,
|
|
|
|
..default()
|
|
|
|
};
|
2024-05-14 03:33:35 +00:00
|
|
|
let achievement_count = achievement_tracker.to_bool_vec().len();
|
2024-05-14 03:17:32 +00:00
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
MenuElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
height: Val::Percent(100.0),
|
|
|
|
left: Val::Percent(2.0),
|
|
|
|
top: Val::Percent(2.0),
|
|
|
|
align_items: AlignItems::Start,
|
|
|
|
justify_content: JustifyContent::Start,
|
2024-05-14 03:17:32 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
visibility: Visibility::Hidden,
|
2024-05-14 03:17:32 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
|
|
|
let mut sections = vec![TextSection::new(
|
|
|
|
"Achievements\n",
|
|
|
|
style_achievement_header.clone(),
|
|
|
|
)];
|
|
|
|
sections.extend(Vec::from_iter(
|
|
|
|
(0..achievement_count).map(|_| TextSection::new("", style_achievement.clone())),
|
|
|
|
));
|
|
|
|
builder.spawn((
|
|
|
|
MenuAchievements,
|
|
|
|
TextBundle {
|
|
|
|
text: Text {
|
|
|
|
sections,
|
|
|
|
justify: JustifyText::Left,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
});
|
2024-05-14 16:32:23 +00:00
|
|
|
|
|
|
|
let keybindings = include_str!("data/keybindings.in");
|
|
|
|
let style_keybindings = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_keybindings,
|
|
|
|
color: settings.hud_color_keybindings,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
MenuElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
width: Val::Percent(96.0),
|
|
|
|
height: Val::Percent(96.0),
|
|
|
|
left: Val::Percent(2.0),
|
|
|
|
top: Val::Percent(2.0),
|
|
|
|
align_items: AlignItems::Start,
|
|
|
|
justify_content: JustifyContent::End,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
visibility: Visibility::Hidden,
|
2024-05-14 16:32:23 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
|
|
|
builder.spawn((TextBundle {
|
2024-05-14 16:32:23 +00:00
|
|
|
text: Text {
|
2024-05-14 16:43:38 +00:00
|
|
|
sections: vec![
|
2024-05-14 17:40:53 +00:00
|
|
|
TextSection::new("Controls\n", style_achievement_header),
|
2024-05-22 02:57:20 +00:00
|
|
|
TextSection::new(keybindings, style_keybindings),
|
2024-05-14 16:43:38 +00:00
|
|
|
],
|
2024-05-14 16:32:23 +00:00
|
|
|
justify: JustifyText::Right,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
2024-05-22 02:57:20 +00:00
|
|
|
},));
|
|
|
|
});
|
2024-05-14 17:08:18 +00:00
|
|
|
|
|
|
|
let style_version = TextStyle {
|
|
|
|
font: font_handle.clone(),
|
|
|
|
font_size: settings.font_size_version,
|
|
|
|
color: settings.hud_color_version,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
MenuElement,
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
width: Val::Percent(96.0),
|
|
|
|
height: Val::Percent(96.0),
|
|
|
|
left: Val::Percent(2.0),
|
|
|
|
top: Val::Percent(2.0),
|
|
|
|
align_items: AlignItems::End,
|
|
|
|
justify_content: JustifyContent::End,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
visibility: Visibility::Hidden,
|
2024-05-14 17:08:18 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-05-22 02:57:20 +00:00
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
2024-10-26 19:22:03 +00:00
|
|
|
builder.spawn((
|
|
|
|
FooterElement,
|
|
|
|
TextBundle {
|
|
|
|
text: Text {
|
|
|
|
sections: vec![TextSection::new(
|
|
|
|
format!("{} {}", GAME_NAME, settings.version.as_str()),
|
|
|
|
style_version,
|
|
|
|
)],
|
|
|
|
justify: JustifyText::Right,
|
|
|
|
..default()
|
|
|
|
},
|
2024-05-14 17:08:18 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-10-26 19:22:03 +00:00
|
|
|
));
|
2024-05-22 02:57:20 +00:00
|
|
|
});
|
2024-05-12 20:17:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn show_deathscreen(
|
|
|
|
mut er_deathscreen: EventReader<DeathScreenEvent>,
|
|
|
|
mut q_vis: Query<&mut Visibility, With<DeathScreenElement>>,
|
|
|
|
mut q_text: Query<&mut Text, With<DeathText>>,
|
2024-05-13 02:33:03 +00:00
|
|
|
mut ew_pausesfx: EventWriter<audio::PauseAllSfxEvent>,
|
2024-05-13 18:53:08 +00:00
|
|
|
mut ew_game: EventWriter<GameEvent>,
|
2024-05-12 20:17:17 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
|
|
|
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
2024-05-12 20:26:40 +00:00
|
|
|
mut ew_respawn: EventWriter<world::RespawnEvent>,
|
2024-05-13 02:41:17 +00:00
|
|
|
mut ew_respawnaudiosinks: EventWriter<audio::RespawnSinksEvent>,
|
2024-05-13 18:42:34 +00:00
|
|
|
mut timer: ResMut<DeathScreenInputDelayTimer>,
|
2024-05-13 18:53:08 +00:00
|
|
|
mut menustate: ResMut<MenuState>,
|
2024-05-12 20:17:17 +00:00
|
|
|
mut settings: ResMut<Settings>,
|
2024-05-14 04:28:14 +00:00
|
|
|
achievement_tracker: Res<var::AchievementTracker>,
|
2024-05-12 20:17:17 +00:00
|
|
|
) {
|
|
|
|
for event in er_deathscreen.read() {
|
|
|
|
let show = *event == DeathScreenEvent::Show;
|
|
|
|
for mut vis in &mut q_vis {
|
|
|
|
*vis = bool2vis(show);
|
|
|
|
}
|
|
|
|
settings.deathscreen_active = show;
|
2024-05-13 02:47:47 +00:00
|
|
|
settings.alive = !show;
|
2024-05-12 20:17:17 +00:00
|
|
|
|
|
|
|
if show {
|
2024-05-13 18:42:34 +00:00
|
|
|
timer.0.reset();
|
2024-05-13 18:53:08 +00:00
|
|
|
*menustate = MenuState::default();
|
|
|
|
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
2024-05-13 02:33:03 +00:00
|
|
|
ew_pausesfx.send(audio::PauseAllSfxEvent);
|
2024-05-12 20:17:17 +00:00
|
|
|
if let Ok(mut text) = q_text.get_single_mut() {
|
2024-05-12 23:42:22 +00:00
|
|
|
let poems: Vec<&str> = POEMS.split("\n\n").collect();
|
|
|
|
if poems.len() > 0 {
|
|
|
|
let poem_index = fastrand::usize(..poems.len());
|
|
|
|
let poem = poems[poem_index].to_string();
|
|
|
|
text.sections[0].value = poem + "\n\n\n\n";
|
|
|
|
}
|
|
|
|
text.sections[3].value = settings.death_cause.clone();
|
2024-05-14 04:28:14 +00:00
|
|
|
text.sections[4].value = achievement_tracker.to_summary();
|
2024-05-12 20:17:17 +00:00
|
|
|
}
|
|
|
|
} else {
|
2024-05-13 02:41:17 +00:00
|
|
|
ew_respawnaudiosinks.send(audio::RespawnSinksEvent);
|
2024-05-12 20:17:17 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
2024-05-22 02:57:20 +00:00
|
|
|
ew_effect.send(visual::SpawnEffectEvent {
|
|
|
|
class: visual::Effects::FadeIn(Color::BLACK),
|
|
|
|
duration: 0.3,
|
|
|
|
});
|
2024-05-12 20:26:40 +00:00
|
|
|
ew_respawn.send(world::RespawnEvent);
|
2024-05-12 20:17:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_deathscreen_input(
|
2024-05-13 18:42:34 +00:00
|
|
|
time: Res<Time>,
|
2024-05-12 20:17:17 +00:00
|
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
2024-05-13 18:42:34 +00:00
|
|
|
mut timer: ResMut<DeathScreenInputDelayTimer>,
|
2024-05-12 20:17:17 +00:00
|
|
|
mut ew_deathscreen: EventWriter<DeathScreenEvent>,
|
2024-05-13 15:19:07 +00:00
|
|
|
settings: Res<Settings>,
|
2024-05-12 20:17:17 +00:00
|
|
|
) {
|
2024-05-13 18:42:34 +00:00
|
|
|
if !settings.deathscreen_active || !timer.0.tick(time.delta()).finished() {
|
2024-05-12 20:17:17 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if keyboard_input.pressed(settings.key_interact) {
|
|
|
|
ew_deathscreen.send(DeathScreenEvent::Hide);
|
|
|
|
}
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
|
|
|
|
#[derive(Resource, Debug, Default)]
|
|
|
|
pub struct MenuState {
|
|
|
|
cursor: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update_menu(
|
|
|
|
mut q_text: Query<&mut Text, With<MenuTopLevel>>,
|
2024-10-26 19:22:03 +00:00
|
|
|
mut q_footer: Query<&mut Text, (With<FooterElement>, Without<MenuTopLevel>)>,
|
|
|
|
mut q_achievement_text: Query<
|
|
|
|
&mut Text,
|
|
|
|
(
|
|
|
|
With<MenuAchievements>,
|
|
|
|
Without<MenuTopLevel>,
|
|
|
|
Without<FooterElement>,
|
|
|
|
),
|
|
|
|
>,
|
2024-05-13 19:37:57 +00:00
|
|
|
mut q_vis: Query<&mut Visibility, With<menu::MenuElement>>,
|
2024-10-26 19:22:03 +00:00
|
|
|
id2pos: Res<game::Id2Pos>,
|
2024-05-14 03:17:32 +00:00
|
|
|
achievement_tracker: Res<var::AchievementTracker>,
|
2024-05-13 18:21:56 +00:00
|
|
|
menustate: Res<MenuState>,
|
|
|
|
settings: Res<Settings>,
|
2024-09-22 01:10:11 +00:00
|
|
|
prefs: Res<Preferences>,
|
2024-05-13 18:21:56 +00:00
|
|
|
) {
|
2024-05-13 19:37:57 +00:00
|
|
|
for mut vis in &mut q_vis {
|
|
|
|
*vis = bool2vis(settings.menu_active);
|
|
|
|
}
|
|
|
|
fn bool2string(boolean: bool) -> String {
|
|
|
|
if boolean { "On" } else { "Off" }.to_string()
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
2024-05-14 03:17:32 +00:00
|
|
|
|
|
|
|
let bools = achievement_tracker.to_bool_vec();
|
|
|
|
let rendered = achievement_tracker.to_textsections();
|
2024-10-26 19:22:03 +00:00
|
|
|
if let (Ok(mut text), Some(player_pos), Some(jupiter_pos)) = (
|
|
|
|
q_footer.get_single_mut(),
|
|
|
|
id2pos.0.get(cmd::ID_PLAYER),
|
|
|
|
id2pos.0.get(cmd::ID_JUPITER),
|
|
|
|
) {
|
|
|
|
let (clock_max, clock_current) =
|
|
|
|
nature::pos_to_orbit_time(*player_pos, *jupiter_pos, nature::JUPITER_MASS);
|
|
|
|
text.sections[0].value = format!(
|
|
|
|
"Orbital Clock:\n{} of {}\n{} {}",
|
|
|
|
nature::format_seconds_to_hour_min(clock_current),
|
|
|
|
nature::format_seconds_to_hour_min(clock_max),
|
|
|
|
GAME_NAME,
|
|
|
|
settings.version.as_str()
|
|
|
|
);
|
|
|
|
}
|
2024-05-14 03:17:32 +00:00
|
|
|
if let Ok(mut text) = q_achievement_text.get_single_mut() {
|
|
|
|
for i in 0..text.sections.len() - 1 {
|
|
|
|
text.sections[i + 1].style.color = if bools[i] {
|
|
|
|
settings.hud_color_achievement_accomplished
|
|
|
|
} else {
|
|
|
|
settings.hud_color_achievement
|
|
|
|
};
|
|
|
|
text.sections[i + 1].value = rendered[i].clone();
|
|
|
|
}
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
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;
|
|
|
|
}
|
2024-05-13 19:37:57 +00:00
|
|
|
|
|
|
|
match MENUDEF[i].1 {
|
|
|
|
MenuAction::ToggleSound => {
|
2024-06-16 23:16:53 +00:00
|
|
|
let noisecancel = if let Some(noisecancel) = settings
|
|
|
|
.noise_cancellation_modes
|
|
|
|
.get(settings.noise_cancellation_mode)
|
|
|
|
{
|
2024-10-03 21:59:15 +00:00
|
|
|
&noisecancel.0
|
2024-06-16 23:16:53 +00:00
|
|
|
} else {
|
2024-10-03 21:59:15 +00:00
|
|
|
&settings.noise_cancellation_modes[0].0
|
2024-06-16 23:16:53 +00:00
|
|
|
};
|
2024-09-22 14:21:18 +00:00
|
|
|
text.sections[i].value = format!("\nNoise Cancellation: {noisecancel}\n");
|
2024-05-13 19:37:57 +00:00
|
|
|
}
|
|
|
|
MenuAction::ToggleMusic => {
|
2024-06-13 01:26:19 +00:00
|
|
|
let station =
|
|
|
|
if let Some(station) = settings.radio_modes.get(settings.radio_mode) {
|
|
|
|
station
|
|
|
|
} else {
|
|
|
|
&settings.radio_modes[0]
|
|
|
|
};
|
2024-09-15 19:17:21 +00:00
|
|
|
text.sections[i].value = format!("Speakers: {station}\n");
|
2024-05-13 19:37:57 +00:00
|
|
|
}
|
|
|
|
MenuAction::ToggleAR => {
|
|
|
|
let onoff = bool2string(settings.hud_active);
|
2024-10-03 04:08:51 +00:00
|
|
|
let p = if settings.hud_active {
|
|
|
|
actor::POWER_DRAIN_AR
|
|
|
|
} else {
|
|
|
|
0.0
|
|
|
|
};
|
|
|
|
let w = if p > 0.0 {
|
|
|
|
format!(" ({p}W)")
|
|
|
|
} else {
|
|
|
|
String::from("")
|
|
|
|
};
|
2024-09-22 15:12:23 +00:00
|
|
|
text.sections[i].value = format!("Augmented Reality: {onoff}{w} [TAB]\n");
|
2024-05-13 19:37:57 +00:00
|
|
|
}
|
2024-09-22 01:10:11 +00:00
|
|
|
MenuAction::ModLightAmp => {
|
2024-09-22 15:12:23 +00:00
|
|
|
let p = actor::POWER_DRAIN_LIGHTAMP[prefs.light_amp];
|
|
|
|
text.sections[i].value = format!("\nLight Amplification: {p}W\n");
|
2024-09-22 01:10:11 +00:00
|
|
|
}
|
2024-09-22 01:40:12 +00:00
|
|
|
MenuAction::ModFlashlightPower => {
|
2024-09-22 15:12:23 +00:00
|
|
|
let p = actor::POWER_DRAIN_FLASHLIGHT[prefs.flashlight_power];
|
|
|
|
text.sections[i].value = format!("Flashlight Power: {p}W\n");
|
2024-09-22 01:40:12 +00:00
|
|
|
}
|
2024-09-22 02:34:36 +00:00
|
|
|
MenuAction::ModThrusterBoost => {
|
|
|
|
let state = match prefs.thruster_boost {
|
2024-09-22 04:42:56 +00:00
|
|
|
0 => "For braking",
|
2024-09-22 04:39:21 +00:00
|
|
|
1 => "Always",
|
2024-09-22 02:34:36 +00:00
|
|
|
2 => "Off",
|
|
|
|
_ => "ERROR",
|
|
|
|
};
|
2024-09-22 15:12:23 +00:00
|
|
|
let p = actor::POWER_DRAIN_THRUSTER[prefs.thruster_boost];
|
2024-10-03 04:08:51 +00:00
|
|
|
let w = if p > 0.0 {
|
|
|
|
format!(" ({p}W)")
|
|
|
|
} else {
|
|
|
|
String::from("")
|
|
|
|
};
|
2024-09-22 15:12:23 +00:00
|
|
|
text.sections[i].value = format!("Thruster Boost: {state}{w}\n");
|
2024-09-22 02:34:36 +00:00
|
|
|
}
|
2024-09-22 04:38:44 +00:00
|
|
|
MenuAction::ModReactor => {
|
|
|
|
let state = match settings.reactor_state {
|
|
|
|
0 => "Off",
|
2024-09-22 04:42:56 +00:00
|
|
|
1 => "On",
|
2024-10-03 22:08:04 +00:00
|
|
|
2 => "OVERLOAD ☢",
|
2024-09-22 04:38:44 +00:00
|
|
|
_ => "ERROR",
|
|
|
|
};
|
2024-09-22 15:12:23 +00:00
|
|
|
let p = actor::POWER_GAIN_REACTOR[settings.reactor_state];
|
2024-10-03 04:08:51 +00:00
|
|
|
let w = if p > 0.0 {
|
|
|
|
format!(" (+{p}W)")
|
|
|
|
} else {
|
|
|
|
String::from("")
|
|
|
|
};
|
2024-09-22 15:12:23 +00:00
|
|
|
text.sections[i].value = format!("Reactor: {state}{w}\n");
|
2024-09-22 04:38:44 +00:00
|
|
|
}
|
2024-05-23 03:02:59 +00:00
|
|
|
MenuAction::ChangeARAvatar => {
|
|
|
|
if let Some(ava) = hud::PLAYER_AR_AVATARS.get(settings.ar_avatar) {
|
|
|
|
let avatar_title = ava.3;
|
|
|
|
text.sections[i].value = format!("Avatar: {avatar_title}\n");
|
|
|
|
}
|
|
|
|
}
|
2024-10-12 03:16:04 +00:00
|
|
|
MenuAction::ChangePointer => {
|
|
|
|
for pointer in hud::POINTERS {
|
|
|
|
if pointer.0 == prefs.pointer {
|
|
|
|
text.sections[i].value = format!("Pointer: {}\n", pointer.2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-13 19:37:57 +00:00
|
|
|
MenuAction::ToggleMap => {
|
|
|
|
let onoff = bool2string(settings.map_active);
|
|
|
|
text.sections[i].value = format!("Map: {onoff} [M]\n");
|
|
|
|
}
|
2024-05-13 21:51:18 +00:00
|
|
|
MenuAction::ToggleCamera => {
|
|
|
|
let onoff = if settings.third_person {
|
|
|
|
"3rd Person"
|
|
|
|
} else {
|
|
|
|
"1st Person"
|
|
|
|
};
|
2024-09-22 14:21:18 +00:00
|
|
|
text.sections[i].value = format!("\nCamera: {onoff} [C]\n");
|
2024-05-13 21:51:18 +00:00
|
|
|
}
|
2024-05-13 19:37:57 +00:00
|
|
|
MenuAction::ToggleShadows => {
|
2024-10-03 04:08:51 +00:00
|
|
|
let onoff = if settings.shadows_sun { "High" } else { "Low" };
|
2024-05-13 19:37:57 +00:00
|
|
|
text.sections[i].value = format!("Shadows: {onoff}\n");
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_input(
|
|
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
|
|
|
mut settings: ResMut<Settings>,
|
2024-09-22 01:10:11 +00:00
|
|
|
mut prefs: ResMut<Preferences>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut menustate: ResMut<MenuState>,
|
2024-07-09 00:19:44 +00:00
|
|
|
mut app_exit_events: ResMut<Events<AppExit>>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut ew_game: EventWriter<game::GameEvent>,
|
2024-05-13 18:53:08 +00:00
|
|
|
mut ew_playerdies: EventWriter<game::PlayerDiesEvent>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
2024-05-13 19:37:57 +00:00
|
|
|
mut ew_updatemenu: EventWriter<UpdateMenuEvent>,
|
2024-05-23 03:02:59 +00:00
|
|
|
mut ew_updateavatar: EventWriter<hud::UpdateAvatarEvent>,
|
2024-10-12 03:16:04 +00:00
|
|
|
mut ew_updatepointer: EventWriter<hud::UpdatePointerEvent>,
|
2024-09-22 01:10:11 +00:00
|
|
|
mut ew_updateoverlays: EventWriter<hud::UpdateOverlayVisibility>,
|
2024-05-13 18:21:56 +00:00
|
|
|
) {
|
2024-05-14 16:58:47 +00:00
|
|
|
let last_menu_entry = MENUDEF.len() - 1;
|
|
|
|
|
2024-05-13 18:21:56 +00:00
|
|
|
if keyboard_input.just_pressed(settings.key_menu)
|
2024-05-22 02:57:20 +00:00
|
|
|
|| keyboard_input.just_pressed(settings.key_vehicle) && settings.menu_active
|
2024-05-13 18:21:56 +00:00
|
|
|
{
|
2024-05-13 18:53:08 +00:00
|
|
|
ew_game.send(GameEvent::SetMenu(Toggle));
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
if !settings.menu_active {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if keyboard_input.just_pressed(settings.key_forward)
|
2024-05-22 02:57:20 +00:00
|
|
|
|| keyboard_input.just_pressed(settings.key_mouseup)
|
|
|
|
|| keyboard_input.just_pressed(KeyCode::ArrowUp)
|
2024-05-13 18:21:56 +00:00
|
|
|
{
|
2024-05-14 16:58:47 +00:00
|
|
|
menustate.cursor = if menustate.cursor == 0 {
|
|
|
|
last_menu_entry
|
|
|
|
} else {
|
|
|
|
menustate.cursor.saturating_sub(1)
|
|
|
|
};
|
2024-09-16 23:10:55 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click3));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
if keyboard_input.just_pressed(settings.key_back)
|
2024-05-22 02:57:20 +00:00
|
|
|
|| keyboard_input.just_pressed(settings.key_mousedown)
|
|
|
|
|| keyboard_input.just_pressed(KeyCode::ArrowDown)
|
2024-05-13 18:21:56 +00:00
|
|
|
{
|
2024-05-14 16:58:47 +00:00
|
|
|
menustate.cursor = if menustate.cursor == last_menu_entry {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
menustate.cursor.saturating_add(1)
|
|
|
|
};
|
2024-09-16 23:10:55 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click3));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
if keyboard_input.just_pressed(settings.key_interact)
|
2024-05-22 02:57:20 +00:00
|
|
|
|| keyboard_input.just_pressed(KeyCode::Enter)
|
2024-05-13 18:21:56 +00:00
|
|
|
{
|
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
|
|
|
match MENUDEF[menustate.cursor].1 {
|
|
|
|
MenuAction::ToggleMap => {
|
2024-05-13 18:25:25 +00:00
|
|
|
ew_game.send(GameEvent::SetMap(Toggle));
|
2024-05-13 18:53:08 +00:00
|
|
|
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
MenuAction::ToggleAR => {
|
2024-05-13 18:25:25 +00:00
|
|
|
ew_game.send(GameEvent::SetAR(Toggle));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-23 03:02:59 +00:00
|
|
|
MenuAction::ChangeARAvatar => {
|
|
|
|
settings.ar_avatar += 1;
|
|
|
|
ew_updateavatar.send(hud::UpdateAvatarEvent);
|
|
|
|
}
|
2024-10-12 03:16:04 +00:00
|
|
|
MenuAction::ChangePointer => {
|
|
|
|
let mut index = 0;
|
|
|
|
for (i, pointer) in hud::POINTERS.iter().enumerate() {
|
|
|
|
if pointer.0 == prefs.pointer {
|
|
|
|
index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
index = (index + 1) % hud::POINTERS.len();
|
|
|
|
for (i, pointer) in hud::POINTERS.iter().enumerate() {
|
|
|
|
if i == index {
|
|
|
|
prefs.pointer = pointer.0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prefs.save();
|
|
|
|
ew_updatepointer.send(hud::UpdatePointerEvent);
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-09-22 01:10:11 +00:00
|
|
|
MenuAction::ModLightAmp => {
|
|
|
|
prefs.light_amp += 1;
|
|
|
|
if prefs.light_amp > 3 {
|
|
|
|
prefs.light_amp = 0;
|
|
|
|
}
|
2024-09-22 05:15:07 +00:00
|
|
|
prefs.save();
|
2024-09-22 01:10:11 +00:00
|
|
|
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-09-22 01:40:12 +00:00
|
|
|
MenuAction::ModFlashlightPower => {
|
|
|
|
prefs.flashlight_power += 1;
|
2024-09-22 04:39:21 +00:00
|
|
|
if prefs.flashlight_power > 3 {
|
2024-09-22 01:40:12 +00:00
|
|
|
prefs.flashlight_power = 0;
|
|
|
|
}
|
2024-09-22 05:15:07 +00:00
|
|
|
prefs.save();
|
2024-09-22 01:40:12 +00:00
|
|
|
ew_game.send(GameEvent::UpdateFlashlight);
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-09-22 02:34:36 +00:00
|
|
|
MenuAction::ModThrusterBoost => {
|
|
|
|
prefs.thruster_boost += 1;
|
|
|
|
if prefs.thruster_boost > 2 {
|
|
|
|
prefs.thruster_boost = 0;
|
|
|
|
}
|
2024-09-22 05:15:07 +00:00
|
|
|
prefs.save();
|
2024-09-22 02:34:36 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-09-22 04:38:44 +00:00
|
|
|
MenuAction::ModReactor => {
|
|
|
|
settings.reactor_state += 1;
|
|
|
|
if settings.reactor_state > 2 {
|
|
|
|
settings.reactor_state = 0;
|
|
|
|
}
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
MenuAction::ToggleMusic => {
|
2024-06-13 01:26:19 +00:00
|
|
|
ew_game.send(GameEvent::SetMusic(Next));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
MenuAction::ToggleSound => {
|
2024-06-13 02:06:15 +00:00
|
|
|
ew_game.send(GameEvent::SetSound(Next));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 21:51:18 +00:00
|
|
|
MenuAction::ToggleCamera => {
|
|
|
|
ew_game.send(GameEvent::SetThirdPerson(Toggle));
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
MenuAction::ToggleFullscreen => {
|
2024-05-13 18:25:25 +00:00
|
|
|
ew_game.send(GameEvent::SetFullscreen(Toggle));
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 19:11:27 +00:00
|
|
|
MenuAction::ToggleShadows => {
|
|
|
|
ew_game.send(GameEvent::SetShadows(Toggle));
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-10-26 17:36:54 +00:00
|
|
|
MenuAction::PhoneCall => {
|
|
|
|
ew_game.send(GameEvent::PhoneCall);
|
|
|
|
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
|
|
|
ew_updatemenu.send(UpdateMenuEvent);
|
|
|
|
}
|
2024-05-13 18:53:08 +00:00
|
|
|
MenuAction::Restart => {
|
|
|
|
settings.god_mode = false;
|
2024-05-14 17:12:36 +00:00
|
|
|
ew_playerdies.send(game::PlayerDiesEvent(actor::DamageType::Depressurization));
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
MenuAction::Quit => {
|
2024-07-09 00:19:44 +00:00
|
|
|
app_exit_events.send(AppExit::Success);
|
2024-05-22 02:57:20 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|