* Added to the F3 Debug Overlay is a "Texture:" label that counts the number
of textures currently loaded by the (SDL2) render engine.
* Added Teardown() functions to Level, Doodad and the Chunker they both use
to free up SDL2 textures for all their cached graphics.
* The Canvas.Destroy() function now cleans up all textures that the Canvas
is responsible for: calling the Teardown() of the Level or Doodad, calling
Destroy() on all level actors, and cleaning up Wallpaper textures.
* The Destroy() method of the game's various Scenes will properly Destroy()
their canvases to clean up when transitioning to another scene. The
MainScene, MenuScene, EditorScene and PlayScene.
* Fix the sprites package to actually cache the ui.Image widgets. The game
has very few sprites so no need to free them just yet.
Some tricky places that were leaking textures have been cleaned up:
* Canvas.InstallActors() destroys the canvases of existing actors before it
reinitializes the list and installs the replacements.
* The DraggableActor when the user is dragging an actor around their level
cleans up the blueprint masked drag/drop actor before nulling it out.
Misc changes:
* The player character cheats during Play Mode will immediately swap out the
player character on the current level.
* Properly call the Close() function instead of Hide() to dismiss popup
windows. The Close() function itself calls Hide() but also triggers
WindowClose event handlers. The Doodad Dropper subscribes to its close
event to free textures for all its doodad canvases.
* Bird is not solid when colliding with other birds.
* If the dev shell is used to run JavaScript during Play Mode, consider
it cheating (so player can't `$ d.Scene.ResetTimer()` for example)
* On Survival Mode levels, DieByFire immediately opens the End Level
(silver score) modal rather than respawn from checkpoint, so levels
don't need checkpoint contraptions to end the level.
* During level loading screens, wait and call doodads' main() function
until the very end.
New features:
* Flood Tool for the editor. It replaces pixels of one color with another,
contiguously. Has limits on how far from the original pixel it will color,
to avoid infinite loops in case the user clicked on wide open void. The
limit when clicking an existing color is 1200px or only a 600px limit if
clicking into the void.
* Cheat code: 'master key' to play locked Story Mode levels.
Level GameRules feature added:
* A new tab in the Level Properties dialog
* Difficulty has been moved to this tab
* Survival Mode: for silver high score, longest time alive is better than
fastest time, for Azulian Tag maps. Gold high score is still based on
fastest time - find the hidden level exit without dying!
Tweaks to the Azulians' jump heights:
* Blue Azulian: 12 -> 14
* Red Azulian: 14 -> 18
* White Azulian: 16 -> 20
Bugs fixed:
* When editing your Palette to rename a color or add a new color, it wasn't
possible to draw with that color until the editor was completely unloaded
and reloaded; this is now fixed.
* Minor bugfix in Difficulty.String() for Peaceful (-1) difficulty to avoid
a negative array index.
* Try and prevent user giving the same name to multiple swatches on their
palette. Replacing the whole palette can let duplication through still.
* Loadscreen: put the progress bar between the Title and Subtitle so it
looks good even on mobile landscape orientation (narrow height)
* Bugfixes around window OnResize events: the loadscreen handles
resizing correctly now and the Level Editor (or w/e) will also be the
right size if you resized the window during loading.
* When playing as the Bird, the dive attack is able to destroy other
mobile doodads such as Azulians and Thieves.
* The Box has been made invulnerable so it can't be destroyed by Anvils
or player-controlled Birds.
* Bugfixes with pop-up modals:
* The quit game confirm modal doesn't appear if another modal is
already active on screen.
* The Escape key can dismiss Alert and Confirm modals.
* Add "Level" menu items to Play Mode to restart the level or retry from
the last checkpoint (in case of softlocks, etc.)
* The title screen now loads the default maps from a LevelPack. The game
no longer ships with the Tutorial levels in the "levels" folder as
default; they are in the LevelPack so the "Edit Drawing" screen begins
as a blank slate for only user levels.
* Add the Zoo level to the Tutorial levelpack
* Bugfixes around changing the player character to work around clipping
issues if the character has changed height drastically.
* Add methods `Invulnerable() bool` and `SetInvulnerable(bool)` to the
Actor API accessible in JavaScript (e.g. `Self.SetInvulnerable(true)`)
* The Anvil is invulnerable - when played as, it can crush other mobs by
jumping on them but is not defeated by those mobs at the same time.
* Anvils don't destroy invulnerable mobs, such as other Anvils.
* Bugfix: the Electric Door is considered to be opened from the first
frame of animation when the door begins opening, and remains opened
until the final frame of animation when it is closing.
* New cheat code: `megaton weight` to play as the Anvil by default.
Adds support for Xbox and Nintendo style game controllers. The gamepad
controls are documented on the README and in the game's Settings window.
The buttons are not customizable yet, except that the player can choose
between two button styles:
* X Style (default): "A" button is on the bottom and "B" on the right.
* N Style: swaps the A/B and the X/Y buttons to use a Nintendo-style
layout instead of an Xbox-style.
Link a Doodad to a Checkpoint Flag (like you would a Start Flag) and
crossing the flag will replace the player with that doodad. Multiple
checkpoint flags like this can toggle you between characters.
* Azulians are now friendly to player characters who have the word
"Azulian" in their title.
* Improve Bird as the playable character:
* Dive animation if the player flies diagonally downwards
* Animation loop while hovering in the air instead of pausing
* Checkpoint flags don't spam each other on PubSub so much which could
sometimes lead to deadlocks!
SetPlayerCharacter added to the JavaScript API. The Checkpoint Flag
(not the region) can link to a doodad and replace the player character
with that linked doodad when you activate the checkpoint:
Actors.SetPlayerCharacter(filename string): like "boy.doodad"
Add various panic catchers to make JavaScript safer and log issues
to console.
* Respawning from a checkpoint grants 3 seconds of immunity in case
enemies are spawn camping.
* Add the white Azulian as an even faster and harder enemy than the red
Azulian: twice as fast, jumps higher, and can detect the player from
further away.
* If the player runs the PlayAsBird cheat they shouldn't be able to win
a high score on a level, so at level startup it detects whether the
DefaultPlayerCharacterDoodad has changed from default on a level that
doesn't use the Start Flag to set a specific doodad - and immediately
marks the session as cheated
* New doodad: Invisible Warp Door
* All warp doors require the player to be grounded (if affected by
gravity) to open them. No jumping or falling thru and opening
a warp door mid-air!
* Title Screen now randomly selects from a couple of levels.
* Title Screen: if it fails to load a level it sets up a basic
blank level with a wallpaper instead.
* New developer shell command: titlescreen <level>
Opens the MainScene with a custom user level as the background.
* Add Auto-save to the Editor to save your drawing every 5 minutes
* Add a MenuBar to the Play Scene for easier navigation to other
features of the game.
* Doodad JS API: time.Since() now available.
* Adds pkg/savegame to store user progress thru Level Packs.
* The savegame.json is mildly tamper resistant by including a checksum
along with the JSON body.
* The checksum combines the JSON string + an app secret (in savegame.go)
+ user specific entropy (stored in their settings.json). If the user
modifies their save file and the checksum becomes invalid the game
will not load the save file, acting like it didn't exist, resetting
all their high scores.
Updates to the Story Mode window:
* On the LevelPacks list: shows e.g. "[completed 0 of 3 levels]" showing
a user's progress thru the level pack.
* Below the levels on the Detail screen:
* Shows an indicator whether the level is completed or not.
* Shows high scores (fastest times beating the level)
* Shows a padlock icon if levels are locked and the player hasn't
reached them yet. Pops up an Alert modal if a locked level is
clicked on.
Scoring is based around your fastest time elapsed to finish the level.
* Perfect Time (gold coin): player has not died during the level.
* Best Time (silver coin): player has continued from a checkpoint.
In-game an elapsed timer is shown in the top left corner along with the
gold or silver coin indicating if your run has been Perfect.
If the user enters any Cheat Codes during gameplay they are not eligible
to win a high score, but the level will still be marked as completed.
The icon next to the in-game timer disappears when a cheat code has been
entered.
* The "Story Mode" button on the MainScene opens the levelpacks window.
* Levelpacks from all places are shown (built-in and user files), basic
level picker works.
* When playing a level out of a levelpack: the PlayScene gets the file
data from the zipfile and plays it OK.
* When a levelpack level is solved, the "Next Level" button appears on
the success modal and hitting Return will advance to the next level in
the pack. The final level doesn't show this button.
* The user can edit levelpack levels! Clicking the "Edit" button on the
Play Mode moves the loaded level over to the EditScene and the user
could save it to disk or edit/playtest it perfectly OK! The link to
the levelpack is lost upon opening in the editor, so the "Next Level"
victory button doesn't appear.
- Fix a memory sharing bug in the Giant Screenshot feature.
- Main Menu to eagerload chunks in the background to make scrolling less
jittery. No time for a loadscreen!
- Extra script debugging: names/IDs of doodads are shown when they send
messages to one another.
- Level Properties: you can edit the Bounded max width/height values for
the level.
Doodad changes:
- Buttons: fix a timing bug and keep better track of who is stepping on it,
only popping up when all colliders have left. The effect: they pop up
immediately (not after 200ms) and are more reliable.
- Keys: zero-qty keys will no longer put themselves into the inventory of
characters who already have one except for the player character. So
the Thief will not steal them if she already has the key.
Added to the JavaScript API:
* time.Hour, time.Minute, time.Second, time.Millisecond, time.Microsecond
* Recolor some of the region doodads
* Add command: `doodad edit-level --remove-actor` to remove actors from
your level.
* Tweak the player jump velocity from playtesting levels.
* The "Giant Screenshot" feature takes a very long time, so it is made
asynchronous. If you try and run a second one while the first is busy,
you get an error flash. You can continue editing the level, even
playtest it, or load a different level, and it will continue crunching
on the Giant Screenshot and flash when it's finished.
* Updated the player physics to use proper Velocity to jump off the
ground rather than the hacky timer-based fixed speed approach.
* FlashError() function to flash "error level" messages to the screen.
They appear in orange text instead of the usual blue, and most error
messages in the game use this now. The dev console "error <msg>"
command can simulate an error message.
* Flashed message fonts are updated. The blue font now uses softer
stroke and shadow colors and the same algorithm applies to the orange
error flashes.
Some other changes to player physics:
* Max velocity, acceleration speed, and gravity have been tweaked.
* Fast turn-around if you are moving right and then need to go left.
Your velocity resets to zero at the transition so you quickly get
going the way you want to go.
Some levels that need a bit of love for the new platforming physics:
* Tutorial 3.level
In the level editor, the "Play (P)" button has a new feature: Play
From Here. On mouse down you begin dragging a silhouette of Boy or
whoever the default player character is, as if you were dragging a
doodad onto your level.
Drop the silhouette on your level and enter Play Mode from that
location instead of the Start Flag.
Release your cursor over the Play button or press the "P" key to
spawn at the Start Flag as usual.
In the editor, clicking and dragging with the middle mouse button
will scroll the view of the editor in place of the arrow keys.
When entering Play Mode, the original scroll position in the level
editor is remembered for when you come back - no more having to
scroll from 0,0 each time to get back to where you were working!
The game can now be played using only a touch screen! The left
mouse click (Button1) can now move and control the player
character.
* A box in the very middle of the screen is the "Use" button and
a deadzone for directional inputs.
* Anywhere outside the middle and to the left registers a Left
button, to the right a Right button, above the top of the middle
is a Jump button, and below the bottom of the middle is a down
input (for antigravity mode).
* Tight platforming is possible: above and below the middle box,
the left/right split is tight in the middle of the window. You
can get tight jumps if jumping or go below if you don't want to
jump. The left/right deadzone is only over the space of the Use
button.
If the player is idle for a while with no controller inputs, some
hints will fade in about the touch controls.
Note: the ScrollboxOffset to track the player character is changed
to 60,60 from 60,100 so the camera will track tighter to the player
and so the player will mostly be over the Use button on touch
controls as long as he's away from a level boundary.
* New Doodad: Checkpoint Flag. They update the player's spawn point
whenever the player passes one. The most recently activated
checkpoint is rendered brighter than the others.
* End Level Modal: the fake alert box window drawn by the Play Mode
is replaced with a fancy modal widget (similar to Alert and Confirm).
It handles level victory or failure conditions and can show or hide
all the buttons as needed.
* Gameplay: There is a "Retry from Checkpoint" option added, which
appears in the level failure modal. It will teleport you back to
the Start Flag or the last Checkpoint Flag you had touched, without
resetting the level -- your keys, unlocked doors, etc. will be
preserved so you can retry.
* Set a maximum speed on the "Camera Follows Actor" logic of 64
pixels per tick. This results in a smoother scrolling transition
when the player jumps to a new location on the map, such as by
a Warp Door.
* Update the default color palettes:
* All: Add a "hint" magenta color.
* Colored Pencil: Add a "darkstone" solid color.
Updates to the Doodads JavaScript API:
* SetCheckpoint(Point(x, y)): set the player character's spawn
position. Giving it Self.Position() is an easy way to set the
player spawn to your doodad's location.
* Added the Death Barrier to Play Mode to catch players from falling off
the map and then falling indefinitely, especially on Unbounded maps.
* The Death Barrier is set 1,000 pixels below the lowest point on your
map. If the player falls here they get a death message: "Watch out for
falling off the map!"
* Added cheat codes to change the default Player Character doodad, as a
way to force play as a different character (for levels which don't
specify a custom character):
* Play as Bird: fly like a bird
* Play as Blue Azulian: the cell
* Play as Thief: play as thief
* Reset to default (Boy): pinocchio
New feature: link a Start Flag to another doodad in your level
and you will play as that doodad instead of Boy. All Creatures
are designed to be playable. Playing as "other" doodads leads
to interesting effects, like not being able to activate buttons,
switches, or warp doors and not having an inventory to pick up
keys. The Anvil is fun: it can destroy other mobile doodads by
jumping on them.
If the actor does not specify that it has gravity, the gameplay
starts in antigravity mode. This will be the vast majority of
non-mobile doodads and the Bird.
Other changes:
* The Blue and Red Azulians now share a doodad script.
* The Azulians AI is still to walk back and forth, pickup keys and
press buttons. The Blue Azulian walks slower than the red one.
* The Blue Azulian is no longer hidden from the doodads list.
* Actor UUID values in levels are now V1 UUIDs (time-ordered).
This will help to reliably resolve conflicts in draw order
of overlapping doodads (newest added to level wins).
* Link Tool: clicking on a pair of already-linked doodads will
now unlink them, so you don't have to delete one to delete
the link.
* Actor Tool: deleting an actor immediately calls PruneLinks()
to clean up any links that the deleted doodad might have.
This commit adds the Thief character with starter graphics
(no animations).
The Thief walks back and forth and will steal items from other
doodads, including the player. For singleton items that have no
quantity, like the Colored Keys, the Thief will only steal one
if he does not already have it. Quantitied items like the
Small Key are always stolen.
Flexibility in the playable character is introduced: Boy,
Azulian, Bird, and Thief all respond to playable controls.
There is not currently a method to enable these apart from
modifying balance.PlayerCharacterDoodad at compile time.
New and Changed Doodads
* Thief: new doodad that walks back and forth and will steal
items from other characters inventory.
* Bird: has no inventory and cannot pick up items, unless player
controlled. Its hitbox has also been fixed so it collides with
floors correctly - not something normally seen in the Bird.
* Boy: opts in to have inventory.
* Keys (all): only gives themselves to actors having inventories.
JavaScript API - New functions available
* Self.IsPlayer() - returns if the current actor IS the player.
* Self.SetInventory(bool) - doodads must opt-in to having an
inventory. Keys should only give themselves to doodads having
an inventory.
* Self.HasInventory() bool
* Self.AddItem(filename, qty)
* Self.RemoveItem(filename, qty)
* Self.HasItem(filename)
* Self.Inventory() - returns map[string]int
* Self.ClearInventory()
* Self.OnLeave(func(e)) now receives a CollideEvent as parameter
instead of the useless actor ID. Notably, e.Actor is the
leaving actor and e.Settled is always true.
Other Changes
* Play Mode: if playing as a character which doesn't obey gravity,
such as the bird, antigravity controls are enabled by default.
If you `import antigravity` you can turn gravity back on.
* Doodad collision scripts are no longer run in parallel
goroutines. It made the Thief's job difficult trying to steal
items in many threads simultaneously!
* The Anvil doodad is affected by gravity and becomes dangerous when
falling. If it lands on the player character, you die! If it lands on
any other mobile doodad, it destroys it! It can land on solid doodads
such as the Electric Trapdoor and the Crumbly Floor. It will activate
a Crumbly Floor if it lands on one, and can activate buttons and
switches that it passes.
* JavaScript API: FailLevel(message) can be called from a doodad to kill
the player character. The Anvil does this if it collides with the
player while it's been falling.
* The loading screen for Edit and Play modes is stable and the risk of
game crash is removed. The root cause was the setupAsync() functions
running on a background goroutine, and running SDL2 draw functions
while NOT on the main thread, which causes problems.
* The fix is all SDL2 Texture draws become lazy loaded: when the main
thread is presenting, any Wallpaper or ui.Image that has no texture
yet gets one created at that time from the cached image.Image.
* All internal game logic then uses image.Image types, to cache bitmaps
of Level Chunks, Wallpaper images, Sprite icons, etc. and the game is
free to prepare these asynchronously; only the main thread ever
Presents and the SDL2 textures initialize on first appearance.
* Several functions had arguments cleaned up: Canvas.LoadLevel() does
not need the render.Engine as (e.g. wallpaper) textures don't render
at that stage.
* pkg/loadscreen implements a global Loading Screen for loading heavy
levels for playing or editing.
* All chunks in a level are pre-rendered to bitmap before gameplay
begins, which reduces stutter as chunks were being lazily rendered on
first appearance before.
* The loading screen can be played with in the developer console:
$ loadscreen.Show()
$ loadscreen.Hide()
Along with ShowWithProgress(), SetProgress(float64) and IsActive()
* Chunker: separate the concerns between Bitmaps an (SDL2) Textures.
* Chunker.Prerender() converts a chunk to a bitmap (a Go image.Image)
and caches it, only re-rendering if marked as dirty.
* Chunker.Texture() will use the pre-cached bitmap if available to
immediately produce the SDL2 texture.
Other miscellaneous changes:
* Added to the Colored Pencil palette: Sandstone
* Added "perlin noise" brush pattern
Note: this commit introduces instability and crashes:
* New `asyncSetup()` functions run on a goroutine, but SDL2 texture
calls must run on the main thread.
* Chunker avoids this by caching bitmaps, not textures.
* Wallpaper though is unstable, sometimes works, sometimes has graphical
glitches, sometimes crashes the game.
* Wallpaper.Load() and the *Texture() functions are where it crashes.
* Added a Settings window for game options, such as enabling the
horizontal toolbars in Edit Mode. The Settings window also has a
Controls tab showing the gameplay buttons and keyboard shortcuts.
* The Settings window is available as a button on the home screen OR
from the Edit->Settings menu in the EditScene.
* Bugfix: using WASD to move the player character now works better and
is considered by the game to be identical to the arrow key inputs. Boy
now updates his animation based on these keys, and they register as
boolean on/off keys instead of affected by key-repeat.
* Refactor the boolProps: they are all part of usercfg now, and if you
run e.g. "boolProp show-all-doodads true" and then cause the user
settings to save to disk, that boolProp will be permanently enabled
until turned off again.
* The F4 key to draw collision boxes works reliably again: it draws the
player's hitbox in world-space using the canvas.DrawStrokes()
function, rather than in screen-space so it follows the player
reliably.
* The F4 key also draws hitboxes for ALL other actors in the level:
buttons, enemies, doors, etc.
* The level geometry collision function is updated to respect a doodad's
declared Hitbox from their script, which may result in a smaller box
than their raw Canvas size. The result is tighter collision between
doodads, and Boy's sprite is rather narrow for its square Canvas so
collision on rightward geometry is tighter for the player character.
* Collision checks between actors also respect the actor's declared
hitboxes now, allowing for Boy to get even closer to a locked door
before being blocked.
New doodad interactions:
* Sticky Buttons will emit a "sticky:down" event to linked doodads, with
a boolean value showing the Sticky Button's state.
* Normal Buttons will listen for "sticky:down" -- when a linked Sticky
Button is pressed, the normal Button presses in as well, and stays
pressed while the sticky:down signal is true.
* When the Sticky Button is released (e.g. because it received power
from another doodad), any linked buttons which were sticky:down
release as well.
* Switch doodads emit a new "switch:toggle" event JUST BEFORE sending
the "power" event. Sensitive Doodads can listen for switches in
particular this way.
* The Electric Door listens for switch:toggle; if a Switch is activated,
the Electric Door always flips its current state (open to close, or
vice versa) and ignores the immediately following power event. This
allows doors to toggle on/off regardless of sync with a Switch.
Other changes:
* When the player character dies by fire, instead of the message saying
"Watch out for fire!" it will use the name of the fire swatch that
hurt the player. This way levels could make it say "Watch out for
spikes!" or "lava" or whatever they want. The "Fire" attribute now
just means "instantly kills the player."
* Level Editor: You can now edit the Title and Author name of your level
in the Page Settings window.
* Bugfix: only the player character ends the game by dying in fire.
Other mobile doodads just turn dark but don't end the game.
* Increase the size of Trapdoor doodad sprites by 150% as they were a
bit small for the player character.
* Rename the game from "Project: Doodle" to "Sketchy Maze"
* The "Use Key" (Q or Spacebar) now activates the Warp Door instead of a
collision event doing so.
* Warp Doors are now functional: the player opens a door, disappears,
the door closes; player is teleported to the linked door which opens,
appears the player and closes.
* If the player exits thru a Blue or Orange door which is disabled
(dotted outline), the door still opens and drops the player off but
returns to a Disabled state, acting as a one-way door.
* Clean up several debug log lines from Doodle and doodad scripts.
* pkg/keybinds holds central functions to check global keybinds, like
DebugOverlay (F3), Undo (Ctrl-Z), GotoPlay/GotoEdit (p/e), etc.
* The Tools menu in the editor mode lists out more options to select
various drawing tools (line, pencil, etc.) - and showing the hotkey
for each tool.
* Added initial walking sprites for the player character, "Boy."
* Player doodad filename and title screen level are now configurable in
the balance/numbers.go package.
* Take advantage of the new Window Manager feature of the UI toolkit.
* Move the MenuScene's "New Level" and "Play/Edit Level" windows into
stand-alone functions in new pkg/windows/ package. The 'windows'
package is isolated from the rest of Doodle and communicates using
config variables and callback functions to avoid circular dependency.
* MenuScene calls the window constructors from the new package.
* Add an "Options" button to the Menu Bar in the Editor Scene, which
opens the "New Level" window to allow changing the wallpaper or
bounding type of the level currently being edited.
* Move the cheat codes into their own file, cheats.go
* Player character now experiences acceleration and friction when
walking around the map!
* Actor position and movement had to be converted from int's
(render.Point) to float64's to support fine-grained acceleration
steps.
* Added "physics" package and physics.Vector to be a float64 counterpart
for render.Point. Vector is used for uix.Actor.Position() for the sake
of movement math. Vector is flattened back to a render.Point for
collision purposes, since the levels and hitboxes are pixel-bound.
* Refactor the uix.Actor to no longer extend the doodads.Drawing (so it
can have a Position that's a Vector instead of a Point). This broke
some code that expected `.Doodad` to directly reference the
Drawing.Doodad: now you had to refer to it as `a.Drawing.Doodad` which
was ugly. Added convenience method .Doodad() for a shortcut.
* Moved functions like GetBoundingRect() from doodads package to
collision, where it uses its own slimmer Actor interface for just the
relevant methods it needs.
* Added an inventory system for actors as a replacement to the arbitrary
key/value data store. Colored keys now add themselves to the player's
inventory, and colored doors check the inventory.
* Inventory is a map[string]int between doodad filenames
(red-key.doodad) and quantity (0 for key items/unlimited qty).
* API methods to add and remove inventory.
* Items HUD appears in Play Mode in lower-left corner showing doodad
sprites of all the items in the Player's inventory.
* Update code for recent changes in UI toolkit around event handlers for
buttons.
* Add tooltips to various buttons in the Editor Mode. The left toolbar
shows the names of each tool, the Doodad Palette shows the title of
each doodad and the Color Palette shows the swatch attributes (solid,
fire, water, etc.)
* Recent collision update caused a regression where the player would get
"stuck" while standing on top of a solid doodad, unable to walk left
or right.
* When deciding if the actor is on top of a doodad, use the doodad's
Hitbox (if available) instead of the bounding box. This fixes the
upside-down trapdoor acting solid when landed on from the top, since
its Hitbox Y coordinate is not the same as the top of its sprite.
* Cheats: when using the noclip cheat in Play Mode, you can hold down
the Shift key while moving to only move one pixel at a time.
* Fix the level collision bug that allowed clipping thru a ceiling while
climbing up a wall.
* Fix the scrolling behavior to keep the character on-screen no matter
how fast the character is moving, especially downwards.
* Increase player speed and gravity.
* New cheat: "ghost mode" disables clipping for the player character.
* Mark an actor as "grounded" if they fall and are stopped by the lower
level border, so they may jump again.
Add new doodads:
* Start Flag: place this in a level to set the spawn point of the player
character. If no flag is found, the player spawns at 0,0 in the top
corner of the map. Only use one Start Flag per level, otherwise the
player will randomly spawn at one of them.
* Crumbly Floor: a solid floor that begins to shake and then fall apart
after a moment when a mobile character steps on it. The floor respawns
after 5 seconds.
* State Blocks: blue and orange blocks that toggle between solid and
pass-thru whenever a State Button is activated.
* State Button: a solid "ON/OFF" block that toggles State Blocks back
and forth when touched. Only activates if touched on the side or bottom;
acts as a solid floor when walked on from the top.
New features for doodad scripts:
* Actor scripts: call SetMobile(true) to mark an actor as a mobile mob
(i.e. player character or enemy). Other doodads can check if the actor
colliding with them IsMobile so they don't activate if placed too close
to other (non-mobile) doodads in a level. The Blue and Red Azulians
are the only mobile characters so far.
* Message.Broadcast allows sending a pub/sub message out to ALL doodads
in the level, instead of only to linked doodads as Message.Publish does.
This is used for the State Blocks to globally communicate on/off status
without needing to link them all together manually.
* Touching "fire" pixels in a level will pop up the End Level alert box
saying you've died by fire and can restart the level.
* Update level.WriteFile() to prune broken links between actors before
save. So when a linked actor is deleted, the leftover link data is
cleaned up.
* Slight optimization in Canvas.drawStrokes: if either end of the stroke
is not within view of the screen, don't show the stroke.