Update Guidebook for v0.13.1

master
Noah 2022-10-10 12:54:19 -07:00
parent 63a90db941
commit 5897d0dd59
56 changed files with 2133 additions and 747 deletions

View File

@ -1,50 +1,23 @@
# About _Sketchy Maze_
**Sketchy Maze** is a "drawing-based maze game" themed around hand-drawn maps
on paper.
**Sketchy Maze** is a "drawing-based maze game" themed around hand-drawn maps on paper.
![Screenshot of the level editor](images/about.png)
You can draw your own custom maps, freehand or with some basic drawing tools,
and then drag and drop pre-made "[doodads](doodads.md)" into your level for
interactive things like keys, doors and buttons.
This is an **early alpha build** of the game. Longer term it will include a couple
of built-in "single player campaigns" of multiple themed levels, but for now it
just includes a few example levels and the editor itself.
You can draw your own custom maps, freehand or with some basic drawing tools, and then drag and drop pre-made "[doodads](doodads.md)" into your level for interactive things like keys, doors and buttons.
## Inspiration
When I was a kid in the era of Sega Genesys and Super Nintendo, I liked to
draw my own levels with pen and paper and "play" them with my imagination. My
"mouse mazes" had all sorts of features borrowed from videogames I liked.
There were buttons that the player had to push that would open a door far
across the level (and I'd draw a little dotted line showing the connection
between the button and the door). Trapdoors would have the player fall through
and then close behind them so they can't go back. Keys in different shapes
could unlock doors in similar shapes (you'd just have to remember which keys
you've got!)
Growing up in the era of 2D platformer games on the likes of the Sega Genesys and Super Nintendo, as a kid I would often draw my own levels with pen and paper and "play" them with my imagination. My mazes had all sorts of features borrowed from videogames I liked. There would be buttons that the player had to push that would open a door far across the level (and I'd draw a little dotted line showing the connection between the button and the door). Trapdoors would have the player fall through and then close behind them so they can't go back. Keys in different shapes could unlock doors in similar shapes (you'd just have to remember which keys you've got!)
When you're drawing with a pen and paper, the level can be anything you want.
You can draw a castle or a cave, place little gizmos and traps throughout the
level (doors, keys, buttons and things) and create puzzles. Do you want the red
pixels to mean "fire" and burn the player? Or do you want to decorate the tops
of "bloody" spikes with red? Either way, the 'fire' attribute on that color will
do the job and you can draw whatever shape you want for your level hazards.
When you're just drawing with a pen and paper, the level can be anything you want. You can draw a castle or a cave, place little gizmos and traps throughout the level (doors, keys, buttons and things) and create puzzles. Do you want the red pixels to mean "fire" and burn the player? Or do you want to decorate the tops of "bloody" spikes with red? Either way, the 'fire' attribute on that color will do the job and you can draw whatever shape you want for your level hazards.
![Palette editor](images/palette.png)
## Mod Friendly
While the game ships with a [handful of built-in doodads](doodads.md) to
spice up your level, you can also create your own and program them to do
whatever you want, [with JavaScript](custom-doodads/scripts.md)!
While the game ships with a [handful of built-in doodads](doodads.md) to spice up your level, you can also create your own and program them to do whatever you want, [with JavaScript](custom-doodads/scripts.md)!
The game also has a few "generic" scripts built in that you can choose from,
to create your own simple doodads with no programming required! In the future,
template doodads can be created to be able to draw custom player characters,
Warp Doors and other complex doodads with a script already attached.
The game also has a few "generic" scripts built in that you can choose from, to create your own simple doodads with no programming required! In the future, template doodads can be created to be able to draw custom player characters, Warp Doors and other complex doodads with a script already attached.
Full versions of the game let you bundle custom doodads _inside_ of your
custom levels for easy sharing to other players. Custom wallpaper images embed
in a level file for all versions of the game.
Full versions of the game let you bundle custom doodads _inside_ of your custom levels for easy sharing to other players. Custom wallpaper images embed in a level file for all versions of the game.

View File

@ -1,5 +1,569 @@
# Changes
## v0.13.1 (Oct 10 2022)
This release brings a handful of minor new features to the game.
First, there are a couple of new Pixel Attributes available in the level editor:
* Semi-Solid: pixels with this attribute only behave as "solid" when walked on
from above. The player can jump through the bottom of a Semi-Solid and land
on top, and gradual slopes can be walked up and down as well, but a steep
slope or a wall can be simply passed through as though it were just decoration.
* Slippery: the player's acceleration and friction are reduced when walking on
a slippery floor. In the future, players and other mobile doodads may slide
down slippery slopes automatically as well (not yet implemented).
* These attributes are available in the Level Editor by clicking the "Edit"
button on your Palette (or the "Tools -> Edit Palette" menu). The Palette
Editor now has small icon images for the various attributes to make room for
the expanded arsenal of options.
Doodad/Actor Runtime Options have been added:
* In the Doodad Editor's "Doodad Properties" window, see the new "Options" tab.
* Doodad Options allow a map creator to customize certain properties about your
doodad, on a per-instance basis (instances of doodads are called "actors" when
placed in your level).
* In the Level Editor when the Actor Tool is selected, mousing over a doodad on
your level will show a new gear icon in the corner. Clicking the icon will open
the Actor Properties window, where you may toggle some of the doodad options
(if a doodad has any options available).
* Options can be of type boolean, string, or integer and have a custom name and a
default value at the doodad level. In the Level Editor, the map creator can
set values for the available options which the doodad script can read using the
`Self.GetOption()` method.
* Several of the game's built-in doodads have options you can play with, which are
documented below.
New and updated doodads:
* "Look At Me" is a new Technical doodad that will draw the camera's attention
to it when it receives a power signal from a linked button. For example, if
a button would open an Electric Door far across the level, you can also place
a "Look At Me" near the door and link the button to both doodads. When the
button is pressed, the camera will scroll to the "Look At Me" and the player
can see that the door has opened.
* Anvils will now attract the camera's attention while they are falling.
Several of the game's built-in doodads have new Actor Runtime Options you can
configure in your custom levels:
* Warp Doors: "locked (exit only)" will make it so the player can not enter the
warp door - they will get a message on-screen that it is locked, similar to
how warp doors behave when they aren't linked to another door. If it is linked
to another door, the player may still exit from the 'locked' door -
essentially creating a one-way warp, without needing to rely on the
orange/blue state doors. The "Invisible Warp Door" technical doodad also
supports this option.
* Electric Door & Electric Trapdoor: check the "opened" option and these doors
will be opened by default when the level gameplay begins. A switch may still
toggle the doors closed, or if the doors receive and then lose a power signal
they will close as normal.
* Colored Doors & Small Key Door: you may mark the doors as "unlocked" at the
start of your level, and they won't require a key to open.
* Colored Keys & Small Key: you may mark the keys as "has gravity" and they
will be subject to the force of gravity and be considered a "mobile" doodad
that may activate buttons or trapdoors that they fall onto.
* Gemstones: these items already had gravity by default, and now they have a
"has gravity" option you may disable if you'd prefer gemstones not to be
subject to gravity (and make them behave the way keys used to).
* Gemstome Totems: for cosmetic purposes you may toggle the "has gemstone"
option and the totem will already have its stone inserted at level start.
These gemstones will NOT emit a power signal or interact normally with
linked totems - they should be configured this way only for the cosmetic
appearance, e.g., to have one totem filled and some others empty; only the
empty totems should be linked together and to a door that would open when
they are all filled.
* Fire Region: you may pick a custom "name" for this doodad (default is "fire")
to make it better behave as normal fire pixels do: "Watch out for (name)!"
Improvements in support of custom content:
* Add a JavaScript "Exception Catcher" window in-game. If your doodad scripts
encounter a scripting error, a red window will pop up showing the text of
the exception with buttons to copy the full text to your clipboard (in case
it doesn't all fit on-screen) and to suppress any further exceptions for
the rest of your game session (in case a broken doodad is spamming you with
error messages). Cheat codes can invoke the Exception Catcher for testing:
`throw <message>` to show custom text, `throw2` to test a "long" message
and `throw3` to throw a realistic message.
* Calling `console.log()` and similar from doodad scripts will now prefix the
log message with the doodad's filename and level ID.
There are new JavaScript API methods available to doodad scripts:
* `Self.CameraFollowMe()` will attract the game's camera viewport to center
on your doodad, taking the camera's focus away from the player character.
The camera will return to the player if they enter a directional input.
* `Self.Options()` returns a string array of all of the options available on
the current doodad.
* `Self.GetOption(name)` returns the configured value for a given option.
Some improvements to the `doodad` command-line tool:
* `doodad show` will print the Options on a .doodad file and, when showing
a .level file with the `--actors` option, will list any Options configured
on a level's actors where they differ from the doodad's defaults.
* `doodad edit-doodad` adds a `--option` parameter to define an option on a
doodad programmatically. The syntax is like `--option name=type=default`
for example `--option unlocked=bool=true` or `--option unlocked=bool`; the
default value is optional if you want it to be the "zero value" (false,
zero, or empty string).
Minor fixes and improvements:
* Add a "Wait" modal with a progress bar. Not used yet but may be useful
for long operations like Giant Screenshot or level saving to block input
to the game while it's busy doing something. Can be tested using the
cheat code "test wait screen"
* Detect the presence of a touchscreen device and automatically disable
on-screen touch hints during gameplay if not on a touch screen.
* Mobile Linux: mark the Sketchy Maze launcher as supporting the mobile
form-factor for the Phosh desktop shell especially.
* Fix the Crusher doodad sometimes not falling until it hits the ground
and stopping early on slower computers.
* Small tweaks to player physics - acceleration increased from 0.025 to
0.04 pixels per tick.
## v0.13.0 (May 7 2022)
This is a major update that brings deep architectural changes and a lot
of new content to the game.
Swimming physics have been added:
* The **water** pixels finally do something besides turn the character
blue: swimming physics have finally been hooked up!
* While the player is touching water pixels (and is colored blue),
your gravity and jump speed are reduced, but you can "jump"
infinite times in order to swim higher in the water. Hold the jump
button to climb slowly, spam it to climb quickly.
* The **Azulians** understand how to swim, too, though left to their
own devices they will sink to the bottom of a body of water. They'll
swim (jump) up if you're detected and above them. The Blue Azulian
has the shortest vertical aggro radius, so it's not a very good swimmer
but the White Azulian can traverse water with ease and track you
from a greater distance.
New levels:
* **The Jungle** (First Quest) - a direct sequel to the Boat level, it's
a jungle and Mayan themed platformer featuring many of the new doodads
such as snakes, gemstones, and crushers.
* **Gems & Totems** (Tutorial, Lesson 4) - a tutorial level about the
new Gem and Totem doodads.
* **Swimming** (Tutorial, Lesson 5) - a tutorial level to learn how
"water pixels" work with some moderately safe platforming puzzles
included.
* **Night Sky** (Azulian Tag) - a moderately difficult Azulian Tag level
with relatively few enemies but plenty of tricky platforming.
* Some of the existing levels have had minor updates to take advantage
of newer game features, such as the water being re-done for the Castle
level.
New doodads:
* **Blue Bird:** a blue version of the Bird which flies in a sine wave
pattern about its original altitude, and has a larger search range to
dive at the player character.
* **Snake:** a green snake that sits coiled up and always faces the
player. If you get nearby and try and jump over, the Snake will jump
up and hope to catch you.
* **Crusher:** a block-headed enemy with an iron helmet which tries to
drop on you from above. Its helmet makes a safe platform to ride
back up like an elevator.
* **Gems and Totems:** four collectible gems (in different colors and
shapes) that slot into Totems of a matching shape. Totems can link
together to require multiple gemstones before they'll emit a power
signal to other linked doodads.
New **File Formats**:
* Levels and Doodads have a new file format based on ZIP files,
like levelpacks.
* It massively improves loading screen times and helps the
game keep a substantially lighter memory footprint (up to 85%
less memory used, like 1.5 GB -> 200 MB on _Azulian Tag - Forest_).
* **Your old levels and doodads still work**! The next time you save
them, they will be converted to the new file format automatically.
* The `doodad` tool can also upgrade your levels by running:
`doodad edit-level --touch <filename>.level`
Other new content to use in your levels:
* New wallpapers: Dotted paper (dark), Parchment paper (red, blue,
green, and yellow).
* New palette: Neon Bright, all bright colors to pair with a dark level
wallpaper.
* New brush pattern: Bubbles. The default "water" color of all the game's
palettes will now use the Bubbles pattern by default.
New cheat codes:
* `super azulian`: play as the Red Azulian.
* `hyper azulian`: play as the White Azulian.
* `bluebird`: play as the Blue Bird.
* `warp whistle`: automatically win the current level, with a snarky
cheater message in the victory dialog. It will mark the level as
Completed but not reward a high score.
* `$ d.SetPlayerCharacter("anything.doodad")` - set your character to any
doodad you want, besides the ones that have dedicated cheat codes. The
".doodad" suffix is optional. Some to try out are "key-blue", "anvil",
or "box". If you are playing as a key, a mob might be able to collect
you! This will softlock the level, but another call to
`d.SetPlayerCharacter()` will fix it! Use the `pinocchio` cheat or
restart the game to return to the default character (boy.doodad)
Updates to the JavaScript API for doodads:
* `Self.IsWet() bool` can test if your actor is currently in water.
Other changes this release:
* Editor: fancy **mouse cursors** gives some visual feedback about what
tool is active in the editor, with a Pencil and a Flood Fill
cursor when those tools are selected.
* Editor: your **Palette** buttons will now show their pattern with their
color as the button face, rather than just the color.
* Editor: Auto-save is run on a background thread so that, for large
levels, it doesn't momentarily freeze the editor on save when it runs.
* Editor: Fix the Link Tool forgetting connections when you pick up and
drop one of the linked doodads.
* Link Tool: if you click a doodad and don't want to link it to another,
click the first doodad again to de-select it (or change tools).
* Editor: your last scroll position on a level is saved with it, so the
editor will be where you left it when you reopen your drawing.
* Doodad tool: `doodad edit-level --resize <int>` can re-encode a level
file using a different chunk size (the default has been 128).
Experimental! Very large or small chunk sizes can lead to different
performance characteristics in game!
* Fixed the bug where characters' white eyes were showing as transparent.
## v0.12.1 (April 16 2022)
This update focuses on memory and performance improvements for the game.
Some larger levels such as "Azulian Tag - Forest" could run out of
memory on 32-bit systems. To improve on memory usage, the game more
aggressively frees SDL2 textures when no longer needed and it doesn't
try to keep the _whole_ level's chunks ready in memory (as rendered
images) -- only the chunks near the window viewport are loaded, and
chunks that leave the viewport are freed to reclaim memory. Improvements
are still a work in progress.
New fields are added to the F3 debug overlay to peek at its performance:
* "Textures:" shows the count of SDL2 textures currently loaded
in memory. Some textures, such as toolbar buttons and the
loadscreen wallpaper, lazy load and persist in memory. Most level
and doodad textures should free when the level exits.
* "Sys/Heap:" shows current memory utilization in terms of MiB taken
from the OS and MiB of active heap memory, respectively.
* "Threads:" counts the number of goroutines currently active. In
gameplay, each actor monitors its PubSub queue on a goroutine and
these should clean up when the level exits.
* "Chunks:" shows the count of level chunks inside and outside the
loading viewport.
Some other changes and bug fixes in this release include:
* Fixed the bug where the player was able to "climb" vertical walls to
their right.
* When entering a cheat code that changes the default player character
during gameplay, you immediately become that character.
## v0.12.0 (March 27 2022)
This update adds several new features to gameplay and the Level Editor.
A **Game Rules** feature has been added to the Level Editor which allows
customizing certain gameplay features while that level is being played.
These settings are available in the Level Properties window of the editor:
* The **Difficulty** rule can modify the behavior of enemy doodads
when the level is played. Choose between Peaceful, Normal, or Hard.
* On Peaceful, Azulians and Birds don't attack the player, acting
like pre-0.11.0 versions that ignored the player character.
* On Hard difficulty, Azulians have an infinite aggro radius
(they'll immediately hunt the player from any distance on
level start) and they are hostile to _all_ player creatures.
* **Survival Mode** changes the definition of "high score" for levels
where the player is very likely to die at least once.
* The silver high score (respawned at checkpoint) will be for the
_longest_ time survived on the level rather than the fastest time
to complete it.
* The gold high score (got to an Exit Flag without dying once) is
still rewarded to the fastest time completing the level.
An update to the Level Editor's toolbar:
* New **Text Tool** to easily paste messages onto your level, selecting
from the game's built-in fonts.
* New **Pan Tool** to be able to scroll the level safely by dragging with
your mouse or finger.
* New **Flood Tool** (or paint bucket tool) can be used to replace
contiguous areas of your level from one color to another.
* The toolbar buttons are smaller and rearranged. On medium-size screens
or larger, the toolbar buttons are drawn side-by-side in two columns.
On narrower screens with less real estate, it will use a single column
when it fits better.
New levels:
* An **Azulian Tag** levelpack has been added featuring two levels so
far of the Azulian Tag game mode.
New doodads:
* A technical doodad for **Reset Level Timer** resets the timer to zero,
one time, when touched by the player. If the doodad receives a power
signal from a linked doodad, it can reset the level timer again if the
player touches it once more.
Updates to the JavaScript API for custom doodads:
* New global integer `Level.Difficulty` is available to doodad scripts to
query the difficulty setting of the current level:
* Peaceful (-1): `Level.Difficulty < 0`
* Normal (0): `Level.Difficulty == 0`
* Hard (1): `Level.Difficulty > 1`
* New function `Level.ResetTimer()` resets the in-game timer to zero.
New cheat codes:
* `test load screen` tests the loading screen UI for a few seconds.
* `master key` allows playing locked Story Mode levels without unlocking
them first by completing the earlier levels.
Other changes:
* Several screens were re-worked to be responsive to mobile screen sizes,
including the Settings window, loading screen, and the level editor.
* Fixed a bug where making the app window bigger during a loading screen
caused the Editor to not adapt to the larger window.
* Don't show the _autosave.doodad in the Doodad Dropper.
* The Azulians have had their jump heights buffed slightly.
* Birds no longer register as solid when colliding with other birds (or
more generally, characters unaffected by gravity).
Bugs fixed:
* When modifying your Palette to rename a color or add an additional
color, it wasn't possible to draw with that new color without fully
exiting and reloading the editor - this is now resolved.
* The palette editor will try and prevent the user from giving the same
name to different colors.
## v0.11.0 (Feb 21 2022)
New features:
* **Game Controller** support has been added! The game can now be played
with an Xbox style controller, including Nintendo Pro Controllers. The
game supports an "X Style" and "N Style" button layout, the latter of
which swaps the A/B and the X/Y buttons so gameplay controls match the
button labels in your controller.
* The **JavaScript Engine** for doodad scripts has been switched from
github.com/robertkrimen/otto to github.com/dop251/goja which helps
"modernize" the experience of writing doodads. Goja supports many
common ES6 features already, such as:
* Arrow functions
* `let` and `const` keywords
* Promises
* for-of loops
* The **JavaScript API** has been expanded with new functions and
many of the built-in Creatures have gotten an A.I. update.
* For full versions of the game, the **Publish Level** function is now
more streamlined to just a checkbox for automatically bundling your
doodads next time you save the level.
New levels:
* **The Zoo:** part of the Tutorial levelpack, it shows off all of the
game's built-in doodads and features a character selection room to
play as different creatures.
* **Shapeshifter:** a new level on the First Quest where you switch
controls between the Boy, Azulian and the Bird in order to clear the
level.
Some of the built-in doodads have updates to their A.I. and creatures
are becoming more dangerous:
* The **Bird** now searches for the player diagonally in front of
it for about 240px or so. If spotted it will dive toward you and
it is dangerous when diving! When _playing_ as the bird, the dive sprite
is used when flying diagonally downwards. The player-controlled bird
can kill mobile doodads by diving into them.
* The **Azulians** will start to follow the player when you get
close and they are dangerous when they touch you -- but not if
you're the **Thief.** The red Azulian has a wider search radius,
higher jump and faster speed than the blue Azulian.
* A new **White Azulian** has been added to the game. It is even faster
than the Red Azulian! And it can jump higher, too!
* The **Checkpoint Flag** can now re-assign the player character when
activated! Just link a doodad to the Checkpoint Flag like you do the
Start Flag. When the player reaches the checkpoint, their character
sprite is replaced with the linked doodad!
* The **Anvil** is invulnerable -- if the player character is the Anvil
it can not die by fire or hostile enemies, and Anvils can not destroy
other Anvils.
* The **Box** is also made invulnerable so it can't be destroyed by a
player-controlled Anvil or Bird.
New functions are available on the JavaScript API for doodads:
* `Actors.At(Point) []*Actor`: returns actors intersecting a point
* `Actors.FindPlayer() *Actor`: returns the nearest player character
* `Actors.New(filename string)`: create a new actor (NOT TESTED YET!)
* `Self.Grounded() bool`: query the grounded status of current actor
* `Actors.SetPlayerCharacter(filename string)`: replace the nearest
player character with the named doodad, e.g. "boy.doodad"
* `Self.Invulnerable() bool` and `Self.SetInvulnerable(bool)`: set a
doodad is invulnerable, especially for the player character, e.g.
if playing as the Anvil you can't be defeated by mobs or fire.
New cheat codes:
* `god mode`: toggle invincibility. When on, fire pixels and hostile
mobs can't make you fail the level.
* `megaton weight`: play as the Anvil by default on levels that don't
specify a player character otherwise.
Other changes:
* When respawning from a checkpoint, the player is granted 3 seconds of
invulnerability; so if hostile mobs are spawn camping the player, you
don't get soft-locked!
* The draw order of actors on a level is now deterministic: the most
recently added actor will always draw on top when overlapping another,
and the player actor is always on top.
* JavaScript exceptions raised in doodad scripts will be logged to the
console instead of crashing the game. In the future these will be
caught and presented nicely in an in-game popup window.
* When playing as the Bird, the flying animation now loops while the
player is staying still rather than pausing.
* The "Level" menu in Play Mode has options to restart the level or
retry from last checkpoint, in case a player got softlocked.
* When the game checks if there's an update available via
<https://download.sketchymaze.com/version.json> it will send a user
agent header like: "Sketchy Maze v0.10.2 on linux/amd64" sending only
static data about the version and OS.
## v0.10.1 (Jan 9 2022)
New features:
* **High scores and level progression:** when playing levels out of
a Level Pack, the game will save your progress and high scores on
each level you play. See details on how scoring works so far, below.
* **Auto-save** for the Editor. Automatically saves your drawing every
5 minutes. Look for e.g. the _autosave.level to recover your drawing
if the game crashed or exited wrongly!
* **Color picker UI:** when asked to choose a color (e.g. for your level
palette) a UI window makes picking a color easy! You can still manually
enter a hexadecimal color value but this is no longer required!
Scoring system:
* The high score on a level is based on how quickly you complete it.
A timer is shown in-game and there are two possible high scores
for each level in a pack:
* Perfect Time (gold): if you complete the level without dying and
restarting at a checkpoint.
* Best Time (silver): if you had used a checkpoint.
* The gold/silver icon is displayed next to the timer in-game; it
starts gold and drops to silver if you die and restart from checkpoint.
It gives you a preview of which high score you'll be competing with.
* If cheat codes are used, the user is not eligible for a high score
but may still mark the level "completed."
* Level Packs may have some of their later levels locked by default,
with only one or a few available immediately. Completing a level will
unlock the next level until they have all been unlocked.
New and changed doodads:
* **Invisible Warp Door:** a technical doodad to create an invisible
Warp Door (press Space/'Use' key to activate).
* All **Warp Doors** now require the player to be grounded before they
can be opened, or else under the effects of antigravity. You shouldn't
be able to open Warp Doors while falling or jumping off the ground
at them.
Revised levels:
* Desert-2of2.level uses a new work-around for the unfortunate glitch
of sometimes getting stuck on two boxes, instead of a cheat code
being necessary to resolve.
* Revised difficulty on Tutorial 2 and Tutorial 3.
Miscellaneous changes:
* Title Screen: picks a random level from a few options, in the future
it will pick random user levels too.
* Play Mode gets a menu bar like the Editor for easier navigation to
other game features.
* New dev shell command: `titlescreen <level name>` to load the Title
Screen with the named level as its background, can be used to load
user levels _now_.
* For the doodads JavaScript API: `time.Since()` is now available (from
the Go standard library)
* Fix a cosmetic bug where doodads scrolling off the top or left edges
of the level were being drawn incorrectly.
* Fix the Editor's status bar where it shows your cursor position
relative to the level and absolute to the app window to show the
correct values of each (they were reversed before).
* The developer shell now has a chatbot in it powered by RiveScript.
## v0.10.0 (Dec 30 2021)
New features and changes:
* **Level Packs:** you can group a set of levels into a sequential
adventure. The game's built-in levels have been migrated into Level
Packs and users can create their own, too!
* **Crosshair Option:** in the level editor you can have a crosshair
drawn at your cursor position, which may help align things while
making a level. Find the option in the game's Settings window.
* **Smaller Palette Colors:** the color buttons in the Level Editor are
smaller and fit two to a row. This allows for more colors but may be
difficult for touch controls.
* Doodad AI updates: the **Bird** records its original altitude and will
attempt to fly back there when it can, so in case it slid up or down a
ramp it will correct its height when it comes back the other way.
* The "New Level" and "New Doodad" functions on the main menu are
consolidated into a window together that can create either, bringing
a proper UI to creating a doodad.
* Added a setting to **hide touch control hints** from Play Mode.
* The title screen is more adaptive to mobile. If the window height isn't
tall enough to show the menu, it switches to a 'landscape mode' layout.
* Adds a custom icon to the application window.
A few notes about level packs:
* A levelpack is basically a zip file containing levels and custom
doodads. **Note:** the game does not yet handle the doodads folder
of a levelpack at all.
* The `doodad` command-line tool can create .levelpack files. See
`doodad levelpack create --help`. This is the easiest way to
generate the `index.json` file.
* In the future, a .levelpack will be able to hold custom doodads on
behalf of the levels it contains, de-duplicating files and saving
on space. Currently, the levels inside your levelpack should embed
their own custom doodads each.
* Free (shareware) editions of the game can create and play custom
level packs that use only the game's built-in doodads. Free versions
of the game can't play levels with embedded custom doodads. You can
always copy custom .doodad files into your profile directory though!
Bugs fixed:
* Undo/Redo now works again for the Doodad Editor.
* Fix crash when opening the Doodad Editor (v0.9.0 regression).
* The Play Level/Edit Drawing window is more responsive to small screens
and will draw fewer columns of filenames.
* Alert and Confirm popup modals always re-center themselves, especially
to adapt to the user switching from Portrait to Landscape orientation
on mobile.
## v0.9.0 (Oct 9, 2021)
New features:
@ -337,11 +901,11 @@ The new features:
* **Choice of Default Palette for New Levels:** when creating a new level, a
"Palette:" option appears which allows you to set the default colors to start
your level with. The options include:
* Default: the classic default 4 colors (black, grey, red, blue).
* Colored Pencil: a set with more earthy tones for outdoorsy levels
(grass, dirt, stone, fire, water)
* Blueprint: the classic Blueprint wallpaper theme, a bright version of Default
for dark level backgrounds.
* Default: the classic default 4 colors (black, grey, red, blue).
* Colored Pencil: a set with more earthy tones for outdoorsy levels
(grass, dirt, stone, fire, water)
* Blueprint: the classic Blueprint wallpaper theme, a bright version of Default
for dark level backgrounds.
* **Custom Wallpapers:** unhappy with the default, paper-themed level background
images? You can now use your own! They attach to your level data for easy
transport when sharing your level with others.

View File

@ -1,27 +1,66 @@
# Controls
**Sketchy Maze** currently uses a mouse and keyboard for inputs, but eventually
will be usable from gamepad controllers, especially if it branches out and
targets platforms other than desktop computers in the future.
**Sketchy Maze** can be played with a variety of input devices including keyboard & mouse, touch screen, or Xbox-style game controllers.
The list of controls is also viewable in-game in the Settings Window, accessible
from the title screen or the Edit->Settings menu of the editor. The controls can
not be customized at this time.
The list of controls is also viewable in-game in the Settings Window, accessible from the title screen or the Edit->Settings menu of the editor. The controls can not be customized at this time.
![Controls UI](images/controls.png)
## During Gameplay
## Gamepad Controls
An Xbox-style game controller can be used with Sketchy Maze. Nintendo-style controllers also work, such as a Switch Pro Controller. The game has a built-in "X Style" or "N Style" setting to make sure the A/B and X/Y buttons map correctly based on your controller. You can choose which button style you prefer in the game's Settings window.
The gamepad controls currently operate under two different "modes" which are described below.
### Mouse Mode
The default gamepad controls will emulate a mouse cursor, allowing you to navigate the game's screens and work with the Level Editor. Under Mouse Mode, the gamepad controls are as follows:
* The **Left analog stick** moves a mouse cursor around the screen.
* The **Right analog stick** can scroll the level on the title screen or editor.
* The **A** and **X** buttons act as a "left click" to activate buttons and draw pixels in the level editor.
* The **B** and **Y** buttons act as a "right click," e.g. to remove doodads from your level while the Doodad Tool is selected.
* The **Left shoulder** button (L1) acts as a "middle click", e.g. to pan the level around on the title screen or editor.
* The **Left trigger** button (L2) will close the top-most popup window in the level editor (similar to the Backspace key on keyboard controls).
### Gameplay Mode
When level gameplay begins, the game controller will move the player character around using familiar controls.
* The **Left analog stick** and the **D-Pad** will move the player character left or right. If playing as the Bird or under the effects of antigravity, the control stick can also move the player up and down.
* The **A** and **X** buttons are to "Use" an object, such as to open a Warp Door.
* The **B** and **Y** buttons are to "Jump"
* The **Right shoulder** button (R1) toggles the gamepad controls between Gameplay Mode and Mouse Mode. So, during gameplay you may access the menu bar or on-screen buttons by toggling into Mouse Mode.
## Touch Controls
Buttons and UI windows can be touched and dragged around as though your finger is a mouse cursor. There are also some gestures supported:
* Drag two fingers across the screen to **pan a scrollable drawing** such as in the Level Editor or the Title Screen.
* Tip: for the Level Editor select the Actor Tool or Link Tool before panning the level, otherwise you might plot some pixels on your map before the game realizes you were just wanting to scroll!
During gameplay, touch any region of the screen surrounding the center to move the player character in the relative direction you touched. By default, if you are idle in a level for a few seconds some on-screen hints will appear about the touch controls:
![Touch Controls Hint](images/touch-controls.png)
The square in the middle that says "Touch here to 'use' objects" is the center of your screen and all the other touch regions are directly touching this square.
* Touch anywhere above center to **Jump.**
* Touch anywhere left of center to **Move left.**
* Touch anywhere right of center to **Move right.**
* Touch anywhere below center to **Move downwards.** (note: for flying characters like the Bird or during times of antigravity)
* Touch the center to **Use** objects (such as to open a Warp Door).
Generally the center is aligned on the player character unless you're close to level boundaries.
## Keyboard Controls (Gameplay)
While playing a level, the following keys are used to control the player character:
* **Left** and **Right** arrow keys move the player left or right.
* **Up** arrow to make the player jump.
* **Space Bar** is used to "activate" certain doodads. Currently, only the
[Warp Doors](doodads.md#warp-doors) require deliberate activation; Buttons and
Switches activate _automatically_ when the player character (or other mobile
doodad) touches them.
* **Left** and **Right** arrow keys move the player left or right.
* **Up** arrow to make the player jump.
* **Space Bar** is used to "activate" certain doodads. Currently, only the [Warp Doors](doodads.md#warp-doors) require deliberate activation; Buttons and Switches activate _automatically_ when the player character (or other mobile doodad) touches them.
## Hotkeys
See the [Hotkeys](hotkeys.md) page for shortcut keys, especially around the
Level Editor feature.
See the [Hotkeys](hotkeys.md) page for shortcut keys, especially around the Level Editor feature.

View File

@ -0,0 +1,745 @@
# JavaScript API
This page describes the JavaScript API available to Doodad scripts. These take the form of globally available functions that provide hooks into the game's logic and empower doodads to react to their surroundings and communicate with other doodads on the level.
During gameplay, the doodads are called "actors" which are the _instantiated_ form of the doodad; meaning, they are running their scripts and have come "alive" during gameplay. The surface area of functions available on the [Self](#self) object (which points to the current actor) is also available on other actors, such as the `e.Actor` property of [OnCollide](#eventsoncollide-funcevent) events or the actor returned by the [Actors.FindPlayer()](#actorsfindplayer-actor) function.
Table of Contents:
* [Global Functions](#global-functions)
* [EndLevel()](#endlevel)
* [FailLevel()](#faillevelmessage-string)
* [SetCheckpoint()](#setcheckpointpoint)
* [Flash()](#flashmessage-string-args)
* [GetTick()](#gettick-uint64)
* [time.Now()](#timenow-timetime)
* [time.Add()](#timeaddt-timetime-milliseconds-int64-timetime)
* [time.Since()](#timesincetimetime-timeduration) `v0.10.1`
* [Console Logging](#console-logging)
* [Self](#self) - functions for the current (and other) actors
* [Self.ID()](#selfid-string)
* [Self.IsPlayer()](#selfisplayer-bool) `v0.8.0`
* [Self.GetTag()](#selfgettagstring-name-string)
* [Self.Options()](#selfoptions-string) `v0.13.1`
* [Self.GetOption()](#selfgetoptionstring-name-any) `v0.13.1`
* [Self.Position()](#selfposition-point)
* [Self.MoveTo()](#selfmovetopoint)
* [Self.CameraFollowMe()](#selfcamerafollowme) `v0.13.1`
* [Self.SetHitbox()](#selfsethitboxx-y-w-h-int)
* [Self.Hitbox()](#selfhitbox-rect) `v0.8.0`
* [Self.SetVelocity()](#selfsetvelocityvelocity)
* [Self.GetVelocity()](#selfgetvelocity-velocity) `v0.9.0`
* [Self.SetMobile()](#selfsetmobilebool)
* [Self.SetGravity()](#selfsetgravitybool)
* [Self.SetInvulnerable()](#selfsetinvulnerablebool) `v0.11.0`
* [Self.Invulnerable()](#selfinvulnerable-bool) `v0.11.0`
* [Self.Hide(), Self.Show()](#selfhide-selfshow) `v0.9.0`
* [Self.SetInventory()](#selfsetinventorybool)
* [Self.AddItem()](#selfadditemfilename-string-quantity-int)
* [Self.RemoveItem()](#selfremoveitemfilename-string-quantity-int)
* [Self.HasItem()](#selfhasitemfilename-string-bool)
* [Self.Inventory()](#selfinventory-mapstringint)
* [Self.ShowLayer()](#selfshowlayerindex-int)
* [Self.ShowLayerNamed()](#selfshowlayernamedname-string)
* [Self.AddAnimation()](#selfaddanimationname-string-interval-int-layers-list)
* [Self.PlayAnimation()](#selfplayanimationname-string-callback-func)
* [Self.IsAnimating()](#selfisanimating-bool)
* [Self.StopAnimation()](#selfstopanimation)
* [Self.Destroy()](#selfdestroy)
* [Actors](#actors) - actor management functions
* [Actors.At()](#actorsatpoint-actor) `v0.11.0`
* [Actors.FindPlayer()](#actorsfindplayer-actor) `v0.11.0`
* [Actors.New()](#actorsnewfilename-string-actor) `v0.11.0`
* [Actors.SetPlayerCharacter()](#actorssetplayercharacterfilename-string) `v0.11.0`
* [Level](#level) - functions and variables relating to the level
* [Level.Difficulty](#leveldifficulty-int) `v0.12.0`
* [Level.ResetTimer](#levelresettimer) `v0.12.0`
* [Timers and Intervals](#timers-and-intervals)
* [setTimeout()](#settimeoutfunction-milliseconds-int-int)
* [setInterval()](#setintervalfunction-milliseconds-int-int)
* [clearTimeout()](#cleartimeoutid-int)
* [clearInterval()](#clearintervalid-int)
* [Type Constructors](#type-constructors) - native types
* [RGBA(r,g,b,a)](#rgbared-green-blue-alpha-uint8)
* [Point(x,y)](#pointx-y-int)
* [Vector(x,y)](#vectorx-y-float64)
* [Event Handlers](#event-handlers)
* [Events.OnCollide()](#eventsoncollide-funcevent)
* [Events.OnLeave()](#eventsonleave-funcevent)
* [Events.RunKeypress()](#eventsrunkeypress-funcevent)
* [Pub/Sub Communication](#pubsub-communication) - for linked doodads
* [Official, Standard Pub/Sub Messages](#official-standard-pubsub-messages)
* [Message.Publish()](#messagepublishname-string-data)
* [Message.Subscribe()](#messagesubscribename-string-function)
* [Message.Broadcast()](#messagebroadcastname-string-data)
* * *
## Global Functions
Some useful globally available functions:
### EndLevel()
This ends the current level in a win condition, i.e. to be used by the goal flag.
### FailLevel(message string)
Trigger a failure condition in the level. For example, a hazardous doodad can cause a death message as though the player had touched a "fire" pixel on the level.
### SetCheckpoint(Point)
Set the respawn point for the player character. Usually, this will be relative to a checkpoint flag's location on the level.
```javascript
Events.OnCollide(function(e) {
if (e.Settled && e.Actor.IsPlayer()) {
SetCheckpoint(Self.Position());
}
})
```
### 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.
### time.Since(time.Time) time.Duration
**New in v0.10.1**
Get the duration since a time. Example:
```javascript
var now = time.Now()
// later
if (time.Since(now) > 5 * time.Minute) {
// 5 minutes have passed
}
```
The `time` global exposes the following duration intervals as seen in the above example:
* time.Hour
* time.Minute
* time.Second
* time.Millisecond
* time.Microsecond
* * *
## 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");
```
* * *
## Self
Self holds data about the current doodad instance loaded inside of a level. Many of these are available on other actors that collide with your doodad in the OnCollide handler, at event.Actor.
**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.IsPlayer() bool
**New in v0.8.0**
Check if the doodad is the player character. Some enemy creature doodads check this so as to disable their normal A.I. movement pattern and allow player controls to set its animations.
### 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 configure the tags in-game in the Doodad Properties window of the editor, or 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. For example, the colored keys & doors are tagged with their color so they know which color doodads they interact with.
### Self.Options() []string
**New in v0.13.1**
Returns the list of Option names defined on the doodad.
### Self.GetOption(string name) Any
**New in v0.13.1**
Get the value for an Option on this actor.
If the map creator has customized the option value, it will be returned here; otherwise the default Option at the doodad level has its value returned. If the option name didn't exist, it returns `null`
The type of the returned value will be a correct type based on the Option settings: booleans will be proper `true/false` types, integers proper Number types, etc.
### 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.MoveTo(Point)
Teleport the current doodad to an exact point on the level.
```javascript
// Teleport to origin.
Self.MoveTo(Point(0, 0))
```
### Self.CameraFollowMe()
**New in v0.13.1**
Attract the focus of the game's camera to be centered on your doodad, instead of the player character.
If the player inputs a directional control, they will reclaim the camera's focus.
### Self.SetHitbox(x, y, w, h int)
Configure the "solid hitbox" of this doodad.
You can configure the hitbox in-game in the Doodad Properties window. It is not necessary to call SetHitbox() from your doodad script.
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.Hitbox() Rect
**New in v0.8.0**
Return the current hitbox of your doodad. If you did not call Self.SetHitbox() yourself, then this will return the hitbox that was configured on the Doodad's Properties.
Check Self.Hitbox().IsZero() to see whether the doodad has a hitbox configured at all (having a value of 0,0,0,0). For example, the generic doodad scripts run checks like this:
```javascript
function main() {
// If the doodad does not have a hitbox set, default it to
// the full square canvas size of this doodad.
if (Self.Hitbox().IsZero()) {
var size = Self.Size();
Self.SetHitbox(0, 0, size, size);
}
}
```
### 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.GetVelocity() Velocity
**New in v0.9.0**
Returns the current velocity of the doodad.
Note: for playable characters, velocity is currently managed by the game engine.
### 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);
// HasGravity to check.
console.log(Self.HasGravity()); // true
```
### Self.SetInvulnerable(bool)
**New in v0.11.0**
Set whether the actor should be marked as invulnerable.
If the player character's actor is invulnerable, it will be immune to fire pixels and hostile mobs won't be able to harm them.
Certain doodads will behave differently to a doodad which is marked as invulnerable. For example, the Anvil can destroy all mobile doodads but it won't destroy other Anvils because they are invulnerable. If the player is playing _as_ the Anvil, it can destroy mobs by jumping onto them but the mobs can not harm the player.
### Self.Invulnerable() bool
**New in v0.11.0**
Returns whether the invulnerable flag is currently set on an actor.
### Self.Hide(), Self.Show()
**New in v0.9.0**
Hide the current doodad to make it invisible and Show it again.
### Self.SetInventory(bool)
Set whether this doodad has an inventory and can carry items. Doodads without inventories can not pick up keys and other items.
The player character always has an inventory, regardless which doodad they are playing as.
```javascript
Self.SetInventory(true);
Self.GetInventory(); // true
```
### Self.AddItem(filename string, quantity int)
Add an item to the current doodad's inventory. The filename is the name of the item to add, such as "key-blue.doodad"
If the quantity is zero, the item goes in as a "key item" which does not show a quantity in your inventory. The four colored keys are examples of this, as compared to the Small Key which has a quantity.
### Self.RemoveItem(filename string, quantity int)
Remove items from the current doodad's inventory.
### Self.HasItem(filename string) bool
Tests if the item is in the inventory.
### Self.Inventory() map\[string\]int
Returns the doodad's full inventory data, an object that maps filename strings to quantity integers.
### 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.
* * *
## Actors
The Actors API provides functions relating to other doodads (actors) on the level.
### Actors.At(Point) \[\]\*Actor
**New in v0.11.0**
Returns a list of the actors that intersect the given point in the level. For example, the Bird seeks the player character by scanning in a 45-degree angle and checking Actors.At() to see whether the player is at any of those points.
```javascript
let actors = Actors.At(Point(123, 456));
for (let a of actors) {
console.log("Saw actor %s", a.ID());
}
```
### Actors.FindPlayer() \*Actor
**New in v0.11.0**
Returns the nearest player character. While the game is single player, this will return **the** player character no matter where they are on the level. When multiplayer is added, it will return the player nearest to the position of the actor which asked.
The Azulians use this to identify the player and decide if the player's position is within aggro range, or to move the Azulian in the direction closest to the player character.
### Actors.New(filename string) \*Actor
**New in v0.11.0**
> **NOT TESTED:** this function supports future growth in the game but has not been tested yet and may have bugs.
Create a new actor and place them on the level. The filename will be the doodad name, like `example.doodad`. Returns the newly created Actor so that you may call functions like MoveTo() or SetVelocity() on it.
### Actors.SetPlayerCharacter(filename string)
**New in v0.11.0**
Replace the nearest player character with the named doodad.
This is used by the Checkpoint Flag when the flag is linked to a doodad so that it replaces the player character with it.
* * *
## Level
### Level.Difficulty int
**New in v0.12.0**
This integer holds the difficulty setting of the current level:
* \-1 is Peaceful
* 0 is Normal (default)
* 1 is Hard
An enemy doodad can alter its behavior by checking the difficulty level. For example, the Azulians become more aggressive on Hard difficulty.
```javascript
if (Level.Difficulty > 0) {
// Hard mode
} else if (Level.Difficulty < 0) {
// Peaceful mode
}
```
### Level.ResetTimer()
**New in v0.12.0**
Calling this function will reset the in-game level timer to zero.
* * *
## 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.
* * *
## 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).
The event argument is the same as OnCollide, with the Actor available and Settled=true (others left as default zero values).
### 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:ready | (none) | The level is ready and it is now safe for doodads to publish messages to others. |
| broadcast:state-change | boolean | An "ON/OFF" button was hit and all state blocks should flip. |
| broadcast:checkpoint | string | A checkpoint flag was reached. Value is the actor ID of the checkpoint flag. |
| sticky:down | boolean | A sticky button is pressed Down. If linked to other normal buttons, it tells them to press down as well. Sends a `false` when the Sticky Button itself pops back up. |
| switch:toggle | boolean | A switch has been toggled from on to off. |
### 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);
}
})
}
```

View File

@ -1,33 +1,22 @@
# Creating Custom Levels
One of the core gameplay features is the Level Editor which lets you draw your
own custom maps to play and share with others.
One of the core gameplay features is the 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.
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:
When creating a new level, you first choose some settings for it. These are described below:
![Screenshot of New Level dialog](../images/newlevel-1.png)
### Page Type
This setting controls the size and boundaries of your level, and control the
appearance of the notebook paper background of your level.
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.
* **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. You can adjust the boundary size later if you need more room!
* **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.
You can change these settings later if you change your mind.
@ -35,328 +24,208 @@ You can change these settings later if you change your mind.
![Wallpaper options on New Level](../images/palettes.png)
The wallpaper affects the "theme" of your level. Sketchy Maze is themed around
hand-drawn mazes on paper, so the built-in themes look like various kinds of
paper.
The wallpaper affects the "theme" of your level. Sketchy Maze 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.
* **Graph paper** `new in v0.6.0` is a white paper with a repeating grid pattern
of light-grey lines about 32px each; a darker dotted gray grid every 3x3 units;
and a teal grid every 6x6 units. A light version of Blueprint.
* **Dotted paper** `new in v0.6.0` is a white page with a repeating pattern of
single dots every 32 pixels apart. Most dots are light blue but with a grid of
red dots every 6x6 units. It has the same grid spacing as the Graph paper.
* **Blueprint** is a dark blueprint paper background with a repeating grid pattern.
A dark version of Graph paper; you'll want to pair it with the **Blueprint Palette**
which has light colors for level geometry by default.
* **Pure White** is a blank, white (#FFFFFE) background color with nothing going
on except for what you draw on your level.
* **Custom wallpaper...** lets you use your own wallpaper image. See
[Custom Wallpaper](custom-wallpaper.md).
* **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.
* **Graph paper** `new in v0.6.0` is a white paper with a repeating grid pattern of light-grey lines about 32px each; a darker dotted gray grid every 3x3 units; and a teal grid every 6x6 units. A light version of Blueprint.
* **Dotted paper** `new in v0.6.0` is a white page with a repeating pattern of single dots every 32 pixels apart. Most dots are light blue but with a grid of red dots every 6x6 units. It has the same grid spacing as the Graph paper.
* **Blueprint** is a dark blueprint paper background with a repeating grid pattern. A dark version of Graph paper; you'll want to pair it with the **Blueprint Palette** which has light colors for level geometry by default.
* **Pure White** is a blank, white (#FFFFFE) background color with nothing going on except for what you draw on your level.
* **Custom wallpaper...** lets you use your own wallpaper image. See [Custom Wallpaper](custom-wallpaper.md).
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:
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.
* **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.
### Default Palette
When starting a **new** level, you may choose a default Palette to start out
with. The available options as of **version 0.6.0** are:
When starting a **new** level, you may choose a default Palette to start out with. The available options as of **version 0.10.0** are:
* **Default:** the classic palette from previous alpha game releases.
1. **solid**: black, solid geometry
2. **decoration**: light grey
3. **fire**: red, fire
4. **water**: blue, water
5. **hint**: pink
* **Colored Pencil:** a new palette with some more varied default colors.
1. **grass**: green, solid geometry
2. **dirt**: brown, solid
3. **stone**: dark grey, solid
4. **fire**: red, fire
5. **water**: light blue (#0099FF), water
6. **hint**: pink
* **Blueprint:** the classic palette for levels with the Blueprint wallpaper:
1. **solid**: white, solid
2. **decoration:** light grey
3. **fire**: light red (#FF5000), fire
4. **water**: light blue (#0099FF), water
5. **electric**: yellow, solid
6. **hint**: pink
* **Default:** the classic palette from previous alpha game releases.
1. **solid**: black, solid geometry
2. **decoration**: light grey
3. **fire**: red, fire
4. **water**: blue, water
5. **hint**: pink
* **Colored Pencil:** a new palette with some more varied default colors.
1. **darkstone**: dark grey, solid geometry
2. **grass**: green, solid
3. **dirt**: brown, solid
4. **stone**: grey, solid
5. **sandstone**: orangey, solid
6. **fire**: red, fire
7. **water**: light blue (#0099FF), water
8. **hint**: pink
* **Blueprint:** the classic palette for levels with the Blueprint wallpaper:
1. **solid**: white, solid
2. **decoration:** light grey
3. **fire**: light red (#FF5000), fire
4. **water**: light blue (#0099FF), water
5. **electric**: yellow, solid
6. **hint**: pink
In earlier alpha versions of the game, the Blueprint palette was chosen by
default when the level starts out with the Blueprint wallpaper, which has a
very dark background color and Blueprint was basically a bright version of
the Default palette. As of v0.6.0, the user can select the palette separately
from the wallpaper.
In earlier alpha versions of the game, the Blueprint palette was chosen by default when the level starts out with the Blueprint wallpaper, which has a very dark background color and Blueprint was basically a bright version of the Default palette. As of v0.6.0, the user can select the palette separately from the wallpaper.
If you're using the Blueprint wallpaper, pick the Colored Pencil or Blueprint
palettes for best results: the default black color for level geometry won't
show well on the Blueprint wallpaper!
If you're using the Blueprint wallpaper, pick the Colored Pencil or Blueprint palettes for best results: the default black color for level geometry won't show well on the Blueprint wallpaper!
### Title and Author
After you start editing your level, access the "Level -> Level
Properties" menu to reopen the level settings and you will find the
options to edit the Title of your level and change the Author name
if you like. The default author copies your OS username.
After you start editing your level, access the "Level -> Level Properties" menu to reopen the level settings and you will find the options to edit the Title of your level and change the Author name if you like. The default author copies your OS username.
### Game Rules
Opening the "Level -> Level Properties" window from the editor, the Game Rules can be customized for your level and can tweak some of the gameplay behaviors to allow for some more interesting maps.
The current game rules available include:
#### Difficulty Setting
The default level difficulty is Normal but you can also set your level's difficulty to the Peaceful or Hard options:
* On Peaceful mode, enemy mobs will generally ignore the player character entirely and do not attack.
* On Hard mode, enemy mobs become more aggressive. For example, the Azulians will have an infinite aggro radius and will come at the player character no matter how far away you are.
Specific behaviors of enemies are up to their programming. Doodad scripts can query the `Level.Difficulty` value and react accordingly.
#### Survival Mode
On levels where player death is all but guaranteed (e.g. a Hard Mode, [Azulian Tag](../story-mode.md#azulian-tag)) map) the Survival Mode setting will invert the definition of "high score" for the silver tier in Story Mode.
Normally, the high score is rewarded to the fastest time to complete a level. If the player dies and respawns from checkpoint, they get a silver medal and if they make it to the Exit Flag unscathed they get a gold medal. For Survival levels, the level is completed if the player dies but they are rewarded for _how long_ they survived rather than how quickly they died. The gold medal for Survival levels is still based on how quickly they beat the level.
See [Azulian Tag](../story-mode.md#azulian-tag) for more details.
## Editor Interface
![Level Editor View](../images/newlevel-2.png)
This is the level editor. You can click and drag on the blank page and begin
drawing a level. The toolbar down the left has various drawing tools: Pencil
(freehand), Line, Rectangle, Ellipse. The toolbar down the right is your level
palette. You can mouse-over most buttons and see an immediate tooltip appear
that describes what it is.
This is the level editor. You can click and drag on the blank page and begin drawing a level. The toolbar down the left has various drawing tools: Pencil (freehand), Line, Rectangle, Ellipse. The toolbar down the right is your level palette. You can mouse-over most buttons and see an immediate tooltip appear that describes what it is.
Quick 5-second overview of the editor interface:
* The top of the window has your [**Menu Bar**](#menu-bar), a common sight on desktop applications.
* The panel on the left side of the window is your **Tool Box**. Clicking these
buttons activates a specific drawing tool or mode:
* <img src="../images/sprites/pencil-tool.png" width="16" height="16">
**Pencil Tool** lets you click, drag, and draw pixels of your selected
Palette color onto your level freehand. Shortcut key: `f`
* <img src="../images/sprites/line-tool.png" width="16" height="16">
**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.
Shortcut key: `l`
* <img src="../images/sprites/rect-tool.png" width="16" height="16">
**Rectangle Tool** lets you easily draw rectangles on your level. Shortcut: `r`
* <img src="../images/sprites/ellipse-tool.png" width="16" height="16">
**Ellipse Tool** lets you draw circles or elliptical shapes. Shortcut: `c`
* <img src="../images/sprites/actor-tool.png" width="16" height="16">
**Doodad Tool** lets you drag doodads such as buttons and doors onto your
level. See the [Doodad Tool](#doodad-tool) below. Shortcut: `d`
* <img src="../images/sprites/link-tool.png" width="16" height="16">
**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.
* <img src="../images/sprites/eraser-tool.png" width="16" height="16">
**Eraser Tool** cleans up your mistakes. Click and drag over pixels you've
drawn to delete the pixels from your level. Shortcut: `x`
* The **Size:** label on the left controls 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. Mouse over a color to see its name and properties -- different
colors may mean different things!
* The **Edit** button will open the Palette Editor where you can change a
color, rename it, and set its properties. See [Color Attributes](#color-attributes)
* The top of the window has your [**Menu Bar**](#menu-bar), a common sight on desktop applications.
* The panel on the left side of the window is your **Tool Box**. Clicking these buttons activates a specific drawing tool or mode:
* ![](../images/sprites/pan-tool.png) **Pan Tool** lets you scroll the level viewport by clicking and dragging. Ideal for mobile where you rely on a touch screen and don't want to accidentally draw onto your level! Middle-click and two-finger swipe gestures can also scroll the level viewport at all times.
* ![](../images/sprites/pencil-tool.png) **Pencil Tool** lets you click, drag, and draw pixels of your selected Palette color onto your level freehand. Shortcut key: `f`
* ![](../images/sprites/line-tool.png) **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. Shortcut key: `l`
* ![](../images/sprites/rect-tool.png) **Rectangle Tool** lets you easily draw rectangles on your level. Shortcut: `r`
* ![](../images/sprites/ellipse-tool.png) **Ellipse Tool** lets you draw circles or elliptical shapes. Shortcut: `c`
* ![](../images/sprites/text-tool.png) **Text Tool** lets you stamp text messages onto your level. You can choose between the game's built-in fonts (including the Azulian font) and set the size and color from your palette.
* ![](../images/sprites/flood-tool.png) **Flood Tool** (or 'paint bucket tool') can easily replace a contiguous area of your drawing from one color to another.
* ![](../images/sprites/eraser-tool.png) **Eraser Tool** cleans up your mistakes. Click and drag over pixels you've drawn to delete the pixels from your level. Shortcut: `x`
* ![](../images/sprites/actor-tool.png) **Doodad Tool** lets you drag doodads such as buttons and doors onto your level. See the [Doodad Tool](#doodad-tool) below. Shortcut: `q`
* ![](../images/sprites/link-tool.png) **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.
* The **Size:** label on the left controls 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. Mouse over a color to see its name and properties -- different colors may mean different things!
* The **Edit** button will open the Palette Editor where you can change a color, rename it, and set its properties. See [Color Attributes](#color-attributes)
---
* * *
## Doodad Tool
When clicking on the <img src="../images/sprites/actor-tool.png" width="16" height="16"> **Doodad Tool** or
pressing the `d` key, the Doodads window will appear in the level editor:
When clicking on the ![](../images/sprites/actor-tool.png) **Doodad Tool** or pressing the `q` key, the Doodads window will appear in the level editor:
![Doodads window](../images/doodads.png)
Doodads are objects you drag and drop into your level to add interactive elements
such as enemies and buttons. Mousing over a doodad will tell you its name, and
the pager buttons at the bottom can show more options. See the
[list of built-in doodads](../doodads.md) for details on what each one does.
Doodads are objects you drag and drop into your level to add interactive elements such as enemies and buttons. Mousing over a doodad will tell you its name, and the pager buttons at the bottom can show more options. See the [list of built-in doodads](../doodads.md) for details on what each one does.
Click and drag a doodad from the Doodads window onto your level to place it.
While the **Doodad Tool** is active on the left toolbar, when you mouse over an
existing doodad on your level, and orange box will appear around it. You may
click and drag to move this doodad somewhere else. Right-click it to remove it
from your level.
While the **Doodad Tool** is active on the left toolbar, when you mouse over an existing doodad on your level, and orange box will appear around it. You may click and drag to move this doodad somewhere else. Right-click it to remove it from your level.
* Left click: **move a doodad** somewhere else on your level.
* Right click: **removes the doodad** from your level.
* Left click: **move a doodad** somewhere else on your level.
* Right click: **removes the doodad** from your level.
Doodads provide various useful features to your level:
* **Keys and Locked Doors** let you place collectible keys around the level which,
when obtained, allow the player to permanently unlock doors of the same color
and open new paths on the level. There are four pairs of keys and doors.
* **Buttons and Switches** let you open and close **Electric Doors** and trigger
other devices to which they are linked.
* **Trapdoors** provide one-way gates; once the door has swung shut, it can not