From 509309ff1d31eac8c2fc4f7125172b6a399b9049 Mon Sep 17 00:00:00 2001 From: wszhdshys <136774049+wszhdshys@users.noreply.github.com> Date: Sun, 17 May 2026 17:49:24 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=96=B9=E9=9D=A2=E7=9A=84=E5=87=86=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- savedata/user.toml | 10 ++++-- src/config/character_volume.rs | 65 ++++++++++++++++++++++++++++++++++ src/config/mod.rs | 6 ++++ src/config/system.rs | 6 ++++ src/config/text.rs | 5 +++ src/config/user.rs | 49 +++++++++++++++++++++---- src/config/volume.rs | 6 ++++ src/executor/mod.rs | 1 + ui/main_window.slint | 6 ++++ 9 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 src/config/character_volume.rs diff --git a/savedata/user.toml b/savedata/user.toml index 55ae25b..c7cc892 100644 --- a/savedata/user.toml +++ b/savedata/user.toml @@ -1,12 +1,16 @@ [auto] -delay = 2.5382617 +delay = 3.5 is_wait = true [text] -speed = 14.647284 -opacity = 0.2754137 +speed = 50.0 +opacity = 0.8 [volume] main = 0.0 bgm = 100.0 voice = 100.0 + +[character_volume] +rir = 100.0 +rar = 100.0 diff --git a/src/config/character_volume.rs b/src/config/character_volume.rs new file mode 100644 index 0000000..856ca98 --- /dev/null +++ b/src/config/character_volume.rs @@ -0,0 +1,65 @@ +use std::collections::HashMap; +use std::rc::Rc; +use serde::{Deserialize, Serialize}; +use slint::{Model, Weak}; +use crate::config::ENGINE_CONFIG; +use crate::config::user::USER_CONFIG; +use crate::executor::executor::Executor; +use crate::ui::ui::{CharacterVolume, MainWindow}; + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct CharacterVolumeConfig { + #[serde(flatten)] + pub(crate) volumes: HashMap, +} + +impl CharacterVolumeConfig { + pub(crate) fn default_from_engine() -> Self { + CharacterVolumeConfig { + volumes: ENGINE_CONFIG + .characters() + .iter() + .map(|name| (name.clone(), 100.0_f32)) + .collect(), + } + } + + pub(crate) fn fill_missing(&mut self) { + for name in ENGINE_CONFIG.characters() { + self.volumes.entry(name.clone()).or_insert(100.0); + } + } + + pub fn from_weak(weak: Weak) -> Self { + if let Some(window) = weak.upgrade() { + let model = window.get_character_volumes(); + let volumes = (0..model.row_count()) + .filter_map(|i| model.row_data(i)) + .map(|item| (item.name.to_string(), item.volume)) + .collect(); + CharacterVolumeConfig { volumes } + } else { + unreachable!() + } + } +} + +impl Executor { + pub fn load_character_volumes(&self) { + let weak = self.get_weak(); + if let Some(window) = weak.upgrade() { + let volumes: Vec = ENGINE_CONFIG + .characters() + .iter() + .map(|name| CharacterVolume { + name: name.as_str().into(), + volume: USER_CONFIG.character_volume(name), + }) + .collect(); + + window.set_character_volumes( + Rc::new(slint::VecModel::from(volumes)).into() + ); + } + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index caa4325..f7f19d5 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use crate::config::initialize::{Character, InitializeConfig}; use serde::{Deserialize, Serialize}; use std::fs; @@ -13,6 +14,7 @@ pub mod voice; pub mod volume; pub mod extra; pub mod cg; +pub mod character_volume; lazy_static::lazy_static! { pub static ref ENGINE_CONFIG: EngineConfig = load_engine_config(); @@ -52,6 +54,10 @@ impl EngineConfig { pub fn save_path(&self) -> &str { &self.initialize.save_path } + + pub fn characters(&self) -> &HashSet { + &self.character.list + } } fn load_engine_config() -> EngineConfig { diff --git a/src/config/system.rs b/src/config/system.rs index bf6962c..b31a8fb 100644 --- a/src/config/system.rs +++ b/src/config/system.rs @@ -10,6 +10,12 @@ pub struct AutoConfig { is_wait: bool, } +impl Default for AutoConfig { + fn default() -> Self { + AutoConfig { delay: 3.5, is_wait: true } + } +} + impl AutoConfig { pub fn delay(&self) -> f32 { self.delay diff --git a/src/config/text.rs b/src/config/text.rs index 25ec62c..bf0cb0a 100644 --- a/src/config/text.rs +++ b/src/config/text.rs @@ -10,6 +10,11 @@ pub struct TextConfig { opacity: f32, } +impl Default for TextConfig { + fn default() -> Self { + TextConfig { speed: 50.0, opacity: 0.8 } + } +} impl TextConfig { pub fn speed(&self) -> f32 { self.speed diff --git a/src/config/user.rs b/src/config/user.rs index 87d41cb..b6a57ab 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -7,6 +7,7 @@ use crate::ui::ui::MainWindow; use serde::{Deserialize, Serialize}; use slint::Weak; use std::fs; +use crate::config::character_volume::CharacterVolumeConfig; lazy_static::lazy_static! { pub static ref USER_CONFIG: UserConfig = load_user_config(); @@ -17,9 +18,19 @@ pub struct UserConfig { auto: AutoConfig, text: TextConfig, volume: VolumeConfig, + character_volume: CharacterVolumeConfig, } impl UserConfig { + fn default() -> Self { + UserConfig { + auto: AutoConfig::default(), + text: TextConfig::default(), + volume: VolumeConfig::default(), + character_volume: CharacterVolumeConfig::default_from_engine(), + } + } + pub fn delay(&self) -> f32 { self.auto.delay() } @@ -48,25 +59,49 @@ impl UserConfig { self.text.opacity() } + pub fn character_volume(&self, name: &str) -> f32 { + self.character_volume.volumes.get(name).unwrap().clone() + } + pub fn from_weak(weak: Weak) -> Self { UserConfig { auto: AutoConfig::from_weak(weak.clone()), text: TextConfig::from_weak(weak.clone()), - volume: VolumeConfig::from_weak(weak), + volume: VolumeConfig::from_weak(weak.clone()), + character_volume: CharacterVolumeConfig::from_weak(weak), } } } fn load_user_config() -> UserConfig { - let content = fs::read_to_string(format!("{}/user.toml", ENGINE_CONFIG.save_path())).unwrap(); - toml::from_str(&content).unwrap() + let path = format!("{}/user.toml", ENGINE_CONFIG.save_path()); + + match fs::read_to_string(&path) { + Ok(content) => match toml::from_str::(&content) { + Ok(mut config) => { + config.character_volume.fill_missing(); + config + } + Err(_) => { + let config = UserConfig::default(); + let _ = write_config(&path, &config); + config + } + }, + Err(_) => { + let config = UserConfig::default(); + let _ = write_config(&path, &config); + config + } + } } pub fn save_user_config(weak: Weak) -> Result<(), EngineError> { - fs::write( - format!("{}/user.toml", ENGINE_CONFIG.save_path()), - toml::to_string(&UserConfig::from_weak(weak))?, - )?; + let path = format!("{}/user.toml", ENGINE_CONFIG.save_path()); + write_config(&path, &UserConfig::from_weak(weak)) +} +fn write_config(path: &str, config: &UserConfig) -> Result<(), EngineError> { + fs::write(path, toml::to_string(config)?)?; Ok(()) } diff --git a/src/config/volume.rs b/src/config/volume.rs index 98132ee..179a3d6 100644 --- a/src/config/volume.rs +++ b/src/config/volume.rs @@ -11,6 +11,12 @@ pub(crate) struct VolumeConfig { voice: f32, } +impl Default for VolumeConfig { + fn default() -> Self { + VolumeConfig { main: 100.0, bgm: 100.0, voice: 100.0 } + } +} + impl VolumeConfig { pub fn main(&self) -> f32 { self.main diff --git a/src/executor/mod.rs b/src/executor/mod.rs index bb8eb24..917e779 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -75,6 +75,7 @@ pub fn load_data(executor: &mut Executor) -> Result { executor.load_save_data()?; executor.load_volume(); + executor.load_character_volumes(); executor.load_auto(); executor.load_text(); executor.load_extra(); diff --git a/ui/main_window.slint b/ui/main_window.slint index 8a403da..5708e99 100644 --- a/ui/main_window.slint +++ b/ui/main_window.slint @@ -5,6 +5,11 @@ import { ExtraView } from "components/extra.slint"; import { SettingsView } from "components/main_config.slint"; import { StoryView } from "components/story.slint"; +struct CharacterVolume { + name: string, + volume: float, +} + export component MainWindow inherits Window { min-width: 1280px; min-height: 720px; @@ -66,6 +71,7 @@ export component MainWindow inherits Window { in-out property main-volume; in-out property bgm-volume; in-out property voice-volume; + in-out property <[CharacterVolume]> character_volumes: []; // 计算容器尺寸 property container-size: min(self.width, self.height * 16 / 9); From afc53d38c4c252f1ee7efaafd67384b2f6ece2ec Mon Sep 17 00:00:00 2001 From: KirkLee123 <98153606+KirkLee12345@users.noreply.github.com> Date: Mon, 18 May 2026 22:45:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(audio):=20=E5=AE=9E=E7=8E=B0=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E9=9F=B3=E9=87=8F=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(audio): 实现通过配置文件控制角色音量的功能 --- src/executor/executor.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/executor/executor.rs b/src/executor/executor.rs index 93b7e34..829a009 100644 --- a/src/executor/executor.rs +++ b/src/executor/executor.rs @@ -573,9 +573,17 @@ impl Executor { let voice_player = self.voice_player.borrow_mut(); let volume = window.get_main_volume() / 100.0; let voice_volume = window.get_voice_volume() / 100.0; + let character_volumes = window.get_character_volumes(); + let mut character_volume = 100.0; + for character_volume_struct in character_volumes.iter() { + if character_volume_struct.name == name { + character_volume = character_volume_struct.volume / 100.0; + break; + } + } voice_player.play_voice( &format!("{}/{}/{}.ogg", ENGINE_CONFIG.voice_path(), name, voice), - volume * voice_volume, + volume * voice_volume * character_volume, ); duration += length.get(voice).unwrap().clone(); }