Add the ability for the free version of the game to allow loading levels that
use embedded custom doodads if those levels are signed.
* Uses the same signing keys as the JWT token for license registrations.
* Levels and Levelpacks can both be signed. So individual levels with embedded
doodads can work in free versions of the game.
* Levelpacks now support embedded doodads properly: the individual levels in
the pack don't need to embed a custom doodad, but if the doodad exists in
the levelpack's doodads/ folder it will load from there instead - for full
versions of the game OR when the levelpack is signed.
Signatures are computed by getting a listing of embedded assets inside the
zipfile (the assets/ folder in levels, and the doodads/ + levels/ folders
in levelpacks). Thus for individual signed levels, the level geometry and
metadata may be changed without breaking the signature but if custom doodads
are changed the signature will break.
The doodle-admin command adds subcommands to `sign-level` and `verify-level`
to manage signatures on levels and levelpacks.
When using the `doodad levelpack create` command, any custom doodads the
levels mention that are found in your profile directory get embedded into
the zipfile by default (with --doodads custom).
* Fix display bug with rectangular doodads scrolling off screen.
* The default Author of new files will be your registration name, if available
before using your $USER name.
Convert the Chunker size to a uint8 so chunk sizes are limited to 255px. This
means that inside of a chunk, uint8's can track the relative pixel coordinates
and result in a great memory savings since all of these uint8's are currently
64-bits wide apiece.
WIP on rectangular shaped doodads:
* You can create such a doodad in the editor and draw it normally.
* It doesn't draw the right size when dragged into your level however:
- In uix.Actor.Size() it gets a rect of the doodad's square Chunker size,
instead of getting the proper doodad.Size rect.
- If you give it the doodad.Size rect, it draws the Canvas size correctly
instead of a square - the full drawing appears and in gameplay its hitbox
(assuming the same large rectangle size) works correctly in-game.
- But, the doodad has scrolling issues when it gets to the top or left edge
of the screen! This old gnarly bug has come back. For some reason square
canvas doodads draw correctly but rectangular ones have the drawing scroll
just a bit - how far it scrolls is proportional to how big the doodad is,
with the Start Flag only scrolling a few pixels before it stops.
* Added a Cheats Menu UI accessible from the Settings window's "Experimental"
tab and from there you can enable the Cheats Menu from the "Help" screen of
the gameplay mode.
* Commonly used cheats all have corresponding buttons to click on, especially
helpful for touchscreen devices like the Pinephone where keyboard input
doesn't always work reliably.
* The buttons in the Cheats Menu just automate entry of the cheat commands.
* `boolProp` command has a new `flip` option to toggle their value (e.g.
`boolProp show-hidden-doodads flip`)
* New script API method: Self.CameraFollowMe() to draw camera focus toward
your doodad (it sets the Canvas.FollowActor target.)
* The camera will go back to following the player on any action inputs
(arrow keys, jump, use, etc.); if the player is constantly on the move
the camera stays on him even if another actor is trying to take the focus.
* The first few ticks of Play Mode the player character is always followed,
to allow for Anvils to settle into place without taking the focus.
* Canvas FollowActor: if the actor is 4 times the max scroll speed away,
allow scrolling in greater leaps of 4 times the max scroll speed.
New and Changed Doodads
* Anvils will take the camera focus while they are falling.
* New doodad: "Look At Me" - a 'camera region' technical doodad. Link it to
any power source such as a Button - when this doodad receives power it will
take the camera focus for a few frames. Use it to highlight a door that
opened far off screen by linking the Button to both an Electric Door and
a "Look At Me" near the door.
* Add an exception catcher that pops open a UI window showing errors that
occur in doodad scripts during gameplay.
* Shows a preview of the header of the error (character wrapped) with a
Copy button to copy the full raw text to clipboard for inspection.
* Buttons to dismiss the modal once or stop any further errors from
opening during this gameplay session (until next restart).
* Add developer shell commands to test the exception catcher:
- 'throw <message>' to throw a custom message.
- 'throw2' to stress test a "long" message.
- 'throw3' to throw a realistic message copied from an actual error.
* Scripting engine: console.log() and friends will now insert the script
VM's name in front of its messages (the filename + actor ID).
* Made the loadscreen useful again (give it work to do async so the game
doesn't simply freeze during): does a first call to LoadUnloadChunks
to preload the viewport chunks.
* Hide the mouse cursor when movement keys are pressed.
* On the failure (but still success) dialog on Survival Mode levels
(e.g. Azulian Tag): make the default be to retry the level but
show a "pity" Next Level button below, as the level is marked as
completed (silver score) and the next one is unlocked.
Previously: the Chunker tracks with chunks were gotten during the
current game tick and the N-1 and N-2 ticks, and chunks not accessed in
two ticks were freed immediately.
Now: they go into a "garbage collection" pool with a minimum number of
game ticks to free. So if they're needed again, they're saved from the
gc pool. F3 overlay data shows the count of the gc pool.
Water pixels finally do something other than turn your character blue!
* When the player character is "wet" (touching water pixels, and so appearing in
a blue mask), water physics apply: gravity is slower, your jump height is
halved, but you get infinite jumps to swim higher in the water.
* Holding the jump key under water will incur a short delay between jumps, so
that you don't just fly straight up to the surface. Tap the jump button to
move up quicker, you can spam it all you want.
Azulians are also able to handle being under water:
* They'll sink to the bottom and keep walking back and forth normally.
* If you are above them and noticed, they'll jump (swim) up towards you,
aware of the water and it jumps like you do.
* The Blue Azulian has the poorest vertical aggro range so it isn't a
very good swimmer. The White Azulian is very good at navigating water
as it can pursue the player from the furthest distance of them all.
Changes to the editor:
* New brush pattern added: bubbles.png
* It's the default pattern now for the "water" color of all
of the built-in palettes instead of ink.png
* A repeating pattern of bubbles carved out showing the
level wallpaper.
* The old "Bubbles (circles.png)" is renamed "Circles"
* The last scroll position is saved with the Level file, so when you reload
the level later it's scrolled at where you left it.
* New built-in wallpaper: "Dotted paper (dark)" is a dark-themed wallpaper.
* New built-in palette: "Neon Bright" with bright colors for dark levels.
* New cheat: "warp whistle" to automatically win the level.
* In case the user has a VERY LARGE screen resolution bigger than the full
bounds of a Bounded level, the Play Scene will cap the size and center
the level canvas onto the window. This is preferable to being able to see
beyond the level's boundaries and hitting an invisible wall in-game.
* Make the titlescreen Lazy Scroll work on unbounded levels. It can't bounce
off scroll boundaries but it will reverse course if it reaches the level's
furthest limits.
* Bugfix: characters' white eyes were transparent in-game. Multiple culprits
from the `doodad convert` tool defaulting the chroma key to white, to the
SDL2 textures considering white to be transparent. For the latter, the game
offsets the color by -1 blue.
* Editor: Auto-save on a background goroutine so you don't randomly freeze
the editor up during.
* Fix actor linking issues when you drag and re-place a linked doodad: the
level was too eagerly calling PruneLinks() whenever a doodad was 'destroyed'
(such as the one just picked up) breaking half of the link connection.
* Chunk unloader: do not unload a chunk that has been modified (Set or Delete
called on), keep them in memory until the next ZIP file save to flush them
out to disk.
* Link Tool: if you clicked an actor and don't want to connect a link, click
the first actor again to de-select it.
Updates to the `doodad` tool:
* `doodad edit-level --resize <int>` can re-chunk a level to use a different
chunk size than the default 128. Large chunk sizes 512+ lead to performance
problems.
* The level.FileSystem type has updated to support ZIP files too.
* Legacy levels loaded from gz/json have their old FileSystem as a
simple map[filename]data and this parses from JSON OK.
* On save to zip, the legacy loaded file data gets exported to ZIP.
* Going forward: newly added or deleted files during runtime are kept in
the legacy file map until the next save when the filemap is again
flushed out to ZIP.
* For regular read-access, the FileSystem reads from the ZIP file if the
data is not in the hot map (legacy file or recently modified
attachment).
* Bugfix: be sure to Inflate() the Level/Doodad after loading from
zipfile - it used to be that directly after a save, trying to play the
level failed because the Level.Actors struct was missing their IDs,
and similarly recently written chunks would error out (become black
voids) on levels/doodads so we Inflate() both after save/replacing
their zip handle.
Especially to further optimize memory for large levels, Levels and
Doodads can now read and write to a ZIP file format on disk with
chunks in external files within the zip.
Existing doodads and levels can still load as normal, and will be
converted into ZIP files on the next save:
* The Chunker.ChunkMap which used to hold ALL chunks in the main json/gz
file, now becomes the cache of "hot chunks" loaded from ZIP. If there is
a ZIP file, chunks not accessed recently are flushed from the ChunkMap
to save on memory.
* During save, the ChunkMap is flushed to ZIP along with any non-loaded
chunks from a previous zipfile. So legacy levels "just work" when
saving, and levels loaded FROM Zip will manage their ChunkMap hot
memory more carefully.
Memory savings observed on "Azulian Tag - Forest.level":
* Before: 1716 MB was loaded from the old level format into RAM along
with a slow load screen.
* After: only 243 MB memory was used by the game and it loaded with
a VERY FAST load screen.
Updates to the F3 Debug Overlay:
* "Chunks: 20 in 45 out 20 cached" shows the count of chunks inside the
viewport (having bitmaps and textures loaded) vs. chunks outside which
have their textures freed (but data kept), and the number of chunks
currently hot cached in the ChunkMap.
The `doodad` tool has new commands to "touch" your existing levels
and doodads, to upgrade them to the new format (or you can simply
open and re-save them in-game):
doodad edit-level --touch ./example.level
doodad edit-doodad --touch ./example.doodad
The output from that and `doodad show` should say "File format: zipfile"
in the headers section.
To do:
* File attachments should also go in as ZIP files, e.g. wallpapers
Instead of the loadscreen eager-loading ALL level chunks to Go Images, only
load the chunks within the "LoadingViewport" - which is the on-screen
Viewport plus a margin of chunks off the screen edges.
During gameplay, every few ticks, reevaluate which chunks are inside or
outside the LoadingViewport; for chunks outside, free their SDL2 textures
and free their cached bitmaps to keep overall memory usage down. The
AzulianTag-Forest level now stays under 200 Textures at any given time
and the loadscreen goes faster as it doesn't have to load every chunk's
images up front.
The LoadUnloadChunk feature can be turned on/off with feature flags. If
disabled the old behavior is restored: loadscreen loads all images and
the LoadUnloadChunks function is not run.
Other changes:
* loadscreen: do not free textures in the Hide() function as this runs on
a different goroutine and may break. The 4 wallpaper textures are OK
to keep in memory anyway, the loadscreen is reused often!
* Free more leaked textures: on the Inventory frame and when an actor
calls Self.Destroy()
* Stop leaking goroutines in the PubSub feature of the doodad script
engine; scripting.Supervisor.Teardown() sends a stop signal to all
scripts to clean up neatly. Canvas.Destroy() tears down its scripting
supervisor automatically.
* 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.