diff --git a/dev-assets/doodads/azulian/azulian.js b/dev-assets/doodads/azulian/azulian.js index 40291ca..5178cc7 100644 --- a/dev-assets/doodads/azulian/azulian.js +++ b/dev-assets/doodads/azulian/azulian.js @@ -1,9 +1,6 @@ function main() { log.Info("Azulian '%s' initialized!", Self.Doodad.Title); - - Self.Canvas.SetBackground(RGBA(0, 153, 255, 100)); - var playerSpeed = 12; var gravity = 4; var Vx = Vy = 0; diff --git a/dev-assets/doodads/buttons/button.js b/dev-assets/doodads/buttons/button.js index 4cb1d9e..5ae0a17 100644 --- a/dev-assets/doodads/buttons/button.js +++ b/dev-assets/doodads/buttons/button.js @@ -6,11 +6,10 @@ function main() { Events.OnCollide(function(e) { // Verify they've touched the button. if (e.Overlap.Y + e.Overlap.H < 24) { - Self.Canvas.SetBackground(RGBA(0, 255, 0, 153)); return; } - Self.Canvas.SetBackground(RGBA(255, 255, 0, 153)); + Message.Publish("power", true); if (timer > 0) { clearTimeout(timer); @@ -19,12 +18,13 @@ function main() { Self.ShowLayer(1); timer = setTimeout(function() { Self.ShowLayer(0); + Message.Publish("power", false); timer = 0; }, 200); }); - Events.OnLeave(function(e) { - console.log("%s has stopped touching %s", e, Self.Doodad.Title) - Self.Canvas.SetBackground(RGBA(0, 0, 1, 0)); - }) + // Events.OnLeave(function(e) { + // console.log("%s has stopped touching %s", e, Self.Doodad.Title) + // Self.Canvas.SetBackground(RGBA(0, 0, 1, 0)); + // }) } diff --git a/dev-assets/doodads/buttons/sticky.js b/dev-assets/doodads/buttons/sticky.js index a3e1373..bd29aee 100644 --- a/dev-assets/doodads/buttons/sticky.js +++ b/dev-assets/doodads/buttons/sticky.js @@ -3,6 +3,15 @@ function main() { var pressed = false; + // When a sticky button receives power, it pops back up. + Message.Subscribe("power", function(powered) { + if (powered && pressed) { + Self.ShowLayer(0); + pressed = false; + Message.Publish("power", false); + } + }) + Events.OnCollide(function(e) { if (pressed) { return; @@ -15,5 +24,6 @@ function main() { Self.ShowLayer(1); pressed = true; + Message.Publish("power", true); }); } diff --git a/dev-assets/doodads/doors/electric-door.js b/dev-assets/doodads/doors/electric-door.js index b9ae2b7..cd028b7 100644 --- a/dev-assets/doodads/doors/electric-door.js +++ b/dev-assets/doodads/doors/electric-door.js @@ -6,24 +6,42 @@ function main() { var animating = false; var opened = false; - Events.OnCollide(function(e) { - if (animating || opened) { - return; - } + Self.SetHitbox(16, 0, 32, 64); + + Message.Subscribe("power", function(powered) { + console.log("%s got power=%+v", Self.Doodad.Title, powered); + + if (powered) { + if (animating || opened) { + return; + } - if (e.Overlap.X + e.Overlap.W >= 16 && e.Overlap.X < 48) { animating = true; Self.PlayAnimation("open", function() { opened = true; animating = false; }); + } else { + animating = true; + Self.PlayAnimation("close", function() { + opened = false; + animating = false; + }) + } + }); + + Events.OnCollide(function(e) { + if (e.InHitbox) { + if (!opened) { + return false; + } } }); Events.OnLeave(function() { - if (opened) { - Self.PlayAnimation("close", function() { - opened = false; - }); - } + // if (opened) { + // Self.PlayAnimation("close", function() { + // opened = false; + // }); + // } }) } diff --git a/dev-assets/doodads/doors/locked-door.js b/dev-assets/doodads/doors/locked-door.js index fee9610..28f6d70 100644 --- a/dev-assets/doodads/doors/locked-door.js +++ b/dev-assets/doodads/doors/locked-door.js @@ -2,7 +2,7 @@ function main() { Self.AddAnimation("open", 0, [1]); var unlocked = false; - Self.Canvas.SetBackground(RGBA(0, 255, 255, 100)); + // Self.Canvas.SetBackground(RGBA(0, 255, 255, 100)); // Map our door names to key names. var KeyMap = { @@ -12,8 +12,8 @@ function main() { "Yellow Door": "Yellow Key" } - log.Warn("%s loaded!", Self.Doodad.Title); - console.log("%s Setting hitbox", Self.Doodad.Title); + // log.Warn("%s loaded!", Self.Doodad.Title); + // console.log("%s Setting hitbox", Self.Doodad.Title); Self.SetHitbox(16, 0, 32, 64); Events.OnCollide(function(e) { @@ -32,7 +32,7 @@ function main() { Self.PlayAnimation("open", null); } }); - Events.OnLeave(function(e) { - console.log("%s has stopped touching %s", e, Self.Doodad.Title) - }) + // Events.OnLeave(function(e) { + // console.log("%s has stopped touching %s", e, Self.Doodad.Title) + // }) } diff --git a/pkg/scripting/pubsub.go b/pkg/scripting/pubsub.go new file mode 100644 index 0000000..5876ad9 --- /dev/null +++ b/pkg/scripting/pubsub.go @@ -0,0 +1,60 @@ +package scripting + +import ( + "git.kirsle.net/apps/doodle/pkg/log" + "github.com/robertkrimen/otto" +) + +// Message holds data being published from one script VM with information sent +// to the linked VMs. +type Message struct { + Name string + Args []interface{} +} + +/* +RegisterPublishHooks adds the pub/sub hooks to a JavaScript VM. + +This adds the global methods `Message.Subscribe(name, func)` and +`Message.Publish(name, args)` to the JavaScript VM's scope. +*/ +func RegisterPublishHooks(vm *VM) { + // Goroutine to watch the VM's inbound channel and invoke Subscribe handlers + // for any matching messages received. + go func() { + for msg := range vm.Inbound { + log.Warn("vm: %s msg: %+v", vm.Name, msg) + if _, ok := vm.subscribe[msg.Name]; ok { + for _, callback := range vm.subscribe[msg.Name] { + callback.Call(otto.Value{}, msg.Args...) + } + } + } + }() + + // Register the Message.Subscribe and Message.Publish functions. + vm.vm.Set("Message", map[string]interface{}{ + "Subscribe": func(name string, callback otto.Value) { + log.Error("SUBSCRIBE: %s", name) + if !callback.IsFunction() { + log.Error("SUBSCRIBE(%s): callback is not a function", name) + return + } + if _, ok := vm.subscribe[name]; !ok { + vm.subscribe[name] = []otto.Value{} + } + + vm.subscribe[name] = append(vm.subscribe[name], callback) + }, + + "Publish": func(name string, v ...interface{}) { + log.Error("PUBLISH: %s %+v", name, v) + for _, channel := range vm.Outbound { + channel <- Message{ + Name: name, + Args: v, + } + } + }, + }) +} diff --git a/pkg/scripting/scripting.go b/pkg/scripting/scripting.go index 6d79e01..001fcc3 100644 --- a/pkg/scripting/scripting.go +++ b/pkg/scripting/scripting.go @@ -40,6 +40,20 @@ func (s *Supervisor) InstallScripts(level *level.Level) error { return err } } + + // Loop again to bridge channels together for linked VMs. + for _, actor := range level.Actors { + // Add linked actor IDs. + if len(actor.Links) > 0 { + // Bridge the links up. + var thisVM = s.scripts[actor.ID()] + for _, id := range actor.Links { + // Assign this target actor's Inbound channel to the source + // actor's array of Outbound channels. + thisVM.Outbound = append(thisVM.Outbound, s.scripts[id].Inbound) + } + } + } return nil } @@ -52,6 +66,7 @@ func (s *Supervisor) AddLevelScript(id string) error { } s.scripts[id] = NewVM(id) + RegisterPublishHooks(s.scripts[id]) if err := s.scripts[id].RegisterLevelHooks(); err != nil { return err } diff --git a/pkg/scripting/vm.go b/pkg/scripting/vm.go index 63d0082..34b86e3 100644 --- a/pkg/scripting/vm.go +++ b/pkg/scripting/vm.go @@ -18,6 +18,16 @@ type VM struct { Events *Events Self interface{} + // Channels for inbound and outbound PubSub messages. + // Each VM has a single Inbound channel that watches for received messages + // and invokes the Message.Subscribe() handlers for relevant ones. + // Each VM also has an array of Outbound channels which map to the Inbound + // channel of the VMs it is linked to, for pushing out Message.Publish() + // messages. + Inbound chan Message + Outbound []chan Message + subscribe map[string][]otto.Value // Subscribed message handlers by name. + vm *otto.Otto // setTimeout and setInterval variables. @@ -32,6 +42,11 @@ func NewVM(name string) *VM { Events: NewEvents(), vm: otto.New(), timers: map[int]*Timer{}, + + // Pub/sub structs. + Inbound: make(chan Message), + Outbound: []chan Message{}, + subscribe: map[string][]otto.Value{}, } return vm }