implement choice selection

This commit is contained in:
yuni 2024-04-13 20:23:38 +02:00
parent 8df6914dba
commit e7df698225

View file

@ -113,7 +113,14 @@ impl ChatDB {
return Err(format!("No chat with the conversation ID `{id}` was found.")); return Err(format!("No chat with the conversation ID `{id}` was found."));
} }
fn search_choice(&self, yaml: Option<&Value>) -> Option<String> { // For a given Value, check whether it's a Value::Mapping and whether it
// contains a choice. Mappings that will be detected as choices:
// - `{"What's up?": [...]}`
// - `{"What's up?": [...], "if": "value > 3"}`
// Not acceptable:
// - `"What's up?"`
// - `{"goto": "foo"}`
fn search_choice(&self, yaml: Option<&Value>) -> Option<(String, Value)> {
let non_choice_tokens = NON_CHOICE_TOKENS.to_vec(); let non_choice_tokens = NON_CHOICE_TOKENS.to_vec();
if let Some(Value::Mapping(hash)) = yaml { if let Some(Value::Mapping(hash)) = yaml {
for key in hash.keys() { for key in hash.keys() {
@ -121,7 +128,7 @@ impl ChatDB {
if non_choice_tokens.contains(&key.as_str()) { if non_choice_tokens.contains(&key.as_str()) {
continue; continue;
} }
return Some(key.into()); return Some((key.into(), hash[key].clone()));
} }
} }
} }
@ -130,23 +137,33 @@ impl ChatDB {
// returns false if the advanced pointer is out of bounds // returns false if the advanced pointer is out of bounds
fn advance_pointer(&self, chat: &mut Chat) -> bool { fn advance_pointer(&self, chat: &mut Chat) -> bool {
let index = chat.position.len() - 1; let len = chat.position.len();
chat.position[index] += 1; if len == 0 {
return false;
}
chat.position[len - 1] += 1;
while chat.position.len() > 0 { while chat.position.len() > 0 {
dbg!(&chat.position);
dbg!(self.at(chat.id, &chat.position)); dbg!(self.at(chat.id, &chat.position));
match self.at(chat.id, &chat.position) { match self.at(chat.id, &chat.position) {
None => { None => {
dbg!("Pop."); dbg!("Pop.");
chat.position.pop(); chat.position.pop();
if chat.position.len() > 0 {
let index = chat.position.len() - 1; let index = chat.position.len() - 1;
chat.position[index] += 1; chat.position[index] += 1;
}
}, },
Some(_) => { Some(_) => {
break; break;
} }
} }
} }
if chat.position.len() == 0 {
// out of bounds, return false.
return false;
}
return true; return true;
} }
@ -156,22 +173,47 @@ impl ChatDB {
position[index] += 1; position[index] += 1;
} }
fn at(&self, id: usize, position: &Vec<usize>) -> Option<&Value> { // Returns the Value at the given ID/position, as-is.
// If it's a choice, it returns the entire {"choice text": [...]} mapping.
fn at(&self, id: usize, position: &Vec<usize>) -> Option<Value> {
if position.len() == 0 { if position.len() == 0 {
return None; return None;
} }
let mut pointer: Option<&Value> = Some(&self.0[id]); let mut result: Option<Value> = None;
let mut pointer: Option<Value> = Some(self.0[id].clone());
let mut next_pointer: Option<Value> = None;
for index in position { for index in position {
if let Some(Value::Sequence(seq)) = self.0.get(id) { if let Some(Value::Sequence(seq)) = &pointer {
// TODO: handle mappings let value = seq.get(*index);
if let Some(value) = seq.get(*index) { match value {
pointer = Some(value); Some(Value::String(value_string)) => {
result = Some(Value::String(value_string.into()));
} }
Some(Value::Mapping(mapping)) => {
if let Some((_choicetext, subconversation)) = self.search_choice(value) {
result = Some(Value::Mapping(mapping.clone()));
next_pointer = Some(subconversation);
}
else {
result = Some(Value::Mapping(mapping.clone()));
}
}
None => {
// Out of bounds.
return None;
}
_ => {
error!("Could not handle YAML value {value:?}");
return None;
}
}
pointer = next_pointer;
next_pointer = None;
} else { } else {
return None; return None;
} }
} }
return pointer; return result;
} }
pub fn advance_chat(&self, chat: &mut Chat, event: &mut EventWriter<ChatEvent>) { pub fn advance_chat(&self, chat: &mut Chat, event: &mut EventWriter<ChatEvent>) {
@ -188,7 +230,7 @@ impl ChatDB {
event.send(ChatEvent::DespawnAllChats); event.send(ChatEvent::DespawnAllChats);
return; return;
} }
else if let Some(_) = self.search_choice(self.at(chat.id, &chat.position)) { else if let Some(_) = self.search_choice(self.at(chat.id, &chat.position).as_ref()) {
is_skipping_through = true; is_skipping_through = true;
} }
else if let Some(Value::String(message)) = self.at(chat.id, &chat.position) { else if let Some(Value::String(message)) = self.at(chat.id, &chat.position) {
@ -203,7 +245,11 @@ impl ChatDB {
if is_skipping_through /*|| pos[0] >= conv.len()*/ { // TODO: out of bounds checking if is_skipping_through /*|| pos[0] >= conv.len()*/ { // TODO: out of bounds checking
break; break;
} }
if let Some(choice) = self.search_choice(self.at(chat.id, &pos)) { dbg!(&pos);
dbg!(self.at(chat.id, &pos));
let choice = self.search_choice(self.at(chat.id, &pos).as_ref());
dbg!(&choice);
if let Some((choice, _)) = choice {
let mut goto: Vec<usize> = pos.clone(); let mut goto: Vec<usize> = pos.clone();
goto.push(0); goto.push(0);
event.send(ChatEvent::SpawnChoice(choice, key, goto)); event.send(ChatEvent::SpawnChoice(choice, key, goto));
@ -246,6 +292,21 @@ pub fn load_chats(mut chatdb: ResMut<ChatDB>) {
error!("Could not load chat definitions. Validate files in `src/chats/` path."); error!("Could not load chat definitions. Validate files in `src/chats/` path.");
} }
} }
// let mut chat = Chat {
// id: 2,
// position: vec![0],
// timer: 0.0,
// talker: Talker {
// conv_id: "Icarus".to_string(),
// name: None,
// pronoun: None,
// talking_speed: 1.0,
// }
// };
dbg!(chatdb.at(2, &vec![0]));
dbg!(chatdb.at(2, &vec![1]));
dbg!(chatdb.at(2, &vec![2]));
dbg!(chatdb.at(2, &vec![3]));
} }
pub fn handle_new_conversations( pub fn handle_new_conversations(