Commit Graph

116 Commits

Author SHA1 Message Date
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
7d15651ff6 "Look At Me" for Doodad Scripts
* New script API method: Self.CameraFollowMe() to draw camera focus toward
  your doodad (it sets the Canvas.FollowActor target.)
* The camera will go back to following the player on any action inputs
  (arrow keys, jump, use, etc.); if the player is constantly on the move
  the camera stays on him even if another actor is trying to take the focus.
* The first few ticks of Play Mode the player character is always followed,
  to allow for Anvils to settle into place without taking the focus.
* Canvas FollowActor: if the actor is 4 times the max scroll speed away,
  allow scrolling in greater leaps of 4 times the max scroll speed.

New and Changed Doodads

* Anvils will take the camera focus while they are falling.
* New doodad: "Look At Me" - a 'camera region' technical doodad. Link it to
  any power source such as a Button - when this doodad receives power it will
  take the camera focus for a few frames. Use it to highlight a door that
  opened far off screen by linking the Button to both an Electric Door and
  a "Look At Me" near the door.
2022-09-24 23:54:51 -07:00
653184b8f8 JavaScript Exception Catcher UI
* Add an exception catcher that pops open a UI window showing errors that
  occur in doodad scripts during gameplay.
* Shows a preview of the header of the error (character wrapped) with a
  Copy button to copy the full raw text to clipboard for inspection.
* Buttons to dismiss the modal once or stop any further errors from
  opening during this gameplay session (until next restart).
* Add developer shell commands to test the exception catcher:
  - 'throw <message>' to throw a custom message.
  - 'throw2' to stress test a "long" message.
  - 'throw3' to throw a realistic message copied from an actual error.
* Scripting engine: console.log() and friends will now insert the script
  VM's name in front of its messages (the filename + actor ID).
