Checkpoint Flag & Retry from Checkpoint
* New Doodad: Checkpoint Flag. They update the player's spawn point whenever the player passes one. The most recently activated checkpoint is rendered brighter than the others. * End Level Modal: the fake alert box window drawn by the Play Mode is replaced with a fancy modal widget (similar to Alert and Confirm). It handles level victory or failure conditions and can show or hide all the buttons as needed. * Gameplay: There is a "Retry from Checkpoint" option added, which appears in the level failure modal. It will teleport you back to the Start Flag or the last Checkpoint Flag you had touched, without resetting the level -- your keys, unlocked doors, etc. will be preserved so you can retry. * Set a maximum speed on the "Camera Follows Actor" logic of 64 pixels per tick. This results in a smoother scrolling transition when the player jumps to a new location on the map, such as by a Warp Door. * Update the default color palettes: * All: Add a "hint" magenta color. * Colored Pencil: Add a "darkstone" solid color. Updates to the Doodads JavaScript API: * SetCheckpoint(Point(x, y)): set the player character's spawn position. Giving it Self.Position() is an easy way to set the player spawn to your doodad's location.
This commit is contained in:
parent
43f8e3d9b2
commit
1ac85c9297
|
@ -2,7 +2,7 @@ function main() {
|
|||
var pressed = false;
|
||||
|
||||
// When a sticky button receives power, it pops back up.
|
||||
Message.Subscribe("power", function(powered) {
|
||||
Message.Subscribe("power", function (powered) {
|
||||
if (powered && pressed) {
|
||||
Self.ShowLayer(0);
|
||||
pressed = false;
|
||||
|
@ -12,7 +12,7 @@ function main() {
|
|||
}
|
||||
})
|
||||
|
||||
Events.OnCollide(function(e) {
|
||||
Events.OnCollide(function (e) {
|
||||
if (!e.Settled) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@ build:
|
|||
doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad
|
||||
doodad install-script exit-flag.js exit-flag.doodad
|
||||
|
||||
# Checkpoint Flag
|
||||
doodad convert -t "Checkpoint Flag" checkpoint-active.png \
|
||||
checkpoint-inactive.png checkpoint-flag.doodad
|
||||
doodad install-script checkpoint-flag.js checkpoint-flag.doodad
|
||||
|
||||
# Anvil
|
||||
doodad convert -t "Anvil" anvil.png anvil.doodad
|
||||
doodad install-script anvil.js anvil.doodad
|
||||
|
|
BIN
dev-assets/doodads/objects/checkpoint-active.png
Normal file
BIN
dev-assets/doodads/objects/checkpoint-active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
38
dev-assets/doodads/objects/checkpoint-flag.js
Normal file
38
dev-assets/doodads/objects/checkpoint-flag.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Checkpoint Flag.
|
||||
var isCurrentCheckpoint = false;
|
||||
|
||||
function main() {
|
||||
Self.SetHitbox(22 + 16, 16, 75 - 16, 86);
|
||||
setActive(false);
|
||||
|
||||
// Checkpoints broadcast to all of their peers so they all
|
||||
// know which one is the most recently activated.
|
||||
Message.Subscribe("broadcast:checkpoint", function (currentID) {
|
||||
setActive(false);
|
||||
});
|
||||
|
||||
Events.OnCollide(function (e) {
|
||||
if (!e.Settled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only care about the player character.
|
||||
if (!e.Actor.IsPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the player checkpoint.
|
||||
SetCheckpoint(Self.Position());
|
||||
setActive(true);
|
||||
Message.Broadcast("broadcast:checkpoint", Self.ID())
|
||||
});
|
||||
}
|
||||
|
||||
function setActive(v) {
|
||||
if (v && !isCurrentCheckpoint) {
|
||||
Flash("Checkpoint!");
|
||||
}
|
||||
|
||||
isCurrentCheckpoint = v;
|
||||
Self.ShowLayerNamed(v ? "checkpoint-active" : "checkpoint-inactive");
|
||||
}
|
BIN
dev-assets/doodads/objects/checkpoint-inactive.png
Normal file
BIN
dev-assets/doodads/objects/checkpoint-inactive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
|
@ -9,7 +9,8 @@ var (
|
|||
Height = 768
|
||||
|
||||
// Speed to scroll a canvas with arrow keys in Edit Mode.
|
||||
CanvasScrollSpeed = 8
|
||||
CanvasScrollSpeed = 8
|
||||
FollowActorMaxScrollSpeed = 64
|
||||
|
||||
// Window scrolling behavior in Play Mode.
|
||||
ScrollboxOffset = render.Point{ // from center of screen
|
||||
|
|
|
@ -38,11 +38,22 @@ var (
|
|||
Water: true,
|
||||
Pattern: "ink.png",
|
||||
},
|
||||
{
|
||||
Name: "hint",
|
||||
Color: render.MustHexColor("#F0F"),
|
||||
Pattern: "marker.png",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"Colored Pencil": {
|
||||
Swatches: []*Swatch{
|
||||
{
|
||||
Name: "darkstone",
|
||||
Color: render.MustHexColor("#777"),
|
||||
Pattern: "noise.png",
|
||||
Solid: true,
|
||||
},
|
||||
{
|
||||
Name: "grass",
|
||||
Color: render.DarkGreen,
|
||||
|
@ -79,6 +90,11 @@ var (
|
|||
Water: true,
|
||||
Pattern: "ink.png",
|
||||
},
|
||||
{
|
||||
Name: "hint",
|
||||
Color: render.MustHexColor("#F0F"),
|
||||
Pattern: "marker.png",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -113,6 +129,11 @@ var (
|
|||
Solid: true,
|
||||
Pattern: "marker.png",
|
||||
},
|
||||
{
|
||||
Name: "hint",
|
||||
Color: render.MustHexColor("#F0F"),
|
||||
Pattern: "marker.png",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
147
pkg/modal/end_level.go
Normal file
147
pkg/modal/end_level.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
package modal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// ConfigEndLevel sets options for the EndLevel modal.
|
||||
type ConfigEndLevel struct {
|
||||
Success bool // false = failure condition
|
||||
|
||||
// Handler functions - what you don't define will not
|
||||
// show as buttons in the modal.
|
||||
OnRestartLevel func() // Restart Level
|
||||
OnRetryCheckpoint func() // Continue from checkpoint
|
||||
OnEditLevel func()
|
||||
OnNextLevel func() // Next Level
|
||||
OnExitToMenu func() // Exit to Menu
|
||||
}
|
||||
|
||||
// EndLevel shows the End Level modal.
|
||||
func EndLevel(cfg ConfigEndLevel, title, message string, args ...interface{}) *Modal {
|
||||
if !ready {
|
||||
panic("modal.EndLevel(): not ready")
|
||||
} else if current != nil {
|
||||
return current
|
||||
}
|
||||
|
||||
// Reset the supervisor.
|
||||
supervisor = ui.NewSupervisor()
|
||||
|
||||
m := &Modal{
|
||||
title: title,
|
||||
message: fmt.Sprintf(message, args...),
|
||||
}
|
||||
m.window = makeEndLevel(m, cfg)
|
||||
|
||||
center(m.window)
|
||||
current = m
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// makeEndLevel creates the ui.Window for the Confirm modal.
|
||||
func makeEndLevel(m *Modal, cfg ConfigEndLevel) *ui.Window {
|
||||
win := ui.NewWindow("EndLevel")
|
||||
_, title := win.TitleBar()
|
||||
title.TextVariable = &m.title
|
||||
|
||||
msgFrame := ui.NewFrame("Confirm Message")
|
||||
win.Pack(msgFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
})
|
||||
|
||||
msg := ui.NewLabel(ui.Label{
|
||||
TextVariable: &m.message,
|
||||
Font: balance.UIFont,
|
||||
})
|
||||
msgFrame.Pack(msg, ui.Pack{
|
||||
Side: ui.N,
|
||||
})
|
||||
|
||||
// Ok/Cancel button bar.
|
||||
btnBar := ui.NewFrame("Button Bar")
|
||||
msgFrame.Pack(btnBar, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: 4,
|
||||
})
|
||||
|
||||
var buttons []*ui.Button
|
||||
var primaryFunc func()
|
||||
for _, btn := range []struct {
|
||||
Label string
|
||||
F func()
|
||||
}{
|
||||
{
|
||||
Label: "Next Level",
|
||||
F: cfg.OnNextLevel,
|
||||
},
|
||||
{
|
||||
Label: "Retry from Checkpoint",
|
||||
F: cfg.OnRetryCheckpoint,
|
||||
},
|
||||
{
|
||||
Label: "Restart Level",
|
||||
F: cfg.OnRestartLevel,
|
||||
},
|
||||
{
|
||||
Label: "Edit Level",
|
||||
F: cfg.OnEditLevel,
|
||||
},
|
||||
{
|
||||
Label: "Exit to Menu",
|
||||
F: cfg.OnExitToMenu,
|
||||
},
|
||||
} {
|
||||
btn := btn
|
||||
if btn.F == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if primaryFunc == nil {
|
||||
primaryFunc = btn.F
|
||||
}
|
||||
|
||||
button := ui.NewButton(btn.Label+"Button", ui.NewLabel(ui.Label{
|
||||
Text: btn.Label,
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
button.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
btn.F()
|
||||
m.Dismiss(false)
|
||||
return nil
|
||||
})
|
||||
button.Compute(engine)
|
||||
buttons = append(buttons, button)
|
||||
supervisor.Add(button)
|
||||
|
||||
btnBar.Pack(button, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: 2,
|
||||
FillX: true,
|
||||
})
|
||||
|
||||
// // Make a new row of buttons?
|
||||
// if i > 0 && i%3 == 0 {
|
||||
// btnBar = ui.NewFrame("Button Bar")
|
||||
// msgFrame.Pack(btnBar, ui.Pack{
|
||||
// Side: ui.N,
|
||||
// PadY: 0,
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
// Mark the first button the primary button.
|
||||
if primaryFunc != nil {
|
||||
m.Then(primaryFunc)
|
||||
}
|
||||
buttons[0].SetStyle(&balance.ButtonPrimary)
|
||||
|
||||
win.Compute(engine)
|
||||
win.Supervise(supervisor)
|
||||
|
||||
return win
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/pkg/keybind"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/modal"
|
||||
"git.kirsle.net/apps/doodle/pkg/modal/loadscreen"
|
||||
"git.kirsle.net/apps/doodle/pkg/physics"
|
||||
"git.kirsle.net/apps/doodle/pkg/scripting"
|
||||
|
@ -38,16 +39,6 @@ type PlayScene struct {
|
|||
screen *ui.Frame // A window sized invisible frame to position UI elements.
|
||||
editButton *ui.Button
|
||||
|
||||
// The alert box shows up when the level goal is reached and includes
|
||||
// buttons what to do next.
|
||||
alertBox *ui.Window
|
||||
alertBoxLabel *ui.Label
|
||||
alertBoxValue string
|
||||
alertReplayButton *ui.Button // Replay level
|
||||
alertEditButton *ui.Button // Edit Level
|
||||
alertNextButton *ui.Button // Next Level
|
||||
alertExitButton *ui.Button // Exit to menu
|
||||
|
||||
// Custom debug labels.
|
||||
debPosition *string
|
||||
debViewport *string
|
||||
|
@ -57,6 +48,7 @@ type PlayScene struct {
|
|||
// Player character
|
||||
Player *uix.Actor
|
||||
playerPhysics *physics.Mover
|
||||
lastCheckpoint render.Point
|
||||
antigravity bool // Cheat: disable player gravity
|
||||
noclip bool // Cheat: disable player clipping
|
||||
playerJumpCounter int // limit jump length
|
||||
|
@ -100,51 +92,9 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
s.screen.Resize(render.NewRect(d.width, d.height))
|
||||
|
||||
// Level Exit handler.
|
||||
s.SetupAlertbox()
|
||||
s.scripting.OnLevelExit(func() {
|
||||
d.Flash("Hurray!")
|
||||
|
||||
// Pause the simulation.
|
||||
s.running = false
|
||||
|
||||
// Toggle the relevant buttons on.
|
||||
if s.CanEdit {
|
||||
s.alertEditButton.Show()
|
||||
}
|
||||
if s.HasNext {
|
||||
s.alertNextButton.Show()
|
||||
}
|
||||
|
||||
// Always-visible buttons.
|
||||
s.alertReplayButton.Show()
|
||||
s.alertExitButton.Show()
|
||||
|
||||
// Show the alert box.
|
||||
s.alertBox.Title = "Level Completed"
|
||||
s.alertBoxValue = "Congratulations on clearing the level!"
|
||||
s.alertBox.Show()
|
||||
})
|
||||
s.scripting.OnLevelFail(func(message string) {
|
||||
d.Flash(message)
|
||||
|
||||
// Pause the simulation.
|
||||
s.running = false
|
||||
|
||||
// Toggle the relevant buttons on.
|
||||
if s.CanEdit {
|
||||
s.alertEditButton.Show()
|
||||
}
|
||||
s.alertNextButton.Hide()
|
||||
|
||||
// Always-visible buttons.
|
||||
s.alertReplayButton.Show()
|
||||
s.alertExitButton.Show()
|
||||
|
||||
// Show the alert box.
|
||||
s.alertBox.Title = "You've died!"
|
||||
s.alertBoxValue = message
|
||||
s.alertBox.Show()
|
||||
})
|
||||
s.scripting.OnLevelExit(s.BeatLevel)
|
||||
s.scripting.OnLevelFail(s.FailLevel)
|
||||
s.scripting.OnSetCheckpoint(s.SetCheckpoint)
|
||||
|
||||
// Initialize debug overlay values.
|
||||
s.debPosition = new(string)
|
||||
|
@ -288,6 +238,9 @@ func (s *PlayScene) setupPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
// The Start Flag becomes the player's initial checkpoint.
|
||||
s.lastCheckpoint = flag.Point
|
||||
|
||||
// Load in the player character.
|
||||
player, err := doodads.LoadFile(playerCharacterFilename)
|
||||
if err != nil {
|
||||
|
@ -329,86 +282,6 @@ func (s *PlayScene) setupPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
// SetupAlertbox configures the alert box UI.
|
||||
func (s *PlayScene) SetupAlertbox() {
|
||||
window := ui.NewWindow("Level Completed")
|
||||
window.Configure(ui.Config{
|
||||
Width: 320,
|
||||
Height: 160,
|
||||
Background: render.Grey,
|
||||
})
|
||||
window.Compute(s.d.Engine)
|
||||
|
||||
{
|
||||
frame := ui.NewFrame("Open Drawing Frame")
|
||||
window.Pack(frame, ui.Pack{
|
||||
Side: ui.N,
|
||||
Fill: true,
|
||||
Expand: true,
|
||||
})
|
||||
|
||||
/******************
|
||||
* Frame for selecting User Levels
|
||||
******************/
|
||||
|
||||
s.alertBoxLabel = ui.NewLabel(ui.Label{
|
||||
TextVariable: &s.alertBoxValue,
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
frame.Pack(s.alertBoxLabel, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 16,
|
||||
})
|
||||
|
||||
/******************
|
||||
* Confirm/cancel buttons.
|
||||
******************/
|
||||
|
||||
bottomFrame := ui.NewFrame("Button Frame")
|
||||
frame.Pack(bottomFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 8,
|
||||
})
|
||||
|
||||
// Button factory for the various options.
|
||||
makeButton := func(text string, handler func()) *ui.Button {
|
||||
btn := ui.NewButton(text, ui.NewLabel(ui.Label{
|
||||
Font: balance.LabelFont,
|
||||
Text: text,
|
||||
}))
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
handler()
|
||||
return nil
|
||||
})
|
||||
bottomFrame.Pack(btn, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 2,
|
||||
})
|
||||
s.supervisor.Add(btn)
|
||||
btn.Hide() // all buttons hidden by default
|
||||
return btn
|
||||
}
|
||||
|
||||
s.alertReplayButton = makeButton("Play Again", func() {
|
||||
s.RestartLevel()
|
||||
})
|
||||
s.alertEditButton = makeButton("Edit Level", func() {
|
||||
s.EditLevel()
|
||||
})
|
||||
s.alertNextButton = makeButton("Next Level", func() {
|
||||
s.d.Flash("Not Implemented")
|
||||
})
|
||||
s.alertExitButton = makeButton("Exit to Menu", func() {
|
||||
s.d.Goto(&MainScene{})
|
||||
})
|
||||
}
|
||||
|
||||
s.alertBox = window
|
||||
s.alertBox.Hide()
|
||||
}
|
||||
|
||||
// EditLevel toggles out of Play Mode to edit the level.
|
||||
func (s *PlayScene) EditLevel() {
|
||||
log.Info("Edit Mode, Go!")
|
||||
|
@ -428,19 +301,67 @@ func (s *PlayScene) RestartLevel() {
|
|||
})
|
||||
}
|
||||
|
||||
// SetCheckpoint sets the player's checkpoint.
|
||||
func (s *PlayScene) SetCheckpoint(where render.Point) {
|
||||
s.lastCheckpoint = where
|
||||
}
|
||||
|
||||
// RetryCheckpoint moves the player back to their last checkpoint.
|
||||
func (s *PlayScene) RetryCheckpoint() {
|
||||
log.Info("Move player back to last checkpoint")
|
||||
s.Player.MoveTo(s.lastCheckpoint)
|
||||
s.running = true
|
||||
}
|
||||
|
||||
// BeatLevel handles the level success condition.
|
||||
func (s *PlayScene) BeatLevel() {
|
||||
s.d.Flash("Hurray!")
|
||||
s.ShowEndLevelModal(
|
||||
true,
|
||||
"Level Completed",
|
||||
"Congratulations on clearing the level!",
|
||||
)
|
||||
}
|
||||
|
||||
// FailLevel handles a level failure triggered by a doodad.
|
||||
func (s *PlayScene) FailLevel(message string) {
|
||||
s.d.Flash(message)
|
||||
s.ShowEndLevelModal(
|
||||
false,
|
||||
"You've died!",
|
||||
message,
|
||||
)
|
||||
}
|
||||
|
||||
// DieByFire ends the level by "fire", or w/e the swatch is named.
|
||||
func (s *PlayScene) DieByFire(name string) {
|
||||
log.Info("Watch out for %s!", name)
|
||||
s.alertBox.Title = "You've died!"
|
||||
s.alertBoxValue = fmt.Sprintf("Watch out for %s!", name)
|
||||
s.FailLevel(fmt.Sprintf("Watch out for %s!", name))
|
||||
}
|
||||
|
||||
s.alertReplayButton.Show()
|
||||
if s.CanEdit {
|
||||
s.alertEditButton.Show()
|
||||
// ShowEndLevelModal centralizes the EndLevel modal config.
|
||||
// This is the common handler function between easy methods such as
|
||||
// BeatLevel, FailLevel, and DieByFire.
|
||||
func (s *PlayScene) ShowEndLevelModal(success bool, title, message string) {
|
||||
config := modal.ConfigEndLevel{
|
||||
Success: success,
|
||||
OnRestartLevel: s.RestartLevel,
|
||||
OnRetryCheckpoint: s.RetryCheckpoint,
|
||||
OnExitToMenu: func() {
|
||||
s.d.Goto(&MainScene{})
|
||||
},
|
||||
}
|
||||
s.alertExitButton.Show()
|
||||
|
||||
s.alertBox.Show()
|
||||
if s.CanEdit {
|
||||
config.OnEditLevel = s.EditLevel
|
||||
}
|
||||
|
||||
// Beaten the level?
|
||||
if success {
|
||||
config.OnRetryCheckpoint = nil
|
||||
}
|
||||
|
||||
// Show the modal.
|
||||
modal.EndLevel(config, title, message)
|
||||
|
||||
// Stop the simulation.
|
||||
s.running = false
|
||||
|
@ -538,16 +459,6 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
|||
})
|
||||
s.editButton.Present(d.Engine, s.editButton.Point())
|
||||
|
||||
// Draw the alert box window.
|
||||
if !s.alertBox.Hidden() {
|
||||
s.alertBox.Compute(d.Engine)
|
||||
s.alertBox.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (s.alertBox.Size().W / 2),
|
||||
Y: (d.height / 2) - (s.alertBox.Size().H / 2),
|
||||
})
|
||||
s.alertBox.Present(d.Engine, s.alertBox.Point())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
||||
// Supervisor manages the JavaScript VMs for each doodad by its
|
||||
|
@ -16,8 +17,9 @@ type Supervisor struct {
|
|||
scripts map[string]*VM
|
||||
|
||||
// Global event handlers.
|
||||
onLevelExit func()
|
||||
onLevelFail func(message string)
|
||||
onLevelExit func()
|
||||
onLevelFail func(message string)
|
||||
onSetCheckpoint func(where render.Point)
|
||||
}
|
||||
|
||||
// NewSupervisor creates a new JavaScript Supervior.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package scripting
|
||||
|
||||
import "git.kirsle.net/go/render"
|
||||
|
||||
/*
|
||||
RegisterEventHooks attaches the supervisor level event hooks into a JS VM.
|
||||
|
||||
|
@ -21,6 +23,12 @@ func RegisterEventHooks(s *Supervisor, vm *VM) {
|
|||
}
|
||||
s.onLevelFail(message)
|
||||
})
|
||||
vm.Set("SetCheckpoint", func(p render.Point) {
|
||||
if s.onSetCheckpoint == nil {
|
||||
panic("JS SetCheckpoint(): No OnSetCheckpoint handler attached to script supervisor")
|
||||
}
|
||||
s.onSetCheckpoint(p)
|
||||
})
|
||||
}
|
||||
|
||||
// OnLevelExit registers an event hook for when a Level Exit doodad is reached.
|
||||
|
@ -32,3 +40,8 @@ func (s *Supervisor) OnLevelExit(handler func()) {
|
|||
func (s *Supervisor) OnLevelFail(handler func(string)) {
|
||||
s.onLevelFail = handler
|
||||
}
|
||||
|
||||
// OnSetCheckpoint registers an event hook for setting player checkpoints.
|
||||
func (s *Supervisor) OnSetCheckpoint(handler func(render.Point)) {
|
||||
s.onSetCheckpoint = handler
|
||||
}
|
||||
|
|
|
@ -179,6 +179,18 @@ func (w *Canvas) loopFollowActor(ev *event.State) error {
|
|||
scrollBy.Y = delta
|
||||
}
|
||||
|
||||
// Constrain the maximum scroll speed.
|
||||
if scrollBy.X > balance.FollowActorMaxScrollSpeed {
|
||||
scrollBy.X = balance.FollowActorMaxScrollSpeed
|
||||
} else if scrollBy.X < -balance.FollowActorMaxScrollSpeed {
|
||||
scrollBy.X = -balance.FollowActorMaxScrollSpeed
|
||||
}
|
||||
if scrollBy.Y > balance.FollowActorMaxScrollSpeed {
|
||||
scrollBy.Y = balance.FollowActorMaxScrollSpeed
|
||||
} else if scrollBy.Y < -balance.FollowActorMaxScrollSpeed {
|
||||
scrollBy.Y = -balance.FollowActorMaxScrollSpeed
|
||||
}
|
||||
|
||||
if scrollBy != render.Origin {
|
||||
w.ScrollBy(scrollBy)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user