summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'content/learn')
-rw-r--r--content/learn/01.start/_index.md43
-rw-r--r--content/learn/02.choices/_index.md71
-rw-r--r--content/learn/03.variables/default.md90
-rw-r--r--content/learn/04.sequences/default.md123
-rw-r--r--content/learn/05.files/default.md113
-rw-r--r--content/learn/06.pointers/default.md171
-rw-r--r--content/learn/07.structures/default.md201
-rw-r--r--content/learn/08.collections/default.md408
-rw-r--r--content/learn/09.lambdas/default.md366
-rw-r--r--content/learn/10.conditions/default.md402
-rw-r--r--content/learn/_index.md8
11 files changed, 1996 insertions, 0 deletions
diff --git a/content/learn/01.start/_index.md b/content/learn/01.start/_index.md
new file mode 100644
index 0000000..1741772
--- /dev/null
+++ b/content/learn/01.start/_index.md
@@ -0,0 +1,43 @@
+---
+menuTitle: "Starting"
+title: "Writing a story in Fate"
+weight: 1
+---
+* Create a folder for your story. This is not mandatory, but helps keeping
+ track of the created files.
+* Open up your favorite text editor, and write in `main.fate`:
+
+{{< fatecode >}}(fate_version 1)
+Once upon a time, starting a story with these words wasn't considered
+a cliche. Starting in a tavern might also not be seen as very
+original. Having the main character be an street orphan, raised by
+some mysterious sage all to end up as a mercenary with an uncommonly
+strong sense of honor probably isn't going to lead to any praises for
+novelty either. Maybe you should drink to that.
+(newline)
+Or maybe you shouldn't. This isn't your first mug. Not your second
+either. Drinking to forget that you are a stereotypical hero isn't
+going to solve anything. Worse, the alcoholic trait is part of the
+image.
+(newline)
+As you contemplate your own pointless description, your gaze leaves
+what turns out to be an already empty glass in your hand and finds the
+barman.
+(end)
+{{< /fatecode >}}
+
+This is a very minimal story in Fate. Let's look at what is it made of:
+
+* The `(fate_version 1)` line must be at the top of every file. It is just
+ there to inform the compiler of the used language version. It has no effect on
+ the output.
+* The text is mostly printed out as-is: newlines, tabs and spaces are
+ considered to be a single space. No matter how many of them follow. This
+ makes indentation have no effect on the output text. Furthermore such
+ characters preceding text are ignored.
+* `(newline)` inserts a newline in the output.
+* `(end)` signals the end of the story. It needs to be there.
+
+With this, you know how to make a static, purely textual story in Fate. Our poor
+protagonist needs a refill, so we need to introduce
+[Player Choices](/learn/choices) next.
diff --git a/content/learn/02.choices/_index.md b/content/learn/02.choices/_index.md
new file mode 100644
index 0000000..a5ba4d7
--- /dev/null
+++ b/content/learn/02.choices/_index.md
@@ -0,0 +1,71 @@
+---
+menuTitle: "Player Choices"
+title: "Asking the Player"
+weight: 2
+---
+In [the previous step](/learn/start), we saw a minimal text story written in Fate.
+Now, we need to interact with the player.
+
+There are three ways to interact with the player:
+* Asking them to choose between options.
+* Prompting them for an integer.
+* Prompting them for a string.
+
+The last two require notions that haven't been introduced yet, so let's give
+the player a simple choice instead:
+
+**main.fate:**
+{{< fatecode >}}(fate_version 1)
+Once upon a time, starting a story with these words wasn't considered
+a cliche. Starting in a tavern might also not be seen as very
+original. Having the main character be an street orphan, raised by
+some mysterious sage all to end up as a mercenary with an uncommonly
+strong sense of honor probably isn't going to lead to any praises for
+novelty either. Maybe you should drink to that.
+(newline)
+Or maybe you shouldn't. This isn't your first mug. Not your second
+either. Drinking to forget that you are a stereotypical hero isn't
+going to solve anything. Worse, the alcoholic trait is part of the
+image.
+(newline)
+As you contemplate your own pointless description, your gaze leaves
+what turns out to be an already empty glass in your hand and finds the
+barman.
+
+(player_choice
+ (
+ ( Ask the barman for a refill )
+ Staring straight at the barman, you raise your glass and
+ proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ )
+ (
+ ( Fall asleep )
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ )
+ (
+ ( Resolve whether P=NP )
+ Sadly, the output for this choice would require some concepts
+ that haven't been introduced yet, so let's never mention it
+ again.
+ )
+)
+
+(end)
+{{< /fatecode >}}
+
+In this version, the player is able to interact with the story: once
+`player_choice` is reached, the output stops and the player is presented with
+options. Here, three choices are available. A choice is made of a label and a
+list of instructions. The label is the text which is displayed to player (for
+example `Fall asleep`). The list of instructions is what will be performed if
+that choice is selected. Putting text where instructions are expected simply
+outputs the text. In fact, all the content of the previous step was
+instructions. Once a choice has been made and the instructions have been
+performed, the story continues past the `player_choice` construct.
+
+But something critical is missing. Indeed, how can you get a refill without
+money? Just how much money does our hero have? Will it be enough to quench that
+terrible thirst? Let's [introduce variables](/learn/variables).
diff --git a/content/learn/03.variables/default.md b/content/learn/03.variables/default.md
new file mode 100644
index 0000000..6c2cdf4
--- /dev/null
+++ b/content/learn/03.variables/default.md
@@ -0,0 +1,90 @@
+---
+menuTitle: Variables
+title: "Adding Variables"
+weight: 3
+---
+In [the previous step](/learn/start), we introduced player choices. Dynamic
+content is here, but it is not going far without variables.
+
+Fate is a strongly typed language, meaning that variables **must** be assigned
+a precise type and cannot deviate from it.
+
+Variables have to be declared before being used. Let us keep things simple for
+now, and declare variables using the `local` instruction. The alternative is
+`global`. The difference between the two being about access from other
+contexts, something that is introduced in the next chapter.
+
+We are trying to add a variable that corresponds to money. An `int` is thus
+appropriate.
+
+**main.fate:**
+
+ (fate_version 1)
+
+ (local int hero_money)
+ (local int price_of_booze)
+
+ (set hero_money 42)
+ (set price_of_booze 12)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ Staring straight at the barman, you raise your glass and
+ proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the
+ beer being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (set hero_money
+ (- (var hero_money) (var price_of_booze))
+ )
+ )
+ (
+ ( Fall asleep )
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that
+ someone stole all your money.
+ (set hero_money 0)
+ )
+ )
+
+ (end)
+
+* `(local int hero_money)` declares an `int` variable with the name
+ `hero_money`.
+* `(set hero_money 42)` sets the value of the `hero_money` variable to 42.
+* `(var price_of_booze)` returns the current value of the `price_of_booze`
+ variable.
+* `(- (var hero_money) (var price_of_booze))` returns the result of subtracting
+ the value of `price_of_booze` to the value of `hero_money`. All operators
+ use a prefixed form.
+
+`local`, `set`, `player_choice`, and `end`, are instructions. Instructions do
+not return any value. Thus, they do not add to the printed text.
+
+Having to continue the story from within the `player_choice` structure is going
+to get tedious very fast. Let's [introduce sequences](/learn/sequences).
diff --git a/content/learn/04.sequences/default.md b/content/learn/04.sequences/default.md
new file mode 100644
index 0000000..cecbff8
--- /dev/null
+++ b/content/learn/04.sequences/default.md
@@ -0,0 +1,123 @@
+---
+menuTitle: Sequences
+title: "Introducing Sequences"
+weight: 4
+---
+In [the previous step](/learn/variables), we introduced variables. The story is
+starting to have branches, but writing them from within a `player_choice`
+construct is awkward. To resolve this, *sequences* are introduced.
+
+Sequences are named lists of instructions. They do *not* have to be defined
+before being used, but the definition must be found at some point. Since
+instructions do not return any value, neither do sequences.
+
+Sequences define their own context, meaning that `local` variables from outside
+the sequence cannot be accessed and, conversely, `local` variables from the
+sequence cannot be accessed outside of it.
+
+Entering a sequence can be done in two ways:
+* By visiting, in which case the sequence is executed and the story continues.
+* By jumping, in which case the sequence replaces the current story execution.
+
+When in doubt, prefer visiting to jumping, as the latter is mainly intended for
+optimization purposes.
+
+Sequences can be entered again from themselves, making recursion possible.
+
+Sequences can take parameters.
+
+
+**main.fate:**
+
+ (fate_version 1)
+
+ (global int hero_money)
+
+ (set hero_money 42)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero_money) (var cost))
+ )
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ )
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero_money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+ (end)
+
+* `(visit get_a_refill)` makes a visit to the sequence `get_a_refill`. Since
+ that sequence does not take parameters, none need to be provided.
+* `(jump_to fall_asleep)` stops the execution of the main instruction list and
+ proceeds to using the sequence `fall_asleep` instead. Here again, no
+ arguments are expected by `fall_asleep`. Notice how the `fall_asleep` sequence
+ ends with `(end)`: since there is no return from it, the original `(end)`
+ would not be reached and thus a new one is required to end the story.
+* `(visit pay (var price_of_booze))` makes a visit to the `pay` sequence, which
+ does require a parameter.
+* `(global int hero_money)` has replaced `(local int hero_money)`, because that
+ variable needs to be accessible from within the sequences.
+* `(local int price_of_booze)` has been moved to the `get_a_refill` sequence,
+ as there is no reason to have it be defined across the whole story.
+* The `pay` sequence cannot directly access the `price_of_booze` variable, as
+ it is `local` and from another sequence, hence the use of a parameter to
+ communicate the correct amount.
+
+With this, the `player_choice` have become much more readable. However, the file
+itself is starting to become hard to read. The solution is then to [split the
+content into multiple files](/learn/files).
diff --git a/content/learn/05.files/default.md b/content/learn/05.files/default.md
new file mode 100644
index 0000000..da78206
--- /dev/null
+++ b/content/learn/05.files/default.md
@@ -0,0 +1,113 @@
+---
+menuTitle: Files
+title: "Splitting into Multiple Files"
+weight: 5
+---
+In [the previous step](/learn/sequences), we introduced sequences. This made
+branching the story easier, but having many sequences in a file made the flow
+difficult to read. To resolve this, the content is going to be split into
+multiple files.
+
+By using `(require path_to_file)`, the content of `path_to_file` is explored,
+but only if that file has not already been explored.
+
+* Create a new file, `data.fate`, with the following content:
+
+ (fate_version 1)
+
+ (global int hero_money)
+
+ (set hero_money 42)
+
+* Create a new file, `actions.fate`, with the following content:
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero_money) (var cost))
+ )
+ )
+
+* Create a new file, `get_a_refill.fate`, with the following content:
+
+ (fate_version 1)
+
+ (require actions.fate)
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ )
+
+* Create a new file, `falling_asleep.fate`, with the following content:
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero_money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
+
+With this, the story is much more easy to follow. Let's continue by looking
+at [the actually-not-scary-at-all pointers](/learn/pointers).
diff --git a/content/learn/06.pointers/default.md b/content/learn/06.pointers/default.md
new file mode 100644
index 0000000..a4108bb
--- /dev/null
+++ b/content/learn/06.pointers/default.md
@@ -0,0 +1,171 @@
+---
+menuTitle: Pointers
+title: "Addressing Pointers"
+weight: 6
+---
+In [the previous step](/learn/files), we split the story into multiple files
+to make it more readable. Now, we'll see pointers, because they were needed for
+two features that were glossed over in what was presented before:
+* Using sequences as imperative procedures.
+* Prompting the user for content.
+
+A pointer is a value that indicates a location in memory where some
+data is stored. This can be used as a value that will tell some instruction
+where to put data.
+
+Pointers have types. For example, `(ptr int)` is the type corresponding to
+pointers to `int` data.
+
+To compute a pointer to a variable `v`, simply write `(ptr v)`.
+
+Given a pointer `p`, the variable being pointed to can be referred to using
+`(at p)`.
+
+**data.fate:**
+
+ (fate_version 1)
+
+ (global int hero_money)
+ (global string hero_name)
+
+ (set hero_money 42)
+
+**get_a_refill.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+ (require actions.fate)
+
+ (define_sequence lower_price_of_booze
+ (
+ ((ptr int) price_pointer)
+ (int decrease)
+ )
+ Great! The price of booze just lowered from (at price_pointer)
+ (set (at price_pointer)
+ (-
+ (at price_pointer)
+ (var decrease)
+ )
+ )
+ to (at price_pointer)!
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ (newline)
+ The barman sighs, then asks:
+ (prompt_string (ptr hero_name) 2 64 What is your name, then, hero?)
+ (var hero_name)?
+ (newline)
+ The barman looks surprised.
+ (newline)
+ (visit lower_price_of_booze (ptr price_of_booze) 4)
+ (newline)
+ "I have heard of you, (var hero_name)," the barman exclaims, "I have
+ a quest for you!"
+ (newline)
+ It's your turn to sigh.
+ (newline)
+ The barman hands you a bag, and says:
+ (newline)
+ "Take this pre-payment and head to the smithy."
+ (newline)
+ )
+
+* `(prompt_string (ptr hero_name) 2 64 What is your name, then, hero?)` prompts
+ the player with `What is your name, then, hero?` and expects a string input
+ with a size between `2` and `64` characters. This input is stored at `(ptr
+ hero_name)`, which means that `hero_name` takes that value.
+* The `lower_price_of_booze` sequence shows how pointers can be used to modify
+ variables outside of a sequence's range.
+* `(var price_of_booze)` is equivalent to `(at (ptr price_of_booze))`.
+
+Our hero, who'll obviously end up being the lost heir of some royal family,
+should already have good equipment. It would be useful to have a character
+sheet, so [let's create one](/learn/structures).
+
+----
+
+## Unchanged Files
+**actions.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero_money) (var cost))
+ )
+ )
+
+**falling_asleep.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero_money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
diff --git a/content/learn/07.structures/default.md b/content/learn/07.structures/default.md
new file mode 100644
index 0000000..a87af08
--- /dev/null
+++ b/content/learn/07.structures/default.md
@@ -0,0 +1,201 @@
+---
+menuTitle: Structures
+title: "Structuring the Data"
+weight: 7
+---
+In [the previous step](/learn/pointers), we added pointers, which made it
+possible to change data from other contexts/sequences. As we add more and more
+data, it becomes clear that some structuring is needed.
+
+Structures are types. Types must be declared before being used. This also
+prevents recursive types.
+
+The fields of a structure are initialized at the same time as the structure.
+
+Fields of a structure can be accessed in two ways: using the
+`struct_var.field_name` notation, or the `(field struct_var field_name)` one.
+If `struct_ptr` is a pointer to a structure, `struct_ptr.field_name` will also
+work.
+
+To set the value of a structure's fields, one can use the `set` instruction or,
+to set multiple fields at once, the `set_fields!` one. Note that omitting the
+`!` at the end of `set_fields` is also valid Fate, but performs a computation
+instead of a instruction: the structure is not modified, but a copy with the
+modifications performed is returned.
+
+**data.fate:**
+
+ (fate_version 1)
+
+ (declare_structure weapon
+ (text name)
+ (int attack)
+ (int precision)
+ )
+
+ (declare_structure armor
+ (text name)
+ (int defense)
+ )
+
+ (declare_structure character
+ (string name)
+ (int money)
+ (weapon weapon)
+ (armor armor)
+ )
+
+ (global character hero)
+
+ (set_fields! hero.weapon
+ (name (text "Legendary" sword))
+ (attack 3)
+ (precision 50)
+ )
+
+ (set_fields! hero.armor
+ (name (text "Refined" attire))
+ (defense 1)
+ )
+
+ (set hero.money 42)
+
+* `(text "Refined" attire)` generates a `text` value containing a textual
+ representation of the values passed as parameter. `"Refined" attire` is, by
+ itself, a `string`, and thus incompatible with the `text` field without a
+ conversion taking place.
+* Because `hero_money` and `hero_name` have been removed in favor of a
+ structure, they should be replaced in the other files by `hero.money` and
+ `hero.name`.
+
+With this, it is time for our hero to get some proper gear, [let's see what
+collection is available at the smithy](/learn/collections).
+
+----
+
+## (Mostly) Unchanged Files
+
+**get_a_refill.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+ (require actions.fate)
+
+ (define_sequence lower_price_of_booze
+ (
+ ((ptr int) price_pointer)
+ (int decrease)
+ )
+ Great! The price of booze just lowered from (at price_pointer)
+ (set (at price_pointer)
+ (-
+ (at price_pointer)
+ (var decrease)
+ )
+ )
+ to (at price_pointer)!
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ (newline)
+ The barman sighs, then asks:
+ (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+ (var hero.name)?
+ (newline)
+ The barman looks surprised.
+ (newline)
+ (visit lower_price_of_booze (ptr price_of_booze) 4)
+ (newline)
+ "I have heard of you, (var hero.name)," the barman exclaims, "I have
+ a quest for you!"
+ (newline)
+ It's your turn to sigh.
+ (newline)
+ The barman hands you a bag, and says:
+ (newline)
+ "Take this pre-payment and head to the smithy."
+ (newline)
+ )
+
+**actions.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero.money) (var cost))
+ )
+ )
+
+**falling_asleep.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero.money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
diff --git a/content/learn/08.collections/default.md b/content/learn/08.collections/default.md
new file mode 100644
index 0000000..b4bae98
--- /dev/null
+++ b/content/learn/08.collections/default.md
@@ -0,0 +1,408 @@
+---
+menuTitle: Collections
+title: "Collections"
+weight: 8
+---
+In [the previous step](/learn/structures), we added structures, which made
+having some amounts of data more manageable. Still, what about when we need to
+have hundreds of elements? Collections are here to manage that.
+
+Fate has three types of collections:
+* Sets, which only contain a single instance of each element, can only be used
+ to store elements of one of the comparable types (`string`, `text`, `int`,
+ `float`, `bool`) and pointers. The elements are automatically sorted in
+ ascending order.
+* Lists, which can be made for one of any type.
+* Cons, which are pairs of two elements of any types.
+
+There are [quite a few computations](/fate_v1/computations/collections) and
+[instructions](/fate_v1/instructions/collections) available to handle
+collections.
+
+Let's add a new file, `smithy_inventory.fate`:
+ (fate_version 1)
+
+ (require data.fate)
+
+ (global (list (cons weapon int)) smithy_weapons)
+ (global (list (cons weapon int)) smithy_armors)
+
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text An Iron Rod))
+ (attack 10)
+ (precision 70)
+ )
+ 176
+ )
+ smithy_weapons
+ )
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text A Magnificient Brick))
+ (attack 6)
+ (precision 90)
+ )
+ 110
+ )
+ smithy_weapons
+ )
+
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A raincoat?!))
+ (defense 7)
+ )
+ 160
+ )
+ smithy_armors
+ )
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A nice cape))
+ (defense 3)
+ )
+ 50
+ )
+ smithy_armors
+ )
+
+We'll also need the actual smithy scene, so let's put in another file,
+`smithy.fate`.
+
+**NOTE:** Don't worry if it looks awful at the moment, the next chapters are
+going to introduce a lot of things to make it *much*, *much* easier to write.
+
+**smithy.fate:**
+
+ (fate_version 1)
+
+ (require smithy_inventory.fate)
+
+ (define_sequence visit_smithy ()
+ ;; This thing is going to show up every time, which isn't great.
+ As you approach the smithy, you notice that no one's there. All the
+ wares are out for selling. It's almost as if this story didn't need
+ more examples of lengthy dialogues.
+ (newline)
+ ;; We'll want to start here the next time we enter this sequence.
+ You have (var hero.money) coins.
+ (newline)
+ What will you look at?
+ (player_choice
+ (
+ ( Let's see the weapons )
+ (jump_to see_weapons)
+ )
+ (
+ ( Let's see the armors )
+ (jump_to see_armors)
+ )
+ (
+ ( Nothing, let's go back to the bar )
+ )
+ )
+ )
+
+ (define_sequence see_weapons ()
+ ;; We'll soon replace this mess with something way better.
+
+ (local text weapon_a_label)
+ (local text weapon_b_label)
+
+ (visit get_weapon_label
+ (access smithy_weapons 0)
+ (ptr weapon_a_label)
+ )
+ (visit get_weapon_label
+ (access smithy_weapons 1)
+ (ptr weapon_b_label)
+ )
+
+ (player_choice
+ (
+ ( (var weapon_a_label) )
+ (visit buy_weapon (access smithy_weapons 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (var weapon_a_label) )
+ (visit buy_weapon (access smithy_weapons 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ (define_sequence see_armors ()
+ ;; We'll soon replace this mess with something way better.
+
+ (local text armor_a_label)
+ (local text armor_b_label)
+
+ (visit get_armor_label
+ (access smithy_armors 0)
+ (ptr armor_a_label)
+ )
+ (visit get_armor_label
+ (access smithy_armors 1)
+ (ptr armor_b_label)
+ )
+
+ (player_choice
+ (
+ ( (var armor_a_label) )
+ (visit buy_armor (access smithy_armors 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (var armor_a_label) )
+ (visit buy_armor (access smithy_armors 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ ;; A terrible way to get labels
+ (define_sequence get_weapon_label
+ (
+ ((cons weapon int) weapon_offer)
+ ((ptr text) label)
+ )
+ (local weapon weapon)
+ (local int price)
+
+ (set weapon (car weapon_offer))
+ (set price (cdr weapon_offer))
+
+ (set (at label)
+ (
+ Buy "(var weapon.name)" \(attack: (var weapon.attack),
+ precision: (var weapon.precision)\) for (var price) coins.
+ )
+ )
+ )
+
+ ;; A terrible way to get labels
+ (define_sequence get_armor_label
+ (
+ ((cons armor int) armor_offer)
+ ((ptr text) label)
+ )
+ (local armor armor)
+ (local int price)
+
+ (set armor (car armor_offer))
+ (set price (cdr armor_offer))
+
+ (set (at label)
+ (
+ Buy "(var armor.name)" \(defense: (var armor.defense)\),
+ for (var price) coins.
+ )
+ )
+ )
+
+ (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+ ;; We can't even deny a sell yet...
+ (set hero.weapon (car weapon))
+ Equipped (var hero.weapon.name).
+ (newline)
+ )
+
+ (define_sequence buy_armor ( ((cons armor int) armor) )
+ ;; We can't even deny a sell yet...
+ (set hero.armor (car armor))
+ Equipped (var hero.armor.name).
+ (newline)
+ )
+
+* `(list (cons weapon int))` indicates a list of `weapon` and `int` pairs.
+* `(add! something smithy_weapons)` adds `something` to the `smithy_weapons`
+ collection. Without the `!`, this would be a computation returning a copy of
+ `smithy_weapons` with the added weapon, but no modification of the
+ `smithy_weapons` collection itself would occur.
+* `(cons something something_else)` creates a pair with these two elements.
+* `(car weapon_offer)` returns the first element of the `weapon_offer` pair.
+* `(cdr weapon_offer)` returns the second element of the `weapon_offer` pair.
+
+Overall, the `smithy.fate` file is a mess. Let's start cleaning it up. We'll
+use loops, conditionals, and lambda functions to make it much cleaner. Let's
+start with the least expected one: [lambda functions](/learn/lambdas).
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+ (fate_version 1)
+
+ (declare_structure weapon
+ (text name)
+ (int attack)
+ (int precision)
+ )
+
+ (declare_structure armor
+ (text name)
+ (int defense)
+ )
+
+ (declare_structure character
+ (string name)
+ (int money)
+ (weapon weapon)
+ (armor armor)
+ )
+
+ (global character hero)
+
+ (set_fields! hero.weapon
+ (name (text "Legendary" sword))
+ (attack 3)
+ (precision 50)
+ )
+
+ (set_fields! hero.armor
+ (name (text "Refined" attire))
+ (defense 1)
+ )
+
+ (set hero.money 42)
+
+**get_a_refill.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+ (require actions.fate)
+
+ (define_sequence lower_price_of_booze
+ (
+ ((ptr int) price_pointer)
+ (int decrease)
+ )
+ Great! The price of booze just lowered from (at price_pointer)
+ (set (at price_pointer)
+ (-
+ (at price_pointer)
+ (var decrease)
+ )
+ )
+ to (at price_pointer)!
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ (newline)
+ The barman sighs, then asks:
+ (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+ (var hero.name)?
+ (newline)
+ The barman looks surprised.
+ (newline)
+ (visit lower_price_of_booze (ptr price_of_booze) 4)
+ (newline)
+ "I have heard of you, (var hero.name)," the barman exclaims, "I have
+ a quest for you!"
+ (newline)
+ It's your turn to sigh.
+ (newline)
+ The barman hands you a bag, and says:
+ (newline)
+ "Take this pre-payment and head to the smithy."
+ (newline)
+ )
+
+**actions.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero.money) (var cost))
+ )
+ )
+
+**falling_asleep.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero.money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
diff --git a/content/learn/09.lambdas/default.md b/content/learn/09.lambdas/default.md
new file mode 100644
index 0000000..a2e6c24
--- /dev/null
+++ b/content/learn/09.lambdas/default.md
@@ -0,0 +1,366 @@
+---
+menuTitle: Lambda Functions
+title: "Lambda Functions"
+weight: 9
+---
+In [the previous step](/learn/collections), we added collections, which made
+having large amounts of data possible. We also ended up with a really ugly file,
+`smithy.fate`, which had way too much code to be readable. We'll start by
+removing the label generation sequences. Instead, we'll use lambda functions.
+
+A lambda function is computation description that can be stored for later use.
+It can also take parameters. Since it's a computation, it cannot alter the
+memory by itself, nor can it contain any instructions.
+
+**smithy.fate:**
+
+ (fate_version 1)
+
+ (require smithy_inventory.fate)
+
+ (global (lambda text ((cons weapon int))) get_weapon_offer_label)
+ (global (lambda text ((cons armor int))) get_armor_offer_label)
+
+ (set get_weapon_offer_label
+ (lambda
+ ( ((cons weapon int) offer) )
+ (let
+ (
+ (weapon (car offer))
+ (price (cdr offer))
+ )
+ (text
+ Buy "(var weapon.name)" \(attack: (var weapon.attack),
+ precision: (var weapon.precision)\) for (var price) coins.
+ )
+ )
+ )
+ )
+
+ (set get_armor_offer_label
+ (lambda
+ ( ((cons armor int) offer) )
+ (let
+ (
+ (armor (car offer))
+ (price (cdr offer))
+ )
+ (text
+ Buy "(var armor.name)" \(defense: (var armor.defense)\),
+ for (var price) coins.
+ )
+ )
+ )
+ )
+
+ (define_sequence visit_smithy ()
+ ;; This thing is going to show up every time, which isn't great.
+ As you approach the smithy, you notice that no one's there. All the
+ wares are out for selling. It's almost as if this story didn't need
+ more examples of lengthy dialogues.
+ (newline)
+ ;; We'll want to start here the next time we enter this sequence.
+ You have (var hero.money) coins.
+ (newline)
+ What will you look at?
+ (player_choice
+ (
+ ( Let's see the weapons )
+ (jump_to see_weapons)
+ )
+ (
+ ( Let's see the armors )
+ (jump_to see_armors)
+ )
+ (
+ ( Nothing, let's go back to the bar )
+ )
+ )
+ )
+
+ (define_sequence see_weapons ()
+ ;; Still can be improved.
+ (player_choice
+ (
+ ( (eval get_weapon_offer_label (access smithy_weapons 0)) )
+ (visit buy_weapon (access smithy_weapons 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (eval get_weapon_offer_label (access smithy_weapons 1)) )
+ (visit buy_weapon (access smithy_weapons 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ (define_sequence see_armors ()
+ ;; Still can be improved.
+ (player_choice
+ (
+ ( (eval get_armor_offer_label (access smithy_armors 0)) )
+ (visit buy_armor (access smithy_armors 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (eval get_armor_offer_label (access smithy_armors 1)) )
+ (visit buy_armor (access smithy_armors 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+ ;; We can't even deny a sell yet...
+ (set hero.weapon (car weapon))
+ Equipped (var hero.weapon.name).
+ (newline)
+ )
+
+ (define_sequence buy_armor ( ((cons armor int) armor) )
+ ;; We can't even deny a sell yet...
+ (set hero.armor (car armor))
+ Equipped (var hero.armor.name).
+ (newline)
+ )
+
+* `(lambda return_type (param_type0 param_type1 ...))` is the type corresponding
+ to a lambda function returning a value of type `return_type` and taking as
+ parameters values of types `param_type0`, `param_type1`, ...
+* `(eval variable_name param0 param1 ...)` computes and returns the result of
+ the lambda function stored in `variable_name` by giving it as parameter the
+ values `param0`, `param1`, ...
+* `(let ( (name0 val0) ... ) computation)` computes and returns `computation` as
+ if `name0` was a defined variable of value `val0`.
+
+Lambda functions are *very* useful. They can be used to manipulate lists in many
+ways. They can also be used to abstract away some complicated computation.
+
+This was a first step toward cleaning up `smithy.fate`. Next, we'll use
+[conditions](/learn/conditions) to improve things further.
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+ (fate_version 1)
+
+ (declare_structure weapon
+ (text name)
+ (int attack)
+ (int precision)
+ )
+
+ (declare_structure armor
+ (text name)
+ (int defense)
+ )
+
+ (declare_structure character
+ (string name)
+ (int money)
+ (weapon weapon)
+ (armor armor)
+ )
+
+ (global character hero)
+
+ (set_fields! hero.weapon
+ (name (text "Legendary" sword))
+ (attack 3)
+ (precision 50)
+ )
+
+ (set_fields! hero.armor
+ (name (text "Refined" attire))
+ (defense 1)
+ )
+
+ (set hero.money 42)
+
+**get_a_refill.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+ (require actions.fate)
+
+ (define_sequence lower_price_of_booze
+ (
+ ((ptr int) price_pointer)
+ (int decrease)
+ )
+ Great! The price of booze just lowered from (at price_pointer)
+ (set (at price_pointer)
+ (-
+ (at price_pointer)
+ (var decrease)
+ )
+ )
+ to (at price_pointer)!
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ (newline)
+ The barman sighs, then asks:
+ (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+ (var hero.name)?
+ (newline)
+ The barman looks surprised.
+ (newline)
+ (visit lower_price_of_booze (ptr price_of_booze) 4)
+ (newline)
+ "I have heard of you, (var hero.name)," the barman exclaims, "I have
+ a quest for you!"
+ (newline)
+ It's your turn to sigh.
+ (newline)
+ The barman hands you a bag, and says:
+ (newline)
+ "Take this pre-payment and head to the smithy."
+ (newline)
+ )
+
+**actions.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero.money) (var cost))
+ )
+ )
+
+**falling_asleep.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero.money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
+
+**smithy_inventory.fate:**
+ (fate_version 1)
+
+ (require data.fate)
+
+ (global (list (cons weapon int)) smithy_weapons)
+ (global (list (cons weapon int)) smithy_armors)
+
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text An Iron Rod))
+ (attack 10)
+ (precision 70)
+ )
+ 176
+ )
+ smithy_weapons
+ )
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text A Magnificient Brick))
+ (attack 6)
+ (precision 90)
+ )
+ 110
+ )
+ smithy_weapons
+ )
+
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A raincoat?!))
+ (defense 7)
+ )
+ 160
+ )
+ smithy_armors
+ )
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A nice cape))
+ (defense 3)
+ )
+ 50
+ )
+ smithy_armors
+ )
diff --git a/content/learn/10.conditions/default.md b/content/learn/10.conditions/default.md
new file mode 100644
index 0000000..8a4f339
--- /dev/null
+++ b/content/learn/10.conditions/default.md
@@ -0,0 +1,402 @@
+---
+menuTitle: Conditionals
+title: "Expressing Conditions"
+weight: 10
+---
+In [the previous step](/learn/lambdas), we added lambda functions, which made
+it possible to store computation descriptions for later use. The `smithy.fate`
+is still a mess, and we couldn't handle the hero not having enough money for a
+purchase. Let's add conditions to our story.
+
+Fate has quite a few conditional constructs. `if_else`, `switch`, and `cond`
+can be used both as instruction and computation. `if` is only for instructions.
+* `if_else` selects between two options according to a given `bool` value.
+* `switch` has a list of options, each of which is associated to a value.
+ It selects the first listed option whose value is equal to the targeted
+ value. When used as a computation, a default option with no associated value
+ must be provided.
+* `cond` has a list of options, each of which is associated to a `bool`
+ computation. The first listed option for which the computation yields `(true)`
+ is chosen. In the case of a computation, the last option is chosen even as a
+ default value. With instructions, if no option has its condition satisfied,
+ nothing happens.
+* `if` is only for instructions, and applies its content only if the condition
+ is verified.
+
+All of these can be used within a `player_choice` to control what options are
+available.
+
+**smithy.fate:**
+
+ (fate_version 1)
+
+ (require smithy_inventory.fate)
+
+ (global (lambda text ((cons weapon int))) get_weapon_offer_label)
+ (global (lambda text ((cons armor int))) get_armor_offer_label)
+
+ (set get_weapon_offer_label
+ (lambda
+ ( ((cons weapon int) offer) )
+ (let
+ (
+ (weapon (car offer))
+ (price (cdr offer))
+ )
+ (text
+ Buy "(var weapon.name)" \(attack: (var weapon.attack),
+ precision: (var weapon.precision)\) for (var price) coins.
+ )
+ )
+ )
+ )
+
+ (set get_armor_offer_label
+ (lambda
+ ( ((cons armor int) offer) )
+ (let
+ (
+ (armor (car offer))
+ (price (cdr offer))
+ )
+ (text
+ Buy "(var armor.name)" \(defense: (var armor.defense)\),
+ for (var price) coins.
+ )
+ )
+ )
+ )
+
+ (global bool has_visited_smithy)
+
+ (set has_visited_smithy (false))
+
+ (define_sequence visit_smithy ()
+ (if (not (var has_visited_smithy))
+ As you approach the smithy, you notice that no one's there. All
+ the wares are out for selling. It's almost as if this story
+ didn't need more examples of lengthy dialogues.
+ (newline)
+ (set has_visited_smithy (true))
+ )
+ You have (var hero.money) coins.
+ (newline)
+ What will you look at?
+ (player_choice
+ (
+ ( Let's see the weapons )
+ (jump_to see_weapons)
+ )
+ (
+ ( Let's see the armors )
+ (jump_to see_armors)
+ )
+ (
+ ( Nothing, let's go back to the bar )
+ )
+ )
+ )
+
+ (define_sequence see_weapons ()
+ ;; Still can be improved.
+ (player_choice
+ (
+ ( (eval get_weapon_offer_label (access smithy_weapons 0)) )
+ (visit buy_weapon (access smithy_weapons 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (eval get_weapon_offer_label (access smithy_weapons 1)) )
+ (visit buy_weapon (access smithy_weapons 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ (define_sequence see_armors ()
+ ;; Still can be improved.
+ (player_choice
+ (
+ ( (eval get_armor_offer_label (access smithy_armors 0)) )
+ (visit buy_armor (access smithy_armors 0))
+ (jump_to visit_smithy)
+ )
+ (
+ ( (eval get_armor_offer_label (access smithy_armors 1)) )
+ (visit buy_armor (access smithy_armors 1))
+ (jump_to visit_smithy)
+ )
+ (
+ ( Nevermind )
+ (jump_to visit_smithy)
+ )
+ )
+ )
+
+ (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+ ;; We can't even deny a sell yet...
+ (local int money_after)
+
+ (set money_after (- (var hero.money) (cdr weapon)))
+
+ (if_else (< (var money_after) 0)
+ (
+ You can't afford that.
+ (newline)
+ You would need (abs (var money_after)) more coins.
+ )
+ (
+ (set hero.weapon (car weapon))
+ (set hero.money (var money_after))
+ Equipped (var hero.weapon.name).
+ )
+ )
+ (newline)
+ )
+
+ (define_sequence buy_armor ( ((cons armor int) armor) )
+ ;; We can't even deny a sell yet...
+ (local int money_after)
+
+ (set money_after (- (var hero.money) (cdr armor)))
+
+ (if_else (< (var money_after) 0)
+ (
+ You can't afford that.
+ (newline)
+ You would need (abs (var money_after)) more coins.
+ )
+ (
+ (set hero.armor (car armor))
+ (set hero.money (var money_after))
+ Equipped (var hero.armor.name).
+ )
+ )
+ (newline)
+ )
+
+
+This was a first step toward cleaning up `smithy.fate`. Next, we'll use
+[conditions](/learn/conditions) to improve things further.
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+ (fate_version 1)
+
+ (declare_structure weapon
+ (text name)
+ (int attack)
+ (int precision)
+ )
+
+ (declare_structure armor
+ (text name)
+ (int defense)
+ )
+
+ (declare_structure character
+ (string name)
+ (int money)
+ (weapon weapon)
+ (armor armor)
+ )
+
+ (global character hero)
+
+ (set_fields! hero.weapon
+ (name (text "Legendary" sword))
+ (attack 3)
+ (precision 50)
+ )
+
+ (set_fields! hero.armor
+ (name (text "Refined" attire))
+ (defense 1)
+ )
+
+ (set hero.money 42)
+
+**get_a_refill.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+ (require actions.fate)
+
+ (define_sequence lower_price_of_booze
+ (
+ ((ptr int) price_pointer)
+ (int decrease)
+ )
+ Great! The price of booze just lowered from (at price_pointer)
+ (set (at price_pointer)
+ (-
+ (at price_pointer)
+ (var decrease)
+ )
+ )
+ to (at price_pointer)!
+ )
+
+ (define_sequence get_a_refill ()
+ (local int price_of_booze)
+
+ (set price_of_booze 12)
+
+ Staring straight at the barman, you raise your glass and proclaim:
+ (newline)
+ "This soon-to-be world savior needs more booze!"
+ (newline)
+ The barman's lack of reaction is disappointing, but seeing the beer
+ being poured does help improve the mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit pay (var price_of_booze))
+ (newline)
+ The barman sighs, then asks:
+ (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+ (var hero.name)?
+ (newline)
+ The barman looks surprised.
+ (newline)
+ (visit lower_price_of_booze (ptr price_of_booze) 4)
+ (newline)
+ "I have heard of you, (var hero.name)," the barman exclaims, "I have
+ a quest for you!"
+ (newline)
+ It's your turn to sigh.
+ (newline)
+ The barman hands you a bag, and says:
+ (newline)
+ "Take this pre-payment and head to the smithy."
+ (newline)
+ )
+
+**actions.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence pay ( (int cost) )
+ (set hero_money
+ (- (var hero.money) (var cost))
+ )
+ )
+
+**falling_asleep.fate:**
+
+ (fate_version 1)
+
+ (require data.fate)
+
+ (define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you promptly
+ fall asleep.
+ (newline)
+ ...
+ (newline)
+ Upon waking up, your hard-trained reflexes inform you that someone
+ stole all your money.
+ (set hero.money 0)
+ (newline)
+ This set-back was more than you could take. You give up on this
+ barely coherent story.
+ (end)
+ )
+
+**main.fate:**
+
+ (fate_version 1)
+
+ Once upon a time, starting a story with these words wasn't considered
+ a cliche. Starting in a tavern might also not be seen as very
+ original. Having the main character be an street orphan, raised by
+ some mysterious sage all to end up as a mercenary with an uncommonly
+ strong sense of honor probably isn't going to lead to any praises for
+ novelty either. Maybe you should drink to that.
+ (newline)
+ Or maybe you shouldn't. This isn't your first mug. Not your second
+ either. Drinking to forget that you are a stereotypical hero isn't
+ going to solve anything. Worse, the alcoholic trait is part of the
+ image.
+ (newline)
+ As you contemplate your own pointless description, your gaze leaves
+ what turns out to be an already empty glass in your hand and finds the
+ barman.
+
+ (player_choice
+ (
+ ( Ask the barman for a refill )
+ (visit get_a_refill)
+ )
+ (
+ ( Fall asleep )
+ (jump_to fall_asleep)
+ )
+ )
+
+ (require get_a_refill.fate)
+ (require falling_asleep.fate)
+
+ (end)
+
+**smithy_inventory.fate:**
+ (fate_version 1)
+
+ (require data.fate)
+
+ (global (list (cons weapon int)) smithy_weapons)
+ (global (list (cons weapon int)) smithy_armors)
+
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text An Iron Rod))
+ (attack 10)
+ (precision 70)
+ )
+ 176
+ )
+ smithy_weapons
+ )
+ (add!
+ (cons
+ (set_fields (default weapon)
+ (name (text A Magnificient Brick))
+ (attack 6)
+ (precision 90)
+ )
+ 110
+ )
+ smithy_weapons
+ )
+
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A raincoat?!))
+ (defense 7)
+ )
+ 160
+ )
+ smithy_armors
+ )
+ (add!
+ (cons
+ (set_fields (default armor)
+ (name (text A nice cape))
+ (defense 3)
+ )
+ 50
+ )
+ smithy_armors
+ )
diff --git a/content/learn/_index.md b/content/learn/_index.md
new file mode 100644
index 0000000..62706fc
--- /dev/null
+++ b/content/learn/_index.md
@@ -0,0 +1,8 @@
+---
+title: "Starting"
+menuTitle: "Learn"
+chapter: true
+---
+# Writing a story in Fate.
+This chapter provides an example of narrative written in Fate. Concepts are
+introduced progressively.