705 lines
18 KiB
Go
705 lines
18 KiB
Go
package windows
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
|
|
"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/modal"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
|
magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form"
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/wallpaper"
|
|
"git.kirsle.net/go/render"
|
|
"git.kirsle.net/go/ui"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// AddEditLevel is the "Create New Level & Edit Level Properties" window
|
|
type AddEditLevel struct {
|
|
Supervisor *ui.Supervisor
|
|
Engine render.Engine
|
|
|
|
// Editing settings for an existing level?
|
|
EditLevel *level.Level
|
|
|
|
// Show the "New Doodad" tab by default?
|
|
NewDoodad bool
|
|
|
|
// Callback functions.
|
|
OnChangePageTypeAndWallpaper func(pageType level.PageType, wallpaper string)
|
|
OnCreateNewLevel func(*level.Level)
|
|
OnCreateNewDoodad func(width, height int)
|
|
OnUpdateScreenshot func() error
|
|
OnReload func()
|
|
OnCancel func()
|
|
}
|
|
|
|
// NewAddEditLevel initializes the window.
|
|
func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
|
// Default options.
|
|
var (
|
|
title = "New Drawing"
|
|
)
|
|
|
|
// Given a level to edit?
|
|
if config.EditLevel != nil {
|
|
title = "Level Properties"
|
|
}
|
|
|
|
window := ui.NewWindow(title)
|
|
window.SetButtons(ui.CloseButton)
|
|
window.Configure(ui.Config{
|
|
Width: 400,
|
|
Height: 290,
|
|
Background: render.Grey,
|
|
})
|
|
|
|
// Tabbed UI for New Level or New Doodad.
|
|
tabframe := ui.NewTabFrame("Level Tabs")
|
|
window.Pack(tabframe, ui.Pack{
|
|
Side: ui.N,
|
|
Fill: true,
|
|
Expand: true,
|
|
})
|
|
|
|
// Add the tabs.
|
|
config.setupLevelFrame(tabframe) // Level Properties (always)
|
|
if config.EditLevel == nil {
|
|
// New Doodad properties (New window only)
|
|
config.setupDoodadFrame(tabframe)
|
|
} else {
|
|
// Additional Level tabs (existing level only)
|
|
config.setupGameRuleFrame(tabframe)
|
|
config.setupScreenshotFrame(tabframe)
|
|
config.setupAdvancedFrame(tabframe)
|
|
}
|
|
|
|
tabframe.Supervise(config.Supervisor)
|
|
|
|
// Show the doodad tab?
|
|
if config.NewDoodad {
|
|
tabframe.SetTab("doodad")
|
|
}
|
|
|
|
window.Hide()
|
|
return window
|
|
}
|
|
|
|
// Creates the Create/Edit Level tab ("index").
|
|
func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|
// Default options.
|
|
var (
|
|
tabLabel = "New Level"
|
|
newPageType = level.Bounded.String()
|
|
newWallpaper = "notebook.png"
|
|
paletteName = level.DefaultPaletteNames[0]
|
|
isNewLevel = config.EditLevel == nil
|
|
|
|
// Default text for the Palette drop-down for already-existing levels.
|
|
// (needs --experimental feature flag to enable the UI).
|
|
textCurrentPalette = "Keep current palette"
|
|
|
|
// For NEW levels, if a custom wallpaper is selected from disk, cache
|
|
// it in these vars. For pre-existing levels, the wallpaper updates
|
|
// immediately in the live config.EditLevel object.
|
|
newWallpaperB64 string
|
|
)
|
|
|
|
// Given a level to edit?
|
|
if !isNewLevel {
|
|
tabLabel = "Properties"
|
|
newPageType = config.EditLevel.PageType.String()
|
|
newWallpaper = config.EditLevel.Wallpaper
|
|
paletteName = textCurrentPalette
|
|
}
|
|
|
|
frame := tf.AddTab("index", ui.NewLabel(ui.Label{
|
|
Text: tabLabel,
|
|
Font: balance.TabFont,
|
|
}))
|
|
|
|
/******************
|
|
* Frame for selecting Page Type
|
|
******************/
|
|
|
|
// Selected "Page Type" property.
|
|
var pageType = level.Bounded
|
|
if !isNewLevel {
|
|
pageType = config.EditLevel.PageType
|
|
}
|
|
|
|
form := magicform.Form{
|
|
Supervisor: config.Supervisor,
|
|
Engine: config.Engine,
|
|
Vertical: true,
|
|
LabelWidth: 120,
|
|
PadY: 2,
|
|
}
|
|
fields := []magicform.Field{
|
|
{
|
|
Label: "Page type:",
|
|
Font: balance.UIFont,
|
|
Options: []magicform.Option{
|
|
{
|
|
Label: "Bounded",
|
|
Value: level.Bounded,
|
|
},
|
|
{
|
|
Label: "Unbounded",
|
|
Value: level.Unbounded,
|
|
},
|
|
{
|
|
Label: "No Negative Space",
|
|
Value: level.NoNegativeSpace,
|
|
},
|
|
},
|
|
SelectValue: pageType,
|
|
OnSelect: func(v interface{}) {
|
|
value, _ := v.(level.PageType)
|
|
newPageType = value.String() // for the "New" screen background
|
|
config.OnChangePageTypeAndWallpaper(value, newWallpaper)
|
|
},
|
|
},
|
|
}
|
|
|
|
/******************
|
|
* Wallpaper settings
|
|
******************/
|
|
|
|
var selectedWallpaper = "notebook.png"
|
|
if config.EditLevel != nil {
|
|
selectedWallpaper = config.EditLevel.Wallpaper
|
|
}
|
|
|
|
fields = append(fields, []magicform.Field{
|
|
{
|
|
Label: "Wallpaper:",
|
|
Font: balance.UIFont,
|
|
SelectValue: selectedWallpaper,
|
|
Options: balance.Wallpapers,
|
|
OnSelect: func(v interface{}) {
|
|
if filename, ok := v.(string); ok {
|
|
// Picking the Custom option?
|
|
if filename == balance.CustomWallpaperFilename {
|
|
filename, err := native.OpenFile("Choose a custom wallpaper:", "*.png *.jpg *.gif")
|
|
if err == nil {
|
|
b64data, err := wallpaper.FileToB64(filename)
|
|
if err != nil {
|
|
shmem.Flash("Error loading wallpaper: %s", err)
|
|
return
|
|
}
|
|
|
|
// If editing a level, apply the update straight away.
|
|
if config.EditLevel != nil {
|
|
config.EditLevel.SetFile(balance.CustomWallpaperEmbedPath, []byte(b64data))
|
|
newWallpaper = balance.CustomWallpaperFilename
|
|
|
|
// Trigger the page type change to the caller.
|
|
if pageType, ok := level.PageTypeFromString(newPageType); ok {
|
|
config.OnChangePageTypeAndWallpaper(pageType, balance.CustomWallpaperFilename)
|
|
}
|
|
} else {
|
|
// Hold onto the new wallpaper until the level is created.
|
|
newWallpaper = balance.CustomWallpaperFilename
|
|
newWallpaperB64 = b64data
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
if pageType, ok := level.PageTypeFromString(newPageType); ok {
|
|
config.OnChangePageTypeAndWallpaper(pageType, filename)
|
|
newWallpaper = filename
|
|
}
|
|
}
|
|
},
|
|
},
|
|
}...)
|
|
|
|
/******************
|
|
* Frame for picking a default color palette.
|
|
******************/
|
|
|
|
// For new level or --experimental only.
|
|
if config.EditLevel == nil || balance.Feature.ChangePalette {
|
|
var (
|
|
palettes = []magicform.Option{}
|
|
)
|
|
|
|
if config.EditLevel != nil {
|
|
palettes = append(palettes, []magicform.Option{
|
|
{
|
|
Label: paletteName, // "Keep current palette"
|
|
Value: paletteName,
|
|
},
|
|
{
|
|
Separator: true,
|
|
},
|
|
}...)
|
|
}
|
|
|
|
for _, palName := range level.DefaultPaletteNames {
|
|
palettes = append(palettes, magicform.Option{
|
|
Label: palName,
|
|
Value: palName,
|
|
})
|
|
}
|
|
|
|
// Add form fields.
|
|
fields = append(fields, []magicform.Field{
|
|
{
|
|
Label: "Palette:",
|
|
Font: balance.UIFont,
|
|
Options: palettes,
|
|
OnSelect: func(v interface{}) {
|
|
value, _ := v.(string)
|
|
paletteName = value
|
|
},
|
|
},
|
|
}...)
|
|
}
|
|
|
|
/******************
|
|
* Extended options for editing existing level (vs. Create New screen)
|
|
******************/
|
|
|
|
if config.EditLevel != nil {
|
|
var (
|
|
levelSizeStr = fmt.Sprintf("%dx%d", config.EditLevel.MaxWidth, config.EditLevel.MaxHeight)
|
|
levelSizeRegexp = regexp.MustCompile(`^(\d+)x(\d+)$`)
|
|
)
|
|
fields = append(fields, []magicform.Field{
|
|
{
|
|
Label: "Limits (bounded):",
|
|
Font: balance.UIFont,
|
|
TextVariable: &levelSizeStr,
|
|
OnClick: func() {
|
|
shmem.Prompt(fmt.Sprintf("Enter new limits in WxH format or [%s]: ", levelSizeStr), func(answer string) {
|
|
if answer == "" {
|
|
return
|
|
}
|
|
|
|
match := levelSizeRegexp.FindStringSubmatch(answer)
|
|
if match == nil {
|
|
return
|
|
}
|
|
|
|
levelSizeStr = match[0]
|
|
width, _ := strconv.Atoi(match[1])
|
|
height, _ := strconv.Atoi(match[2])
|
|
|
|
config.EditLevel.MaxWidth = int64(width)
|
|
config.EditLevel.MaxHeight = int64(height)
|
|
})
|
|
},
|
|
},
|
|
{
|
|
Label: "Metadata",
|
|
Font: balance.LabelFont,
|
|
},
|
|
{
|
|
Label: "Title:",
|
|
Font: balance.UIFont,
|
|
TextVariable: &config.EditLevel.Title,
|
|
PromptUser: func(answer string) {
|
|
config.EditLevel.Title = answer
|
|
},
|
|
},
|
|
{
|
|
Label: "Author:",
|
|
Font: balance.UIFont,
|
|
TextVariable: &config.EditLevel.Author,
|
|
PromptUser: func(answer string) {
|
|
config.EditLevel.Author = answer
|
|
},
|
|
},
|
|
}...)
|
|
}
|
|
|
|
// The confirm/cancel buttons.
|
|
var okLabel = "Apply"
|
|
if config.EditLevel == nil {
|
|
okLabel = "Continue"
|
|
}
|
|
fields = append(fields, []magicform.Field{
|
|
{
|
|
Buttons: []magicform.Field{
|
|
{
|
|
ButtonStyle: &balance.ButtonPrimary,
|
|
Label: okLabel,
|
|
Font: balance.UIFont,
|
|
OnClick: func() {
|
|
// Is it a NEW level?
|
|
if config.EditLevel == nil {
|
|
shmem.Flash("Create new map with %s page type and %s wallpaper", newPageType, newWallpaper)
|
|
pageType, ok := level.PageTypeFromString(newPageType)
|
|
if !ok {
|
|
shmem.Flash("Invalid Page Type '%s'", newPageType)
|
|
return
|
|
}
|
|
|
|
lvl := level.New()
|
|
lvl.Palette = level.DefaultPalettes[paletteName]
|
|
lvl.Wallpaper = newWallpaper
|
|
lvl.PageType = pageType
|
|
|
|
// Was a custom wallpaper selected for our NEW level?
|
|
if lvl.Wallpaper == balance.CustomWallpaperFilename && len(newWallpaperB64) > 0 {
|
|
lvl.SetFile(balance.CustomWallpaperEmbedPath, []byte(newWallpaperB64))
|
|
}
|
|
|
|
if config.OnCreateNewLevel != nil {
|
|
config.OnCreateNewLevel(lvl)
|
|
} else {
|
|
shmem.FlashError("OnCreateNewLevel not attached")
|
|
}
|
|
} else {
|
|
// Editing an existing level.
|
|
|
|
// If we're editing a level, did we select a new palette?
|
|
// Warn the user about if they want to change palettes.
|
|
if paletteName != textCurrentPalette {
|
|
modal.Confirm(
|
|
"Are you sure you want to change the level palette?\n" +
|
|
"Existing pixels drawn on your level may change, and\n" +
|
|
"if the new palette is smaller, some pixels may be\n" +
|
|
"lost from your level. OK to continue?",
|
|
).WithTitle("Change Level Palette").Then(func() {
|
|
// Install the new level palette.
|
|
config.EditLevel.ReplacePalette(level.DefaultPalettes[paletteName])
|
|
if config.OnReload != nil {
|
|
config.OnReload()
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
config.OnCancel()
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Label: "Cancel",
|
|
Font: balance.UIFont,
|
|
OnClick: func() {
|
|
config.OnCancel()
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}...)
|
|
|
|
form.Create(frame, fields)
|
|
}
|
|
|
|
// Creates the "New Doodad" frame.
|
|
func (config AddEditLevel) setupDoodadFrame(tf *ui.TabFrame) {
|
|
// Default options.
|
|
var (
|
|
doodadWidth = 64
|
|
doodadHeight = doodadWidth
|
|
)
|
|
|
|
frame := tf.AddTab("doodad", ui.NewLabel(ui.Label{
|
|
Text: "New Doodad",
|
|
Font: balance.TabFont,
|
|
}))
|
|
|
|
/******************
|
|
* Frame for selecting Page Type
|
|
******************/
|
|
|
|
var sizeOptions = []magicform.Option{
|
|
{Label: "32", Value: 32},
|
|
{Label: "64", Value: 64},
|
|
{Label: "96", Value: 96},
|
|
{Label: "128", Value: 128},
|
|
{Label: "200", Value: 200},
|
|
{Label: "256", Value: 256},
|
|
{Label: "Custom...", Value: 0},
|
|
}
|
|
|
|
form := magicform.Form{
|
|
Supervisor: config.Supervisor,
|
|
Engine: config.Engine,
|
|
Vertical: true,
|
|
LabelWidth: 90,
|
|
}
|
|
form.Create(frame, []magicform.Field{
|
|
{
|
|
Label: "Width:",
|
|
Font: balance.LabelFont,
|
|
Type: magicform.Selectbox,
|
|
IntVariable: &doodadWidth,
|
|
Options: sizeOptions,
|
|
OnSelect: func(v interface{}) {
|
|
if v.(int) == 0 {
|
|
shmem.Prompt("Enter a custom size for the doodad width: ", func(answer string) {
|
|
if a, err := strconv.Atoi(answer); err == nil && a > 0 {
|
|
doodadWidth = a
|
|
} else {
|
|
shmem.FlashError("Doodad size should be a number greater than zero.")
|
|
}
|
|
})
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Label: "Height:",
|
|
Font: balance.LabelFont,
|
|
Type: magicform.Selectbox,
|
|
IntVariable: &doodadHeight,
|
|
Options: sizeOptions,
|
|
OnSelect: func(v interface{}) {
|
|
if v.(int) == 0 {
|
|
shmem.Prompt("Enter a custom size for the doodad height: ", func(answer string) {
|
|
if a, err := strconv.Atoi(answer); err == nil && a > 0 {
|
|
doodadHeight = a
|
|
} else {
|
|
shmem.FlashError("Doodad size should be a number greater than zero.")
|
|
}
|
|
})
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Buttons: []magicform.Field{
|
|
{
|
|
Label: "Continue",
|
|
Font: balance.UIFont,
|
|
ButtonStyle: &balance.ButtonPrimary,
|
|
OnClick: func() {
|
|
if config.OnCreateNewDoodad != nil {
|
|
config.OnCreateNewDoodad(doodadWidth, doodadHeight)
|
|
} else {
|
|
shmem.FlashError("OnCreateNewDoodad not attached")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Label: "Cancel",
|
|
Font: balance.UIFont,
|
|
ButtonStyle: &balance.ButtonPrimary,
|
|
OnClick: func() {
|
|
if config.OnCancel != nil {
|
|
config.OnCancel()
|
|
} else {
|
|
shmem.FlashError("OnCancel not attached")
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
}
|
|
|
|
// Creates the Game Rules frame for existing level (set difficulty, etc.)
|
|
func (config AddEditLevel) setupGameRuleFrame(tf *ui.TabFrame) {
|
|
frame := tf.AddTab("GameRules", ui.NewLabel(ui.Label{
|
|
Text: "Game Rules",
|
|
Font: balance.TabFont,
|
|
}))
|
|
|
|
form := magicform.Form{
|
|
Supervisor: config.Supervisor,
|
|
Engine: config.Engine,
|
|
Vertical: true,
|
|
LabelWidth: 120,
|
|
PadY: 2,
|
|
}
|
|
fields := []magicform.Field{
|
|
{
|
|
Label: "Game Rules are specific to this level and can change some of\n" +
|
|
"the game's default behaviors.",
|
|
Font: balance.UIFont,
|
|
},
|
|
{
|
|
Label: "Difficulty:",
|
|
Font: balance.UIFont,
|
|
SelectValue: config.EditLevel.GameRule.Difficulty,
|
|
Tooltip: ui.Tooltip{
|
|
Text: "Peaceful: enemies may not attack\n" +
|
|
"Normal: default difficulty\n" +
|
|
"Hard: enemies may be more aggressive",
|
|
Edge: ui.Top,
|
|
},
|
|
Options: []magicform.Option{
|
|
{
|
|
Label: "Peaceful",
|
|
Value: enum.Peaceful,
|
|
},
|
|
{
|
|
Label: "Normal (recommended)",
|
|
Value: enum.Normal,
|
|
},
|
|
{
|
|
Label: "Hard",
|
|
Value: enum.Hard,
|
|
},
|
|
},
|
|
OnSelect: func(v interface{}) {
|
|
value, _ := v.(enum.Difficulty)
|
|
config.EditLevel.GameRule.Difficulty = value
|
|
log.Info("Set level difficulty to: %d (%s)", value, value)
|
|
},
|
|
},
|
|
{
|
|
Label: "Survival Mode (silver high score)",
|
|
Font: balance.UIFont,
|
|
BoolVariable: &config.EditLevel.GameRule.Survival,
|
|
Tooltip: ui.Tooltip{
|
|
Text: "Use for levels where dying at least once is very likely\n" +
|
|
"(e.g. Azulian Tag). The silver high score will be for\n" +
|
|
"longest time rather than fastest time. The gold high\n" +
|
|
"score will still be for fastest time.",
|
|
Edge: ui.Top,
|
|
},
|
|
},
|
|
}
|
|
|
|
form.Create(frame, fields)
|
|
}
|
|
|
|
// Level Screenshot management frame.
|
|
func (config AddEditLevel) setupScreenshotFrame(tf *ui.TabFrame) {
|
|
frame := tf.AddTab("Screenshot", ui.NewLabel(ui.Label{
|
|
Text: "Screenshot",
|
|
Font: balance.TabFont,
|
|
}))
|
|
|
|
var image *ui.Image
|
|
|
|
// Have a screenshot already?
|
|
if config.EditLevel.HasScreenshot() {
|
|
if img, err := config.EditLevel.GetScreenshotImageAsUIImage(balance.LevelScreenshotSmallFilename); err != nil {
|
|
lbl := ui.NewLabel(ui.Label{
|
|
Text: err.Error(),
|
|
Font: balance.DangerFont,
|
|
})
|
|
frame.Pack(lbl, ui.Pack{
|
|
Side: ui.N,
|
|
})
|
|
} else {
|
|
// Draw the image.
|
|
log.Error("Got img: %+v", img)
|
|
frame.Pack(img, ui.Pack{
|
|
Side: ui.N,
|
|
})
|
|
|
|
// Hold onto it in case we need to refresh it.
|
|
image = img
|
|
}
|
|
} else {
|
|
lbl := ui.NewLabel(ui.Label{
|
|
Text: "This level has no screenshot available. If this is\n" +
|
|
"a new drawing, its first screenshot will be created\n" +
|
|
"upon save; playtest or close and re-open the level\n" +
|
|
"then and its screenshot should appear here and can\n" +
|
|
"be refreshed.",
|
|
Font: balance.DangerFont,
|
|
})
|
|
frame.Pack(lbl, ui.Pack{
|
|
Side: ui.N,
|
|
})
|
|
|
|
// Exit now - don't add the Refresh button in case of nil pointer exception
|
|
// while we are in this state.
|
|
return
|
|
}
|
|
|
|
// Image refresh button.
|
|
btn := ui.NewButton("Refresh", ui.NewLabel(ui.Label{
|
|
Text: "Update Screenshot",
|
|
Font: balance.UIFont,
|
|
}))
|
|
ui.NewTooltip(btn, ui.Tooltip{
|
|
Text: "Update the screenshot from your current scroll point\nin the level editor.",
|
|
})
|
|
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
// Take a new screenshot.
|
|
if config.OnUpdateScreenshot == nil {
|
|
shmem.FlashError("OnUpdateScreenshot handler not configured!")
|
|
} else {
|
|
if err := config.OnUpdateScreenshot(); err != nil {
|
|
modal.Alert(err.Error()).WithTitle("Error updating screenshot")
|
|
} else {
|
|
// Get the updated screenshot image from the level.
|
|
if img, err := config.EditLevel.GetScreenshotImage(balance.LevelScreenshotSmallFilename); err != nil {
|
|
shmem.FlashError("Couldn't reload screenshot from level: %s", err)
|
|
} else {
|
|
image.ReplaceFromImage(img)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
btn.Compute(config.Engine)
|
|
|
|
frame.Pack(btn, ui.Pack{
|
|
Side: ui.N,
|
|
Expand: true,
|
|
})
|
|
|
|
config.Supervisor.Add(btn)
|
|
}
|
|
|
|
// Creates the Game Rules frame for existing level (set difficulty, etc.)
|
|
func (config AddEditLevel) setupAdvancedFrame(tf *ui.TabFrame) {
|
|
frame := tf.AddTab("Advanced", ui.NewLabel(ui.Label{
|
|
Text: "Advanced",
|
|
Font: balance.TabFont,
|
|
}))
|
|
|
|
form := magicform.Form{
|
|
Supervisor: config.Supervisor,
|
|
Engine: config.Engine,
|
|
Vertical: true,
|
|
LabelWidth: 120,
|
|
PadY: 2,
|
|
}
|
|
fields := []magicform.Field{
|
|
{
|
|
Label: "Level UUID Number",
|
|
Font: balance.LabelFont,
|
|
},
|
|
{
|
|
Label: "Levels are assigned a unique identifier (UUID) for the purpose\n" +
|
|
"of saving your high scores for them (in levels which are part of\n" +
|
|
"level packs). Your level's UUID is shown below. Click it to\n" +
|
|
"re-roll a new UUID number (e.g. in case you save a new copy\n" +
|
|
"of a level that you want to be distinct from its original.)",
|
|
Font: balance.UIFont,
|
|
},
|
|
{
|
|
Label: "Level UUID:",
|
|
Font: balance.UIFont,
|
|
TextVariable: &config.EditLevel.UUID,
|
|
Tooltip: ui.Tooltip{
|
|
Text: "Click to re-roll a new UUID value.",
|
|
Edge: ui.Top,
|
|
},
|
|
OnClick: func() {
|
|
modal.Confirm(
|
|
"Are you sure you want to re-roll a new UUID value?\n\n" +
|
|
"Saving with a new UUID will mark this level as distinct\n" +
|
|
"from the previous version - if you place both versions\n" +
|
|
"in a levelpack together they will have separate high\n" +
|
|
"score values.",
|
|
).WithTitle("Re-roll UUID").Then(func() {
|
|
config.EditLevel.UUID = uuid.New().String()
|
|
})
|
|
},
|
|
},
|
|
}
|
|
|
|
form.Create(frame, fields)
|
|
}
|