Commit Graph

394 Commits

Author SHA1 Message Date
0a8bce708e Actor Zoom + Experimental Settings GUI
Improvements to the Zoom feature:
* Actor position and size within your level scales up and down
  appropriately. The canvas size of the actor is scaled and its canvas
  is told the Zoom number of the parent so it will render its own
  graphic scaled correctly too.

Other features:
* "Experimental" tab added to the Settings window as a UI version of the
  --experimental CLI option. The option saves persistently to disk.
* The "Replace Palette" experimental feature now works better. Debating
  whether it's a useful feature to even have.
2021-09-11 21:18:22 -07:00
ecdfc46358 Zoom And Edit
Progress on the Zoom feature: when you zoom in and out, you can draw
shapes accurately onto the level. Seems a little buggy if you edit
while scrolling (as in drawing a very long line).

The title screen buttons are now more colorful.
2021-09-11 17:02:01 -07:00
13ae66e1fa Bash for Makefiles 2021-09-06 14:06:55 -07:00
4d3e336ca1 Update release date for v0.8.0 2021-09-04 12:44:10 -07:00
449a30dc2c Small typo fix 2021-09-03 21:45:48 -07:00
f446ed9130 Prepare v0.8.0 for release 2021-09-03 21:35:12 -07:00
7866f618da First-class Doodad Hitboxes + Generic Item Script
A new property is added to the Doodad struct: Hitbox (Rect).

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

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

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

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

Other changes:

* New script: Generic Collectible Item. Selecting it will add a
  "quantity" tag to your doodad, to easily configure the script.
* JavaScript API: "Self.Hitbox()" returns your doodad's current hitbox.
  You can check "Self.Hitbox.IsZero()" to check if it's empty.
2021-09-03 20:39:44 -07:00
c499a15c71 Animations for Thief + No More Moonwalking
* Adds walking animations for the Thief.
* Mobile doodads no longer moonwalk: their A.I. used to wait for the
  animation to finish before setting the appropriate animation, so when
  it changed directions it would "moonwalk" for a time. Their A.I. is
  now updated to cancel the animation if they change directions so to
  immediately play the correct animation.
2021-09-03 19:54:10 -07:00
7ea86b4ffc Generic Doodad Script Selection
In the Doodad Properties window, instead of browsing to select a .js
file to install your script, a SelectBox of built-in generic scripts are
available. These scripts implement simple behaviors and adapt to the
full canvas size of the doodad.

Built-in scripts so far include:

* generic-anvil.js: behaves just like the Anvil.
* generic-fire.js: the entire canvas hitbox acts like fire pixels,
  "burning" mobile doodads and failing the level for the player.
* generic-solid.js: the entire canvas hitbox acts solid
2021-09-02 22:33:28 -07:00
0fa1bf8a76 Editor: Doodad Properties Window
The Doodad Properties window brings many features that used to be
available only in the `doodad` CLI tool into the Doodad Editor.

* In the Doodad Editor there is a new menubar item: "Doodad" which
  corresponds to the "Level" menu when you're editing a level.
* The "Doodad" menu has two items:
  - "Doodad Properties" (NEW)
  - "Layers" (moved here from the Tools menu)
* The Doodad Properties window lets you edit the Title and Author values
  of the doodad, as well as modify its Tags and manage its Script.
* Its script can be attached (browse for .js file on disk), its existing
  script saved back to disk (dev shell prompt) or deleted altogether
  from the doodad.
* You can create, modify, and delete Tags on the doodad.

Other changes:

* In the Level Editor, the "Level->Page Settings" menu is renamed to
  "Level->Level Properties" to match with "Doodad->Doodad Properties"
  and the pop-up window is retitled accordingly.
* The Exit Flag only exits if the Player touches it - not just any
  mobile doodad!
2021-09-02 21:26:55 -07:00
0cc1d17f4f Sort levels and doodads in the Open menu 2021-08-15 20:27:05 -07:00
1ac85c9297 Checkpoint Flag & Retry from Checkpoint
* New Doodad: Checkpoint Flag. They update the player's spawn point
  whenever the player passes one. The most recently activated
  checkpoint is rendered brighter than the others.
