Level Exit Doodad
* Add a Level Exit doodad, which for now is a little blue flag on a pole that reads "END" * JavaScript API: global function EndLevel() will end the level. The exit doodad calls this when touched by the player. * Add a "Level Completed" alert box UI to PlayScene with dynamic button layouts. * The alert box pops up when a doodad calls EndLevel() and contains action buttons what to do next. * "Play Again" restarts the current level again. * "Edit Level" if you came from the EditorScene; otherwise this button is not visible. * "Next Level" is a to-be-implemented button to advance in the single player story mode. Only shows up when PlayScene.HasNext=true. * "Exit to Menu" is always visible and closes out to the MainScene.
This commit is contained in:
parent
3d08291bc5
commit
0c22ecae5e
|
@ -86,7 +86,19 @@ azulians() {
|
||||||
cd ..
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objects() {
|
||||||
|
cd objects/
|
||||||
|
|
||||||
|
doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad
|
||||||
|
doodad install-script exit-flag.js exit-flag.doodad
|
||||||
|
|
||||||
|
cp *.doodad ../../../assets/doodads/
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
buttons
|
buttons
|
||||||
doors
|
doors
|
||||||
trapdoors
|
trapdoors
|
||||||
azulians
|
azulians
|
||||||
|
objects
|
||||||
|
|
11
dev-assets/doodads/objects/exit-flag.js
Normal file
11
dev-assets/doodads/objects/exit-flag.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// Exit Flag.
|
||||||
|
function main() {
|
||||||
|
console.log("%s initialized!", Self.Doodad.Title);
|
||||||
|
Self.SetHitbox(22+16, 16, 75-16, 86);
|
||||||
|
|
||||||
|
Events.OnCollide(function(e) {
|
||||||
|
if (e.InHitbox) {
|
||||||
|
EndLevel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
BIN
dev-assets/doodads/objects/exit-flag.png
Normal file
BIN
dev-assets/doodads/objects/exit-flag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 932 B |
|
@ -2,6 +2,7 @@ package doodle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -223,11 +224,7 @@ func (d *Doodle) NewDoodad(size int) {
|
||||||
// EditDrawing loads a drawing (Level or Doodad) in Edit Mode.
|
// EditDrawing loads a drawing (Level or Doodad) in Edit Mode.
|
||||||
func (d *Doodle) EditDrawing(filename string) error {
|
func (d *Doodle) EditDrawing(filename string) error {
|
||||||
log.Info("Loading drawing from file: %s", filename)
|
log.Info("Loading drawing from file: %s", filename)
|
||||||
parts := strings.Split(filename, ".")
|
ext := strings.ToLower(filepath.Ext(filename))
|
||||||
if len(parts) < 2 {
|
|
||||||
return fmt.Errorf("filename `%s` has no file extension", filename)
|
|
||||||
}
|
|
||||||
ext := strings.ToLower(parts[len(parts)-1])
|
|
||||||
|
|
||||||
scene := &EditorScene{
|
scene := &EditorScene{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
|
@ -235,11 +232,11 @@ func (d *Doodle) EditDrawing(filename string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ext {
|
switch ext {
|
||||||
case "level":
|
case ".level":
|
||||||
case "map":
|
case ".map":
|
||||||
log.Info("is a Level type")
|
log.Info("is a Level type")
|
||||||
scene.DrawingType = enum.LevelDrawing
|
scene.DrawingType = enum.LevelDrawing
|
||||||
case "doodad":
|
case ".doodad":
|
||||||
if balance.FreeVersion {
|
if balance.FreeVersion {
|
||||||
return fmt.Errorf("Doodad editor not supported in your version of the game")
|
return fmt.Errorf("Doodad editor not supported in your version of the game")
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ func (s *EditorScene) Playtest() {
|
||||||
s.d.Goto(&PlayScene{
|
s.d.Goto(&PlayScene{
|
||||||
Filename: s.filename,
|
Filename: s.filename,
|
||||||
Level: s.Level,
|
Level: s.Level,
|
||||||
|
CanEdit: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
// Draw the current frame.
|
// Draw the current frame.
|
||||||
func (s *EditorScene) Draw(d *Doodle) error {
|
func (s *EditorScene) Draw(d *Doodle) error {
|
||||||
// Clear the canvas and fill it with magenta so it's clear if any spots are missed.
|
// Clear the canvas and fill it with magenta so it's clear if any spots are missed.
|
||||||
d.Engine.Clear(render.Magenta)
|
d.Engine.Clear(render.RGBA(160, 120, 160, 255))
|
||||||
|
|
||||||
s.UI.Present(d.Engine)
|
s.UI.Present(d.Engine)
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ func (u *EditorUI) Resized(d *Doodle) {
|
||||||
|
|
||||||
// Position the workspace around with the other widgets.
|
// Position the workspace around with the other widgets.
|
||||||
{
|
{
|
||||||
|
|
||||||
frame := u.Workspace
|
frame := u.Workspace
|
||||||
frame.MoveTo(render.NewPoint(
|
frame.MoveTo(render.NewPoint(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -19,16 +19,27 @@ type PlayScene struct {
|
||||||
// Configuration attributes.
|
// Configuration attributes.
|
||||||
Filename string
|
Filename string
|
||||||
Level *level.Level
|
Level *level.Level
|
||||||
|
CanEdit bool // i.e. you came from the Editor Mode
|
||||||
|
HasNext bool // has a next level to load next
|
||||||
|
|
||||||
// Private variables.
|
// Private variables.
|
||||||
d *Doodle
|
d *Doodle
|
||||||
drawing *uix.Canvas
|
drawing *uix.Canvas
|
||||||
scripting *scripting.Supervisor
|
scripting *scripting.Supervisor
|
||||||
|
running bool
|
||||||
|
|
||||||
// UI widgets.
|
// UI widgets.
|
||||||
supervisor *ui.Supervisor
|
supervisor *ui.Supervisor
|
||||||
editButton *ui.Button
|
editButton *ui.Button
|
||||||
|
|
||||||
|
// The alert box shows up when the level goal is reached and includes
|
||||||
|
// buttons what to do next.
|
||||||
|
alertBox *ui.Window
|
||||||
|
alertReplayButton *ui.Button // Replay level
|
||||||
|
alertEditButton *ui.Button // Edit Level
|
||||||
|
alertNextButton *ui.Button // Next Level
|
||||||
|
alertExitButton *ui.Button // Exit to menu
|
||||||
|
|
||||||
// Custom debug labels.
|
// Custom debug labels.
|
||||||
debPosition *string
|
debPosition *string
|
||||||
debViewport *string
|
debViewport *string
|
||||||
|
@ -50,6 +61,30 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
||||||
s.scripting = scripting.NewSupervisor()
|
s.scripting = scripting.NewSupervisor()
|
||||||
s.supervisor = ui.NewSupervisor()
|
s.supervisor = ui.NewSupervisor()
|
||||||
|
|
||||||
|
// 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.Show()
|
||||||
|
})
|
||||||
|
|
||||||
// Initialize debug overlay values.
|
// Initialize debug overlay values.
|
||||||
s.debPosition = new(string)
|
s.debPosition = new(string)
|
||||||
s.debViewport = new(string)
|
s.debViewport = new(string)
|
||||||
|
@ -126,10 +161,90 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Flash("Entered Play Mode. Press 'E' to edit this map.")
|
d.Flash("Entered Play Mode. Press 'E' to edit this map.")
|
||||||
|
s.running = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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{
|
||||||
|
Anchor: ui.N,
|
||||||
|
Fill: true,
|
||||||
|
Expand: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
/******************
|
||||||
|
* Frame for selecting User Levels
|
||||||
|
******************/
|
||||||
|
|
||||||
|
label1 := ui.NewLabel(ui.Label{
|
||||||
|
Text: "Congratulations on clearing the level!",
|
||||||
|
Font: balance.LabelFont,
|
||||||
|
})
|
||||||
|
frame.Pack(label1, ui.Pack{
|
||||||
|
Anchor: ui.N,
|
||||||
|
FillX: true,
|
||||||
|
PadY: 16,
|
||||||
|
})
|
||||||
|
|
||||||
|
/******************
|
||||||
|
* Confirm/cancel buttons.
|
||||||
|
******************/
|
||||||
|
|
||||||
|
bottomFrame := ui.NewFrame("Button Frame")
|
||||||
|
frame.Pack(bottomFrame, ui.Pack{
|
||||||
|
Anchor: 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(p render.Point) {
|
||||||
|
handler()
|
||||||
|
})
|
||||||
|
bottomFrame.Pack(btn, ui.Pack{
|
||||||
|
Anchor: 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.
|
// EditLevel toggles out of Play Mode to edit the level.
|
||||||
func (s *PlayScene) EditLevel() {
|
func (s *PlayScene) EditLevel() {
|
||||||
log.Info("Edit Mode, Go!")
|
log.Info("Edit Mode, Go!")
|
||||||
|
@ -139,6 +254,16 @@ func (s *PlayScene) EditLevel() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestartLevel starts the level over again.
|
||||||
|
func (s *PlayScene) RestartLevel() {
|
||||||
|
log.Info("Restart Level")
|
||||||
|
s.d.Goto(&PlayScene{
|
||||||
|
Filename: s.Filename,
|
||||||
|
Level: s.Level,
|
||||||
|
CanEdit: s.CanEdit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Loop the editor scene.
|
// Loop the editor scene.
|
||||||
func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
|
func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
// Update debug overlay values.
|
// Update debug overlay values.
|
||||||
|
@ -166,14 +291,17 @@ func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop the script supervisor so timeouts/intervals can fire in scripts.
|
// Is the simulation still running?
|
||||||
if err := s.scripting.Loop(); err != nil {
|
if s.running {
|
||||||
log.Error("PlayScene.Loop: scripting.Loop: %s", err)
|
// Loop the script supervisor so timeouts/intervals can fire in scripts.
|
||||||
}
|
if err := s.scripting.Loop(); err != nil {
|
||||||
|
log.Error("PlayScene.Loop: scripting.Loop: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
s.movePlayer(ev)
|
s.movePlayer(ev)
|
||||||
if err := s.drawing.Loop(ev); err != nil {
|
if err := s.drawing.Loop(ev); err != nil {
|
||||||
log.Error("Drawing loop error: %s", err.Error())
|
log.Error("Drawing loop error: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -201,6 +329,16 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
||||||
Y: canSize.H - size.H - padding,
|
Y: canSize.H - size.H - padding,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Draw the alert box window.
|
||||||
|
if !s.alertBox.Hidden() {
|
||||||
|
s.alertBox.Compute(d.Engine)
|
||||||
|
s.alertBox.MoveTo(render.Point{
|
||||||
|
X: int32(d.width/2) - (s.alertBox.Size().W / 2),
|
||||||
|
Y: int32(d.height/2) - (s.alertBox.Size().H / 2),
|
||||||
|
})
|
||||||
|
s.alertBox.Present(d.Engine, s.alertBox.Point())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,14 +386,14 @@ func (s *PlayScene) Drawing() *uix.Canvas {
|
||||||
func (s *PlayScene) LoadLevel(filename string) error {
|
func (s *PlayScene) LoadLevel(filename string) error {
|
||||||
s.Filename = filename
|
s.Filename = filename
|
||||||
|
|
||||||
level, err := level.LoadJSON(filename)
|
level, err := level.LoadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("PlayScene.LoadLevel(%s): %s", filename, err)
|
return fmt.Errorf("PlayScene.LoadLevel(%s): %s", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Level = level
|
s.Level = level
|
||||||
s.drawing.LoadLevel(s.d.Engine, s.Level)
|
s.drawing.LoadLevel(s.d.Engine, s.Level)
|
||||||
// s.drawing.InstallActors(s.Level.Actors)
|
s.drawing.InstallActors(s.Level.Actors)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ import (
|
||||||
// unique ID.
|
// unique ID.
|
||||||
type Supervisor struct {
|
type Supervisor struct {
|
||||||
scripts map[string]*VM
|
scripts map[string]*VM
|
||||||
|
|
||||||
|
// Global event handlers.
|
||||||
|
onLevelExit func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSupervisor creates a new JavaScript Supervior.
|
// NewSupervisor creates a new JavaScript Supervior.
|
||||||
|
@ -67,6 +70,7 @@ func (s *Supervisor) AddLevelScript(id string) error {
|
||||||
|
|
||||||
s.scripts[id] = NewVM(id)
|
s.scripts[id] = NewVM(id)
|
||||||
RegisterPublishHooks(s.scripts[id])
|
RegisterPublishHooks(s.scripts[id])
|
||||||
|
RegisterEventHooks(s, s.scripts[id])
|
||||||
if err := s.scripts[id].RegisterLevelHooks(); err != nil {
|
if err := s.scripts[id].RegisterLevelHooks(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
23
pkg/scripting/supervisor_events.go
Normal file
23
pkg/scripting/supervisor_events.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package scripting
|
||||||
|
|
||||||
|
/*
|
||||||
|
RegisterEventHooks attaches the supervisor level event hooks into a JS VM.
|
||||||
|
|
||||||
|
Names registered:
|
||||||
|
|
||||||
|
- EndLevel(): for a doodad to exit the level. Panics if the OnLevelExit
|
||||||
|
handler isn't defined.
|
||||||
|
*/
|
||||||
|
func RegisterEventHooks(s *Supervisor, vm *VM) {
|
||||||
|
vm.Set("EndLevel", func() {
|
||||||
|
if s.onLevelExit == nil {
|
||||||
|
panic("JS EndLevel(): no OnLevelExit handler attached to script supervisor")
|
||||||
|
}
|
||||||
|
s.onLevelExit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnLevelExit registers an event hook for when a Level Exit doodad is reached.
|
||||||
|
func (s *Supervisor) OnLevelExit(handler func()) {
|
||||||
|
s.onLevelExit = handler
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user