Noah Petherbridge
4 years ago
commit
5d6e118bb0
15 changed files with 928 additions and 0 deletions
@ -0,0 +1 @@ |
|||||
|
site/ |
@ -0,0 +1,18 @@ |
|||||
|
SHELL := /bin/bash |
||||
|
|
||||
|
ALL: build |
||||
|
|
||||
|
# `make serve` to serve the mkdocs site on localhost.
|
||||
|
.PHONY: serve |
||||
|
serve: |
||||
|
mkdocs serve |
||||
|
|
||||
|
# `make build` to build the static documentation site.
|
||||
|
.PHONY: build |
||||
|
build: |
||||
|
mkdocs build |
||||
|
|
||||
|
# `make clean` cleans everything up.
|
||||
|
.PHONY: clean |
||||
|
clean: |
||||
|
rm -rf site |
@ -0,0 +1,11 @@ |
|||||
|
# Guidebook |
||||
|
|
||||
|
This is `mkdocs` sources for the user guidebook. |
||||
|
|
||||
|
## Setup |
||||
|
|
||||
|
Install mkdocs, i.e. `dnf install mkdocs` on Fedora and use the Makefile: |
||||
|
|
||||
|
* `make build` renders the website |
||||
|
* `make serve` runs the mkdocs development server |
||||
|
* `make clean` cleans up the rendered website |
@ -0,0 +1,3 @@ |
|||||
|
# About Project: Doodle |
||||
|
|
||||
|
Lorem ipsum. |
@ -0,0 +1,20 @@ |
|||||
|
# Drawing Doodads in an External Program |
||||
|
|
||||
|
Doodad sprites can be drawn using any image editor and saved as .png files |
||||
|
(with transparency). You can then create a doodad file from your series of |
||||
|
PNG images. |
||||
|
|
||||
|
Most of the built-in doodads that ship with the game were created in this way. |
||||
|
|
||||
|
## Create a Doodad from Images |
||||
|
|
||||
|
Save each frame of your doodad as a separate PNG image and then use the `doodad` |
||||
|
command-line tool to convert them to a `.doodad` file. |
||||
|
|
||||
|
```bash |
||||
|
# Usage: |
||||
|
doodad convert [options] <inputs.png> <output.doodad> |
||||
|
|
||||
|
# Example: |
||||
|
doodad convert door-closed.png door-open.png door.doodad |
||||
|
``` |
@ -0,0 +1,27 @@ |
|||||
|
# Drawing a Doodad In-Game |
||||
|
|
||||
|
Project: Doodle has some **limited** support to draw your doodad sprites |
||||
|
in-game. Currently you can only draw one frame (image) for the doodad |
||||
|
and save it to disk. |
||||
|
|
||||
|
To start a new doodad, open the game and enter the level editor. |
||||
|
|
||||
|
Select the "New Doodad" button at the top of the screen to start drawing a |
||||
|
new doodad. Choose the size (square) of its sprite when prompted. |
||||
|
|
||||
|
Doodads saved in-game go in your user config directory for the game. On Linux, |
||||
|
this is at ~/.config/doodle. |
||||
|
|
||||
|
If you want to create a doodad with multiple frames (to animate it or have |
||||
|
varying states that change the doodad's appearance in the level), the |
||||
|
`doodad` tool is recommended. See |
||||
|
[drawing images in an external program](edit-external.md). |
||||
|
|
||||
|
## Future Planned Features |
||||
|
|
||||
|
Creating doodads in-game is intended to be a fully supported feature. The |
||||
|
following features are planned to be supported: |
||||
|
|
||||
|
* Support editing multiple frames instead of only the first frame. |
||||
|
* Implement some features only available on the `doodad` tool using in-game |
||||
|
UI, such as attaching JavaScripts to the doodad. |
@ -0,0 +1,43 @@ |
|||||
|
# Creating Custom Doodads |
||||
|
|
||||
|
Project: Doodle is designed to be modder friendly and provides tools to help |
||||
|
you create your own custom doodads to use in your levels. |
||||
|
|
||||
|
You can draw the sprites for the doodad either in-game or using an external |
||||
|
image editor. Then, you can program their logic using JavaScript to make them |
||||
|
"do" stuff in-game and interact with the player and other doodads. |
||||
|
|
||||
|
* Drawing your Doodad's Sprites |
||||
|
* [In-Game](edit-in-game.md) |
||||
|
* [In an External Program](edit-external.md) |
||||
|
* Program its Behavior |
||||
|
* [JavaScript](scripts.md) |
||||
|
|
||||
|
## doodad (Command Line Tool) |
||||
|
|
||||
|
Your copy of the game should have shipped with a `doodad` command-line tool |
||||
|
bundled with it. On Windows it's called `doodad.exe` and should be in the same |
||||
|
folder as the game executable. On Mac OS, it is inside the .app bundle. |
||||
|
|
||||
|
The `doodad` tool provides a command-line interface to create and inspect |
||||
|
doodad and level files from the game. You'll need to use this tool, at the very |
||||
|
least, to attach a JavaScript to your doodad to make it "do" stuff in-game. |
||||
|
|
||||
|
You can create a doodad from PNG images on disk, attach or view the JavaScript |
||||
|
source on them, and view/edit metadata. |
||||
|
|
||||
|
```bash |
||||
|
# (the $ represents the shell prompt in a command-line terminal) |
||||
|
|
||||
|
# See metadata about a doodad file. |
||||
|
$ doodad show /path/to/custom.doodad |
||||
|
|
||||
|
# Create a new doodad based on PNG images on disk. |
||||
|
$ doodad convert frame0.png frame1.png frame2.png output.doodad |
||||
|
|
||||
|
# Add and view a custom script attached to the doodad. |
||||
|
$ doodad install-script index.js custom.doodad |
||||
|
$ doodad show --script custom.doodad |
||||
|
``` |
||||
|
|
||||
|
More info on the [`doodad` tool](../doodad-tool.md) here. |
@ -0,0 +1,564 @@ |
|||||
|
# Doodad Scripts |
||||
|
|
||||
|
Doodads are programmed using JavaScript which gives them their behavior |
||||
|
and ability to interact with the player and other doodads. |
||||
|
|
||||
|
Doodad scripts are run during "Play Mode" when a level _containing_ the doodad |
||||
|
is being played. You can install a JavaScript (.js) file into a doodad using |
||||
|
the command-line `doodad` program. |
||||
|
|
||||
|
An example Doodad script looks like the following: |
||||
|
|
||||
|
```javascript |
||||
|
// main() is called on level initialization for each |
||||
|
// instance ("actor") of the doodad. |
||||
|
function main() { |
||||
|
// Logs go to the game's log file (standard output on Linux/Mac). |
||||
|
console.log("%s initialized!", Self.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; |
||||
|
} |
||||
|
|
||||
|
// When movement is finalized, OnCollide is called one final time |
||||
|
// with e.Settled=true; it is only then that a doodad should run |
||||
|
// event handlers for a logical collide event. |
||||
|
if (e.Settled) { |
||||
|
// do something |
||||
|
Message.Publish("power", true); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 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()); |
||||
|
}) |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
# Installing a Doodad Script |
||||
|
|
||||
|
Use the command-line `doodad` tool to attach a script to your doodad file: |
||||
|
|
||||
|
```bash |
||||
|
# Attach the JavaScript at "script.js" to the doodad file "filename.doodad" |
||||
|
doodad install-script script.js filename.doodad |
||||
|
|
||||
|
# To view the script currently attached to a doodad |
||||
|
# (prints the script to your terminal) |
||||
|
doodad show --script filename.doodad |
||||
|
``` |
||||
|
|
||||
|
# Testing Your Script |
||||
|
|
||||
|
The best way to test your doodad script is to use it in a level! |
||||
|
|
||||
|
Run the game in a console to watch the log output, and you can use functions |
||||
|
like `console.log()` in your script to help debug issues. Drag your custom |
||||
|
doodad into a level and playtest it! Your script's main() function is called |
||||
|
when the level instance of your doodad is initialized. |
||||
|
|
||||
|
# JavaScript API |
||||
|
|
||||
|
The following global variables are available to all Doodad scripts. |
||||
|
|
||||
|
## Self |
||||
|
|
||||
|
Self holds data about the current doodad instance loaded inside of a level. |
||||
|
|
||||
|
**String attributes:** |
||||
|
|
||||
|
* Self.Title: the doodad title. |
||||
|
* Self.Filename: the doodad filename (useful for inventory items). |
||||
|
|
||||
|
Methods are below. |
||||
|
|
||||
|
### Self.ID() string |
||||
|
|
||||
|
Returns the "actor ID" of the doodad instance loaded inside of a level. This |
||||
|
is usually a random UUID string that was saved with the level data. |
||||
|
|
||||
|
### Self.GetTag(string name) string |
||||
|
|
||||
|
Return a "tag" that was saved with the doodad's file data. |
||||
|
|
||||
|
Tags are an arbitrary key/value data store attached to the doodad file. |
||||
|
You can use the `doodad.exe` tool shipped with the game to view and manage tags |
||||
|
on your own custom doodads: |
||||
|
|
||||
|
```bash |
||||
|
# Command-line doodad tool usage: |
||||
|
|
||||
|
# Show information about a doodad, look for the "Tags:" section. |
||||
|
doodad show filename.doodad |
||||
|
|
||||
|
# Set a tag. "-t" for short. |
||||
|
doodad edit-doodad --tag 'color=blue' filename.doodad |
||||
|
|
||||
|
# Set the tag to empty to remove it. |
||||
|
doodad edit-doodad -t 'color=' filename.doodad |
||||
|
``` |
||||
|
|
||||
|
This is useful for a set of multiple doodads to share the same script but |
||||
|
have different behavior depending on how each is tagged. |
||||
|
|
||||
|
### Self.Position() Point |
||||
|
|
||||
|
Returns the doodad's current position in the level. |
||||
|
|
||||
|
Point is an object with .X and .Y integer values. |
||||
|
|
||||
|
```javascript |
||||
|
var p = Self.Position() |
||||
|
console.log("I am at %d,%d", p.X, p.Y) |
||||
|
``` |
||||
|
|
||||
|
### Self.SetHitbox(x, y, w, h int) |
||||
|
|
||||
|
Configure the "solid hitbox" of this doodad. |
||||
|
|
||||
|
The X and Y coordinates are relative to the doodad's sprite: (0,0) is the top |
||||
|
left pixel of the doodad. The W and H are the width and height of the hitbox |
||||
|
starting at those coordinates. |
||||
|
|
||||
|
When another doodad enters the area of your doodad's sprite (for example, the |
||||
|
player character has entered the square shape of your doodad sprite) your script |
||||
|
begins to receive OnCollide events from the approaching actor. |
||||
|
|
||||
|
The OnCollide event tells you if the invading doodad is inside your custom |
||||
|
hitbox which you define here (`InHitbox`) making it easy to make choices based |
||||
|
on that status. |
||||
|
|
||||
|
Here's an example script for a hypothetical "locked door" doodad that acts |
||||
|
solid but only on a thin rectangle in the middle of its sprite: |
||||
|
|
||||
|
```javascript |
||||
|
// Example script for a "locked door" |
||||
|
function main() { |
||||
|
// Suppose the doodad's sprite size is 64x64 pixels square. |
||||
|
// The door is in side profile where the door itself ranges from pixels |
||||
|
// (20, 0) to (24, 64) |
||||
|
Self.SetHitbox(20, 0, 24, 64) |
||||
|
|
||||
|
// OnCollide handlers. |
||||
|
Events.OnCollide(function(e) { |
||||
|
// The convenient e.InHitbox tells you if the colliding actor is |
||||
|
// inside the hitbox we defined. |
||||
|
if (e.InHitbox) { |
||||
|
// Return false to protest the collision (act solid). |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Self.SetVelocity(Velocity) |
||||
|
|
||||
|
Set the doodad's velocity. Velocity is a type that can be created with the |
||||
|
Velocity() constructor, which takes an X and Y value: |
||||
|
|
||||
|
```javascript |
||||
|
Self.SetVelocity( Velocity(3.2, 7.0) ); |
||||
|
``` |
||||
|
|
||||
|
A positive X velocity propels the doodad to the right. A positive Y velocity |
||||
|
propels the doodad downward. |
||||
|
|
||||
|
### Self.SetMobile(bool) |
||||
|
|
||||
|
Call `SetMobile(true)` if the doodad will move on its own. |
||||
|
|
||||
|
This is for mobile doodads such as the player character and enemy mobs. |
||||
|
Stationary doodads like buttons, doors, and trapdoors do not mark themselves |
||||
|
as mobile. |
||||
|
|
||||
|
Mobile doodads incur extra work for the game doing collision checking so only |
||||
|
set this to `true` if your doodad will move (i.e. changes its Velocity or |
||||
|
Position). |
||||
|
|
||||
|
```javascript |
||||
|
Self.SetMobile(true); |
||||
|
``` |
||||
|
|
||||
|
### Self.SetGravity(bool) |
||||
|
|
||||
|
Set whether gravity applies to this doodad. By default doodads are stationary |
||||
|
and do not fall downwards. The player character and some mobile enemies that |
||||
|
want to be affected by gravity should opt in to this. |
||||
|
|
||||
|
```javascript |
||||
|
Self.SetGravity(true); |
||||
|
``` |
||||
|
|
||||
|
### Self.ShowLayer(index int) |
||||
|
|
||||
|
Switch the active layer of the doodad to the layer at this index. |
||||
|
|
||||
|
A doodad file can contain multiple layers, or images. The first and default |
||||
|
layer is at index zero, the second layer at index 1, and so on. |
||||
|
|
||||
|
```javascript |
||||
|
Self.ShowLayer(0); // 0 is the first and default layer |
||||
|
Self.ShowLayer(1); // show the second layer instead |
||||
|
``` |
||||
|
|
||||
|
### Self.ShowLayerNamed(name string) |
||||
|
|
||||
|
Switch the active layer by name instead of index. |
||||
|
|
||||
|
Each layer has an arbitrary name that it can be addressed by instead of needing |
||||
|
to keep track of the layer index. |
||||
|
|
||||
|
Doodads created by the command-line `doodad` tool will have their layers named |
||||
|
automatically by their file name. The layer **indexes** will retain the same |
||||
|
order of file names passed in, with 0 being the first file: |
||||
|
|
||||
|
```bash |
||||
|
# Doodad tool-created doodads have layers named after their file names. |
||||
|
# example "open-1.png" will be named "open-1" |
||||
|
doodad convert door.png open-1.png open-2.png open-3.png my-door.doodad |
||||
|
``` |
||||
|
|
||||
|
### Self.AddAnimation(name string, interval int, layers list) |
||||
|
|
||||
|
Register a named animation for your doodad. `interval` is the time in |
||||
|
milliseconds before going to the next frame. `layers` is an array of layer |
||||
|
names or indexes to be used for the animation. |
||||
|
|
||||
|
Doodads can animate by having multiple frames (images) in the same file. |
||||
|
Layers are ordered (layer 0 is the first, then increments from there) and |
||||
|
each has a name. This function can take either identifier to specify |
||||
|
which layers are part of the animation. |
||||
|
|
||||
|
```javascript |
||||
|
// Animation named "open" using named layers, 100ms delay between frames. |
||||
|
Self.AddAnimation("open", 100, ["open-1", "open-2", "open-3"]); |
||||
|
|
||||
|
// Animation named "close" using layers by index. |
||||
|
Self.AddAnimation("close", 100, [3, 2, 1]); |
||||
|
``` |
||||
|
|
||||
|
### Self.PlayAnimation(name string, callback func()) |
||||
|
|
||||
|
This starts playing the named animation. The callback function will be called |
||||
|
when the animation has completed. |
||||
|
|
||||
|
```javascript |
||||
|
Self.PlayAnimation("open", function() { |
||||
|
console.log("I've finished opening!"); |
||||
|
|
||||
|
// The callback is optional; use null if you don't need it. |
||||
|
Self.PlayAnimation("close", null); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### Self.IsAnimating() bool |
||||
|
|
||||
|
Returns true if an animation is currently being played. |
||||
|
|
||||
|
### Self.StopAnimation() |
||||
|
|
||||
|
Stops any currently playing animation. |
||||
|
|
||||
|
|
||||
|
* 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. |
||||
|
|
||||
|
### Self.Destroy() |
||||
|
|
||||
|
This destroys the current instance of the doodad as it appears in a level. |
||||
|
|
||||
|
For example, a Key destroys itself when it's picked up so that it disappears |
||||
|
from the level and can't be picked up again. Call this function when the |
||||
|
doodad instance should be destroyed and removed from the active level. |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
## Console Logging |
||||
|
|
||||
|
Like in node.js and the web browser, `console.log` and friends are available |
||||
|
for logging from a doodad script. Logs are emitted to the same place as the |
||||
|
game's logs are. |
||||
|
|
||||
|
```javascript |
||||
|
console.log("Hello world!"); |
||||
|
console.log("Interpolate strings '%s' and numbers '%d'", "string", 123); |
||||
|
console.debug("Debug messages shown when the game is in debug mode"); |
||||
|
console.warn("Warning-level messages"); |
||||
|
console.error("Error-level messages"); |
||||
|
``` |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
## Timers and Intervals |
||||
|
|
||||
|
Like in a web browser, functions such as setTimeout and setInterval are |
||||
|
supported in doodad scripts. |
||||
|
|
||||
|
### setTimeout(function, milliseconds int) int |
||||
|
|
||||
|
setTimeout calls your function after the specified number of milliseconds. |
||||
|
|
||||
|
1000ms are in one second. |
||||
|
|
||||
|
Returns an integer "timeout ID" that you'll need if you want to cancel the |
||||
|
timeout with clearTimeout. |
||||
|
|
||||
|
### setInterval(function, milliseconds int) int |
||||
|
|
||||
|
setInterval calls your function repeatedly after every specified number of |
||||
|
milliseconds. |
||||
|
|
||||
|
Returns an integer "interval ID" that you'll need if you want to cancel the |
||||
|
interval with clearInterval. |
||||
|
|
||||
|
### clearTimeout(id int) |
||||
|
|
||||
|
Cancels the timeout with the given ID. |
||||
|
|
||||
|
### clearInterval(id int) |
||||
|
|
||||
|
Cancels the interval with the given ID. |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
## Type Constructors |
||||
|
|
||||
|
Some methods may need data of certain native types that aren't available in |
||||
|
JavaScript. These global functions will initialize data of the correct types: |
||||
|
|
||||
|
### RGBA(red, green, blue, alpha uint8) |
||||
|
|
||||
|
Creates a Color type from red, green, blue and alpha values (integers between |
||||
|
0 and 255). |
||||
|
|
||||
|
### Point(x, y int) |
||||
|
|
||||
|
Creates a Point object with X and Y coordinates. |
||||
|
|
||||
|
### Vector(x, y float64) |
||||
|
|
||||
|
Creates a Vector object with X and Y dimensions. |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
## Global Functions |
||||
|
|
||||
|
Some useful globally available functions: |
||||
|
|
||||
|
### EndLevel() |
||||
|
|
||||
|
This ends the current level, i.e. to be used by the goal flag. |
||||
|
|
||||
|
### Flash(message string, args...) |
||||
|
|
||||
|
Flash a message on screen to the user. |
||||
|
|
||||
|
Flashed messages appear at the bottom of the screen and fade out after a few |
||||
|
moments. If multiple messages are flashed at the same time, they stack from the |
||||
|
bottom of the window with the newest message on bottom. |
||||
|
|
||||
|
Don't abuse this feature as spamming it may annoy the player. |
||||
|
|
||||
|
### GetTick() uint64 |
||||
|
|
||||
|
Returns the current game tick. This value started at zero when the game was |
||||
|
launched and increments every frame while running. |
||||
|
|
||||
|
### time.Now() time.Time |
||||
|
|
||||
|
This exposes the Go standard library function `time.Now()` that returns the |
||||
|
current date and time as a Go time.Time value. |
||||
|
|
||||
|
### time.Add(t time.Time, milliseconds int64) time.Time |
||||
|
|
||||
|
Add a number of milliseconds to a Go Time value. |
||||
|
|
||||
|
-------- |
||||
|
|
||||
|
## Event Handlers |
||||
|
|
||||
|
Doodad scripts can respond to certain events using functions on the global |
||||
|
`Events` variable. |
||||
|
|
||||
|
### Events.OnCollide( func(event) ) |
||||
|
|
||||
|
OnCollide is called when another actor is colliding with your doodad's sprite |
||||
|
box. The function is given a CollideEvent object which has the following |
||||
|
attributes: |
||||
|
|
||||
|
* Actor: the doodad which is colliding with your doodad. |
||||
|
* Overlap (Rect): a rectangle of where the two doodads' boxes are overlapping, |
||||
|
relative to your doodad sprite's box. That is, if the Actor was moving in from |
||||
|
the left side of your doodad, the X value would be zero and W would be the |
||||
|
number of pixels of overlap. |
||||
|
* InHitbox (bool): true if the colliding actor's hitbox is intersecting with |
||||
|
the hitbox you defined with SetHitbox(). |
||||
|
* Settled (bool): This is `false` when the game is trying to move the colliding |
||||
|
doodad and is sussing out whether or not your doodad will act solid and |
||||
|
protest its movement. When the game has settled the location of the colliding |
||||
|
doodad it will call OnCollide a final time with Settled=true. If your doodad |
||||
|
has special behavior when touched (i.e. a button that presses in), you should |
||||
|
wait until Settled=true before running your handler for that. |
||||
|
|
||||
|
### Events.OnLeave( func(event) ) |
||||
|
|
||||
|
Called when an actor that _was_ colliding with your doodad is no longer |
||||
|
colliding (or has left your doodad's sprite box). |
||||
|
|
||||
|
### Events.RunKeypress( func(event) ) |
||||
|
|
||||
|
Handle a keypress. `event` is an `event.State` from the render engine. |
||||
|
|
||||
|
TODO: document that. |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
## Pub/Sub Communication |
||||
|
|
||||
|
Doodads in a level are able to send and receive messages to other doodads, |
||||
|
either those that they are **linked** to or those that listen on a more |
||||
|
'broadcast' frequency. |
||||
|
|
||||
|
> **Linking** is when the level author connected two doodads together with |
||||
|
> the Link Tool. The two doodads' scripts can communicate with each other |
||||
|
> in-game over that link. |
||||
|
|
||||
|
For example, if the level author links a Button to an Electric Door, the button |
||||
|
can send a "power" event to the door so that it can open when a player touches |
||||
|
the button. |
||||
|
|
||||
|
Doodads communicate in a "publisher/subscriber" model: one doodad publishes an |
||||
|
event with a name and data, and other doodads subscribe to the named event to |
||||
|
receive that data. |
||||
|
|
||||
|
### Official, Standard Pub/Sub Messages |
||||
|
|
||||
|
The following message names and data types are used by the game's default |
||||
|
doodads. You're free to use these in your own custom doodads. |
||||
|
|
||||
|
If extending this list with your own custom events, be careful to choose a |
||||
|
unique namespace to prevent collision with other users' custom doodads and |
||||
|
their custom event names. |
||||
|
|
||||
|
| Name | Data Type | Description | |
||||
|
|------|-----------|--------------| |
||||
|
| power | boolean | Communicates a "powered" (true) or "not powered" state, as in a Button to an Electric Door. | |
||||
|
| broadcast:state-change | boolean | An "ON/OFF" button was hit and all state blocks should flip. | |
||||
|
|
||||
|
### Message.Publish(name string, data...) |
||||
|
|
||||
|
Publish a named message to all of your **linked** doodads. |
||||
|
|
||||
|
`data` is a list of arbitrary arguments to send with the message. |
||||
|
|
||||
|
```javascript |
||||
|
// Example button doodad that emits a "power" (bool) state to linked doodads |
||||
|
// that subscribe to this event. |
||||
|
function main() { |
||||
|
// When an actor collides with the button, emit a powered state. |
||||
|
Events.OnCollide(function(e) { |
||||
|
Message.Publish("power", true); |
||||
|
}); |
||||
|
|
||||
|
// When the actor leaves the button, turn off the power. |
||||
|
Events.OnLeave(function(e) { |
||||
|
Message.Publish("power", false); |
||||
|
}) |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Message.Subscribe(name string, function) |
||||
|
|
||||
|
Subscribe to a named message from any **linked** doodads. |
||||
|
|
||||
|
The function receives the data that was passed with the message. Its data type |
||||
|
is arbitrary and will depend on the type of message. |
||||
|
|
||||
|
```javascript |
||||
|
// Example electronic device doodad that responds to power from linked buttons. |
||||
|
function main() { |
||||
|
// Boolean to store if our electric device has juice. |
||||
|
var powered = false; |
||||
|
|
||||
|
// Do something while powered |
||||
|
setInterval(function() { |
||||
|
if (powered) { |
||||
|
console.log("Brmm...") |
||||
|
} |
||||
|
}, 1000); |
||||
|
|
||||
|
// Subscribe to the `power` event by a linked button or other power source. |
||||
|
Message.Subscribe("power", function(boolValue) { |
||||
|
console.log("Powered %s!", boolValue === true ? "on" : "off"); |
||||
|
powered = boolValue; |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Message.Broadcast(name string, data...) |
||||
|
|
||||
|
This publishes a named message to **every** doodad in the level, whether it |
||||
|
was linked to the broadcaster or not. |
||||
|
|
||||
|
For example the "ON/OFF" button globally toggles a boolean state in every |
||||
|
state block that subscribes to the `broadcast:state-change` event. |
||||
|
|
||||
|
If you were to broadcast an event like `power` it would activate every single |
||||
|
power-sensitive doodad on the level. |
||||
|
|
||||
|
```javascript |
||||
|
// Example two-state block that globally receives the state-change broadcast. |
||||
|
function main() { |
||||
|
var myState = false; |
||||
|
Message.Subscribe("broadcast:state-change", function(boolValue) { |
||||
|
// Most two-state blocks just flip their own state, ignoring the |
||||
|
// boolValue passed with this message. |
||||
|
myState = !myState; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// Example ON/OFF block that emits the state-change broadcast. It also |
||||
|
// subscribes to the event to keep its own state in sync with all the other |
||||
|
// ON/OFF blocks on the level when they get hit. |
||||
|
function main() { |
||||
|
var myState = false; |
||||
|
|
||||
|
// Listen for other ON/OFF button activations to keep our state in |
||||
|
// sync with theirs. |
||||
|
Message.Subscribe("broadcast:state-change", function(boolValue) { |
||||
|
myState = boolValue; |
||||
|
}); |
||||
|
|
||||
|
// When collided with, broadcast the state toggle to all state blocks. |
||||
|
Events.OnCollide(function(e) { |
||||
|
if (e.Settled) { |
||||
|
myState = !!myState; |
||||
|
Message.Broadcast("broadcast:state-change", myState); |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
``` |
@ -0,0 +1,99 @@ |
|||||
|
# Creating Custom Levels |
||||
|
|
||||
|
One of the core gameplay features is its Level Editor which lets you draw your |
||||
|
own custom maps to play and share with others. |
||||
|
|
||||
|
From the game's Main Menu, click on the "Create a Level" button to open the |
||||
|
level editor. To edit an existing custom level, click on the "Edit a Level" |
||||
|
button instead. |
||||
|
|
||||
|
## Level Properties |
||||
|
|
||||
|
When creating a new level, you first choose some settings for it. These are |
||||
|
described below: |
||||
|
|
||||
|
### Page Type |
||||
|
|
||||
|
This setting controls the size and boundaries of your level, and control the |
||||
|
appearance of the notebook paper background of your level. |
||||
|
|
||||
|
* **Bounded** is the default. The camera won't scroll past the top-left corner |
||||
|
of the page (0,0), and the level size is capped to 2550x3300, or the |
||||
|
approximate size of an 11:9 standard sheet of notebook paper in pixels. |
||||
|
* **No Negative Space** is like Bounded, but the width and height of the level |
||||
|
have no boundary. Levels can grow "infinitely" to the right and downward |
||||
|
but no negative coordinates past the top or left edge. |
||||
|
* **Unbounded** allows for "infinite" sized maps that give unlimited room to |
||||
|
grow your level. The wallpaper on this level type only uses the "tiling" |
||||
|
pattern, so notebook-themed levels won't show the top/left decorations. |
||||
|
|
||||
|
### Wallpaper |
||||
|
|
||||
|
The wallpaper affects the "theme" of your level. Project: Doodle is themed around |
||||
|
hand-drawn mazes on paper, so the built-in themes look like various kinds of |
||||
|
paper. |
||||
|
|
||||
|
* **Notebook** looks like standard ruled notebook paper. It's a white paper with |
||||
|
blue horizontal lines, a single red vertical line down the left, and a wide |
||||
|
margin on the top and left edges. |
||||
|
* **Legal Pad** looks like ruled yellow legal pad. It's similar to Notebook but |
||||
|
has yellow paper and a second vertical red line down the left. |
||||
|
* **Blueprint** is a dark blueprint paper background with a repeating grid pattern. |
||||
|
Notably, the default Color Palette for this theme is different than normal: |
||||
|
"solid" lines are white instead of black, to show up better against the dark |
||||
|
background. |
||||
|
|
||||
|
The decorations of the wallpaper vary based on the Page Type. For example, the |
||||
|
Notebook and Legal Pad have extra padding on the top of the page and red lines |
||||
|
going down just the left side, and the rest of the level uses the repeating blue |
||||
|
lines pattern. The page types and their effect on the wallpapers are: |
||||
|
|
||||
|
* **Bounded** and **No Negative Space** will show the decorations for the top |
||||
|
and left edges of the page, as these levels are bounded on their top/left |
||||
|
corner. |
||||
|
* **Unbounded** levels only use the repeating tiled pattern across the entire |
||||
|
level, because there is no top-left boundary to anchor those decorations to. |
||||
|
|
||||
|
## Editor Mode Interface |
||||
|
|
||||
|
![Level Editor View](../images/editor-1.png) |
||||
|
|
||||
|
Quick 5-minute tour of what you're looking at: |
||||
|
|
||||
|
* The top of the window has your **Menu Bar**: |
||||
|
* **New Level** opens the "Create a New Level" menu. |
||||
|
* **New Doodad** opens the Doodad Editor for drawing a new custom doodad. |
||||
|
You're prompted for the size of the doodad, which will be its width and |
||||
|
height boundary. For example, a size of "100" means a 100x100 pixel graphic |
||||
|
for your custom doodad. |
||||
|
* **Save** and **Save as...** let you save the current Level or Doodad you're |
||||
|
drawing to disk. "Save" will only ask for the filename once whereas "Save as" |
||||
|
asks every time. |
||||
|
* **Load** opens the "Edit a Level" menu to choose a Level or Doodad to edit. |
||||
|
* **Options** options the Level Options dialog so you can modify the page type |
||||
|
and wallpaper setting. |
||||
|
* The panel on the left side of the window is your **Tool Box**. Clicking these |
||||
|
buttons activates a specific drawing tool or mode: |
||||
|
* **Pencil Tool** lets you click, drag, and draw pixels of your selected |
||||
|
Palette color onto your level freehand. |
||||
|
* **Line Tool** lets you easily draw a straight line between two points. Click |
||||
|
in your level where you want the first point to be, and drag your mouse to |
||||
|
the second point. Release the mouse to commit the line to your drawing. |
||||
|
* **Rectangle Tool** lets you easily draw rectangles on your level. |
||||
|
* **Ellipse Tool** lets you draw circles or elliptical shapes. |
||||
|
* **Doodad Tool** lets you drag doodads such as buttons and doors onto your |
||||
|
level. See the [Doodad Tool](#doodad-tool) below. |
||||
|
* **Link Tool** lets you link doodads together so that they can interact off |
||||
|
each other. For example, a Button connected to an Electric Door will cause |
||||
|
the door to open and close when the button is pressed. See [Link Tool](#link-tool) |
||||
|
below. |
||||
|
* **Eraser Tool** cleans up your mistakes. Click and drag over pixels you've |
||||
|
drawn to delete the pixels from your level. |
||||
|
* **Brush Size:** the "Size:" label shows the brush size of your current drawing |
||||
|
tool. This translates to the line thickness, or how big your pixels are when |
||||
|
drawn into the level. Click the + and - buttons to increase or decrease the |
||||
|
brush size, and draw thicker or thinner lines. |
||||
|
* The panel on the right side of the window is your **Palette** of colors to |
||||
|
draw with. |
||||
|
|
||||
|
![Doodad Toolbar View](../images/editor-2.png) |
@ -0,0 +1,118 @@ |
|||||
|
# Doodad Tool |
||||
|
|
||||
|
The game ships with a command-line program called `doodad` which assists in |
||||
|
creating and managing custom doodads and levels. |
||||
|
|
||||
|
The `doodad` tool can show and set details on .doodad and .level files used by |
||||
|
the game, create new doodads from PNG images and attach custom JavaScript source |
||||
|
to program behavior of doodads. |
||||
|
|
||||
|
## Where to Find It |
||||
|
|
||||
|
The `doodad` tool should be in the same place as the game executable. |
||||
|
|
||||
|
On Windows, the program is called `doodad.exe` and comes in the zip file next |
||||
|
to the game executable, `doodle.exe`. |
||||
|
|
||||
|
On Linux, it will typically be at `/opt/project-doodle/doodad`. |
||||
|
|
||||
|
On Mac OS, it is found inside the .app bundle. |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
Run `doodad --help` to get usage information. |
||||
|
|
||||
|
The program includes several sub-commands, such as `doodad convert`. Type a |
||||
|
subcommand and `--help` to get help on that command, for example: |
||||
|
|
||||
|
```bash |
||||
|
doodad convert --help |
||||
|
``` |
||||
|
|
||||
|
# Examples |
||||
|
|
||||
|
Here are some common scenarios and use cases for the doodad tool. |
||||
|
|
||||
|
## Show |
||||
|
|
||||
|
```bash |
||||
|
# Usage: |
||||
|
doodad show [doodad or level filename] |
||||
|
``` |
||||
|
|
||||
|
Shows metadata and details about a level or doodad file. |
||||
|
|
||||
|
Examples: |
||||
|
|
||||
|
```bash |
||||
|
$ doodad show button.doodad |
||||
|
===== Doodad: button.doodad ===== |
||||
|
Headers: |
||||
|
File version: 1 |
||||
|
Game version: 0.0.10-alpha |
||||
|
Doodad title: Button |
||||
|
Author: Noah |
||||
|
Locked: true |
||||
|
Hidden: false |
||||
|
Script size: 473 bytes |
||||
|
|
||||
|
Palette: |
||||
|
- Swatch name: Color<#000000+ff> |
||||
|
Attributes: solid |
||||
|
Color: #000000 |
||||
|
- Swatch name: Color<#666666+ff> |
||||
|
Attributes: none |
||||
|
Color: #666666 |
||||
|
- Swatch name: Color<#999999+ff> |
||||
|
Attributes: fire |
||||
|
Color: #999999 |
||||
|
|
||||
|
Layer 0: button1 |
||||
|
Chunks: |
||||
|
Pixels Per Chunk: 37^2 |
||||
|
Number Generated: 1 |
||||
|
Coordinate Range: (0,0) ... (36,36) |
||||
|
World Dimensions: 36x36 |
||||
|
Use -chunks or -verbose to serialize Chunks |
||||
|
|
||||
|
Layer 1: button2 |
||||
|
Chunks: |
||||
|
Pixels Per Chunk: 37^2 |
||||
|
Number Generated: 1 |
||||
|
Coordinate Range: (0,0) ... (36,36) |
||||
|
World Dimensions: 36x36 |
||||
|
Use -chunks or -verbose to serialize Chunks |
||||
|
``` |
||||
|
|
||||
|
## Convert |
||||
|
|
||||
|
```bash |
||||
|
# Usage: |
||||
|
doodad convert [options] <input files.png> <output file.doodad> |
||||
|
``` |
||||
|
|
||||
|
### Creating a Doodad from PNG images |
||||
|
|
||||
|
Suppose you have PNG images named "frame0.png" through "frame3.png" and want |
||||
|
to create a doodad from those images. This will convert them to the doodad |
||||
|
file "custom.doodad": |
||||
|
|
||||
|
```bash |
||||
|
# Convert PNG images into a doodad. |
||||
|
doodad convert frame0.png frame1.png frame2.png frame3.png custom.doodad |
||||
|
|
||||
|
# The same, but also attach custom tags with the doodad. |
||||
|
doodad convert --tag color=blue frame{0,1,2,3}.png custom.doodad |
||||
|
``` |
||||
|
|
||||
|
### Convert a level to a PNG image |
||||
|
|
||||
|
```bash |
||||
|
doodad convert my.level output.png |
||||
|
``` |
||||
|
|
||||
|
### Create a level from a PNG image |
||||
|
|
||||
|
```bash |
||||
|
doodad convert level.png output.level |
||||
|
``` |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 106 KiB |
@ -0,0 +1,15 @@ |
|||||
|
# Welcome to Project: Doodle |
||||
|
|
||||
|
![](images/main-menu.png) |
||||
|
|
||||
|
[Project: Doodle](about.md) is a drawing-based maze game themed around hand-drawn |
||||
|
maps on paper. You can draw a level for a 2D platformer game, drag-and-drop |
||||
|
"doodads" such as buttons and doors into your level, and play it. |
||||
|
|
||||
|
## Table of Contents |
||||
|
|
||||
|
* [Creating Custom Levels](custom-levels/) |
||||
|
* [Creating Custom Doodads](custom-doodads/) |
||||
|
* [Draw Sprites In-Game](custom-doodads/edit-in-game.md) |
||||
|
* [Draw Sprites with an External Program](custom-doodads/edit-external.md) |
||||
|
* [Program Them with JavaScript](custom-doodads/scripts.md) |
@ -0,0 +1,9 @@ |
|||||
|
site_name: "Project: Doodle Guidebook" |
||||
|
use_directory_urls: false |
||||
|
nav: |
||||
|
- Home: index.md |
||||
|
- About: about.md |
||||
|
- "Custom Doodads": custom-doodads/index.md |
||||
|
markdown_extensions: |
||||
|
- toc: |
||||
|
permalink: "#" |
Loading…
Reference in new issue