From ec4723c4cf99879173534cd1201cde94e9f7eb81 Mon Sep 17 00:00:00 2001 From: nanase <1925792291@qq.com> Date: Thu, 30 Jul 2026 11:54:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E5=88=B0=E9=80=89=E6=8B=A9=E5=8F=AA=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- savedata/data.toml | 3 +- savedata/user.toml | 2 +- src/executors/executor.rs | 92 ++++++++++++++++++------------------- src/parser/script_parser.rs | 8 ++-- src/script.rs | 33 +++++++++++-- src/ui/initialize.rs | 13 ++++-- ui/components/story.slint | 21 +++++++-- ui/main_window.slint | 2 + 8 files changed, 109 insertions(+), 65 deletions(-) diff --git a/savedata/data.toml b/savedata/data.toml index e108565..9078489 100644 --- a/savedata/data.toml +++ b/savedata/data.toml @@ -2,5 +2,6 @@ cg = [1022] [read] ky02 = 11 -ky01 = 3 ky03 = 3 +ky04 = 1 +ky01 = 4 diff --git a/savedata/user.toml b/savedata/user.toml index 2c9b4f7..22b98e4 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/executors/executor.rs b/src/executors/executor.rs index 0041374..87c0e61 100644 --- a/src/executors/executor.rs +++ b/src/executors/executor.rs @@ -128,12 +128,10 @@ impl Executor { self.save_tx = Some(save_tx); } - /// 读取某剧本已记录的已读进度(不存在则为 0) fn read_of(&self, name: &str) -> usize { self.user_data.borrow().read.get(name).copied().unwrap_or(0) } - /// 向异步保存 task 推送最新快照(非阻塞) fn request_save(&self) { if let Some(tx) = &self.save_tx { let _ = tx.send(Some(self.user_data.borrow().clone())); @@ -265,6 +263,7 @@ impl Executor { pub(crate) fn execute_load(&mut self, name: String, index: i32) -> Result<(), EngineError> { if !name.is_empty() { let weak = self.weak.clone(); + *self.choose_lock.borrow_mut() = false; if let Some(window) = weak.upgrade() { window.set_current_screen(2); window.set_current_choose(0); @@ -346,32 +345,29 @@ impl Executor { } pub(crate) fn execute_jump(&mut self, label: Jump) -> Result<(), EngineError> { - { - let mut script = self.script.borrow_mut(); - let backlog = script.to_owned().take_backlog(); - let jump_index = match label { - Jump::Label((name, label)) => { - if name != script.name() { - let mut scr = Parser::load(&name)?; - scr.set_backlog(backlog); - scr.set_read_block(self.read_of(&name)); - *script = scr; - } - script.find_label(&label).copied() + let mut script = self.script.borrow_mut(); + let backlog = script.to_owned().take_backlog(); + let jump_index = match label { + Jump::Label((name, label)) => { + if name != script.name() { + let mut scr = Parser::load(&name)?; + scr.set_backlog(backlog); + scr.set_read_block(self.read_of(&name)); + *script = scr; } - Jump::Index((name, index)) => { - if name != script.name() { - let mut scr = Parser::load(&name)?; - scr.set_backlog(backlog); - scr.set_read_block(self.read_of(&name)); - *script = scr; - } - Some(index as usize) + script.find_label(&label).copied() + } + Jump::Index((name, index)) => { + if name != script.name() { + let mut scr = Parser::load(&name)?; + scr.set_backlog(backlog); + scr.set_read_block(self.read_of(&name)); + *script = scr; } - }; - - script.set_pre_items(jump_index); - } + Some(index as usize) + } + }; + script.set_pre_items(jump_index); self.clean_fg("All")?; Ok(()) @@ -434,15 +430,22 @@ impl Executor { Ok(()) } + pub(crate) fn execute_skip_choice(&mut self) -> Result<(), EngineError> { + let choice = self.script.borrow().find_next_choice(); + + if let Some(choice) = choice { + self.execute_jump(Jump::Index(choice))?; + self.execute_script() + } else { + Ok(()) + } + } + pub(crate) fn execute_script(&mut self) -> Result<(), EngineError> { - { - let scr = self.script.clone(); - let scr = scr.borrow(); - if scr.in_clear() { - self.delay_channels.as_ref().unwrap().clear_all(); - } else { - self.delay_channels.as_ref().unwrap().skip_all(); - } + if self.script.borrow().in_clear() { + self.delay_channels.as_ref().unwrap().clear_all(); + } else { + self.delay_channels.as_ref().unwrap().skip_all(); } let res = { @@ -466,14 +469,10 @@ impl Executor { return Ok(()); } - let mut duration = Duration::default(); - let mut is_wait = true; - let mut is_auto = false; - if let Some(window) = self.weak.upgrade() { - duration += Duration::from_millis((window.get_delay() * 1000.0) as u64); - is_wait = window.get_is_wait(); - is_auto = window.get_is_auto(); - } + let (is_wait, is_auto, mut duration) = match self.weak.upgrade() { + Some(window) => (window.get_is_wait(), window.get_is_auto(), Duration::from_millis((window.get_delay() * 1000.0) as u64)), + None => (true, false, Duration::default()), + }; if *self.choose_lock.borrow() { return Ok(()); @@ -483,14 +482,15 @@ impl Executor { return Ok(()); } - let mut commands = Commands::EmptyCmd; - { + let commands = { let scr = self.script.clone(); let mut scr = scr.borrow_mut(); if let Some(cmds) = scr.next_command() { - commands = cmds.clone(); + cmds.clone() + } else { + Commands::EmptyCmd } - } + }; let delay = match commands { Commands::EmptyCmd => unreachable!(), Commands::OneCmd(command) => self.apply_command(command)?, diff --git a/src/parser/script_parser.rs b/src/parser/script_parser.rs index ef13947..413cfb2 100644 --- a/src/parser/script_parser.rs +++ b/src/parser/script_parser.rs @@ -171,7 +171,7 @@ impl Parser { } "choose" => { let num = arg.parse::().map_err(ScriptError::from)?; - let mut choose_branch = HashMap::with_capacity(num); + let mut choices_branch = HashMap::with_capacity(num); let explain = lines[index + 1].1.clone(); for (i, line) in lines.iter().take(index + num + 1 + 1).skip(index + 2) { @@ -199,15 +199,15 @@ impl Parser { ), _ => unreachable!(), }; - choose_branch.insert(choice.clone(), label.clone()); - self.script.insert_choice(choice, label); + choices_branch.insert(choice.clone(), label.clone()); } else { return Err(EngineError::from(ScriptError::Choice(format!( "Invalid choice at line {i}: {line}" )))); } } - block_commands.push(Choice((explain, choose_branch))); + self.script.insert_choice(self.block_index, choices_branch.clone()); + block_commands.push(Choice((explain, choices_branch))); break; } "voice" => { diff --git a/src/script.rs b/src/script.rs index 5f58a26..8c6acdb 100644 --- a/src/script.rs +++ b/src/script.rs @@ -19,6 +19,7 @@ pub(crate) struct Timeline { bgm: BTreeMap, backgrounds: BTreeMap, figures: BTreeMap, + choices: BTreeMap> } impl Timeline { @@ -30,6 +31,22 @@ impl Timeline { self.backgrounds.insert(index, command); } + fn insert_choices(&mut self, index: usize, choices: HashMap) { + self.choices.insert(index, choices); + } + + fn get_choice_label(&self, index: usize, choice: &str) -> Option<&Label> { + if let Some(choices) = self.choices.get(&index) { + choices.get(choice) + } else { + None + } + } + + fn find_next_choice(&self, index: usize) -> Option<&usize> { + self.choices.range(index..).next().map(|(i, _)| i) + } + fn update_figures(&mut self, index: usize, distance: &str, position: &str, command: Command) { self.figures .entry(index) @@ -96,7 +113,6 @@ pub(crate) struct Script { pre_voice: Option<(SharedString, SharedString)>, timeline: Timeline, clear: HashSet, - choices: HashMap, labels: HashMap, pre_items: PreItems, } @@ -115,7 +131,6 @@ impl Script { pre_voice: None, timeline: Timeline::default(), clear: HashSet::new(), - choices: HashMap::new(), labels: HashMap::new(), pre_items: PreItems::default(), } @@ -199,8 +214,8 @@ impl Script { self.timeline.insert_bgm(index, bgm); } - pub(crate) fn insert_choice(&mut self, choice: String, label: Label) { - self.choices.insert(choice, label); + pub(crate) fn insert_choice(&mut self, index: usize, choices: HashMap) { + self.timeline.insert_choices(index, choices); } pub(crate) fn insert_clear(&mut self, index: usize) { @@ -281,12 +296,20 @@ impl Script { (pre_items.pre_bg, pre_items.pre_bgm, pre_items.pre_figures) } + pub(crate) fn find_next_choice(&self) -> Option<(String, i32)> { + if let Some(&index) = self.timeline.find_next_choice(self.current_block) { + Some((self.name.to_string(), index as i32)) + } else { + None + } + } + pub(crate) fn find_label(&self, name: &str) -> Option<&usize> { self.labels.get(name) } pub(crate) fn get_choice_label(&self, name: &str) -> Option<&Label> { - self.choices.get(name) + self.timeline.get_choice_label(self.current_block - 1, name) } pub(crate) fn change_figure( diff --git a/src/ui/initialize.rs b/src/ui/initialize.rs index ac481c7..bf3daba 100644 --- a/src/ui/initialize.rs +++ b/src/ui/initialize.rs @@ -147,7 +147,7 @@ pub(crate) async fn ui() -> Result<(), EngineError> { move |symbol| { executor .execute_status(Status::Auto, symbol) - .expect("TODO: panic message"); + .expect("Auto play panicked"); } }); @@ -156,16 +156,23 @@ pub(crate) async fn ui() -> Result<(), EngineError> { move |symbol| { executor .execute_status(Status::Skip, symbol) - .expect("TODO: panic message"); + .expect("Skip play panicked"); } }); + window.on_skip_choice({ + let mut executor = executor.clone(); + move || { + executor.execute_skip_choice().expect("Skip choice panicked"); + } + }); + window.on_normal_play({ let mut executor = executor.clone(); move || { executor .execute_status(Status::Normal, false) - .expect("TODO: panic message"); + .expect("Normal play panicked"); } }); diff --git a/ui/components/story.slint b/ui/components/story.slint index 8fd6d90..8b20197 100644 --- a/ui/components/story.slint +++ b/ui/components/story.slint @@ -47,6 +47,7 @@ export component StoryView { callback auto-play(bool); callback skip-play(bool); + callback skip-choice(); callback backlog(); callback backlog-change(int); callback backlog-jump(string, int); @@ -251,7 +252,7 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.61 - self.width; + x: parent.width * 0.55 - self.width; y: 0; text: "语音"; clicked => { root.replay-voice(); } @@ -260,7 +261,7 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.67 - self.width; + x: parent.width * 0.61 - self.width; y: 0; text: "存档"; clicked => { root.save-game(); } @@ -269,7 +270,7 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.73 - self.width; + x: parent.width * 0.67 - self.width; y: 0; text: "读档"; clicked => { root.load-game(); } @@ -278,7 +279,7 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.79 - self.width; + x: parent.width * 0.73 - self.width; y: 0; text: "自动"; is-on: is-auto; @@ -288,13 +289,23 @@ export component StoryView { CustomRoundButton { width: parent.height; height: parent.height; - x: parent.width * 0.85 - self.width; + x: parent.width * 0.79 - self.width; y: 0; text: "快进"; is-on: is-skip; clicked => { root.skip-play(is-skip); } } + CustomRoundButton { + width: parent.height; + height: parent.height; + x: parent.width * 0.85 - self.width; + y: 0; + text: "选择"; + is-on: is-skip; + clicked => { root.skip-choice(); } + } + CustomRoundButton { width: parent.height; height: parent.height; diff --git a/ui/main_window.slint b/ui/main_window.slint index 1a6287c..473d15a 100644 --- a/ui/main_window.slint +++ b/ui/main_window.slint @@ -188,6 +188,7 @@ export component MainWindow inherits Window { auto-play(b) => { root.auto-play(b); } skip-play(b) => { root.skip-play(b); } + skip-choice => { root.skip-choice(); } backlog => { root.backlog(); } backlog-change(i) => { root.backlog-change(i); } backlog-jump(s, i) => { root.backlog-jump(s, i); } @@ -240,6 +241,7 @@ export component MainWindow inherits Window { callback auto-play(bool); callback skip-play(bool); + callback skip-choice(); callback normal-play(); callback clicked(); callback toggle-fullscreen(); From fbb8310cc9d6fe9d56a657f691b870961c32e754 Mon Sep 17 00:00:00 2001 From: nanase <1925792291@qq.com> Date: Thu, 30 Jul 2026 11:58:21 +0800 Subject: [PATCH 2/2] fmt --- src/executors/executor.rs | 8 ++++++-- src/parser/script_parser.rs | 3 ++- src/script.rs | 2 +- src/ui/initialize.rs | 10 ++++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/executors/executor.rs b/src/executors/executor.rs index 87c0e61..921002e 100644 --- a/src/executors/executor.rs +++ b/src/executors/executor.rs @@ -469,8 +469,12 @@ impl Executor { return Ok(()); } - let (is_wait, is_auto, mut duration) = match self.weak.upgrade() { - Some(window) => (window.get_is_wait(), window.get_is_auto(), Duration::from_millis((window.get_delay() * 1000.0) as u64)), + let (is_wait, is_auto, mut duration) = match self.weak.upgrade() { + Some(window) => ( + window.get_is_wait(), + window.get_is_auto(), + Duration::from_millis((window.get_delay() * 1000.0) as u64), + ), None => (true, false, Duration::default()), }; diff --git a/src/parser/script_parser.rs b/src/parser/script_parser.rs index 413cfb2..991f337 100644 --- a/src/parser/script_parser.rs +++ b/src/parser/script_parser.rs @@ -206,7 +206,8 @@ impl Parser { )))); } } - self.script.insert_choice(self.block_index, choices_branch.clone()); + self.script + .insert_choice(self.block_index, choices_branch.clone()); block_commands.push(Choice((explain, choices_branch))); break; } diff --git a/src/script.rs b/src/script.rs index 8c6acdb..7d4abe6 100644 --- a/src/script.rs +++ b/src/script.rs @@ -19,7 +19,7 @@ pub(crate) struct Timeline { bgm: BTreeMap, backgrounds: BTreeMap, figures: BTreeMap, - choices: BTreeMap> + choices: BTreeMap>, } impl Timeline { diff --git a/src/ui/initialize.rs b/src/ui/initialize.rs index bf3daba..9932f06 100644 --- a/src/ui/initialize.rs +++ b/src/ui/initialize.rs @@ -161,10 +161,12 @@ pub(crate) async fn ui() -> Result<(), EngineError> { }); window.on_skip_choice({ - let mut executor = executor.clone(); - move || { - executor.execute_skip_choice().expect("Skip choice panicked"); - } + let mut executor = executor.clone(); + move || { + executor + .execute_skip_choice() + .expect("Skip choice panicked"); + } }); window.on_normal_play({