implement conditions on choices

main
hut 2024-04-14 20:44:29 +02:00
parent d21f8b4b09
commit 2e57f911ed
1 changed files with 71 additions and 32 deletions

View File

@ -117,13 +117,29 @@ pub enum ChatEvent {
DespawnAllChoices,
DespawnAllChats,
SpawnMessage(String, hud::LogLevel, String),
SpawnChoice(String, usize, ChatPos, bool),
SpawnChoice(String, usize, ChatPos, bool, Option<String>),
RunScript(String),
SleepSeconds(f64),
SetVariable(String),
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.
#[derive(Resource)]
pub struct ChatDB(Vec<Value>);
@ -229,28 +245,38 @@ impl ChatDB {
// Not acceptable:
// - `"What's up?"`
// - `{"goto": "foo"}`
// Returns (choice text, sub-conversation branch, nowait flag)
fn search_choice(&self, yaml: Option<&Value>) -> Option<(String, Value, bool)> {
fn is_choice(&self, yaml: Option<&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 mut result: Option<(String, Value, bool)> = None;
let mut nowait = false;
if let Some(Value::Mapping(map)) = yaml {
let mut result: Extracted = Extracted::default();
for (key, value) in map {
if let Value::String(key) = key {
if key == TOKEN_NOWAIT && value.as_bool() == Some(true) {
nowait = true;
result.nowait = true;
}
if non_choice_tokens.contains(&key.as_str()) {
continue;
else if key == TOKEN_IF {
if let Some(condition) = value.as_str() {
result.condition = Some(condition.to_string());
}
}
if key.as_str().starts_with(TOKEN_IF_INLINE) {
continue;
else if non_choice_tokens.contains(&key.as_str()) {
// skip over the other non-choice tokens
}
else {
result.choice_text = Some(key.to_string());
}
result = Some((key.into(), map[key].clone(), nowait));
}
}
return Some(result);
}
return result;
return None;
}
fn search_label_recursively(&self, sequence: &Value, label: &String, mut pos: ChatPos) -> Option<ChatPos> {
@ -317,7 +343,7 @@ impl ChatDB {
}
},
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
// choice. let's seek past all the choices until we find
// the next non-dialog-choice item.
@ -426,7 +452,7 @@ impl ChatDB {
let mut sound = DEFAULT_SOUND.to_string();
// 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;
}
@ -542,16 +568,21 @@ impl ChatDB {
// Spawn choices until we reach a non-choice item or the end of the branch
let mut key: usize = 0;
let mut reached_end_of_branch = false;
while let Some((choice, _, nowait)) =
self.search_choice(self.at(chat.internal_id, &chat.position).as_ref()) {
if reached_end_of_branch {
while let Some(data) = self.extract(self.at(chat.internal_id, &chat.position).as_ref()) {
if let Some(choice_text) = data.choice_text {
if reached_end_of_branch {
break;
}
let mut goto: Vec<usize> = chat.position.clone();
goto.push(0);
event.send(ChatEvent::SpawnChoice(choice_text,
key, goto, data.nowait, data.condition));
key += 1;
reached_end_of_branch = self.advance_pointer(chat);
}
else {
break;
}
let mut goto: Vec<usize> = chat.position.clone();
goto.push(0);
event.send(ChatEvent::SpawnChoice(choice, key, goto, nowait));
key += 1;
reached_end_of_branch = self.advance_pointer(chat);
}
}
}
@ -661,17 +692,25 @@ pub fn handle_chat_events(
let sfx = audio::str2sfx(sound);
ew_sfx.send(audio::PlaySfxEvent(sfx));
}
ChatEvent::SpawnChoice(replytext, key, goto, nowait) => {
commands.spawn((
world::DespawnOnPlayerDeath,
Choice {
text: replytext.into(),
key: *key,
goto: goto.clone(),
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((
world::DespawnOnPlayerDeath,
Choice {
text: replytext.into(),
key: *key,
goto: goto.clone(),
}
));
if !nowait {
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) => {