* The Publisher is all hooked up. No native Save File dialogs yet, so
uses the dev shell Prompt() to ask for output filename.
* Custom-only or builtin doodads too can be stored in the level's file
data, at "assets/doodads/*.doodad"
* When loading the embedded level in the Editor: it gets its custom
doodads out of its file, and you can drag and drop them elsehwere,
link them, Play Mode can use them, etc. but they won't appear in the
Doodad Dropper if they are not installed in your local doodads
directory.
* Fleshed out serialization API for the Doodad files:
- LoadFromEmbeddable() looks to load a doodad from embeddable file
data in addition to the usual places.
- Serialize() returns the doodad in bytes, for easy access to embed
into level data.
- Deserialize() to parse and return from bytes.
* When loading a level that references doodads not found in its embedded
data or the filesystem: an Alert modal appears listing the missing
doodads. The rest of the level loads fine, but the actors referenced
by these doodads don't load.
* File->Publish Level in the Level Editor opens the Publish window,
where you can embed custom doodads into your level and export a
portable .level file you can share with others.
* Currently does not actually export a level file yet.
* The dialog lists all unique doodad names in use in your level, and
designates which are built-ins and which are custom (paginated).
* A checkbox would let the user embed built-in doodads into their level,
as well, locking it in to those versions and not using updated
versions from future game releases.
UI Improvements:
* Added styling for a "Primary" UI button, rendered in deep blue.
* Pop-up modals (Alert, Confirm) color their Ok button as Primary.
* The Enter key pressed during an Alert or Confirm modal will invoke its
default button and close the modal, corresponding to its Primary
button.
* The developer console is now opened with the tilde/grave key ` instead
of the Enter key, so that the Enter key is free to click through
modals.
* In the "Open/Edit Drawing" window, a "Browse..." button is added to
the level and doodad sections, spawning a native File Open dialog to
pick a .level or .doodad outside the config root.
Palette swatches gain a new property: Pattern.
Patterns are grayscale textures that the swatch color will sample
against when drawing pixels to the level, by taking the world coordinate
modulo a value inside the texture.
A few algorithms were tried (Screen, Overlay), this branch lands on one
that tries to cast the color from grayscale which comes out rather dark;
to get a patterned color to look black while still seeing the pattern,
the color needs to be as bright as #777 to get the effect.
Adds a lazy scroll algorithm that basically:
- Zigzags right/down a certain distance, then up again
- Then enters a bounce phase where it bounces off the level
boundaries like a screensaver.
Arrow keys can still scroll the level manually, but the
automated scroll takes over otherwise.
* You can now browse for a custom wallpaper image to use with your
levels. A platform-native file picker dialog is used (no WASM support)
* In the New/Edit Level Properties dialog, the Wallpaper drop-down
includes an option to browse for a custom map.
* When editing an existing level: the wallpaper takes effect immediately
in your level once the file is picked. For NEW levels, the wallpaper
will appear once the "Continue" button is pressed.
* All common image types supported: png, jpeg, gif.
* The wallpaper is embedded in the level using the filepath
"assets/wallpapers/custom.b64img" as a Base64-encoded blob of the
image data.
* The `doodad show` command will list the names and sizes of files
embedded in levels. `doodad show --attachment <name>` will get an
attachment and print it to the console window.
* To extract a wallpaper image from a level:
`doodad show -a assets/wallpapers/custom.b64img | base64 -d > out.png`
* Replace the radio buttons for Page Type and Wallpaper with the new
SelectBox widgets from the UI toolkit.
* Choice of default palette also switched from a MenuButton to
a SelectBox widget.
* Experimental "Browse..." option added to the Wallpaper drop-down when
run in --experimental mode; not yet functional.
* In the "New Level" dialog, a "Palette:" option shows a MenuButton
drop-down with options: Default, Colored Pencil, and Blueprint. These
control the set of colors the new level starts with.
* Actors can now walk up gentle inclines to the left as well as they can
to the right. The bug was introduced as a hack to prevent clipping
thru the left wall of a 90 degree corner, but that problem seems
resolved now.
* 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.
New doodad interactions:
* Sticky Buttons will emit a "sticky:down" event to linked doodads, with
a boolean value showing the Sticky Button's state.
* Normal Buttons will listen for "sticky:down" -- when a linked Sticky
Button is pressed, the normal Button presses in as well, and stays
pressed while the sticky:down signal is true.
* When the Sticky Button is released (e.g. because it received power
from another doodad), any linked buttons which were sticky:down
release as well.
* Switch doodads emit a new "switch:toggle" event JUST BEFORE sending
the "power" event. Sensitive Doodads can listen for switches in
particular this way.
* The Electric Door listens for switch:toggle; if a Switch is activated,
the Electric Door always flips its current state (open to close, or
vice versa) and ignores the immediately following power event. This
allows doors to toggle on/off regardless of sync with a Switch.
Other changes:
* When the player character dies by fire, instead of the message saying
"Watch out for fire!" it will use the name of the fire swatch that
hurt the player. This way levels could make it say "Watch out for
spikes!" or "lava" or whatever they want. The "Fire" attribute now
just means "instantly kills the player."
* Level Editor: You can now edit the Title and Author name of your level
in the Page Settings window.
* Bugfix: only the player character ends the game by dying in fire.
Other mobile doodads just turn dark but don't end the game.
* Increase the size of Trapdoor doodad sprites by 150% as they were a
bit small for the player character.
* Rename the game from "Project: Doodle" to "Sketchy Maze"
* The crumbly floor doodad was made 50% larger.
* New doodad: Small Key and Small Key Door. These work like the colored
doors and locks except each Small Key is consumed when it unlocks a
door. The door's appearance is of iron bars.
* The inventory HUD displays a small quantity label in the lower-right
corner of items that have a quantity, such as the Small Key. This is
done as a Canvas.CornerLabel string attribute on uix.Canvas.
* The "give all keys" cheat adds 99 Small Keys to your inventory.
* 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.
* The blue and orange ON/OFF state blocks have all been increased in
size to better match the player character (42x42 up from 33x33)
* Added a new mob: the Red Bird. It flies back and forth while
maintaining its altitude, similar to the Red Azulian. Planned AI
behavior is to divebomb the player when it gets close. Dive sprites
are included but not yet hooked up in JavaScript.
* Warp Doors! (WIP). They have a golden "W" on them and come in three
varieties: Brown, Blue and Orange. The blue and orange ones are
sensitive to the State Block and will become dotted outlines when
inactive (and can not be entered in this state). The door opens for
the player character, makes him disappear, then closes again. The plan
is it will then warp you to the location of a linked Warp Door
elsewhere on the level, but for now it will just make the player
re-appear after completing the Close Door animation.
* 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.
* pkg/keybinds holds central functions to check global keybinds, like
DebugOverlay (F3), Undo (Ctrl-Z), GotoPlay/GotoEdit (p/e), etc.
* The Tools menu in the editor mode lists out more options to select
various drawing tools (line, pencil, etc.) - and showing the hotkey
for each tool.
* 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.
* Added initial walking sprites for the player character, "Boy."
* Player doodad filename and title screen level are now configurable in
the balance/numbers.go package.
* Add sound effect and music support to Doodle.
* Fix WASM build to use the 'null' sound driver for now.
* Add a Settings button to the main menu; UI for it is WIP.
* 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.
* Integrate the new ui.MenuBar into the Editor Scene.
* File: New Level/Doodad, Save [as], Open, Close, Exit
* Edit: Undo, Redo, Level options
* Level: Playtest
* Tools: Debug overlay, Command shell
* Help: User Manual, About
* Add an About dialog accessible from the Help menu.
Adds support for sound effects in Doodle and configures some for various
doodads to start out with:
* Buttons and Switches: "Clicked down" and "clicked up" sounds.
* Colored Doors: an "unlocked" sound and a "door opened" sound.
* Electric Door: sci-fi sounds when opening and closing.
* Keys: sound effect for collecting keys.
JavaScript API for Doodads adds a global function `Sound.Play(filename)`
to play sounds. All sounds in the `rtp/sfx/` folder are pre-loaded on
startup for efficient use in the app. Otherwise sounds are lazy-loaded
on first playback.
* 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
There was a clipping bug where the player could sometimes clip thru a
left-side wall, if the left wall and floor made a 90 degree bend and the
player was holding the Left key while jumping slightly into the wall.
A band-aid that seems to work involved two steps:
1. When capping their leftward movement, add a "+ 1" to the cap.
2. At the start of the point loop, enforce the left cap like we do the
ceiling cap.
This seems to patch the problem, BUT it breaks the ability to walk up
slopes while moving left. Right-facing slopes can be climbed fine still.
Note: the original bug never was a problem against right walls, only
left ones, but the true root cause was not identified. See TODO comments
in collide_level.go.
* With the Window Manager update you can open the Level Settings window
while editing a level, to change its wallpaper or page type. But you
could "draw" in the level "through" the opened window. This bug is now
fixed: if the cursor is on top of a managed UI window, the Canvas loop
is not called.
* 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.
* Revamped the sprites for the four colored locked doors. They now have
a side-view profile perspective rather than a front view.
* Doors open facing the left or the right based on what direction the
colliding actor approached it from.
* Update code for recent changes in UI toolkit around event handlers for
buttons.
* Add tooltips to various buttons in the Editor Mode. The left toolbar
shows the names of each tool, the Doodad Palette shows the title of
each doodad and the Color Palette shows the swatch attributes (solid,
fire, water, etc.)
* The `doodad edit-doodad` command now allows setting custom key/value
tags in doodad files, for extra data storage useful to their scripts.
* Colored keys and doors now store a `color` tag with the appropriate
color so that their scripts don't have to parse their Title to find
that information.
* Trapdoors now store a `direction` tag to hold the direction the door
is facing.
* 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.
* New logger module supports js/wasm build by skipping the dependency on
ssh/terminal (which detected interactive consoles, not applicable to
JS). In WASM the logs go to the browser console and ANSI color codes
not needed.
* Discovered a bug where if you hit the Undo key to erase pixels and an
entire chunk became empty by it, the chunk would have rendering errors
and show as a solid black square instead of the level wallpaper
showing through.
* Chunks that have no pixels in them are culled from the chunker
immediately when you call a Delete() operation.
* The level file saver also calls a maintenance function to prune all
empty chunks upon saving the file. So existing levels with broken
chunks need only be re-saved to fix them.
* 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.
* Update the Makefile to choose MacOS friendly `date` formats.
* Build the Windows doodle.exe binary as a GUI application to skip the
console window.
* Added Mac OS build instructions.
* 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.
* Rudimentary scrolling shows a Left and Right button at the top of the
Doodad Palette if your window is deemed not tall enough to contain all
of the doodads.
* A "progress bar" is shown between the buttons indicating the
percentage of your scroll down the doodad list. When you're able to
see the final row of doodads, the progress bar is at 100%.
* Toolbar has icon buttons for the Pencil Tool, Line Tool, Rect Tool,
Actor Tool and Link Tool.
* Remove the tab buttons from the top of the Palette window. The palette
tab is now toggled between Swatches and Doodads by the tool selected
on the tool bar, instead of the tab buttons setting the tool.
* Remove the "Link Doodads" button from the Doodad Palette. The Link
Tool has its own dedicated toolbar button with the others.
* 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.
* Add a Level Exit doodad, which for now is a little blue flag on a pole
that reads "END"
* JavaScript API: global function EndLevel() will end the level. The
exit doodad calls this when touched by the player.
* Add a "Level Completed" alert box UI to PlayScene with dynamic button
layouts.
* The alert box pops up when a doodad calls EndLevel() and contains
action buttons what to do next.
* "Play Again" restarts the current level again.
* "Edit Level" if you came from the EditorScene; otherwise this button
is not visible.
* "Next Level" is a to-be-implemented button to advance in the single
player story mode. Only shows up when PlayScene.HasNext=true.
* "Exit to Menu" is always visible and closes out to the MainScene.
* 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.
* In WASM build, user levels and doodads are written to localStorage
using their userdir path as keys (".config/levels/test.level")
* LoadFile() and WriteFile() for both Levels and Doodads interact with
the localStorage for WASM build instead of filesystem for desktop.
* userdir.ListLevels() and ListDoodads() for WASM scan the localStorage
keys for file names.
* userdir.ResolvePath() now works for WASM (previously was dummied out),
checks for the file in localStorage.
* Use `go-bindata` to embed built-in doodads and levels directly into
the Doodle binary. `make bindata` produces the bindata source file.
* Add `FromJSON()` method to Levels and Doodads to load objects from
JSON strings in memory (for bindata built-ins or WASM ajax requests)
* Update file loading functions to check the embedded bindata files.
* pkg/config.go#EditFile:
* Supports editing a level from bindata (TODO: remove this support)
* If the "assets/levels/%(simple-name.level)" exists in bindata,
edits that drawing.
* No such support for editing built-in doodads.
* WASM has no filesystem access to edit files except built-in
levels (yet)
* pkg/doodads#ListDoodads:
* Prepends built-in doodads from bindata to the returned list.
* WASM: no filesystem access so gets only the built-ins.
* pkg/doodads#LoadFile:
* Checks built-in bindata store first for doodad files.
* WASM: tries an HTTP request if not found in bindata but can go no
further if not found (no filesystem access)
* pkg/filesystem#FindFile:
* This function finds a level/doodad by checking all the places.
* If the level or doodad exists in bindata built-in, always returns
its system path like "assets/doodads/test.doodad"
* WASM: always returns the built-in candidate path even if not found
in bindata so that ajax GET can be attempted.
* pkg/level#ListSystemLevels:
* New function that lists the system level files, similar to the
equivalent doodads function.
* Prepends the bindata built-in level files.
* WASM: only returns the built-ins (no filesystem support)
* Desktop: also lists and returns the assets/levels/ directory.
* pkg/level#LoadFile:
* Like the doodads.LoadFile, tries from built-in bindata first, then
ajax request (WASM) before accessing the filesystem (desktop)
* Menu Scene: TODO, list the built-in levels in the Load Level menu.
This feature will soon go away when WASM gets its own storage for user
levels (localStorage instead of filesystem)
Since SDL2 is using in-memory bitmaps the same as Canvas engine, the
function names of the render.Engine interface have been cleaned up:
* NewTexture(filename, image) -> StoreTexture(name, image)
Create a new cached texture with a given name.
* NewBitmap(filename) -> LoadTexture(name)
Recall a stored texture with a given name.
* level.Chunk.ToBitmap uses simpler names for the textures instead of
userdir.CacheFilename file-like paths.
* Refactor texture caching in render.Engine:
* New interface method: NewTexture(filename string, image.Image)
* WASM immediately encodes the image to PNG and generates a JavaScript
`Image()` object to load it with a data URI and keep it in memory.
* SDL2 saves the bitmap to disk as it did before.
* WASM: deprecate the sessionStorage for holding image data. Session
storage methods panic if called. The image data is directly kept in
Go memory as a js.Value holding an Image().
* Shared Memory workaround: the level.Chunk.ToBitmap() function is where
chunk textures get cached, but it had no access to the render.Engine
used in the game. The `pkg/shmem` package holds global pointers to
common structures like the CurrentRenderEngine as a work-around.
* Also shmem.Flash() so Doodle can make its d.Flash() function
globally available, any sub-package can now flash text to the screen
regardless of source code location.
* JavaScript API for Doodads now has a global Flash() function
available.
* WASM: Handle window resize so Doodle can recompute its dimensions
instead of scaling/shrinking the view.
* 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.
* Refactor the event system in the WASM render engine to serialize the
async JavaScript events into a channel, so that queued events are read
off serially in the main loop similar to SDL. This fixes keyboard
input issues, altho if you type really fast some input keys get lost.
* 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!)
* Instead of needing to press the "P" and "E" keys to toggle from edit
mode to play mode (and back again), respectively, the UI now draws a
"Play (P)" or "Edit (E)" button on the bottom right corner of the
level canvas. Clicking it will toggle the mode.
* Fix the EditorUI not showing the correct palette baked into the level
and only showing the default. This was tricky because the palette UI
can only be configured at setup time but not updated later.
* Add a new default palette for the Blueprint theme. Blueprint has a
dark background, so the palette colors should be bright. This palette
is chosen when you start a map with the blueprint wallpaper.
* Add a background Canvas to the MenuScene. In the "New Level" screen,
the background canvas will update to show the wallpaper settings
you've chosen as a preview of the level theme you're about to create.
* To the MenuScene add the "Load Drawing" window UI.
* Displays the user's Levels and Doodads using rows of buttons, 4
buttons per row. Clicking the button loads the EditorScene with that
filename.
* Free Version does not display the Doodads label or button on this
menu screen.
* 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.
* Implement the pub/sub message passing system that lets the JavaScript
VM of one actor (say, a Button) send messages to other linked actors
in the level (say, an Electric Door)
* Buttons now emit a "power(true)" message while pressed and
"power(false)" when released. Sticky Buttons do not release and so do
not send the power(false) message.
* Electric Doors listen for the "power" event and open or close
themselves based on the boolean value received.
* If a Sticky Button receives power and is currently pressed down, it
will pop back up (reset to "off" position) and notify its linked
actors that they have lost power too. So if a Sticky Button held an
Electric Door open, and another Button powers the Sticky Button, it
would pop back up and also close the Electric Door.
* 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.
* Add some encoding/decoding functions for binary msgpack format for
levels and doodads. Currently it writes msgpack files that can be
decoded and printed by Python (mp2json.py) but it can't re-read from
the binary format. For now, levels will continue to write in JSON
format.
* Add filesystem abstraction functions to the balance/ package to search
multiple paths to find Levels and Doodads, to make way for
system-level doodads.
* Build the app with -tags="shareware" to compile the free/shareware
build of the game.
* `make build-free` compiles both binaries to the bin/ folder in
shareware mode.
* The constant balance.FreeVersion is true in the shareware build and
all functionality related to the Doodad Editor UI mode is disabled
in this build mode.
* 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)
* Improve the `doodad convert` command to convert a series of input
images into multiple Frames of a Doodad:
`doodad convert frame1.png frame2.png frameN.png output.doodad`
* Add the initial round of dev-asset sprites for the default Doodads:
* Button, Button-TypeB and Sticky Button
* Red, Blue, Green and Yellow Locked Doors and Keys
* Electric Door
* Trapdoor Down
* Add dev-assets/palette.json that defines our default doodad color
palette. Eventually the JSON will be used by the `doodad` tool to give
the layers meaningful names.
The "Save As" and "Open" buttons still used an old implementation that
assumed you were talking about files only in the "./maps" folder instead
of loading them from the user's levels and doodads folders.
Now all of the menu buttons in Edit Mode intelligently open files the
same as the `edit <filename>` command from the dev console. You can omit
the file extension and it will attempt to load a Level before a Doodad
to find the first named file, or provide the full extension to be
specific.
* 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.
* Scenes can insert custom key/value labels to the debug overlay and
track string variables in real time
* Added ability to unthrottle FPS in main loop
* Added Windows build instructions to Building.md and added a
"make mingw" command to cross-compile the Windows binary into
the bin/ folder.
* Fix a bug in the Wallpaper texture loader where it would error out
when caching textures to disk the first time.
Implement the Wallpaper system into the levels and the concept of
Bounded and Unbounded levels.
The first wallpaper image is notepad.png which looks like standard ruled
notebook paper. On bounded levels, the top/left edges of the page look
as you would expect and the blue lines tile indefinitely in the positive
directions. On unbounded levels, you only get the repeating blue lines
but not the edge pieces.
A wallpaper is just a rectangular image file. The image is divided into
four equal quadrants to be the Corner, Top, Left and Repeat textures for
the wallpaper. The Repeat texture is ALWAYS used and fills all the empty
space behind the drawing. (Doodads draw with blank canvases as before
because only levels have wallpapers!)
Levels have four options of a "Page Type":
- Unbounded (default, infinite space)
- NoNegativeSpace (has a top left edge but can grow infinitely)
- Bounded (has a top left edge and bounded size)
- Bordered (bounded with bordered texture; NOT IMPLEMENTED!)
The scrollable viewport of a Canvas will respect the wallpaper and page
type settings of a Level loaded into it. That is, if the level has a top
left edge (not Unbounded) you can NOT scroll to see negative coordinates
below (0,0) -- and if the level has a max dimension set, you can't
scroll to see pixels outside those dimensions.
The Canvas property NoLimitScroll=true will override the scroll locking
and let you see outside the bounds, for debugging.
- Default map settings for New Level are now:
- Page Type: NoNegativeSpace
- Wallpaper: notepad.png (default)
- MaxWidth: 2550 (8.5" * 300 ppi)
- MaxHeight: 3300 ( 11" * 300 ppi)
Add the ability to drag and drop Doodads onto the level. The Doodad
buttons on the palette now trigger a Drag/Drop behavior when clicked,
and a "blueprint colored" version of the Doodad follows your cursor,
centered on it.
Actors are assigned a random UUID ID when they are placed into a level.
The Canvas gained a MaskColor property that forces all pixels in the
drawing to render as the same color. This is a visual-only effect, and
is used when dragging Doodads in so they render as "blueprints" instead
of their actual colors until they are dropped.
Fix the chunk bitmap cache system so it saves in the $XDG_CACHE_FOLDER
instead of /tmp and has better names. They go into
`~/.config/doodle/chunks/` and have UUID file names -- but they
disappear quickly! As soon as they are cached into SDL2 they are removed
from disk.
Other changes:
- UI: Add Hovering() method that returns the widgets that are beneath
a point (your cursor) and those that are not, for easy querying
for event propagation.
- UI: Add ability to return an ErrStopPropagation to tell the master
Scene (outside the UI) not to continue sending events to other
parts of the code, so that you don't draw pixels during a drag
event.
Add the JSON format for embedding Actors (Doodad instances) inside of a
Level. I made a test map that manually inserted a couple of actors.
Actors are given to the Canvas responsible for the Level via the
function `InstallActors()`. So it means you'll call LoadLevel and then
InstallActors to hook everything up.
The Canvas creates sub-Canvas widgets from each Actor.
After drawing the main level geometry from the Canvas.Chunker, it calls
the drawActors() function which does the same but for Actors.
Levels keep a global map of all Actors that exist. For any Actors that
are visible within the Viewport, their sub-Canvas widgets are presented
appropriately on top of the parent Canvas. In case their sub-Canvas
overlaps the parent's boundaries, their sub-Canvas is resized and moved
appropriately.
- Allow the MainWindow to be resized at run time, and the UI
recalculates its sizing and position.
- Made the in-game Shell properties editable via environment variables.
The kirsle.env file sets a blue and pink color scheme.
- Begin the ground work for Levels and Doodads to embed files inside
their data via the level.FileSystem type.
- UI: Labels can now contain line break characters. It will
appropriately render multiple lines of render.Text and take into
account the proper BoxSize to contain them all.
- Add environment variable DOODLE_DEBUG_ALL=true that will turn on ALL
debug overlay and visualization options.
- Add debug overlay to "tag" each Canvas widget with some of its
details, like its Name and World Position. Can be enabled with the
environment variable DEBUG_CANVAS_LABEL=true
- Improved the FPS debug overlay to show in labeled columns and multiple
colors, with easy ability to add new data points to it.