* End Level Modal: the fake alert box window drawn by the Play Mode
  is replaced with a fancy modal widget (similar to Alert and Confirm).
  It handles level victory or failure conditions and can show or hide
  all the buttons as needed.
* Gameplay: There is a "Retry from Checkpoint" option added, which
  appears in the level failure modal. It will teleport you back to
  the Start Flag or the last Checkpoint Flag you had touched, without
  resetting the level -- your keys, unlocked doors, etc. will be
  preserved so you can retry.
* Set a maximum speed on the "Camera Follows Actor" logic of 64
  pixels per tick. This results in a smoother scrolling transition
  when the player jumps to a new location on the map, such as by
  a Warp Door.
* Update the default color palettes:
    * All: Add a "hint" magenta color.
    * Colored Pencil: Add a "darkstone" solid color.

Updates to the Doodads JavaScript API:

* SetCheckpoint(Point(x, y)): set the player character's spawn
  position. Giving it Self.Position() is an easy way to set the
  player spawn to your doodad's location.
2021-08-15 20:17:53 -07:00
43f8e3d9b2 Thief: Ability to Steal as Player Character
* The Thief's ability is now available to the player character in levels
  where you'll play as the Thief.
* The Thief is able to steal items from all characters it contacts,
  including Azulians and other Thieves.
* A.I. Thieves will not steal items from each other, to prevent loops.
  Only a Thief controlled by the player will steal items from a Thief.
2021-08-15 17:33:05 -07:00
0bf5045a53 Death Barrier and Player Character Cheat Codes
* Added the Death Barrier to Play Mode to catch players from falling off
  the map and then falling indefinitely, especially on Unbounded maps.
* The Death Barrier is set 1,000 pixels below the lowest point on your
  map. If the player falls here they get a death message: "Watch out for
  falling off the map!"
* Added cheat codes to change the default Player Character doodad, as a
  way to force play as a different character (for levels which don't
  specify a custom character):
  * Play as Bird: fly like a bird
  * Play as Blue Azulian: the cell
  * Play as Thief: play as thief
  * Reset to default (Boy): pinocchio
2021-08-15 17:01:18 -07:00
405aaf509d Link Start Flags to Change Characters
New feature: link a Start Flag to another doodad in your level
and you will play as that doodad instead of Boy. All Creatures
are designed to be playable. Playing as "other" doodads leads
to interesting effects, like not being able to activate buttons,
switches, or warp doors and not having an inventory to pick up
keys. The Anvil is fun: it can destroy other mobile doodads by
jumping on them.

If the actor does not specify that it has gravity, the gameplay
starts in antigravity mode. This will be the vast majority of
non-mobile doodads and the Bird.

Other changes:

* The Blue and Red Azulians now share a doodad script.
* The Azulians AI is still to walk back and forth, pickup keys and
  press buttons. The Blue Azulian walks slower than the red one.
* The Blue Azulian is no longer hidden from the doodads list.
* Actor UUID values in levels are now V1 UUIDs (time-ordered).
  This will help to reliably resolve conflicts in draw order
  of overlapping doodads (newest added to level wins).
* Link Tool: clicking on a pair of already-linked doodads will
  now unlink them, so you don't have to delete one to delete
  the link.
* Actor Tool: deleting an actor immediately calls PruneLinks()
  to clean up any links that the deleted doodad might have.
2021-08-11 20:40:31 -07:00
d7a96d1770 Thief and Inventory APIs
This commit adds the Thief character with starter graphics
(no animations).

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

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

New and Changed Doodads

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

JavaScript API - New functions available

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

Other Changes

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

Updates to the JavaScript API for doodads:

* Self.MoveTo(Point) is now available to set the actor's position in
  world coordinates.
2021-08-08 20:10:42 -07:00
49876c4fdf New TabFrame Widget for Doodads and Settings
* Install the new ui.TabFrame widget into the Settings and Doodad
  Dropper windows to give them properly tabbed interfaces.
* Doodad Dropper's new tabs divide the list of doodads into categories
  to make them easier to find.
* The officially defined categories so far are:
  - Objects (Start/End Flags and Box)
  - Doors (All locked doors and keys, Warp Doors, and Electric Door)
  - Gizmos (All buttons, switches, state blocks/doors, Electric Door)
  - Creatures (Blue/Red Azulian, Bird, Boy)
* The "All" tab of the Doodad Dropper will show every doodad regardless
  of its category or whether it fit one of the official categories.
* How doodads are assigned categories is by a special "category" tag in
  their metadata, e.g. "category=doors,gizmos" - multiple supported.
2021-07-25 21:46:55 -07:00
fbeeb207a8 Embed: Normalize path separator for Windows
* Windows was asking for files like "assets\doodads\" and the embedded
  files used Unix-like paths so no assets could be loaded!
2021-07-19 22:15:40 -07:00
3949934bc1 Prepare v0.7.2 for release 2021-07-19 21:20:22 -07:00
215ed5c847 Stabilize Load Screen by Deferring SDL2 Calls
* The loading screen for Edit and Play modes is stable and the risk of
  game crash is removed. The root cause was the setupAsync() functions
  running on a background goroutine, and running SDL2 draw functions
  while NOT on the main thread, which causes problems.
* The fix is all SDL2 Texture draws become lazy loaded: when the main
  thread is presenting, any Wallpaper or ui.Image that has no texture
  yet gets one created at that time from the cached image.Image.
* All internal game logic then uses image.Image types, to cache bitmaps
  of Level Chunks, Wallpaper images, Sprite icons, etc. and the game is
  free to prepare these asynchronously; only the main thread ever
  Presents and the SDL2 textures initialize on first appearance.
* Several functions had arguments cleaned up: Canvas.LoadLevel() does
  not need the render.Engine as (e.g. wallpaper) textures don't render
  at that stage.
2021-07-19 17:14:00 -07:00
2d1b926e4f New Doodad: Box
The Box can be pushed around by the player or other mobile doodads.

It is affected by gravity and can be pushed off ledges.

If the player gets under a box they can bump it up with their head.
2021-07-18 21:22:53 -07:00
d4e6d9babb Loading Screen
* pkg/loadscreen implements a global Loading Screen for loading heavy
  levels for playing or editing.
* All chunks in a level are pre-rendered to bitmap before gameplay
  begins, which reduces stutter as chunks were being lazily rendered on
  first appearance before.
* The loading screen can be played with in the developer console:
  $ loadscreen.Show()
  $ loadscreen.Hide()
  Along with ShowWithProgress(), SetProgress(float64) and IsActive()
* Chunker: separate the concerns between Bitmaps an (SDL2) Textures.
* Chunker.Prerender() converts a chunk to a bitmap (a Go image.Image)
  and caches it, only re-rendering if marked as dirty.
* Chunker.Texture() will use the pre-cached bitmap if available to
  immediately produce the SDL2 texture.

Other miscellaneous changes:

* Added to the Colored Pencil palette: Sandstone
* Added "perlin noise" brush pattern

Note: this commit introduces instability and crashes:

* New `asyncSetup()` functions run on a goroutine, but SDL2 texture
  calls must run on the main thread.
* Chunker avoids this by caching bitmaps, not textures.
* Wallpaper though is unstable, sometimes works, sometimes has graphical
  glitches, sometimes crashes the game.
* Wallpaper.Load() and the *Texture() functions are where it crashes.
2021-07-18 21:19:52 -07:00
2885b2c3d0 Remove bindata references from bootstrap.py 2021-07-13 20:16:16 -07:00
8603c43c58 Gzip Compression for Levels and Doodads
* Levels and Doodad files will be written in gzip-compressed JSON format
* `boolProp compress-drawings false` to disable compression and save as
  classic JSON format directly
* The game can still read uncompressed JSON files

The file size savings on some built-in assets:

* Tutorial 2.level: 2.2M -> 414K (82% smaller)
* warp-door-orange.doodad: 105K -> 17K (84% smaller)
2021-07-13 20:06:57 -07:00
3486050702 Go 1.16 embed instead of go-bindata
* Migrate off go-bindata to embed built-in fonts, levels and doodads in
  favor of Go 1.16 native embed functionality.
