summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'content/learn/11.loops/default.md')
-rw-r--r--content/learn/11.loops/default.md457
1 files changed, 457 insertions, 0 deletions
diff --git a/content/learn/11.loops/default.md b/content/learn/11.loops/default.md
new file mode 100644
index 0000000..26aa949
--- /dev/null
+++ b/content/learn/11.loops/default.md
@@ -0,0 +1,457 @@
+---
+menuTitle: Loops
+title: "Doing Things Over and Over"
+weight: 11
+---
+[The previous step](/learn/conditions) introduced conditions, making it
+possible to change the flow of execution according to the value of variables.
+In this step, we look at loops, which will make instructions be executed a
+certain number time depending on a condition, or just for each element of a
+collection.
+
+Fate offers the following loop constructs:
+* `while`, to execute instructions if and as many time as a condition is
+ verified.
+* `do_while`, to execute instructions at least once, then as many time as a
+ condition is verified.
+* `for`, a loop that has pre-instructions, a condition to verify before each
+ execution of the main body, the main body of instructions itself, then
+ post-instructions, which are executed before checking the condition.
+* `for_each`, a loop that executes for each member of a collection.
+
+The `for_each` loops can also be used within a `player_choice!` to control what
+options are available.
+
+Let's change how armors and weapons are displayed to the player:
+
+**smithy.fate:**
+{{< fatecode >}}(fate_version 1)
+(require smithy_inventory.fate)
+
+;; Maybe it would be better to put this in a different file, but oh well...
+(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)" (lp)attack: (var weapon.attack),
+ precision: (var weapon.precision)(rp) 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)" (lp)defense: (var armor.defense)(rp),
+ for (var price) coins.
+ )
+ )
+ )
+)
+
+(global bool has_visited_smithy (false))
+
+(define_sequence visit_smithy ()
+ (if (not 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!
+ (option ( Let's see the weapons )
+ (jump_to! see_weapons)
+ )
+ (option ( Let's see the armors )
+ (jump_to! see_armors)
+ )
+ (option ( Nothing, let's go back to the bar )
+ )
+ )
+)
+
+(define_sequence see_weapons ()
+ (player_choice!
+ (foreach smithy_weapons weapon
+ (option ( (eval get_weapon_offer_label weapon) )
+ (visit! buy_weapon weapon)
+ )
+ )
+ (option ( Nevermind ) )
+ )
+ (jump_to! visit_smithy)
+)
+
+(define_sequence see_armors ()
+ ;; Not as good as a for_each in this case, but let's use a for to show how
+ ;; it can be done.
+
+ (player_choice!
+ (for
+ ( (i 0) )
+ (< i (list:size smithy_armors))
+ ( (i (+ i 1)) )
+
+ (option ( (eval get_armor_offer_label (list:access i smithy_armors)) )
+ (visit! buy_armor (list:access i smithy_armors))
+ )
+ )
+ (option ( Nevermind ) )
+ )
+ (jump_to! visit_smithy)
+)
+
+(define_sequence buy_weapon ( ((cons #weapon int) weapon) )
+ (local int money_after)
+
+ (set! money_after (- hero.money (cdr weapon)))
+
+ (if_else (< money_after 0)
+ (
+ You can't afford that.
+ (newline)
+ You would need (abs money_after) more coins.
+ )
+ (
+ (set! hero.weapon (car weapon))
+ (set! hero.money money_after)
+ Equipped (var hero.weapon.name).
+ )
+ )
+ (newline)
+)
+
+(define_sequence buy_armor ( ((cons #armor int) armor) )
+ (local int money_after)
+
+ (set! money_after (- hero.money (cdr armor)))
+
+ (if_else (< money_after 0)
+ (
+ You can't afford that.
+ (newline)
+ You would need (abs money_after) more coins.
+ )
+ (
+ (set! hero.armor (car armor))
+ (set! hero.money money_after)
+ Equipped (var hero.armor.name).
+ )
+ )
+ (newline)
+)
+{{< /fatecode >}}
+
+Let's also look at a loop not within a `player_choice!` construct. We'll have
+the player try to count sheeps:
+
+**falling_asleep.fate:**
+{{< fatecode >}}(fate_version 1)
+(require data.fate)
+
+(global (list string) affirmative_messages)
+
+(list:add!
+ Indeed.
+ (string Sounds about right.)
+ Yes.
+ Agreed.
+ (string That's correct.)
+ True.
+ Undoubtedly.
+ Certainly.
+ (string Without a doubt.)
+ Correct.
+ Affirmative.
+ affirmative_messages
+)
+
+(global (lambda string ()) get_affirmative_message
+ (lambda ()
+ (list:access
+ (rand 0 (- (list:size affirmative_messages) 1))
+ affirmative_messages
+ )
+ )
+)
+
+(define_sequence count_sheeps ()
+ (local int sheeps 0)
+
+ You start imagining some pasture with no sheeps.
+
+ (while (< sheeps 100)
+ (local int player_guess)
+ (local int new_sheeps (rand 1 20))
+
+ (if_else (= new_sheeps 1)
+ ( A single sheep appears. )
+ ( (var new_sheeps) sheeps appear. )
+ )
+
+ (set! sheeps (+ sheeps new_sheeps))
+
+ (prompt_integer! (ptr player_guess) 0 120 How many sheeps are there now?)
+
+ (if_else (= sheeps player_guess)
+ (eval get_affirmative_message)
+ (
+ No. It was (var sheeps), you're pretty sure. The doubt wakes you up.
+ (newline)
+ (break!)
+ )
+ )
+ (newline)
+ )
+)
+
+(define_sequence fall_asleep ()
+ Deciding to break away from the expected storyline, you attempt to fall
+ asleep. It's not easy, though. Maybe counting sheeps would help?
+
+ (visit! count_sheeps)
+ (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 handle. You give up on this barely
+ coherent story.
+ (end!)
+)
+{{< /fatecode >}}
+
+[The next step of this tutorial](/learn/going_deeper) goes deeper into all the
+concepts that were already explored.
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+{{< fatecode >}}(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)
+
+(struct:set_fields! hero.weapon
+ (name (text "Legendary" sword))
+ (attack 3)
+ (precision 50)
+)
+
+(struct:set_fields! hero.armor
+ (name (text "Refined" attire))
+ (defense 1)
+)
+
+(set! hero.money 200)
+
+{{< /fatecode >}}
+
+**get_a_refill.fate:**
+{{< fatecode >}}(fate_version 1)
+
+(require data.fate)
+(require actions.fate)
+(require smithy.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) 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 improve your mood.
+ (newline)
+ Satisfied, you hand the barman (var price_of_booze) copper coins.
+ (visit! pay price_of_booze)
+ The barman sighs, then asks:
+ (prompt_string! (ptr hero.name) 2 64 What is your name, then, hero?)
+ "(var hero.name)?"
+ (newline)
+ (switch hero.name
+ (Fred
+ You brace for the inevitable mockery.
+ )
+ (Link
+ You show the back of your hand to the barman.
+ )
+ ((string Lancelot of Camelot)
+ You place halves of a coconut shell on the bar.
+ )
+ 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)
+ (visit! visit_smithy)
+)
+{{< /fatecode >}}
+
+**actions.fate:**
+{{< fatecode >}}(fate_version 1)
+
+(require data.fate)
+
+(define_sequence pay ( (int cost) )
+ (set! hero.money (- hero.money cost))
+)
+{{< /fatecode >}}
+
+**main.fate:**
+{{< fatecode >}}(fate_version 1)
+
+(require get_a_refill.fate)
+(require falling_asleep.fate)
+
+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!
+ (option ( Ask the barman for a refill )
+ (visit! get_a_refill)
+ )
+ (option ( Fall asleep )
+ (jump_to! fall_asleep)
+ )
+)
+
+;; Let's analyze how well the player did
+(cond
+ ((> hero.weapon.attack 7)
+ Your feel ready to strike down any challenge.
+ )
+ ((>= hero.armor.defense 7)
+ Your feel invincible.
+ )
+ ((>= hero.money 50)
+ You feel good about having spent your coins sparingly.
+ )
+ ((true)
+ You feel like you wasted your evening.
+ )
+)
+(end!)
+{{< /fatecode >}}
+
+**smithy_inventory.fate:**
+{{< fatecode >}}(fate_version 1)
+
+(require data.fate)
+
+(global (list (cons #weapon int)) smithy_weapons)
+(global (list (cons #armor int)) smithy_armors)
+
+(list:add!
+ (cons
+ (struct:set_fields (default #weapon)
+ (name (text An Iron Rod))
+ (attack 10)
+ (precision 70)
+ )
+ 176
+ )
+ smithy_weapons
+)
+(list:add!
+ (cons
+ (struct:set_fields (default #weapon)
+ (name (text A Magnificient Brick))
+ (attack 6)
+ (precision 90)
+ )
+ 110
+ )
+ smithy_weapons
+)
+
+(list:add!
+ (cons
+ (struct:set_fields (default #armor)
+ (name (text A raincoat?!))
+ (defense 7)
+ )
+ 160
+ )
+ (cons
+ (struct:set_fields (default #armor)
+ (name (text A nice cape))
+ (defense 3)
+ )
+ 50
+ )
+ smithy_armors
+)
+{{< /fatecode >}}