"Look At Me" for Doodad Scripts

* New script API method: Self.CameraFollowMe() to draw camera focus toward
  your doodad (it sets the Canvas.FollowActor target.)
* The camera will go back to following the player on any action inputs
  (arrow keys, jump, use, etc.); if the player is constantly on the move
  the camera stays on him even if another actor is trying to take the focus.
* The first few ticks of Play Mode the player character is always followed,
  to allow for Anvils to settle into place without taking the focus.
* Canvas FollowActor: if the actor is 4 times the max scroll speed away,
  allow scrolling in greater leaps of 4 times the max scroll speed.

New and Changed Doodads

* Anvils will take the camera focus while they are falling.
* New doodad: "Look At Me" - a 'camera region' technical doodad. Link it to
  any power source such as a Button - when this doodad receives power it will
  take the camera focus for a few frames. Use it to highlight a door that
  opened far off screen by linking the Button to both an Electric Door and
  a "Look At Me" near the door.
This commit is contained in:
Noah 2022-09-24 23:54:51 -07:00
parent 653184b8f8
commit 7d15651ff6
6 changed files with 50 additions and 11 deletions

View File

@ -46,6 +46,11 @@ var (
SwimJumpCooldown uint64 = 24 // number of frames of cooldown between swim-jumps
SlopeMaxHeight = 8 // max pixel height for player to walk up a slope
// Number of game ticks to insist the canvas follows the player at the start
// of a level - to overcome Anvils settling into their starting positions so
// they don't steal the camera focus straight away.
FollowPlayerFirstTicks uint64 = 60
// Default chunk size for canvases.
ChunkSize = 128

View File

@ -68,16 +68,17 @@ type PlayScene struct {
debLoadUnload *string
// Player character
Player *uix.Actor
playerPhysics *physics.Mover
lastCheckpoint render.Point
playerLastDirection float64 // player's heading last tick
antigravity bool // Cheat: disable player gravity
noclip bool // Cheat: disable player clipping
godMode bool // Cheat: player can't die
godModeUntil time.Time // Invulnerability timer at respawn.
playerJumpCounter int // limit jump length
jumpCooldownUntil uint64 // future game tick for jump cooldown (swimming esp.)
Player *uix.Actor
playerPhysics *physics.Mover
lastCheckpoint render.Point
playerLastDirection float64 // player's heading last tick
antigravity bool // Cheat: disable player gravity
noclip bool // Cheat: disable player clipping
godMode bool // Cheat: player can't die
godModeUntil time.Time // Invulnerability timer at respawn.
playerJumpCounter int // limit jump length
jumpCooldownUntil uint64 // future game tick for jump cooldown (swimming esp.)
mustFollowPlayerUntil uint64 // first frames where anvils don't take focus from player
// Inventory HUD. Impl. in play_inventory.go
invenFrame *ui.Frame

View File

@ -115,5 +115,18 @@ func (s *PlayScene) movePlayer(ev *event.State) {
// If the "Use" key is pressed, set an actor flag on the player.
s.Player.SetUsing(keybind.Use(ev))
// Camera behaviors: Anvils can take the camera's focus while they're falling
// but player inputs will take control back to the player. Most anvils will fall
// a couple pixels upon level load - prevent them taking the camera's focus for
// the first few frames of gameplay.
if s.mustFollowPlayerUntil == 0 {
s.mustFollowPlayerUntil = shmem.Tick + balance.FollowPlayerFirstTicks
}
// If we insist that the canvas follow the player doodad.
if shmem.Tick < s.mustFollowPlayerUntil || keybind.Up(ev) || keybind.Left(ev) || keybind.Right(ev) || keybind.Use(ev) {
s.drawing.FollowActor = s.Player.ID()
}
s.scripting.To(s.Player.ID()).Events.RunKeypress(keybind.FromEvent(ev))
}

View File

@ -413,7 +413,9 @@ func (a *Actor) ShowLayerNamed(name string) error {
a.Actor.Filename,
name,
)
return fmt.Errorf("the layer named %s was not found", 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.

View File

@ -246,6 +246,18 @@ func (w *Canvas) loopFollowActor(ev *event.State) error {
scrollBy.Y = delta
}
// If we are VERY FAR away, allow greater leaps.
if scrollBy.X > balance.FollowActorMaxScrollSpeed*4 {
scrollBy.X = balance.FollowActorMaxScrollSpeed * 4
} else if scrollBy.X < -balance.FollowActorMaxScrollSpeed*4 {
scrollBy.X = -balance.FollowActorMaxScrollSpeed * 4
}
if scrollBy.Y > balance.FollowActorMaxScrollSpeed*4 {
scrollBy.Y = balance.FollowActorMaxScrollSpeed * 4
} else if scrollBy.Y < -balance.FollowActorMaxScrollSpeed*4 {
scrollBy.Y = -balance.FollowActorMaxScrollSpeed * 4
}
// Constrain the maximum scroll speed.
if scrollBy.X > balance.FollowActorMaxScrollSpeed {
scrollBy.X = balance.FollowActorMaxScrollSpeed

View File

@ -132,5 +132,11 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
}
return result
},
// Attract the camera's attention.
"CameraFollowMe": func() {
// Update the doodad that the camera should focus on.
w.FollowActor = actor.ID()
},
}
}