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 ..
|
||||
}
|
||||
|
||||
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
|
||||
doors
|
||||
trapdoors
|
||||
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 (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -223,11 +224,7 @@ func (d *Doodle) NewDoodad(size int) {
|
|||
// EditDrawing loads a drawing (Level or Doodad) in Edit Mode.
|
||||
func (d *Doodle) EditDrawing(filename string) error {
|
||||
log.Info("Loading drawing from file: %s", filename)
|
||||
parts := strings.Split(filename, ".")
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("filename `%s` has no file extension", filename)
|
||||
}
|
||||
ext := strings.ToLower(parts[len(parts)-1])
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
|
||||
scene := &EditorScene{
|
||||
Filename: filename,
|
||||
|
@ -235,11 +232,11 @@ func (d *Doodle) EditDrawing(filename string) error {
|
|||
}
|
||||
|
||||
switch ext {
|
||||
case "level":
|
||||
case "map":
|
||||
case ".level":
|
||||
case ".map":
|
||||
log.Info("is a Level type")
|
||||
scene.DrawingType = enum.LevelDrawing
|
||||
case "doodad":
|
||||
case ".doodad":
|
||||
if balance.FreeVersion {
|
||||
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{
|
||||
Filename: s.filename,
|
||||
Level: s.Level,
|
||||
CanEdit: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,7 +168,7 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
|||
// Draw the current frame.
|
||||
func (s *EditorScene) Draw(d *Doodle) error {
|
||||
// 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)
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ func (u *EditorUI) Resized(d *Doodle) {
|
|||
|
||||
// Position the workspace around with the other widgets.
|
||||
{
|
||||
|
||||
frame := u.Workspace
|
||||
frame.MoveTo(render.NewPoint(
|
||||
0,
|
||||
|
|
|
@ -19,16 +19,27 @@ type PlayScene struct {
|
|||
// Configuration attributes.
|
||||
Filename string
|
||||
Level *level.Level
|
||||
CanEdit bool // i.e. you came from the Editor Mode
|
||||
HasNext bool // has a next level to load next
|
||||
|
||||
// Private variables.
|
||||
d *Doodle
|
||||
drawing *uix.Canvas
|
||||
scripting *scripting.Supervisor
|
||||
running bool
|
||||
|
||||
// UI widgets.
|
||||
supervisor *ui.Supervisor
|
||||
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.
|
||||
debPosition *string
|
||||
debViewport *string
|
||||
|
@ -50,6 +61,30 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
|||
s.scripting = scripting.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.
|
||||
s.debPosition = 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.")
|
||||
s.running = true
|
||||
|
||||
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.
|
||||
func (s *PlayScene) EditLevel() {
|
||||
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.
|
||||
func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
|
||||
// Update debug overlay values.
|
||||
|
@ -166,14 +291,17 @@ func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
// Is the simulation still running?
|
||||
if s.running {
|
||||
// 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)
|
||||
if err := s.drawing.Loop(ev); err != nil {
|
||||
log.Error("Drawing loop error: %s", err.Error())
|
||||
s.movePlayer(ev)
|
||||
if err := s.drawing.Loop(ev); err != nil {
|
||||
log.Error("Drawing loop error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -201,6 +329,16 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -248,14 +386,14 @@ func (s *PlayScene) Drawing() *uix.Canvas {
|
|||
func (s *PlayScene) LoadLevel(filename string) error {
|
||||
s.Filename = filename
|
||||
|
||||
level, err := level.LoadJSON(filename)
|
||||
level, err := level.LoadFile(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PlayScene.LoadLevel(%s): %s", filename, err)
|
||||
}
|
||||
|
||||
s.Level = level
|
||||
s.drawing.LoadLevel(s.d.Engine, s.Level)
|
||||
// s.drawing.InstallActors(s.Level.Actors)
|
||||
s.drawing.InstallActors(s.Level.Actors)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ import (
|
|||
// unique ID.
|
||||
type Supervisor struct {
|
||||
scripts map[string]*VM
|
||||
|
||||
// Global event handlers.
|
||||
onLevelExit func()
|
||||
}
|
||||
|
||||
// NewSupervisor creates a new JavaScript Supervior.
|
||||
|
@ -67,6 +70,7 @@ func (s *Supervisor) AddLevelScript(id string) error {
|
|||
|
||||
s.scripts[id] = NewVM(id)
|
||||
RegisterPublishHooks(s.scripts[id])
|
||||
RegisterEventHooks(s, s.scripts[id])
|
||||
if err := s.scripts[id].RegisterLevelHooks(); err != nil {
|
||||
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