mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-10-31 19:44:20 +00:00
settings menu rework
This commit is contained in:
parent
c815c91541
commit
a601d6c044
|
@ -1,16 +1,19 @@
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
use crate::common::Rect;
|
use crate::common::Rect;
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::GameResult;
|
use crate::framework::error::GameResult;
|
||||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||||
use crate::player::skin::basic::BasicPlayerSkin;
|
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
use std::cell::Cell;
|
|
||||||
|
pub mod settings_menu;
|
||||||
|
|
||||||
pub struct MenuSaveInfo {}
|
pub struct MenuSaveInfo {}
|
||||||
|
|
||||||
pub enum MenuEntry {
|
pub enum MenuEntry {
|
||||||
Hidden,
|
Hidden,
|
||||||
Active(String),
|
Active(String),
|
||||||
|
DisabledWhite(String),
|
||||||
Disabled(String),
|
Disabled(String),
|
||||||
Toggle(String, bool),
|
Toggle(String, bool),
|
||||||
Options(String, usize, Vec<String>),
|
Options(String, usize, Vec<String>),
|
||||||
|
@ -23,6 +26,7 @@ impl MenuEntry {
|
||||||
match self {
|
match self {
|
||||||
MenuEntry::Hidden => 0.0,
|
MenuEntry::Hidden => 0.0,
|
||||||
MenuEntry::Active(_) => 14.0,
|
MenuEntry::Active(_) => 14.0,
|
||||||
|
MenuEntry::DisabledWhite(_) => 14.0,
|
||||||
MenuEntry::Disabled(_) => 14.0,
|
MenuEntry::Disabled(_) => 14.0,
|
||||||
MenuEntry::Toggle(_, _) => 14.0,
|
MenuEntry::Toggle(_, _) => 14.0,
|
||||||
MenuEntry::Options(_, _, _) => 14.0,
|
MenuEntry::Options(_, _, _) => 14.0,
|
||||||
|
@ -30,6 +34,19 @@ impl MenuEntry {
|
||||||
MenuEntry::NewSave => 30.0,
|
MenuEntry::NewSave => 30.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn selectable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
MenuEntry::Hidden => false,
|
||||||
|
MenuEntry::Active(_) => true,
|
||||||
|
MenuEntry::DisabledWhite(_) => false,
|
||||||
|
MenuEntry::Disabled(_) => false,
|
||||||
|
MenuEntry::Toggle(_, _) => true,
|
||||||
|
MenuEntry::Options(_, _, _) => true,
|
||||||
|
MenuEntry::SaveData(_) => true,
|
||||||
|
MenuEntry::NewSave => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MenuSelectionResult<'a> {
|
pub enum MenuSelectionResult<'a> {
|
||||||
|
@ -210,7 +227,7 @@ impl Menu {
|
||||||
y = self.y as f32 + 6.0;
|
y = self.y as f32 + 6.0;
|
||||||
for entry in self.entries.iter() {
|
for entry in self.entries.iter() {
|
||||||
match entry {
|
match entry {
|
||||||
MenuEntry::Active(name) => {
|
MenuEntry::Active(name) | MenuEntry::DisabledWhite(name) => {
|
||||||
state.font.draw_text(
|
state.font.draw_text(
|
||||||
name.chars(),
|
name.chars(),
|
||||||
self.x as f32 + 20.0,
|
self.x as f32 + 20.0,
|
||||||
|
@ -253,7 +270,28 @@ impl Menu {
|
||||||
ctx,
|
ctx,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
MenuEntry::Hidden => {}
|
MenuEntry::Options(name, index, value) => {
|
||||||
|
let value_text = if let Some(text) = value.get(*index) { text.as_str() } else { "???" };
|
||||||
|
let val_text_len = state.font.text_width(value_text.chars(), &state.constants);
|
||||||
|
|
||||||
|
state.font.draw_text(
|
||||||
|
name.chars(),
|
||||||
|
self.x as f32 + 20.0,
|
||||||
|
y,
|
||||||
|
&state.constants,
|
||||||
|
&mut state.texture_set,
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
state.font.draw_text(
|
||||||
|
value_text.chars(),
|
||||||
|
self.x as f32 + self.width as f32 - val_text_len,
|
||||||
|
y,
|
||||||
|
&state.constants,
|
||||||
|
&mut state.texture_set,
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,14 +327,8 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(entry) = self.entries.get(self.selected) {
|
if let Some(entry) = self.entries.get(self.selected) {
|
||||||
match entry {
|
if entry.selectable() {
|
||||||
MenuEntry::Active(_) => {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
MenuEntry::Toggle(_, _) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -314,7 +346,7 @@ impl Menu {
|
||||||
y += entry.height() as f32;
|
y += entry.height() as f32;
|
||||||
|
|
||||||
match entry {
|
match entry {
|
||||||
MenuEntry::Active(_) | MenuEntry::Toggle(_, _)
|
MenuEntry::Active(_) | MenuEntry::Toggle(_, _) | MenuEntry::Options(_, _, _)
|
||||||
if (self.selected == idx && controller.trigger_ok())
|
if (self.selected == idx && controller.trigger_ok())
|
||||||
|| state.touch_controls.consume_click_in(entry_bounds) =>
|
|| state.touch_controls.consume_click_in(entry_bounds) =>
|
||||||
{
|
{
|
||||||
|
|
233
src/menu/settings_menu.rs
Normal file
233
src/menu/settings_menu.rs
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
use crate::framework::context::Context;
|
||||||
|
use crate::framework::error::GameResult;
|
||||||
|
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||||
|
use crate::menu::{Menu, MenuSelectionResult};
|
||||||
|
use crate::menu::MenuEntry;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SettingsMenu {
|
||||||
|
current: CurrentMenu,
|
||||||
|
main: Menu,
|
||||||
|
graphics: Menu,
|
||||||
|
sound: Menu,
|
||||||
|
}
|
||||||
|
|
||||||
|
static DISCORD_LINK: &str = "https://discord.gg/fbRsNNB";
|
||||||
|
|
||||||
|
impl SettingsMenu {
|
||||||
|
pub fn new() -> SettingsMenu {
|
||||||
|
let main = Menu::new(0, 0, 200, 0);
|
||||||
|
let graphics = Menu::new(0, 0, 180, 0);
|
||||||
|
let sound = Menu::new(0, 0, 260, 0);
|
||||||
|
|
||||||
|
SettingsMenu { current: CurrentMenu::MainMenu, main, graphics, sound }
|
||||||
|
}
|
||||||
|
|
||||||
|
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("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(),
|
||||||
|
if state.timing_mode == TimingMode::_50Hz { 0 } else { 1 },
|
||||||
|
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()));
|
||||||
|
|
||||||
|
self.sound.push_entry(MenuEntry::DisabledWhite("BGM Interpolation:".to_owned()));
|
||||||
|
self.sound.push_entry(MenuEntry::Options(
|
||||||
|
"".to_owned(),
|
||||||
|
state.settings.organya_interpolation as usize,
|
||||||
|
vec![
|
||||||
|
"Nearest (fastest, lowest quality)".to_owned(),
|
||||||
|
"Linear (fast, similar to freeware on Vista+)".to_owned(),
|
||||||
|
"Cosine".to_owned(),
|
||||||
|
"Cubic".to_owned(),
|
||||||
|
"Polyphase (slowest, similar to freeware on XP)".to_owned()
|
||||||
|
],
|
||||||
|
));
|
||||||
|
self.sound.push_entry(MenuEntry::Disabled(format!("Soundtrack: {}", state.settings.soundtrack)));
|
||||||
|
self.sound.push_entry(MenuEntry::Active("< Back".to_owned()));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
if let MenuEntry::Options(_, value, _) = toggle {
|
||||||
|
match state.timing_mode {
|
||||||
|
TimingMode::_50Hz => {
|
||||||
|
state.timing_mode = TimingMode::_60Hz;
|
||||||
|
*value = 1;
|
||||||
|
}
|
||||||
|
TimingMode::_60Hz => {
|
||||||
|
state.timing_mode = TimingMode::_50Hz;
|
||||||
|
*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) => {
|
||||||
|
if let MenuEntry::Toggle(_, value) = toggle {
|
||||||
|
state.settings.motion_interpolation = !state.settings.motion_interpolation;
|
||||||
|
let _ = state.settings.save(ctx);
|
||||||
|
|
||||||
|
*value = state.settings.motion_interpolation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuSelectionResult::Selected(2, toggle) => {
|
||||||
|
if let MenuEntry::Toggle(_, value) = toggle {
|
||||||
|
state.settings.subpixel_coords = !state.settings.subpixel_coords;
|
||||||
|
let _ = state.settings.save(ctx);
|
||||||
|
|
||||||
|
*value = state.settings.subpixel_coords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuSelectionResult::Selected(3, toggle) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuSelectionResult::Selected(4, toggle) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuSelectionResult::Selected(6, _) | MenuSelectionResult::Canceled => {
|
||||||
|
self.current = CurrentMenu::MainMenu
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
CurrentMenu::SoundMenu => match self.sound.tick(controller, state) {
|
||||||
|
MenuSelectionResult::Selected(1, toggle) => {
|
||||||
|
if let MenuEntry::Options(_, value, _) = toggle {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuSelectionResult::Selected(3, _) | MenuSelectionResult::Canceled => {
|
||||||
|
self.current = CurrentMenu::MainMenu
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,10 @@ use crate::framework::error::GameResult;
|
||||||
use crate::framework::graphics;
|
use crate::framework::graphics;
|
||||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||||
use crate::input::touch_controls::TouchControlType;
|
use crate::input::touch_controls::TouchControlType;
|
||||||
|
use crate::menu::settings_menu::SettingsMenu;
|
||||||
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -25,7 +26,7 @@ pub struct TitleScene {
|
||||||
controller: CombinedMenuController,
|
controller: CombinedMenuController,
|
||||||
current_menu: CurrentMenu,
|
current_menu: CurrentMenu,
|
||||||
main_menu: Menu,
|
main_menu: Menu,
|
||||||
option_menu: Menu,
|
option_menu: SettingsMenu,
|
||||||
save_select_menu: Menu,
|
save_select_menu: Menu,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ impl TitleScene {
|
||||||
controller: CombinedMenuController::new(),
|
controller: CombinedMenuController::new(),
|
||||||
current_menu: CurrentMenu::MainMenu,
|
current_menu: CurrentMenu::MainMenu,
|
||||||
main_menu: Menu::new(0, 0, 100, 0),
|
main_menu: Menu::new(0, 0, 100, 0),
|
||||||
option_menu: Menu::new(0, 0, 180, 0),
|
option_menu: SettingsMenu::new(),
|
||||||
save_select_menu: Menu::new(0, 0, 200, 0),
|
save_select_menu: Menu::new(0, 0, 200, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,6 @@ impl TitleScene {
|
||||||
|
|
||||||
// asset copyright for freeware version
|
// asset copyright for freeware version
|
||||||
static COPYRIGHT_PIXEL: &str = "2004.12 Studio Pixel";
|
static COPYRIGHT_PIXEL: &str = "2004.12 Studio Pixel";
|
||||||
static DISCORD_LINK: &str = "https://discord.gg/fbRsNNB";
|
|
||||||
|
|
||||||
impl Scene for TitleScene {
|
impl Scene for TitleScene {
|
||||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
|
@ -103,27 +103,7 @@ impl Scene for TitleScene {
|
||||||
}
|
}
|
||||||
self.main_menu.push_entry(MenuEntry::Active("Quit".to_string()));
|
self.main_menu.push_entry(MenuEntry::Active("Quit".to_string()));
|
||||||
|
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle(
|
self.option_menu.init(state, ctx)?;
|
||||||
"Original timing (50TPS)".to_string(),
|
|
||||||
state.timing_mode == TimingMode::_50Hz,
|
|
||||||
));
|
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
|
|
||||||
if state.constants.supports_og_textures {
|
|
||||||
self.option_menu
|
|
||||||
.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
|
|
||||||
} else {
|
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled("Original textures".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.constants.is_cs_plus {
|
|
||||||
self.option_menu
|
|
||||||
.push_entry(MenuEntry::Toggle("Seasonal textures".to_string(), state.settings.seasonal_textures));
|
|
||||||
} else {
|
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled("Seasonal textures".to_string()));
|
|
||||||
}
|
|
||||||
self.option_menu.push_entry(MenuEntry::Active(DISCORD_LINK.to_owned()));
|
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled(["Renderer: ", &ctx.renderer.as_ref().unwrap().renderer_name()].join("")));
|
|
||||||
self.option_menu.push_entry(MenuEntry::Active("Back".to_string()));
|
|
||||||
|
|
||||||
self.save_select_menu.push_entry(MenuEntry::NewSave);
|
self.save_select_menu.push_entry(MenuEntry::NewSave);
|
||||||
self.save_select_menu.push_entry(MenuEntry::NewSave);
|
self.save_select_menu.push_entry(MenuEntry::NewSave);
|
||||||
|
@ -146,10 +126,6 @@ impl Scene for TitleScene {
|
||||||
self.main_menu.x = ((state.canvas_size.0 - self.main_menu.width as f32) / 2.0).floor() as isize;
|
self.main_menu.x = ((state.canvas_size.0 - self.main_menu.width as f32) / 2.0).floor() as isize;
|
||||||
self.main_menu.y = ((state.canvas_size.1 + 70.0 - self.main_menu.height as f32) / 2.0).floor() as isize;
|
self.main_menu.y = ((state.canvas_size.1 + 70.0 - self.main_menu.height as f32) / 2.0).floor() as isize;
|
||||||
|
|
||||||
self.option_menu.update_height();
|
|
||||||
self.option_menu.x = ((state.canvas_size.0 - self.option_menu.width as f32) / 2.0).floor() as isize;
|
|
||||||
self.option_menu.y = ((state.canvas_size.1 + 70.0 - self.option_menu.height as f32) / 2.0).floor() as isize;
|
|
||||||
|
|
||||||
match self.current_menu {
|
match self.current_menu {
|
||||||
CurrentMenu::MainMenu => match self.main_menu.tick(&mut self.controller, state) {
|
CurrentMenu::MainMenu => match self.main_menu.tick(&mut self.controller, state) {
|
||||||
MenuSelectionResult::Selected(0, _) => {
|
MenuSelectionResult::Selected(0, _) => {
|
||||||
|
@ -171,55 +147,17 @@ impl Scene for TitleScene {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
CurrentMenu::OptionMenu => match self.option_menu.tick(&mut self.controller, state) {
|
CurrentMenu::OptionMenu => {
|
||||||
MenuSelectionResult::Selected(0, toggle) => {
|
let cm = &mut self.current_menu;
|
||||||
if let MenuEntry::Toggle(_, value) = toggle {
|
self.option_menu.tick(
|
||||||
match state.timing_mode {
|
&mut || {
|
||||||
TimingMode::_50Hz => state.timing_mode = TimingMode::_60Hz,
|
*cm = CurrentMenu::MainMenu;
|
||||||
TimingMode::_60Hz => state.timing_mode = TimingMode::_50Hz,
|
},
|
||||||
_ => {}
|
&mut self.controller,
|
||||||
}
|
state,
|
||||||
let _ = state.settings.save(ctx);
|
ctx,
|
||||||
|
)?;
|
||||||
*value = state.timing_mode == TimingMode::_50Hz;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuSelectionResult::Selected(1, 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(2, toggle) => {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuSelectionResult::Selected(3, toggle) => {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuSelectionResult::Selected(4, _) => {
|
|
||||||
if let Err(e) = webbrowser::open(DISCORD_LINK) {
|
|
||||||
log::warn!("Error opening web browser: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuSelectionResult::Selected(6, _) | MenuSelectionResult::Canceled => {
|
|
||||||
self.current_menu = CurrentMenu::MainMenu;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
CurrentMenu::StartGame => {
|
CurrentMenu::StartGame => {
|
||||||
if self.tick == 10 {
|
if self.tick == 10 {
|
||||||
state.reset_skip_flags();
|
state.reset_skip_flags();
|
||||||
|
@ -262,12 +200,8 @@ impl Scene for TitleScene {
|
||||||
self.draw_text_centered(COPYRIGHT_PIXEL, state.canvas_size.1 - 30.0, state, ctx)?;
|
self.draw_text_centered(COPYRIGHT_PIXEL, state.canvas_size.1 - 30.0, state, ctx)?;
|
||||||
|
|
||||||
match self.current_menu {
|
match self.current_menu {
|
||||||
CurrentMenu::MainMenu => {
|
CurrentMenu::MainMenu => self.main_menu.draw(state, ctx)?,
|
||||||
self.main_menu.draw(state, ctx)?;
|
CurrentMenu::OptionMenu => self.option_menu.draw(state, ctx)?,
|
||||||
}
|
|
||||||
CurrentMenu::OptionMenu => {
|
|
||||||
self.option_menu.draw(state, ctx)?;
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::GameResult;
|
use crate::framework::error::GameResult;
|
||||||
use crate::framework::filesystem::{user_create, user_open};
|
use crate::framework::filesystem::{user_create, user_open};
|
||||||
|
@ -8,9 +6,12 @@ use crate::input::keyboard_player_controller::KeyboardController;
|
||||||
use crate::input::player_controller::PlayerController;
|
use crate::input::player_controller::PlayerController;
|
||||||
use crate::input::touch_player_controller::TouchPlayerController;
|
use crate::input::touch_player_controller::TouchPlayerController;
|
||||||
use crate::player::TargetPlayer;
|
use crate::player::TargetPlayer;
|
||||||
|
use crate::sound::InterpolationMode;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
#[serde(default = "current_version")]
|
||||||
|
pub version: u32,
|
||||||
pub seasonal_textures: bool,
|
pub seasonal_textures: bool,
|
||||||
pub original_textures: bool,
|
pub original_textures: bool,
|
||||||
pub shader_effects: bool,
|
pub shader_effects: bool,
|
||||||
|
@ -18,6 +19,8 @@ pub struct Settings {
|
||||||
pub motion_interpolation: bool,
|
pub motion_interpolation: bool,
|
||||||
pub touch_controls: bool,
|
pub touch_controls: bool,
|
||||||
pub soundtrack: String,
|
pub soundtrack: String,
|
||||||
|
#[serde(default = "default_interpolation")]
|
||||||
|
pub organya_interpolation: InterpolationMode,
|
||||||
#[serde(default = "p1_default_keymap")]
|
#[serde(default = "p1_default_keymap")]
|
||||||
pub player1_key_map: PlayerKeyMap,
|
pub player1_key_map: PlayerKeyMap,
|
||||||
#[serde(default = "p2_default_keymap")]
|
#[serde(default = "p2_default_keymap")]
|
||||||
|
@ -32,6 +35,13 @@ pub struct Settings {
|
||||||
pub debug_outlines: bool,
|
pub debug_outlines: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn current_version() -> u32 { 2 }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_interpolation() -> InterpolationMode { InterpolationMode::Linear }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn default_speed() -> f64 {
|
fn default_speed() -> f64 {
|
||||||
1.0
|
1.0
|
||||||
}
|
}
|
||||||
|
@ -70,6 +80,7 @@ impl Settings {
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Settings {
|
Settings {
|
||||||
|
version: 2,
|
||||||
seasonal_textures: true,
|
seasonal_textures: true,
|
||||||
original_textures: false,
|
original_textures: false,
|
||||||
shader_effects: true,
|
shader_effects: true,
|
||||||
|
@ -77,6 +88,7 @@ impl Default for Settings {
|
||||||
motion_interpolation: true,
|
motion_interpolation: true,
|
||||||
touch_controls: cfg!(target_os = "android"),
|
touch_controls: cfg!(target_os = "android"),
|
||||||
soundtrack: "".to_string(),
|
soundtrack: "".to_string(),
|
||||||
|
organya_interpolation: InterpolationMode::Linear,
|
||||||
player1_key_map: p1_default_keymap(),
|
player1_key_map: p1_default_keymap(),
|
||||||
player2_key_map: p2_default_keymap(),
|
player2_key_map: p2_default_keymap(),
|
||||||
speed: 1.0,
|
speed: 1.0,
|
||||||
|
@ -87,7 +99,7 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
pub struct PlayerKeyMap {
|
pub struct PlayerKeyMap {
|
||||||
pub left: ScanCode,
|
pub left: ScanCode,
|
||||||
pub up: ScanCode,
|
pub up: ScanCode,
|
||||||
|
@ -102,6 +114,7 @@ pub struct PlayerKeyMap {
|
||||||
pub map: ScanCode,
|
pub map: ScanCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn p1_default_keymap() -> PlayerKeyMap {
|
fn p1_default_keymap() -> PlayerKeyMap {
|
||||||
PlayerKeyMap {
|
PlayerKeyMap {
|
||||||
left: ScanCode::Left,
|
left: ScanCode::Left,
|
||||||
|
@ -118,6 +131,7 @@ fn p1_default_keymap() -> PlayerKeyMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn p2_default_keymap() -> PlayerKeyMap {
|
fn p2_default_keymap() -> PlayerKeyMap {
|
||||||
PlayerKeyMap {
|
PlayerKeyMap {
|
||||||
left: ScanCode::Comma,
|
left: ScanCode::Comma,
|
||||||
|
|
|
@ -96,6 +96,10 @@ impl SoundManager {
|
||||||
let _ = self.tx.send(PlaybackMessage::StopSample(id));
|
let _ = self.tx.send(PlaybackMessage::StopSample(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_org_interpolation(&self, interpolation: InterpolationMode) {
|
||||||
|
let _ = self.tx.send(PlaybackMessage::SetOrgInterpolation(interpolation));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn play_song(
|
pub fn play_song(
|
||||||
&mut self,
|
&mut self,
|
||||||
song_id: usize,
|
song_id: usize,
|
||||||
|
@ -113,6 +117,7 @@ impl SoundManager {
|
||||||
self.prev_song_id = self.current_song_id;
|
self.prev_song_id = self.current_song_id;
|
||||||
self.current_song_id = 0;
|
self.current_song_id = 0;
|
||||||
|
|
||||||
|
self.tx.send(PlaybackMessage::SetOrgInterpolation(settings.organya_interpolation))?;
|
||||||
self.tx.send(PlaybackMessage::SaveState)?;
|
self.tx.send(PlaybackMessage::SaveState)?;
|
||||||
self.tx.send(PlaybackMessage::Stop)?;
|
self.tx.send(PlaybackMessage::Stop)?;
|
||||||
} else if let Some(song_name) = constants.music_table.get(song_id) {
|
} else if let Some(song_name) = constants.music_table.get(song_id) {
|
||||||
|
@ -152,6 +157,7 @@ impl SoundManager {
|
||||||
|
|
||||||
self.prev_song_id = self.current_song_id;
|
self.prev_song_id = self.current_song_id;
|
||||||
self.current_song_id = song_id;
|
self.current_song_id = song_id;
|
||||||
|
self.tx.send(PlaybackMessage::SetOrgInterpolation(settings.organya_interpolation))?;
|
||||||
self.tx.send(PlaybackMessage::SaveState)?;
|
self.tx.send(PlaybackMessage::SaveState)?;
|
||||||
self.tx.send(PlaybackMessage::PlayOrganyaSong(Box::new(org)))?;
|
self.tx.send(PlaybackMessage::PlayOrganyaSong(Box::new(org)))?;
|
||||||
|
|
||||||
|
@ -339,6 +345,7 @@ enum PlaybackMessage {
|
||||||
SaveState,
|
SaveState,
|
||||||
RestoreState,
|
RestoreState,
|
||||||
SetSampleParams(u8, PixToneParameters),
|
SetSampleParams(u8, PixToneParameters),
|
||||||
|
SetOrgInterpolation(InterpolationMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
|
@ -520,6 +527,9 @@ where
|
||||||
Ok(PlaybackMessage::SetSampleParams(id, params)) => {
|
Ok(PlaybackMessage::SetSampleParams(id, params)) => {
|
||||||
pixtone.set_sample_parameters(id, params);
|
pixtone.set_sample_parameters(id, params);
|
||||||
}
|
}
|
||||||
|
Ok(PlaybackMessage::SetOrgInterpolation(interpolation)) => {
|
||||||
|
org_engine.interpolation = interpolation;
|
||||||
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -626,11 +636,11 @@ where
|
||||||
|
|
||||||
if state {
|
if state {
|
||||||
if let Err(e) = stream.pause() {
|
if let Err(e) = stream.pause() {
|
||||||
log::error!("Failed to pause the stream: {}", e);
|
log::error!("Failed to pause the stream: {:?}", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) = stream.play() {
|
if let Err(e) = stream.play() {
|
||||||
log::error!("Failed to unpause the stream: {}", e);
|
log::error!("Failed to unpause the stream: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue