Slippery Pixels + Update Changelog for 0.13.1
This commit is contained in:
parent
ecaa8c6cef
commit
8b5dab6d6f
125
Changes.md
125
Changes.md
|
@ -1,5 +1,130 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## v0.13.1 (Oct 10 2022)
|
||||||
|
|
||||||
|
This release brings a handful of minor new features to the game.
|
||||||
|
|
||||||
|
First, there are a couple of new Pixel Attributes available in the level editor:
|
||||||
|
|
||||||
|
* Semi-Solid: pixels with this attribute only behave as "solid" when walked on
|
||||||
|
from above. The player can jump through the bottom of a Semi-Solid and land
|
||||||
|
on top, and gradual slopes can be walked up and down as well, but a steep
|
||||||
|
slope or a wall can be simply passed through as though it were just decoration.
|
||||||
|
* Slippery: the player's acceleration and friction are reduced when walking on
|
||||||
|
a slippery floor. In the future, players and other mobile doodads may slide
|
||||||
|
down slippery slopes automatically as well (not yet implemented).
|
||||||
|
* These attributes are available in the Level Editor by clicking the "Edit"
|
||||||
|
button on your Palette (or the "Tools -> Edit Palette" menu). The Palette
|
||||||
|
Editor now has small icon images for the various attributes to make room for
|
||||||
|
the expanded arsenal of options.
|
||||||
|
|
||||||
|
Doodad/Actor Runtime Options have been added:
|
||||||
|
|
||||||
|
* In the Doodad Editor's "Doodad Properties" window, see the new "Options" tab.
|
||||||
|
* Doodad Options allow a map creator to customize certain properties about your
|
||||||
|
doodad, on a per-instance basis (instances of doodads are called "actors" when
|
||||||
|
placed in your level).
|
||||||
|
* In the Level Editor when the Actor Tool is selected, mousing over a doodad on
|
||||||
|
your level will show a new gear icon in the corner. Clicking the icon will open
|
||||||
|
the Actor Properties window, where you may toggle some of the doodad options
|
||||||
|
(if a doodad has any options available).
|
||||||
|
* Options can be of type boolean, string, or integer and have a custom name and a
|
||||||
|
default value at the doodad level. In the Level Editor, the map creator can
|
||||||
|
set values for the available options which the doodad script can read using the
|
||||||
|
`Self.GetOption()` method.
|
||||||
|
* Several of the game's built-in doodads have options you can play with, which are
|
||||||
|
documented below.
|
||||||
|
|
||||||
|
New and updated doodads:
|
||||||
|
|
||||||
|
* "Look At Me" is a new Technical doodad that will draw the camera's attention
|
||||||
|
to it when it receives a power signal from a linked button. For example, if
|
||||||
|
a button would open an Electric Door far across the level, you can also place
|
||||||
|
a "Look At Me" near the door and link the button to both doodads. When the
|
||||||
|
button is pressed, the camera will scroll to the "Look At Me" and the player
|
||||||
|
can see that the door has opened.
|
||||||
|
* Anvils will now attract the camera's attention while they are falling.
|
||||||
|
|
||||||
|
Several of the game's built-in doodads have new Actor Runtime Options you can
|
||||||
|
configure in your custom levels:
|
||||||
|
|
||||||
|
* Warp Doors: "locked (exit only)" will make it so the player can not enter the
|
||||||
|
warp door - they will get a message on-screen that it is locked, similar to
|
||||||
|
how warp doors behave when they aren't linked to another door. If it is linked
|
||||||
|
to another door, the player may still exit from the 'locked' door -
|
||||||
|
essentially creating a one-way warp, without needing to rely on the
|
||||||
|
orange/blue state doors. The "Invisible Warp Door" technical doodad also
|
||||||
|
supports this option.
|
||||||
|
* Electric Door & Electric Trapdoor: check the "opened" option and these doors
|
||||||
|
will be opened by default when the level gameplay begins. A switch may still
|
||||||
|
toggle the doors closed, or if the doors receive and then lose a power signal
|
||||||
|
they will close as normal.
|
||||||
|
* Colored Doors & Small Key Door: you may mark the doors as "unlocked" at the
|
||||||
|
start of your level, and they won't require a key to open.
|
||||||
|
* Colored Keys & Small Key: you may mark the keys as "has gravity" and they
|
||||||
|
will be subject to the force of gravity and be considered a "mobile" doodad
|
||||||
|
that may activate buttons or trapdoors that they fall onto.
|
||||||
|
* Gemstones: these items already had gravity by default, and now they have a
|
||||||
|
"has gravity" option you may disable if you'd prefer gemstones not to be
|
||||||
|
subject to gravity (and make them behave the way keys used to).
|
||||||
|
* Gemstome Totems: for cosmetic purposes you may toggle the "has gemstone"
|
||||||
|
option and the totem will already have its stone inserted at level start.
|
||||||
|
These gemstones will NOT emit a power signal or interact normally with
|
||||||
|
linked totems - they should be configured this way only for the cosmetic
|
||||||
|
appearance, e.g., to have one totem filled and some others empty; only the
|
||||||
|
empty totems should be linked together and to a door that would open when
|
||||||
|
they are all filled.
|
||||||
|
* Fire Region: you may pick a custom "name" for this doodad (default is "fire")
|
||||||
|
to make it better behave as normal fire pixels do: "Watch out for (name)!"
|
||||||
|
|
||||||
|
Improvements in support of custom content:
|
||||||
|
|
||||||
|
* Add a JavaScript "Exception Catcher" window in-game. If your doodad scripts
|
||||||
|
encounter a scripting error, a red window will pop up showing the text of
|
||||||
|
the exception with buttons to copy the full text to your clipboard (in case
|
||||||
|
it doesn't all fit on-screen) and to suppress any further exceptions for
|
||||||
|
the rest of your game session (in case a broken doodad is spamming you with
|
||||||
|
error messages). Cheat codes can invoke the Exception Catcher for testing:
|
||||||
|
`throw <message>` to show custom text, `throw2` to test a "long" message
|
||||||
|
and `throw3` to throw a realistic message.
|
||||||
|
* Calling `console.log()` and similar from doodad scripts will now prefix the
|
||||||
|
log message with the doodad's filename and level ID.
|
||||||
|
|
||||||
|
There are new JavaScript API methods available to doodad scripts:
|
||||||
|
|
||||||
|
* `Self.CameraFollowMe()` will attract the game's camera viewport to center
|
||||||
|
on your doodad, taking the camera's focus away from the player character.
|
||||||
|
The camera will return to the player if they enter a directional input.
|
||||||
|
* `Self.Options()` returns a string array of all of the options available on
|
||||||
|
the current doodad.
|
||||||
|
* `Self.GetOption(name)` returns the configured value for a given option.
|
||||||
|
|
||||||
|
Some improvements to the `doodad` command-line tool:
|
||||||
|
|
||||||
|
* `doodad show` will print the Options on a .doodad file and, when showing
|
||||||
|
a .level file with the `--actors` option, will list any Options configured
|
||||||
|
on a level's actors where they differ from the doodad's defaults.
|
||||||
|
* `doodad edit-doodad` adds a `--option` parameter to define an option on a
|
||||||
|
doodad programmatically. The syntax is like `--option name=type=default`
|
||||||
|
for example `--option unlocked=bool=true` or `--option unlocked=bool`; the
|
||||||
|
default value is optional if you want it to be the "zero value" (false,
|
||||||
|
zero, or empty string).
|
||||||
|
|
||||||
|
Minor fixes and improvements:
|
||||||
|
|
||||||
|
* Add a "Wait" modal with a progress bar. Not used yet but may be useful
|
||||||
|
for long operations like Giant Screenshot or level saving to block input
|
||||||
|
to the game while it's busy doing something. Can be tested using the
|
||||||
|
cheat code "test wait screen"
|
||||||
|
* Detect the presence of a touchscreen device and automatically disable
|
||||||
|
on-screen touch hints during gameplay if not on a touch screen.
|
||||||
|
* Mobile Linux: mark the Sketchy Maze launcher as supporting the mobile
|
||||||
|
form-factor for the Phosh desktop shell especially.
|
||||||
|
* Fix the Crusher doodad sometimes not falling until it hits the ground
|
||||||
|
and stopping early on slower computers.
|
||||||
|
* Small tweaks to player physics - acceleration increased from 0.025 to
|
||||||
|
0.04 pixels per tick.
|
||||||
|
|
||||||
## v0.13.0 (May 7 2022)
|
## v0.13.0 (May 7 2022)
|
||||||
|
|
||||||
This is a major update that brings deep architectural changes and a lot
|
This is a major update that brings deep architectural changes and a lot
|
||||||
|
|
|
@ -36,15 +36,18 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player speeds
|
// Player speeds
|
||||||
PlayerMaxVelocity float64 = 7
|
PlayerMaxVelocity float64 = 7
|
||||||
PlayerJumpVelocity float64 = -23
|
PlayerJumpVelocity float64 = -23
|
||||||
PlayerAcceleration float64 = 0.12
|
PlayerAcceleration float64 = 0.04 // 0.12
|
||||||
Gravity float64 = 7
|
PlayerFriction float64 = 0.1
|
||||||
GravityAcceleration float64 = 0.1
|
SlipperyAcceleration float64 = 0.02
|
||||||
SwimGravity float64 = 3
|
SlipperyFriction float64 = 0.02
|
||||||
SwimJumpVelocity float64 = -12
|
Gravity float64 = 7
|
||||||
SwimJumpCooldown uint64 = 24 // number of frames of cooldown between swim-jumps
|
GravityAcceleration float64 = 0.1
|
||||||
SlopeMaxHeight = 8 // max pixel height for player to walk up a slope
|
SwimGravity float64 = 3
|
||||||
|
SwimJumpVelocity float64 = -12
|
||||||
|
SwimJumpCooldown uint64 = 24 // number of frames of cooldown between swim-jumps
|
||||||
|
SlopeMaxHeight = 8 // max pixel height for player to walk up a slope
|
||||||
|
|
||||||
// Number of game ticks to insist the canvas follows the player at the start
|
// Number of game ticks to insist the canvas follows the player at the start
|
||||||
// of a level - to overcome Anvils settling into their starting positions so
|
// of a level - to overcome Anvils settling into their starting positions so
|
||||||
|
|
|
@ -25,8 +25,9 @@ type Collide struct {
|
||||||
MoveTo render.Point
|
MoveTo render.Point
|
||||||
|
|
||||||
// Swatch attributes affecting the collision at this time.
|
// Swatch attributes affecting the collision at this time.
|
||||||
InFire string // the name of the swatch, Fire = general ouchy color.
|
InFire string // the name of the swatch, Fire = general ouchy color.
|
||||||
InWater bool
|
InWater bool
|
||||||
|
IsSlippery bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset a Collide struct flipping all the bools off, but keeping MoveTo.
|
// Reset a Collide struct flipping all the bools off, but keeping MoveTo.
|
||||||
|
@ -296,6 +297,11 @@ func (c *Collide) ScanGridLine(p1, p2 render.Point, grid *level.Chunker, side Si
|
||||||
c.InWater = true
|
c.InWater = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slippery floor?
|
||||||
|
if side == Bottom && swatch.Slippery {
|
||||||
|
c.IsSlippery = true
|
||||||
|
}
|
||||||
|
|
||||||
// Non-solid swatches don't collide so don't pay them attention.
|
// Non-solid swatches don't collide so don't pay them attention.
|
||||||
if !swatch.Solid && !swatch.SemiSolid {
|
if !swatch.Solid && !swatch.SemiSolid {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
|
|
||||||
// Options for runtime, user configurable.
|
// Options for runtime, user configurable.
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Type string // bool, str, int
|
Type string `json:"type"` // bool, str, int
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Default interface{}
|
Default interface{} `json:"default"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption sets an actor option, safely.
|
// SetOption sets an actor option, safely.
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
|
|
||||||
// Option for runtime, user configurable overrides of Doodad Options.
|
// Option for runtime, user configurable overrides of Doodad Options.
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Type string // bool, str, int
|
Type string `json:"type"` // bool, str, int
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Value interface{}
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption sets an actor option, safely.
|
// SetOption sets an actor option, safely.
|
||||||
|
|
|
@ -36,11 +36,11 @@ func (m ActorMap) Remove(a *Actor) bool {
|
||||||
|
|
||||||
// Actor is an instance of a Doodad in the level.
|
// Actor is an instance of a Doodad in the level.
|
||||||
type Actor struct {
|
type Actor struct {
|
||||||
id string // NOTE: read only, use ID() to access.
|
id string // NOTE: read only, use ID() to access.
|
||||||
Filename string `json:"filename"` // like "exit.doodad"
|
Filename string `json:"filename"` // like "exit.doodad"
|
||||||
Point render.Point `json:"point"`
|
Point render.Point `json:"point"`
|
||||||
Links []string `json:"links,omitempty"` // IDs of linked actors
|
Links []string `json:"links,omitempty"` // IDs of linked actors
|
||||||
Options map[string]*Option
|
Options map[string]*Option `json:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewActor initializes a level.Actor.
|
// NewActor initializes a level.Actor.
|
||||||
|
|
|
@ -57,6 +57,9 @@ func (s *Swatch) Attributes() string {
|
||||||
if s.Solid {
|
if s.Solid {
|
||||||
result += "solid,"
|
result += "solid,"
|
||||||
}
|
}
|
||||||
|
if s.SemiSolid {
|
||||||
|
result += "semi-solid,"
|
||||||
|
}
|
||||||
if s.Fire {
|
if s.Fire {
|
||||||
result += "fire,"
|
result += "fire,"
|
||||||
}
|
}
|
||||||
|
@ -66,6 +69,9 @@ func (s *Swatch) Attributes() string {
|
||||||
if s.isSparse {
|
if s.isSparse {
|
||||||
result += "sparse,"
|
result += "sparse,"
|
||||||
}
|
}
|
||||||
|
if s.Slippery {
|
||||||
|
result += "slippery,"
|
||||||
|
}
|
||||||
|
|
||||||
if result == "" {
|
if result == "" {
|
||||||
result = "none,"
|
result = "none,"
|
||||||
|
|
|
@ -70,8 +70,10 @@ type PlayScene struct {
|
||||||
// Player character
|
// Player character
|
||||||
Player *uix.Actor
|
Player *uix.Actor
|
||||||
playerPhysics *physics.Mover
|
playerPhysics *physics.Mover
|
||||||
|
slipperyPhysics *physics.Mover
|
||||||
lastCheckpoint render.Point
|
lastCheckpoint render.Point
|
||||||
playerLastDirection float64 // player's heading last tick
|
playerLastDirection float64 // player's heading last tick
|
||||||
|
slippery bool // player is on a slippery surface
|
||||||
antigravity bool // Cheat: disable player gravity
|
antigravity bool // Cheat: disable player gravity
|
||||||
noclip bool // Cheat: disable player clipping
|
noclip bool // Cheat: disable player clipping
|
||||||
godMode bool // Cheat: player can't die
|
godMode bool // Cheat: player can't die
|
||||||
|
@ -228,6 +230,9 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
||||||
} else {
|
} else {
|
||||||
a.Canvas.MaskColor = render.Invisible
|
a.Canvas.MaskColor = render.Invisible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slippery floor?
|
||||||
|
s.slippery = col.IsSlippery
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a doodad changing the player character.
|
// Handle a doodad changing the player character.
|
||||||
|
@ -481,8 +486,13 @@ func (s *PlayScene) installPlayerDoodad(filename string, spawn render.Point, cen
|
||||||
// Set up the movement physics for the player.
|
// Set up the movement physics for the player.
|
||||||
s.playerPhysics = &physics.Mover{
|
s.playerPhysics = &physics.Mover{
|
||||||
MaxSpeed: physics.NewVector(balance.PlayerMaxVelocity, balance.PlayerMaxVelocity),
|
MaxSpeed: physics.NewVector(balance.PlayerMaxVelocity, balance.PlayerMaxVelocity),
|
||||||
Acceleration: 0.025,
|
Acceleration: balance.PlayerAcceleration,
|
||||||
Friction: 0.1,
|
Friction: balance.PlayerFriction,
|
||||||
|
}
|
||||||
|
s.slipperyPhysics = &physics.Mover{
|
||||||
|
MaxSpeed: s.playerPhysics.MaxSpeed,
|
||||||
|
Acceleration: balance.SlipperyAcceleration,
|
||||||
|
Friction: balance.SlipperyFriction,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the player character's script in the VM.
|
// Set up the player character's script in the VM.
|
||||||
|
|
|
@ -17,9 +17,14 @@ func (s *PlayScene) movePlayer(ev *event.State) {
|
||||||
velocity = s.Player.Velocity()
|
velocity = s.Player.Velocity()
|
||||||
direction float64
|
direction float64
|
||||||
jumping bool
|
jumping bool
|
||||||
|
phys = s.playerPhysics
|
||||||
// holdingJump bool // holding down the jump button vs. tapping it
|
// holdingJump bool // holding down the jump button vs. tapping it
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if s.slippery {
|
||||||
|
phys = s.slipperyPhysics
|
||||||
|
}
|
||||||
|
|
||||||
// Antigravity: player can move anywhere with arrow keys.
|
// Antigravity: player can move anywhere with arrow keys.
|
||||||
if s.antigravity || !s.Player.HasGravity() {
|
if s.antigravity || !s.Player.HasGravity() {
|
||||||
velocity.X = 0
|
velocity.X = 0
|
||||||
|
@ -77,15 +82,15 @@ func (s *PlayScene) movePlayer(ev *event.State) {
|
||||||
// slip and slide while their velocity updates.
|
// slip and slide while their velocity updates.
|
||||||
velocity.X = physics.Lerp(
|
velocity.X = physics.Lerp(
|
||||||
velocity.X,
|
velocity.X,
|
||||||
direction*s.playerPhysics.MaxSpeed.X,
|
direction*phys.MaxSpeed.X,
|
||||||
s.playerPhysics.Acceleration,
|
phys.Acceleration,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Slow them back to zero using friction.
|
// Slow them back to zero using friction.
|
||||||
velocity.X = physics.Lerp(
|
velocity.X = physics.Lerp(
|
||||||
velocity.X,
|
velocity.X,
|
||||||
0,
|
0,
|
||||||
s.playerPhysics.Friction,
|
phys.Friction,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,9 +200,9 @@ func (a *Actor) Grounded() bool {
|
||||||
// SetGrounded sets the actor's grounded value. If true, also sets their Y velocity to zero.
|
// SetGrounded sets the actor's grounded value. If true, also sets their Y velocity to zero.
|
||||||
func (a *Actor) SetGrounded(v bool) {
|
func (a *Actor) SetGrounded(v bool) {
|
||||||
a.grounded = v
|
a.grounded = v
|
||||||
if v {
|
// if v && a.velocity.Y > 0 {
|
||||||
a.velocity.Y = 0
|
// a.velocity.Y = 0
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide makes the actor invisible.
|
// Hide makes the actor invisible.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user