diff --git a/.gitignore b/.gitignore
index 50160e5..e8b2308 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@ fonts/
maps/
bin/
dist/
+dev-assets/guidebook/venv
+dev-assets/guidebook/compiled/pages
wasm/assets/
*.wasm
*.doodad
diff --git a/dev-assets/doodads/doors/electric.gif b/dev-assets/doodads/doors/electric.gif
new file mode 100644
index 0000000..c280b2d
Binary files /dev/null and b/dev-assets/doodads/doors/electric.gif differ
diff --git a/dev-assets/doodads/trapdoors/down.gif b/dev-assets/doodads/trapdoors/down.gif
new file mode 100644
index 0000000..07a1fc9
Binary files /dev/null and b/dev-assets/doodads/trapdoors/down.gif differ
diff --git a/dev-assets/guidebook/build.py b/dev-assets/guidebook/build.py
new file mode 100644
index 0000000..d3789fd
--- /dev/null
+++ b/dev-assets/guidebook/build.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+
+import codecs
+import glob
+import os
+import markdown
+import jinja2
+import re
+
+def main():
+ if not os.path.isdir("./compiled"):
+ print("Make output directory: ./compiled")
+ os.mkdir("./compiled")
+ os.mkdir("./compiled/pages")
+
+ # Render the main index.html template.
+ with codecs.open("./pages/index.html", "r", "utf-8") as fh:
+ html = fh.read()
+ html = render_template(html)
+ with open("./compiled/index.html", "w") as outfh:
+ outfh.write(html)
+
+ # Load the Markdown wrapper HTML template.
+ html_wrapper = "$CONTENT"
+ with codecs.open("./pages/markdown.html", "r", "utf-8") as fh:
+ html_wrapper = fh.read()
+
+ for md in glob.glob("./pages/*.md"):
+ filename = md.split(os.path.sep)[-1]
+ htmlname = filename.replace(".md", ".html")
+ print("Compile Markdown: {} -> {}".format(filename, htmlname))
+
+ with codecs.open(md, 'r', 'utf-8') as fh:
+ data = fh.read()
+ rendered = markdown.markdown(data,
+ extensions=["codehilite", "fenced_code"],
+ )
+ html = html_wrapper.replace("$CONTENT", rendered)
+ html = render_template(html,
+ title=title_from_markdown(data),
+ )
+
+ with open(os.path.join("compiled", "pages", htmlname), "w") as outfh:
+ outfh.write(html)
+
+jinja_env = jinja2.Environment()
+
+def render_template(input, *args, **kwargs):
+ templ = jinja_env.from_string(input)
+ return templ.render(
+ app_name="Project: Doodle",
+ app_version=get_app_version(),
+ *args, **kwargs
+ )
+
+def title_from_markdown(text):
+ """Retrieve the title from the first Markdown header."""
+ for line in text.split("\n"):
+ if line.startswith("# "):
+ return line[2:]
+
+def get_app_version():
+ """Get the app version from pkg/branding/branding.go in Doodle"""
+ ver = re.compile(r'Version\s*=\s*"(.+?)"')
+ with codecs.open("../../pkg/branding/branding.go", "r", "utf-8") as fh:
+ text = fh.read()
+ for line in text.split("\n"):
+ m = ver.search(line)
+ if m:
+ return m[1]
+
+if __name__ == "__main__":
+ main()
diff --git a/dev-assets/guidebook/build.sh b/dev-assets/guidebook/build.sh
new file mode 100755
index 0000000..11d620b
--- /dev/null
+++ b/dev-assets/guidebook/build.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if [[ ! -d "./venv" ]]; then
+ echo Creating Python virtualenv...
+ python3 -m venv ./venv
+ source ./venv/bin/activate
+ pip install -r requirements.txt
+else
+ source ./venv/bin/activate
+fi
+
+python build.py
+
+# Copy static files in.
+mkdir -p compiled/pages/res
+cp -r pages/res/*.* compiled/pages/res/
diff --git a/dev-assets/guidebook/compiled/index.html b/dev-assets/guidebook/compiled/index.html
new file mode 100644
index 0000000..cabb96c
--- /dev/null
+++ b/dev-assets/guidebook/compiled/index.html
@@ -0,0 +1,28 @@
+
+
+
+ Project: Doodle Guidebook
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dev-assets/guidebook/pages/DoodadScripts.md b/dev-assets/guidebook/pages/DoodadScripts.md
new file mode 100644
index 0000000..c4dece8
--- /dev/null
+++ b/dev-assets/guidebook/pages/DoodadScripts.md
@@ -0,0 +1,130 @@
+# Doodad Scripts
+
+Doodads are programmed using JavaScript which gives them their behavior
+and ability to interact with the player and other doodads.
+
+An example Doodad script looks like the following:
+
+```javascript
+// The main function is called when the doodad is initialized in Play Mode
+// at the start of the level.
+function main() {
+ // Important global variables:
+ // - Self: information about the current Doodad running this script.
+ // - Events: handle events raised during gameplay.
+ // - Message: publish or subscribe to named messages to interact with
+ // other doodads.
+
+ // Logs go to the game's log file (standard output on Linux/Mac).
+ console.log("%s initialized!", Self.Doodad.Title);
+
+ // If our doodad has 'solid' parts that should prohibit movement,
+ // define the hitbox here. Coordinates are relative so 0,0 is the
+ // top-left pixel of the doodad's sprite.
+ Self.SetHitbox(0, 0, 64, 12);
+
+ // Handle a collision when another doodad (or player) has entered
+ // the space of our doodad.
+ Events.OnCollide(function(e) {
+ // The `e` object holds information about the event.
+ console.log("Actor %s has entered our hitbox!", e.Actor.ID());
+
+ // InHitbox is `true` if we defined a hitbox for ourselves, and
+ // the colliding actor is inside of the hitbox we defined.
+ if (e.InHitbox) {
+ // To prohibit movement, return false from the OnCollide handler.
+ // If you don't return false, the actor is allowed to keep on
+ // moving through.
+ return false;
+ }
+ });
+
+ // OnLeave is called when an actor, who was previously colliding with
+ // us, is no longer doing so.
+ Events.OnLeave(function(e) {
+ console.log("Actor %s has stopped colliding!", e.Actor.ID());
+ })
+}
+```
+
+# JavaScript API
+
+## Global Variables
+
+The following global variables are available to all Doodad scripts.
+
+### Self
+
+Self holds information about the current doodad. The full surface area of
+the Self object is subject to change, but some useful things you can access
+from it include:
+
+* Self.Doodad: a pointer to the doodad's file data.
+ * Self.Doodad.Title: get the title of the doodad file.
+ * Self.Doodad.Author: the name of the author who wrote the doodad.
+ * Self.Doodad.Script: the doodad's JavaScript source code. Note that
+ modifying this won't have any effect in-game, as the script had already
+ been loaded into the interpreter.
+ * Self.Doodad.GameVersion: the version of {{ app_name }} that was used
+ when the doodad was created.
+
+### Events
+
+### Message
+
+## Global Functions
+
+The following useful functions are also available globally:
+
+### Timers and Intervals
+
+Doodad scripts implement setTimeout() and setInterval() functions similar
+to those found in web browsers.
+
+```javascript
+// Call a function after 5 seconds.
+setTimeout(function() {
+ console.log("I've been called!");
+}, 5000);
+```
+
+setTimeout() and setInterval() return an ID number for the timer created.
+If you wish to cancel a timer before it has finished, or to stop an interval
+from running, you need to pass its ID number into `clearTimeout()` or
+`clearInterval()`, respectively.
+
+```javascript
+// Start a 1-second interval
+var id = setInterval(function() {
+ console.log("Tick...");
+}, 1000);
+
+// Cancel it after 30 seconds.
+setTimeout(function() {
+ clearInterval(id);
+}, 30000);
+```
+
+### Console Logging
+
+Doodad scripts also implement the `console.log()` and similar functions as
+found in web browser APIs. They support "printf" style variable placeholders.
+
+```javascript
+console.log("Hello world!");
+console.error("The answer is %d!", 42);
+console.warn("Actor '%s' has collided with us!", e.Actor.ID());
+console.debug("This only logs when the game is in debug mode!");
+```
+
+### RGBA(red, green, blue, alpha uint8)
+
+RGBA initializes a Color variable using the game's native Color type. May
+be useful for certain game APIs that take color values.
+
+Example: RGBA(255, 0, 255, 255) creates an opaque magenta color.
+
+### Point(x, y int)
+
+Returns a Point object which refers to a location in the game world. This
+type is required for certain game APIs.
diff --git a/dev-assets/guidebook/pages/Home.md b/dev-assets/guidebook/pages/Home.md
new file mode 100644
index 0000000..c7be728
--- /dev/null
+++ b/dev-assets/guidebook/pages/Home.md
@@ -0,0 +1,9 @@
+# Guidebook to {{ app_name }}
+
+This is the users manual to {{ app_name }}, a drawing-based maze game.
+
+## Creating Custom Content
+
+* [Doodad Scripts](DoodadScripts.html)
+
+v{{ app_version }}
diff --git a/dev-assets/guidebook/pages/index.html b/dev-assets/guidebook/pages/index.html
new file mode 100644
index 0000000..bb332e4
--- /dev/null
+++ b/dev-assets/guidebook/pages/index.html
@@ -0,0 +1,28 @@
+
+
+
+ {{ app_name }} Guidebook
+
+
+
+
+
+
+