* `make bindata` prints a deprecation warning to not break older build
  scripts
* Removes all references of bindata from the program
2021-07-13 18:04:25 -07:00
26b1ac88dd Shift to scroll slowly + Doodads on Q
* Holding Shift while pressing arrow keys in the editor will scroll by
  just 1 pixel per tick to aid in precise debugging with the Zoom In/Out
  feature.
* The keybinds used in canvas_editable.go to catch the arrow keys are
  updated to use our nice keybind package. As a consequence, the WASD
  keys will also scroll the level.
* The "d for Doodads" keybind is renamed "q" so as not to open the
  Doodads window whenever scrolling right using the WASD keys.
2021-07-13 18:04:25 -07:00
37f6177a17 Zoom In/Out Feature: WorldIndexAt Fixed
WorldIndexAt() translates the pixel below the mouse cursor in screen
space (0,0 at top-left corner of the application window) into a world
coordinate in the level shown inside the canvas, taking into account the
canvas's position on the window and the scroll position.

It now translates correctly when zoom In or Out, so the "Abs:" mouse
position level in the status bar shows correctly.

Zoom features that are still jank:

- Scrolling while zoomed in, the chunks to the top/left start unloading
  too rapidly and outpacing the scroll, eventually level is invisible
- Drawing and committing pixels to the image while zoomed in/out is
  unpredictable where the pixels actually land.
- Actors in the level don't move or zoom at all.
2021-07-13 18:04:25 -07:00
ed492a4451 Progress on the Zoom In/Out Feature
* Got the level chunks AND the wallpaper to both scale UP and DOWN
  consistently together.
* Trying to draw new pixels while zoomed in/out ends up offsetting the
  pixels by 2X still. Still seems an issue between screen coordinates
  and world coordinates. Zoom in 2X and try and draw a line 64px from
  the corners of the screen? The committed line appropriately lands at
  the 64px coord on the level data but, zoomed in, it appears 2X to the
  right on the screen from where I dropped the cursor!
* When zooming OUT, the limit on number of chunks the viewport will try
  and render is not increased, leaving dead space in the screen; more
  chunks should render when there's room.
2021-07-11 21:54:28 -07:00
456863839e release.sh steps for easy Mac OS distribution 2021-07-11 14:38:26 -07:00
d1ef9d2932 Prepare v0.7.1 for release 2021-07-11 14:09:18 -07:00
1c7678c48e Improve automated distribution and release 2021-07-11 14:09:18 -07:00
0af4dd40bc Bugfix (Windows): Bindata wallpapers weren't resolving
* The Windows build of v0.6.0 couldn't load embedded wallpapers such as
  legal.png when asked, but could load the hard-coded default.png
* Root cause was the filesystem.FindFile() checking for path separators
  in the filepath, and on Win32 this is \ but the internal wallpaper
  paths use /
2021-07-11 14:09:18 -07:00
1105d9312a Updater: Better SemVer version checks for updates
* Instead of a simple "cur. ver != latest ver" check, parse the Major,
  Minor and Patch components and do a detailed check.
* So a x.x.1 release could be made for a specific platform that had a
  bad build, and it won't mind when it sees the latest version is the
  older x.x.0 build that other platforms had working fine.
2021-07-11 14:09:18 -07:00
fa15a8bcf5 Fix mac-app.sh script 2021-06-20 15:11:03 -07:00
78f9d0dbfa Fix fpmbundle script 2021-06-20 14:40:23 -07:00
99c93dc174 Fix Profile Directory file:// URI for Windows 2021-06-20 13:21:47 -07:00
53123dff1d Prepare v0.7.0 for release 2021-06-20 13:10:23 -07:00
386e0b2b0c Brighten Patterns + Update Default Palettes
* The pattern textures for level palettes have been brightened and work
  better with bright colors.
* The three default palettes for new levels now have patterns applied to
  each of their colors.
* Bugfix around resetting keybind states for Zoom In/Out, Scroll to
  Origin and Reset Zoom Level bindings.
