doodle/pkg/uix/scripting.go
Noah Petherbridge c4456ac51b Scripting: Bring Self API up to Actor API parity
Updates the Self API to expose more uix.Actor fields:

* Doodad()
* GetBoundingRect()
* HasGravity()
* IsFrozen()
* IsMobile()
* LayerCount()
* ListItems()
* SetGrounded()
* SetWet()
* Velocity() - note: it was Self.GetVelocity() before.

Self.GetVelocity is deprecated and made an alias to Velocity.
2024-04-26 22:34:13 -07:00

190 lines
5.2 KiB
Go

package uix
import (
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/scripting"
"git.kirsle.net/go/render"
)
// Functions relating to the Doodad JavaScript API for Canvas Actors.
// MakeScriptAPI makes several useful globals available to doodad
// scripts such as Actors.At()
func (w *Canvas) MakeScriptAPI(vm *scripting.VM) {
vm.Set("Actors", map[string]interface{}{
// Actors.At(Point)
"At": func(p render.Point) []*Actor {
var result = []*Actor{}
for _, actor := range w.actors {
var box = actor.Hitbox().AddPoint(actor.Position())
if actor != nil && p.Inside(box) {
result = append(result, actor)
}
}
return result
},
// Actors.FindPlayer: returns the nearest player character.
"FindPlayer": func() *Actor {
for _, actor := range w.actors {
if actor.IsPlayer() {
return actor
}
}
return nil
},
// Actors.CameraFollowPlayer tells the camera to follow the player character.
"CameraFollowPlayer": func() {
for _, actor := range w.actors {
if actor.IsPlayer() {
w.FollowActor = actor.ID()
}
}
},
// Actors.New: create a new actor.
"New": func(filename string) *Actor {
doodad, err := doodads.LoadFile(filename)
if err != nil {
panic(err)
}
actor := NewActor("_new", &level.Actor{}, doodad)
w.AddActor(actor)
// Set up the player character's script in the VM.
if err := w.scripting.AddLevelScript(actor.ID(), filename); err != nil {
log.Error("Actors.New(%s): scripting.InstallActor(player) failed: %s", filename, err)
}
return actor
},
// Actors.SetPlayerCharacter: remake the player character.
"SetPlayerCharacter": func(filename string) {
if w.OnSetPlayerCharacter != nil {
w.OnSetPlayerCharacter(filename)
} else {
log.Error("Actors.SetPlayerCharacter: caller was not ready")
}
},
})
vm.Set("Level", map[string]interface{}{
"Difficulty": w.level.GameRule.Difficulty,
"ResetTimer": func() {
if w.OnResetTimer != nil {
w.OnResetTimer()
} else {
log.Error("Level.ResetTimer: caller was not ready")
}
},
})
}
// MakeSelfAPI generates the `Self` object for the scripting API in
// reference to a live Canvas actor in the level.
func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
return map[string]interface{}{
"Filename": actor.Doodad().Filename,
"Title": actor.Doodad().Title,
/*
Unique options to the Self API over regular Actors.
*/
"GetLinks": func() []map[string]interface{} {
var result = []map[string]interface{}{}
for _, linked := range w.GetLinkedActors(actor) {
result = append(result, w.MakeSelfAPI(linked))
}
return result
},
// Attract the camera's attention.
"CameraFollowMe": func() {
// Update the doodad that the camera should focus on.
w.FollowActor = actor.ID()
},
// functions
"GetTag": actor.Doodad().Tag,
/*
Expose the uix.Actor API surface area, in the same order
that these functions are defined in their respective files
to keep it easy to maintain.
*/
// actor.go
"ID": actor.ID,
"Doodad": actor.Doodad,
"SetGravity": actor.SetGravity,
"SetMobile": actor.SetMobile,
"SetInventory": actor.SetInventory,
"IsMobile": actor.IsMobile,
"IsPlayer": actor.IsPlayer,
"HasInventory": actor.HasInventory,
"HasGravity": actor.HasGravity,
"Invulnerable": actor.Invulnerable,
"SetInvulnerable": actor.SetInvulnerable,
"IsWet": actor.IsWet,
"IsOnScreen": actor.IsOnScreen,
"SetWet": actor.SetWet,
"Size": actor.Size,
"Velocity": actor.Velocity,
"GetVelocity": actor.Velocity, // TODO: deprecated, old Self.GetVelocity inconsistency
"SetVelocity": actor.SetVelocity,
"Position": actor.Position,
"MoveTo": func(p render.Point) {
actor.MoveTo(p)
actor.SetGrounded(false)
},
"MoveBy": func(p render.Point) {
actor.MoveBy(p)
actor.SetGrounded(false)
},
"Grounded": actor.Grounded,
"SetGrounded": actor.SetGrounded,
"IsCoyoteTime": actor.IsCoyoteTime,
"Hide": actor.Hide,
"Show": actor.Show,
"Freeze": actor.Freeze,
"Unfreeze": actor.Unfreeze,
"IsFrozen": actor.IsFrozen,
// Not: SetUsing, SetNoclip
"AddItem": actor.AddItem,
"RemoveItem": actor.RemoveItem,
"ClearInventory": actor.ClearInventory,
"HasItem": actor.HasItem,
"ListItems": actor.ListItems,
"Inventory": actor.Inventory,
"GetBoundingRect": actor.GetBoundingRect,
"SetHitbox": actor.SetHitbox,
"Hitbox": actor.Hitbox,
"Options": actor.Options,
"GetOption": func(name string) interface{} {
opt := actor.GetOption(name)
if opt == nil {
return nil
}
return opt.Value
},
"LayerCount": actor.LayerCount,
"ShowLayer": actor.ShowLayer,
"ShowLayerNamed": actor.ShowLayerNamed,
"Destroy": actor.Destroy,
// actor_animation.go
"AddAnimation": actor.AddAnimation,
"PlayAnimation": actor.PlayAnimation,
"IsAnimating": actor.IsAnimating,
"StopAnimation": actor.StopAnimation,
}
}