* Added Feature Flag support, run doodle with --experimental to enable
all flags. Eraser Tool is behind a feature flag now.
* + and - on the top row of keyboard keys will zoom the drawing in and
out in Edit Mode. The wallpaper zooms nicely enough, but level
chunkers need work.
* A View menu is added with Zoom in/out, reset zoom, and scroll to
origin options. The whole menu is behind the Zoom feature flag.
* Update README with lots of details for fun debug mode options to play
around with.
* When editing a doodad in the Editor Mode, the toolbar has a "Lyr."
button that opens the Layers window.
* The Layers window allows switching the active doodad layer that you
are drawing on, as well as create and rename layers.
* With this feature, Doodads may be fully drawn in-game, including
adding alternate named layers for animations and multiple-state
doodads.
* Update the Pager component to have a configurable MaxPageButtons.
Controls that have more pages than this limit will stop having buttons
drawn after the limit. The "Forward" and "Next" buttons can still
navigate into the extra pages.
* Refactored and centralized the various popup windows in Editor Mode
into editor_ui_popups.go; the SetupPopups() and various methods such
as ShowPaletteWindow() and ShowDoodadDropper() make management of
popups simple for the editor_ui!
* The Menu Bar in Editor Mode now has context-specific tools in the
Tools menu: the Doodad Dropper for levels and Layers for doodads.
* Bugfix the Palette Editor window to work equally between Levels and
Doodads, by only having it care about the Palette and not the Level
that owns it.
* Adds global modal support in the pkg/modal/ package. It has easy
Alert() and Confirm() methods to prompt the user before calling a
callback function on affirmative response.
* Modals have global app state: they're processed in the main loop in
pkg/doodle.go similar to the global command shell.
* When a modal is active, a semitransparent black frame covers the
screen (gameplay loop paused, last game frame rendered below) and the
modal window appears on top.
* The developer console retains higher priority than the modal system
and always renders on top.
* Editor Mode: track when the level pixels have been modified, and
confirm the user about unsaved changes when they attempt to close the
level (New, Open, Close, etc.)
* Global: the Escape key no longer immediately shuts down the game, but
will confirm the user's intent via a modal.
* File->Quit in the Editor Mode also invokes the confirm shutdown modal.
* Start the program window maximized with the `-w maximized` CLI option.
* Move the Doodad Palette off the right-side dock of the Editor Scene and
into its own pop-up window: the DoodadDropper.
* Shrink the width of the Color Palette panel and show only the colors in
the buttons. The name of the swatch is available in the mouse-over tooltip.
* Added an "Edit" button to the Color Palette. It opens a Palette Editor
window where you can rename, change colors and attributes of existing colors
OR insert new colors into your palette. (Deleting colors not yet supported).
* level.Chunker gets a Redraw method: invalidates all cached textures of all
chunks forcing the level to redraw itself, possibly with an updated palette.
* Tightens up the surface area of API methods available to the
JavaScript VMs for doodads. Variables and functions are carefully
passed in one-by-one so the doodad script can only access intended
functions and not snoop on undocumented APIs.
* Wrote tons of user documentation for Doodad Scripts: documented the
full surface area of the exposed JavaScript API now that the surface
area is known and limited.
* Early WIP code for the Campaign JSON
* Take advantage of the new Window Manager feature of the UI toolkit.
* Move the MenuScene's "New Level" and "Play/Edit Level" windows into
stand-alone functions in new pkg/windows/ package. The 'windows'
package is isolated from the rest of Doodle and communicates using
config variables and callback functions to avoid circular dependency.
* MenuScene calls the window constructors from the new package.
* Add an "Options" button to the Menu Bar in the Editor Scene, which
opens the "New Level" window to allow changing the wallpaper or
bounding type of the level currently being edited.
* Move the cheat codes into their own file, cheats.go
* 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.
* Added an inventory system for actors as a replacement to the arbitrary
key/value data store. Colored keys now add themselves to the player's
inventory, and colored doors check the inventory.
* Inventory is a map[string]int between doodad filenames
(red-key.doodad) and quantity (0 for key items/unlimited qty).
* API methods to add and remove inventory.
* Items HUD appears in Play Mode in lower-left corner showing doodad
sprites of all the items in the Player's inventory.
* 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.
* 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.
* 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.
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.
* 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.
* 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.
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
* The `doodad` CLI tool got a lot of new commands:
* `doodad show` to verbosely print details about Levels and Doodads.
* `edit-level` and `edit-doodad` to update details about Levels and
Doodads, such as their Title, Author, page type and size, etc.
* Doodads gain a `Hidden bool` that hides them from the palette in
Editor Mode. The player character (Blue Azulian) is Hidden.
* Add some boolProps to the balance/ package and made a dynamic system
to easily configure these with the in-game dev console.
* Command: `boolProp list` returns available balance.boolProps
* `boolProp <name>` returns the current value.
* `boolProp <name> <true or false>` sets the value.
* The new boolProps are:
* showAllDoodads: enable Hidden doodads on the palette UI (NOTE:
reload the editor to take effect)
* writeLockOverride: edit files that are write locked anyway
* prettyJSON: pretty-format the JSON files saved by the game.
* Touching "fire" pixels in a level will pop up the End Level alert box
saying you've died by fire and can restart the level.
* Update level.WriteFile() to prune broken links between actors before
save. So when a linked actor is deleted, the leftover link data is
cleaned up.
* Slight optimization in Canvas.drawStrokes: if either end of the stroke
is not within view of the screen, don't show the stroke.
* 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.
* The game's tick counter was moved from Doodle.ticks to shmem.Tick
where it is more easily available from every corner of the code.
* Fix a bug in the Level Editor where dragging an already-existing actor
from one part of your map to another, would cause it to lose all its
data (especially its UUID), breaking links to other doodads. Now the
existing Actor catches a ride on the drag object to be reinserted
later.
* Animate the Link Line visualizers between actors. They now animate a
blinking color between magenta and grey-ish.
* 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.
* Add support for the LineTool and RectTool while in the EditorMode to
easily draw straight lines and rectangle outlines.
* Key bindings were added to toggle tools in lieu of a proper UI to
select the tool from a toolbar.
* "F" for Pencil (Freehand) Tool (since "P" is for "Playtest")
* "L" for Line Tool
* "R" for Rectangle Tool
* If the current edit tool is Actor or Link, use the new drawtool.Stroke
object to draw visual lines connecting every pair of linked actors in
the level. The lines are hidden during normal editing and gameplay and
only appear when you're possibly manipulating your actors and links.
* Add new pkg/drawtool with utilities to abstract away drawing actions
into Strokes and track undo/redo History for them.
* The freehand Pencil tool in EditorMode has been refactored to create a
Stroke of Shape=Freehand and queue up its world pixels there instead
of directly modifying the level chunker in real time. When the mouse
button is released, the freehand Stroke is committed to the level
chunker and added to the UndoHistory.
* UndoHistory is (temporarily) stored with the level.Level so it can
survive trips to PlayScene and back, but is not stored as JSON on
disk.
* Ctrl-Z and Ctrl-Y in EditorMode for undo and redo, respectively.
* Load SDL2 fonts from go-bindata storage so we don't have to ship
external font files on disk.
* Dedupe names of doodads so we don't show double on the front-end
(go-bindata bundled doodads + those on local filesystem)
* Use go-bindata for accessing wallpaper images.
* Better flashed messages walking you through the Link Tool.
* Stylize the title screen (MainScene) by rendering a live example level
as the background wallpaper, with mobile doodads in motion.
* Add RGBA color blending support in WASM build.
* Initial texture caching API for Canvas renderer engine. The WASM build
writes the chunk caches as a "data:image/png" base64 URL on the
browser's sessionStorage, for access to copy into the Canvas.
* Separated the ClickEvent from the MouseEvent (motion) in the WASM
event queue system, to allow clicking and dragging.
* Added the EscapeKey handler, which will abruptly terminate the WASM
application, same as it kills the window in the desktop build.
* Optimization fix: I discovered that if the user clicks and holds over
a single pixel when drawing a level, repeated Set() operations were
firing meaning multiple cache invalidations. Not noticeable on PC but
on WebAssembly it crippled the browser. Now if the cursor isn't moving
it doesn't do anything.
* Initial WebAssembly build target for Doodle in the wasm/ folder.
* Add a new render.Engine implementation, lib/render/canvas that uses
the HTML 5 Canvas API instead of SDL2 for the WebAssembly target.
* Ported the basic DrawLine(), DrawBox() etc. functions from SDL2 to
Canvas context2d API.
* Fonts are handled with CSS embedded fonts named after the font
filename and defined in wasm/index.html
* `make wasm` builds the WASM program, and `make wasm-serve` runs a dev
Go server that hosts the WASM file for development. The server also
watches the dev tree for *.go files and rebuilds the WASM binary
automatically on change.
* This build "basically" runs the game. UI and fonts all work and mouse
movements and clicks are detected. No wallpaper support yet or texture
caching (which will crash the game as soon as you click and draw a
pixel in your map!)
* 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.
* On the Doodads tab is the Link button to enter the Link Tool.
* Click Link, then click the 1st doodad on the level, then click the 2nd
doodad to complete the link.
* The actors struct in the Level holds the link IDs for each actor.
* 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.
* Events.OnCollide now receives a CollideEvent object, which makes
available the .Actor who collided and the .Overlap rect which is
zero-relative to the target actor. Doodad scripts can use the .Overlap
to see WHERE in their own box the other actor has intruded.
* Update the LockedDoor and ElectricDoor doodads to detect when the
player has entered their inner rect (since their doors are narrower
than their doodad size)
* Update the Button doodads to only press in when the player actually
touches them (because their sizes are shorter than their doodad
height)
* Update the Trapdoor to only trigger its animation when the board
along its top has been touched, not when the empty space below was
touched from the bottom.
* Events.OnLeave now implemented and fires when an actor who was
previously intersecting your doodad has left.
* The engine detects when an event JS callback returns false.
Eventually, the OnCollide can return false to signify the collision is
not accepted and the actor should be bumped away as if they hit solid
geometry.
* Add sync.WaitGroup to some parts of the level collision detection
function and Canvas.Loop() to speed up the frame rate by load
balancing some work in parallel across multiple cores.
* Improves FPS from 30 to 55+ even for busy scenes with lots of mobile
enemies walking around.
* Before the level collision optimization, framerate would sometimes dip
to 30 FPS simply to move the player character on a completely blank
map!
* Add a Red Azulian as a test for mobile enemies.
* Its A.I. has it walk back and forth, changing directions when it
comes up against an obstacle for a few moments.
* It plays walking animations and can trigger collision events with
other Doodads, such as the Electric Door and Trapdoor.
* Move Gravity responsibility to the doodad scripts themselves.
* Call `Self.SetGravity(true)` to opt the Doodad in to gravity.
* The canvas.Loop() adds gravity to any doodad that has it enabled.
* Add animation support for Doodad actors (Play Mode) into the core
engine, so that the Doodad script can register named animations and
play them without managing all the details themselves.
* Doodad API functions on Self: AddAnimation, PlayAnimation,
StopAnimation, IsAnimating
* CLI: the `doodad convert` command will name each layer after the
filename used as the input image.
* CLI: fix the `doodad convert` command creating duplicate Palette
colors when converting a series of input images into a Doodad.
* CLI: fix the `doodad convert` command to share the same Palette when
converting each frame (layer) of a doodad so subsequent layers find
the correct color swatches for serialization.
* Scripting: add timers and intervals to Doodad scripts to allow them to
animate themselves or add delayed callbacks. The timers have the same
API as a web browser: setTimeout(), setInterval(), clearTimeout(),
clearInterval().
* Add support for uix.Actor to change its currently rendered layer in
the level. For example a Button Doodad can set its image to Layer 1
(pressed) when touched by the player, and Trapdoors can cycle through
their layers to animate opening and closing.
* Usage from a Doodad script: Self.ShowLayer(1)
* Default Doodads: added scripts for all Buttons, Doors, Keys and the
Trapdoor to run their various animations when touched (in the case of
Keys, destroy themselves when touched, because there is no player
inventory yet)
* Add the JavaScript system for Doodads to run their scripts in levels,
and wire initial OnCollide() handler support.
* CLI: Add a `doodad install-script` command to the doodad tool.
* Usage: `doodad install-script <index.js> <filename.doodad>`
* Add dev-assets folder for storing source files for the official
default doodads, sprites, levels, etc. and for now add a JavaScript
for the first test doodad.
* Move all collision code into the pkg/collision package.
* pkg/doodads/collision.go -> pkg/collision/collide_level.go
* pkg/doodads/collide_actors.go for new Actor collide support
* Add initial collision detection code between actors in Play Mode.
Fixes:
* Move the call to CollidesWithGrid() inside the Canvas instead of
outside in the PlayScene.movePlayer() so it can apply to all Actors
in motion.
* PlayScene.movePlayer() in turn just sets the player's Velocity so the
Canvas.Loop() can move the actor itself.
* When keeping the player inside the level boundaries: previously it was
assuming the player Position was relative to the window, and was
checking the WorldIndexAt and getting wrong results.
* Canvas scrolling (loopFollowActor): check that the actor is getting
close to the screen edge using the Viewport into the world, NOT the
screen-relative coordinates of the Canvas bounding boxes.