Commit Graph

91 Commits

Author SHA1 Message Date
1f00af5741 Collision Detection Fix + Doodad CLI Fixes
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.
2024-05-27 15:14:00 -07:00
f35bc48c05 Code cleanup for RLE compression 2024-05-24 16:43:11 -07:00
c7a3c7a797 Remove never-used GridType accessor + documentation [PTO]
* Add documentation for the game's file formats and RLE encoding
* Remove the never-used GridType Chunk Accessor constant
2024-05-24 16:06:43 -07:00
6be2f86b58 RLE Encoding Code Cleanup [PTO]
* 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.
2024-05-24 15:03:32 -07:00
4851730ccf Fix RLE Encoding Off-by-One Errors [PTO]
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.
2024-05-24 13:54:41 -07:00
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
b1d7c7a384 WIP Run Length Encoding for Levels 2024-05-23 19:15:10 -07:00
a79601f983 D++ Default Author and Embedded Doodads Error
* 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.
2024-04-18 22:31:11 -07:00
a06787411d Resolve circular import errors for Doodle++ plugin
* 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.
2024-04-18 22:12:56 -07:00
7eb7f6148c WIP Doodle++ 2024-04-18 20:23:07 -07:00
6def8f7625 Walk up slopes smoothly, texture freeing improvement
* 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.
2024-02-07 22:14:48 -08:00
1a9706c09f Level Thumbnails on Story Mode Select
* 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.
2023-12-09 14:59:31 -08:00
9cce93f431 Dust off WASM build support 2023-12-08 21:52:34 -08:00
da83231559 Level Screenshots and Thumbnails
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.
2023-12-08 19:48:02 -08:00
ffb9068fb6 Unit test fixes and code cleanup 2023-12-02 12:33:14 -08:00
cf1bc81f25 Update savegame format, Allow out-of-bounds camera
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.
2023-03-07 21:55:10 -08:00
82884c79ae Signed Levels and Levelpacks
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).
2023-02-18 17:37:54 -08:00
856de848c9 Add cheat code to send power to all actors 2023-02-18 14:21:07 -08:00
03cd1d4ca0 Binary format for chunks in zipfiles 2023-02-18 12:45:36 -08:00
31097881ff Finalize Non-square Doodads
* 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.
2023-02-17 21:09:11 -08:00
ddcad27485 WIP: Chunker size to uint8 and Rectangular Doodads
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.
2023-02-16 21:47:18 -08:00
2dd6b5e34b Update default level palettes for new pixel attributes
* Default palette: adds "semisolid"
* Colored Pencil: adds "planks" (semisolid) and "ice" (slippery)
* Neon Bright: make "electric" semisolid and add "ice blue"
* Blueprint: make "electric" semisolid and add "ice" (slippery)
2022-10-10 11:17:11 -07:00
8b5dab6d6f Slippery Pixels + Update Changelog for 0.13.1 2022-10-10 10:52:28 -07:00
ecaa8c6cef SemiSolid Pixels + Icons
* Add new pixel attributes: SemiSolid and Slippery (the latter is WIP)
* SemiSolid pixels are only solid below the player character. You can walk on
  them and up and down SemiSolid slopes, but can freely pass through from the
  sides or jump through from below.
* Update the Palette Editor UI to replace the Attributes buttons: instead of
  text labels they now have smaller icons (w/ tooltips) for the Solid,
  SemiSolid, Fire, Water and Slippery attributes.
* Bugfix in Palette Editor: use cropped (24x24) images for the Tex buttons so
  that the large Bubbles texture stays within its designated space!
* uix.Actor.SetGrounded() to also set the Y velocity to zero when an actor
  becomes grounded. This fixes a minor bug where the player's Y velocity (due
  to gravity) was not updated while they were grounded, which may eventually
  become useful to allow them to jump down thru a SemiSolid floor. Warp Doors
  needed a fix to work around the bug, to set the player's Grounded(false) or
  else they would hover a few pixels above the ground at their destination,
  since Grounded status paused gravity calculations.
2022-10-09 21:39:43 -07:00
701073cecc Doodad/Actor Runtime Options
* 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.
2022-10-09 17:41:24 -07:00
546b5705db Detect touchscreen and tweak some behaviors 2022-09-24 17:45:54 -07:00
ec0b5ba6ca Rename Go module 2022-09-24 15:17:25 -07:00
450c6b3bb2 Spit and polish
* 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.
2022-05-07 17:42:38 -07:00
ffc2c6f69b Performance?: Don't unload chunks so eagerly
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.
2022-05-07 17:16:03 -07:00
94d0da78e7 Swimming Physics and Bubble Pattern
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.
2022-05-05 21:35:32 -07:00
9b75f1b039 Spit and polish
* 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.
2022-05-03 21:15:39 -07:00
75fa0c7e56 Stability and Bugfixes
* 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.
2022-05-02 20:35:53 -07:00
fc736abd5f Doodads: Gems, Snake and Crusher
Adds several new doodads to the game and 5 new wallpapers (parchment
paper in blue, green, red, white and yellow).

New doodads:

