Skip to content

Commit

Permalink
Stable Dialog Systemgit add -A
Browse files Browse the repository at this point in the history
  • Loading branch information
QueenOfSquiggles committed Jan 11, 2024
1 parent f3a3e11 commit 0f270ee
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 143 deletions.
3 changes: 1 addition & 2 deletions scenes/testing_scene.gd
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
extends Node

func _ready() -> void:
CoreDialog.load_track("res://Dialogic/example.json")
CoreDialog.blackboard_action("set shotgun true")
CoreDialog.load_track_file("res://Dialogic/example.json")
CoreDialog.event_bus.track_ended.connect(_on_track_end)

func _on_track_end() -> void:
Expand Down
76 changes: 54 additions & 22 deletions src/scene/dialog/core_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ use crate::util::SquigglesUtil;
use super::{
dialog_blackboard::{Blackboard, Entry},
dialog_events::DialogEvents,
dialog_gui::{DialogGUI, DialogSettings},
dialog_gui::DialogGUI,
dialog_settings::DialogSettings,
dialog_track::{DialogError, DialogTrack, Line},
};

#[derive(GodotClass)]
#[class(init, base=Object)]
pub struct CoreDialog {
current_track: Option<DialogTrack>,

#[var]
override_settings: Option<Gd<DialogSettings>>,
#[var]
Expand Down Expand Up @@ -54,7 +56,39 @@ impl CoreDialog {
}

#[func]
pub fn load_track(&mut self, file_path: GString) {
pub fn load_track_file(&mut self, file_path: GString) {
let result = DialogTrack::load_from_json(file_path.clone());
if result.is_ok() {
self.current_track = Some(result.unwrap());
self.load_track();
} else {
Self::handle_dialog_error(result.unwrap_err());

Check failure on line 65 in src/scene/dialog/core_dialog.rs

View workflow job for this annotation

GitHub Actions / Clippy

called `unwrap_err` on `result` after checking its variant with `is_ok`
}
}

#[func]
pub fn load_track_text(&mut self, track_text: GString) {
let result = DialogTrack::load_from_text(track_text, "<internal text>".to_godot());
if result.is_ok() {
self.current_track = Some(result.unwrap());
self.load_track();
} else {
Self::handle_dialog_error(result.unwrap_err());

Check failure on line 76 in src/scene/dialog/core_dialog.rs

View workflow job for this annotation

GitHub Actions / Clippy

called `unwrap_err` on `result` after checking its variant with `is_ok`
}
}

#[func]
pub fn load_track_dict(&mut self, track_dict: Dictionary) {
let result = DialogTrack::load_from_dict(track_dict, "<internal dict>".to_godot());
if result.is_ok() {
self.current_track = Some(result.unwrap());
self.load_track();
} else {
Self::handle_dialog_error(result.unwrap_err());

Check failure on line 87 in src/scene/dialog/core_dialog.rs

View workflow job for this annotation

GitHub Actions / Clippy

called `unwrap_err` on `result` after checking its variant with `is_ok`
}
}

pub fn load_track(&mut self) {
if self.event_bus.is_none() {
self.init_event_bus();
}
Expand All @@ -68,25 +102,30 @@ impl CoreDialog {
};

// load track data
match DialogTrack::load_from_json(file_path.clone()) {
Err(error) => Self::handle_dialog_error(error),
Ok(track) => self.current_track = Some(track),
}
let Some(track) = &self.current_track else {
godot_warn!("Failed to load a dialog track from {}", file_path);
godot_warn!("Failed to load a dialog track");
return;
};

// kill old GUI
if let Some(gui) = self.gui.as_mut() {
gui.queue_free();
}
self.gui.as_mut().map(|g| {

Check failure on line 111 in src/scene/dialog/core_dialog.rs

View workflow job for this annotation

GitHub Actions / Clippy

called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
if g.is_instance_valid() {
g.queue_free()
}
});

// Creates Really Bad Panic
// if let Some(gui) = &mut self.gui.clone() {
// if gui.is_instance_valid() {
// gui.queue_free();
// }
// }

// create and add GUI
let mut gui = DialogGUI::new_alloc();

gui.bind_mut().track = Some(VecDeque::from_iter(track.lines.clone()));
SquigglesUtil::add_child_deferred(&mut root.upcast(), &gui.clone().upcast());
gui.bind_mut().track = Some(VecDeque::from_iter(track.lines.clone()));
self.gui = Some(gui);
}

Expand All @@ -101,14 +140,11 @@ impl CoreDialog {
gui.bind_mut().make_dialog_choice(selection)
}

pub fn handle_line_action(&mut self, line: &Line) {
pub fn handle_dialog_signal(&mut self, line: &Line) {
if self.event_bus.is_none() {
self.init_event_bus();
}
match line {

Check failure on line 147 in src/scene/dialog/core_dialog.rs

View workflow job for this annotation

GitHub Actions / Clippy

you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
Line::Action { action } => {
self.blackboard_action(action.to_godot());
}
Line::Signal { name, args } => {
let Some(bus) = &mut self.event_bus else {
return;
Expand Down Expand Up @@ -148,7 +184,7 @@ impl CoreDialog {
let Entry::Number(index) = event_arg else {
return;
};
let index = index.floor() as usize;
let index = (index.floor() - 1f32) as usize;
let Some(gui) = &mut self.gui else {
return;
};
Expand All @@ -175,12 +211,8 @@ impl CoreDialog {
// self.blackboard.debug_print();
}

pub fn jump(&mut self, index: usize) {
godot_print!("Jumping to {}", index);
}

pub fn end(&mut self) {
godot_print!("Ending dialog");
pub fn blackboard_parse(&self, text: String) -> String {
self.blackboard.format_text(text)
}

pub fn singleton() -> Gd<CoreDialog> {
Expand Down
59 changes: 51 additions & 8 deletions src/scene/dialog/dialog_blackboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use std::{
rc::Rc,
};

use godot::{engine::Json, prelude::*};
use godot::{
engine::{global::Error, Json},
prelude::*,
};

#[derive(Debug, PartialEq, Clone)]
pub enum Entry {
Expand Down Expand Up @@ -303,13 +306,20 @@ impl Blackboard {
self.entries.get(&key.to_string()).unwrap().clone() // we should be safe to unwrap here???
} else {
// from constant
let var = Json::parse_string(key.to_godot());
match var.get_type() {
VariantType::Float => Entry::Number(f32::from_variant(&var)),
VariantType::Int => Entry::Number(i32::from_variant(&var) as f32),
VariantType::Bool => Entry::Bool(bool::from_variant(&var)),
VariantType::String => Entry::String(String::from_variant(&var)),
_ => Entry::None,
let mut json = Json::new_gd();
let err = json.parse(key.to_godot());
if err != Error::OK {
// for whatever reason we failed, return a none value
Entry::None
} else {
let var = json.get_data();
match var.get_type() {
VariantType::Float => Entry::Number(f32::from_variant(&var)),
VariantType::Int => Entry::Number(i32::from_variant(&var) as f32),
VariantType::Bool => Entry::Bool(bool::from_variant(&var)),
VariantType::String => Entry::String(String::from_variant(&var)),
_ => Entry::None,
}
}
};
match entry {
Expand All @@ -327,6 +337,39 @@ impl Blackboard {
}
}

pub fn format_text(&self, text: String) -> String {
let mut buffer = String::with_capacity(text.len());
let mut remaining = text.as_str();
const LOOP_LIMITER: u32 = 9999;
const DELIM_OPEN: &str = "{{";
const DELIM_CLOS: &str = "}}";
for _ in 0..LOOP_LIMITER {
if remaining.is_empty() {
break;
}
let next = remaining.split_once(DELIM_OPEN);
let Some(next) = next else {
buffer += remaining;
break;
};
let (pre, post) = next;
buffer += pre;
let Some(fin) = post.split_once(DELIM_CLOS) else {
buffer += post;
break;
};
let (key, after) = fin;
let key = key.trim();
let var = self.get_variant_entry(key.trim());
if var.is_nil() {
godot_warn!("Found null when parsing dialog key: {}", key);
}
buffer += var.to_string().as_str();
remaining = after;
}
buffer
}

pub fn get_variant_entry(&self, key: &str) -> Variant {
let Some(entry) = self.entries.get(key) else {
godot_warn!("Entry not found \"{}\", returning nil", key);
Expand Down
84 changes: 84 additions & 0 deletions src/scene/dialog/dialog_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use godot::prelude::*;

#[derive(GodotClass)]
#[class(init, base=Object)]
pub struct DialogBuilder {
nodes: Array<Dictionary>,
#[base]
node: Base<Object>,
}
#[godot_api]
impl DialogBuilder {
#[func]
fn push_text(&mut self, text: GString, character: GString, requirements: GString) {
let mut node = Dictionary::new();
node.set("type", "text");
node.set("content", text);
node.set("character", character);
node.set("requires", requirements);
self.nodes.push(node);
}

#[func]
fn push_choice(&mut self, prompt: GString, character: GString) {
let mut node = Dictionary::new();
node.set("type", "choice");
node.set("prompt", prompt);
node.set("character", character);
node.set("options", Array::<Variant>::new());
self.nodes.push(node);
}

#[func]
fn push_choice_option(&mut self, text: GString, requires: GString, action: GString) {
let Some(prev) = &mut self.nodes.last() else {
godot_warn!("Cannot push a choice option into an empty node!");
return;
};
if String::try_from_variant(&prev.get("type").unwrap_or_default()).unwrap_or_default()
!= "choice"
{
godot_warn!(
"When pushing a choice option, the previously pushed node must be a choice node!"
);
return;
}
let Ok(choices_array) =
&mut Array::<Variant>::try_from_variant(&prev.get_or_nil("options"))
else {
godot_warn!("Failed to parse array from previous node's 'options' property");
return;
};
let mut node = Dictionary::new();
node.set("text", text);
node.set("action", action);
node.set("requires", requires);
choices_array.push(node.to_variant());
prev.set("options", choices_array.to_variant());
}

#[func]
fn push_signal(&mut self, text: GString, character: GString, requirements: GString) {
let mut node = Dictionary::new();
node.set("content", text);
node.set("character", character);
node.set("requires", requirements);
self.nodes.push(node);
}

#[func]
fn push_action(&mut self, text: GString, character: GString, requirements: GString) {
let mut node = Dictionary::new();
node.set("content", text);
node.set("character", character);
node.set("requires", requirements);
self.nodes.push(node);
}

#[func]
fn get_dialog_track(&self) -> Dictionary {
let mut dict = Dictionary::new();
dict.set("nodes".to_godot(), self.nodes.clone());
dict
}
}
Loading

0 comments on commit 0f270ee

Please sign in to comment.