Commit Graph

26 Commits

Author SHA1 Message Date
373028a166 Code cleanup
Clean up and improve the between-actors collision code:

* Give names to the A, B tuples from BetweenBoxes and call them the stable and
  mover doodads. All ops are from the perspective of the stable (A) box in
  relation to the mover (B).
* Replace the map[*Actor]*Actor collision struct to one that supports arrays of
  colliding actors, enabling one actor to post OnLeave events correctly when it
  overlapped several actors at once.
* Possibly improve on some of the collision bugs along the way.

Some issues remaining:

* If playing AS an offset-hitbox character:
    * Solid hitboxes against closed doors and platforms works OK.
    * Button InHitbox (overlap) checks do not fire (buttons won't press).
* If playing with a non-offset character (e.g. boy.doodad)
    * Solid hitboxes work and Buttons will press correctly.
* Both types of player hitboxes:
    * When touching a static object such as the Start Flag which has an offset
      hitbox of its own: the Start Flag will creep up the level on its Y axis
      when touched before it settles and won't move.
2024-07-07 22:45:21 -07:00
618d4b07c5 WIP: Fix actor collisions with offset hitboxes
When an actor's Hitbox doesn't begin at 0,0 the collision checks between
boxes is buggy. Current progress:

* Player is a 32x64 size sprite with a hitbox of 0,32 32x32 (bottom half)
* Landing onTop works
* Hitting onBottom works
* Bug: colliding from the side currently pushes the player 32px down
  into the floor. With non-offset doodads walking sideways into e.g. a
  locked door halts the X and Y movement until you let go, but offset
  doodads get pushed down mysteriously.
2024-05-28 22:36:53 -07:00
8216e5863b Tweak gravity and player physics 2024-02-06 19:04:47 -08:00
ec0b5ba6ca Rename Go module 2022-09-24 15:17:25 -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
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
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
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
be47dc21c7 Decouple gravity from player velocities 2021-06-02 22:18:25 -07:00
d14eaf7df2 Collision Box Updates
* The F4 key to draw collision boxes works reliably again: it draws the
  player's hitbox in world-space using the canvas.DrawStrokes()
  function, rather than in screen-space so it follows the player
  reliably.
* The F4 key also draws hitboxes for ALL other actors in the level:
  buttons, enemies, doors, etc.
* The level geometry collision function is updated to respect a doodad's
  declared Hitbox from their script, which may result in a smaller box
  than their raw Canvas size. The result is tighter collision between
  doodads, and Boy's sprite is rather narrow for its square Canvas so
  collision on rightward geometry is tighter for the player character.
* Collision checks between actors also respect the actor's declared
  hitboxes now, allowing for Boy to get even closer to a locked door
  before being blocked.
2021-06-02 20:50:28 -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
2bd420ff54 Fix getting stuck atop solid doodads
The platformer physics change introduced a regression where the player
character got "stuck" when standing on top of solid doodads.

Fixes #21
2020-04-04 21:21:11 -07:00
08e65c32b5 Overhaul the Platformer Physics System
* Player character now experiences acceleration and friction when
  walking around the map!
* Actor position and movement had to be converted from int's
  (render.Point) to float64's to support fine-grained acceleration
  steps.
* Added "physics" package and physics.Vector to be a float64 counterpart
  for render.Point. Vector is used for uix.Actor.Position() for the sake
  of movement math. Vector is flattened back to a render.Point for
  collision purposes, since the levels and hitboxes are pixel-bound.
* Refactor the uix.Actor to no longer extend the doodads.Drawing (so it
  can have a Position that's a Vector instead of a Point). This broke
  some code that expected `.Doodad` to directly reference the
  Drawing.Doodad: now you had to refer to it as `a.Drawing.Doodad` which
  was ugly. Added convenience method .Doodad() for a shortcut.
* Moved functions like GetBoundingRect() from doodads package to
  collision, where it uses its own slimmer Actor interface for just the
  relevant methods it needs.
2020-04-04 21:00:32 -07:00
0e3a30e633 Fix Actor Collision Checks Again
* Recent collision update caused a regression where the player would get
  "stuck" while standing on top of a solid doodad, unable to walk left
  or right.
* When deciding if the actor is on top of a doodad, use the doodad's
  Hitbox (if available) instead of the bounding box. This fixes the
  upside-down trapdoor acting solid when landed on from the top, since
  its Hitbox Y coordinate is not the same as the top of its sprite.
* Cheats: when using the noclip cheat in Play Mode, you can hold down
  the Shift key while moving to only move one pixel at a time.
2020-01-02 22:05:49 -08:00
a43e45fad0 Level Collision and Scrolling Fixes
* Fix the level collision bug that allowed clipping thru a ceiling while
  climbing up a wall.
* Fix the scrolling behavior to keep the character on-screen no matter
  how fast the character is moving, especially downwards.
* Increase player speed and gravity.
* New cheat: "ghost mode" disables clipping for the player character.
* Mark an actor as "grounded" if they fall and are stopped by the lower
  level border, so they may jump again.
2020-01-02 20:23:27 -08:00
7b3aec0fef Fix Two-State Blocks & Collision Detection
* Two-state Buttons now also subscribe to the state change message, so
  other on/off buttons in the same level update to match the state of
  the button that was hit.
* Add lock mutexes around the scripting engine to protect from
  concurrent event handlers.
2020-01-02 17:58:22 -08:00
8965a7d86a Doodads: Crumbly Floor, Start Flag & State Blocks
Add new doodads:

* Start Flag: place this in a level to set the spawn point of the player
  character. If no flag is found, the player spawns at 0,0 in the top
  corner of the map. Only use one Start Flag per level, otherwise the
  player will randomly spawn at one of them.
* Crumbly Floor: a solid floor that begins to shake and then fall apart
  after a moment when a mobile character steps on it. The floor respawns
  after 5 seconds.
* State Blocks: blue and orange blocks that toggle between solid and
  pass-thru whenever a State Button is activated.
* State Button: a solid "ON/OFF" block that toggles State Blocks back
  and forth when touched. Only activates if touched on the side or bottom;
  acts as a solid floor when walked on from the top.

New features for doodad scripts:

* Actor scripts: call SetMobile(true) to mark an actor as a mobile mob
  (i.e. player character or enemy). Other doodads can check if the actor
  colliding with them IsMobile so they don't activate if placed too close
  to other (non-mobile) doodads in a level. The Blue and Red Azulians
  are the only mobile characters so far.
* Message.Broadcast allows sending a pub/sub message out to ALL doodads
  in the level, instead of only to linked doodads as Message.Publish does.
  This is used for the State Blocks to globally communicate on/off status
  without needing to link them all together manually.
2019-12-30 18:13:28 -08:00
0437adfbf8 Change types int32 -> int per upstream render and ui library 2019-12-27 19:16:34 -08:00
ea0b41a781 Cut lib/render into its own package, change all imports 2019-12-22 18:21:58 -08:00
6af60f1128 Improve Collision Detection: More Active w/ Actors
* Improve the collision detection algorithm so that Actor OnCollide
  scripts get called more often WHILE an actor is moving, to prevent a
  fast-moving actor from zipping right through the "solid" hitbox and
  not giving the subject actor time to protest the movement.
* It's implemented by adding a `Settled` boolean to the OnCollide event
  object. When the game is testing out movement, Settled=false to give
  the actor a chance to say "I'm solid!" and have the moving party be
  stopped early.
* After all this is done, for any pair of actors still with overlapping
  hitboxes, OnCollide is called one last time with Settled=true. This is
  when the actor should run its actions (like publishing messages to
  other actors, changing state as in a trapdoor, etc.)
* The new collision detection algorithm works as follows:
  * Stage 1 is the same as before, all mobile actors are moved and
    tested against level geometry. They record their Original and New
    position during this phase.
  * Stage 2 is where we re-run that movement but ping actors being
    intersected each step of the way. We trace the steps between
    Original and New position, test OnCollide handler, and if it returns
    false we move the mobile actor to the Last Good Position along the
    trace.
  * Stage 3 we run the final OnCollide(Settled=true) to let actors run
    actions they wanted to for their collide handler, WITHOUT spamming
    those actions during Stage 2.
* This should now allow for tweaking of gravity speed and player speed
  without breaking all actor collision checking.
2019-07-16 21:07:38 -07:00
0c6c77a423 Lemon-shaped Ellipse Tool (WIP)
* Add initial Ellipse Tool to the Editor Mode. Currently there's
  something wrong with the algorithm and the ellipses have a sort of
  'lemon shape' to them.
* Refactor the IterLine/IterLine2 functions to be more consistent.
  IterLine used to be the raw algorithm that took a bunch of coordinate
  numbers and IterLine2 took two render.Point's and was the main one
  used throughout the app. Now, IterLine takes the two Points and the
  raw algorithm function removed.
2019-07-14 14:18:44 -07:00
cb02feff1d Add Switches, Fire/Water Collision and Play Menu
* New doodads: Switches.
  * They come in four varieties: wall switch (background element, with
    "ON/OFF" text) and three side-profile switches for the floor, left
    or right walls.
  * On collision with the player, they flip their state from "OFF" to
    "ON" or vice versa. If the player walks away and then collides
    again, the switch flips again.
  * Can be used to open/close Electric Doors when turned on/off. Their
    default state is "off"
  * If a switch receives a power signal from another linked switch, it
    sets its own state to match. So, two "on/off" switches that are
    connected to a door AND to each other will both flip on/off when one
    of them flips.
* Update the Level Collision logic to support Decoration, Fire and Water
  pixel collisions.
  * Previously, ALL pixels in the level were acting as though solid.
  * Non-solid pixels don't count for collision detection, but their
    attributes (fire and water) are collected and returned.
* Updated the MenuScene to support loading a map file in Play Mode
  instead of Edit Mode. Updated the title screen menu to add a button
  for playing levels instead of editing them.
* Wrote some documentation.
2019-07-06 18:30:03 -07:00
dc2695cfc9 Add More Trapdoor Doodads
* Add the other trapdoor directions: Left, Right and Up.
* UI: Show a color square in each Palette Swatch button in Edit Mode.
  * Instead of just the label like "solid", "fire", "decoration" it also
    shows a square box colored as the swatch color. The label and box
    are left-aligned in the button.
* Minor Play Mode physics update:
  * The player jump is now limited: they may only continue to move
    upwards for 20 ticks, after which they must touch ground before
    jumping again.
  * Remove the "press Down to move down" button. Only gravity moves you
    down.
* Fix a crash in the Editor Mode when you dragged doodads on top of each
  other. Source of bug was the loopActorCollision() function, which only
  should be useful to Play Mode, and it expected the scripting engine to
  be attached to the Canvas. In EditorMode there is no scripting engine.
2019-07-05 15:02:22 -07:00
4dd1bebc5f Add MenuScene with New Level UI
* Debug mode: no longer enables the DebugOverlay (F3) by default, but
  does now insert the current FPS counter into the window title bar.
* ui.Frame: set a default "mostly transparent" BG color so the frame
  background doesn't render as white.
* Add the MenuScene which will house the game's main menus.
* The "New Level" menu is first to be added.
  * UI lets you pick Page Type and Wallpaper using radio buttons.
  * Page Type: Unbounded, Bounded (default), No Negative Space, Bordered
  * Fix bugs in uix.Canvas to fully support all these page types.
2019-06-25 15:01:37 -07:00
1523deeb9c Return False: Solid Collision Between Actors
* Implement the handler code for `return false` when actors are
  colliding with each other and wish to act like solid walls.
* The locked doors will `return false` when they're closed and the
  colliding actor does not have the matching key.
* Add arbitrary key/value storage to Actors. The colored keys will set
  an actor value "key:%TITLE%" on the one who touched the key before
  destroying itself. The colored doors check that key when touched to
  decide whether to open.
* The trapdoor now only opens if you're touching it from the top (your
  overlap box Y value is 0), but if you touch it from below and the door
  is closed, it acts like a solid object.
2019-05-28 21:43:30 -07:00