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.
This commit is contained in:
Noah 2024-04-26 22:34:13 -07:00
parent 21847f5e57
commit c4456ac51b
3 changed files with 146 additions and 100 deletions

View File

@ -49,6 +49,16 @@ game:
* If you receive a map with custom doodads, you can **install** the doodads * If you receive a map with custom doodads, you can **install** the doodads
into your copy of the game and use them in your own maps. into your copy of the game and use them in your own maps.
# Developer Documentation
In case you are reading this from the game's open source repository, take a
look in the `docs/` folder or the `*.md` files in the root of the repository.
Some to start with:
* [Building](Building.md) the game (tl;dr. run bootstrap.py)
* [Tour of the Code](docs/Tour%20of%20the%20Code.md)
# Keybindings # Keybindings
Global Keybindings: Global Keybindings:
@ -134,52 +144,6 @@ four directions but characters with gravity only move left and right
and have the dedicated "Jump" button. This differs from regular and have the dedicated "Jump" button. This differs from regular
keyboard controls where the Up arrow is to Jump. keyboard controls where the Up arrow is to Jump.
# Built-In Doodads
A brief introduction to the built-in doodads available so far:
- **Characters**
- Blue Azulian: this is used as the player character for now. If
dragged into a level, it doesn't do anything but is affected
by gravity.
- Red Azulian: an example mobile mob for now. It walks back and
forth, changing directions only when it comes across an
obstacle and can't proceed any further.
- **Doors and Keys**
- Colored Doors: these act like solid barriers until the player or
another doodad collects the matching colored key.
- Colored Keys: these are collectable items that allow the player or
another doodad to open the door of matching color. Note that
inventories are doodad-specific, so other mobs besides the
player can steal a key before the player gets it! (For now)
- Electric Door: this mechanical door stays closed and only
opens when it receives a power signal from a linked button.
- Trapdoor: this door allows one-way access, but once it's closed
behind you, you can't pass through it from that side!
- **Buttons**
- Button: while pressed by a player or other doodad, the button
emits a power signal to any doodad it is linked to. Link a button
to an electric door, and the door will open while the button is
pressed and close when the button is released.
- Sticky Button: this button will stay pressed once activated and
will not release unless it receives a power signal from another
linked doodad. For example, one Button that links to a Sticky
Button will release the sticky button if pressed.
- **Switches**
- Switch: when touched by the player or other doodad, the switch will
toggle its state from "OFF" to "ON" or vice versa. Link it to an
Electric Door to open/close the door. Link switches _to each other_ as
well as to a door, and all switches will stay in sync with their ON/OFF
state when any switch is pressed.
- **Crumbly Floor**
- This rocky floor will break and fall away after being stepped on.
- **Two State Blocks**
- Blue and orange blocks that will toggle between solid and pass-thru
whenever the corresponding ON/OFF block is hit.
- **Start and Exit Flags**
- The "Go" flag lets you pick a spawn point for the player character.
- The "Exit" flag marks the level goal.
# Developer Console # Developer Console
Press `Enter` at any time to open the developer console. The console Press `Enter` at any time to open the developer console. The console

57
docs/Tour of the Code.md Normal file
View File

@ -0,0 +1,57 @@
# Tour of the Code
Here is a brief tour of where to find important parts of the game's code.
## Basic App Structure
**cmd/doodle/main.go** is the main function for Sketchy Maze and handles the command line flags and bootstrapping the game.
**pkg/doodle.go** holds the root Doodle (`d`) struct, the base object for the meat of the program - its setup funcs and MainLoop are here.
### Scenes
The gameplay is divided into scenes, each in a **pkg/\*_scene.go** file:
* MainScene is the title screen (default)
* PlayScene is play mode
* EditScene is the level editor, etc.
**pkg/scene.go** defines the interface for Scene itself and the `d.Goto(scene)` function.
The game changes scenes with Goto and the MainLoop calls the active scene's functions (Loop and Draw).
## Doodad JavaScript API
### pkg/scripting
This package provides the generic JavaScript API used by the developer console and doodads.
**pkg/scripting/js_api.go** defines global and generic JavaScript functions to doodads' scope, e.g.:
* console.log and friends
* Sound.Play
* RGBA, Point, and Vector
* Flash, GetTick
* time.Now() and friends
* Events
* setTimeout, setInterval..
* Self: provided externally.
### pkg/uix/scripting.go
The home of **MakeScriptAPI()** which installs various Play Mode objects into the script's environment, e.g.
* Actors and Level API
* Self: a surface of uix.Actor capabilities that point to the local actor.
## Collision Detection
### pkg/collision
The algorithmic stuff is here - the collision package focused on just the math and general types to handle logic for:
* Collision with level geometry ('pixel perfect')
* Collision between actors
* BoundingRect and collision boxes
### pkg/uix/actor_collision.go

View File

@ -93,60 +93,10 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
"Filename": actor.Doodad().Filename, "Filename": actor.Doodad().Filename,
"Title": actor.Doodad().Title, "Title": actor.Doodad().Title,
// functions /*
"ID": actor.ID, Unique options to the Self API over regular Actors.
"Size": actor.Size, */
"GetTag": actor.Doodad().Tag,
"Options": actor.Options,
"GetOption": func(name string) interface{} {
opt := actor.GetOption(name)
if opt == nil {
return nil
}
return opt.Value
},
"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)
},
"IsOnScreen": actor.IsOnScreen,
// // TODO: passing this to actor.IsOnScreen didn't work?
// return actor.Position().Inside(actor.Canvas.ViewportRelative())
// },
"Grounded": actor.Grounded,
"SetHitbox": actor.SetHitbox,
"Hitbox": actor.Hitbox,
"SetVelocity": actor.SetVelocity,
"GetVelocity": actor.Velocity,
"SetMobile": actor.SetMobile,
"SetInventory": actor.SetInventory,
"HasInventory": actor.HasInventory,
"SetGravity": actor.SetGravity,
"Invulnerable": actor.Invulnerable,
"SetInvulnerable": actor.SetInvulnerable,
"AddAnimation": actor.AddAnimation,
"IsAnimating": actor.IsAnimating,
"IsPlayer": actor.IsPlayer,
"PlayAnimation": actor.PlayAnimation,
"StopAnimation": actor.StopAnimation,
"ShowLayer": actor.ShowLayer,
"ShowLayerNamed": actor.ShowLayerNamed,
"Inventory": actor.Inventory,
"AddItem": actor.AddItem,
"RemoveItem": actor.RemoveItem,
"HasItem": actor.HasItem,
"ClearInventory": actor.ClearInventory,
"Destroy": actor.Destroy,
"Freeze": actor.Freeze,
"Unfreeze": actor.Unfreeze,
"IsWet": actor.IsWet,
"Hide": actor.Hide,
"Show": actor.Show,
"GetLinks": func() []map[string]interface{} { "GetLinks": func() []map[string]interface{} {
var result = []map[string]interface{}{} var result = []map[string]interface{}{}
for _, linked := range w.GetLinkedActors(actor) { for _, linked := range w.GetLinkedActors(actor) {
@ -160,5 +110,80 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
// Update the doodad that the camera should focus on. // Update the doodad that the camera should focus on.
w.FollowActor = actor.ID() 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,
} }
} }