From 7d15651ff6a6b774c7ca277c99d29c1373876e7c Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 24 Sep 2022 23:54:51 -0700 Subject: [PATCH] "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. --- pkg/balance/numbers.go | 5 +++++ pkg/play_scene.go | 21 +++++++++++---------- pkg/player_physics.go | 13 +++++++++++++ pkg/uix/actor.go | 4 +++- pkg/uix/canvas_scrolling.go | 12 ++++++++++++ pkg/uix/scripting.go | 6 ++++++ 6 files changed, 50 insertions(+), 11 deletions(-) diff --git a/pkg/balance/numbers.go b/pkg/balance/numbers.go index 5376737..da4444f 100644 --- a/pkg/balance/numbers.go +++ b/pkg/balance/numbers.go @@ -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 diff --git a/pkg/play_scene.go b/pkg/play_scene.go index 1977a99..5c73bec 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -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 diff --git a/pkg/player_physics.go b/pkg/player_physics.go index 29a073a..ec8b3c2 100644 --- a/pkg/player_physics.go +++ b/pkg/player_physics.go @@ -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)) } diff --git a/pkg/uix/actor.go b/pkg/uix/actor.go index 322038c..7632475 100644 --- a/pkg/uix/actor.go +++ b/pkg/uix/actor.go @@ -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. diff --git a/pkg/uix/canvas_scrolling.go b/pkg/uix/canvas_scrolling.go index 8c1a978..cb07a4a 100644 --- a/pkg/uix/canvas_scrolling.go +++ b/pkg/uix/canvas_scrolling.go @@ -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 diff --git a/pkg/uix/scripting.go b/pkg/uix/scripting.go index fb91ba7..23e04ce 100644 --- a/pkg/uix/scripting.go +++ b/pkg/uix/scripting.go @@ -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() + }, } }