diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5b464b..9c33b7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@1.92.0 with: components: rustfmt @@ -37,7 +37,7 @@ jobs: - uses: actions/checkout@v4 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@1.92.0 with: components: clippy diff --git a/savedata/data.toml b/savedata/data.toml index 9078489..dbab7c1 100644 --- a/savedata/data.toml +++ b/savedata/data.toml @@ -2,6 +2,6 @@ cg = [1022] [read] ky02 = 11 -ky03 = 3 ky04 = 1 +ky03 = 7 ky01 = 4 diff --git a/savedata/user.toml b/savedata/user.toml index 22b98e4..2c9b4f7 100644 --- a/savedata/user.toml +++ b/savedata/user.toml @@ -15,5 +15,5 @@ bgm = 100.0 voice = 100.0 [character_volume] -"美倉礼良" = 100.0 "聖莉々子" = 100.0 +"美倉礼良" = 100.0 diff --git a/src/config/save_load.rs b/src/config/save_load.rs index a0afa78..d58bc54 100644 --- a/src/config/save_load.rs +++ b/src/config/save_load.rs @@ -15,10 +15,16 @@ pub(crate) struct SaveData { } #[derive(Debug, Deserialize, Serialize)] -struct SaveDataWrapper { +pub(crate) struct SaveDataWrapper { save_data: Vec, } +impl SaveDataWrapper { + pub(crate) fn new(save_data: Vec) -> SaveDataWrapper { + SaveDataWrapper { save_data } + } +} + impl SaveData { pub(crate) fn new( script: String, diff --git a/src/executors/executor.rs b/src/executors/executor.rs index 921002e..3a989b5 100644 --- a/src/executors/executor.rs +++ b/src/executors/executor.rs @@ -1,7 +1,7 @@ use crate::config::cg::get_cg; use crate::config::{ - cg::CG_CONFIG, figure::FIGURE_CONFIG, save_load::SaveData, user::save_user_config, - voice::VOICE_LENGTH, ENGINE_CONFIG, + cg::CG_CONFIG, figure::FIGURE_CONFIG, save_load::SaveData, save_load::SaveDataWrapper, + user::save_user_config, voice::VOICE_LENGTH, ENGINE_CONFIG, }; use crate::data::UserData; use crate::error::{EngineError, SaveError}; @@ -171,12 +171,12 @@ impl Executor { } pub(crate) fn can_skip(&self) -> bool { - let scr = self.script.borrow(); if let Some(window) = self.weak.upgrade() { if window.get_skip_conf() { return true; } } + let scr = self.script.borrow(); scr.read_block() > scr.index() } @@ -224,6 +224,19 @@ impl Executor { Ok(()) } + pub(crate) fn execute_quick_load(&mut self) -> Result<(), EngineError> { + let load_data = match self.weak.upgrade() { + Some(window) => window.get_save_items().row_data(9).unwrap().row_data(15), + None => None, + }; + + if let Some(data) = load_data { + self.execute_load(data.name.to_string(), data.index)? + } + + Ok(()) + } + pub(crate) fn execute_save(&mut self, index: i32, page_num: i32) -> Result<(), EngineError> { if let Some(window) = self.weak.upgrade() { let script = self.script.borrow(); @@ -239,19 +252,29 @@ impl Executor { name: SharedString::from(script.name()), }, ); - exists_save_items.set_row_data(page_num as usize, save_page); + exists_save_items.set_row_data(page_num as usize, save_page.clone()); + let save_data = save_page + .iter() + .map(|item| { + SaveData::new( + item.name.to_string(), + item.index as usize, + item.explain.to_string(), + item.bg + .path() + .and_then(|p| p.to_str().map(|s| s.to_string())) + .unwrap_or_default(), + ) + }) + .collect::>(); + let path = format!("{}{}.toml", ENGINE_CONFIG.save_path(), page_num); fs::write( - format!("{}{}.toml", ENGINE_CONFIG.save_path(), index), - toml::to_string_pretty(&SaveData::new( - script.name().to_string(), - script.index(), - script.explain().to_string(), - bg.0.path().unwrap().to_str().unwrap().to_string(), - )) - .map_err(SaveError::from)?, + &path, + toml::to_string_pretty(&SaveDataWrapper::new(save_data)) + .map_err(SaveError::from)?, ) .map_err(|e| SaveError::Write { - path: format!("{}{}.toml", ENGINE_CONFIG.save_path(), index), + path: path.clone(), source: e, })?; window.set_save_items(exists_save_items); @@ -536,9 +559,7 @@ impl Executor { let mut duration = Duration::from_secs(0); if let Some(window) = self.weak.upgrade() { - let mut scr = self.script.borrow_mut(); - let (pre_bg, pre_bgm, pre_figures) = scr.pre_items(); - drop(scr); + let (pre_bg, pre_bgm, pre_figures) = self.script.borrow_mut().pre_items(); if let Some(bg) = pre_bg { self.show_bg(&bg)?; diff --git a/src/ui/initialize.rs b/src/ui/initialize.rs index 9932f06..613fa0d 100644 --- a/src/ui/initialize.rs +++ b/src/ui/initialize.rs @@ -25,6 +25,20 @@ pub(crate) async fn ui() -> Result<(), EngineError> { } }); + window.on_quick_save({ + let mut executor = executor.clone(); + move || { + executor.execute_save(15, 9).expect("Quick save panicked"); + } + }); + + window.on_quick_load({ + let mut executor = executor.clone(); + move || { + executor.execute_quick_load().expect("Quick load panicked"); + } + }); + window.on_save({ let mut executor = executor.clone(); move |index, page_num| { @@ -194,16 +208,10 @@ pub(crate) async fn ui() -> Result<(), EngineError> { window.on_exit({ let weak = executor.get_weak(); move || { - slint::spawn_local({ - let weak = weak.clone(); - async move { - if let Some(window) = weak.upgrade() { - let _ = window.hide(); - } - let _ = slint::quit_event_loop(); - } - }) - .expect("Exit panicked"); + if let Some(window) = weak.upgrade() { + let _ = window.hide(); + } + let _ = slint::quit_event_loop(); } }); diff --git a/ui/components/story.slint b/ui/components/story.slint index 8b20197..f0728ac 100644 --- a/ui/components/story.slint +++ b/ui/components/story.slint @@ -56,6 +56,9 @@ export component StoryView { callback choose(string); callback settings(); callback replay_voice(); + + callback quick-save(); + callback quick-load(); callback save-game(); callback load-game(); callback stop-video(); @@ -252,12 +255,30 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.55 - self.width; + x: parent.width * 0.43 - self.width; y: 0; text: "语音"; clicked => { root.replay-voice(); } } + CustomRoundButton { + width: parent.height; + height: parent.height; + x: parent.width * 0.49 - self.width; + y: 0; + text: "快存"; + clicked => { root.quick-save(); } + } + + CustomRoundButton { + width: parent.height; + height: parent.height; + x: parent.width * 0.55 - self.width; + y: 0; + text: "快读"; + clicked => { root.quick-load(); } + } + CustomRoundButton { width: parent.height; height: parent.height; diff --git a/ui/main_window.slint b/ui/main_window.slint index 473d15a..99295b0 100644 --- a/ui/main_window.slint +++ b/ui/main_window.slint @@ -12,7 +12,7 @@ export component MainWindow inherits Window { min-height: 720px; title: "RustEng"; - // 所有原有的属性保持不变 + // Story in property dialogue-1; in property dialogue-2; in property dialogue-3; @@ -21,18 +21,23 @@ export component MainWindow inherits Window { in property<[{ index: int, text: string }]> choose-branch; in property <{ img: image, x_offset: float, y_offset: float, zoom: float }> bg; in property <[FigureItem]> figure-items; + in property current-choose: 0; + in property video-frame; + + // Story states in property is-fullscreen: false; in-out property is_backlog: false; in-out property is-auto; - in-out property is-wait; - in-out property skip-conf; - in-out property delay; - in-out property text-speed; in-out property is-skip; - in property current-choose: 0; in-out property is-video: false; - in property video-frame; + // System + in-out property delay; + in-out property is-wait; + in-out property skip-conf; + + // Text + in-out property text-speed; in-out property dialogue-opacity; in-out property is-bold; in-out property show-shadow; @@ -40,12 +45,15 @@ export component MainWindow inherits Window { in-out property is-read; in property <[string]> font-list; + // Screen in-out property current-screen: 0; in-out property last-screen: 0; + // Save & Load in property <[[SaveItem]]> save-items; in property <[[ExItem]]> ex-items; + // Volume in-out property main-volume; in-out property bgm-volume; in-out property voice-volume; @@ -136,13 +144,19 @@ export component MainWindow inherits Window { container-width: parent.width; container-height: parent.height; is-fullscreen: root.is-fullscreen; + + // Volume main-volume <=> root.main-volume; bgm-volume <=> root.bgm-volume; voice-volume <=> root.voice-volume; character_volumes <=> root.character_volumes; + + // System is-wait <=> root.is-wait; skip-conf <=> root.skip-conf; delay <=> root.delay; + + // Text dialogue-opacity <=> root.dialogue-opacity; is-bold <=> root.is-bold; show-shadow <=> root.show-shadow; @@ -171,6 +185,8 @@ export component MainWindow inherits Window { bg: root.bg; current-choose: root.current-choose; figure-items: root.figure-items; + + // Text dialogue-opacity: root.dialogue-opacity; is-bold: root.is-bold; show-shadow: root.show-shadow; @@ -196,6 +212,8 @@ export component MainWindow inherits Window { clicked => { root.clicked(); } choose(text) => { root.choose(text); } replay-voice => { root.replay-voice(); } + quick-save() => { root.quick-save(); } + quick-load() => { root.quick-load(); } save-game => { esc_story(3); root.normal-play(); @@ -257,6 +275,8 @@ export component MainWindow inherits Window { callback replay-voice(); callback save(int, int); callback load(string, int); + callback quick-save(); + callback quick-load(); callback get-ex(); callback exit(); callback stop-video();