* Crusher: A purple block-headed mob wearing an iron helmet. It tries
  to crush the player when you get underneath. Its flat helmet can be
  ridden on like an elevator back up.
* Snake: A green stationary mob that always faces toward the player.
  If the player is nearby and jumps, the Snake will jump too and hope
  to catch the player in mid-air.
* Gems and Totems: A new key & lock collectible. Gems have quantity so
  you can collect multiple, and place them into matching Totems. A
  Totem gives off a power signal when its gem is placed and all other
  Totems it is linked to have also been activated. A single Totem may
  link to an Electric Door and require only one gem to open it, or it
  can link to other Totems and they all require gems before the power
  signal is sent out.
2022-05-01 15:18:23 -07:00
ad67e2b42b New Doodad: Blue Bird
* The blue bird follows the same base AI as the red bird (it has a
  target altitude that it tries to maintain, and it will dive at the
  player) but the blue bird flies in a sine wave pattern around its
  target altitude. It also has a longer scan radius to search for the
  player than the red bird.
* The sine wave pattern of the blue bird means you may fly under its
  radar depending how high it is on average.

Cheat codes that replace the player character are refactored to make
it easier to extend, and new cheats have been added:

* super azulian: play as the Red Azulian.
* hyper azulian: play as the White Azulian.
* bluebird: play as the new Bird (blue).
2022-04-30 17:59:55 -07:00
402b5efa7e Zipfiles for Attached Files Too
* 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.
2022-04-30 12:50:00 -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
c5353df211 LoadUnloadChunk for Memory Optimization
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.
2022-04-10 12:40:25 -07:00
db5760ee83 Optimize memory by freeing up SDL2 textures
* 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.
2022-04-09 14:41:24 -07:00
ba373553cb Prepare v0.12.0 for release 2022-03-27 14:23:25 -07:00
af6b8625d6 Flood Tool, Survival Mode for Azulian Tag
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.
2022-03-26 13:55:06 -07:00
647124495b Level Difficulty + UI Polish
Added a new level property: Difficulty

* An enum ranging from -1, 0, 1 (Peaceful, Normal, Hard)
* Default difficulty is Normal; pre-existing levels are Normal by
  default per the zero value.

Doodad scripts can read the difficulty via the new global variable
`Level.Difficulty` and some doodads have been updated:

* Azulians: on Peaceful they ignore all player characters, and on Hard
  they are in "hunt mode": infinite aggro radius and they're aggressive
  to all characters.
* Bird: on Peaceful they will not dive and attack any player character.

Other spit and polish:

* New Level/Level Properties UI reworked into a magicform.
* New "PromptPre(question, answer, func)" function for prompting the
  user with the developer shell, but pre-filling in an answer for them
  to either post or edit.
* magicform has a PromptUser field option for simple Text/Int fields
  which present as buttons, so magicform can prompt and update the
  variable itself.
* Don't show the _autosave.doodad in the Doodad Dropper window.
2022-03-06 22:20:53 -08:00
1cc6eee5c8 Refactor Level Publishing + MagicForm
* magicform is a helper package that may eventually be part of the go/ui
  library, for easily creating structured form layouts.
* The Level Publisher UI is the first to utilize magicform.

Refactor how level publishing works:

* Level data now stores SaveDoodads and SaveBuiltins (bools) and when
  the level editor saves the file, it will attach custom and/or builtin
  doodads just before save.
* Move the menu item from the File menu to Level->Publish
* The Publisher UI just shows the checkboxes to toggle the level
  settings and a convenient Save button along with descriptive text.
* Free versions get the "Register" window popping up if they click the
  Save Now button from within the publisher window.

Note: free versions can still toggle the booleans on/off but their game
will not attach any new doodads on save.

* Free games which open a level w/ embedded doodads will get a pop-up
  warning that the doodads aren't available.
* If they DON'T turn off the SaveDoodads option, they can still edit and
  save the level and keep the existing doodads attached.
* If they UNCHECK the option and save, all attached doodads are removed
  from the level.
2022-01-17 18:51:11 -08: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
1a8a5eb94b Polish and bugfixes
- 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
2021-10-09 20:45:38 -07:00
fb5a8a1ae8 Async Giant Screenshot, Player Physics and UI Polish
* 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
2021-10-07 18:27:38 -07:00
4469847c72 Giant Screenshot Feature
In the Level Editor, the "Level->Giant Screenshot" menu will take a full
scale PNG screenshot of the entire level, with its wallpaper and
doodads, and save it in ~/.config/doodle/screenshots.

It is currently CPU intensive and slow. With future work it should be
made asynchronous. The function is abstracted away nicely so that the
doodad CLI tool may support this as well.
2021-10-03 17:21:17 -07:00
0a8bce708e Actor Zoom + Experimental Settings GUI
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.
2021-09-11 21:18:22 -07:00
1ac85c9297 Checkpoint Flag & Retry from Checkpoint
* 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.
2021-08-15 20:17:53 -07:00
405aaf509d Link Start Flags to Change Characters
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.
2021-08-11 20:40:31 -07:00
d4e6d9babb Loading Screen
* 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.
2021-07-18 21:19:52 -07:00