Levels can now be converted to RLE encoded chunk accessors and be re-saved
continuously without any loss of information.
Off-by-one errors resolved:
* The rle.NewGrid() was adding a +1 everywhere making the 2D grids have 129
elements to a side for a 128 chunk size.
* In rle.Decompress() the cursor value and translation to X,Y coordinates is
fixed to avoid a pixel going missing at the end of the first row (128,0)
* The abs.X-- hack in UnmarshalBinary is no longer needed to prevent the
chunks from scooting a pixel to the right on every save.
Doodad tool updates:
* Remove unused CLI flags in `doodad resave` (actors, chunks, script,
attachment, verbose) and add a `--output` flag to save to a different file
name to the original.
* Update `doodad show` to allow debugging of RLE compressed chunks:
* CLI flag `--chunk=1,2` to specify a single chunk coordinate to debug
* CLI flag `--visualize-rle` will Visualize() RLE compressed chunks in
their 2D grid form in your terminal window (VERY noisy for large
levels! Use the --chunk option to narrow to one chunk).
Bug fixes and misc changes:
* Chunk.Usage() to return a better percentage of chunk utilization.
* Chunker.ChunkFromZipfile() was split out into two functions:
* RawChunkFromZipfile retrieves the raw bytes of the chunk as well as the
file extension discovered (.bin or .json) so the caller can interpret
the bytes correctly.
* ChunkFromZipfile calls the former function and then depending on file
extension, unmarshals from binary or json.
* The Raw function enables the `doodad show` command to debug and visualize
the raw contents of the RLE compressed chunks.
* Updated the Visualize() function for the RLE encoder: instead of converting
palette indexes to hex (0-F) which would begin causing problems for palette
indexes above 16 (as they would use two+ characters), indexes are mapped to
a wider range of symbols (0-9A-Z) and roll over if you have more than 36
colors on your level. This at least keeps the Visualize() grid an easy to
read 128x128 characters in your terminal.
Finally add a second option for Chunk MapAccessor implementation besides the
MapAccessor. The RLEAccessor is basically a MapAccessor that will compress
your drawing with Run Length Encoding (RLE) in the on-disk format in the ZIP
file.
This slashes the file sizes of most levels:
* Shapeshifter: 21.8 MB -> 8.1 MB
* Jungle: 10.4 MB -> 4.1 MB
* Zoo: 2.8 MB -> 1.3 MB
Implementation details:
* The RLE binary format for Chunks is a stream of Uvarint pairs storing the
palette index number and the number of pixels to repeat it (along the Y,X
axis of the chunk).
* Null colors are represented by a Uvarint that decodes to 0xFFFF
or 65535 in decimal.
* Gameplay logic currently limits maps to 256 colors.
* The default for newly created chunks in-game will be RLE by default.
* Its in-memory representation is still a MapAccessor (a map of absolute
world coordinates to palette index).
* The game can still open and play legacy MapAccessor maps.
* On save in the editor, the game will upgrade/convert MapAccessor chunks over
to RLEAccessors, improving on your level's file size with a simple re-save.
Current Bugs
* On every re-save to RLE, one pixel is lost in the bottom-right corner of
each chunk. Each subsequent re-save loses one more pixel to the left, so what
starts as a single pixel per chunk slowly evolves into a horizontal line.
* Some pixels smear vertically as well.
* Off-by-negative-one errors when some chunks Iter() their pixels but compute
a relative coordinate of (-1,0)! Some mismatch between the stored world coords
of a pixel inside the chunk vs. the chunk's assigned coordinate by the Chunker:
certain combinations of chunk coord/abs coord.
To Do
* The `doodad touch` command should re-save existing levels to upgrade them.
* Mac app: exclude the redundant copy of rtp/ folder in the .dmg disk
image as it only needs to live inside the .app bundle.
* Fix the settings.json file to be initialized and saved to disk on
first launch of the game, and the crosshair color default.
* Fix the chdir detection in main.go especially to locate the rtp/
folder inside the macOS app bundle.
* Remove several unused functions in doodad.Drawing (velocity, acceleration,
grounded, etc.) - uix.Actor is where these are actually managed.
* In the JavaScript API, setTimeout() and setInterval() will translate the
milliseconds from wallclock time into a fixed number of game ticks to match
the target frame rate for better deterministic timing.
Updates the Self API to expose more uix.Actor fields:
* Doodad()
* GetBoundingRect()
* HasGravity()
* IsFrozen()
* IsMobile()
* LayerCount()
* ListItems()
* SetGrounded()
* SetWet()
* Velocity() - note: it was Self.GetVelocity() before.
Self.GetVelocity is deprecated and made an alias to Velocity.
Made some fixes to touchscreen control detection:
* TouchScreenMode is activated on the first SDL2 FingerDown
* TouchScreenMode deactivates after the last finger is removed, and a
mouse event happens at least 5 ticks later.
* Touchscreen mode used to be detected based on SDL2 GetNumTouchDevices
but on a Macbook, the trackpad registers as a touch device - worse,
GetNumTouchDevices will only start returning 1 the first time some
devices are touched.
* The result was that on macOS the custom mouse cursor was drawn by
default, but on the first trackpad touch, would disappear in favor of
assuming the game is running on a touch screen device (which is not
the case).
* New method: the render engine has an IsFingerDown boolean which will
be true as long as at least one finger has registered a FingerDown
event, but not yet a FingerUp event.
* So as long as one finger is down, the mouse cursor can disappear and
then it comes back on release. This isn't perfectly ideal for pure
touch devices (ideally the cursor remains hidden until a mouse
movement without touch occurs).
* Update native.DefaultAuthor to get the name registered from the user's JWT
license in a way that avoids cyclic dependency errors.
* When plus_dpp.go#GetRegistration succeeds, it updates DefaultAuthor to the
registered name. The main.go now gets and prints the registered owner to
ensure this is populated on startup.
* Return correct ErrRegisteredFeature error when the FOSS version fails
to load embedded doodads.
* pkg/plus/dpp is the main plugin bridge, and defines nothing but an interface
that defines the Doodle++ surface area (referring to internal game types such
as doodad.Doodad or level.Level), but not their implementations.
* dpp.Driver (an interface) is the main API that other parts of the game will
call, for example "dpp.Driver.IsLevelSigned()"
* plus_dpp.go and plus_foss.go provide the dpp.Driver implementation for their
build; with plus_dpp.go generally forwarding function calls directly to the
proprietary dpp package and plus_foss.go generally returning false/errors.
* The bootstrap package simply assigns the above stub function to dpp.Driver
* pkg/plus/bootstrap is a package directly imported by main (in the doodle and
doodad programs) and it works around circular dependency issues: this package
simply assigns dpp.Driver to the DPP or FOSS version.
Miscellaneous fixes:
* File->Open in the editor and PlayScene will use the new Open Level window
instead of loading the legacy GotoLoadMenu scene.
* Deprecated legacy scenes: d.GotoLoadMenu() and d.GotoPlayMenu().
* The doodle-admin program depends on the private dpp package, so can not be
compiled in FOSS mode.
* Fix collision detection to allow actors to walk up slopes smoothly, without
losing any horizontal velocity.
* Fix scrolling a level canvas so that chunks near the right or bottom edge
of the viewpoint were getting culled prematurely.
* Centralize JavaScript exception catching logic to attach Go and JS stack
traces where possible to be more useful for debugging.
* Performance: flush all SDL2 textures from memory between scene transitions
in the app. Also add a `flush-textures` dev console command to flush the
textures at any time - they all should regenerate if still needed based on
underlying go.Images which can be garbage collected.
* Rework the Story Mode UI to display level thumbnails.
* Responsive UI: defaults to wide screen mode and shows 3 levels horizontally
but on narrow/mobile display, shows 2 levels per page in portrait.
* Add "Tiny" screenshot size (224x126) to fit the Story Mode UI.
* Make the pager buttons bigger and more touchable.
* Maximize the game window on startup unless the -w option with a specific
window resolution is provided.
Adds some support for "less giant" level screenshots.
* In the Editor, the Level->Take Screenshot menu will render a cropped screen
shot of just the level viewport on screen. Note: it is not an SDL2 screen
copy but generated from scratch from the level data.
* In levels themselves, screenshots can be stored inside the level data in
three different sizes: large (1280x720), medium and small (each a halved
size of the previous).
* The first screenshot is created when the level is saved, starting from
wherever the scroll position in the editor is at, and recording the 720p
view of the level from there.
* The level screenshot can be previewed and updated in the Level Properties
window of the editor: so you can scroll the editor to just the right position
and take a good screenshot to represent your level.
* In the future: these embedded level screenshots will be displayed on the
Story Mode and other screens to see a preview of each level.
Other tweaks:
* When taking a Giant Screenshot: a confirm modal will warn the player that
it may take a while. And during the screenshot, show the new Wait Modal to
block player interaction until the screenshot has finished.
* Overhaul the clunky old alpha Edit Level/Doodad menu with a modernized
version featuring the new ListBox widget.
* The new level loader is a Window that can be spawned from anywhere instead
of on a dedicated MenuScene.
Updates to doodad scripts:
* Actor.IsOnScreen() checks whether an actor's visual sprite box is on-screen
in the level viewport. `Self.IsOnScreen()` will check for the current actor.
Other changes
* PlaySound() to deduplicate the same sound effect from playing at once.
Updates the savegame.json file format:
* Levels now have a UUID value assigned at first save.
* The savegame.json will now track level completion/score based on UUID,
making it robust to filename changes in either levels or levelpacks.
* The savegame file is auto-migrated on startup - for any levels not
found or have no UUID, no change is made, it's backwards compatible.
* Level Properties window adds an "Advanced" tab to show/re-roll UUID.
New JavaScript API for doodad scripts:
* `Actors.CameraFollowPlayer()` tells the camera to return focus to the
player character. Useful for "cutscene" doodads that freeze the player,
call `Self.CameraFollowMe()` and do a thing before unfreezing and sending the
camera back to the player. (Or it will follow them at their next directional
input control).
* `Self.MoveBy(Point(x, y int))` to move the current actor a bit.
New option for the `doodad` command-line tool:
* `doodad resave <.level or .doodad>` will load and re-save a drawing, to
migrate it to the newest file format versions.
Small tweaks:
* On bounded levels, allow the camera to still follow the player if the player
finds themselves WELL far out of bounds (40 pixels margin). So on bounded
levels you can create "interior rooms" out-of-bounds to Warp Door into.
* New wallpaper: "Atmosphere" has a black starscape pattern that fades into a
solid blue atmosphere.
* Camera strictly follows the player the first 20 ticks, not 60 of level start
* If player is frozen, directional inputs do not take the camera focus back.
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`)
* Add a Dockerfile to this repo for self-contained easy releases.
Run it from an x86_64 Linux host and it will produce 64-bit and
32-bit Linux (rpm, deb, AppImage, tar.gz) and Windows releases.
* The `make appimage` command is more self-sufficient: it will
download the appimagetool-x86_64.AppImage program for your $ARCH
for an easy no-dependencies run after you have run `make dist`
The newer goja caused problems calling RunCollide or RunKeypress on
doodad scripts - resulting in a broken player character and no collision
events running on doodad scripts. Investigate later.