2021-06-20 10:42:51 -07:00
864156da53 Settings Window + Bugfix
* Added a Settings window for game options, such as enabling the
  horizontal toolbars in Edit Mode. The Settings window also has a
  Controls tab showing the gameplay buttons and keyboard shortcuts.
* The Settings window is available as a button on the home screen OR
  from the Edit->Settings menu in the EditScene.
* Bugfix: using WASD to move the player character now works better and
  is considered by the game to be identical to the arrow key inputs. Boy
  now updates his animation based on these keys, and they register as
  boolean on/off keys instead of affected by key-repeat.
* Refactor the boolProps: they are all part of usercfg now, and if you
  run e.g. "boolProp show-all-doodads true" and then cause the user
  settings to save to disk, that boolProp will be permanently enabled
  until turned off again.
2021-06-19 22:14:41 -07:00
d0cfa50625 Hook up keybinds like Ctrl-N, Ctrl-S
* Menu keybinds that weren't working before, like Ctrl-N, Ctrl-S, Ctrl-O
  to create and open levels are now working.
2021-06-17 19:43:30 -07:00
dce32ea14b Diverge Free vs. Paid Features
* Free (shareware) versions of the game will not be able to Publish
  Levels (attach custom doodads to the level file) and they will not be
  able to load a level which relies on embedded doodads.
* The UI for the Publish Level window is still available, but clicking
  on the confirm button will just open the Register (License) window.
* When loading a level containing embedded doodads: if some can't load
  because they're embedded and you're using the free version of the
  game, the error message is customized to reflect that.
2021-06-16 22:35:01 -07:00
0449737607 License Key Registration with ECDSA JWT Tokens
* New command-line tool: doodle-admin for signing license keys for
  users. Includes functions to initialize a keypair, sign license keys
  and validate existing keys.
* The Main Menu screen shows a blue "Register Game" button in the bottom
  right corner of the screen, for unregistered users only.
* In Edit Mode, there is a "Help -> Register" menu item that opens the
  License Window.
* The License UI Window lets the user select the license.key file to
  register the game with. If registered, a copy of the key is placed in
  Doodle's profile directory and the licensee name/email is shown in the
  License UI window.
* Unregistered games will show the word "(shareware)" next to the title
  screen version number and Edit Mode status bar.
* No restrictions are yet placed on free versions of the game.
2021-06-16 21:56:30 -07:00
d6f86487f5 Horizontal Toolbars Option for Editor Mode
On small screen sizes like the Pinephone, the toolbars in the Level
Editor are best made horizontal across the top and bottom of the screen
leaving more room for the drawing.

Enable it with a boolProp for now, and then reopen the level editor:

    boolProp horizontalToolbars true

When launching `doodle -w mobile` it will automatically enable this
option.
2021-06-13 21:23:26 -07:00
e6b71f5512 Fix Scroll-Follow-Actor Behavior
* The scrollbox by which the game follows the player character has been
  revised, it is now an offset away from the window's center instead of
  fixed pixel distances from the window's edges.
* Mobile form-factor (Pinephone) now scrolls OK instead of jerking back
  and forth rapidly when moving left.
2021-06-13 20:25:42 -07:00
c5e3fc297c Manage Embedded Files In Levels
In the Level Editor, the "Level->Attached files" menu opens the
FileSystem Window, which shows a paginated list of attached files and a
"Delete" button to remove them.

- Custom doodads which also exist locally can be deleted from the
  level's filesystem at any time.
- If a custom doodad does NOT exist locally, and one of them is still
  placed somewhere within the level, you can not delete it.
- You can't delete the custom wallpaper image IF the level is still
  using it. Change to a default wallpaper and then you can delete the
  custom wallpaper image.
2021-06-13 16:03:32 -07:00
7093b102e3 Embeddable Doodads In Levels
* 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.
2021-06-13 14:59:03 -07:00
d9bca2152a WIP Publish Dialog + UI Improvements
* 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.
2021-06-10 22:36:22 -07:00
eb24858830 Brush Pattern Textures
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.
2021-06-09 22:36:32 -07:00