2022-09-24 21:58:01 -07:00
ec0b5ba6ca Rename Go module 2022-09-24 15:17:25 -07:00
434416d3a4 Spit and polish
* Made the loadscreen useful again (give it work to do async so the game
  doesn't simply freeze during): does a first call to LoadUnloadChunks
  to preload the viewport chunks.
* Hide the mouse cursor when movement keys are pressed.
2022-05-07 18:54:37 -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
4efa8d00fc Fancy Mouse Cursors
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)
2022-05-04 22:38:26 -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
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
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
38a23f00b2 Reset Timer Doodad + Various Fixes
* Bird is not solid when colliding with other birds.
* If the dev shell is used to run JavaScript during Play Mode, consider
  it cheating (so player can't `$ d.Scene.ResetTimer()` for example)
* On Survival Mode levels, DieByFire immediately opens the End Level
  (silver score) modal rather than respawn from checkpoint, so levels
  don't need checkpoint contraptions to end the level.
* During level loading screens, wait and call doodads' main() function
  until the very end.
2022-03-27 11:51:14 -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
44122d4130 Spit and polish
UI improvements specifically for mobile (running the game with the
`-w mobile` or `-w landscape` options) screen sizes.

* Rework the Settings window to be mobile friendly to landscape
  oriented screens (`doodle -w landscape`) and migrate Options tab
  to magicform.
* The toolbar in the Editor will be a single column of buttons
  on small screens, such as `-w mobile` (375x812) portrait mode
  smartphone. On larger screens the toolbar shows in two columns
  of buttons.
* Fix tooltips not drawing on top.
* Centralize the hard-coded references to specific font filenames
* Add cheat code: `test load screen` to bring a sample loading screen up
  for a few seconds. It needs improvement on `-w landscape`
2022-03-05 22:44:54 -08:00
77297fd60d Text Tool and Pan Tool
Two new tools added to the Level Editor:

* Pan Tool: left-click to scroll the level around safely.
* Text Tool: write text onto your level.

Features of the Text Tool:

* Can choose from the game's built-in fonts, size and enter the message
  you want to write.
* The mouse cursor previews the text when hovered over the level.
* Click to "stamp" the text onto your level. The currently selected
  color swatch will be used to color the text in.
* Adds two new fonts: Azulian.ttf and Rive.ttf that can be selected in
  the Text Tool.

Some implementation notes:

* Added package native/engine_sdl.go that handles the lower-level
  SDL2_TTF logic to rasterize the text into a black&white image.
* WASM not supported yet (if the game even still built for WASM);
  native/engine_wasm.go stubs out the TextToImage() call with a "not
  supported" error just in case.

Other changes:

* New Toolbar icons: they are 24x24 instead of 32x32 to make more room
  for more tools.
* The toolbar now shows two buttons per row for a more densely packed
  layout. For very narrow screen widths (< 600px) the default Vertical
  Toolbar layout will use one-button-per-row to not eat too much screen
  real estate.
* In the Horizontal Toolbars layout there are 2 buttons per column.
2022-03-05 15:34:20 -08:00
1205dc2cd3 Invulnerable Anvil and other fixes
* Add methods `Invulnerable() bool` and `SetInvulnerable(bool)` to the
  Actor API accessible in JavaScript (e.g. `Self.SetInvulnerable(true)`)
* The Anvil is invulnerable - when played as, it can crush other mobs by
  jumping on them but is not defeated by those mobs at the same time.
* Anvils don't destroy invulnerable mobs, such as other Anvils.
* Bugfix: the Electric Door is considered to be opened from the first
  frame of animation when the door begins opening, and remains opened
  until the final frame of animation when it is closing.
* New cheat code: `megaton weight` to play as the Anvil by default.
2022-02-20 11:48:36 -08:00
4de0126b19 Game Controller Support
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.
2022-02-19 18:31:22 -08:00
626fd53a84 Checkpoint Flag can Re-assign Player Character
Link a Doodad to a Checkpoint Flag (like you would a Start Flag) and
crossing the flag will replace the player with that doodad. Multiple
checkpoint flags like this can toggle you between characters.

* Azulians are now friendly to player characters who have the word
  "Azulian" in their title.
* Improve Bird as the playable character:
  * Dive animation if the player flies diagonally downwards
  * Animation loop while hovering in the air instead of pausing
* Checkpoint flags don't spam each other on PubSub so much which could
  sometimes lead to deadlocks!

SetPlayerCharacter added to the JavaScript API. The Checkpoint Flag
(not the region) can link to a doodad and replace the player character
with that linked doodad when you activate the checkpoint:

    Actors.SetPlayerCharacter(filename string): like "boy.doodad"

Add various panic catchers to make JavaScript safer and log issues
to console.
2022-01-18 21:24:36 -08:00
9201475060 Update Doodad JS API + Hostile mobs
New functions are available on the JavaScript API for doodads:

* Actors.At(Point) []*Actor: returns actors intersecting a point
* Actors.FindPlayer() *Actor: returns the nearest player character
* Actors.New(filename string): create a new actor (NOT TESTED YET!)
* Self.Grounded() bool: query the grounded status of current actor

With this the game's built-in doodads have been revised:

* Bird: will now scan 240 pixels diagonally searching for the player
  character and will dive if seen. The Bird is dangerous while
  diving. It will return to its original altitude once it touches
  the ground.
* Azulians: the Azulians are now dangerous to player characters but
  not to the Thief. Azulians will begin to follow the player when
  they are within the aggro range and will hop if the player is
  above them to try and overcome obstacles.
  * Blue Azulian: aggro is (250, 100) jump speed 12 movement 2
  * Red Azulian: aggro is (250, 200) jump speed 14 movement 4
2022-01-17 21:28:05 -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
5ca87c752f Sort level actors deterministically by their (time sensitive) ID 2022-01-16 20:20:48 -08:00
4d08bf1d85 Switch JavaScript engine to goja
* Switch from otto to goja for JavaScript engine.
* goja supports many ES6 syntax features like arrow functions,
  const/let, for-of with more coming soon.
* Same great features as otto, more modern environment for doodads!
2022-01-16 20:09:27 -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
690fdedb91 Add the ui.ColorPicker 2022-01-01 18:48:34 -08:00
ddf0074099 Condensed Palette, Bird AI Update
* The Red Bird now records its original altitude on the level and will
  try and return there should it accidentally climb up or down a wall.
  Sometimes goes into a wavy pattern surrounding its original altitude.
* Editor UI: in the default (vertical) toolbar, the Palette now has a
  two column view to show more color choices on screen at once.
* User setting added: hide the touch control hints.
2021-10-12 20:49:48 -07:00
3a9cc83e78 Bugfix: Undo/Redo works for the Doodad Editor
Changed dependencies around so the undo/redo feature works on doodads as
well as levels.
2021-10-11 16:10:04 -07:00
0ec259b171 Crosshair Option + Doodad Editor crash fix
* 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.
2021-10-11 15:57:33 -07: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
d6acee5a66 Adjust Gravity and Prevent Moonwalking
* Tweak max gravity speed to match player max velocity.
* Boy's script watches for his velocity to flip suddenly and stops
  animations, limiting the moonwalking a bit.
* JS API: Self.GetVelocity() added.
2021-10-07 18:49:09 -07:00
a24c94a161 Multitouch Level Panning
Add multi-touch gesture support so that the player can scroll the level
in the editor (and title screen) by treating a two finger swipe to be
equivalent to a middle click drag.

Fun quirks found with SDL2's MultiGestureEvent:

* They don't begin sending us the event until motion is detected after
  two fingers have touched the screen; not the moment the second finger
  touches it.
* It spams us with events when it detects any tiny change and a lot of
  cool details like rotate/pinch deltas, but it never tells us when the
  multitouch STOPS! The game has to block left clicks while multitouch
  happens so the user doesn't draw all over their level, so it needs to
  know when touch has ended.
* The workaround is to track the mouse cursor position at the first
  touch and each delta thereafter; if the deltas stop changing tick to
  tick, unset the "is touching" variable.
2021-10-06 20:02:09 -07:00
c2c91e45a9 Middle-click to Pan + Remember Scroll Position
In the editor, clicking and dragging with the middle mouse button
will scroll the view of the editor in place of the arrow keys.

When entering Play Mode, the original scroll position in the level
editor is remembered for when you come back - no more having to
scroll from 0,0 each time to get back to where you were working!
2021-10-04 20:49:11 -07:00
97e179716c Add Technical Doodads + UI Fixes
New category for the Doodad Dropper: "Technical"

Technical doodads have a dashed outline and label for now, and they
turn invisible on level start, and are for hidden technical effects on
your level.

The doodads include:

* Goal Region: acts like an invisible Exit Flag (128x128), the level is
  won when the player character touches this region.
* Fire Region: acts like a death barrier (128x128), kills the player
  when a generic "You have died!" message.
* Power Source: on level start, acts like a switch and emits a
  power(true) signal to all linked doodads. Link it to your Electric
  Door for it to be open by default in your level!
* Stall Player (250ms): The player is paused for a moment the first time
  it touches this region. Useful to work around timing issues, e.g.
  help prevent the player from winning a race against another character.

There are some UI improvements to the Doodad Dropper window:

* If the first page of doodads is short, extra spacers are added so the
  alignment and size shows correctly.
* Added a 'background pattern' to the window: any unoccupied icon space
  has an inset rectangle slot.
* "Last pages" which are short still render weirdly without reserving
  the correct height in the TabFrame.

Doodad scripting engine updates:

* Self.Hide() and Self.Show() available.
* Subscribe to "broadcast:ready" to know when the level is ready, so you
  can safely Publish messages without deadlocks!
2021-10-02 20:52:16 -07:00
0a1d86e1f5 Bugfix: Scroll constraint favors top/left edge
For levels having a top/left scroll boundary, the top/left point takes
higher priority for resolving out-of-bounds scroll ranges instead of the
bottom/right.

This fixes a bug where you Zoom Out of a level far enough that the
entire boundaries of a Bounded level are smaller than the viewport into
the level. It could happen if playing normal levels in Play Mode on a
very high-resolution monitor. Previously, the level would anchor to the
bottom/right corner of your screen.

With the Zoom In/Out Feature this broke the ability to scroll well on
the level; so the easy fix is to put the X>0, Y>0 bounds check after the
above, so the level will hug the top/left corner of the screen which
fixes both problems.
2021-09-12 15:59:40 -07:00
21520e71e9 Zoom: Fix scrolling into negative coordinates
* If you open a wide unbounded level like Castle.level and zoom out and
  scroll left (into negative world coordinates), the level chunks
  display correctly now.
2021-09-12 15:47:16 -07:00
fd730483b0 Zoom Improvements
* Doodad outline while dragging is now sized properly for the zoom level
* Make doodad hitboxes for Actor/Link Tool more accurate while zoomed
* Fix chunks low on the level not loading while zoomed in
* Fix Link lines drawn between doodads while zoomed - they point to the
  correct position and their DrawLine calls have been optimized so they
  don't lag out the level when lots of them are drawn at once.
2021-09-12 15:27:37 -07:00
6f5bd910c8 Zoom Progress: Actor/Link Tool Hitboxes
* When the Actor Tool or Link Tool is active, mouse-over hitboxes on the
  level's actors now works correctly while zoomed and scrolling in the
  level.
* Regression: Level chunks don't appear outside a certain range from
  origin while zoomed in.
* Regression: Actors don't draw their sprite while zoomed in, but do
  when zoomed out.
2021-09-12 14:42:39 -07:00
731d142dd6 WIP Zoom hell 2021-09-11 22:30:45 -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
ecdfc46358 Zoom And Edit
Progress on the Zoom feature: when you zoom in and out, you can draw
shapes accurately onto the level. Seems a little buggy if you edit
while scrolling (as in drawing a very long line).

The title screen buttons are now more colorful.
2021-09-11 17:02:01 -07:00
7866f618da First-class Doodad Hitboxes + Generic Item Script
A new property is added to the Doodad struct: Hitbox (Rect).

The uix.Actor for Play Mode will defer to the Doodad.Hitbox until the
JavaScript has manually set its own via Self.SetHitbox(). So in effect,
scripts no longer need to worry about their hitbox! The one assigned to
the Doodad will be the default.

Scripts can check if their hitbox is zero before setting a default:

  if (Self.Hitbox().IsZero()) {
    var size = Self.Size()           // get doodad canvas size
    Self.SetHitbox(0, 0, size, size) // the full square
  }

The built-in generic doodad scripts have made this change, so that your
simple doodad can have a custom hitbox defined easily using in-game
tools.

Other changes:

* New script: Generic Collectible Item. Selecting it will add a
  "quantity" tag to your doodad, to easily configure the script.
* JavaScript API: "Self.Hitbox()" returns your doodad's current hitbox.
  You can check "Self.Hitbox.IsZero()" to check if it's empty.
2021-09-03 20:39:44 -07:00
7ea86b4ffc Generic Doodad Script Selection
In the Doodad Properties window, instead of browsing to select a .js
file to install your script, a SelectBox of built-in generic scripts are
available. These scripts implement simple behaviors and adapt to the
full canvas size of the doodad.

Built-in scripts so far include:

* generic-anvil.js: behaves just like the Anvil.
* generic-fire.js: the entire canvas hitbox acts like fire pixels,
  "burning" mobile doodads and failing the level for the player.
* generic-solid.js: the entire canvas hitbox acts solid
2021-09-02 22:33:28 -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
d7a96d1770 Thief and Inventory APIs
This commit adds the Thief character with starter graphics
(no animations).

The Thief walks back and forth and will steal items from other
doodads, including the player. For singleton items that have no
quantity, like the Colored Keys, the Thief will only steal one
if he does not already have it. Quantitied items like the
Small Key are always stolen.

Flexibility in the playable character is introduced: Boy,
Azulian, Bird, and Thief all respond to playable controls.
There is not currently a method to enable these apart from
modifying balance.PlayerCharacterDoodad at compile time.

New and Changed Doodads

* Thief: new doodad that walks back and forth and will steal
  items from other characters inventory.
* Bird: has no inventory and cannot pick up items, unless player
  controlled. Its hitbox has also been fixed so it collides with
  floors correctly - not something normally seen in the Bird.
* Boy: opts in to have inventory.
* Keys (all): only gives themselves to actors having inventories.

JavaScript API - New functions available

* Self.IsPlayer() - returns if the current actor IS the player.
* Self.SetInventory(bool) - doodads must opt-in to having an
  inventory. Keys should only give themselves to doodads having
  an inventory.
* Self.HasInventory() bool
* Self.AddItem(filename, qty)
* Self.RemoveItem(filename, qty)
* Self.HasItem(filename)
* Self.Inventory() - returns map[string]int
* Self.ClearInventory()
* Self.OnLeave(func(e)) now receives a CollideEvent as parameter
  instead of the useless actor ID. Notably, e.Actor is the
  leaving actor and e.Settled is always true.

Other Changes

* Play Mode: if playing as a character which doesn't obey gravity,
  such as the bird, antigravity controls are enabled by default.
  If you `import antigravity` you can turn gravity back on.
* Doodad collision scripts are no longer run in parallel
  goroutines. It made the Thief's job difficult trying to steal
  items in many threads simultaneously!
2021-08-09 22:42:22 -07:00
0518df226c New Doodad: Anvil
* The Anvil doodad is affected by gravity and becomes dangerous when
  falling. If it lands on the player character, you die! If it lands on
  any other mobile doodad, it destroys it! It can land on solid doodads
  such as the Electric Trapdoor and the Crumbly Floor. It will activate
  a Crumbly Floor if it lands on one, and can activate buttons and
  switches that it passes.
* JavaScript API: FailLevel(message) can be called from a doodad to kill
  the player character. The Anvil does this if it collides with the
  player while it's been falling.
2021-08-08 21:57:41 -07:00
810ba193d9 Doodads: Electric Trapdoor and Resettable Box
* New doodad: Electric Trapdoor. It is a horizontal version of the
  Electric Door. Opens while powered by a button or a switch and closes
  when it loses power.
* The Box doodad will reset to its original location if it receives a
  power signal from a linked Button or Switch. So for box pushing
  puzzles you can add a reset button in case the boxes get stuck.
* Refactored the Doodad build scripts into many Makefiles for easier
  iteration (don't need to compile ALL doodads to test one).

Updates to the JavaScript API for doodads:

* Self.MoveTo(Point) is now available to set the actor's position in
  world coordinates.
2021-08-08 20:10:42 -07:00