chatter/
lib.rs

1/*!
2
3Chatter provides a macro that translates a human-readable script
4into a format that can be easily used in Rust.
5
6# Basic Usage
7
8The following is an example of a basic [chatter] script:
9
10``` plaintext
11This is an example line
12This line is followed by a choice:
13- continue to branch 1
14- switch to branch 2 -> branch_2
15
16# branch_1
17This line is in branch 1
18-> end
19
20# branch_2
21This line is in branch 2
22
23# end
24```
25
26Chatter parses the above script into a useful format, a [Chat].
27However, how that chat is run is left to the user.
28[Chatterbox](https://github.com/appybara13/chatterbox) is an example
29of a cli runner.
30
31The basic flow of running a chat follows:
32
33``` rust
34use chatter::{chatter, Chat, ChatContent, ChatPosition, Choice, Line};
35
36fn main() {
37    let example_chat = chatter! {r#"
38    INSERT CHATTER SCRIPT HERE
39    "#};
40
41    run(&example_chat);
42}
43
44fn run(chat: &Chat) {
45    let mut current_position = ChatPosition::start();
46
47    while current_position != ChatPosition::end() {
48        current_position = step(chat, current_position);
49    }
50}
51
52fn step(chat: &Chat, position: ChatPosition) -> ChatPosition {
53    let content = match chat.get(position) {
54        Some(content) => content,
55        None => return ChatPosition::end(),
56    };
57
58    match content {
59        ChatContent::Line(line) => handle_line(line),
60        ChatContent::Choices(choices) => handle_choices(choices),
61    }
62}
63
64fn handle_line(line: &Line) -> ChatPosition {
65    // Present the line to the player
66    // Wait for timer/player to skip
67    // Return line.next()
68
69    todo!()
70}
71
72fn handle_choices(choices: &Vec<Choice>) -> ChatPosition {
73    // Present the choices to the player
74    // Wait for player to make a selection
75    // Return the selection choice's next()
76
77    todo!()
78}
79```
80
81# Tags
82
83To define more complex behaviour, you can use tags.
84
85Tags can be applied to both lines and choices. A chatter script example is provided below.
86
87``` plaintext
88[Narrator] You attempt to open the door, but it doesn't budge.
89- [Player, strength_over_14] Kick down the door.
90- [Player, thievery_atleast_expert inventory_includes_lockpick] Pick the lock.
91- [Player] Call out "Is anyone in there?"
92- [Player] Walk way.
93```
94The list of tags for a line or choice can be obtained as strings.
95More usefully, Chatter provides a [Tag] trait. Some of the above
96choices include requirements. They could be represented by the following
97type:
98
99``` rust
100use chatter::Tag;
101
102enum Requirement {
103  Ability{ability: Ability, min: u32},
104  Skill{skill: Skill, level: SkillLevel},
105  Inventory{item: Item, count: u32}
106}
107
108impl Requirement {
109  fn fufilled(&self, player: &Player) -> bool { todo!() }
110}
111
112impl Tag for Requirement {
113  fn from(string: &str) -> Option<Self> { todo!() }
114}
115```
116
117The list of requirements (ignoring other tags) can be be easily
118obtained from a given [Choice]. Then, that choice could be offered
119to the player only if all the requirements are met.
120
121Chatter doesn't define this behaviour - the interpretation and use of tags is left
122to the program using the library.
123
124A non-exhaustive list of possible uses follows:
125- Character names
126- Art selection (A character might have different art for different facial expressions, for example)
127- Text decoration (font, size, color, bold, italic)
128- Choice requirements (As above, but could also be used for NPC dialogue; if the player has no gold say A, else say B)
129- Choice randomization (tags could be used to assign weights)
130- Game state changes (enter combat, invetory changes, relationship/alignment changes)
131
132# Chatter Definitions
133
134A **Line** is any line that doesn't start with **-** or **#**. If it starts with square brackets,
135then the contents of the brackets are **Tag**s. Then, it must include text. **\[tag\] -> branch** is invalid.
136
137A **Choice** starts with **-** but otherwise the follows the same rules as a **Line**. **Choice**s must exist in groups of at least two.
138An empty line separates two seperate groups of **Choice**s. Any **Choice** without a **Goto** will be followed
139by the next line after the group.
140
141A **Branch** is a line that starts with **#** and acts as a marker for **Goto**s.
142
143A **Goto** is marked by **->** and must be followed by a branch label that exists.
144They be appended to the end of a **Line** or **Choice**, and indicate that the dialog
145moves to the relevant branch after that **Line** or if that **Choice** is selected.
146They can also be placed on a new line, applying to the whatever precedes it.
147
148**Tag**s are separated by commas and whitespace and contained within square brackets,
149at the start of a **Line** or **Choice**.
150
151**Tag**s and **Branch** names can only contain alphanumeric characters and underscores.
152
153Text in a **Line** or **Choice** can contain anything except a new line, or **->**. It also
154cannot start with **-** or **#**.
155
156Comments start with \/\* and end with \*\/.
157
158**Line**s and **Choice**s don't overflow; a new line indications a new **Line** or **Choice**.
159
160# A More Complex Example
161
162A final, more complicated example of a chatter script follows:
163
164``` plaintext
165[Guard] By order of the Jarl, stop right there!
166[Guard] You have commited crimes against Skyrim and her people. What say you in your defense?
167
168- [Player] You caught me. I'll pay off my bounty. -> bounty
169- [Player] I submit. Take me to jail. -> jail
170- [Player, starts_combat] I'd rather die than go to prison!
171
172- [Guard, random_weight_1.0 requires_imperial] Then suffer the Emperor's wrath.
173- [Guard, random_weight_1.0 requires_stormcloak] Skyrim has no use for your kind.
174- [Guard, random_weight_1.0] Then pay with your blood.
175- [Guard, random_weight_2.0] That can be arranged.
176- [Guard, random_weight_1.0] So be it.
177
178-> end
179
180# jail
181[Guard, imprison_player] I guess you're smarter than you look.
182-> end
183
184# bounty
185- [Guard, requires_player_male] Smart man.
186- [Guard, requires_player_female] Smart woman.
187[Guard] Now come along with us. We'll take any stolen goods, and you'll be free to go.
188[Guard] After you pay the fine, of course.
189
190# end
191```
192
193*/
194mod chat;
195mod choices;
196mod line;
197mod tag;
198
199/// Represents a whole dialogue, created with the [chatter] macro.
200pub use chat::Chat;
201
202/// Either a [Line] or a Vec of [Choice]s.
203pub use chat::ChatContent;
204
205/// Every [ChatContent] in a [Chat] has a position, and every [Line] and [Choice]
206/// has a next position.
207/// No [ChatContent] ever exists at ``ChatPosition::end()``.
208pub use chat::ChatPosition;
209
210/// A Vec of choices represents a set of possible options, one of which can be taken.
211/// They allow the dialogue to branch.
212pub use choices::Choice;
213
214/// Represents a line of dialogue. A [Chat] that consists of just lines and no choices
215/// will play the same every time.
216pub use line::Line;
217
218/// A trait that allows more ergonomic checking tags on a [Tagged] type.
219pub use tag::Tag;
220
221/// Both [Line] and [Choice] can be tagged.
222pub use tag::Tagged;
223
224/// The macro for parsing chatter script into a [Chat].
225/// The script must be a raw string literal, ``r#"SCRIPT"#``.
226pub use chatter_macros::chatter;