Commit Graph

13 Commits

Author SHA1 Message Date
5654145fd8 (Experimental) Run Length Encoding for Levels
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.
2024-05-23 23:02:01 -07:00
ec0b5ba6ca Rename Go module 2022-09-24 15:17:25 -07:00
93623e4e8a Zipfiles as File Format for Levels and Doodads
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
2022-04-29 20:34:59 -07:00
9e4f34864d Remove MsgPack, Fix doodad display on top/left edges
* Clean up unused msgpack code for levels and doodads
* Fix the cosmetic bug where actors in your level would display wrongly
  when scrolling off the top/left edges of the screen: they used to
  anchor at their own 0,0 coordinate and crop their width/height leading
  to a 'scrolling' effect that didn't happen on the right/bottom edges.
2022-01-09 13:16:29 -08:00
3486050702 Go 1.16 embed instead of go-bindata
* 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
2021-07-13 18:04:25 -07:00
3892087932 Doodads: Use Key and Working Warp Doors
* 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.
2021-01-03 15:19:21 -08:00
65a811db0d Auto-prune Empty Chunks in Level Files
* Discovered a bug where if you hit the Undo key to erase pixels and an
  entire chunk became empty by it, the chunk would have rendering errors
  and show as a solid black square instead of the level wallpaper
  showing through.
* Chunks that have no pixels in them are culled from the chunker
  immediately when you call a Delete() operation.
* The level file saver also calls a maintenance function to prune all
  empty chunks upon saving the file. So existing levels with broken
  chunks need only be re-saved to fix them.
2019-07-16 22:10:18 -07:00
6476a67faf Make Fire Deadly
* 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.
2019-07-06 20:31:50 -07:00
35a89e5dbe WASM: Store User Files in localStorage
* In WASM build, user levels and doodads are written to localStorage
  using their userdir path as keys (".config/levels/test.level")
* LoadFile() and WriteFile() for both Levels and Doodads interact with
  the localStorage for WASM build instead of filesystem for desktop.
* userdir.ListLevels() and ListDoodads() for WASM scan the localStorage
  keys for file names.
* userdir.ResolvePath() now works for WASM (previously was dummied out),
  checks for the file in localStorage.
2019-06-27 15:59:18 -07:00
b17ca34de2 Bindata: Embedding Doodads and Levels (for WASM)
* Use `go-bindata` to embed built-in doodads and levels directly into
  the Doodle binary. `make bindata` produces the bindata source file.
* Add `FromJSON()` method to Levels and Doodads to load objects from
  JSON strings in memory (for bindata built-ins or WASM ajax requests)
* Update file loading functions to check the embedded bindata files.
  * pkg/config.go#EditFile:
    * Supports editing a level from bindata (TODO: remove this support)
    * If the "assets/levels/%(simple-name.level)" exists in bindata,
      edits that drawing.
    * No such support for editing built-in doodads.
    * WASM has no filesystem access to edit files except built-in
      levels (yet)
  * pkg/doodads#ListDoodads:
    * Prepends built-in doodads from bindata to the returned list.
    * WASM: no filesystem access so gets only the built-ins.
  * pkg/doodads#LoadFile:
    * Checks built-in bindata store first for doodad files.
    * WASM: tries an HTTP request if not found in bindata but can go no
      further if not found (no filesystem access)
  * pkg/filesystem#FindFile:
    * This function finds a level/doodad by checking all the places.
    * If the level or doodad exists in bindata built-in, always returns
      its system path like "assets/doodads/test.doodad"
    * WASM: always returns the built-in candidate path even if not found
      in bindata so that ajax GET can be attempted.
  * pkg/level#ListSystemLevels:
    * New function that lists the system level files, similar to the
      equivalent doodads function.
    * Prepends the bindata built-in level files.
    * WASM: only returns the built-ins (no filesystem support)
    * Desktop: also lists and returns the assets/levels/ directory.
  * pkg/level#LoadFile:
    * Like the doodads.LoadFile, tries from built-in bindata first, then
      ajax request (WASM) before accessing the filesystem (desktop)
* Menu Scene: TODO, list the built-in levels in the Load Level menu.
  This feature will soon go away when WASM gets its own storage for user
  levels (localStorage instead of filesystem)
2019-06-27 15:07:34 -07:00
1150d6d3e9 Add Branding Module for Centralized Game Info
* Moves the game's Title, Summary and Version into pkg/branding where it
  is centrally controlled.
2019-06-23 17:52:48 -07:00
ac490473b3 Load Doodads from System Path as well as User Path 2019-05-06 13:35:08 -07:00
f76ba6fbb7 WIP: MsgPack stubs, Level Filesystem Module
* Add some encoding/decoding functions for binary msgpack format for
  levels and doodads. Currently it writes msgpack files that can be
  decoded and printed by Python (mp2json.py) but it can't re-read from
  the binary format. For now, levels will continue to write in JSON
  format.
* Add filesystem abstraction functions to the balance/ package to search
  multiple paths to find Levels and Doodads, to make way for
  system-level doodads.
2019-05-06 12:41:46 -07:00