summaryrefslogtreecommitdiff
blob: 50d24f676ef0692444f030449c437e322fcaa6bc (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
---
title: "Fate (Version 1)"
menuTitle: "Fate"
weight: 3
---

When using Tonkadur, Fate is the language the author writes in. This language is
meant to provide the writer with as many tools as possible, so that they do not
feel constrained by the limitations of the language, nor find writing complex
narratives tedious.

## A few warnings first

Fate uses strong typing. This is intended to make detection of mistakes easier,
but may lead to some frustrations for authors only used to languages using
dynamic typing.

Fate does not have any special features for multi-seating/multi-player
narratives. This is by design, as adding proper support for this would make the
underlying language more complex, and thus would make it harder to create an
interpreter for it. This is however something considered for an extension
(see
[this GitHub issue about it](https://github.com/nsensfel/tonkadur/issues/5)).

You'll need to learn parentheses management, if you make anything complex. Fate
uses a LISP inspired syntax, so parentheses are everywhere. The syntax gives you
free reign over indentation, but so many parentheses does require at least some
discipline in order to be readable.

Fate is meant to be compiled. This does add an extra step to your existing
process.

Fate is very much a new language. There isn't many (any?) tools for it.

If for some reason you want to perform memory allocation, there is no automatic
garbage collection, so you will have to free whatever you allocate.

In what may seem weird for narrative scripting, Fate does not feature string
manipulation operations. Strings can be made into rich text, with complex
attributes, but you will not be able to use things like regular expressions
(or anything other than simple concatenation, really) with just the base
language. This is because of these operations would need to be implemented
directly by the interpreter anyway, and their use isn't actually common in
a narrative description, which would make requiring support for them of
any Tonkadur interpreter problematic.

## Some nice features

If you haven't closed the tab yet, here are some reasons why you *might* want
to use Fate.

It *is* a LISP inspired syntax. If that's what you like, you might even enjoy
this take on it. There's no fiddling with indentation, no wondering what symbol
does what. It's all about parentheses indicating something is performed on a
list of other things. It's not a purely functional language, though. In fact,
it's a mix of both declarative and imperative programming: sequences (or
procedures) are imperative. Computations are functional. There are lambda
functions, but they're strongly typed. Collections and structures can only be
modified through specific instructions, so it's not quite really a LISP-like
language.

There are pointers. Oh, come on, it's a plus, *right?* This makes it a lot
easier to refer to things. There's no pointer arithmetic, so you don't have to
worry about the scary stuff. Also, this lets you allocate memory, which is
useful when you want a unspecified amount of distinct instances of something.
Want to describe a story where the player can push a button as many time as
they choose, create a clone of a creature every time they do so, yet let them
fight any of these creature as their distinct instance? Well, with memory
allocation and pointers, it's easily done. Didn't you know? When writing a
narrative, the RAM's the limit. So yeah, maybe don't forget to `free` the
thousands of monster the player will no doubt create.

You can do recursion. Wait! I *am* listing the nice features! Do people really
not like recursion and pointers? Oh, well... Still, you can do recursions with
both procedures and lambda functions. Not a fan of recursion? That's alright,
you also have imperative loops: `for`, `while`, `do_while`, and even
`for_each`.

## The basics

#### Procedures (or Sequences) and Instructions
Procedures are lists of instructions and things to display. Instructions can be
applied to values, but they themselves do not return any value.  Any value not
part of an instruction found in a procedure is displayed. Any value being part
of an instruction is **not** displayed.

One way to think about it is to consider procedures as being actually two types
of things: sequences and procedures. A sequence is then a scene in the story,
and a procedure is just a construct called upon to perform instructions.

Procedures can take arguments. Values are passed by copy.

Procedures can be called in two ways: a `call` or a `jump`. A `call` will
perform the procedure before continuing with the current sequence of
instructions. A `jump` will perform the procedure **instead of** the
**current** sequence of instructions.

Procedures can be called before their definition. The compiler will simply tell
you if the definition ends up being incompatible or missing.

#### Computations
Computations are operations returning a value. Computations do not modify
anything, they simply return the result of an operation.

A special type of computation, called *lambda function*, allows the creation of
a computation that will be performed only when called upon. Those can take
parameters. What's the use of something as advanced in a narrative scripting
language? It's convenient when you want to express something like a character's
name who depends on what the player figured out. Sure, you *could* make each
instance of the name feature a series of tests to find which value to use, or
you could just use a lambda function and just put
`(eval name_of_mysterious_character)` to get the right value.

One last thing about lambda functions, which really are the only potentially
complicated thing about computations: these *are* computations, so you can use
them as such. They also cannot modify anything, as they only contain
computations and not instructions.

#### Declarations and First Level Instructions
Declarations and First Level Instructions are a special type of instruction
which can only be done from outside a procedure. This distinction means that,
for example, you cannot define a procedure from within a procedure.

#### Events
Sometimes, you might need to communicate something to the interpreter which
cannot be expressed in Fate. For example, you might want to say: "pause for 30
seconds", or "play this music". This kind of thing has been considered to not
be generic enough to mandate every interpreter supports it. Instead, events are
used.

Events are first declared, by being given a name and a list of types
corresponding to the parameters they take. Then, the `(event ...)` instruction
can be used to indicate that a certain event should occur. This will pause the
execution, and make the interpreter react to the event before continuing.

The effect of an event is purely the interpreter's responsibility. It cannot be
described in Fate. Thus, you will need to refer to the interpreter you use to
see what events are available.

#### Types
The basic `string`, `int`, `float`, and `bool` types are what one would expect.

`rich_text` corresponds to text decorations, see the text effect sub-section.

Two collection types are available: `(list [TYPE])` is a list of `[TYPE]`
elements, and `(set [COMPARABLE])` is a set of `[COMPARABLE]` elements.

`(ptr [TYPE])` is an address to a value of type `[TYPE]`.

Structures can be defined. They may contain other structures, but cannot be
recursive: only already defined types can be used. Pointers cannot be used to
resolve this conundrum in this version of Fate, but it may be added in the next
version (see [this GitHub
issue](https://github.com/nsensfel/tonkadur/issues/6)).

#### Player Choices
Inputs from the players are limited to the `(player_choice ...)` instruction.
It presents the player with `rich text` options to choose from, which execute
an associated list of instructions if chosen.

More complicated inputs, such as retrieving a `string` or an `int` from the
player require the definition and use of events.