diff --git a/README.md b/README.md
index c7897dcd..e77afb61 100644
--- a/README.md
+++ b/README.md
@@ -5,4 +5,4 @@
![Enu Screenshot](media/screenshot_2.png)
Enu lets you build and explore worlds using a familiar block-building interface and a Logo inspired API.
-It aspires to make 3D development more accessible, and will eventually be usable to create standalone games.
+It tries to make 3D development more accessible, and will eventually be usable to create standalone games.
diff --git a/docs/book.nim b/docs/book.nim
index 74b00f34..22a770ee 100644
--- a/docs/book.nim
+++ b/docs/book.nim
@@ -2,31 +2,27 @@ import nimibook
var book = init_book_with_toc:
section "Introduction", "intro":
- entry "Goals", "intro/goals"
- entry "Demos", "intro/demos"
+ entry "Examples", "intro/examples"
entry "Install or Build", "intro/building"
-
- section "Basics", "basics":
- entry "Controls and Usage", "basics/controls"
- entry "Programming", "basics/programming"
- entry "Config", "basics/config"
+ entry "Controls and Usage", "intro/controls"
+ entry "Multiplayer (Experimental)", "intro/multiplayer"
+ entry "Config", "intro/config"
section "Coding Enu", "coding":
entry "Concepts", "coding/concepts"
- entry "Commands", "coding/commands"
+ entry "Built-in Commands", "coding/commands"
entry "Shorthand Commands", "coding/shorthand"
entry "Random Numbers", "coding/random_numbers"
- section "Action Loops", "action_loops":
- entry "Actions", "action_loops/actions"
- entry "Loops", "action_loops/loops"
- entry "Arrows", "action_loops/arrows"
- entry "Child Loops", "action_loops/child_loops"
- entry "More About Action Loops", "action_loops/more"
-
- section "Examples", "examples":
- entry "Shapes", "example/shapes"
+ section "Command Loops", "command_loops":
+ entry "Writing Commands", "command_loops/commands"
+ entry "Arrows", "command_loops/arrows"
+ entry "Loops", "command_loops/loops"
+ entry "Child Loops", "command_loops/child_loops"
+ entry "More About Command Loops", "command_loops/more"
+ entry "Goals", "goals"
+ entry "Demos", "demos"
entry "TODO", "todo"
nimibook_cli(book)
diff --git a/docs/book/action_loops.nim b/docs/book/action_loops.nim
deleted file mode 100644
index f9955764..00000000
--- a/docs/book/action_loops.nim
+++ /dev/null
@@ -1,2 +0,0 @@
-import enuib
-md "action_loops.md"
diff --git a/docs/book/action_loops/actions.nim b/docs/book/action_loops/actions.nim
deleted file mode 100644
index 09b15f16..00000000
--- a/docs/book/action_loops/actions.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-import nimib, nimibook, pretty
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: """
-
-# Actions
-
-Procedures/functions in Enu are referred to as actions, mainly to avoid explaining the term procedure, subroutine, or
-function, and to tie them to [Action Loops](#action-loops) defined below. Their syntax resembles markdown lists, and
-have the same parameter rules as [prototype](#prototypes) names. That is, mostly the same
-as Nim procs, but types can be omitted, making the parameter implicitly `auto`.
-
-```nim
-- hello(name):
- echo "hello ", name
-
-- goodbye(name = "Vin"):
- echo "goodbye ", name
-
-hello "Claire"
-goodbye "Cal"
-```
-
-Action parameters are automatically shadowed by a variable with the same name and value, making them mutable within the
-action. Enu tries to avoid the concept of immutable values.
-
-It's also possible to specify a return type between the closing bracket of the parameter list and the colon:
-
-```nim
-- hello(name) string:
- "hello " & $name
-
-echo hello("Scott")
-```
-
-However, at this point it's probably better to use a `proc`.
-
-# Action Loops
-
-*Note for Nimions: Action Loops are state machines, and any proc can be a state. If the proc has a return value it will
-be discarded.*
-
-Action Loops can help control the complexity of the logic for your units. They allow you to run complicated lists of
-actions and switch between them easily when situations change.
-
-You can create your own [actions](#actions), or you can call any of the built-in Enu [commands](#commands) like
-`forward`, `back`, `turn`, `sleep`, etc.
-
-"""
-nb_save
diff --git a/docs/book/action_loops/child_loops.nim b/docs/book/action_loops/child_loops.nim
deleted file mode 100644
index 2c1b3cf9..00000000
--- a/docs/book/action_loops/child_loops.nim
+++ /dev/null
@@ -1,73 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: """
-
-# Child Loops
-
-Actions are generally just a simple lists of commands. It's fine to put logic in them, but anything complicated
-will quickly get unwieldy. Imagine we have a unit that performs two complicated actions, `find_treasure` and
-`fight_monster`. `find_treasure` might need to `navigate` an area, `locate` items of interest, `interact` with them,
-then return to `home_base` to deposit them. `fight_monster` could require actions like `evade`, `attack`, `hide`, and
-`flee`.
-
-Combining all of this in a single action loop would give us a lot of actions, many of which are mostly unrelated,
-and managing our switches could get very complicated. However, making them entirely separate isn't ideal either, as
-there's probably some common functionality between them (die if our health gets too low, respawn if we get stuck). We
-also need to switch between the two actions.
-
-A good way to manage this is by making `find_treasure` and `fight_monster` child loops rather than regular actions.
-We can treat them like regular actions in our main `loop`, but when we switch to them they'll be able to perform
-more sophisticated logic than a normal action could. In addition, our main loop will continue to run along side
-the the child loop, so we can quickly switch out of the child loop with a big switch arrow `==>` in response to
-certain conditions, without either loop needing to worry about higher level concerns.
-
-Our main loop could look something like this:
-
-```nim
-loop find_treasure:
- # our treasure logic goes here. This loop doesn't have an exit condition.
- if treasure_found:
- # We found it. Start looking again. This will switch from any action apart
- # from `look_for_chest`
- others ==> look_for_chest
-
-loop fight_monster:
- # fight logic here. This loop should exit when the monster dies
- if monster.dead:
- any ==> nil
-
-loop:
- # We want our unit to find_treasure indefinately. `find_treasure` doesn't exit (switch to nil)
- nil -> find_treasure
- if monster.near:
- # find_treasure doesn't need to know anything about monsters. We can break out of it
- # with a big switch arrow if we encounter one.
- find_treasure ==> fight_monster
-
- # fight_monster does have an exit condition (the monster dies), so we can wait for it to finish
- # using the little switch arrow, then go back to finding treasure.
- fight_monster -> find_treasure
-
- if health == 0:
- # if our health drops to 0, it doesn't matter what else we're doing. Die immediately. Break out
- # of any action (except die) with a big switch arrow.
- (any, -die) ==> die
-
- # this would also work:
- # others ==> die
- if stuck:
- # respawn if we're stuck, but only from our two child loops. We don't want to respawn if we're
- # already respawning, or we're dead. Implementation of `respawn` not shown.
- (find_treasure, fight_monster) ==> respawn
-
- # we're done respawning. Treasure time!
- respawn -> find_treasure
-```
-
-Child loops can also call other child loops, in which case both the parent and grandparent loops can use `==>` to break
-out of the top level loop. There's no set limit to nesting depth.
-
-"""
-nb_save
diff --git a/docs/book/action_loops/loops.nim b/docs/book/action_loops/loops.nim
deleted file mode 100644
index 51f31dd1..00000000
--- a/docs/book/action_loops/loops.nim
+++ /dev/null
@@ -1,108 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: """
-
-# Loops
-
-An Action Loop always has one and only one current action, which it will call repeatedly until you switch to some
-other action. The default action is `nil`. The first thing a loop must do is switch from `nil` to another action, using
-the little switch arrow `->`.
-
-```nim
-loop:
- nil -> forward
- # I'll go forward forever!
-```
-
-The little switch arrow (`->`) will switch from the action on the left to the action on the right if it's encountered
-and the left action has just completed. If the loop goes through and no switches match, the current action will be
-run again.
-
-We can control which switches get run by putting them in `if` statements.
-
-```nim
-loop:
- nil -> forward
- if player.far:
- forward -> back
- if player.near:
- back -> forward
-```
-
-In the above example, the loop immediately switches to `forward`, and will go forward indefinitely until one of the
-conditions is met and the action is switched to something else. If the player gets too far away (more than 100m) and
-the action is `forward`, the action will be switched to `back`. If the player is near (5m) and the action is `back`,
-it will switch to `forward`. However, if the player is near and the action is `forward`, nothing will change. The
-`if player.near` statement will be true, but `back -> forward` is ignored, since the current action isn't `back`.
-
-If you want your loop to end at some point, you can switch back to `nil`:
-
-```nim
-loop:
- nil -> forward(10) # Some actions can take additional parameters.
- forward -> nil
- # I'll run `forward(10)` a single time, then stop and end the loop.
-```
-
-Let's look at something more complicated, and introduce the big switch arrow (`==>`) and change actions.
-
-```nim
-- wander:
- speed = 1
- forward 5..10
- turn -45..45
-
-- charge:
- speed = 5
- turn player
- forward 1
-
-- flee:
- speed = 3
- turn -player
- forward 1
-
-- attack:
- player.bounce 5
- turn 360
-
-var health = 3
-
-loop:
- nil -> wander
- if 1 in 50:
- # when each `wander` action finishes, there's a 1 in 50 (2%) chance of our unit getting a sudden
- # burst of energy and switching to the `charge` action. Otherwise we just keep wandering.
- wander -> charge
-
- if health == 0:
- # we died. Exit the loop. We want this to happen immediately, not after the action finishes, so we use
- # the big switch arrow. We use the special `any` action to say that this should happen regardless
- # of the running action.
- any ==> nil
-
- if player.hit:
- # if the player touches us while we're wandering, we flee. We want it to happen the instant the player touches us,
- # not when our current `wander` is done, so we use the big switch arrow
- wander ==> flee:
- # this is a change action. If the action switches here, the change action will also run once.
- health -= 1
- # if the player touches us while we're charging, we attack immediately.
- charge ==> attack
-
- if player.far:
- # if we're fleeing the player, we go back to wandering when they get far away
- flee -> wander
-
- # Switch to wander when our attack is done. We always want this to happen, so it isn't in a
- # conditional. It only does anything if the current action is `attack`, and we only do it when
- # the attack is done becuase we're using the little switch arrow
- attack -> wander
-
-```
-
-
-"""
-nb_save
diff --git a/docs/book/action_loops/more.nim b/docs/book/action_loops/more.nim
deleted file mode 100644
index 4d8b1cfa..00000000
--- a/docs/book/action_loops/more.nim
+++ /dev/null
@@ -1,55 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: """
-
-# More about Action Loops
-
-### `as`
-
-Actions are just procedures, and they can take parameters. Sometimes you want to run the same action
-in different situations with a different action name. You could do this by creating a new action that
-calls the first one, but you can also use `as` to give an action a different name.
-
-```nim
-# explore action and health var not shown.
-- flee(distance = 100):
- turn -player
- forward distance
-
-loop:
- nil -> explore
- if player.near and health > 2:
- explore -> flee
- elif player.near and health <= 2:
- explore ==> flee(200) as really_flee
- if player.far:
- flee -> explore
- if player.far(150):
- really_flee -> explore
-```
-
-### Special from actions
-
-Often loops will switch from a single action to another. However, sometimes you want to allow switching from
-a variety of actions.
-
-- `any -> some_action` - switch from any action to the target action. This will switch (and run any change action)
- even if we're already running the target action. In this example we used a little switch arrow, so it still won't
- happen until the current action actually completes.
-- `others -> some_action` - Same as `any`, but it excludes the target action.
-- `(action1, action2)` - Multiple from actions can be supported by putting them in a tuple.
-- `(any, -action2, -action3)` - Switch from any action except `action2` and `action3`.
-
-### When do action loops run?
-
-An action loops will run whenever its executing action finishes. In addition, action loops will run
-every 0.5 seconds, and when something triggers an interrupt. Currently only the start and end of a collision with the
-player trigger an interrupt, but this will be expanded.
-
-When using child loops, the top level loop runs first, then walks down the stack of loops until the currently executing
-loop is reached.
-
-"""
-nb_save
diff --git a/docs/book/assets/css/enu.css b/docs/book/assets/css/enu.css
index 4bfa685c..534c7cd0 100644
--- a/docs/book/assets/css/enu.css
+++ b/docs/book/assets/css/enu.css
@@ -1,52 +1,60 @@
.dark code:not(.hljs){
- color: #FF73FD
+ color: #FF73FD
}
.dark h1, h2, h3, h4, h5 {
- color: #96CBFE;
+ color: #96CBFE;
}
body {
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
- font-size: 16pt;
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+ font-size: 16pt;
}
.dark {
- --bg: #0b0517;
- --fg: #F6F3E8;
+ --bg: #0b0517;
+ --fg: #F6F3E8;
- --sidebar-bg: #000000;
- --sidebar-fg: #F6F3E8;
- --sidebar-non-existant: #505274;
- --sidebar-active: #C6C5FE;
- --sidebar-spacer: #2d334f;
+ --sidebar-bg: #000000;
+ --sidebar-fg: #F6F3E8;
+ --sidebar-non-existant: #505274;
+ --sidebar-active: #C6C5FE;
+ --sidebar-spacer: #2d334f;
- --scrollbar: #F6F3E8;
+ --scrollbar: #F6F3E8;
- --icons: #737480;
- --icons-hover: #b7b9cc;
+ --icons: #737480;
+ --icons-hover: #b7b9cc;
- --links: #C6C5FE;
+ --links: #C6C5FE;
- --inline-code-color: #c5c8c6;;
+ --inline-code-color: #c5c8c6;;
- --theme-popup-bg: #161923;
- --theme-popup-border: #737480;
- --theme-hover: #282e40;
+ --theme-popup-bg: #161923;
+ --theme-popup-border: #737480;
+ --theme-hover: #282e40;
- --quote-bg: hsl(226, 15%, 17%);
- --quote-border: hsl(226, 15%, 22%);
+ --quote-bg: hsl(226, 15%, 17%);
+ --quote-border: hsl(226, 15%, 22%);
- --table-border-color: hsl(226, 23%, 16%);
- --table-header-bg: hsl(226, 23%, 31%);
- --table-alternate-bg: hsl(226, 23%, 14%);
+ --table-border-color: hsl(226, 23%, 16%);
+ --table-header-bg: hsl(226, 23%, 31%);
+ --table-alternate-bg: hsl(226, 23%, 14%);
- --searchbar-border-color: #aaa;
- --searchbar-bg: #aeaec6;
- --searchbar-fg: #000;
- --searchbar-shadow-color: #aaa;
- --searchresults-header-fg: #5f5f71;
- --searchresults-border-color: #5c5c68;
- --searchresults-li-bg: #242430;
- --search-mark-bg: #a2cff5;
+ --searchbar-border-color: #aaa;
+ --searchbar-bg: #aeaec6;
+ --searchbar-fg: #000;
+ --searchbar-shadow-color: #aaa;
+ --searchresults-header-fg: #5f5f71;
+ --searchresults-border-color: #5c5c68;
+ --searchresults-li-bg: #242430;
+ --search-mark-bg: #a2cff5;
+}
+
+details.note {
+ font-style: italic;
+}
+details.note > summary {
+ font-weight: bold;
+ cursor: pointer;
}
diff --git a/docs/book/basics.nim b/docs/book/basics.nim
deleted file mode 100644
index 476c9bc4..00000000
--- a/docs/book/basics.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: "# Basics"
-nb_save
diff --git a/docs/book/basics/config.md b/docs/book/basics/config.md
deleted file mode 100644
index d706aa89..00000000
--- a/docs/book/basics/config.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Config
-
-The Enu data directory lives in `~/Library/Application Support/enu` on Mac, `%AppData%\enu` on Windows, and
-`~/.local/share/enu` on Linux. `config.json` has a few configurable options:
-
-`mega_pixels`: The render resolution, in mega pixels. Increase for more detail. Decrease for better performance.
-
-`font_size`: The font size. DPI is currently ignored, so hidpi screens will require a higher number.
-
-`dock_icon_size`: Size of the icons in the dock. DPI is currently ignored, so hidpi screens will require a higher number.
-
-`world`: The world/project to load. Change this to create a new world.
-
-`show_stats`: Show FPS and other stats.
-
-`start_full_screen`: Whether to start Enu full screen, or in a window.
-
-`semicolon_as_colon`: Both `;` and `:` will be interpreted as `:`, allowing `:` to be typed without shift. Sometimes useful for new typists.
diff --git a/docs/book/basics/controls.md b/docs/book/basics/controls.md
deleted file mode 100644
index e016dc5e..00000000
--- a/docs/book/basics/controls.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Usage
-
-## Keyboard/Mouse
-
-- `ESC` - toggle mouse capture and to dismiss editor windows. Reloads script changes.
-- `W, A, S, D` - move around when mouse is captured.
-- `Space` - jump. Double jump to toggle flying. Hold to go up while flying.
-- `Shift` - run.
-- `C` - go down while flying.
-- `~` - toggle the console.
-- `F` - toggle fullscreen.
-- `1` - enter edit mode.
-- `2 - 9` - change active action.
-- `Mouse Wheel Up/Down` - change active action.
-- `Alt` - reload script changes. Hold to temporarily capture the mouse and move, so you can
- change your view without having to switch away from what you're doing.
-- `Cmd+P / Ctrl+P` - Pause scripts.
-- `Cmd+Shift+S / Ctrl+Shift+S` - Save and reload all scripts, then pause. If you have a script that makes a unit
- inaccessible (ex. moves the unit below the ground) this is a way to get things back to their start positions so they
- can be edited.
-- `Left Click` - Place a block/object or open the code for the currently selected object.
-- `Right Click` - Remove a block/object.
-
-## XBox / Playstation Controller
-
-- `Left Stick` - move.
-- `Right Stick` - change view.
-- `A / X` - jump. Double jump to toggle flying. Hold to go up while flying.
-- `B / ◯` - go down while flying. Dismiss code editor.
-- `Y / △` - toggle edit mode.
-- `L1 / R1` - change active action.
-- `L2` - place a block/object or open the code for the currently selected object.
-- `R2` - remove a block/object.
-- `L3` - run.
-
-Enu currently includes 6 block types/colors, and 1 object model (a robot). This will be greatly
-expanded in the future.
-
-## Building
-
-Drop a block or robot with the left mouse button/controller trigger, remove it with the right. Adjoining blocks will be
-combined into a single structure. With the mouse captured, building works more or less like MineCraft. Release the mouse
-by pressing ESC to draw blocks using the mouse cursor.
-
-Code by switching to the code tool by left clicking/triggering on an object or structure. Changes are applied when the
-code window is closed (ESC key) or CTRL is pressed. Holding CTRL will also temprarly grab the mouse and allow you to
-change your position.
diff --git a/docs/book/basics/programming.nim b/docs/book/basics/programming.nim
deleted file mode 100644
index 101c1f9c..00000000
--- a/docs/book/basics/programming.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: "# Programming"
-nb_save
diff --git a/docs/book/coding.md b/docs/book/coding.md
index 07717741..fceba3c3 100644
--- a/docs/book/coding.md
+++ b/docs/book/coding.md
@@ -1,20 +1,31 @@
# Coding Enu
-Enu is programmed with a language called [Nim](https://nim-lang.org). This tutorial teach some of the
-basics of Nim, as well as a few special features that are unique to Enu.
+Enu is programmed with a language called [Nim](https://nim-lang.org). This
+tutorial covers some of the basics of Nim, as well as a few special features
+that are unique to Enu.
+
+
+Note for Nimions
+
+Enu simplifies some Nim concepts, mainly to defer explaining unfamiliar terms.
+In particular, Enu tries to be forgiving with types, calls procs 'commands',
+and avoids immutable variables.
+
+
## Comments
-Comments are a way to leave notes to yourself or other programmers. They can be
-used for lots of different things, but generally provide more information on how
-something works or why it was done a certain way. They can also have more
-general information, such as the author of the code and when it was written.
+Comments are a way to leave notes to yourself or other programmers. They can be
+used for lots of different things, but generally provide more information on how
+something works or why it was done a certain way. They can also have more
+general information, such as the author of the code and when it was written.
They start with a `#` sign. Everything else on the line is ignored.
```nim
# Copyright Scott Wadden, 2023
-var last_row = false # we only want to change colors on the last row
+# we only want to change colors on the last row
+var last_row = false
```
## Types
@@ -23,27 +34,26 @@ Every piece of data in Enu has a type. These are the most common:
- `bool` - a `true` or `false` value. Example: `drawing = false`
-- `Number` - a number with a decimal, like `1.0`. Numbers without decimals, like
- `1` will usually auto convert, but if something isn't working, try adding a
+- `Number` - a number with a decimal, like `1.0`. Numbers without decimals, like
+ `1` will usually auto convert, but if something isn't working, try adding a
decimal. Example: `var age = 12.5`
-- `Text` - Any combination of letters, numbers and punctuation, contained inside
+- `Text` - Any combination of letters, numbers and punctuation, contained inside
double quotes. Example: `var name = "Sackville Coding Club"`
-- `Color` - Any one of `blue`, `red`, `green`, `black`, `white`, `brown` or
+- `Color` - Any one of `blue`, `red`, `green`, `black`, `white`, `brown` or
`eraser`. Someday more colors will be available. Example `color = green`
-- `Position` - The place of something in the world. Example:
+- `Position` - The place of something in the world. Example:
`me.position = player.position`
-- `Thing` - Anything that exists in the Enu world. This could be something you
- build, a robot, or the player.
-
-
+- `Unit` - Anything that exists in the Enu world. This could be something you
+ build, a robot, or the player. This will be renamed to `Thing` in a future
+ version.
## Variables
-A variable is used to store data. The value is usually set when it is created,
+A variable is used to store data. The value is usually set when it is created,
and can be modified later. A variable must always hold the same type of data.
```nim
@@ -61,27 +71,26 @@ if birthday:
age = "13 years old"
```
-Usually variables are defined with just a value, but sometimes you need to
-specify their type as well. This could be because you're not ready to give it a
-value, or because you want it to contain more than one kind of `Thing`.
+Usually variables are defined with just a value, but sometimes you need to
+specify their type as well. This could be because you're not ready to give it a
+value, or because you want it to contain more than one kind of `Unit`.
For example:
```nim
-# this won't work because `enemy` gets automatically set to the type of
-# `Player`, so other things won't work:
+# this won't work because `enemy` gets automatically set
+# to the type of `Player`, so other units won't work:
var enemy = player
enemy = me
-# it will work if we do it like this, since `player` and `me` are both `Thing`
-var enemy: Thing
+# it will work if we do it like this, since `player` and
+# `me` are both `Unit`
+var enemy: Unit
enemy = player
enemy = me
```
-
-
-Usually each variable starts with `var`, but you can also indent to define
+Usually each variable starts with `var`, but you can also indent to define
multiple variables with a single `var`.
```nim
@@ -101,31 +110,30 @@ var
Enu has some built in variables.
-- `me` - the `Thing` that we're working on in the current script.
+- `me` - the `Unit` that we're working on in the current script.
- `speed` - a `Number` to get or set our current speed. `1.0` by default.
- `color` - the current drawing `Color`. `blue` by default.
-- `scale` - a `Number` to grow or shrink a `Thing`. `1.0` by default. Be careful
- about making this too small. You might lose the `Thing` you're working on.
+- `scale` - a `Number` to grow or shrink a `Unit`. `1.0` by default. Be careful
+ about making this too small. You might lose the `Unit` you're working on.
-- `position` - where a `Thing` is. You can use this to move something
+- `position` - where a `Unit` is. You can use this to move something
immediately. `position = player.position` would teleport `me` to the player.
-- `drawing` - a `bool` that indicates if commands like `forward` or `back` should
- draw blocks. `true` by default. `drawing = false` would let you move without
- drawing anything, which could be useful for making a hole or a new object.
+- `drawing` - a `bool` that indicates if commands like `forward` or `back`
+ should draw blocks. `true` by default. `drawing = false` lets you move
+ without drawing anything, which can be useful for making a hole or a new
+ object.
-- `glow` - how bright a `Thing` is. Can be used to make something flash, or to
+- `glow` - how bright a `Unit` is. Can be used to make something flash, or to
highlight it.
-
-
-## Blocks
+## Code Blocks
-Blocks start with a `:`, and are then indented by two spaces. Everything that's
-inside the block is controlled by whatever started it.
+Code Blocks start with a `:`, and are then indented by two spaces. Everything
+that's inside the block is controlled by whatever started it.
- `if` - an `if` block will only run if the statement is `true`.
@@ -150,11 +158,12 @@ back 20
# this only happens once, since it isn't in a `times` block.
8.times(side):
- # It's also possible to see how many times we've already performed
- # the action by passing `times` an index variable.
- # This starts from 0. in this example we're storing the counter in
- # a variable called `side`. Because we're doing this 8 times,
- # `side` will be set first to 0, then 1, 2, 3, 4, 5, 6, 7.
+ # It's also possible to see how many times we've
+ # performed the action by passing `times` an index
+ # variable. This starts from 0. in this example we're
+ # storing the counter in a variable called `side`.
+ # Because we're doing this 8 times, `side` will be set
+ # first to 0, then 1, 2, 3, 4, 5, 6, 7.
forward 5
if side != 4:
turn right
@@ -162,6 +171,6 @@ back 20
turn left
echo "We drew a bow tie!"
-echo "This will only be printed once because it isn't in a times block"
+echo "This will only be printed once."
+echo "Because it isn't in a times block."
```
-
diff --git a/docs/book/coding/commands.nim b/docs/book/coding/commands.nim
index 9beeba31..4466e204 100644
--- a/docs/book/coding/commands.nim
+++ b/docs/book/coding/commands.nim
@@ -4,13 +4,15 @@ import enuib
nb_init(theme = use_enu)
nb_text: """
-# Commands
+# Built-in Commands
## `move` / `build`
-When dealing with a `Build` unit, commands can do different things depending on whether the unit is in `build`
-mode or `move` mode. `move` mode moves the unit around, while `build` creates new blocks. By default a `Build` is in
-`build` mode. Often you'll pass the `me` unit to `move/build`, but it's also possible to pass other units. For example:
+When dealing with a `Build` unit, commands can do different things depending on
+whether the unit is in `build` mode or `move` mode. `move` mode moves the unit
+around, while `build` creates new blocks. By default a `Build` is in `build`
+mode. Often you'll pass the `me` unit to `move/build`, but it's also possible to
+pass other units. For example:
```nim
build me # generally not required, as it's the default
@@ -33,7 +35,8 @@ move enemy
up 5
```
-It's also possible to call commands directly against a unit instance, but they will always use `move` mode, regardless
+It's also possible to call commands directly against a unit instance, but they
+will always use `move` mode, regardless
of which mode is in use:
```nim
@@ -44,7 +47,8 @@ enemy.up 5 # move up 5
## `forward` / `back` / `up` / `down` / `left` / `right`
-Move or build x number of blocks in the specified direction. Defaults to 1 block.
+Move or build x number of blocks in the specified direction. Defaults to 1
+block.
```nim
forward 5
@@ -54,15 +58,19 @@ enemy.up 2
## `turn`
Turn a unit. Can be passed:
-- a number in degrees. Positive for clockwise, negative for counter-clockwise. Ex. `turn 180`
-- a direction (`forward/back/up/down/left/right`) which will turn in that direction. 90 degrees by default.
- Ex. `turn left`, or `turn up, 180`
-- a unit to turn towards. Ex. `turn player`
-- a negative unit to turn away from. Ex. `turn -player`
+- a number in degrees. Positive for clockwise, negative for counter-clockwise.
+ Ex. `turn 180`.
+- a direction (`forward/back/up/down/left/right`) which will turn in that
+ direction. 90 degrees by default. Ex. `turn left`, or `turn up, 180`.
+- a unit to turn towards. Ex. `turn player`.
+- a negative unit to turn away from. Ex. `turn -player`.
-## `near(less_than = 5.0)` / `far(greater_than = 100.0)`
+## `near` / `far`
-Returns true or false if a unit is nearer/farther than the specified distance. For example:
+`near(less_than = 5.0)` / `far(greater_than = 100.0)`
+
+Returns true or false if a unit is nearer/farther than the specified distance.
+For example:
```nim
if player.near:
@@ -80,7 +88,8 @@ if player.far(25):
## `hit`
-If a unit is touching another unit, return the vector of the contact. Defaults to testing against `me`. For example:
+If a unit is touching another unit, return the vector of the contact. Defaults
+to testing against `me`. For example:
```nim
if player.hit:
@@ -99,7 +108,8 @@ Gets or set the position of a unit as a Vector3. `me` by default.
```nim
if player.hit(enemy):
- # if the player hits `enemy`, reset the player position to the center of the world.
+ # if the player hits `enemy`, reset the player position
+ # to the center of the world.
player.position = vec3(0, 0, 0)
```
@@ -111,14 +121,17 @@ The starting position of a unit. Missing currently, but will be in in 0.2.
Gets or sets the speed of a unit. `me` by default.
-While building, speed refers to the number of blocks placed per frame. In the future this will be normalized to 60fps,
-but currently the speed is tied to the framerate. Setting speed to 0 will build everything at once.
+While building, speed refers to the number of blocks placed per frame. In the
+future this will be normalized to 60fps, but currently the speed is tied to the
+framerate. Setting speed to 0 will build everything at once.
While moving, this is the movement speed in meters per second.
-Switching between build and move mode doesn't impact the speed, except in the case of switching to move mode from build
-mode with a speed of 0. `speed = 0` is extremely common for build mode, but makes things appear broken in move mode,
-as nothing will actually move, so switching to move mode with a speed of 0 will automatically reset the speed to 1.
+Switching between build and move mode doesn't impact the speed, except in the
+case of switching to move mode from build mode with a speed of 0. `speed = 0` is
+extremely common for build mode, but makes things appear broken in move mode, as
+nothing will actually move, so switching to move mode with a speed of 0 will
+automatically reset the speed to 1.
## `scale` / `scale=`
@@ -126,15 +139,18 @@ Sets the scale/size of a unit. `me` by default.
## `glow` / `glow=`
-Specifies the glow/brightness of a unit. `me` by default. Currently does nothing for bots, but will in the future.
+Specifies the glow/brightness of a unit. `me` by default. Currently does nothing
+for bots, but will in the future.
## `global` / `global=`
-Specifies if a unit is in global space, or the space of its parent. If `global = true` and the parent unit moves,
-child units are unaffected. If `global = false`, the child will move with its parent. Does nothing for top level units,
-as they're always global.
+Specifies if a unit is in global space, or the space of its parent. If
+`global = true` and the parent unit moves, child units are unaffected. If
+`global = false`, the child will move with its parent. Does nothing for top
+level units, as they're always global.
-By default, new `Build` units are `global = false` and new `Bot` units are `global = true`.
+By default, new `Build` units are `global = false` and new `Bot` units are
+`global = true`.
## `rotation`
@@ -146,8 +162,9 @@ Gets or sets the velocity of a unit, as a Vector3. Currently buggy.
## `color` / `color=`
-Gets or sets a units color. `me` by default. For `Build` units, this only impacts blocks placed after the property
-is set. For `Bot` units this does nothing, but in the future it will change their color.
+Gets or sets a units color. `me` by default. For `Build` units, this only
+impacts blocks placed after the property is set. For `Bot` units this does
+nothing, but in the future it will change their color.
## `bounce`
@@ -155,8 +172,9 @@ Bounces a unit in the air. Currently only works for the player.
## `save` / `restore`
-`Build` units only. `save` the position, direction, drawing state, and color of the draw point, to `restore` it later.
-Can optionally take a name string to enable saving/restoring multiple points.
+`Build` units only. `save` the position, direction, drawing state, and color of
+the draw point, to `restore` it later. Can optionally take a name string to
+enable saving/restoring multiple points.
## `reset`
@@ -164,13 +182,16 @@ Instantly return unit to start position and resets rotation and scale.
## `home`
-Moves a unit to its start position via a `forward`, `left`, `down` sequence with appropriate values. Can fail if there
-are obstructions along the way. Compare `position` to `start_position` after running to test for success.
+Moves a unit to its start position via a `forward`, `left`, `down` sequence with
+appropriate values. Can fail if there are obstructions along the way. Compare
+`position` to `start_position` after running to test for success.
-## `sleep(seconds = -1.0)`
+## `sleep`
+`sleep(seconds = -1.0)`
-Do nothing for the specified number of seconds. If no argument is provided, or the argument is < 0, this will wait for
-0.5 seconds or until unit is interrupted, which will end the `sleep` prematurely. This allows the following:
+Do nothing for the specified number of seconds. If no argument is provided, or
+the argument is < 0, this will wait for 0.5 seconds or until unit is
+interrupted, which will end the `sleep` prematurely. This allows the following:
```nim
forever:
@@ -179,7 +200,8 @@ forever:
echo "ouch!"
```
-Currently, any collision will trigger an interrupt. This will be expanded in the future.
+Currently, any collision will trigger an interrupt. This will be expanded in the
+future.
## `forever`
@@ -187,7 +209,8 @@ Alias for `while true`
## `cycle`
-Alternate between a list of values, returning the next element each time the cycle is called.
+Alternate between a list of values, returning the next element each time the
+cycle is called.
```nim
forever:
diff --git a/docs/book/coding/concepts.nim b/docs/book/coding/concepts.nim
index 161000bb..c7479a93 100644
--- a/docs/book/coding/concepts.nim
+++ b/docs/book/coding/concepts.nim
@@ -8,17 +8,20 @@ nb_text: """
## Units
-Entities/objects in Enu are referred to as units, and have a base type of `Unit`. Currently there are `Build` units
-(voxel objects) and `Bot` units (the robot). There will be more in the future.
+Entities/objects in Enu are referred to as units, and have a base type of
+`Unit`. Currently there are `Build` units (voxel objects) and `Bot` units (the
+robot). There will be more in the future.
-`me` is the unit that owns a script, and is equivalent to `self`/`this` in other environments. `me` was selected
-because it's easier for a child to type. `me.` can be auto-inserted when accessing properties of the unit. For example,
-`me.speed = 10` would commonly be written as `speed = 10`. There are probably bugs with this behavior. Please report
-them.
+`me` is the unit that owns a script, and is equivalent to `self`/`this` in other
+environments. `me` was selected because it's easier for a child to type. `me.`
+can be auto-inserted when accessing properties of the unit. For example,
+`me.speed = 10` would commonly be written as `speed = 10`. There are probably
+bugs with this behavior. Please report them.
## Prototypes
-Enu uses a prototype based object system for units. To allow a unit to be a prototype, you give it a name:
+Enu uses a prototype based object system for units. To allow a unit to be a
+prototype, you give it a name:
```nim
name ghost
@@ -30,14 +33,16 @@ Then create a new instance in a different script with `.new`:
var ghost2 = ghost.new
```
-You can also provide parameters, which can be overridden when creating a new instance:
+You can also provide parameters, which can be overridden when creating a new
+instance:
```nim
name ghost(color = white, speed = 5, spookiness = 10)
```
-These become properties of the unit (ie `me.spookiness = 5`), but can be treated like variables in the unit's script
-due to auto `me` insertion (`spookiness = 200`).
+These become properties of the unit (ie `me.spookiness = 5`), but can be treated
+like variables in the unit's script due to auto `me` insertion
+(`spookiness = 200`).
To create a new instance with specific property values:
@@ -45,13 +50,22 @@ To create a new instance with specific property values:
var ghost2 = ghost.new(spookiness = 11)
```
-Parameters can have a default value (`spookiness = 10`), which makes them optional when creating a new instance. If they
-should be required, or there's no reasonable default value to use, specify a type (`spookiness: int`) instead, or omit
-both the value and the type, which will make the type `auto`. Because `auto` can be implicit, `name ghost(a, b: int)`
-treats parameters differently than `proc ghost(a, b: int)` would. With the proc, `a` and `b` are both `int`, whereas
-the `name` version would make `a` `auto` and `b` `int`.
+Parameters can have a default value (`spookiness = 10`), which makes them
+optional when creating a new instance. If they should be required, or there's no
+reasonable default value to use, specify a type (`spookiness: int`) instead, or
+omit both the value and the type, which will make the type `auto`.
-`speed`, `color`, `global` can always be passed to a new instance, even if the prototype name doesn't include them.
+
+Note for Nimions
+
+Because `auto` can be implicit, `name ghost(a, b: int)` treats parameters
+differently than `proc ghost(a, b: int)` would. With the proc, `a` and `b` are
+both `int`, whereas the `name` version would make `a` `auto` and `b` `int`.
+
+
+
+`speed`, `color`, and `global` can always be passed to a new instance, even if
+the prototype name doesn't include them.
"""
nb_save
diff --git a/docs/book/coding/random_numbers.nim b/docs/book/coding/random_numbers.nim
index cb8fc7c5..8874148d 100644
--- a/docs/book/coding/random_numbers.nim
+++ b/docs/book/coding/random_numbers.nim
@@ -6,18 +6,25 @@ nb_text: """
# Random numbers
-Generally, if an Enu command takes a number, it will be a `float`. However, `int` will auto-convert to `float`, and
-when a numeric `Range` is passed to something expecting a number, a random value within the range will be selected. So, even
-though `forward` expects a `float`, the following are all valid:
+Generally, if an Enu command takes a number, it will be a `float`. However,
+`int` will auto-convert to `float`, and when a numeric `Range` is passed to
+something expecting a number, a random value within the range will be selected.
+So, even though `forward` expects a `float`, the following are all valid:
```nim
forward 1.0
forward 1
-forward 1.0..5.0 # Convert to a random float between 1.0 and 5.0
-forward 1..5 # Convert to a random int between 1 and 5, then convert the int to a float
+
+# Convert to a random float between 1.0 and 5.0
+forward 1.0..5.0
+
+# Convert to a random int between 1 and 5, then convert the
+# int to a float
+forward 1..5
```
-The `in` operator can be used between two numbers to test for random chance. For example:
+The `in` operator can be used between two numbers to test for random chance. For
+example:
```nim
if 1 in 2:
@@ -26,15 +33,18 @@ if 1 in 100:
echo "I should be hit 1% of the time"
```
-By default random numbers in Enu are based partially on the time and will be different each time a script is executed.
-However, sometimes you want randomness to create variety, but want the same values to be chosen each time a script is
-run. This is especially important when using randomness in a `Build` that you plan to manually edit later. To ensure the
-same values are selected each time a script is run, set the unit's `seed` property to some integer of your choosing,
+By default random numbers in Enu are based partially on the time and will be
+different each time a script is executed. However, sometimes you want randomness
+to create variety, but want the same values to be chosen each time a script is
+run. This is especially important when using randomness in a `Build` that you
+plan to manually edit later. To ensure the same values are selected each time a
+script is run, set the unit's `seed` property to some integer of your choosing,
ie `seed = 12345` or `me.seed = 54321`.
-Any child units instanced by a unit with a seed value will get the same seed by default. However, it will still get
-a unique random number generator, so changing the script for a child object won't impact the random numbers selected by
-the parent.
+Any child units instanced by a unit with a seed value will get the same seed by
+default. However, it will still get a unique random number generator, so
+changing the script for a child object won't impact the random numbers selected
+by the parent.
"""
diff --git a/docs/book/coding/shorthand.nim b/docs/book/coding/shorthand.nim
index e36b7ede..9adc54b0 100644
--- a/docs/book/coding/shorthand.nim
+++ b/docs/book/coding/shorthand.nim
@@ -6,8 +6,8 @@ nb_text: """
# Shorthand Commands
-Many Enu command also have a 1 letter alias. These are harder to read, but can reduce friction for folks new to
-typing.
+Many Enu command also have a 1 letter alias. These are harder to read, but can
+reduce friction for folks new to typing.
The aliases are:
@@ -17,7 +17,8 @@ The aliases are:
- `r` - `right`
- `u` - `up`
- `d` - `down`
-- `t` - `turn`. Can be combined with shorthand directions, so `turn right` can be expressed as `t r`
+- `t` - `turn`. Can be combined with shorthand directions, so `turn right` can
+ be expressed as `t r`
- `o` - `while true:` (o was selected because its shape is a loop)
- `x` - `times`. `5.x:` will run a code block 5 times.
diff --git a/docs/book/action_loops.md b/docs/book/command_loops.md
similarity index 59%
rename from docs/book/action_loops.md
rename to docs/book/command_loops.md
index ecd77915..e0bd7ef2 100644
--- a/docs/book/action_loops.md
+++ b/docs/book/command_loops.md
@@ -1,8 +1,6 @@
-# Action Loops
+# Command Loops
-Action loops let you control the behavior of a unit in a (somewhat) straightforward way. At least I think it's straightforward. You might disagree.
-
-They look like this:
+Command loops are how you control the behavior of units. They look like this:
```nim
- boogie:
@@ -17,10 +15,13 @@ They look like this:
loop:
nil -> shake
+
if player.near:
shake -> boogie
+
elif player.far(50):
(rest, boogie) -> shake
+
elif player.far(200):
(boogie, shake) -> rest
```
diff --git a/docs/book/command_loops.nim b/docs/book/command_loops.nim
new file mode 100644
index 00000000..0dc90055
--- /dev/null
+++ b/docs/book/command_loops.nim
@@ -0,0 +1,2 @@
+import enuib
+md "command_loops.md"
diff --git a/docs/book/action_loops/arrows.nim b/docs/book/command_loops/arrows.nim
similarity index 58%
rename from docs/book/action_loops/arrows.nim
rename to docs/book/command_loops/arrows.nim
index a84bbb3c..720a6917 100644
--- a/docs/book/action_loops/arrows.nim
+++ b/docs/book/command_loops/arrows.nim
@@ -8,7 +8,8 @@ nb_text: """
## `->` Little Switch Arrow
-Switches from one action to another, after the first action has finished running.
+Switches from one command to another, after the first command has finished
+running.
```nim
draw_box -> draw_stairs
@@ -16,7 +17,8 @@ draw_box -> draw_stairs
## `==>` Big Switch Arrow
-Switches from one action to another immediately. Will interrupt the running action.
+Switches from one command to another immediately. Will interrupt the running
+command.
```nim
if player.near:
diff --git a/docs/book/command_loops/child_loops.nim b/docs/book/command_loops/child_loops.nim
new file mode 100644
index 00000000..3dcbc32a
--- /dev/null
+++ b/docs/book/command_loops/child_loops.nim
@@ -0,0 +1,88 @@
+import nimib, nimibook
+import enuib
+
+nb_init(theme = use_enu)
+nb_text: """
+
+# Child Loops
+
+Sometimes you want to write a command with its own Command Loop. Imagine we have
+a unit that performs two complicated commands, `find_treasure` and
+`fight_monster`. `find_treasure` might need to `navigate` an area, `locate`
+items of interest, `interact` with them,then return to `home_base` to deposit
+them. `fight_monster` could require commands like `evade`, `attack`, `hide`, and
+`flee`.
+
+Combining all of this in a single command loop would give us a lot of commands,
+many of which are mostly unrelated, and managing our switches could get very
+complicated. However, making them entirely separate isn't ideal either, as
+there's probably some common functionality between them (die if our health gets
+too low, respawn if we get stuck). We also need to switch between the two
+commands.
+
+A good way to manage this is by making `find_treasure` and `fight_monster` child
+loops rather than regular commands. We can treat them like regular commands in
+our main `loop`, but when we switch to them they'll be able to perform more
+sophisticated logic than a normal command could. In addition, our main loop will
+continue to run along side the the child loop, so we can quickly switch out of
+the child loop with a big switch arrow `==>` in response to certain conditions,
+without either loop needing to worry about higher level concerns.
+
+Our main loop could look something like this:
+
+```nim
+loop find_treasure:
+ # Our treasure logic goes here. This loop doesn't have
+ # an exit condition.
+ if treasure_found:
+ # We found it. Start looking again. This will switch
+ # from any command apart from `look_for_chest`.
+ others ==> look_for_chest
+
+loop fight_monster:
+ # Fight logic here. This loop should exit when the
+ # monster dies.
+ if monster.dead:
+ any ==> nil
+
+loop:
+ # We want our unit to find_treasure indefinately.
+ # `find_treasure` doesn't exit (switch to nil).
+ nil -> find_treasure
+ if monster.near:
+ # find_treasure doesn't need to know anything about
+ # monsters. We can break out of it with a big switch
+ # arrow if we encounter one.
+ find_treasure ==> fight_monster
+
+ # fight_monster does have an exit condition (the monster
+ # dies), so we can wait for it to finish using the little
+ # switch arrow, then go back to finding treasure.
+ fight_monster -> find_treasure
+
+ if health == 0:
+ # if our health drops to 0, it doesn't matter what else
+ # we're doing. Die immediately. Break out of any
+ # command (except die) with a big switch arrow.
+ (any, -die) ==> die
+
+ # this would also work:
+ # others ==> die
+
+ if stuck:
+ # respawn if we're stuck, but only from our two child
+ # loops. We don't want to respawn if we're already
+ # respawning, or we're dead. Implementation of
+ # `respawn` not shown.
+ (find_treasure, fight_monster) ==> respawn
+
+ # we're done respawning. Treasure time!
+ respawn -> find_treasure
+```
+
+Child loops can also call other child loops, in which case both the parent and
+grandparent loops can use `==>` to break out of the top level loop. There's no
+limit to nesting depth.
+
+"""
+nb_save
diff --git a/docs/book/command_loops/commands.nim b/docs/book/command_loops/commands.nim
new file mode 100644
index 00000000..14c836c9
--- /dev/null
+++ b/docs/book/command_loops/commands.nim
@@ -0,0 +1,52 @@
+import nimib, nimibook, pretty
+import enuib
+
+nb_init(theme = use_enu)
+nb_text: """
+
+# Writing Commands
+
+A command is a piece of code that can be run to control a unit or to get
+information about the world. Enu has many built in commands like `forward`,
+`back`, `left`, and `right`, and it's easy to add your own.
+
+Commands start on a new line with a `-` and a space, followed by a name. If the
+command needs additional details to do its job you can list them in brackets
+separated by commas. Commands are blocks, so the first line ends with `:` and
+the command body is nested inside.
+
+```nim
+- hello(name):
+ echo "hello ", name
+
+- goodbye(name = "Vin"):
+ echo "goodbye ", name
+
+hello "Claire"
+goodbye "Cal"
+```
+
+If a command produces a result its type must appear before the `:`. In the
+future this will be handled automatically.
+
+```nim
+- hello(name) string:
+ result = "hello " & $name
+
+echo hello("Scott")
+```
+
+
+Note for Nimions
+
+A command is just a `proc`. Anything that can be done with commands can also be
+done with normal procs.
+
+Command parameters (called details here) will default to `auto` if no type is
+provided. Each parameter is shadowed my a mutable variable inside the command
+body.
+
+
+
+"""
+nb_save
diff --git a/docs/book/command_loops/loops.nim b/docs/book/command_loops/loops.nim
new file mode 100644
index 00000000..d5a13780
--- /dev/null
+++ b/docs/book/command_loops/loops.nim
@@ -0,0 +1,143 @@
+import nimib, nimibook
+import enuib
+
+nb_init(theme = use_enu)
+nb_text: """
+
+# Loops
+
+Command Loops can help manage the complexity of the logic for your units. They
+allow you to run complicated lists of commands and switch between them easily
+when situations change.
+
+You can create your [own commands](commands.html), or you can call any of the
+built-in Enu [commands](../coding/commands.html) like `forward`, `back`, `turn`,
+`sleep`, etc.
+
+A Command Loop always has one and only one active command, which it will call
+repeatedly until you switch to some other command. The default command is
+`nil`. The first thing a loop must do is switch from `nil` to another command,
+using the little switch arrow `->`.
+
+```nim
+loop:
+ nil -> forward
+ # I'll go forward forever!
+```
+
+The little switch arrow (`->`) will switch from the command on the left to the
+command on the right if it's encountered and the left command has just
+completed. If the loop goes through and no switches match, the command will be
+run again.
+
+We can control which switches get run by putting them in `if` statements.
+
+```nim
+loop:
+ nil -> forward
+ if player.far:
+ forward -> back
+ if player.near:
+ back -> forward
+```
+
+In the above example, the loop immediately switches to `forward`, and will go
+forward indefinitely until one of the conditions is met and the command is
+switched to something else. If the player gets too far away (more than 100m) and
+the command is `forward`, the command will be switched to `back`. If the player
+is near (5m) and the command is `back`, it will switch to `forward`. However, if
+the player is near and the command is `forward`, nothing will change. The
+`if player.near` statement will be true, but `back -> forward` is ignored, since
+the current command isn't `back`.
+
+If you want your loop to end at some point, you can switch back to `nil`:
+
+```nim
+loop:
+ # Some commands can take parameters.
+ nil -> forward(10)
+ forward -> nil
+ # I'll run `forward(10)` a single time, then stop and end
+ # the loop.
+```
+
+Let's look at something more complicated and introduce the big switch arrow
+(`==>`) and switch blocks.
+
+```nim
+- wander:
+ speed = 1
+ forward 5..10
+ turn -45..45
+
+- charge:
+ speed = 5
+ turn player
+ forward 1
+
+- flee:
+ speed = 3
+ turn -player
+ forward 1
+
+- attack:
+ player.bounce 5
+ turn 360
+
+var health = 3
+
+loop:
+ nil -> wander
+ if 1 in 50:
+ # When each `wander` command finishes there's a 1 in 50
+ # (2%) chance of our unit getting a sudden burst of
+ # energy and switching to the `charge` command.
+ # Otherwise we just keep wandering.
+ wander -> charge
+
+ if health == 0:
+ # We died. Exit the loop. We want this to happen
+ # immediately, not after the command finishes, so we
+ # use the big switch arrow. We use the special `any`
+ # command to say that this should happen regardless of
+ # the running command.
+ any ==> nil
+
+ if player.hit:
+ # If the player touches us while we're wandering, we
+ # flee. We want it to happen the instant the player
+ # touches us, not when our current `wander` is done, so
+ # we use the big switch arrow.
+
+ wander ==> flee:
+ # This is a switch block. If the command switches
+ # here, the switch block will also run once.
+ health -= 1
+
+ # if the player touches us while we're charging, we
+ # attack immediately.
+ charge ==> attack
+
+ if player.far:
+ # If the player is far and we're fleeing, go back to
+ # wandering
+ flee -> wander
+
+ # Switch to wander when our attack is done. We always
+ # want this to happen, so it isn't in a conditional. It
+ # only does anything if the current command is `attack`,
+ # and we only do it when the attack is done becuase we're
+ # using the little switch arrow.
+ attack -> wander
+```
+
+
+Note for Nimions
+
+Command Loops are state machines, and any proc can be a state. If the proc
+has a return value it will be discarded.
+
+
+
+"""
+nb_save
diff --git a/docs/book/command_loops/more.nim b/docs/book/command_loops/more.nim
new file mode 100644
index 00000000..67085bfd
--- /dev/null
+++ b/docs/book/command_loops/more.nim
@@ -0,0 +1,67 @@
+import nimib, nimibook
+import enuib
+
+nb_init(theme = use_enu)
+nb_text: """
+
+# More about Command Loops
+
+### `as`
+
+Sometimes you want to run the same command in different situations and switch to
+or from it with a different name. You could do this by creating a new command
+that calls the first one, but you can also use `as` to give a command a
+different name.
+
+```nim
+# explore command and health var not shown.
+- flee(distance = 100):
+ turn -player
+ forward distance
+
+loop:
+ nil -> explore
+
+ if player.near and health > 2:
+ explore -> flee
+
+ elif player.near and health <= 2:
+ explore ==> flee(200) as fly_you_fool
+
+ if player.far(25):
+ flee -> explore
+
+ if player.far(150):
+ fly_you_fool -> explore
+```
+
+### Special from commands
+
+Often loops will switch from a single command to another. However, sometimes you
+want to allow switching from a variety of commands.
+
+- `any -> some_command` - switch from any command to the target command. This
+ will switch (and run any switch block) even if we're already running the
+ target command. In this example we used a little switch arrow, so it still
+ won't happen until the current command actually completes.
+
+- `others -> some_command` - Same as `any`, but it excludes the target command.
+
+- `(command1, command2)` - Multiple from cmmands can be supported by putting
+ them in a tuple.
+
+- `(any, -command2, -command3)` - Switch from any command except `command2` and
+ `command3`.
+
+### When do command loops run?
+
+A command loops will run whenever the current command finishes. In addition,
+command loops will run every 0.5 seconds, and when something "interesting"
+happens. Currently only the start and end of a collision with the player
+will cause the loop to be run immediately, but this will be expanded.
+
+When using child loops, the top level loop runs first, then walks down the stack
+of loops until the current loop is reached.
+
+"""
+nb_save
diff --git a/docs/book/intro/demos.md b/docs/book/demos.md
similarity index 100%
rename from docs/book/intro/demos.md
rename to docs/book/demos.md
diff --git a/docs/book/intro/demos.nim b/docs/book/demos.nim
similarity index 100%
rename from docs/book/intro/demos.nim
rename to docs/book/demos.nim
diff --git a/docs/book/example/shapes.nim b/docs/book/example/shapes.nim
deleted file mode 100644
index 36c99670..00000000
--- a/docs/book/example/shapes.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-import nimib, nimibook
-import enuib
-
-nb_init(theme = use_enu)
-nb_text: "# Shapes"
-nb_save
diff --git a/docs/book/goals.md b/docs/book/goals.md
new file mode 100644
index 00000000..6dcf43a0
--- /dev/null
+++ b/docs/book/goals.md
@@ -0,0 +1,30 @@
+# Goals
+
+Enu is meant for anyone who wants to explore, experiment, or make games, but
+particular care has been taken to make it usable by children However, rather
+than bypassing the keyboard with a Scratch-like visual programming language,
+Enu attempts to reduce and simplify the keystrokes required for a text-based
+language, while (hopefully) preserving most of the flexibility text-based code
+offers.
+
+With this in mind, Enu tries to:
+
+- Reduce nesting. Indentation can be tricky for new programmers.
+- Reduce the use of the shift key. Lower case is used almost everywhere.
+ Commands are written to avoid underscores and parenthesis. By default (for
+ now at least), a `;` keypress is interpreted as `:`, as `:` is used
+ frequently in Nim (and requires shift, at least on US English keyboards)
+ while `;` is not.
+
+- Omit or shorten identifier names. `me` instead of `self/this`. `-` instead
+ of `proc`. `5.times:` or `5.x:` instead of `for i in 0..5:`. Single letter
+ shortcuts for many common commands.
+
+- Pretends to avoid types. Enu code is Nim code and is statically typed, but
+ types are inferred or auto-converted where possible. Types are great,
+ but are confusing for new programmers.
+
+- Spatial organization. No files. Code is text, but it's accessed through an
+ object in the virtual world.
+
+- Avoids events. Tries to make all flow based on loops and conditionals.
diff --git a/docs/book/intro/goals.nim b/docs/book/goals.nim
similarity index 100%
rename from docs/book/intro/goals.nim
rename to docs/book/goals.nim
diff --git a/docs/book/intro.nim b/docs/book/intro.nim
index 941911e5..a5efecd6 100644
--- a/docs/book/intro.nim
+++ b/docs/book/intro.nim
@@ -6,25 +6,13 @@ nb_text: """
# Introduction
-3D live coding, implemented in Nim.
+Build 3D worlds in Nim.
![Enu Screenshot](assets/screenshot_3.webp)
Enu lets you build and explore worlds using a familiar block-building interface
-and a Logo inspired API. It aspires to make 3D development more accessible, and
-will eventually be usable to create standalone games.
-
-***Note:*** *The docs for Enu 0.2 are a work in progress. Most of the core ideas are here, but a fair bit of revision is required. The 0.2 docs will be targeted
-towards new programmers, with 'Note for Nimions` sections aimed at more
-experienced folks to explain what's really going on. However, things are all
-over the place right now, with the intended audience changing paragraph by
-paragraph.*
-
-***Notes for Nimions:*** *Enu tries to simplify some Nim concepts, mainly to defer
-explaining unfamiliar terms. In particular, Enu tries to hide most things
-related to types, calls procs 'actions', and avoids immutable variables. I
-believe this is the right approach for new programmers, but I expect that more
-sophisticated developers will use a style closer to traditional Nim.*
+and a Logo inspired API. It tries to make 3D development easier, and will
+eventually be able to create standalone games.
"""
nb_save
diff --git a/docs/book/intro/building.md b/docs/book/intro/building.md
index af6ffe2f..56014781 100644
--- a/docs/book/intro/building.md
+++ b/docs/book/intro/building.md
@@ -1,11 +1,9 @@
# Install
-Download from https://github.com/dsrw/enu/releases. The Windows version isn't signed, and
-UAC will warn that it's untrusted. This will be fixed in a future release.
-
-The Linux version hasn't been tested particularly well, but it works for me under Ubuntu 20.04. Please report any issues.
-
-The world format will change in a future release. Worlds created in 0.1 won't be supported in future versions.
+Download from
+[https://github.com/dsrw/enu/releases](https://github.com/dsrw/enu/releases).
+The Windows version isn't signed, and UAC will warn that it's untrusted. This
+will be fixed in a future release.
# Build and Run
@@ -18,7 +16,7 @@ $ nimble start
## Notes
-Enu requires a custom Godot version, which lives in `vendor/godot`. This will be fetched
-and built as part of `nimble prereqs`.
+Enu requires a custom Godot version, which lives in `vendor/godot`. This will
+be fetched and built as part of `nimble prereqs`.
See [Compiling Godot](https://docs.godotengine.org/en/3.5/development/compiling/index.html).
diff --git a/docs/book/intro/config.md b/docs/book/intro/config.md
new file mode 100644
index 00000000..c27c4c0f
--- /dev/null
+++ b/docs/book/intro/config.md
@@ -0,0 +1,66 @@
+# Config
+
+The Enu data directory lives in `~/Library/Application Support/enu` on Mac,
+`%AppData%\enu` on Windows, and `~/.local/share/enu` on Linux. `config.json` has
+a few configurable options:
+
+- `font_size`: The font size. DPI is currently ignored, so hidpi screens will
+ require a higher number.
+
+- `dock_icon_size`: Size of the icons in the dock. DPI is currently ignored, so
+ hidpi screens will require a higher number.
+
+- `world`: The world/project to load. Change this to create a new world. By
+ default, this will be the `base_name` plus `-` and a number. Set to
+ `tutorial-1` by default.
+
+- `show_stats`: Show FPS and other stats.
+
+- `god_mode`: Makes hidden units slightly visible, and allows editing locked
+ units.
+
+- `mega_pixels`: The render resolution, in mega pixels. Increase for more
+ detail. Decrease for better performance.
+
+- `start_full_screen`: Whether to start Enu full screen, or in a window.
+
+- `semicolon_as_colon`: Both `;` and `:` will be interpreted as `:`, allowing
+ `:` to be typed without shift. Sometimes useful for new typists.
+
+- `world_prefix`: The base name that Enu will use when creating or loading
+ a new level. Set to `tutorial` by default.
+
+- `listen_address`: Set this to something reasonable, like `0.0.0.0`, to be a
+ server. Optionally you can add a `:` and a port number. Defaults to port 9632.
+
+- `server_address`: Set this to an IP or host name to connect to a server.
+ Optionally you can add a `:` and a port number. Defaults to port 9632.
+
+- `player_color`: The color of your avatar when in multiplayer. Set to a hex
+ color (like `#ffffff`) or one of Enu's built in colors (`blue`, `red`,
+ `green`, `black`, `white`, and `brown`). Defaults to a random color.
+
+- `channel_size`: A debug option to configure the size of the channel between
+ the worker and the view. 5000 by default. Increasing this may improve
+ performance at the cost of latency and memory usage. This option will probably
+ go away in the future.
+
+## Example
+
+```json
+{
+ "font_size": 38,
+ "dock_icon_size": 193.75,
+ "world": "tutorial-1",
+ "show_stats": true,
+ "god_mode": false,
+ "mega_pixels": 5.0,
+ "start_full_screen": false,
+ "semicolon_as_colon": false,
+ "world_prefix": "tutorial",
+ "listen_address": "",
+ "server_address": "",
+ "player_color": "#6495ED",
+ "channel_size": 5000
+}
+```
diff --git a/docs/book/basics/config.nim b/docs/book/intro/config.nim
similarity index 100%
rename from docs/book/basics/config.nim
rename to docs/book/intro/config.nim
diff --git a/docs/book/intro/controls.md b/docs/book/intro/controls.md
new file mode 100644
index 00000000..bad9c321
--- /dev/null
+++ b/docs/book/intro/controls.md
@@ -0,0 +1,54 @@
+# Usage
+
+## Keyboard/Mouse
+
+- `ESC` - toggle mouse capture and to dismiss editor windows. Reloads script
+ changes.
+- `W`, `A`, `S`, `D` - move around when mouse is captured.
+- `Space` - jump. Double jump to toggle flying. Hold to go up while flying.
+- `Shift` - run.
+- `C` - go down while flying.
+- `~` - toggle the console.
+- `F` - toggle fullscreen.
+- `1` - enter edit mode.
+- `2 - 9` - change active action.
+- `Mouse Wheel Up` / `Down` - change active action.
+- `Alt` / `Option` - reload script changes. Hold to temporarily capture the
+ mouse and move, so you can change your view without having to switch away from
+ what you're doing.
+- `Cmd`+`P` / `Ctrl`+`P` - Pause scripts.
+- `Cmd`+`Shift`+`S` / `Ctrl`+`Shift`+`S` - Save and reload all scripts, then
+ pause. If you have a script that makes a unit
+ inaccessible (ex. moves the unit below the ground) this is a way to get things
+ back to their start positions so they can be edited.
+- `Left Click` - Place a block/object or open the code for the currently
+ selected object.
+- `Right Click` - Remove a block/object.
+
+## XBox / Playstation Controller
+
+- `Left Stick` - move.
+- `Right Stick` - change view.
+- `A` / `X` - jump. Double jump to toggle flying. Hold to go up while flying.
+- `B` / `◯` - go down while flying. Dismiss code editor.
+- `Y` / `△` - toggle edit mode.
+- `L1` / `R1` - change active action.
+- `L2` - place a block/object or open the code for the currently selected
+ object.
+- `R2` - remove a block/object.
+- `L3` - run.
+
+Enu currently includes 6 block types/colors, and 1 object model (a robot). This
+will be expanded in the future.
+
+## Building and Placing Units
+
+Drop a block or robot with the left mouse button/controller trigger, remove it
+with the right. Adjoining blocks will be combined into a single structure. With
+the mouse captured, building works more or less like MineCraft. Release the
+mouse by pressing ESC to draw blocks using the mouse cursor.
+
+Code by switching to the code tool by left clicking/triggering on an object or
+structure. Changes are applied when the code window is closed (ESC key) or CTRL
+is pressed. Holding CTRL will also temporarily grab the mouse and allow you to
+change your position.
diff --git a/docs/book/basics/controls.nim b/docs/book/intro/controls.nim
similarity index 100%
rename from docs/book/basics/controls.nim
rename to docs/book/intro/controls.nim
diff --git a/docs/book/examples.md b/docs/book/intro/examples.md
similarity index 71%
rename from docs/book/examples.md
rename to docs/book/intro/examples.md
index 40e41331..08f63977 100644
--- a/docs/book/examples.md
+++ b/docs/book/intro/examples.md
@@ -1,22 +1,26 @@
# Examples
-*TODO: Include examples of new 0.2 functionality*
-
Draw a square:
+
```nim
forward 10
right 10
back 10
left 10
```
+
or:
+
```nim
4.times:
forward 10
- turn_right()
+ turn right
```
-![Square Example](assets/square_example.png)
+
+![Square Example](../assets/square_example.png)
+
Create a twisting tower:
+
```nim
var
length = 20
@@ -33,36 +37,47 @@ height.times:
turn 5
up 1
```
-![Tower Example](assets/tower_example.png)
+
+![Tower Example](../assets/tower_example.png)
Draw randomly:
+
```nim
up 10
forward 10
+
(50..100).times:
forward 2..5
turn -180..180
up 0..2
```
-![Random Example](assets/random_example.png)
+
+![Random Example](../assets/random_example.png)
Set the color to blue randomly with a 1 in 50 chance. Otherwise set it to white:
+
```nim
if 1 in 50:
color = blue
+
else:
color = white
```
+
or as a one-liner:
+
```nim
color = if 1 in 50: blue else: white
```
-![Random Color Example](assets/random_color_example.png)
+
+![Random Color Example](../assets/random_color_example.png)
Move forward 10 times, cycling through colors:
+
```nim
10.times:
color = cycle(red, black, blue)
forward 1
```
-![Color Cycle Example](assets/cycle_example.png)
+
+![Color Cycle Example](../assets/cycle_example.png)
diff --git a/docs/book/examples.nim b/docs/book/intro/examples.nim
similarity index 100%
rename from docs/book/examples.nim
rename to docs/book/intro/examples.nim
diff --git a/docs/book/intro/goals.md b/docs/book/intro/goals.md
deleted file mode 100644
index 12daa330..00000000
--- a/docs/book/intro/goals.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Goals
-
-Enu is meant for anyone who wants to explore, experiment, or make games, but particular care has been taken to make
-it usable by younger people who may struggle with reading or typing. However, rather than bypassing the keyboard with
-a Scratch-like visual programming language, Enu attempts to reduce and simplify the keystrokes required for a text-based
-language, while (hopefully) preserving most of the flexibility text-based code offers.
-
-With this in mind, Enu tries to:
-
-- Reduce nesting. Indentation can be tricky for new programmers.
-- Reduce the use of the shift key. Lower case is used almost everywhere. Commands are written to
- avoid underscores and parenthesis. By default (for now at least), a `;` keypress is interpreted as `:`, as colons are
- required frequently in Nim (and require shift, at least on US English keyboards) while semi-colons are not.
-
-- Omit or shorten identifier names. `me` instead of `self/this`. `-` instead of `proc`. `5.times:` or `5.x:` instead of
- `for i in 0..5:`. Single letter shortcuts for many common commands.
-
-- Pretends to avoid types. Enu code is Nim code and is statically typed, but a fair amount of effort has been spent
- hiding this fact. Types are great, but are confusing for new programmers.
-
-- Spatial organization. No files. Code is text, but it's accessed through an object in the virtual world.
-
-- Avoids events. Tries to make all flow based on loops and conditionals.
diff --git a/docs/book/intro/multiplayer.md b/docs/book/intro/multiplayer.md
new file mode 100644
index 00000000..8e8e7a45
--- /dev/null
+++ b/docs/book/intro/multiplayer.md
@@ -0,0 +1,14 @@
+# Multiplayer (Experimental)
+
+It's possible to run Enu as a server by setting the `listen_address` to
+`0.0.0.0` in your [config file](config.html). Other players can then connect
+to your server by putting your IP or hostname in the `server_address` setting
+in their config. This still has bugs, and performance isn't great. It's also
+completely insecure (see below).
+
+## Warning!
+
+Enu's multiplayer is insecure. A bad actor could delete or steal your data by
+connecting to your Enu server, or if you connect to theirs. This will be fixed,
+but for now it's important to only connect to Enu players you trust, or to use
+a device with no important data.
diff --git a/docs/book/intro/multiplayer.nim b/docs/book/intro/multiplayer.nim
new file mode 100644
index 00000000..9c3f6578
--- /dev/null
+++ b/docs/book/intro/multiplayer.nim
@@ -0,0 +1,2 @@
+import enuib
+md "multiplayer.md"
diff --git a/docs/book/todo.md b/docs/book/todo.md
index c7b6b6ff..51d7265f 100644
--- a/docs/book/todo.md
+++ b/docs/book/todo.md
@@ -1,31 +1,59 @@
-# TODO for 0.2
+# TODO
+
+There's a lot left to do before Enu 1.0. Here are some of the tasks planned
+for the next year or so.
+
+### Security and Sharing
+
+Right now there's no easy way to download and play Enu worlds created by other
+people. It can be done manually by copying a world into Enu's
+[config directory](intro/config.html), then updating the `world` property in
+`config.json`, but at the moment Enu scripts can read, write, and delete files
+on your computer, so it's very important to only play an Enu world if you trust
+it completely. The same applies when playing
+[multiplayer](intro/multiplayer.html).
+
+Sometime before 0.3, Enu will be updated to run in a sandbox, and the script
+engine will be locked down to restrict dangerous operations. When this is done
+I'll make it easier to share worlds and find servers to connect to.
+
+### Tutorials
+
+More tutorials!
+
+Tutorials!
+
+### Games and Game Features
+
+Write games, and add features to support them.
+
+### Forward Arrow
+
+Show an arrow pointing forward from the current draw point, so it
+will always be clear which way `forward` goes.
### Pivot point
-Currently it isn't possible to change the pivot point for a unit, and the default point isn't properly centered for
-most builds, making it difficult to rotate builds nicely. Enu 0.2 will use the draw point for the pivot point, allowing
-it to be moved, and will shift everything over 0.5m, allowing most builds to rotate in a balanced way. There will
-also be a command to move the draw point (and thus the pivot point) in the exact center of a build.
+Currently it isn't possible to change the pivot point for a unit, and the
+default point isn't properly centered for most builds, making it difficult to
+rotate builds nicely. Enu 0.3 will use the draw point for the pivot point,
+allowing it to be moved, and will shift everything over 0.5m, allowing most
+builds to rotate in a balanced way. There will also be a command to move the
+draw point (and thus the pivot point) in the exact center of a build.
+
+### REPL
+
+A REPL could be used to experiment, run commands, and change settings.
-### REPL?
+### Additional Platforms and App Stores
-We need a way to switch worlds without editing a config file. Adding a REPL may be the easiest way to accomplish
-this, and is something I wanted to add anyway.
+Shortly after 0.2 Enu will be made available on the Mac App Store and Steam. iOS
+and iPadOS will likely be next, with Android and VR platforms following later.
-### Testing and bug fixes
+### Graphics
-Enu has been under heavy development for a year without a great deal of testing, so there are undoubtedly bugs. I
-believe these will be minor, but there are probably a fair number of them.
+Enu's graphics can be slow, especially when using lots of units.
-### v0.2.x - v0.3
+### Other Stuff
-- [ ] iOS support.
-- [ ] Move script execution off the main thread.
-- [ ] Inventory
-- [ ] Settings UI
-- [ ] Allow the editor pane and action bar to be resized from within Enu.
-- [ ] Better collision support
-- [ ] Blocks of any color
-- [ ] In game help
-- [ ] Easy way to switch worlds in-game
-- [ ] Support loading worlds from anywhere, not just the Enu data directory
+Other stuff.
diff --git a/vmlib/enu/base_api.nim b/vmlib/enu/base_api.nim
index b2da1069..fc72b27f 100644
--- a/vmlib/enu/base_api.nim
+++ b/vmlib/enu/base_api.nim
@@ -3,7 +3,6 @@ import random as rnd except rand
import types, state_machine, base_bridge, base_bridge_private
export base_bridge
-template f*(body): untyped = fmt(body)
proc `position=`*(self: Unit, position: Vector3) = self.position_set(position)
proc `position=`*(self: Unit, unit: Unit) = self.position_set(unit.position)