Various updates
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"
|
@ -2,17 +2,30 @@ function main() {
|
||||||
var timer = 0;
|
var timer = 0;
|
||||||
var pressed = false;
|
var pressed = false;
|
||||||
|
|
||||||
|
// Has a linked Sticky Button been pressed permanently down?
|
||||||
|
var stickyDown = false;
|
||||||
|
Message.Subscribe("sticky:down", function(down) {
|
||||||
|
stickyDown = down;
|
||||||
|
Self.ShowLayer(stickyDown ? 1 : 0);
|
||||||
|
});
|
||||||
|
|
||||||
Events.OnCollide(function(e) {
|
Events.OnCollide(function(e) {
|
||||||
if (!e.Settled) {
|
if (!e.Settled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a linked Sticky Button is pressed, button stays down too and
|
||||||
|
// doesn't interact.
|
||||||
|
if (stickyDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify they've touched the button.
|
// Verify they've touched the button.
|
||||||
if (e.Overlap.Y + e.Overlap.H < 24) {
|
if (e.Overlap.Y + e.Overlap.H < 24) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pressed) {
|
if (!pressed && !stickyDown) {
|
||||||
Sound.Play("button-down.wav")
|
Sound.Play("button-down.wav")
|
||||||
Message.Publish("power", true);
|
Message.Publish("power", true);
|
||||||
pressed = true;
|
pressed = true;
|
||||||
|
|
|
@ -8,6 +8,7 @@ function main() {
|
||||||
pressed = false;
|
pressed = false;
|
||||||
Sound.Play("button-up.wav")
|
Sound.Play("button-up.wav")
|
||||||
Message.Publish("power", false);
|
Message.Publish("power", false);
|
||||||
|
Message.Publish("sticky:down", false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -29,5 +30,6 @@ function main() {
|
||||||
Self.ShowLayer(1);
|
Self.ShowLayer(1);
|
||||||
pressed = true;
|
pressed = true;
|
||||||
Message.Publish("power", true);
|
Message.Publish("power", true);
|
||||||
|
Message.Publish("sticky:down", true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Before (image error) Size: 415 B After (image error) Size: 5.2 KiB |
|
@ -1,31 +1,59 @@
|
||||||
|
var animating = false;
|
||||||
|
var opened = false;
|
||||||
|
var powerState = false;
|
||||||
|
|
||||||
|
// Function to handle the door opening or closing.
|
||||||
|
function setPoweredState(powered) {
|
||||||
|
powerState = powered;
|
||||||
|
|
||||||
|
console.log("setPoweredState: %+v", powered)
|
||||||
|
if (powered) {
|
||||||
|
if (animating || opened) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animating = true;
|
||||||
|
Sound.Play("electric-door.wav")
|
||||||
|
Self.PlayAnimation("open", function() {
|
||||||
|
opened = true;
|
||||||
|
animating = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
animating = true;
|
||||||
|
Sound.Play("electric-door.wav")
|
||||||
|
Self.PlayAnimation("close", function() {
|
||||||
|
opened = false;
|
||||||
|
animating = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
Self.AddAnimation("open", 100, [0, 1, 2, 3]);
|
Self.AddAnimation("open", 100, [0, 1, 2, 3]);
|
||||||
Self.AddAnimation("close", 100, [3, 2, 1, 0]);
|
Self.AddAnimation("close", 100, [3, 2, 1, 0]);
|
||||||
var animating = false;
|
|
||||||
var opened = false;
|
|
||||||
|
|
||||||
Self.SetHitbox(0, 0, 34, 76);
|
Self.SetHitbox(0, 0, 34, 76);
|
||||||
|
|
||||||
Message.Subscribe("power", function(powered) {
|
// A linked Switch that activates the door will send the Toggle signal
|
||||||
if (powered) {
|
// immediately before the Power signal. The door can just invert its
|
||||||
if (animating || opened) {
|
// state on this signal, and ignore the very next Power signal. Ordinary
|
||||||
return;
|
// power sources like Buttons will work as normal, as they emit only a power
|
||||||
}
|
// signal.
|
||||||
|
var ignoreNextPower = false;
|
||||||
|
Message.Subscribe("switch:toggle", function(powered) {
|
||||||
|
console.log("A switch powered %+v, setPoweredState(%+v) to opposite", powered, powerState);
|
||||||
|
ignoreNextPower = true;
|
||||||
|
setPoweredState(!powerState);
|
||||||
|
})
|
||||||
|
|
||||||
animating = true;
|
Message.Subscribe("power", function(powered) {
|
||||||
Sound.Play("electric-door.wav")
|
if (ignoreNextPower) {
|
||||||
Self.PlayAnimation("open", function() {
|
ignoreNextPower = false;
|
||||||
opened = true;
|
return;
|
||||||
animating = false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
animating = true;
|
|
||||||
Sound.Play("electric-door.wav")
|
|
||||||
Self.PlayAnimation("close", function() {
|
|
||||||
opened = false;
|
|
||||||
animating = false;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPoweredState(powered);
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.OnCollide(function(e) {
|
Events.OnCollide(function(e) {
|
||||||
|
|
|
@ -20,6 +20,9 @@ function main() {
|
||||||
if (collide === false) {
|
if (collide === false) {
|
||||||
Sound.Play("button-down.wav")
|
Sound.Play("button-down.wav")
|
||||||
state = !state;
|
state = !state;
|
||||||
|
|
||||||
|
var nonce = Math.random() * 2147483647;
|
||||||
|
Message.Publish("switch:toggle", state);
|
||||||
Message.Publish("power", state);
|
Message.Publish("power", state);
|
||||||
showState(state);
|
showState(state);
|
||||||
|
|
||||||
|
|
Before (image error) Size: 1020 B After (image error) Size: 1.0 KiB |
Before (image error) Size: 970 B After (image error) Size: 1.1 KiB |
Before (image error) Size: 1.0 KiB After (image error) Size: 1.1 KiB |
Before (image error) Size: 859 B After (image error) Size: 1007 B |
Before (image error) Size: 912 B After (image error) Size: 1.0 KiB |
Before (image error) Size: 1013 B After (image error) Size: 1.1 KiB |
Before (image error) Size: 1009 B After (image error) Size: 1.1 KiB |
Before (image error) Size: 765 B After (image error) Size: 992 B |
Before (image error) Size: 933 B After (image error) Size: 1.0 KiB |
Before (image error) Size: 1011 B After (image error) Size: 1.1 KiB |
Before (image error) Size: 1.0 KiB After (image error) Size: 1.0 KiB |
Before (image error) Size: 789 B After (image error) Size: 991 B |
|
@ -5,8 +5,8 @@ function main() {
|
||||||
var timer = 0;
|
var timer = 0;
|
||||||
|
|
||||||
// Set our hitbox based on our orientation.
|
// Set our hitbox based on our orientation.
|
||||||
var thickness = 6;
|
var thickness = 10;
|
||||||
var doodadSize = 72;
|
var doodadSize = 86;
|
||||||
if (direction === "left") {
|
if (direction === "left") {
|
||||||
Self.SetHitbox(48, 0, doodadSize, doodadSize);
|
Self.SetHitbox(48, 0, doodadSize, doodadSize);
|
||||||
} else if (direction === "right") {
|
} else if (direction === "right") {
|
||||||
|
|
Before (image error) Size: 831 B After (image error) Size: 1.0 KiB |
Before (image error) Size: 964 B After (image error) Size: 1.1 KiB |
Before (image error) Size: 1.0 KiB After (image error) Size: 1.1 KiB |
Before (image error) Size: 846 B After (image error) Size: 1006 B |
2
go.mod
|
@ -24,6 +24,7 @@ require (
|
||||||
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
|
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
|
||||||
github.com/stripe/safesql v0.2.0 // indirect
|
github.com/stripe/safesql v0.2.0 // indirect
|
||||||
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e // indirect
|
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e // indirect
|
||||||
|
github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9 // indirect
|
||||||
github.com/urfave/cli v1.22.5
|
github.com/urfave/cli v1.22.5
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
github.com/veandco/go-sdl2 v0.4.4
|
github.com/veandco/go-sdl2 v0.4.4
|
||||||
|
@ -38,3 +39,4 @@ require (
|
||||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 // indirect
|
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@ package branding
|
||||||
|
|
||||||
// Constants for branding and version information.
|
// Constants for branding and version information.
|
||||||
const (
|
const (
|
||||||
AppName = "Project: Doodle"
|
AppName = "Sketchy Maze"
|
||||||
Summary = "A drawing-based maze game"
|
Summary = "A drawing-based maze game"
|
||||||
Version = "0.4.0-alpha"
|
Version = "0.5.0-alpha"
|
||||||
Website = "https://www.kirsle.net/tagged/Doodle"
|
Website = "https://www.sketchymaze.com"
|
||||||
Copyright = "2020 Noah Petherbridge"
|
Copyright = "2021 Noah Petherbridge"
|
||||||
|
|
||||||
// Update check URL
|
// Update check URL
|
||||||
UpdateCheckJSON = "https://download.sketchymaze.com/version.json"
|
UpdateCheckJSON = "https://download.sketchymaze.com/version.json"
|
||||||
|
|
|
@ -25,7 +25,7 @@ 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 bool
|
InFire string // the name of the swatch, Fire = general ouchy color.
|
||||||
InWater bool
|
InWater bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ func CollidesWithGrid(d Actor, grid *level.Chunker, target render.Point) (*Colli
|
||||||
// IsColliding returns whether any sort of collision has occurred.
|
// IsColliding returns whether any sort of collision has occurred.
|
||||||
func (c *Collide) IsColliding() bool {
|
func (c *Collide) IsColliding() bool {
|
||||||
return c.Top || c.Bottom || c.Left || c.Right ||
|
return c.Top || c.Bottom || c.Left || c.Right ||
|
||||||
c.InFire || c.InWater
|
c.InFire != "" || c.InWater
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanBoundingBox scans all of the pixels in a bounding box on the grid and
|
// ScanBoundingBox scans all of the pixels in a bounding box on the grid and
|
||||||
|
@ -276,7 +276,7 @@ func (c *Collide) ScanGridLine(p1, p2 render.Point, grid *level.Chunker, side Si
|
||||||
// in our result. If non-solid, we'll collect attributes from it
|
// in our result. If non-solid, we'll collect attributes from it
|
||||||
// and return them in the final result for gameplay behavior.
|
// and return them in the final result for gameplay behavior.
|
||||||
if swatch.Fire {
|
if swatch.Fire {
|
||||||
c.InFire = true
|
c.InFire = swatch.Name
|
||||||
}
|
}
|
||||||
if swatch.Water {
|
if swatch.Water {
|
||||||
c.InWater = true
|
c.InWater = true
|
||||||
|
|
|
@ -138,9 +138,11 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
||||||
|
|
||||||
// Handler when an actor touches water or fire.
|
// Handler when an actor touches water or fire.
|
||||||
s.drawing.OnLevelCollision = func(a *uix.Actor, col *collision.Collide) {
|
s.drawing.OnLevelCollision = func(a *uix.Actor, col *collision.Collide) {
|
||||||
if col.InFire {
|
if col.InFire != "" {
|
||||||
a.Canvas.MaskColor = render.Black
|
a.Canvas.MaskColor = render.Black
|
||||||
s.DieByFire()
|
if a.ID() == "PLAYER" { // only the player dies in fire.
|
||||||
|
s.DieByFire(col.InFire)
|
||||||
|
}
|
||||||
} else if col.InWater {
|
} else if col.InWater {
|
||||||
a.Canvas.MaskColor = render.DarkBlue
|
a.Canvas.MaskColor = render.DarkBlue
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,11 +354,11 @@ func (s *PlayScene) RestartLevel() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DieByFire ends the level by fire.
|
// DieByFire ends the level by "fire", or w/e the swatch is named.
|
||||||
func (s *PlayScene) DieByFire() {
|
func (s *PlayScene) DieByFire(name string) {
|
||||||
log.Info("Watch out for fire!")
|
log.Info("Watch out for %s!", name)
|
||||||
s.alertBox.Title = "You've died!"
|
s.alertBox.Title = "You've died!"
|
||||||
s.alertBoxLabel.Text = "Watch out for fire!"
|
s.alertBoxLabel.Text = fmt.Sprintf("Watch out for %s!", name)
|
||||||
|
|
||||||
s.alertReplayButton.Show()
|
s.alertReplayButton.Show()
|
||||||
if s.CanEdit {
|
if s.CanEdit {
|
||||||
|
|
|
@ -108,6 +108,7 @@ func NewCanvas(size int, editable bool) *Canvas {
|
||||||
Editable: editable,
|
Editable: editable,
|
||||||
Scrollable: editable,
|
Scrollable: editable,
|
||||||
Palette: level.NewPalette(),
|
Palette: level.NewPalette(),
|
||||||
|
BrushSize: 1,
|
||||||
chunks: level.NewChunker(size),
|
chunks: level.NewChunker(size),
|
||||||
actors: make([]*Actor, 0),
|
actors: make([]*Actor, 0),
|
||||||
wallpaper: &Wallpaper{},
|
wallpaper: &Wallpaper{},
|
||||||
|
|
|
@ -43,7 +43,7 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
window.SetButtons(ui.CloseButton)
|
window.SetButtons(ui.CloseButton)
|
||||||
window.Configure(ui.Config{
|
window.Configure(ui.Config{
|
||||||
Width: 400,
|
Width: 400,
|
||||||
Height: 180,
|
Height: 240,
|
||||||
Background: render.Grey,
|
Background: render.Grey,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -167,6 +167,73 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
}(t)
|
}(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************
|
||||||
|
* Frame for giving the level a title.
|
||||||
|
******************/
|
||||||
|
|
||||||
|
if config.EditLevel != nil {
|
||||||
|
label3 := ui.NewLabel(ui.Label{
|
||||||
|
Text: "Metadata",
|
||||||
|
Font: balance.LabelFont,
|
||||||
|
})
|
||||||
|
frame.Pack(label3, ui.Pack{
|
||||||
|
Side: ui.N,
|
||||||
|
FillX: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
type metadataObj struct {
|
||||||
|
Label string
|
||||||
|
Binding *string
|
||||||
|
Update func(string)
|
||||||
|
}
|
||||||
|
var metaRows = []metadataObj{
|
||||||
|
{"Title:", &config.EditLevel.Title, func(v string) { config.EditLevel.Title = v }},
|
||||||
|
{"Author:", &config.EditLevel.Author, func(v string) { config.EditLevel.Author = v }},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mr := range metaRows {
|
||||||
|
mr := mr
|
||||||
|
mrFrame := ui.NewFrame("Metadata " + mr.Label + "Frame")
|
||||||
|
frame.Pack(mrFrame, ui.Pack{
|
||||||
|
Side: ui.N,
|
||||||
|
FillX: true,
|
||||||
|
PadY: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
// The label.
|
||||||
|
mrLabel := ui.NewLabel(ui.Label{
|
||||||
|
Text: mr.Label,
|
||||||
|
Font: balance.MenuFont,
|
||||||
|
})
|
||||||
|
mrLabel.Configure(ui.Config{
|
||||||
|
Width: 75,
|
||||||
|
})
|
||||||
|
mrFrame.Pack(mrLabel, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
})
|
||||||
|
|
||||||
|
// The button.
|
||||||
|
mrButton := ui.NewButton(mr.Label, ui.NewLabel(ui.Label{
|
||||||
|
TextVariable: mr.Binding,
|
||||||
|
Font: balance.MenuFont,
|
||||||
|
}))
|
||||||
|
mrButton.Handle(ui.Click, func(ed ui.EventData) error {
|
||||||
|
shmem.Prompt("Enter a new "+mr.Label, func(answer string) {
|
||||||
|
if answer != "" {
|
||||||
|
mr.Update(answer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
config.Supervisor.Add(mrButton)
|
||||||
|
mrFrame.Pack(mrButton, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
Expand: true,
|
||||||
|
PadX: 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************
|
/******************
|
||||||
* Confirm/cancel buttons.
|
* Confirm/cancel buttons.
|
||||||
******************/
|
******************/
|
||||||
|
|