Pause menu functionality (#89)
* make menu use actual soundsettings values * allow changing volume in pause menu * persist saving settings
This commit is contained in:
91
Cargo.lock
generated
91
Cargo.lock
generated
@@ -267,7 +267,7 @@ dependencies = [
|
|||||||
"objc2-foundation 0.3.2",
|
"objc2-foundation 0.3.2",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
"x11rb",
|
"x11rb",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1359,6 +1359,25 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_pkv"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "356a9c6fdc13faf7897103b43a8b84aafe24e1bbf1599df1fb00dc4e9b7055db"
|
||||||
|
dependencies = [
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
|
"cfg_aliases",
|
||||||
|
"directories",
|
||||||
|
"redb",
|
||||||
|
"rmp-serde",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_platform"
|
name = "bevy_platform"
|
||||||
version = "0.17.3"
|
version = "0.17.3"
|
||||||
@@ -2760,6 +2779,27 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11277822c27bde750de02c5dc5159b91e88bf2661a2c1d98106f2fb1c5c6f590"
|
checksum = "11277822c27bde750de02c5dc5159b91e88bf2661a2c1d98106f2fb1c5c6f590"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "directories"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "disjoint-sets"
|
name = "disjoint-sets"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -3645,6 +3685,7 @@ dependencies = [
|
|||||||
"bevy_ballistic",
|
"bevy_ballistic",
|
||||||
"bevy_common_assets",
|
"bevy_common_assets",
|
||||||
"bevy_debug_log",
|
"bevy_debug_log",
|
||||||
|
"bevy_pkv",
|
||||||
"bevy_replicon",
|
"bevy_replicon",
|
||||||
"bevy_replicon_renet",
|
"bevy_replicon_renet",
|
||||||
"bevy_sprite3d",
|
"bevy_sprite3d",
|
||||||
@@ -4870,6 +4911,12 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.49"
|
version = "0.3.49"
|
||||||
@@ -5411,6 +5458,15 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb"
|
checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redb"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae323eb086579a3769daa2c753bb96deb95993c534711e0dbe881b5192906a06"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -5438,6 +5494,17 @@ dependencies = [
|
|||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"libredox",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.2"
|
version = "1.12.2"
|
||||||
@@ -5507,6 +5574,28 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp"
|
||||||
|
version = "0.8.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"num-traits",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp-serde"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"rmp",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "robust"
|
name = "robust"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ bevy_asset_loader = "=0.24.0-rc.1"
|
|||||||
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", rev = "b08ffec" }
|
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", rev = "b08ffec" }
|
||||||
bevy_common_assets = { version = "0.14.0", features = ["ron"] }
|
bevy_common_assets = { version = "0.14.0", features = ["ron"] }
|
||||||
bevy_debug_log = { git = "https://github.com/rustunit/bevy_debug_log.git", rev = "86051a0" }
|
bevy_debug_log = { git = "https://github.com/rustunit/bevy_debug_log.git", rev = "86051a0" }
|
||||||
|
bevy_pkv = { version = "0.14", default-features = false, features = [
|
||||||
|
"bevy",
|
||||||
|
"redb",
|
||||||
|
] }
|
||||||
bevy_replicon = "0.37.1"
|
bevy_replicon = "0.37.1"
|
||||||
bevy_replicon_renet = "0.13.0"
|
bevy_replicon_renet = "0.13.0"
|
||||||
bevy_sprite3d = "7.0.0"
|
bevy_sprite3d = "7.0.0"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ bevy_asset_loader = { workspace = true }
|
|||||||
bevy_ballistic = { workspace = true }
|
bevy_ballistic = { workspace = true }
|
||||||
bevy_common_assets = { workspace = true }
|
bevy_common_assets = { workspace = true }
|
||||||
bevy_debug_log = { workspace = true }
|
bevy_debug_log = { workspace = true }
|
||||||
|
bevy_pkv = { workspace = true }
|
||||||
bevy_replicon = { workspace = true }
|
bevy_replicon = { workspace = true }
|
||||||
bevy_replicon_renet = { workspace = true }
|
bevy_replicon_renet = { workspace = true }
|
||||||
bevy_sprite3d = { workspace = true }
|
bevy_sprite3d = { workspace = true }
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ mod music;
|
|||||||
mod sounds;
|
mod sounds;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Clone, Copy, Debug)]
|
#[derive(Resource, Reflect, Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct SoundSettings {
|
pub struct SoundSettings {
|
||||||
pub sound_volume: f32,
|
pub sound_volume: f32,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ pub mod debug;
|
|||||||
pub mod enemy;
|
pub mod enemy;
|
||||||
pub mod heal_effect;
|
pub mod heal_effect;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
|
mod settings;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod steam;
|
pub mod steam;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
@@ -49,6 +50,7 @@ pub fn plugin(app: &mut App) {
|
|||||||
audio::plugin,
|
audio::plugin,
|
||||||
steam::plugin,
|
steam::plugin,
|
||||||
ui::plugin,
|
ui::plugin,
|
||||||
|
settings::plugin,
|
||||||
));
|
));
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
|
|||||||
42
crates/hedz_reloaded/src/client/settings.rs
Normal file
42
crates/hedz_reloaded/src/client/settings.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_pkv::prelude::*;
|
||||||
|
|
||||||
|
use crate::client::audio::SoundSettings;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct SaveTimer(Timer);
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.insert_resource(PkvStore::new("Rustunit", "HEDZ"));
|
||||||
|
app.insert_resource(SaveTimer(Timer::from_seconds(1.0, TimerMode::Once)));
|
||||||
|
|
||||||
|
app.add_systems(Update, persist_settings);
|
||||||
|
app.add_systems(Startup, load_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn persist_settings(
|
||||||
|
settings: Res<SoundSettings>,
|
||||||
|
mut pkv: ResMut<PkvStore>,
|
||||||
|
mut timer: ResMut<SaveTimer>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) -> Result {
|
||||||
|
if settings.is_changed() {
|
||||||
|
timer.0.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.0.tick(time.delta());
|
||||||
|
|
||||||
|
if timer.0.just_finished() {
|
||||||
|
pkv.set("audio", &*settings)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_settings(mut settings: ResMut<SoundSettings>, pkv: Res<PkvStore>) -> Result {
|
||||||
|
if let Ok(loaded) = pkv.get::<SoundSettings>("audio") {
|
||||||
|
*settings = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
GameState, HEDZ_GREEN, HEDZ_PURPLE, client::control::CharacterInputEnabled,
|
GameState, HEDZ_GREEN, HEDZ_PURPLE,
|
||||||
|
client::{audio::SoundSettings, control::CharacterInputEnabled},
|
||||||
loading_assets::UIAssets,
|
loading_assets::UIAssets,
|
||||||
};
|
};
|
||||||
use bevy::{color::palettes::css::BLACK, prelude::*};
|
use bevy::{color::palettes::css::BLACK, prelude::*};
|
||||||
@@ -18,16 +19,29 @@ enum ProgressBar {
|
|||||||
Sound,
|
Sound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct VolumeValue(ProgressBar);
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct PauseMenuSelection(ProgressBar);
|
struct PauseMenuSelection(ProgressBar);
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct VolumeChangeCooldown(Timer);
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.init_state::<PauseMenuState>();
|
app.init_state::<PauseMenuState>();
|
||||||
|
|
||||||
app.add_systems(Update, open_pause_menu.run_if(in_state(GameState::Playing)));
|
app.add_systems(Update, open_pause_menu.run_if(in_state(GameState::Playing)));
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(selection_input, selection_changed).run_if(in_state(PauseMenuState::Open)),
|
(
|
||||||
|
selection_input,
|
||||||
|
selection_changed,
|
||||||
|
update_volume_display,
|
||||||
|
change_volume_keyboard,
|
||||||
|
change_volume_gamepad,
|
||||||
|
)
|
||||||
|
.run_if(in_state(PauseMenuState::Open)),
|
||||||
);
|
);
|
||||||
app.add_systems(OnEnter(PauseMenuState::Open), setup);
|
app.add_systems(OnEnter(PauseMenuState::Open), setup);
|
||||||
}
|
}
|
||||||
@@ -58,7 +72,10 @@ fn open_pause_menu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
fn setup(mut commands: Commands, assets: Res<UIAssets>, settings: Res<SoundSettings>) {
|
||||||
|
let sound_volume = (settings.sound_volume * 100.) as u8;
|
||||||
|
let music_volume = (settings.music_volume * 100.) as u8;
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("pause-menu"),
|
Name::new("pause-menu"),
|
||||||
Node {
|
Node {
|
||||||
@@ -73,12 +90,16 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
BackgroundColor(Color::linear_rgba(0., 0., 0., 0.6)),
|
BackgroundColor(Color::linear_rgba(0., 0., 0., 0.6)),
|
||||||
DespawnOnExit(PauseMenuState::Open),
|
DespawnOnExit(PauseMenuState::Open),
|
||||||
children![
|
children![
|
||||||
spawn_progress(ProgressBar::Music, 100, assets.font.clone()),
|
spawn_progress(ProgressBar::Music, music_volume, assets.font.clone()),
|
||||||
spawn_progress(ProgressBar::Sound, 80, assets.font.clone())
|
spawn_progress(ProgressBar::Sound, sound_volume, assets.font.clone())
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.insert_resource(PauseMenuSelection(ProgressBar::Music));
|
commands.insert_resource(PauseMenuSelection(ProgressBar::Music));
|
||||||
|
commands.insert_resource(VolumeChangeCooldown(Timer::from_seconds(
|
||||||
|
0.09,
|
||||||
|
TimerMode::Repeating,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundle {
|
fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundle {
|
||||||
@@ -144,6 +165,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
|||||||
},
|
},
|
||||||
TextColor(HEDZ_GREEN.into()),
|
TextColor(HEDZ_GREEN.into()),
|
||||||
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
||||||
|
VolumeValue(bar),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Node {
|
Node {
|
||||||
@@ -186,3 +208,92 @@ fn selection_changed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_volume_display(
|
||||||
|
settings: Res<SoundSettings>,
|
||||||
|
mut query: Query<(&mut Text, &VolumeValue)>,
|
||||||
|
) {
|
||||||
|
if settings.is_changed() {
|
||||||
|
for (mut text, volume_value) in query.iter_mut() {
|
||||||
|
let new_value = match volume_value.0 {
|
||||||
|
ProgressBar::Music => (settings.music_volume * 100.) as u8,
|
||||||
|
ProgressBar::Sound => (settings.sound_volume * 100.) as u8,
|
||||||
|
};
|
||||||
|
text.0 = format!("{new_value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_volume_keyboard(
|
||||||
|
selection: Res<PauseMenuSelection>,
|
||||||
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut settings: ResMut<SoundSettings>,
|
||||||
|
mut cooldown: ResMut<VolumeChangeCooldown>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
let change = if keyboard.pressed(KeyCode::ArrowLeft) {
|
||||||
|
-0.01
|
||||||
|
} else if keyboard.pressed(KeyCode::ArrowRight) {
|
||||||
|
0.01
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On first press, apply immediately and reset timer
|
||||||
|
if keyboard.just_pressed(KeyCode::ArrowLeft) || keyboard.just_pressed(KeyCode::ArrowRight) {
|
||||||
|
cooldown.0.reset();
|
||||||
|
} else {
|
||||||
|
cooldown.0.tick(time.delta());
|
||||||
|
if !cooldown.0.just_finished() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match selection.0 {
|
||||||
|
ProgressBar::Music => {
|
||||||
|
settings.music_volume = (settings.music_volume + change).clamp(0.0, 1.0);
|
||||||
|
}
|
||||||
|
ProgressBar::Sound => {
|
||||||
|
settings.sound_volume = (settings.sound_volume + change).clamp(0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_volume_gamepad(
|
||||||
|
selection: Res<PauseMenuSelection>,
|
||||||
|
gamepads: Query<&Gamepad>,
|
||||||
|
mut settings: ResMut<SoundSettings>,
|
||||||
|
mut cooldown: ResMut<VolumeChangeCooldown>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
let Some((change, just_pressed)) = gamepads.iter().find_map(|gamepad| {
|
||||||
|
if gamepad.pressed(GamepadButton::DPadLeft) {
|
||||||
|
Some((-0.01, gamepad.just_pressed(GamepadButton::DPadLeft)))
|
||||||
|
} else if gamepad.pressed(GamepadButton::DPadRight) {
|
||||||
|
Some((0.01, gamepad.just_pressed(GamepadButton::DPadRight)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On first press, apply immediately and reset timer
|
||||||
|
if just_pressed {
|
||||||
|
cooldown.0.reset();
|
||||||
|
} else {
|
||||||
|
cooldown.0.tick(time.delta());
|
||||||
|
if !cooldown.0.just_finished() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match selection.0 {
|
||||||
|
ProgressBar::Music => {
|
||||||
|
settings.music_volume = (settings.music_volume + change).clamp(0.0, 1.0);
|
||||||
|
}
|
||||||
|
ProgressBar::Sound => {
|
||||||
|
settings.sound_volume = (settings.sound_volume + change).clamp(0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user