Fix collision detection when an actor's hitbox is offset from 0,0:
* Actors having a hitbox that didn't begin at X,Y 0,0 used to experience
clipping issues with level geometry: because the game tracks their Position
(top left corner of their graphical sprite) and their target position wasn't
being correctly offset by their hitbox offset.
* To resolve the issue, an "ActorOffset" struct is added: you give it the
original game's Actor (with its offset hitbox) and it will record the offset
and give a mocked Actor for collision detection purposes: where the Position
and Target can be offset and where its Hitbox claims to begin at 0,0 matching
its offsetted Position.
* The translation between your original Actor and Offset Actor is handled at the
boundary of the CollidesWithGrid function, so the main algorithm didn't need
to be messed with and the game itself doesn't need to care about the offset.
Make some fixes to the doodad CLI tool:
* Fix palette colors being duplicated/doubled when converting from an image.
* The --palette flag in `doodad convert` now actually functions: so you can
supply an initial palette.json with colors and attributes to e.g. mark which
colors should be solid or fire and give them names. The palette.json doesn't
need to be comprehensive: it will be extended with new distinct colors as
needed during the conversion.
* For the doodad tool: skip the assets embed folder, the doodad binary doesn't
need to include all the game's doodads/levelpacks/etc. and can save on file
size.
* In `doodad resave`, .doodad files with Vacuum() and upgrade their chunker from
the MapAccessor to the RLEAccessor.
* Fix a rare concurrent map read/write error in OptimizeChunkerAccessors.
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.
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.
* 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.
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.
* Add "Options" support for Doodads: these allow for individual Actor instances
on your level to customize properties about the doodad. They're like "Tags"
except the player can customize them on a per-actor basis.
* Doodad Editor: you can specify the Options in the Doodad Properties window.
* Level Editor: when the Actor Tool is selected, on mouse-over of an actor,
clicking on the gear icon will open a new "Actor Properties" window which
shows metadata (title, author, ID, position) and an Options tab to configure
the actor's options.
Updates to the scripting API:
* Self.Options() returns a list of option names defined on the Doodad.
* Self.GetOption(name) returns the value for the named option, or nil if
neither the actor nor its doodad have the option defined. The return type
will be correctly a string, boolean or integer type.
Updates to the doodad command-line tool:
* `doodad show` will print the Options on a .doodad file and, when showing a
.level file with --actors, prints any customized Options with the actors.
* `doodad edit-doodad` adds a --option parameter to define options.
Options added to the game's built-in doodads:
* Warp Doors: "locked (exit only)" will make it so the door can not be opened
by the player, giving the "locked" message (as if it had no linked door),
but the player may still exit from the door if sent by another warp door.
* Electric Door & Electric Trapdoor: "opened" can make the door be opened by
default when the level begins instead of closed. A switch or a button that
removes power will close the door as normal.
* Colored Doors & Small Key Door: "unlocked" will make the door unlocked at
level start, not requiring a key to open it.
* Colored Keys & Small Key: "has gravity" will make the key subject to gravity
and set its Mobile flag so that if it falls onto a button, it will activate.
* Gemstones: they had gravity by default; you can now uncheck "has gravity" to
remove their Gravity and IsMobile status.
* Gemstone Totems: "has gemstone" will set the totem to its unlocked status by
default with the gemstone inserted. No power signal will be emitted; it is
cosmetic only.
* Fire Region: "name" can let you set a name for the fire region similarly to
names for fire pixels: "Watch out for ${name}!"
* Invisible Warp Door: "locked (exit only)" added as well.
The gamepad mouse cursor has become THE mouse cursor. It is always visible and your
real cursor is hidden, and this way the game can swap out other cursors for certain
scenarios:
* The Pencil Tool in the editor will use a pencil cursor over the level canvas.
* The Flood Tool has a custom Flood cursor so you don't forget it's selected!
Other improvements:
* The Palette buttons in the editor now render using their swatch's pattern
instead of only using its color.
* If you have an ultra HD monitor and open a Bounded level in the editor which
is too small to fill your screen, the editor canvas limits its size to fit
the level (preferable over showing parts of the level you can't actually play
as it's out of bounds).
* The "brush size" box is only drawn around the cursor when a relevant tool is
selected (Pencil, Line, Rect, Ellipse, Eraser)
* 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.
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
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.
The Default handler of the developer command shell now calls out to
RiveScript to match the user's message to a friendly reply. If
RiveScript returns NoReplyMatched then give the "command not found"
error.
* SDL2 builds of the game now set their app window icon.
* Create/Edit Level window is updated to show a tabbed UI to create a
new Level or a new Doodad. The dedicated main menu button to create a
new doodad (which immediately prompted for its size) is replaced by
this new tab's UI.
* Edit Drawing/Play Level window is more responsive to smaller screen
sizes by drawing fewer columns of filenames.
* Bugfix: the Alert and Confirm modals always re-center themselves on
screen, especially to adapt between Portrait or Landscape mode on a
mobile device.
Adds `doodad levelpack create` and `doodad levelpack show` commands to
the CLI tool to create levelpacks.
A levelpack is a ZIP file containing a descriptive index.json and
directories for levels and doodads.
* The level scroll logic was getting a null pointer crash if you open a
doodad rather than a level file.
* Add a crosshair option to the level editor, configurable in the Game
Settings window.
* 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.
Improvements to the Zoom feature:
* Actor position and size within your level scales up and down
appropriately. The canvas size of the actor is scaled and its canvas
is told the Zoom number of the parent so it will render its own
graphic scaled correctly too.
Other features:
* "Experimental" tab added to the Settings window as a UI version of the
--experimental CLI option. The option saves persistently to disk.
* The "Replace Palette" experimental feature now works better. Debating
whether it's a useful feature to even have.
* Migrate off go-bindata to embed built-in fonts, levels and doodads in
favor of Go 1.16 native embed functionality.
* `make bindata` prints a deprecation warning to not break older build
scripts
* Removes all references of bindata from the program
* 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.
* New command-line tool: doodle-admin for signing license keys for
users. Includes functions to initialize a keypair, sign license keys
and validate existing keys.
* The Main Menu screen shows a blue "Register Game" button in the bottom
right corner of the screen, for unregistered users only.
* In Edit Mode, there is a "Help -> Register" menu item that opens the
License Window.
* The License UI Window lets the user select the license.key file to
register the game with. If registered, a copy of the key is placed in
Doodle's profile directory and the licensee name/email is shown in the
License UI window.
* Unregistered games will show the word "(shareware)" next to the title
screen version number and Edit Mode status bar.
* No restrictions are yet placed on free versions of the game.
On small screen sizes like the Pinephone, the toolbars in the Level
Editor are best made horizontal across the top and bottom of the screen
leaving more room for the drawing.
Enable it with a boolProp for now, and then reopen the level editor:
boolProp horizontalToolbars true
When launching `doodle -w mobile` it will automatically enable this
option.
Adds a lazy scroll algorithm that basically:
- Zigzags right/down a certain distance, then up again
- Then enters a bounce phase where it bounces off the level
boundaries like a screensaver.
Arrow keys can still scroll the level manually, but the
automated scroll takes over otherwise.
* You can now browse for a custom wallpaper image to use with your
levels. A platform-native file picker dialog is used (no WASM support)
* In the New/Edit Level Properties dialog, the Wallpaper drop-down
includes an option to browse for a custom map.
* When editing an existing level: the wallpaper takes effect immediately
in your level once the file is picked. For NEW levels, the wallpaper
will appear once the "Continue" button is pressed.
* All common image types supported: png, jpeg, gif.
* The wallpaper is embedded in the level using the filepath
"assets/wallpapers/custom.b64img" as a Base64-encoded blob of the
image data.
* The `doodad show` command will list the names and sizes of files
embedded in levels. `doodad show --attachment <name>` will get an
attachment and print it to the console window.
* To extract a wallpaper image from a level:
`doodad show -a assets/wallpapers/custom.b64img | base64 -d > out.png`
* The --chdir CLI option to doodle will set a working directory for the
game to switch to on startup. Flatpak builds place the files at
/app/share/doodle where the ./rtp and ./guidebook files are relative
to and this allows the game to find its sound effects and such.
* Added Feature Flag support, run doodle with --experimental to enable
all flags. Eraser Tool is behind a feature flag now.
* + and - on the top row of keyboard keys will zoom the drawing in and
out in Edit Mode. The wallpaper zooms nicely enough, but level
chunkers need work.
* A View menu is added with Zoom in/out, reset zoom, and scroll to
origin options. The whole menu is behind the Zoom feature flag.
* Update README with lots of details for fun debug mode options to play
around with.