-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.rs
94 lines (71 loc) · 3.39 KB
/
cli.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::error::Error;
use std::io::BufRead;
use yarn_spool::{expand_substitutions, read_string_table_file, Dialogue, DialogueEvent, Program};
fn main() -> Result<(), Box<dyn Error>> {
// A Yarn script compiles down into bytecode (.yarnc files) and
// string tables (.csv files). These are seperated to allow for
// localization.
//
// To generate the inputs for this example, run:
// ysc compile examples/script.yarn --output-name=examples/generated/script
//
// Once you've compiled the script, you can load it in Rust via
// yarn_spool's API. Bytecode is represented as a Program, and
// a string table is represented as a HashMap<String, String>.
let program = Program::from_file("examples/generated/script.yarnc");
let string_table = read_string_table_file("examples/generated/script-Lines.csv");
// To run programs, you need to load them into a Dialogue.
let mut dialogue = Dialogue::new();
dialogue.add_program(&program);
let mut input = String::new();
// To run the dialogue, call the 'advance' method. This will
// return when an event occurs that your game may want to
// handle (or when the program completes).
while let Some(event) = dialogue.advance() {
match event {
// A 'Line' event means that there is a new line of dialogue to display.
//
// Depending on your UI, you may want to stop advancing until there is
// some kind of player input.
DialogueEvent::Line => {
let line = dialogue.current_line();
// To get the text for a line, look up the ID in the string table.
let raw_text = &string_table[&line.id].text;
// If the text includes variables, you will need to substitute them
// in.
let text = expand_substitutions(raw_text, &line.substitutions);
// TODO: Markup parsing is not supported yet.
println!("{}", text);
}
// A 'Command' event means that the script wants to execute a command.
//
// What these commands do is up to your game engine - a common
// example is to have a 'wait' command that pauses the
// dialogue for a set amount of time.
DialogueEvent::Command => {
println!("<<{}>>", dialogue.current_command());
}
// An 'Options' event means that the script is now waiting for the player
// to select an option. Until you tell the Dialogue which option has
// been selected, calling 'advance' will fail.
DialogueEvent::Options => {
for opt in dialogue.current_options() {
// The process for building the player-facing string is the same as
// for a normal line.
let raw_text = &string_table[&opt.line.id].text;
let text = expand_substitutions(raw_text, &opt.line.substitutions);
println!("{}) {}", opt.index, text);
}
let option = read_line(&mut input)?;
dialogue.set_selected_option(option);
}
}
}
Ok(())
}
fn read_line(buf: &mut String) -> Result<usize, Box<dyn Error>> {
buf.clear();
std::io::stdin().lock().read_line(buf)?;
let value = buf.trim().parse()?;
Ok(value)
}