Level Difficulty + UI Polish
Added a new level property: Difficulty * An enum ranging from -1, 0, 1 (Peaceful, Normal, Hard) * Default difficulty is Normal; pre-existing levels are Normal by default per the zero value. Doodad scripts can read the difficulty via the new global variable `Level.Difficulty` and some doodads have been updated: * Azulians: on Peaceful they ignore all player characters, and on Hard they are in "hunt mode": infinite aggro radius and they're aggressive to all characters. * Bird: on Peaceful they will not dive and attack any player character. Other spit and polish: * New Level/Level Properties UI reworked into a magicform. * New "PromptPre(question, answer, func)" function for prompting the user with the developer shell, but pre-filling in an answer for them to either post or edit. * magicform has a PromptUser field option for simple Text/Int fields which present as buttons, so magicform can prompt and update the variable itself. * Don't show the _autosave.doodad in the Doodad Dropper window.
This commit is contained in:
parent
661c5f4365
commit
647124495b
|
@ -71,7 +71,8 @@ function main() {
|
|||
myPt = Self.Position();
|
||||
|
||||
// If the player is within aggro range, move towards.
|
||||
if (Math.abs(playerPt.X - myPt.X) < aggroX && Math.abs(playerPt.Y - myPt.Y) < aggroY) {
|
||||
if ((Math.abs(playerPt.X - myPt.X) < aggroX && Math.abs(playerPt.Y - myPt.Y) < aggroY)
|
||||
|| (Level.Difficulty > 0)) {
|
||||
direction = playerPt.X < myPt.X ? "left" : "right";
|
||||
followPlayer = true;
|
||||
|
||||
|
@ -134,11 +135,16 @@ function playerControls() {
|
|||
// will be hostile towards the player). Boring players will not be chased after and
|
||||
// the Azulian will not harm them if they make contact.
|
||||
function isPlayerFood(actor) {
|
||||
// Not a player or is invulnerable.
|
||||
if (!actor.IsPlayer() || actor.Invulnerable()) {
|
||||
// Not a player or is invulnerable, or Peaceful difficulty.
|
||||
if (!actor.IsPlayer() || actor.Invulnerable() || Level.Difficulty < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// On hard mode they are hostile to any player.
|
||||
if (Level.Difficulty > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Azulians are friendly to Thieves and other Azulians.
|
||||
if (actor.Doodad().Filename === "thief.doodad" || actor.Doodad().Title.indexOf("Azulian") > -1) {
|
||||
return false;
|
||||
|
|
|
@ -109,6 +109,11 @@ function main() {
|
|||
// It's not hostile towards characters that can fly (having
|
||||
// no gravity).
|
||||
function AI_ScanForPlayer() {
|
||||
// If Peaceful difficulty, do not attack.
|
||||
if (Level.Difficulty < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let stepY = 12, // number of pixels to skip
|
||||
stepX = stepY,
|
||||
limit = stepX * 20, // furthest we'll scan
|
||||
|
|
|
@ -73,6 +73,7 @@ func New(debug bool, engine render.Engine) *Doodle {
|
|||
shmem.Flash = d.Flash
|
||||
shmem.FlashError = d.FlashError
|
||||
shmem.Prompt = d.Prompt
|
||||
shmem.PromptPre = d.PromptPre
|
||||
|
||||
if debug {
|
||||
log.Logger.Config.Level = golog.DebugLevel
|
||||
|
|
|
@ -25,3 +25,21 @@ const (
|
|||
ScreenWidthMedium = 800
|
||||
ScreenWidthLarge = 1000
|
||||
)
|
||||
|
||||
type Difficulty int
|
||||
|
||||
const (
|
||||
// The zero value is the default (Normal) so is b/w compatible with
|
||||
// level files pre-difficulty setting.
|
||||
Peaceful Difficulty = iota - 1
|
||||
Normal
|
||||
Hard
|
||||
)
|
||||
|
||||
func (d Difficulty) String() string {
|
||||
return []string{
|
||||
"Normal",
|
||||
"Hard",
|
||||
"Peaceful",
|
||||
}[d]
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
||||
|
@ -31,6 +32,7 @@ type Base struct {
|
|||
type Level struct {
|
||||
Base
|
||||
Password string `json:"passwd"`
|
||||
Difficulty enum.Difficulty `json:"difficulty"`
|
||||
|
||||
// Chunked pixel data.
|
||||
Chunker *Chunker `json:"chunks"`
|
||||
|
|
|
@ -5,7 +5,6 @@ package native
|
|||
import (
|
||||
"image"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/render/sdl"
|
||||
sdl2 "github.com/veandco/go-sdl2/sdl"
|
||||
|
@ -46,7 +45,6 @@ func TextToImage(e render.Engine, text render.Text) (image.Image, error) {
|
|||
return nil, err
|
||||
}
|
||||
defer surface.Free()
|
||||
log.Error("surf fmt: %+v", surface.Format)
|
||||
|
||||
// Convert the Surface into a pixelformat that supports the .At(x,y)
|
||||
// function properly, as the one we got above is "Not implemented"
|
||||
|
|
|
@ -36,6 +36,14 @@ func (d *Doodle) Prompt(question string, callback func(string)) {
|
|||
d.shell.Open = true
|
||||
}
|
||||
|
||||
// PromptPre prompts with a pre-filled value.
|
||||
func (d *Doodle) PromptPre(question string, prefilled string, callback func(string)) {
|
||||
d.shell.Text = prefilled
|
||||
d.shell.Prompt = question
|
||||
d.shell.callback = callback
|
||||
d.shell.Open = true
|
||||
}
|
||||
|
||||
// Shell implements the developer console in-game.
|
||||
type Shell struct {
|
||||
parent *Doodle
|
||||
|
|
|
@ -28,6 +28,7 @@ var (
|
|||
|
||||
// Globally available Prompt() function.
|
||||
Prompt func(string, func(string))
|
||||
PromptPre func(string, string, func(string))
|
||||
|
||||
// Ajax file cache for WASM use.
|
||||
AjaxCache map[string][]byte
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package uix
|
||||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/apps/doodle/pkg/keybind"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
|
@ -109,6 +110,10 @@ func (w *Canvas) commitStroke(tool drawtool.Tool, addHistory bool) {
|
|||
if w.level != nil && addHistory {
|
||||
w.level.UndoHistory.AddStroke(w.currentStroke)
|
||||
} else if w.doodad != nil && addHistory {
|
||||
if w.doodad.UndoHistory == nil {
|
||||
// HACK: if UndoHistory was not initialized properly.
|
||||
w.doodad.UndoHistory = drawtool.NewHistory(balance.UndoHistory)
|
||||
}
|
||||
w.doodad.UndoHistory.AddStroke(w.currentStroke)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ func (w *Canvas) UndoStroke() bool {
|
|||
if w.level != nil {
|
||||
undoer = w.level.UndoHistory
|
||||
} else if w.doodad != nil {
|
||||
if w.doodad.UndoHistory == nil {
|
||||
// HACK: if UndoHistory was not initialized properly.
|
||||
w.doodad.UndoHistory = drawtool.NewHistory(balance.UndoHistory)
|
||||
}
|
||||
undoer = w.doodad.UndoHistory
|
||||
} else {
|
||||
log.Error("Canvas.UndoStroke: no Level or Doodad currently available to the canvas")
|
||||
|
|
|
@ -70,6 +70,10 @@ type Field struct {
|
|||
SelectValue interface{} // Selectbox default choice
|
||||
Color *render.Color // Color
|
||||
|
||||
// For text-type fields, opt-in to let magicform prompt the
|
||||
// user using the game's developer shell.
|
||||
PromptUser func(answer string)
|
||||
|
||||
// Tooltip to add to a form control.
|
||||
// Checkbox only for now.
|
||||
Tooltip ui.Tooltip // config for the tooltip only
|
||||
|
@ -83,6 +87,7 @@ type Field struct {
|
|||
type Option struct {
|
||||
Value interface{}
|
||||
Label string
|
||||
Separator bool
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -278,6 +283,22 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
|||
|
||||
// Handlers
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
// Text boxes, we want to prompt the user to enter new value?
|
||||
if row.PromptUser != nil {
|
||||
var value string
|
||||
if row.TextVariable != nil {
|
||||
value = *row.TextVariable
|
||||
} else if row.IntVariable != nil {
|
||||
value = fmt.Sprintf("%d", *row.IntVariable)
|
||||
}
|
||||
|
||||
shmem.PromptPre("Enter new value: ", value, func(answer string) {
|
||||
if answer != "" {
|
||||
row.PromptUser(answer)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if row.OnClick != nil {
|
||||
row.OnClick()
|
||||
}
|
||||
|
@ -325,6 +346,10 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
|||
|
||||
if row.Options != nil {
|
||||
for _, option := range row.Options {
|
||||
if option.Separator {
|
||||
btn.AddSeparator()
|
||||
continue
|
||||
}
|
||||
btn.AddItem(option.Label, option.Value, func() {})
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +367,12 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
|||
return nil
|
||||
})
|
||||
|
||||
// Tooltip? TODO - make nicer.
|
||||
if row.Tooltip.Text != "" || row.Tooltip.TextVariable != nil {
|
||||
tt := ui.NewTooltip(btn, row.Tooltip)
|
||||
tt.Supervise(form.Supervisor)
|
||||
}
|
||||
|
||||
btn.Supervise(form.Supervisor)
|
||||
form.Supervisor.Add(btn)
|
||||
}
|
||||
|
|
|
@ -64,6 +64,10 @@ func (w *Canvas) MakeScriptAPI(vm *scripting.VM) {
|
|||
}
|
||||
},
|
||||
})
|
||||
|
||||
vm.Set("Level", map[string]interface{}{
|
||||
"Difficulty": w.level.Difficulty,
|
||||
})
|
||||
}
|
||||
|
||||
// MakeSelfAPI generates the `Self` object for the scripting API in
|
||||
|
|
|
@ -4,10 +4,13 @@ import (
|
|||
"strconv"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
"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/native"
|
||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||
magicform "git.kirsle.net/apps/doodle/pkg/uix/magic-form"
|
||||
"git.kirsle.net/apps/doodle/pkg/wallpaper"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
|
@ -90,7 +93,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
)
|
||||
|
||||
// Given a level to edit?
|
||||
if config.EditLevel != nil {
|
||||
if !isNewLevel {
|
||||
newPageType = config.EditLevel.PageType.String()
|
||||
newWallpaper = config.EditLevel.Wallpaper
|
||||
paletteName = textCurrentPalette
|
||||
|
@ -105,205 +108,99 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
* Frame for selecting Page Type
|
||||
******************/
|
||||
|
||||
typeFrame := ui.NewFrame("Page Type Options Frame")
|
||||
frame.Pack(typeFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
})
|
||||
|
||||
label1 := ui.NewLabel(ui.Label{
|
||||
Text: "Page Type:",
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
typeFrame.Pack(label1, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
type typeObj struct {
|
||||
Name string
|
||||
Value level.PageType
|
||||
}
|
||||
var types = []typeObj{
|
||||
{"Bounded", level.Bounded},
|
||||
{"Unbounded", level.Unbounded},
|
||||
{"No Negative Space", level.NoNegativeSpace},
|
||||
// {"Bordered (TODO)", level.Bordered},
|
||||
// Selected "Page Type" property.
|
||||
var pageType = level.Bounded
|
||||
if !isNewLevel {
|
||||
pageType = config.EditLevel.PageType
|
||||
}
|
||||
|
||||
typeBtn := ui.NewSelectBox("Type Select", ui.Label{
|
||||
Font: ui.MenuFont,
|
||||
})
|
||||
typeFrame.Pack(typeBtn, ui.Pack{
|
||||
Side: ui.W,
|
||||
Expand: true,
|
||||
})
|
||||
|
||||
for _, t := range types {
|
||||
// TODO: Hide some options for the free version of the game.
|
||||
// - At launch only Bounded and Bordered will be available
|
||||
// in the shareware version.
|
||||
// - For now, only hide Bordered as it's not yet implemented.
|
||||
// --------
|
||||
// if balance.FreeVersion {
|
||||
// if t.Value == level.Bordered {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
typeBtn.AddItem(t.Name, t.Value, func() {})
|
||||
}
|
||||
|
||||
// If editing an existing level, pre-select the right page type.
|
||||
if config.EditLevel != nil {
|
||||
typeBtn.SetValue(config.EditLevel.PageType)
|
||||
}
|
||||
|
||||
typeBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
||||
if selection, ok := typeBtn.GetValue(); ok {
|
||||
if pageType, ok := selection.Value.(level.PageType); ok {
|
||||
newPageType = pageType.String()
|
||||
config.OnChangePageTypeAndWallpaper(pageType, newWallpaper)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
typeBtn.Supervise(config.Supervisor)
|
||||
config.Supervisor.Add(typeBtn)
|
||||
|
||||
/******************
|
||||
* Frame for selecting Bounded Level Limits.
|
||||
******************/
|
||||
|
||||
if config.EditLevel != nil {
|
||||
boundsFrame := ui.NewFrame("Bounds Frame")
|
||||
frame.Pack(boundsFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
form := magicform.Form{
|
||||
Supervisor: config.Supervisor,
|
||||
Engine: config.Engine,
|
||||
Vertical: true,
|
||||
LabelWidth: 120,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
label := ui.NewLabel(ui.Label{
|
||||
Text: "Bounded limits:",
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
boundsFrame.Pack(label, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
var forms = []struct {
|
||||
label string
|
||||
number *int64
|
||||
}{
|
||||
}
|
||||
fields := []magicform.Field{
|
||||
{
|
||||
label: "Width:",
|
||||
number: &config.EditLevel.MaxWidth,
|
||||
Label: "Page type:",
|
||||
Font: balance.UIFont,
|
||||
Options: []magicform.Option{
|
||||
{
|
||||
Label: "Bounded",
|
||||
Value: level.Bounded,
|
||||
},
|
||||
{
|
||||
label: "Height:",
|
||||
number: &config.EditLevel.MaxHeight,
|
||||
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)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, form := range forms {
|
||||
form := form
|
||||
label := ui.NewLabel(ui.Label{
|
||||
Text: form.label,
|
||||
Font: ui.MenuFont,
|
||||
})
|
||||
|
||||
var intvar = int(*form.number)
|
||||
button := ui.NewButton(form.label, ui.NewLabel(ui.Label{
|
||||
IntVariable: &intvar,
|
||||
Font: ui.MenuFont,
|
||||
}))
|
||||
button.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
shmem.Prompt("Enter new "+form.label+" ", func(answer string) {
|
||||
if answer == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if i, err := strconv.Atoi(answer); err == nil {
|
||||
*form.number = int64(i)
|
||||
intvar = i
|
||||
}
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
config.Supervisor.Add(button)
|
||||
|
||||
boundsFrame.Pack(label, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 1,
|
||||
})
|
||||
boundsFrame.Pack(button, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/******************
|
||||
* Frame for selecting Level Wallpaper
|
||||
* Wallpaper settings
|
||||
******************/
|
||||
|
||||
wpFrame := ui.NewFrame("Wallpaper Frame")
|
||||
frame.Pack(wpFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
label2 := ui.NewLabel(ui.Label{
|
||||
Text: "Wallpaper:",
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
wpFrame.Pack(label2, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
type wallpaperObj struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
var wallpapers = []wallpaperObj{
|
||||
{"Notebook", "notebook.png"},
|
||||
{"Legal Pad", "legal.png"},
|
||||
{"Graph paper", "graph.png"},
|
||||
{"Dotted paper", "dots.png"},
|
||||
{"Blueprint", "blueprint.png"},
|
||||
{"Pure White", "white.png"},
|
||||
// {"Placemat", "placemat.png"},
|
||||
}
|
||||
|
||||
wallBtn := ui.NewSelectBox("Wallpaper Select", ui.Label{
|
||||
Font: balance.MenuFont,
|
||||
})
|
||||
wallBtn.AlwaysChange = true
|
||||
wpFrame.Pack(wallBtn, ui.Pack{
|
||||
Side: ui.W,
|
||||
Expand: true,
|
||||
})
|
||||
|
||||
for _, t := range wallpapers {
|
||||
wallBtn.AddItem(t.Name, t.Value, func() {})
|
||||
}
|
||||
|
||||
// Add custom wallpaper options.
|
||||
if balance.Feature.CustomWallpaper {
|
||||
wallBtn.AddSeparator()
|
||||
wallBtn.AddItem("Custom wallpaper...", balance.CustomWallpaperFilename, func() {})
|
||||
}
|
||||
|
||||
// If editing a level, select the current wallpaper.
|
||||
var selectedWallpaper = "notebook.png"
|
||||
if config.EditLevel != nil {
|
||||
wallBtn.SetValue(config.EditLevel.Wallpaper)
|
||||
selectedWallpaper = config.EditLevel.Wallpaper
|
||||
}
|
||||
|
||||
wallBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
||||
if selection, ok := wallBtn.GetValue(); ok {
|
||||
if filename, ok := selection.Value.(string); ok {
|
||||
fields = append(fields, []magicform.Field{
|
||||
{
|
||||
Label: "Wallpaper:",
|
||||
Font: balance.UIFont,
|
||||
SelectValue: selectedWallpaper,
|
||||
Options: []magicform.Option{
|
||||
{
|
||||
Label: "Notebook",
|
||||
Value: "notebook.png",
|
||||
},
|
||||
{
|
||||
Label: "Legal Pad",
|
||||
Value: "legal.png",
|
||||
},
|
||||
{
|
||||
Label: "Graph paper",
|
||||
Value: "graph.png",
|
||||
},
|
||||
{
|
||||
Label: "Dotted paper",
|
||||
Value: "dots.png",
|
||||
},
|
||||
{
|
||||
Label: "Blueprint",
|
||||
Value: "blueprint.png",
|
||||
},
|
||||
{
|
||||
Label: "Pure white",
|
||||
Value: "white.png",
|
||||
},
|
||||
{
|
||||
Separator: true,
|
||||
},
|
||||
{
|
||||
Label: "Custom wallpaper...",
|
||||
Value: balance.CustomWallpaperFilename,
|
||||
},
|
||||
},
|
||||
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")
|
||||
|
@ -311,7 +208,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
b64data, err := wallpaper.FileToB64(filename)
|
||||
if err != nil {
|
||||
shmem.Flash("Error loading wallpaper: %s", err)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// If editing a level, apply the update straight away.
|
||||
|
@ -329,7 +226,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
newWallpaperB64 = b64data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
if pageType, ok := level.PageTypeFromString(newPageType); ok {
|
||||
|
@ -337,12 +234,9 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
newWallpaper = filename
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
wallBtn.Supervise(config.Supervisor)
|
||||
config.Supervisor.Add(wallBtn)
|
||||
},
|
||||
},
|
||||
}...)
|
||||
|
||||
/******************
|
||||
* Frame for picking a default color palette.
|
||||
|
@ -350,141 +244,122 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
|
||||
// For new level or --experimental only.
|
||||
if config.EditLevel == nil || balance.Feature.ChangePalette {
|
||||
palFrame := ui.NewFrame("Palette Frame")
|
||||
frame.Pack(palFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 4,
|
||||
})
|
||||
|
||||
label3 := ui.NewLabel(ui.Label{
|
||||
Text: "Palette: ",
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
palFrame.Pack(label3, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
palBtn := ui.NewSelectBox("Palette Select", ui.Label{
|
||||
Font: balance.MenuFont,
|
||||
})
|
||||
palBtn.AlwaysChange = true
|
||||
|
||||
palFrame.Pack(palBtn, ui.Pack{
|
||||
Side: ui.W,
|
||||
Expand: true,
|
||||
})
|
||||
var (
|
||||
palettes = []magicform.Option{}
|
||||
)
|
||||
|
||||
if config.EditLevel != nil {
|
||||
palBtn.AddItem(paletteName, paletteName, func() {})
|
||||
palBtn.AddSeparator()
|
||||
palettes = append(palettes, []magicform.Option{
|
||||
{
|
||||
Label: paletteName, // "Keep current palette"
|
||||
Value: paletteName,
|
||||
},
|
||||
{
|
||||
Separator: true,
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
for _, palName := range level.DefaultPaletteNames {
|
||||
palName := palName
|
||||
palBtn.AddItem(palName, palName, func() {})
|
||||
}
|
||||
|
||||
palBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
||||
if val, ok := palBtn.GetValue(); ok {
|
||||
val2, _ := val.Value.(string)
|
||||
paletteName = val2
|
||||
}
|
||||
return nil
|
||||
palettes = append(palettes, magicform.Option{
|
||||
Label: palName,
|
||||
Value: palName,
|
||||
})
|
||||
}
|
||||
|
||||
config.Supervisor.Add(palBtn)
|
||||
palBtn.Supervise(config.Supervisor)
|
||||
// Add form fields.
|
||||
fields = append(fields, []magicform.Field{
|
||||
{
|
||||
Label: "Palette:",
|
||||
Font: balance.UIFont,
|
||||
Options: palettes,
|
||||
OnSelect: func(v interface{}) {
|
||||
value, _ := v.(string)
|
||||
paletteName = value
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
/******************
|
||||
* Frame for giving the level a title.
|
||||
* Extended options for editing existing level (vs. Create New screen)
|
||||
******************/
|
||||
|
||||
if config.EditLevel != nil {
|
||||
label3 := ui.NewLabel(ui.Label{
|
||||
Text: "Metadata",
|
||||
fields = append(fields, []magicform.Field{
|
||||
{
|
||||
Label: "Difficulty:",
|
||||
Font: balance.UIFont,
|
||||
SelectValue: config.EditLevel.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.Difficulty = value
|
||||
log.Info("Set level difficulty to: %d (%s)", value, value)
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "Metadata",
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
frame.Pack(label3, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
})
|
||||
|
||||
type metadataObj struct {
|
||||
Label string
|
||||
Binding *string
|
||||
Update func(string)
|
||||
}
|
||||
var metaRows = []metadataObj{
|
||||
{"Title:", &config.EditLevel.Title, func(v string) { config.EditLevel.Title = v }},
|
||||
{"Author:", &config.EditLevel.Author, func(v string) { config.EditLevel.Author = v }},
|
||||
},
|
||||
{
|
||||
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
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
for _, mr := range metaRows {
|
||||
mr := mr
|
||||
mrFrame := ui.NewFrame("Metadata " + mr.Label + "Frame")
|
||||
frame.Pack(mrFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
// The label.
|
||||
mrLabel := ui.NewLabel(ui.Label{
|
||||
Text: mr.Label,
|
||||
Font: balance.MenuFont,
|
||||
})
|
||||
mrLabel.Configure(ui.Config{
|
||||
Width: 75,
|
||||
})
|
||||
mrFrame.Pack(mrLabel, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
// The button.
|
||||
mrButton := ui.NewButton(mr.Label, ui.NewLabel(ui.Label{
|
||||
TextVariable: mr.Binding,
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
mrButton.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
shmem.Prompt("Enter a new "+mr.Label, func(answer string) {
|
||||
if answer != "" {
|
||||
mr.Update(answer)
|
||||
// The confirm/cancel buttons.
|
||||
var okLabel = "Ok"
|
||||
if config.EditLevel == nil {
|
||||
okLabel = "Continue"
|
||||
}
|
||||
})
|
||||
return nil
|
||||
})
|
||||
config.Supervisor.Add(mrButton)
|
||||
mrFrame.Pack(mrButton, ui.Pack{
|
||||
Side: ui.W,
|
||||
Expand: true,
|
||||
PadX: 2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/******************
|
||||
* Confirm/cancel buttons.
|
||||
******************/
|
||||
|
||||
bottomFrame := ui.NewFrame("Button Frame")
|
||||
frame.Pack(bottomFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 8,
|
||||
})
|
||||
|
||||
var buttons = []struct {
|
||||
Label string
|
||||
F func(ui.EventData) error
|
||||
}{
|
||||
{"Continue", func(ed ui.EventData) error {
|
||||
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 nil
|
||||
return
|
||||
}
|
||||
|
||||
lvl := level.New()
|
||||
|
@ -502,17 +377,11 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
} else {
|
||||
shmem.FlashError("OnCreateNewLevel not attached")
|
||||
}
|
||||
return nil
|
||||
}},
|
||||
} else {
|
||||
// Editing an existing level.
|
||||
|
||||
{"Cancel", func(ed ui.EventData) error {
|
||||
config.OnCancel()
|
||||
return nil
|
||||
}},
|
||||
|
||||
// OK button is for editing an existing level.
|
||||
{"OK", func(ed ui.EventData) error {
|
||||
// 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" +
|
||||
|
@ -526,30 +395,25 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
|||
config.OnReload()
|
||||
}
|
||||
})
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
config.OnCancel()
|
||||
return nil
|
||||
}},
|
||||
}
|
||||
for _, t := range buttons {
|
||||
// If we're editing settings on an existing level, skip the Continue.
|
||||
if (isNewLevel && t.Label == "OK") || (!isNewLevel && t.Label != "OK") {
|
||||
continue
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "Cancel",
|
||||
Font: balance.UIFont,
|
||||
OnClick: func() {
|
||||
config.OnCancel()
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}...)
|
||||
|
||||
form.Create(frame, fields)
|
||||
}
|
||||
|
||||
// Creates the "New Doodad" frame.
|
||||
|
|
|
@ -52,6 +52,10 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
|
|||
// Load all the doodads, skip hidden ones.
|
||||
var items []*doodads.Doodad
|
||||
for _, filename := range doodadsAvailable {
|
||||
if filename == "_autosave.doodad" {
|
||||
continue
|
||||
}
|
||||
|
||||
doodad, err := doodads.LoadFile(filename)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
|
|
@ -103,7 +103,7 @@ func NewTextToolWindow(cfg TextTool) *ui.Window {
|
|||
Font: balance.LabelFont,
|
||||
TextVariable: ¤tText,
|
||||
OnClick: func() {
|
||||
shmem.Prompt("Enter new message: ", func(answer string) {
|
||||
shmem.PromptPre("Enter new message: ", currentText, func(answer string) {
|
||||
if answer != "" {
|
||||
currentText = answer
|
||||
if cfg.OnChangeSettings != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user