doodle/pkg/uix/actor.go

458 lines
12 KiB
Go
Raw Permalink Normal View History

package uix
import (
"errors"
"fmt"
"sort"
"sync"
2024-02-07 04:56:07 +00:00
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
2022-09-24 22:17:25 +00:00
"git.kirsle.net/SketchyMaze/doodle/pkg/collision"
"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/physics"
2024-02-07 04:56:07 +00:00
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
"git.kirsle.net/go/render"
"github.com/dop251/goja"
"github.com/google/uuid"
)
// Actor is an object that marries together the three things that make a
// Doodad instance "tick" while inside a Canvas:
//
// - uix.Actor is a doodads.Drawing so it fulfills doodads.Actor to be a
// dynamic object during gameplay.
// - It has a pointer to the level.Actor indicating its static level data
// as defined in the map: its spawn coordinate and configuration.
// - A uix.Canvas that can present the actor's graphics to the screen.
type Actor struct {
Drawing *doodads.Drawing
Actor *level.Actor
Canvas *Canvas
LevelCanvas *Canvas // the parent Canvas of the level we reside in
activeLayer int // active drawing frame for display
flagDestroy bool // flag the actor for destruction
flagUsing bool // flag that the (player) has pressed the Use key.
// Actor runtime variables.
Thief and Inventory APIs This commit adds the Thief character with starter graphics (no animations). The Thief walks back and forth and will steal items from other doodads, including the player. For singleton items that have no quantity, like the Colored Keys, the Thief will only steal one if he does not already have it. Quantitied items like the Small Key are always stolen. Flexibility in the playable character is introduced: Boy, Azulian, Bird, and Thief all respond to playable controls. There is not currently a method to enable these apart from modifying balance.PlayerCharacterDoodad at compile time. New and Changed Doodads * Thief: new doodad that walks back and forth and will steal items from other characters inventory. * Bird: has no inventory and cannot pick up items, unless player controlled. Its hitbox has also been fixed so it collides with floors correctly - not something normally seen in the Bird. * Boy: opts in to have inventory. * Keys (all): only gives themselves to actors having inventories. JavaScript API - New functions available * Self.IsPlayer() - returns if the current actor IS the player. * Self.SetInventory(bool) - doodads must opt-in to having an inventory. Keys should only give themselves to doodads having an inventory. * Self.HasInventory() bool * Self.AddItem(filename, qty) * Self.RemoveItem(filename, qty) * Self.HasItem(filename) * Self.Inventory() - returns map[string]int * Self.ClearInventory() * Self.OnLeave(func(e)) now receives a CollideEvent as parameter instead of the useless actor ID. Notably, e.Actor is the leaving actor and e.Settled is always true. Other Changes * Play Mode: if playing as a character which doesn't obey gravity, such as the bird, antigravity controls are enabled by default. If you `import antigravity` you can turn gravity back on. * Doodad collision scripts are no longer run in parallel goroutines. It made the Thief's job difficult trying to steal items in many threads simultaneously!
2021-08-10 05:42:22 +00:00
hasGravity bool
hasInventory bool
wet bool
Thief and Inventory APIs This commit adds the Thief character with starter graphics (no animations). The Thief walks back and forth and will steal items from other doodads, including the player. For singleton items that have no quantity, like the Colored Keys, the Thief will only steal one if he does not already have it. Quantitied items like the Small Key are always stolen. Flexibility in the playable character is introduced: Boy, Azulian, Bird, and Thief all respond to playable controls. There is not currently a method to enable these apart from modifying balance.PlayerCharacterDoodad at compile time. New and Changed Doodads * Thief: new doodad that walks back and forth and will steal items from other characters inventory. * Bird: has no inventory and cannot pick up items, unless player controlled. Its hitbox has also been fixed so it collides with floors correctly - not something normally seen in the Bird. * Boy: opts in to have inventory. * Keys (all): only gives themselves to actors having inventories. JavaScript API - New functions available * Self.IsPlayer() - returns if the current actor IS the player. * Self.SetInventory(bool) - doodads must opt-in to having an inventory. Keys should only give themselves to doodads having an inventory. * Self.HasInventory() bool * Self.AddItem(filename, qty) * Self.RemoveItem(filename, qty) * Self.HasItem(filename) * Self.Inventory() - returns map[string]int * Self.ClearInventory() * Self.OnLeave(func(e)) now receives a CollideEvent as parameter instead of the useless actor ID. Notably, e.Actor is the leaving actor and e.Settled is always true. Other Changes * Play Mode: if playing as a character which doesn't obey gravity, such as the bird, antigravity controls are enabled by default. If you `import antigravity` you can turn gravity back on. * Doodad collision scripts are no longer run in parallel goroutines. It made the Thief's job difficult trying to steal items in many threads simultaneously!
2021-08-10 05:42:22 +00:00
isMobile bool // Mobile character, such as the player or an enemy
noclip bool // Disable collision detection
hidden bool // invisible, via Hide() and Show()
frozen bool // Frozen, via Freeze() and Unfreeze()
immortal bool // Invulnerable to damage
Thief and Inventory APIs This commit adds the Thief character with starter graphics (no animations). The Thief walks back and forth and will steal items from other doodads, including the player. For singleton items that have no quantity, like the Colored Keys, the Thief will only steal one if he does not already have it. Quantitied items like the Small Key are always stolen. Flexibility in the playable character is introduced: Boy, Azulian, Bird, and Thief all respond to playable controls. There is not currently a method to enable these apart from modifying balance.PlayerCharacterDoodad at compile time. New and Changed Doodads * Thief: new doodad that walks back and forth and will steal items from other characters inventory. * Bird: has no inventory and cannot pick up items, unless player controlled. Its hitbox has also been fixed so it collides with floors correctly - not something normally seen in the Bird. * Boy: opts in to have inventory. * Keys (all): only gives themselves to actors having inventories. JavaScript API - New functions available * Self.IsPlayer() - returns if the current actor IS the player. * Self.SetInventory(bool) - doodads must opt-in to having an inventory. Keys should only give themselves to doodads having an inventory. * Self.HasInventory() bool * Self.AddItem(filename, qty) * Self.RemoveItem(filename, qty) * Self.HasItem(filename) * Self.Inventory() - returns map[string]int * Self.ClearInventory() * Self.OnLeave(func(e)) now receives a CollideEvent as parameter instead of the useless actor ID. Notably, e.Actor is the leaving actor and e.Settled is always true. Other Changes * Play Mode: if playing as a character which doesn't obey gravity, such as the bird, antigravity controls are enabled by default. If you `import antigravity` you can turn gravity back on. * Doodad collision scripts are no longer run in parallel goroutines. It made the Thief's job difficult trying to steal items in many threads simultaneously!
2021-08-10 05:42:22 +00:00
hitbox render.Rect
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
inventory map[string]int // item inventory. doodad name -> quantity, 0 for key item.
// Movement data.
2024-02-07 04:56:07 +00:00
position render.Point
velocity physics.Vector
grounded bool
lostGroundAt uint64 // tick where grounded last became false, for coyote time
// Animation variables.
animations map[string]*Animation
activeAnimation *Animation
animationCallback goja.Value
// Mutex.
muInventory sync.RWMutex
}
// NewActor sets up a uix.Actor.
// If the id is blank, a new UUIDv4 is generated.
func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor {
if id == "" {
id = uuid.Must(uuid.NewUUID()).String()
}
size := doodad.ChunkSize()
can := NewCanvas(uint8(size), false)
can.Name = id
// TODO: if the Background is render.Invisible it gets defaulted to
// White somewhere and the Doodad masks the level drawing behind it.
can.SetBackground(render.RGBA(0, 0, 1, 0))
can.LoadDoodad(doodad)
can.Resize(doodad.Size)
actor := &Actor{
Drawing: doodads.NewDrawing(id, doodad),
Actor: levelActor,
Canvas: can,
animations: map[string]*Animation{},
inventory: map[string]int{},
}
// Give the Canvas a pointer to its (parent) Actor so it can draw its debug
// label and show the World Position of the actor within the world.
can.actor = actor
return actor
}
// ID returns the actor's ID. This is the underlying doodle.Drawing.ID().
func (a *Actor) ID() string {
return a.Drawing.ID()
}
// Doodad offers access to the underlying Doodad object.
// Shortcut to the `.Drawing.Doodad` property path.
func (a *Actor) Doodad() *doodads.Doodad {
return a.Drawing.Doodad
}
// SetGravity configures whether the actor is affected by gravity.
func (a *Actor) SetGravity(v bool) {
a.hasGravity = v
}
// SetMobile configures whether the actor is a mobile character (i.e. is the
// player or a mobile enemy). Mobile characters can set off certain traps when
// touched but non-mobile actors don't set each other off if touching.
func (a *Actor) SetMobile(v bool) {
a.isMobile = v
}
Thief and Inventory APIs This commit adds the Thief character with starter graphics (no animations). The Thief walks back and forth and will steal items from other doodads, including the player. For singleton items that have no quantity, like the Colored Keys, the Thief will only steal one if he does not already have it. Quantitied items like the Small Key are always stolen. Flexibility in the playable character is introduced: Boy, Azulian, Bird, and Thief all respond to playable controls. There is not currently a method to enable these apart from modifying balance.PlayerCharacterDoodad at compile time. New and Changed Doodads * Thief: new doodad that walks back and forth and will steal items from other characters inventory. * Bird: has no inventory and cannot pick up items, unless player controlled. Its hitbox has also been fixed so it collides with floors correctly - not something normally seen in the Bird. * Boy: opts in to have inventory. * Keys (all): only gives themselves to actors having inventories. JavaScript API - New functions available * Self.IsPlayer() - returns if the current actor IS the player. * Self.SetInventory(bool) - doodads must opt-in to having an inventory. Keys should only give themselves to doodads having an inventory. * Self.HasInventory() bool * Self.AddItem(filename, qty) * Self.RemoveItem(filename, qty) * Self.HasItem(filename) * Self.Inventory() - returns map[string]int * Self.ClearInventory() * Self.OnLeave(func(e)) now receives a CollideEvent as parameter instead of the useless actor ID. Notably, e.Actor is the leaving actor and e.Settled is always true. Other Changes * Play Mode: if playing as a character which doesn't obey gravity, such as the bird, antigravity controls are enabled by default. If you `import antigravity` you can turn gravity back on. * Doodad collision scripts are no longer run in parallel goroutines. It made the Thief's job difficult trying to steal items in many threads simultaneously!
2021-08-10 05:42:22 +00:00
// SetInventory configures whether the actor is capable of carrying items.
func (a *Actor) SetInventory(v bool) {
a.hasInventory = true
}
// IsMobile returns whether the actor is a mobile character.
func (a *Actor) IsMobile() bool {
return a.isMobile
}
// IsPlayer returns whether the actor is the player character.
// It's true when the Actor ID is "PLAYER"
func (a *Actor) IsPlayer() bool {
return a.Canvas.Name == "PLAYER"
}
Thief and Inventory APIs This commit adds the Thief character with starter graphics (no animations). The Thief walks back and forth and will steal items from other doodads, including the player. For singleton items that have no quantity, like the Colored Keys, the Thief will only steal one if he does not already have it. Quantitied items like the Small Key are always stolen. Flexibility in the playable character is introduced: Boy, Azulian, Bird, and Thief all respond to playable controls. There is not currently a method to enable these apart from modifying balance.PlayerCharacterDoodad at compile time. New and Changed Doodads * Thief: new doodad that walks back and forth and will steal items from other characters inventory. * Bird: has no inventory and cannot pick up items, unless player controlled. Its hitbox has also been fixed so it collides with floors correctly - not something normally seen in the Bird. * Boy: opts in to have inventory. * Keys (all): only gives themselves to actors having inventories. JavaScript API - New functions available * Self.IsPlayer() - returns if the current actor IS the player. * Self.SetInventory(bool) - doodads must opt-in to having an inventory. Keys should only give themselves to doodads having an inventory. * Self.HasInventory() bool * Self.AddItem(filename, qty) * Self.RemoveItem(filename, qty) * Self.HasItem(filename) * Self.Inventory() - returns map[string]int * Self.ClearInventory() * Self.OnLeave(func(e)) now receives a CollideEvent as parameter instead of the useless actor ID. Notably, e.Actor is the leaving actor and e.Settled is always true. Other Changes * Play Mode: if playing as a character which doesn't obey gravity, such as the bird, antigravity controls are enabled by default. If you `import antigravity` you can turn gravity back on. * Doodad collision scripts are no longer run in parallel goroutines. It made the Thief's job difficult trying to steal items in many threads simultaneously!
2021-08-10 05:42:22 +00:00
// HasInventory returns if the actor is capable of carrying items.
func (a *Actor) HasInventory() bool {
return a.hasInventory
}
// HasGravity returns if gravity applies to the actor.
func (a *Actor) HasGravity() bool {
return a.hasGravity
}
// Invulnerable returns whether the actor is marked as immortal.
func (a *Actor) Invulnerable() bool {
return a.immortal
}
// SetInvulnerable sets the actor's immortal flag.
func (a *Actor) SetInvulnerable(v bool) {
a.immortal = v
}
// Wet returns whether the actor is in contact with water pixels in a level.
func (a *Actor) IsWet() bool {
return a.wet
}
// IsOnScreen checks whether the actor's visual sprite box is on-screen in the level viewport.
func (a *Actor) IsOnScreen() bool {
var result = a.Position().Inside(a.LevelCanvas.ViewportRelative())
return result
}
// SetWet updates the state of the actor's wet-ness.
func (a *Actor) SetWet(v bool) {
a.wet = v
}
// Size returns the size of the actor, from the underlying doodads.Drawing.
func (a *Actor) Size() render.Rect {
return a.Drawing.Doodad.Size
}
// Velocity returns the actor's current velocity vector.
func (a *Actor) Velocity() physics.Vector {
return a.velocity
}
// SetVelocity updates the actor's velocity vector.
func (a *Actor) SetVelocity(v physics.Vector) {
a.velocity = v
}
// Position returns the actor's position.
func (a *Actor) Position() render.Point {
return a.position
}
// MoveTo sets the actor's position.
func (a *Actor) MoveTo(p render.Point) {
a.position = p
}
// MoveBy adjusts the actor's position.
func (a *Actor) MoveBy(p render.Point) {
a.position.Add(p)
}
// Grounded returns if the actor is touching a floor.
func (a *Actor) Grounded() bool {
return a.grounded
}
// SetGrounded sets the actor's grounded value. If true, also sets their Y velocity to zero.
func (a *Actor) SetGrounded(v bool) {
2024-02-07 04:56:07 +00:00
if a.grounded && !v {
a.lostGroundAt = shmem.Tick
}
a.grounded = v
// if v && a.velocity.Y > 0 {
// a.velocity.Y = 0
// }
}
2024-02-07 04:56:07 +00:00
// IsCoyoteTime returns whether the actor has only just lost ground, so can still
// jump a few frames late.
//
// Pass a true value to unset, or "consume" the coyote time. The next call will
// then return that it is not coyote time, even if the lost ground frame is still
// within the CoyoteFrames threshold.
func (a *Actor) IsCoyoteTime(reset bool) (result bool) {
result = shmem.Tick-a.lostGroundAt <= balance.CoyoteFrames
if reset {
a.lostGroundAt = 0
}
return result
}
// Hide makes the actor invisible.
func (a *Actor) Hide() {
a.hidden = true
}
// Show a hidden actor.
func (a *Actor) Show() {
a.hidden = false
}
// Freeze an actor. For the player character, this means arrow key inputs
// will stop moving the actor.
func (a *Actor) Freeze() {
a.frozen = true
}
// Unfreeze an actor.
func (a *Actor) Unfreeze() {
a.frozen = false
}
// IsFrozen returns true if the actor is frozen.
func (a *Actor) IsFrozen() bool {
return a.frozen
}
// SetUsing enables the "Use Key" flag, mainly for the player character to activate
// certain doodads in the level.
func (a *Actor) SetUsing(v bool) {
a.flagUsing = v
}
// SetNoclip sets the noclip setting for an actor. If true, the actor can
// clip through level geometry.
func (a *Actor) SetNoclip(v bool) {
a.noclip = v
}
// AddItem adds an item doodad to the actor's inventory.
// Item name is usually the doodad filename.
func (a *Actor) AddItem(itemName string, quantity int) {
a.muInventory.Lock()
if _, ok := a.inventory[itemName]; ok {
a.inventory[itemName] += quantity
} else {
a.inventory[itemName] = quantity
}
a.muInventory.Unlock()
}
// RemoveItem removes a quantity of an item from the actor's inventory.
//
// Provide a quantity of 0 to remove the item completely.
// Otherwise provides a number greater than zero and you will subtract this
// quantity from the item. If the item then is at <= zero, it is removed from
// inventory.
func (a *Actor) RemoveItem(itemName string, quantity int) bool {
a.muInventory.RLock()
defer a.muInventory.RUnlock()
if _, ok := a.inventory[itemName]; ok {
// If quantity is zero, remove the item entirely.
if quantity <= 0 {
delete(a.inventory, itemName)
} else {
// Subtract the quantity from inventory. If we have run down to
// zero left, remove the item entirely.
a.inventory[itemName] -= quantity
if a.inventory[itemName] <= 0 {
delete(a.inventory, itemName)
}
}
return true
}
return false
}
// ClearInventory removes all items from the actor's inventory.
func (a *Actor) ClearInventory() {
a.muInventory.Lock()
a.inventory = map[string]int{}
a.muInventory.Unlock()
}
// HasItem checks the actor's inventory for the item and returns the quantity.
//
// A return value of -1 means the item was not found.
// The value 0 indicates a key item (one with no quantity).
// Values >= 1 would be consumable items.
func (a *Actor) HasItem(itemName string) int {
a.muInventory.RLock()
defer a.muInventory.RUnlock()
if quantity, ok := a.inventory[itemName]; ok {
return quantity
}
return -1
}
// ListItems returns a sorted list of the items in the actor's inventory.
func (a *Actor) ListItems() []string {
a.muInventory.RLock()
defer a.muInventory.RUnlock()
var (
result = make([]string, len(a.inventory))
i = 0
)
for k := range a.inventory {
result[i] = k
i++
}
sort.Strings(result)
return result
}
// Inventory returns a copy of the actor's inventory struct.
func (a *Actor) Inventory() map[string]int {
a.muInventory.RLock()
defer a.muInventory.RUnlock()
var result = map[string]int{}
for k, v := range a.inventory {
result[k] = v
}
return result
}
// GetBoundingRect gets the bounding box of the actor's doodad.
func (a *Actor) GetBoundingRect() render.Rect {
return collision.GetBoundingRect(a)
}
// SetHitbox sets the actor's elected hitbox.
func (a *Actor) SetHitbox(x, y, w, h int) {
a.hitbox = render.Rect{
X: x,
Y: y,
W: w,
H: h,
}
}
// Hitbox returns the actor's elected hitbox. If the JavaScript did not set
// a hitbox, it defers to the Doodad's metadata hitbox.
func (a *Actor) Hitbox() render.Rect {
if a.hitbox.IsZero() && !a.Drawing.Doodad.Hitbox.IsZero() {
return a.Drawing.Doodad.Hitbox
}
return a.hitbox
}
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
// Options returns the list of all available Doodad options, sorted.
func (a *Actor) Options() []string {
var result = []string{}
for option := range a.Doodad().Options {
result = append(result, option)
}
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
sort.Strings(result)
return result
}
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
// Get an option value from the actor. If the option is not configured,
// returns the default Doodad option, or nil if not there either.
func (a *Actor) GetOption(name string) *level.Option {
// Actor configured option?
if opt, ok := a.Actor.Options[name]; ok {
return opt
}
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
// Doodad default option?
if opt, ok := a.Doodad().Options[name]; ok {
return &level.Option{
Name: opt.Name,
Type: opt.Type,
Value: opt.Default,
}
}
Doodad/Actor Runtime Options * Add "Options" support for Doodads: these allow for individual Actor instances on your level to customize properties about the doodad. They're like "Tags" except the player can customize them on a per-actor basis. * Doodad Editor: you can specify the Options in the Doodad Properties window. * Level Editor: when the Actor Tool is selected, on mouse-over of an actor, clicking on the gear icon will open a new "Actor Properties" window which shows metadata (title, author, ID, position) and an Options tab to configure the actor's options. Updates to the scripting API: * Self.Options() returns a list of option names defined on the Doodad. * Self.GetOption(name) returns the value for the named option, or nil if neither the actor nor its doodad have the option defined. The return type will be correctly a string, boolean or integer type. Updates to the doodad command-line tool: * `doodad show` will print the Options on a .doodad file and, when showing a .level file with --actors, prints any customized Options with the actors. * `doodad edit-doodad` adds a --option parameter to define options. Options added to the game's built-in doodads: * Warp Doors: "locked (exit only)" will make it so the door can not be opened by the player, giving the "locked" message (as if it had no linked door), but the player may still exit from the door if sent by another warp door. * Electric Door & Electric Trapdoor: "opened" can make the door be opened by default when the level begins instead of closed. A switch or a button that removes power will close the door as normal. * Colored Doors & Small Key Door: "unlocked" will make the door unlocked at level start, not requiring a key to open it. * Colored Keys & Small Key: "has gravity" will make the key subject to gravity and set its Mobile flag so that if it falls onto a button, it will activate. * Gemstones: they had gravity by default; you can now uncheck "has gravity" to remove their Gravity and IsMobile status. * Gemstone Totems: "has gemstone" will set the totem to its unlocked status by default with the gemstone inserted. No power signal will be emitted; it is cosmetic only. * Fire Region: "name" can let you set a name for the fire region similarly to names for fire pixels: "Watch out for ${name}!" * Invisible Warp Door: "locked (exit only)" added as well.
2022-10-10 00:41:24 +00:00
return nil
}
// LayerCount returns the number of layers in this actor's drawing.
func (a *Actor) LayerCount() int {
return len(a.Doodad().Layers)
}
// ShowLayer sets the actor's ActiveLayer to the index given.
func (a *Actor) ShowLayer(index int) error {
if index < 0 {
return errors.New("layer index must be 0 or greater")
} else if index > len(a.Doodad().Layers) {
return fmt.Errorf("layer %d out of range for doodad's layers", index)
}
a.activeLayer = index
a.Canvas.Load(a.Doodad().Palette, a.Doodad().Layers[index].Chunker)
return nil
}
// ShowLayerNamed sets the actor's ActiveLayer to the one named.
func (a *Actor) ShowLayerNamed(name string) error {
// Find the layer.
for i, layer := range a.Doodad().Layers {
if layer.Name == name {
return a.ShowLayer(i)
}
}
log.Warn("Actor(%s) ShowLayerNamed(%s): layer not found",
a.Actor.Filename,
name,
)
// XX: returning an error raises a JavaScript exception in doodads. :/ Warning log is enough.
return nil
}
// Destroy deletes the actor from the running level.
func (a *Actor) Destroy() {
a.flagDestroy = true
}