Noah Petherbridge
fc736abd5f
Adds several new doodads to the game and 5 new wallpapers (parchment paper in blue, green, red, white and yellow). New doodads: * Crusher: A purple block-headed mob wearing an iron helmet. It tries to crush the player when you get underneath. Its flat helmet can be ridden on like an elevator back up. * Snake: A green stationary mob that always faces toward the player. If the player is nearby and jumps, the Snake will jump too and hope to catch the player in mid-air. * Gems and Totems: A new key & lock collectible. Gems have quantity so you can collect multiple, and place them into matching Totems. A Totem gives off a power signal when its gem is placed and all other Totems it is linked to have also been activated. A single Totem may link to an Electric Door and require only one gem to open it, or it can link to other Totems and they all require gems before the power signal is sent out.
100 lines
2.3 KiB
Go
100 lines
2.3 KiB
Go
package scripting
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/log"
|
|
"github.com/dop251/goja"
|
|
)
|
|
|
|
// VM manages a single isolated JavaScript VM.
|
|
type VM struct {
|
|
Name string
|
|
|
|
// Globals available to the scripts.
|
|
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
|
|
stop chan bool
|
|
subscribe map[string][]goja.Value // Subscribed message handlers by name.
|
|
muSubscribe sync.RWMutex
|
|
muPublish sync.Mutex // serialize PubSub publishes
|
|
|
|
vm *goja.Runtime
|
|
|
|
// setTimeout and setInterval variables.
|
|
timerLastID int // becomes 1 when first timer is set
|
|
timers map[int]*Timer
|
|
}
|
|
|
|
// NewVM creates a new JavaScript VM.
|
|
func NewVM(name string) *VM {
|
|
vm := &VM{
|
|
Name: name,
|
|
vm: goja.New(),
|
|
timers: map[int]*Timer{},
|
|
|
|
// Pub/sub structs.
|
|
Inbound: make(chan Message, 100),
|
|
Outbound: []chan Message{},
|
|
stop: make(chan bool, 1),
|
|
subscribe: map[string][]goja.Value{},
|
|
}
|
|
vm.Events = NewEvents(vm.vm)
|
|
return vm
|
|
}
|
|
|
|
// Run code in the VM.
|
|
func (vm *VM) Run(src string) (goja.Value, error) {
|
|
v, err := vm.vm.RunString(src)
|
|
return v, err
|
|
}
|
|
|
|
// Set a value in the VM.
|
|
func (vm *VM) Set(name string, v interface{}) error {
|
|
return vm.vm.Set(name, v)
|
|
}
|
|
|
|
// RegisterLevelHooks registers accessors to the level hooks
|
|
// and Doodad API for Play Mode.
|
|
func (vm *VM) RegisterLevelHooks() error {
|
|
bindings := NewJSProxy(vm)
|
|
for name, v := range bindings {
|
|
err := vm.vm.Set(name, v)
|
|
if err != nil {
|
|
return fmt.Errorf("RegisterLevelHooks(%s): %s",
|
|
name, err,
|
|
)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Main calls the main function of the script.
|
|
func (vm *VM) Main() error {
|
|
function, ok := goja.AssertFunction(vm.vm.Get("main"))
|
|
if !ok {
|
|
return errors.New("didn't find function main()")
|
|
}
|
|
|
|
// Catch panics.
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
log.Error("Panic caught in JavaScript VM: %s", err)
|
|
}
|
|
}()
|
|
|
|
_, err := function(goja.Undefined())
|
|
return err
|
|
}
|