From 196302ad9f95c6e30d20822c0ae9d6f59f1c738a Mon Sep 17 00:00:00 2001 From: Scott Wadden Date: Sun, 10 Sep 2023 00:02:55 -0300 Subject: [PATCH] Final(ish) 0.2 docs --- README.md | 2 +- docs/book.nim | 30 ++-- docs/book/action_loops.nim | 2 - docs/book/action_loops/actions.nim | 51 ------- docs/book/action_loops/child_loops.nim | 73 --------- docs/book/action_loops/loops.nim | 108 ------------- docs/book/action_loops/more.nim | 55 ------- docs/book/assets/css/enu.css | 72 +++++---- docs/book/basics.nim | 6 - docs/book/basics/config.md | 18 --- docs/book/basics/controls.md | 47 ------ docs/book/basics/programming.nim | 6 - docs/book/coding.md | 103 +++++++------ docs/book/coding/commands.nim | 95 +++++++----- docs/book/coding/concepts.nim | 46 ++++-- docs/book/coding/random_numbers.nim | 36 +++-- docs/book/coding/shorthand.nim | 7 +- .../{action_loops.md => command_loops.md} | 9 +- docs/book/command_loops.nim | 2 + .../arrows.nim | 6 +- docs/book/command_loops/child_loops.nim | 88 +++++++++++ docs/book/command_loops/commands.nim | 52 +++++++ docs/book/command_loops/loops.nim | 143 ++++++++++++++++++ docs/book/command_loops/more.nim | 67 ++++++++ docs/book/{intro => }/demos.md | 0 docs/book/{intro => }/demos.nim | 0 docs/book/example/shapes.nim | 6 - docs/book/goals.md | 30 ++++ docs/book/{intro => }/goals.nim | 0 docs/book/intro.nim | 18 +-- docs/book/intro/building.md | 14 +- docs/book/intro/config.md | 66 ++++++++ docs/book/{basics => intro}/config.nim | 0 docs/book/intro/controls.md | 54 +++++++ docs/book/{basics => intro}/controls.nim | 0 docs/book/{ => intro}/examples.md | 31 +++- docs/book/{ => intro}/examples.nim | 0 docs/book/intro/goals.md | 23 --- docs/book/intro/multiplayer.md | 14 ++ docs/book/intro/multiplayer.nim | 2 + docs/book/todo.md | 72 ++++++--- vmlib/enu/base_api.nim | 1 - 42 files changed, 835 insertions(+), 620 deletions(-) delete mode 100644 docs/book/action_loops.nim delete mode 100644 docs/book/action_loops/actions.nim delete mode 100644 docs/book/action_loops/child_loops.nim delete mode 100644 docs/book/action_loops/loops.nim delete mode 100644 docs/book/action_loops/more.nim delete mode 100644 docs/book/basics.nim delete mode 100644 docs/book/basics/config.md delete mode 100644 docs/book/basics/controls.md delete mode 100644 docs/book/basics/programming.nim rename docs/book/{action_loops.md => command_loops.md} (59%) create mode 100644 docs/book/command_loops.nim rename docs/book/{action_loops => command_loops}/arrows.nim (58%) create mode 100644 docs/book/command_loops/child_loops.nim create mode 100644 docs/book/command_loops/commands.nim create mode 100644 docs/book/command_loops/loops.nim create mode 100644 docs/book/command_loops/more.nim rename docs/book/{intro => }/demos.md (100%) rename docs/book/{intro => }/demos.nim (100%) delete mode 100644 docs/book/example/shapes.nim create mode 100644 docs/book/goals.md rename docs/book/{intro => }/goals.nim (100%) create mode 100644 docs/book/intro/config.md rename docs/book/{basics => intro}/config.nim (100%) create mode 100644 docs/book/intro/controls.md rename docs/book/{basics => intro}/controls.nim (100%) rename docs/book/{ => intro}/examples.md (71%) rename docs/book/{ => intro}/examples.nim (100%) delete mode 100644 docs/book/intro/goals.md create mode 100644 docs/book/intro/multiplayer.md create mode 100644 docs/book/intro/multiplayer.nim 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)