doukutsu-rs/src/menu/settings_menu.rs

326 lines
14 KiB
Rust
Raw Normal View History

2022-02-10 06:15:28 +00:00
use itertools::Itertools;
2021-08-16 07:47:11 +00:00
use crate::framework::context::Context;
use crate::framework::error::GameResult;
2022-02-10 06:15:28 +00:00
use crate::framework::filesystem;
2021-08-16 07:47:11 +00:00
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::menu::MenuEntry;
2022-02-03 03:09:29 +00:00
use crate::menu::{Menu, MenuSelectionResult};
2021-08-16 07:47:11 +00:00
use crate::shared_game_state::{SharedGameState, TimingMode};
use crate::sound::InterpolationMode;
#[derive(PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
#[allow(unused)]
enum CurrentMenu {
MainMenu,
GraphicsMenu,
SoundMenu,
2022-02-10 06:15:28 +00:00
SoundtrackMenu,
2021-08-16 07:47:11 +00:00
}
pub struct SettingsMenu {
current: CurrentMenu,
main: Menu,
graphics: Menu,
sound: Menu,
2022-02-10 06:15:28 +00:00
soundtrack: Menu,
pub on_title: bool,
2021-08-16 07:47:11 +00:00
}
static DISCORD_LINK: &str = "https://discord.gg/fbRsNNB";
impl SettingsMenu {
pub fn new() -> SettingsMenu {
let main = Menu::new(0, 0, 220, 0);
2021-08-16 07:47:11 +00:00
let graphics = Menu::new(0, 0, 180, 0);
let sound = Menu::new(0, 0, 260, 0);
2022-02-10 06:15:28 +00:00
let soundtrack = Menu::new(0, 0, 260, 0);
2021-08-16 07:47:11 +00:00
2022-02-10 06:15:28 +00:00
SettingsMenu { current: CurrentMenu::MainMenu, main, graphics, sound, soundtrack, on_title: false }
2021-08-16 07:47:11 +00:00
}
pub fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
self.graphics.push_entry(MenuEntry::Toggle("Lighting effects:".to_string(), state.settings.shader_effects));
self.graphics.push_entry(MenuEntry::Toggle("Weapon light cone:".to_string(), state.settings.light_cone));
2021-08-16 07:47:11 +00:00
self.graphics
.push_entry(MenuEntry::Toggle("Motion interpolation:".to_string(), state.settings.motion_interpolation));
self.graphics.push_entry(MenuEntry::Toggle("Subpixel scrolling:".to_string(), state.settings.subpixel_coords));
if state.constants.supports_og_textures {
self.graphics
.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
} else {
self.graphics.push_entry(MenuEntry::Hidden);
}
if state.constants.is_cs_plus {
self.graphics
.push_entry(MenuEntry::Toggle("Seasonal textures".to_string(), state.settings.seasonal_textures));
} else {
self.graphics.push_entry(MenuEntry::Hidden);
}
self.graphics
.push_entry(MenuEntry::Disabled(format!("Renderer: {}", ctx.renderer.as_ref().unwrap().renderer_name())));
self.graphics.push_entry(MenuEntry::Active("< Back".to_owned()));
self.main.push_entry(MenuEntry::Active("Graphics...".to_owned()));
self.main.push_entry(MenuEntry::Active("Sound...".to_owned()));
self.main.push_entry(MenuEntry::Options(
"Game timing:".to_owned(),
2021-10-14 07:43:17 +00:00
if state.settings.timing_mode == TimingMode::_50Hz { 0 } else { 1 },
2021-08-16 07:47:11 +00:00
vec!["50tps (freeware)".to_owned(), "60tps (CS+)".to_owned()],
));
self.main.push_entry(MenuEntry::Active(DISCORD_LINK.to_owned()));
self.main.push_entry(MenuEntry::Active("< Back".to_owned()));
2022-02-03 03:09:29 +00:00
self.sound.push_entry(MenuEntry::OptionsBar("Music Volume".to_owned(), state.settings.bgm_volume));
self.sound.push_entry(MenuEntry::OptionsBar("Effects Volume".to_owned(), state.settings.sfx_volume));
2022-01-17 19:34:37 +00:00
self.sound.push_entry(MenuEntry::DescriptiveOptions(
"BGM Interpolation:".to_owned(),
2021-08-16 07:47:11 +00:00
state.settings.organya_interpolation as usize,
vec![
"Nearest".to_owned(),
"Linear".to_owned(),
2021-08-16 07:47:11 +00:00
"Cosine".to_owned(),
"Cubic".to_owned(),
2022-02-10 06:15:28 +00:00
"Linear+LP".to_owned(),
],
vec![
"(Fastest, lowest quality)".to_owned(),
"(Fast, similar to freeware on Vista+)".to_owned(),
2022-01-17 19:34:37 +00:00
"(Cosine interpolation)".to_owned(),
"(Cubic interpolation)".to_owned(),
2022-02-03 03:09:29 +00:00
"(Slowest, similar to freeware on XP)".to_owned(),
2021-08-16 07:47:11 +00:00
],
));
self.sound.push_entry(MenuEntry::DisabledWhite("".to_owned()));
2022-02-10 06:15:28 +00:00
self.sound.push_entry(MenuEntry::Active(format!("Soundtrack: {}", state.settings.soundtrack)));
2021-08-16 07:47:11 +00:00
self.sound.push_entry(MenuEntry::Active("< Back".to_owned()));
2022-02-10 06:15:28 +00:00
let mut soundtrack_entries = state.constants.soundtracks.keys().map(|s| s.to_owned()).collect_vec();
soundtrack_entries.push("Organya".to_owned());
if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") {
for entry in dir {
if filesystem::is_dir(ctx, &entry) {
let filename = entry.file_name().unwrap().to_string_lossy().to_string();
if !soundtrack_entries.contains(&filename) {
soundtrack_entries.push(filename);
}
}
}
}
soundtrack_entries.sort();
for soundtrack in soundtrack_entries {
self.soundtrack.push_entry(MenuEntry::Active(soundtrack));
}
self.soundtrack.push_entry(MenuEntry::Active("< Back".to_owned()));
2021-08-16 07:47:11 +00:00
self.update_sizes(state);
Ok(())
}
fn update_sizes(&mut self, state: &SharedGameState) {
self.main.update_height();
self.main.x = ((state.canvas_size.0 - self.main.width as f32) / 2.0).floor() as isize;
self.main.y = 30 + ((state.canvas_size.1 - self.main.height as f32) / 2.0).floor() as isize;
self.graphics.update_height();
self.graphics.x = ((state.canvas_size.0 - self.graphics.width as f32) / 2.0).floor() as isize;
self.graphics.y = 30 + ((state.canvas_size.1 - self.graphics.height as f32) / 2.0).floor() as isize;
self.sound.update_height();
self.sound.x = ((state.canvas_size.0 - self.sound.width as f32) / 2.0).floor() as isize;
self.sound.y = 30 + ((state.canvas_size.1 - self.sound.height as f32) / 2.0).floor() as isize;
2022-02-10 06:15:28 +00:00
self.soundtrack.update_height();
self.soundtrack.x = ((state.canvas_size.0 - self.soundtrack.width as f32) / 2.0).floor() as isize;
self.soundtrack.y = ((state.canvas_size.1 - self.soundtrack.height as f32) / 2.0).floor() as isize;
2021-08-16 07:47:11 +00:00
}
pub fn tick(
&mut self,
exit_action: &mut dyn FnMut(),
controller: &mut CombinedMenuController,
state: &mut SharedGameState,
ctx: &mut Context,
) -> GameResult {
self.update_sizes(state);
match self.current {
CurrentMenu::MainMenu => match self.main.tick(controller, state) {
MenuSelectionResult::Selected(0, _) => {
self.current = CurrentMenu::GraphicsMenu;
}
MenuSelectionResult::Selected(1, _) => {
self.current = CurrentMenu::SoundMenu;
}
MenuSelectionResult::Selected(2, toggle) => {
2022-01-17 19:34:37 +00:00
if let MenuEntry::Options(_, value, _) = toggle {
2021-10-14 07:43:17 +00:00
match state.settings.timing_mode {
2021-08-16 07:47:11 +00:00
TimingMode::_50Hz => {
2021-10-14 07:43:17 +00:00
state.settings.timing_mode = TimingMode::_60Hz;
2021-08-16 07:47:11 +00:00
*value = 1;
}
TimingMode::_60Hz => {
2021-10-14 07:43:17 +00:00
state.settings.timing_mode = TimingMode::_50Hz;
2021-08-16 07:47:11 +00:00
*value = 0;
}
_ => {}
}
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Selected(3, _) => {
if let Err(e) = webbrowser::open(DISCORD_LINK) {
log::warn!("Error opening web browser: {}", e);
}
}
MenuSelectionResult::Selected(4, _) | MenuSelectionResult::Canceled => exit_action(),
_ => (),
},
CurrentMenu::GraphicsMenu => match self.graphics.tick(controller, state) {
MenuSelectionResult::Selected(0, toggle) => {
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.shader_effects = !state.settings.shader_effects;
let _ = state.settings.save(ctx);
*value = state.settings.shader_effects;
}
}
MenuSelectionResult::Selected(1, toggle) => {
2021-10-10 00:31:07 +00:00
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.light_cone = !state.settings.light_cone;
let _ = state.settings.save(ctx);
*value = state.settings.light_cone;
}
}
MenuSelectionResult::Selected(2, toggle) => {
2021-08-16 07:47:11 +00:00
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.motion_interpolation = !state.settings.motion_interpolation;
let _ = state.settings.save(ctx);
*value = state.settings.motion_interpolation;
}
}
2021-10-10 00:31:07 +00:00
MenuSelectionResult::Selected(3, toggle) => {
2021-08-16 07:47:11 +00:00
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.subpixel_coords = !state.settings.subpixel_coords;
let _ = state.settings.save(ctx);
*value = state.settings.subpixel_coords;
}
}
2021-10-10 00:31:07 +00:00
MenuSelectionResult::Selected(4, toggle) => {
2021-08-16 07:47:11 +00:00
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.original_textures = !state.settings.original_textures;
state.reload_textures();
let _ = state.settings.save(ctx);
*value = state.settings.original_textures;
}
}
2021-10-10 00:31:07 +00:00
MenuSelectionResult::Selected(5, toggle) => {
2021-08-16 07:47:11 +00:00
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.seasonal_textures = !state.settings.seasonal_textures;
state.reload_textures();
let _ = state.settings.save(ctx);
*value = state.settings.seasonal_textures;
}
}
2021-10-10 00:31:07 +00:00
MenuSelectionResult::Selected(7, _) | MenuSelectionResult::Canceled => {
2021-08-16 07:47:11 +00:00
self.current = CurrentMenu::MainMenu
}
_ => (),
},
CurrentMenu::SoundMenu => match self.sound.tick(controller, state) {
2022-02-03 03:09:29 +00:00
MenuSelectionResult::Left(0, bgm, direction) | MenuSelectionResult::Right(0, bgm, direction) => {
if let MenuEntry::OptionsBar(_, value) = bgm {
*value = (*value + (direction as f32 * 0.1)).clamp(0.0, 1.0);
state.settings.bgm_volume = *value;
state.sound_manager.set_song_volume(*value);
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Left(1, sfx, direction) | MenuSelectionResult::Right(1, sfx, direction) => {
if let MenuEntry::OptionsBar(_, value) = sfx {
*value = (*value + (direction as f32 * 0.1)).clamp(0.0, 1.0);
state.settings.sfx_volume = *value;
state.sound_manager.set_sfx_volume(*value);
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Selected(2, toggle) => {
2022-01-17 19:34:37 +00:00
if let MenuEntry::DescriptiveOptions(_, value, _, _) = toggle {
2021-08-16 07:47:11 +00:00
let (new_mode, new_value) = match *value {
0 => (InterpolationMode::Linear, 1),
1 => (InterpolationMode::Cosine, 2),
2 => (InterpolationMode::Cubic, 3),
3 => (InterpolationMode::Polyphase, 4),
_ => (InterpolationMode::Nearest, 0),
};
*value = new_value;
state.settings.organya_interpolation = new_mode;
state.sound_manager.set_org_interpolation(new_mode);
let _ = state.settings.save(ctx);
}
}
2022-02-10 06:15:28 +00:00
MenuSelectionResult::Selected(4, _) => self.current = CurrentMenu::SoundtrackMenu,
2022-02-03 03:09:29 +00:00
MenuSelectionResult::Selected(5, _) | MenuSelectionResult::Canceled => {
2021-08-16 07:47:11 +00:00
self.current = CurrentMenu::MainMenu
}
_ => (),
2022-02-03 03:09:29 +00:00
},
2022-02-10 06:15:28 +00:00
CurrentMenu::SoundtrackMenu => {
let last = self.soundtrack.entries.len() - 1;
match self.soundtrack.tick(controller, state) {
MenuSelectionResult::Selected(idx, entry) => {
if let (true, MenuEntry::Active(name)) = (idx != last, entry) {
state.settings.soundtrack = name.to_owned();
self.sound.entries[4] =
MenuEntry::Active(format!("Soundtrack: {}", state.settings.soundtrack));
state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?;
}
self.current = CurrentMenu::SoundMenu;
}
MenuSelectionResult::Canceled => {
self.current = CurrentMenu::SoundMenu;
}
_ => (),
}
}
2021-08-16 07:47:11 +00:00
}
Ok(())
}
pub fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
match self.current {
CurrentMenu::MainMenu => self.main.draw(state, ctx)?,
CurrentMenu::GraphicsMenu => self.graphics.draw(state, ctx)?,
CurrentMenu::SoundMenu => self.sound.draw(state, ctx)?,
2022-02-10 06:15:28 +00:00
CurrentMenu::SoundtrackMenu => self.soundtrack.draw(state, ctx)?,
2021-08-16 07:47:11 +00:00
}
Ok(())
}
}