diff --git a/dev-assets/doodads/box/box.js b/dev-assets/doodads/box/box.js index 1c97439..30d3d6a 100644 --- a/dev-assets/doodads/box/box.js +++ b/dev-assets/doodads/box/box.js @@ -41,9 +41,6 @@ function main() { // When we receive power, we reset to our original position. var origPoint = Self.Position(); Message.Subscribe("power", function (powered) { - console.error("Box received power! %+v", powered); - console.error("MoveTo: %+v", origPoint); - console.error("Keys: %+v", Object.keys(Self)); Self.MoveTo(origPoint); Self.SetVelocity(Vector(0, 0)); }); diff --git a/dev-assets/doodads/build.sh b/dev-assets/doodads/build.sh index f434a94..5597a19 100755 --- a/dev-assets/doodads/build.sh +++ b/dev-assets/doodads/build.sh @@ -58,6 +58,10 @@ objects() { cd box/ make cd .. + + cd crumbly-floor/ + make + cd .. } onoff() { diff --git a/dev-assets/doodads/objects/Makefile b/dev-assets/doodads/objects/Makefile index 0573bc1..c56b8ed 100644 --- a/dev-assets/doodads/objects/Makefile +++ b/dev-assets/doodads/objects/Makefile @@ -2,10 +2,16 @@ ALL: build .PHONY: build build: + # Start Flag + doodad convert -t "Start Flag" start-flag.png start-flag.doodad + + # Exit Flag doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad doodad install-script exit-flag.js exit-flag.doodad - doodad convert -t "Start Flag" start-flag.png start-flag.doodad + # Anvil + doodad convert -t "Anvil" anvil.png anvil.doodad + doodad install-script anvil.js anvil.doodad for i in *.doodad; do\ doodad edit-doodad --tag "category=objects" $${i};\ diff --git a/dev-assets/doodads/objects/anvil.js b/dev-assets/doodads/objects/anvil.js new file mode 100644 index 0000000..2359bca --- /dev/null +++ b/dev-assets/doodads/objects/anvil.js @@ -0,0 +1,51 @@ +// Anvil +var falling = false; + +function main() { + // Note: doodad is not "solid" but hurts if it falls on you. + Self.SetHitbox(0, 0, 48, 25); + Self.SetMobile(true); + Self.SetGravity(true); + + // Monitor our Y position to tell if we've been falling. + var lastPoint = Self.Position(); + setInterval(function () { + var nowAt = Self.Position(); + if (nowAt.Y > lastPoint.Y) { + falling = true; + } else { + falling = false; + } + lastPoint = nowAt; + }, 100); + + Events.OnCollide(function (e) { + if (!e.Settled) { + return; + } + + // Were we falling? + if (falling) { + if (e.InHitbox) { + if (e.Actor.IsPlayer()) { + // Fatal to the player. + Sound.Play("crumbly-break.wav"); + FailLevel("Watch out for anvils!"); + return; + } + else if (e.Actor.IsMobile()) { + // Destroy mobile doodads. + Sound.Play("crumbly-break.wav"); + e.Actor.Destroy(); + } + } + } + }); + + // When we receive power, we reset to our original position. + var origPoint = Self.Position(); + Message.Subscribe("power", function (powered) { + Self.MoveTo(origPoint); + Self.SetVelocity(Vector(0, 0)); + }); +} diff --git a/dev-assets/doodads/objects/anvil.png b/dev-assets/doodads/objects/anvil.png new file mode 100644 index 0000000..205becc Binary files /dev/null and b/dev-assets/doodads/objects/anvil.png differ diff --git a/dev-assets/doodads/switches/switch.js b/dev-assets/doodads/switches/switch.js index 88e8eaf..973a7b3 100644 --- a/dev-assets/doodads/switches/switch.js +++ b/dev-assets/doodads/switches/switch.js @@ -7,13 +7,13 @@ function main() { var state = false; var collide = false; - Message.Subscribe("power", function(powered) { + Message.Subscribe("power", function (powered) { state = powered; showState(state); }); - Events.OnCollide(function(e) { - if (!e.Settled) { + Events.OnCollide(function (e) { + if (!e.Settled || !e.Actor.IsMobile()) { return; } @@ -30,7 +30,7 @@ function main() { } }); - Events.OnLeave(function(e) { + Events.OnLeave(function (e) { collide = false; }); } diff --git a/dev-assets/doodads/trapdoors/electric-trapdoor.js b/dev-assets/doodads/trapdoors/electric-trapdoor.js index 2a33057..836f662 100644 --- a/dev-assets/doodads/trapdoors/electric-trapdoor.js +++ b/dev-assets/doodads/trapdoors/electric-trapdoor.js @@ -49,6 +49,11 @@ function setPoweredState(powered) { Self.PlayAnimation("open", function () { isOpen = true; animating = false; + + // Had we lost power quickly? + if (!powerState) { + setPoweredState(false); + } }); } else { animating = true; diff --git a/docs/Doodad Scripts.md b/docs/Doodad Scripts.md index 1a5271a..79cab8d 100644 --- a/docs/Doodad Scripts.md +++ b/docs/Doodad Scripts.md @@ -30,6 +30,7 @@ type API interface { // Game functions.k EndLevel() // Exit the current level with a victory + FailLevel(message) /************************************ * Event Handler Callback Functions * diff --git a/pkg/play_scene.go b/pkg/play_scene.go index 64ed4eb..2870a38 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -41,6 +41,7 @@ type PlayScene struct { // buttons what to do next. alertBox *ui.Window alertBoxLabel *ui.Label + alertBoxValue string alertReplayButton *ui.Button // Replay level alertEditButton *ui.Button // Edit Level alertNextButton *ui.Button // Next Level @@ -118,6 +119,29 @@ func (s *PlayScene) setupAsync(d *Doodle) error { s.alertExitButton.Show() // Show the alert box. + s.alertBox.Title = "Level Completed" + s.alertBoxValue = "Congratulations on clearing the level!" + s.alertBox.Show() + }) + s.scripting.OnLevelFail(func(message string) { + d.Flash(message) + + // Pause the simulation. + s.running = false + + // Toggle the relevant buttons on. + if s.CanEdit { + s.alertEditButton.Show() + } + s.alertNextButton.Hide() + + // Always-visible buttons. + s.alertReplayButton.Show() + s.alertExitButton.Show() + + // Show the alert box. + s.alertBox.Title = "You've died!" + s.alertBoxValue = message s.alertBox.Show() }) @@ -310,8 +334,8 @@ func (s *PlayScene) SetupAlertbox() { ******************/ s.alertBoxLabel = ui.NewLabel(ui.Label{ - Text: "Congratulations on clearing the level!", - Font: balance.LabelFont, + TextVariable: &s.alertBoxValue, + Font: balance.LabelFont, }) frame.Pack(s.alertBoxLabel, ui.Pack{ Side: ui.N, @@ -390,7 +414,7 @@ func (s *PlayScene) RestartLevel() { func (s *PlayScene) DieByFire(name string) { log.Info("Watch out for %s!", name) s.alertBox.Title = "You've died!" - s.alertBoxLabel.Text = fmt.Sprintf("Watch out for %s!", name) + s.alertBoxValue = fmt.Sprintf("Watch out for %s!", name) s.alertReplayButton.Show() if s.CanEdit { diff --git a/pkg/scripting/pubsub.go b/pkg/scripting/pubsub.go index 8324698..ae2a2fd 100644 --- a/pkg/scripting/pubsub.go +++ b/pkg/scripting/pubsub.go @@ -19,6 +19,8 @@ This adds the global methods `Message.Subscribe(name, func)` and `Message.Publish(name, args)` to the JavaScript VM's scope. */ func RegisterPublishHooks(s *Supervisor, vm *VM) { + log.Error("RegisterPublishHooks called with %+v %+v", s, vm) + // Goroutine to watch the VM's inbound channel and invoke Subscribe handlers // for any matching messages received. go func() { diff --git a/pkg/scripting/scripting.go b/pkg/scripting/scripting.go index 90fa1b8..ab98a6d 100644 --- a/pkg/scripting/scripting.go +++ b/pkg/scripting/scripting.go @@ -17,6 +17,7 @@ type Supervisor struct { // Global event handlers. onLevelExit func() + onLevelFail func(message string) } // NewSupervisor creates a new JavaScript Supervior. diff --git a/pkg/scripting/supervisor_events.go b/pkg/scripting/supervisor_events.go index ca2a80a..12eacf9 100644 --- a/pkg/scripting/supervisor_events.go +++ b/pkg/scripting/supervisor_events.go @@ -10,14 +10,25 @@ Names registered: */ func RegisterEventHooks(s *Supervisor, vm *VM) { vm.Set("EndLevel", func() { - if s.onLevelExit == nil { - panic("JS EndLevel(): no OnLevelExit handler attached to script supervisor") + if s.onLevelFail == nil { + panic("JS FailLevel(): No OnLevelFail handler attached to script supervisor") } s.onLevelExit() }) + vm.Set("FailLevel", func(message string) { + if s.onLevelFail == nil { + panic("JS FailLevel(): No OnLevelFail handler attached to script supervisor") + } + s.onLevelFail(message) + }) } // OnLevelExit registers an event hook for when a Level Exit doodad is reached. func (s *Supervisor) OnLevelExit(handler func()) { s.onLevelExit = handler } + +// OnLevelFail registers an event hook for level failures (doodads killing the player). +func (s *Supervisor) OnLevelFail(handler func(string)) { + s.onLevelFail = handler +} diff --git a/pkg/uix/actor_collision.go b/pkg/uix/actor_collision.go index e6a2acf..940acf9 100644 --- a/pkg/uix/actor_collision.go +++ b/pkg/uix/actor_collision.go @@ -207,6 +207,7 @@ func (w *Canvas) loopActorCollision() error { // Are they on top? aHitbox := collision.SizePlusHitbox(collision.GetBoundingRect(a), a.Hitbox()) if render.AbsInt(test.Y+test.H-aHitbox.Y) == 0 { + // log.Error("ActorCollision: onTop=true at Y=%s", test.Y) onTop = true onTopY = test.Y } @@ -217,6 +218,7 @@ func (w *Canvas) loopActorCollision() error { lockY = lastGoodBox.Y } if onTop { + // log.Error("ActorCollision: setGrounded(true)", test.Y) b.SetGrounded(true) } }