use localized soundtrack names

This commit is contained in:
József Sallai 2024-03-24 00:57:56 +02:00
parent 7630a9b60e
commit 9c95b20f5c
6 changed files with 140 additions and 48 deletions

View File

@ -207,7 +207,7 @@ pub struct AnimatedFace {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExtraSoundtrack { pub struct ExtraSoundtrack {
pub name: String, pub id: String,
pub path: String, pub path: String,
pub available: bool, pub available: bool,
} }
@ -1613,10 +1613,10 @@ impl EngineConstants {
font_path: "csfont.fnt".to_owned(), font_path: "csfont.fnt".to_owned(),
font_space_offset: 0.0, font_space_offset: 0.0,
soundtracks: vec![ soundtracks: vec![
ExtraSoundtrack { name: "Remastered".to_owned(), path: "/base/Ogg11/".to_owned(), available: false }, ExtraSoundtrack { id: "remastered".to_owned(), path: "/base/Ogg11/".to_owned(), available: false },
ExtraSoundtrack { name: "New".to_owned(), path: "/base/Ogg/".to_owned(), available: false }, ExtraSoundtrack { id: "new".to_owned(), path: "/base/Ogg/".to_owned(), available: false },
ExtraSoundtrack { name: "Famitracks".to_owned(), path: "/base/ogg17/".to_owned(), available: false }, ExtraSoundtrack { id: "famitracks".to_owned(), path: "/base/ogg17/".to_owned(), available: false },
ExtraSoundtrack { name: "Ridiculon".to_owned(), path: "/base/ogg_ridic/".to_owned(), available: false }, ExtraSoundtrack { id: "ridiculon".to_owned(), path: "/base/ogg_ridic/".to_owned(), available: false },
], ],
music_table: vec![ music_table: vec![
"xxxx".to_owned(), "xxxx".to_owned(),

View File

@ -95,7 +95,7 @@ fn default_true() -> bool {
#[inline(always)] #[inline(always)]
fn current_version() -> u32 { fn current_version() -> u32 {
24 25
} }
#[inline(always)] #[inline(always)]
@ -347,6 +347,18 @@ impl Settings {
self.allow_strafe = true; self.allow_strafe = true;
} }
if self.version == 24 {
self.version = 25;
self.soundtrack = match self.soundtrack.as_str() {
"Organya" => "organya".to_owned(),
"Remastered" => "remastered".to_owned(),
"New" => "new".to_owned(),
"Famitracks" => "famitracks".to_owned(),
"Ridiculon" => "ridiculon".to_owned(),
_ => self.soundtrack.clone(),
}
}
if self.version != initial_version { if self.version != initial_version {
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version); log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
} }

View File

@ -21,7 +21,9 @@ use crate::game::profile::GameProfile;
#[cfg(feature = "scripting-lua")] #[cfg(feature = "scripting-lua")]
use crate::game::scripting::lua::LuaScriptingState; use crate::game::scripting::lua::LuaScriptingState;
use crate::game::scripting::tsc::credit_script::{CreditScript, CreditScriptVM}; use crate::game::scripting::tsc::credit_script::{CreditScript, CreditScriptVM};
use crate::game::scripting::tsc::text_script::{ScriptMode, TextScript, TextScriptEncoding, TextScriptExecutionState, TextScriptVM}; use crate::game::scripting::tsc::text_script::{
ScriptMode, TextScript, TextScriptEncoding, TextScriptExecutionState, TextScriptVM,
};
use crate::game::settings::Settings; use crate::game::settings::Settings;
use crate::game::stage::StageData; use crate::game::stage::StageData;
use crate::graphics::bmfont::BMFont; use crate::graphics::bmfont::BMFont;
@ -408,7 +410,7 @@ impl SharedGameState {
for soundtrack in constants.soundtracks.iter_mut() { for soundtrack in constants.soundtracks.iter_mut() {
if filesystem::exists(ctx, &soundtrack.path) { if filesystem::exists(ctx, &soundtrack.path) {
log::info!("Enabling soundtrack {} from {}.", soundtrack.name, soundtrack.path); log::info!("Enabling soundtrack {} from {}.", soundtrack.id, soundtrack.path);
soundtrack.available = true; soundtrack.available = true;
} }
} }
@ -420,11 +422,11 @@ impl SharedGameState {
let locale = SharedGameState::get_locale(&constants, &settings.locale).unwrap_or_default(); let locale = SharedGameState::get_locale(&constants, &settings.locale).unwrap_or_default();
if (locale.code == "jp" || locale.code == "en") && constants.is_base() { if (locale.code == "jp" || locale.code == "en") && constants.is_base() {
constants.textscript.encoding = TextScriptEncoding::ShiftJIS constants.textscript.encoding = TextScriptEncoding::ShiftJIS
} else { } else {
constants.textscript.encoding = TextScriptEncoding::UTF8 constants.textscript.encoding = TextScriptEncoding::UTF8
} }
let font = BMFont::load(&constants.base_paths, &locale.font.path, ctx, locale.font.scale).or_else(|e| { let font = BMFont::load(&constants.base_paths, &locale.font.path, ctx, locale.font.scale).or_else(|e| {
log::warn!("Failed to load font, using built-in: {}", e); log::warn!("Failed to load font, using built-in: {}", e);
BMFont::load(&vec!["/".to_owned()], "builtin/builtin_font.fnt", ctx, 1.0) BMFont::load(&vec!["/".to_owned()], "builtin/builtin_font.fnt", ctx, 1.0)
@ -573,9 +575,9 @@ impl SharedGameState {
if let Some(locale) = SharedGameState::get_locale(&self.constants, &self.settings.locale) { if let Some(locale) = SharedGameState::get_locale(&self.constants, &self.settings.locale) {
self.loc = locale; self.loc = locale;
if (self.loc.code == "jp" || self.loc.code == "en") && self.constants.is_base() { if (self.loc.code == "jp" || self.loc.code == "en") && self.constants.is_base() {
self.constants.textscript.encoding = TextScriptEncoding::ShiftJIS self.constants.textscript.encoding = TextScriptEncoding::ShiftJIS
} else { } else {
self.constants.textscript.encoding = TextScriptEncoding::UTF8 self.constants.textscript.encoding = TextScriptEncoding::UTF8
} }
} }
@ -647,7 +649,12 @@ impl SharedGameState {
Ok(()) Ok(())
} }
pub fn save_game(&mut self, game_scene: &mut GameScene, ctx: &mut Context, target_player: Option<TargetPlayer>) -> GameResult { pub fn save_game(
&mut self,
game_scene: &mut GameScene,
ctx: &mut Context,
target_player: Option<TargetPlayer>,
) -> GameResult {
if let Some(save_path) = self.get_save_filename(self.save_slot) { if let Some(save_path) = self.get_save_filename(self.save_slot) {
if let Ok(data) = filesystem::open_options(ctx, save_path, OpenOptions::new().write(true).create(true)) { if let Ok(data) = filesystem::open_options(ctx, save_path, OpenOptions::new().write(true).create(true)) {
let profile = GameProfile::dump(self, game_scene, target_player); let profile = GameProfile::dump(self, game_scene, target_player);
@ -896,6 +903,18 @@ impl SharedGameState {
out_locale out_locale
} }
pub fn get_localized_soundtrack_name(&self, id: &str) -> String {
if id == "organya" {
return self.loc.t("soundtrack.organya").to_owned();
}
self.constants
.soundtracks
.iter()
.find(|s| s.id == id)
.map_or_else(|| id.to_owned(), |s| self.loc.t(format!("soundtrack.{}", s.id).as_str()).to_owned())
}
pub fn tt(&self, key: &str, args: &[(&str, &str)]) -> String { pub fn tt(&self, key: &str, args: &[(&str, &str)]) -> String {
return self.loc.tt(key, args); return self.loc.tt(key, args);
} }

View File

@ -500,18 +500,21 @@ impl SettingsMenu {
); );
self.sound.push_entry( self.sound.push_entry(
SoundMenuEntry::Soundtrack, SoundMenuEntry::Soundtrack,
MenuEntry::Active( MenuEntry::Active(state.loc.tt(
state.tt( "menus.options_menu.sound_menu.soundtrack",
"menus.options_menu.sound_menu.soundtrack", &[("soundtrack", state.get_localized_soundtrack_name(&state.settings.soundtrack).as_str())],
&[("soundtrack", state.settings.soundtrack.as_str())], )),
),
),
); );
self.sound.push_entry(SoundMenuEntry::Back, MenuEntry::Active(state.loc.t("common.back").to_owned())); self.sound.push_entry(SoundMenuEntry::Back, MenuEntry::Active(state.loc.t("common.back").to_owned()));
let mut soundtrack_entries = let mut soundtrack_entries = state
state.constants.soundtracks.iter().filter(|s| s.available).map(|s| s.name.to_owned()).collect_vec(); .constants
soundtrack_entries.push("Organya".to_owned()); .soundtracks
.iter()
.filter(|s| s.available)
.map(|s| state.loc.t(format!("soundtrack.{}", s.id).as_str()).to_owned())
.collect_vec();
soundtrack_entries.push(state.loc.t("soundtrack.organya").to_owned());
if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") { if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") {
for entry in dir { for entry in dir {
@ -882,7 +885,7 @@ impl SettingsMenu {
for (id, entry) in &self.soundtrack.entries { for (id, entry) in &self.soundtrack.entries {
if let MenuEntry::Active(soundtrack) = entry { if let MenuEntry::Active(soundtrack) = entry {
if soundtrack == &state.settings.soundtrack { if soundtrack == &state.get_localized_soundtrack_name(&state.settings.soundtrack) {
active_soundtrack = *id; active_soundtrack = *id;
let _ = state.settings.save(ctx); let _ = state.settings.save(ctx);
break; break;
@ -937,12 +940,28 @@ impl SettingsMenu {
CurrentMenu::SoundtrackMenu => match self.soundtrack.tick(controller, state) { CurrentMenu::SoundtrackMenu => match self.soundtrack.tick(controller, state) {
MenuSelectionResult::Selected(SoundtrackMenuEntry::Soundtrack(_), entry) => { MenuSelectionResult::Selected(SoundtrackMenuEntry::Soundtrack(_), entry) => {
if let MenuEntry::Active(name) = entry { if let MenuEntry::Active(name) = entry {
state.settings.soundtrack = name.to_owned(); state.settings.soundtrack = state
.constants
.soundtracks
.iter()
.find(|s| state.loc.t(format!("soundtrack.{}", s.id).as_str()) == name)
.map_or_else(|| name.to_owned(), |s| s.id.clone());
if state.settings.soundtrack == "Organya" {
state.settings.soundtrack = "organya".to_owned()
}
let _ = state.settings.save(ctx); let _ = state.settings.save(ctx);
self.sound.set_entry( self.sound.set_entry(
SoundMenuEntry::Soundtrack, SoundMenuEntry::Soundtrack,
MenuEntry::Active(format!("Soundtrack: {}", state.settings.soundtrack)), MenuEntry::Active(state.loc.tt(
"menus.options_menu.sound_menu.soundtrack",
&[(
"soundtrack",
state.get_localized_soundtrack_name(&state.settings.soundtrack).as_str(),
)],
)),
); );
state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?; state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?;
} }

View File

@ -3,6 +3,7 @@ use itertools::Itertools;
use crate::common::Color; use crate::common::Color;
use crate::common::Rect; use crate::common::Rect;
use crate::components::background::Background; use crate::components::background::Background;
use crate::engine_constants::ExtraSoundtrack;
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::GameResult; use crate::framework::error::GameResult;
use crate::framework::filesystem; use crate::framework::filesystem;
@ -16,10 +17,43 @@ use crate::input::combined_menu_controller::CombinedMenuController;
use crate::scene::title_scene::TitleScene; use crate::scene::title_scene::TitleScene;
use crate::scene::Scene; use crate::scene::Scene;
#[derive(Clone, Debug)]
pub enum JukeboxSoundtrackKind {
Organya,
Extra(ExtraSoundtrack),
Custom(String),
}
impl JukeboxSoundtrackKind {
pub fn eq_str(&self, other: &str) -> bool {
match self {
JukeboxSoundtrackKind::Organya => other == "organya",
JukeboxSoundtrackKind::Extra(s) => other == s.id,
JukeboxSoundtrackKind::Custom(s) => other == s,
}
}
pub fn to_localized_string(&self, state: &mut SharedGameState) -> String {
match self {
JukeboxSoundtrackKind::Organya => state.loc.t("soundtrack.organya").to_owned(),
JukeboxSoundtrackKind::Extra(s) => state.loc.t(format!("soundtrack.{}", s.id).as_str()).to_owned(),
JukeboxSoundtrackKind::Custom(s) => s.clone(),
}
}
pub fn to_id(&self) -> String {
match self {
JukeboxSoundtrackKind::Organya => "organya".to_owned(),
JukeboxSoundtrackKind::Extra(s) => s.id.clone(),
JukeboxSoundtrackKind::Custom(s) => s.clone(),
}
}
}
pub struct JukeboxScene { pub struct JukeboxScene {
selected_song: u16, selected_song: u16,
song_list: Vec<String>, song_list: Vec<String>,
soundtracks: Vec<String>, soundtracks: Vec<JukeboxSoundtrackKind>,
selected_soundtrack: usize, selected_soundtrack: usize,
controller: CombinedMenuController, controller: CombinedMenuController,
background: Background, background: Background,
@ -79,17 +113,26 @@ impl Scene for JukeboxScene {
.cloned() .cloned()
.collect(); .collect();
let mut soundtrack_entries = let mut soundtrack_entries = state
state.constants.soundtracks.iter().filter(|s| s.available).map(|s| s.name.to_owned()).collect_vec(); .constants
soundtrack_entries.push("Organya".to_owned()); .soundtracks
.iter()
.filter(|s| s.available)
.map(|s| JukeboxSoundtrackKind::Extra(s.clone()))
.collect_vec();
soundtrack_entries.push(JukeboxSoundtrackKind::Organya);
if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") { if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") {
for entry in dir { for entry in dir {
if filesystem::is_dir(ctx, &entry) { if filesystem::is_dir(ctx, &entry) {
let filename = entry.file_name().unwrap().to_string_lossy().to_string(); let filename = entry.file_name().unwrap().to_string_lossy().to_string();
if !soundtrack_entries.contains(&filename) { if soundtrack_entries
soundtrack_entries.push(filename); .iter()
.find(|s| matches!(s, JukeboxSoundtrackKind::Custom(s) if s == &filename))
.is_none()
{
soundtrack_entries.push(JukeboxSoundtrackKind::Custom(filename.clone()));
} }
} }
} }
@ -98,7 +141,7 @@ impl Scene for JukeboxScene {
self.soundtracks = soundtrack_entries.clone(); self.soundtracks = soundtrack_entries.clone();
let selected_soundtrack_index = let selected_soundtrack_index =
self.soundtracks.iter().position(|s| s == &state.settings.soundtrack).unwrap_or(0); self.soundtracks.iter().position(|s| s.eq_str(&state.settings.soundtrack)).unwrap_or(0);
self.selected_soundtrack = selected_soundtrack_index; self.selected_soundtrack = selected_soundtrack_index;
self.previous_pause_on_focus_loss_setting = state.settings.pause_on_focus_loss; self.previous_pause_on_focus_loss_setting = state.settings.pause_on_focus_loss;
@ -150,13 +193,13 @@ impl Scene for JukeboxScene {
if self.controller.trigger_shift_left() { if self.controller.trigger_shift_left() {
self.selected_soundtrack = self.selected_soundtrack.checked_sub(1).unwrap_or(self.soundtracks.len() - 1); self.selected_soundtrack = self.selected_soundtrack.checked_sub(1).unwrap_or(self.soundtracks.len() - 1);
state.settings.soundtrack = self.soundtracks[self.selected_soundtrack].to_string(); state.settings.soundtrack = self.soundtracks[self.selected_soundtrack].to_id();
state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?; state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?;
} }
if self.controller.trigger_shift_right() { if self.controller.trigger_shift_right() {
self.selected_soundtrack = (self.selected_soundtrack + 1) % self.soundtracks.len(); self.selected_soundtrack = (self.selected_soundtrack + 1) % self.soundtracks.len();
state.settings.soundtrack = self.soundtracks[self.selected_soundtrack].to_string(); state.settings.soundtrack = self.soundtracks[self.selected_soundtrack].to_id();
state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?; state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?;
} }
@ -284,9 +327,9 @@ impl Scene for JukeboxScene {
// Write Soundtrack name // Write Soundtrack name
let text = &state.settings.soundtrack; let text = self.soundtracks[self.selected_soundtrack].to_localized_string(state);
state.font.builder().center(state.canvas_size.0).y(20.0).shadow(true).draw( state.font.builder().center(state.canvas_size.0).y(20.0).shadow(true).draw(
text, text.as_str(),
ctx, ctx,
&state.constants, &state.constants,
&mut state.texture_set, &mut state.texture_set,

View File

@ -11,8 +11,8 @@ use num_traits::clamp;
use crate::engine_constants::EngineConstants; use crate::engine_constants::EngineConstants;
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult};
use crate::framework::error::GameError::{AudioError, InvalidValue}; use crate::framework::error::GameError::{AudioError, InvalidValue};
use crate::framework::error::{GameError, GameResult};
use crate::framework::filesystem; use crate::framework::filesystem;
use crate::framework::filesystem::File; use crate::framework::filesystem::File;
use crate::game::settings::Settings; use crate::game::settings::Settings;
@ -286,20 +286,19 @@ impl SoundManager {
paths.insert(0, "/Soundtracks/".to_owned() + &settings.soundtrack + "/"); paths.insert(0, "/Soundtracks/".to_owned() + &settings.soundtrack + "/");
if let Some(soundtrack) = if let Some(soundtrack) = constants.soundtracks.iter().find(|s| s.available && s.id == settings.soundtrack)
constants.soundtracks.iter().find(|s| s.available && s.name == settings.soundtrack)
{ {
paths.insert(0, soundtrack.path.clone()); paths.insert(0, soundtrack.path.clone());
} }
let songs_paths = paths.iter().map(|prefix| { let songs_paths = paths.iter().map(|prefix| {
[ [
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
( (
SongFormat::OggMultiPart, SongFormat::OggMultiPart,
vec![format!("{}{}_intro.ogg", prefix, song_name), format!("{}{}_loop.ogg", prefix, song_name)], vec![format!("{}{}_intro.ogg", prefix, song_name), format!("{}{}_loop.ogg", prefix, song_name)],
), ),
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
(SongFormat::OggSinglePart, vec![format!("{}{}.ogg", prefix, song_name)]), (SongFormat::OggSinglePart, vec![format!("{}{}.ogg", prefix, song_name)]),
(SongFormat::Organya, vec![format!("{}{}.org", prefix, song_name)]), (SongFormat::Organya, vec![format!("{}{}.org", prefix, song_name)]),
] ]
@ -307,7 +306,7 @@ impl SoundManager {
for songs in songs_paths { for songs in songs_paths {
for (format, paths) in for (format, paths) in
songs.iter().filter(|(_, paths)| paths.iter().all(|path| filesystem::exists(ctx, path))) songs.iter().filter(|(_, paths)| paths.iter().all(|path| filesystem::exists(ctx, path)))
{ {
match format { match format {
SongFormat::Organya => { SongFormat::Organya => {
@ -385,7 +384,7 @@ impl SoundManager {
Box::new(song_intro), Box::new(song_intro),
Box::new(song_loop), Box::new(song_loop),
)) ))
.unwrap(); .unwrap();
return Ok(()); return Ok(());
} }
@ -597,8 +596,8 @@ fn run<T>(
device: cpal::Device, device: cpal::Device,
config: cpal::StreamConfig, config: cpal::StreamConfig,
) -> GameResult<cpal::Stream> ) -> GameResult<cpal::Stream>
where where
T: cpal::SizedSample + cpal::FromSample<u16>, T: cpal::SizedSample + cpal::FromSample<u16>,
{ {
let sample_rate = config.sample_rate.0 as f32; let sample_rate = config.sample_rate.0 as f32;
let channels = config.channels as usize; let channels = config.channels as usize;
@ -732,7 +731,7 @@ fn run<T>(
assert!(new_speed > 0.0); assert!(new_speed > 0.0);
speed = new_speed; speed = new_speed;
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
ogg_engine.set_sample_rate((sample_rate / new_speed) as usize); ogg_engine.set_sample_rate((sample_rate / new_speed) as usize);
org_engine.set_sample_rate((sample_rate / new_speed) as usize); org_engine.set_sample_rate((sample_rate / new_speed) as usize);
} }
Ok(PlaybackMessage::SetSongVolume(new_volume)) => { Ok(PlaybackMessage::SetSongVolume(new_volume)) => {
@ -895,7 +894,7 @@ fn run<T>(
} }
}, },
err_fn, err_fn,
None None,
); );
if stream_result.is_err() { if stream_result.is_err() {