Noah Petherbridge
79996ccd34
* Overhaul the clunky old alpha Edit Level/Doodad menu with a modernized version featuring the new ListBox widget. * The new level loader is a Window that can be spawned from anywhere instead of on a dedicated MenuScene. Updates to doodad scripts: * Actor.IsOnScreen() checks whether an actor's visual sprite box is on-screen in the level viewport. `Self.IsOnScreen()` will check for the current actor. Other changes * PlaySound() to deduplicate the same sound effect from playing at once.
307 lines
6.9 KiB
Go
307 lines
6.9 KiB
Go
package windows
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
|
"git.kirsle.net/go/render"
|
|
"git.kirsle.net/go/ui"
|
|
)
|
|
|
|
// OpenLevelEditor is the "Open a Level to Edit It" window
|
|
//
|
|
// DEPRECATED: in favor of OpenDrawing which has a nicer listbox instead
|
|
// of the ugly button grid of this window.
|
|
//
|
|
// You can invoke this old window by the shell commands
|
|
// `$ d.GotoPlayMenu()` or `$ d.GotoLoadMenu()`
|
|
type OpenLevelEditor struct {
|
|
Supervisor *ui.Supervisor
|
|
Engine render.Engine
|
|
|
|
// Load it for playing instead of editing?
|
|
LoadForPlay bool
|
|
|
|
// Callback functions.
|
|
OnPlayLevel func(filename string)
|
|
OnEditLevel func(filename string)
|
|
OnCancel func()
|
|
}
|
|
|
|
// NewOpenLevelEditor initializes the window.
|
|
func NewOpenLevelEditor(config OpenLevelEditor) *ui.Window {
|
|
var (
|
|
width, height = config.Engine.WindowSize()
|
|
columns = 4
|
|
)
|
|
|
|
// Show fewer columns on smaller devices.
|
|
if width <= enum.ScreenWidthXSmall {
|
|
columns = 1
|
|
} else if width <= enum.ScreenWidthSmall {
|
|
columns = 2
|
|
} else if width <= enum.ScreenWidthMedium {
|
|
columns = 3
|
|
}
|
|
|
|
window := ui.NewWindow("Open Drawing")
|
|
window.Configure(ui.Config{
|
|
Width: int(float64(width) * 0.75),
|
|
Height: int(float64(height) * 0.75),
|
|
Background: render.Grey,
|
|
})
|
|
|
|
{
|
|
frame := ui.NewFrame("Open Drawing Frame")
|
|
window.Pack(frame, ui.Pack{
|
|
Side: ui.N,
|
|
Fill: true,
|
|
Expand: true,
|
|
})
|
|
|
|
/******************
|
|
* Frame for selecting User Levels
|
|
******************/
|
|
|
|
label1 := ui.NewLabel(ui.Label{
|
|
Text: "Levels",
|
|
Font: balance.LabelFont,
|
|
})
|
|
frame.Pack(label1, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
})
|
|
|
|
// Get the user's levels.
|
|
levels, _ := userdir.ListLevels()
|
|
|
|
// Embedded levels, TODO
|
|
sysLevels, _ := level.ListSystemLevels()
|
|
levels = append(levels, sysLevels...)
|
|
|
|
// Sort them!
|
|
sort.Slice(
|
|
levels, func(i, j int) bool {
|
|
return strings.ToLower(levels[i]) < strings.ToLower(levels[j])
|
|
},
|
|
)
|
|
|
|
lvlRow := ui.NewFrame("Level Row 0")
|
|
frame.Pack(lvlRow, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
for i, lvl := range levels {
|
|
func(i int, lvl string) {
|
|
btn := ui.NewButton("Level Btn", ui.NewLabel(ui.Label{
|
|
Text: lvl,
|
|
Font: balance.MenuFont.Update(render.Text{
|
|
PadY: 2,
|
|
}),
|
|
}))
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
if config.LoadForPlay {
|
|
config.OnPlayLevel(lvl)
|
|
} else {
|
|
config.OnEditLevel(lvl)
|
|
}
|
|
return nil
|
|
})
|
|
config.Supervisor.Add(btn)
|
|
lvlRow.Pack(btn, ui.Pack{
|
|
Side: ui.W,
|
|
Expand: true,
|
|
Fill: true,
|
|
})
|
|
|
|
if columns == 1 || i > 0 && (i+1)%columns == 0 {
|
|
lvlRow = ui.NewFrame(fmt.Sprintf("Level Row %d", i))
|
|
frame.Pack(lvlRow, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
}
|
|
}(i, lvl)
|
|
}
|
|
|
|
// Browse button for local filesystem.
|
|
browseLevelFrame := ui.NewFrame("Browse Level Frame")
|
|
frame.Pack(browseLevelFrame, ui.Pack{
|
|
Side: ui.N,
|
|
Expand: true,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
|
|
browseLevelButton := ui.NewButton("Browse Level", ui.NewLabel(ui.Label{
|
|
Text: "Browse...",
|
|
Font: balance.MenuFont,
|
|
}))
|
|
browseLevelButton.SetStyle(&balance.ButtonPrimary)
|
|
browseLevelFrame.Pack(browseLevelButton, ui.Pack{
|
|
Side: ui.W,
|
|
})
|
|
|
|
browseLevelButton.Handle(ui.Click, func(ed ui.EventData) error {
|
|
filename, err := native.OpenFile("Choose a .level file", "*.level")
|
|
if err != nil {
|
|
log.Error("Couldn't show file dialog: %s", err)
|
|
return nil
|
|
}
|
|
|
|
if config.LoadForPlay {
|
|
config.OnPlayLevel(filename)
|
|
} else {
|
|
config.OnEditLevel(filename)
|
|
}
|
|
return nil
|
|
})
|
|
config.Supervisor.Add(browseLevelButton)
|
|
|
|
/******************
|
|
* Frame for selecting User Doodads
|
|
******************/
|
|
|
|
// Doodads not shown if we're loading a map to play, nor are they
|
|
// available to the free version.
|
|
if !config.LoadForPlay && !balance.FreeVersion {
|
|
label2 := ui.NewLabel(ui.Label{
|
|
Text: "Doodads",
|
|
Font: balance.LabelFont,
|
|
})
|
|
frame.Pack(label2, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
})
|
|
|
|
files, _ := userdir.ListDoodads()
|
|
|
|
// Sort them!
|
|
sort.Slice(
|
|
files, func(i, j int) bool {
|
|
return strings.ToLower(files[i]) < strings.ToLower(files[j])
|
|
},
|
|
)
|
|
|
|
ddRow := ui.NewFrame("Doodad Row 0")
|
|
frame.Pack(ddRow, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
for i, dd := range files {
|
|
func(i int, dd string) {
|
|
btn := ui.NewButton("Doodad Btn", ui.NewLabel(ui.Label{
|
|
Text: dd,
|
|
Font: balance.MenuFont,
|
|
}))
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
config.OnEditLevel(dd)
|
|
return nil
|
|
})
|
|
config.Supervisor.Add(btn)
|
|
ddRow.Pack(btn, ui.Pack{
|
|
Side: ui.W,
|
|
Expand: true,
|
|
Fill: true,
|
|
})
|
|
|
|
if i > 0 && (i+1)%4 == 0 {
|
|
ddRow = ui.NewFrame(fmt.Sprintf("Doodad Row %d", i))
|
|
frame.Pack(ddRow, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
}
|
|
}(i, dd)
|
|
}
|
|
|
|
// Browse button for local filesystem.
|
|
browseDoodadFrame := ui.NewFrame("Browse Doodad Frame")
|
|
frame.Pack(browseDoodadFrame, ui.Pack{
|
|
Side: ui.N,
|
|
Expand: true,
|
|
FillX: true,
|
|
PadY: 1,
|
|
})
|
|
|
|
browseDoodadButton := ui.NewButton("Browse Doodad", ui.NewLabel(ui.Label{
|
|
Text: "Browse...",
|
|
Font: balance.MenuFont,
|
|
}))
|
|
browseDoodadButton.SetStyle(&balance.ButtonPrimary)
|
|
browseDoodadFrame.Pack(browseDoodadButton, ui.Pack{
|
|
Side: ui.W,
|
|
})
|
|
|
|
browseDoodadButton.Handle(ui.Click, func(ed ui.EventData) error {
|
|
filename, err := native.OpenFile("Choose a .doodad file", "*.doodad")
|
|
if err != nil {
|
|
log.Error("Couldn't show file dialog: %s", err)
|
|
return nil
|
|
}
|
|
|
|
if config.LoadForPlay {
|
|
config.OnPlayLevel(filename)
|
|
} else {
|
|
config.OnEditLevel(filename)
|
|
}
|
|
return nil
|
|
})
|
|
config.Supervisor.Add(browseDoodadButton)
|
|
}
|
|
|
|
/******************
|
|
* Confirm/cancel buttons.
|
|
******************/
|
|
|
|
bottomFrame := ui.NewFrame("Button Frame")
|
|
// bottomFrame.Configure(ui.Config{
|
|
// BorderSize: 1,
|
|
// BorderStyle: ui.BorderSunken,
|
|
// BorderColor: render.Black,
|
|
// })
|
|
// bottomFrame.SetBackground(render.Grey)
|
|
frame.Pack(bottomFrame, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
PadY: 8,
|
|
})
|
|
|
|
var buttons = []struct {
|
|
Label string
|
|
F func(ui.EventData) error
|
|
}{
|
|
{"Cancel", func(ed ui.EventData) error {
|
|
config.OnCancel()
|
|
return nil
|
|
}},
|
|
}
|
|
for _, t := range buttons {
|
|
btn := ui.NewButton(t.Label, ui.NewLabel(ui.Label{
|
|
Text: t.Label,
|
|
Font: balance.MenuFont,
|
|
}))
|
|
btn.Handle(ui.Click, t.F)
|
|
config.Supervisor.Add(btn)
|
|
bottomFrame.Pack(btn, ui.Pack{
|
|
Side: ui.W,
|
|
PadX: 4,
|
|
PadY: 8,
|
|
})
|
|
}
|
|
}
|
|
|
|
return window
|
|
}
|