From 0b22494751bbd497acc95ed3747ff29e9a470fbb Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 14 Apr 2024 01:15:38 +0200 Subject: [PATCH] fix conversation timings, seek past choices when dropping out of branches --- src/chat.rs | 115 ++++++++++++++++++++++++++++++++-------- src/chats/serenity.yaml | 23 ++++---- 2 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/chat.rs b/src/chat.rs index 95c43f2..5901498 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -21,6 +21,7 @@ pub const TOKEN_GOTO: &str = "goto"; pub const TOKEN_LABEL: &str = "label"; pub const TOKEN_SCRIPT: &str = "script"; pub const TOKEN_SOUND: &str = "sound"; +pub const TOKEN_NOWAIT: &str = "nowait"; pub const TOKEN_GOTO_EXIT: &str = "EXIT"; pub const DEFAULT_SOUND: &str = "chat"; @@ -43,6 +44,12 @@ pub const NON_CHOICE_TOKENS: &[&str] = &[ TOKEN_LABEL, TOKEN_SCRIPT, TOKEN_SOUND, + TOKEN_NOWAIT, +]; +pub const SKIPPABLE_TOKENS: &[&str] = &[ + TOKEN_LABEL, + TOKEN_GOTO, + TOKEN_NOWAIT, ]; pub struct ChatPlugin; @@ -102,7 +109,7 @@ pub enum ChatEvent { DespawnAllChoices, DespawnAllChats, SpawnMessage(String, hud::LogLevel, String), - SpawnChoice(String, usize, ChatPos), + SpawnChoice(String, usize, ChatPos, bool), RunScript(String), Sleep(f64), //Script(String, String, String), @@ -160,19 +167,25 @@ impl ChatDB { // Not acceptable: // - `"What's up?"` // - `{"goto": "foo"}` - fn search_choice(&self, yaml: Option<&Value>) -> Option<(String, Value)> { + // Returns (choice text, sub-conversation branch, nowait flag) + fn search_choice(&self, yaml: Option<&Value>) -> Option<(String, Value, bool)> { let non_choice_tokens = NON_CHOICE_TOKENS.to_vec(); + let mut result: Option<(String, Value, bool)> = None; + let mut nowait = false; if let Some(Value::Mapping(map)) = yaml { - for key in map.keys() { + for (key, value) in map { if let Value::String(key) = key { + if key == TOKEN_NOWAIT && value.as_bool() == Some(true) { + nowait = true; + } if non_choice_tokens.contains(&key.as_str()) { continue; } - return Some((key.into(), map[key].clone())); + result = Some((key.into(), map[key].clone(), nowait)); } } } - return None; + return result; } fn search_label_recursively(&self, sequence: &Value, label: &String, mut pos: ChatPos) -> Option { @@ -226,16 +239,32 @@ impl ChatDB { chat.position[len - 1] += 1; let mut popped = false; + let mut seek_past_dialog_choices = false; while chat.position.len() > 0 { match self.at(chat.id, &chat.position) { None => { chat.position.pop(); popped = true; + seek_past_dialog_choices = true; if chat.position.len() > 0 { let index = chat.position.len() - 1; chat.position[index] += 1; } }, + Some(Value::Mapping(map)) => { + if seek_past_dialog_choices && self.search_choice(Some(&Value::Mapping(map))).is_some() { + // we just dropped out of a branch and ended up in a dialog + // choice. let's seek past all the choices until we find + // the next non-dialog-choice item. + if chat.position.len() > 0 { + let index = chat.position.len() - 1; + chat.position[index] += 1; + } + } + else { + break; + } + } Some(_) => { break; } @@ -264,7 +293,7 @@ impl ChatDB { result = Some(Value::String(value_string.into())); } Some(Value::Mapping(mapping)) => { - if let Some((_choicetext, subconversation)) = self.search_choice(value) { + if let Some((_choicetext, subconversation, _)) = self.search_choice(value) { result = Some(Value::Mapping(mapping.clone())); next_pointer = Some(subconversation); } @@ -289,23 +318,52 @@ impl ChatDB { return result; } - pub fn process_yaml_entry( + // Determines whether the item at the current position is "skippable". + // This means that we should process it right away to get to the correct + // position for finding the choices of a message. + // This includes flow control tokens like "goto", no-op tokens like "label", + // but not something with a side effect like "script", and especially not "if". + fn is_skippable(&self, chat: &mut Chat) -> bool { + let current_item = self.at(chat.id, &chat.position); + if current_item.is_none() { + return false; + } + if let Some(map) = current_item.unwrap().as_mapping() { + for key in map.keys() { + if let Some(key) = key.as_str() { + if !SKIPPABLE_TOKENS.contains(&key) { + return false; + } + } + else { + return false; + } + } + // It's a mapping that contains ONLY keys in SKIPPABLE_TOKENS. + return true; + } + return false; + } + + fn process_yaml_entry( &self, chat: &mut Chat, event: &mut EventWriter, ) -> bool { let current_item = self.at(chat.id, &chat.position); - let mut add_choices = true; + let mut processed_a_choice = false; match current_item { Some(Value::String(message)) => { event.send(ChatEvent::SpawnMessage(message.to_string(), hud::LogLevel::Chat, DEFAULT_SOUND.to_string())); } Some(Value::Mapping(map)) => { + let mut sound = DEFAULT_SOUND.to_string(); + + // Is this a dialog choice? if let Some(_) = self.search_choice(Some(&Value::Mapping(map.clone()))) { - add_choices = false; + processed_a_choice = true; } - let mut sound = "chat".to_string(); // We're going through the list of keys/values multiple times // to ensure that dependencies for certain commands are available @@ -314,8 +372,7 @@ impl ChatDB { for (key, value) in &map { let key = key.as_str(); match (key, value) { - (Some(TOKEN_SET), _) => {} - (Some(TOKEN_IF), _) => {} + (Some(TOKEN_IF), _) => {} // TODO (Some(TOKEN_SOUND), Value::String(sound_name)) => { sound = sound_name.clone(); } @@ -339,6 +396,7 @@ impl ChatDB { event.send(ChatEvent::SpawnMessage( message.to_string(), hud::LogLevel::Warning, sound.clone())); } + (Some(TOKEN_SET), _) => {} // TODO _ => {} } } @@ -380,31 +438,40 @@ impl ChatDB { error!("Can't handle YAML value {current_item:?}"); } } - return add_choices; + return processed_a_choice; } pub fn advance_chat(&self, chat: &mut Chat, event: &mut EventWriter) { event.send(ChatEvent::DespawnAllChoices); // Handle this entry in the chat list - let add_choices = self.process_yaml_entry(chat, event); + let processed_a_choice: bool = self.process_yaml_entry(chat, event); // Move on to next entry - let mut finished_branch = self.advance_pointer(chat); + self.advance_pointer(chat); - // Add choices, if available - if add_choices { + // Add the following choices, unless we ended up in the middle of a dialog + // choice list, and should just skip through to the next non-dialog-choice item + if !processed_a_choice { + // Skip/process some entries right away, to be able to fetch the correct choices + while self.is_skippable(chat) { + self.process_yaml_entry(chat, event); + self.advance_pointer(chat); + } + + // Spawn choices until we reach a non-choice item or the end of the branch let mut key: usize = 0; - while let Some((choice, _)) = + let mut reached_end_of_branch = false; + while let Some((choice, _, nowait)) = self.search_choice(self.at(chat.id, &chat.position).as_ref()) { - if finished_branch { + if reached_end_of_branch { break; } let mut goto: Vec = chat.position.clone(); goto.push(0); - event.send(ChatEvent::SpawnChoice(choice, key, goto)); + event.send(ChatEvent::SpawnChoice(choice, key, goto, nowait)); key += 1; - finished_branch = self.advance_pointer(chat); + reached_end_of_branch = self.advance_pointer(chat); } } } @@ -512,7 +579,7 @@ pub fn handle_chat_events( let sfx = audio::str2sfx(sound); ew_sfx.send(audio::PlaySfxEvent(sfx)); } - ChatEvent::SpawnChoice(replytext, key, goto) => { + ChatEvent::SpawnChoice(replytext, key, goto, nowait) => { commands.spawn(( world::DespawnOnPlayerDeath, Choice { @@ -521,7 +588,9 @@ pub fn handle_chat_events( goto: goto.clone(), } )); - chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64; + if !nowait { + chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64; + } } ChatEvent::RunScript(script) => { ew_chatscript.send(ChatScriptEvent(script.clone())); diff --git a/src/chats/serenity.yaml b/src/chats/serenity.yaml index ea56a93..aed97fd 100644 --- a/src/chats/serenity.yaml +++ b/src/chats/serenity.yaml @@ -31,7 +31,7 @@ - Nevermind. Thank you.: - goto: thx - Leave me alone!: - - goto: END + - goto: EXIT - Micros? What's that?: - Micrometeorites. Those tiny 混蛋 that fly right through you, leaving holes in your suit. And your body. - Ouch! Thank you so much.: @@ -43,19 +43,22 @@ - How are you feeling? - label: howru - I feel quite cozy, this space suit feels like a second skin.: + - set: friends - Hah, it does, doesn't it? - But take care, your suit seems to be leaking. I'd patch it up if I were you. - I'm all out of SuitPatch™ SuperGlue™ right now, otherwise i'd share. - Can I help you with anything else, maybe? - - set: friends - I got this apocalyptic headache...: + - set: friends - Heh, probably related to why you were passed out. - Go easy on yourself, I'm sure things will turn for the better. - Meanwhile, can I help you with anything? - - set: friends - I... don't know, I'm pretty disoriented.: - - Oh no. Do you need a lowdown on reality? - set: friends + - Oh no. Do you need a lowdown on reality? +- I just want to be alone right now: + - Oh, sure. Ping me if you need anything. I'll go back to playing my VR game. + - goto: EXIT - label: help @@ -99,9 +102,8 @@ - goto: help - Well, anyway, need anything else? - goto: help -- I just want to be alone right now: - - Oh, sure. Ping me if you need anything. I'll go back to playing my VR game. - - goto: EXIT +- I think I'm good for now: + - goto: chocolate - Well, I hope you're ok. @@ -116,7 +118,6 @@ - label: pizzaplace - Oh and make sure to check out the pizza place! - Will do, bye!: -- system: Disconnected. --- @@ -141,13 +142,15 @@ - Hah, beautiful, right? I carved it out this asteroid myself! - You know how much work it was to neutralize the rotation of the asteroid, so my valued customers don't bang against the walls while drinking my pizza? - Now would you like today's special or not? + - goto: offer - My head hurts, my suit leaks, I think I'm dying...: - Seriously? Let me have a look. Just press the 'Grant Access' button please. - "[GRANT ACCESS TO SPACESUIT WIFI]": - label: hack - warn: MALWARE DETECTED - warn: BITCOIN MINER DETECTED - - Hey, what are you doing with me?: + - nowait: true + Hey, what are you doing with me?: - Just checking your systems, hang on tight - Yeah, suit's fucked, I'd look out for a repair shop - Anyway, wanna order today's special? @@ -155,7 +158,7 @@ - "[DENY ACCESS TO SPACESUIT WIFI]": - Oh come on, do you want my help or not? - "[GRANT ACCESS TO SPACESUIT WIFI]": - - goto hack + - goto: hack - "[DENY ACCESS TO SPACESUIT WIFI]": - Great, the first customer in ages, and they're brain damaged... - Fuck off!: