implement conditions on choices

This commit is contained in:
yuni 2024-04-14 20:44:29 +02:00
parent d21f8b4b09
commit 2e57f911ed

View file

@ -117,13 +117,29 @@ pub enum ChatEvent {
DespawnAllChoices, DespawnAllChoices,
DespawnAllChats, DespawnAllChats,
SpawnMessage(String, hud::LogLevel, String), SpawnMessage(String, hud::LogLevel, String),
SpawnChoice(String, usize, ChatPos, bool), SpawnChoice(String, usize, ChatPos, bool, Option<String>),
RunScript(String), RunScript(String),
SleepSeconds(f64), SleepSeconds(f64),
SetVariable(String), SetVariable(String),
GotoIf(String, ChatPos), GotoIf(String, ChatPos),
} }
pub struct Extracted {
choice_text: Option<String>,
nowait: bool,
condition: Option<String>,
}
impl Default for Extracted {
fn default() -> Self {
Self {
choice_text: None,
nowait: false,
condition: None,
}
}
}
// This is the only place where any YAML interaction should be happening. // This is the only place where any YAML interaction should be happening.
#[derive(Resource)] #[derive(Resource)]
pub struct ChatDB(Vec<Value>); pub struct ChatDB(Vec<Value>);
@ -229,28 +245,38 @@ impl ChatDB {
// Not acceptable: // Not acceptable:
// - `"What's up?"` // - `"What's up?"`
// - `{"goto": "foo"}` // - `{"goto": "foo"}`
// Returns (choice text, sub-conversation branch, nowait flag) fn is_choice(&self, yaml: Option<&Value>) -> bool {
fn search_choice(&self, yaml: Option<&Value>) -> Option<(String, Value, bool)> { if let Some(data) = self.extract(yaml) {
return data.choice_text.is_some();
}
return false;
}
fn extract(&self, yaml: Option<&Value>) -> Option<Extracted> {
let non_choice_tokens = NON_CHOICE_TOKENS.to_vec(); 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 { if let Some(Value::Mapping(map)) = yaml {
let mut result: Extracted = Extracted::default();
for (key, value) in map { for (key, value) in map {
if let Value::String(key) = key { if let Value::String(key) = key {
if key == TOKEN_NOWAIT && value.as_bool() == Some(true) { if key == TOKEN_NOWAIT && value.as_bool() == Some(true) {
nowait = true; result.nowait = true;
} }
if non_choice_tokens.contains(&key.as_str()) { else if key == TOKEN_IF {
continue; if let Some(condition) = value.as_str() {
result.condition = Some(condition.to_string());
} }
if key.as_str().starts_with(TOKEN_IF_INLINE) {
continue;
} }
result = Some((key.into(), map[key].clone(), nowait)); else if non_choice_tokens.contains(&key.as_str()) {
// skip over the other non-choice tokens
}
else {
result.choice_text = Some(key.to_string());
} }
} }
} }
return result; return Some(result);
}
return None;
} }
fn search_label_recursively(&self, sequence: &Value, label: &String, mut pos: ChatPos) -> Option<ChatPos> { fn search_label_recursively(&self, sequence: &Value, label: &String, mut pos: ChatPos) -> Option<ChatPos> {
@ -317,7 +343,7 @@ impl ChatDB {
} }
}, },
Some(Value::Mapping(map)) => { Some(Value::Mapping(map)) => {
if seek_past_dialog_choices && self.search_choice(Some(&Value::Mapping(map))).is_some() { if seek_past_dialog_choices && self.is_choice(Some(&Value::Mapping(map))) {
// we just dropped out of a branch and ended up in a dialog // we just dropped out of a branch and ended up in a dialog
// choice. let's seek past all the choices until we find // choice. let's seek past all the choices until we find
// the next non-dialog-choice item. // the next non-dialog-choice item.
@ -426,7 +452,7 @@ impl ChatDB {
let mut sound = DEFAULT_SOUND.to_string(); let mut sound = DEFAULT_SOUND.to_string();
// Is this a dialog choice? // Is this a dialog choice?
if let Some(_) = self.search_choice(Some(&Value::Mapping(map.clone()))) { if self.is_choice(Some(&Value::Mapping(map.clone()))) {
processed_a_choice = true; processed_a_choice = true;
} }
@ -542,17 +568,22 @@ impl ChatDB {
// Spawn choices until we reach a non-choice item or the end of the branch // Spawn choices until we reach a non-choice item or the end of the branch
let mut key: usize = 0; let mut key: usize = 0;
let mut reached_end_of_branch = false; let mut reached_end_of_branch = false;
while let Some((choice, _, nowait)) = while let Some(data) = self.extract(self.at(chat.internal_id, &chat.position).as_ref()) {
self.search_choice(self.at(chat.internal_id, &chat.position).as_ref()) { if let Some(choice_text) = data.choice_text {
if reached_end_of_branch { if reached_end_of_branch {
break; break;
} }
let mut goto: Vec<usize> = chat.position.clone(); let mut goto: Vec<usize> = chat.position.clone();
goto.push(0); goto.push(0);
event.send(ChatEvent::SpawnChoice(choice, key, goto, nowait)); event.send(ChatEvent::SpawnChoice(choice_text,
key, goto, data.nowait, data.condition));
key += 1; key += 1;
reached_end_of_branch = self.advance_pointer(chat); reached_end_of_branch = self.advance_pointer(chat);
} }
else {
break;
}
}
} }
} }
} }
@ -661,7 +692,14 @@ pub fn handle_chat_events(
let sfx = audio::str2sfx(sound); let sfx = audio::str2sfx(sound);
ew_sfx.send(audio::PlaySfxEvent(sfx)); ew_sfx.send(audio::PlaySfxEvent(sfx));
} }
ChatEvent::SpawnChoice(replytext, key, goto, nowait) => { ChatEvent::SpawnChoice(replytext, key, goto, nowait, condition) => {
'out: {
dbg!(condition);
if let Some(condition) = condition {
if !vars.evaluate_condition(condition, &chat.talker.actor_id) {
break 'out;
}
}
commands.spawn(( commands.spawn((
world::DespawnOnPlayerDeath, world::DespawnOnPlayerDeath,
Choice { Choice {
@ -674,6 +712,7 @@ pub fn handle_chat_events(
chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64; chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64;
} }
} }
}
ChatEvent::RunScript(script) => { ChatEvent::RunScript(script) => {
ew_chatscript.send(ChatScriptEvent(script.clone())); ew_chatscript.send(ChatScriptEvent(script.clone()));
} }